1 /*
2  * Copyright (c) 2009, Giampaolo Rodola'.
3  * Copyright (c) 2015, Ryo ONODERA.
4  * All rights reserved.
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include <Python.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/sysctl.h>
14 #include <sys/socket.h>
15 #include <sys/types.h>
16 #include <netinet/in.h>
17 #include <string.h>
18 #include <sys/cdefs.h>
19 #include <arpa/inet.h>
20 #include <sys/queue.h>
21 #include <sys/un.h>
22 #include <sys/file.h>
23 
24 #include "../../_psutil_common.h"
25 #include "../../_psutil_posix.h"
26 
27 
28 // address family filter
29 enum af_filter {
30     INET,
31     INET4,
32     INET6,
33     TCP,
34     TCP4,
35     TCP6,
36     UDP,
37     UDP4,
38     UDP6,
39     UNIX,
40     ALL,
41 };
42 
43 // kinfo_file results
44 struct kif {
45     SLIST_ENTRY(kif) kifs;
46     struct kinfo_file *kif;
47 };
48 
49 // kinfo_file results list
50 SLIST_HEAD(kifhead, kif) kihead = SLIST_HEAD_INITIALIZER(kihead);
51 
52 
53 // kinfo_pcb results
54 struct kpcb {
55     SLIST_ENTRY(kpcb) kpcbs;
56     struct kinfo_pcb *kpcb;
57 };
58 
59 // kinfo_pcb results list
60 SLIST_HEAD(kpcbhead, kpcb) kpcbhead = SLIST_HEAD_INITIALIZER(kpcbhead);
61 
62 static void psutil_kiflist_init(void);
63 static void psutil_kiflist_clear(void);
64 static void psutil_kpcblist_init(void);
65 static void psutil_kpcblist_clear(void);
66 static int psutil_get_files(void);
67 static int psutil_get_sockets(const char *name);
68 static int psutil_get_info(int aff);
69 
70 
71 // Initialize kinfo_file results list.
72 static void
psutil_kiflist_init(void)73 psutil_kiflist_init(void) {
74     SLIST_INIT(&kihead);
75     return;
76 }
77 
78 
79 // Clear kinfo_file results list.
80 static void
psutil_kiflist_clear(void)81 psutil_kiflist_clear(void) {
82      while (!SLIST_EMPTY(&kihead)) {
83              SLIST_REMOVE_HEAD(&kihead, kifs);
84      }
85 
86     return;
87 }
88 
89 
90 // Initialize kinof_pcb result list.
91 static void
psutil_kpcblist_init(void)92 psutil_kpcblist_init(void) {
93     SLIST_INIT(&kpcbhead);
94     return;
95 }
96 
97 
98 // Clear kinof_pcb result list.
99 static void
psutil_kpcblist_clear(void)100 psutil_kpcblist_clear(void) {
101      while (!SLIST_EMPTY(&kpcbhead)) {
102              SLIST_REMOVE_HEAD(&kpcbhead, kpcbs);
103      }
104 
105     return;
106 }
107 
108 
109 // Get all open files including socket.
110 static int
psutil_get_files(void)111 psutil_get_files(void) {
112     size_t len;
113     size_t j;
114     int mib[6];
115     char *buf;
116     off_t offset;
117 
118     mib[0] = CTL_KERN;
119     mib[1] = KERN_FILE2;
120     mib[2] = KERN_FILE_BYFILE;
121     mib[3] = 0;
122     mib[4] = sizeof(struct kinfo_file);
123     mib[5] = 0;
124 
125     if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
126         PyErr_SetFromErrno(PyExc_OSError);
127         return -1;
128     }
129 
130     offset = len % sizeof(off_t);
131     mib[5] = len / sizeof(struct kinfo_file);
132 
133     if ((buf = malloc(len + offset)) == NULL) {
134         PyErr_NoMemory();
135         return -1;
136     }
137 
138     if (sysctl(mib, 6, buf + offset, &len, NULL, 0) == -1) {
139         free(buf);
140         PyErr_SetFromErrno(PyExc_OSError);
141         return -1;
142     }
143 
144     len /= sizeof(struct kinfo_file);
145     struct kinfo_file *ki = (struct kinfo_file *)(buf + offset);
146 
147     for (j = 0; j < len; j++) {
148         struct kif *kif = malloc(sizeof(struct kif));
149         kif->kif = &ki[j];
150         SLIST_INSERT_HEAD(&kihead, kif, kifs);
151     }
152 
153     /*
154     // debug
155     struct kif *k;
156     SLIST_FOREACH(k, &kihead, kifs) {
157             printf("%d\n", k->kif->ki_pid);  // NOQA
158     }
159     */
160 
161     return 0;
162 }
163 
164 
165 // Get open sockets.
166 static int
psutil_get_sockets(const char * name)167 psutil_get_sockets(const char *name) {
168     size_t namelen;
169     int mib[8];
170     struct kinfo_pcb *pcb;
171     size_t len;
172     size_t j;
173 
174     memset(mib, 0, sizeof(mib));
175 
176     if (sysctlnametomib(name, mib, &namelen) == -1) {
177         PyErr_SetFromErrno(PyExc_OSError);
178         return -1;
179     }
180 
181     if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1) {
182         PyErr_SetFromErrno(PyExc_OSError);
183         return -1;
184     }
185 
186     if ((pcb = malloc(len)) == NULL) {
187         PyErr_NoMemory();
188         return -1;
189     }
190     memset(pcb, 0, len);
191 
192     mib[6] = sizeof(*pcb);
193     mib[7] = len / sizeof(*pcb);
194 
195     if (sysctl(mib, __arraycount(mib), pcb, &len, NULL, 0) == -1) {
196         free(pcb);
197         PyErr_SetFromErrno(PyExc_OSError);
198         return -1;
199     }
200 
201     len /= sizeof(struct kinfo_pcb);
202     struct kinfo_pcb *kp = (struct kinfo_pcb *)pcb;
203 
204     for (j = 0; j < len; j++) {
205         struct kpcb *kpcb = malloc(sizeof(struct kpcb));
206         kpcb->kpcb = &kp[j];
207         SLIST_INSERT_HEAD(&kpcbhead, kpcb, kpcbs);
208     }
209     return 0;
210 }
211 
212 
213 // Collect open file and connections.
214 static int
psutil_get_info(int aff)215 psutil_get_info(int aff) {
216     switch (aff) {
217         case INET:
218             if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
219                 return -1;
220             if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
221                 return -1;
222             if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
223                 return -1;
224             if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
225                 return -1;
226             break;
227         case INET4:
228             if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
229                 return -1;
230             if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
231                 return -1;
232             break;
233         case INET6:
234             if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
235                 return -1;
236             if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
237                 return -1;
238             break;
239         case TCP:
240             if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
241                 return -1;
242             if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
243                 return -1;
244             break;
245         case TCP4:
246             if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
247                 return -1;
248             break;
249         case TCP6:
250             if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
251                 return -1;
252             break;
253         case UDP:
254             if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
255                 return -1;
256             if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
257                 return -1;
258             break;
259         case UDP4:
260             if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
261                 return -1;
262             break;
263         case UDP6:
264             if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
265                 return -1;
266             break;
267         case UNIX:
268             if (psutil_get_sockets("net.local.stream.pcblist") != 0)
269                 return -1;
270             if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0)
271                 return -1;
272             if (psutil_get_sockets("net.local.dgram.pcblist") != 0)
273                 return -1;
274             break;
275         case ALL:
276             if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
277                 return -1;
278             if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
279                 return -1;
280             if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
281                 return -1;
282             if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
283                 return -1;
284             if (psutil_get_sockets("net.local.stream.pcblist") != 0)
285                 return -1;
286             if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0)
287                 return -1;
288             if (psutil_get_sockets("net.local.dgram.pcblist") != 0)
289                 return -1;
290             break;
291     }
292 
293     return 0;
294 }
295 
296 
297 /*
298  * Return system-wide connections (unless a pid != -1 is passed).
299  */
300 PyObject *
psutil_net_connections(PyObject * self,PyObject * args)301 psutil_net_connections(PyObject *self, PyObject *args) {
302     char laddr[PATH_MAX];
303     char raddr[PATH_MAX];
304     int32_t lport;
305     int32_t rport;
306     int32_t status;
307     pid_t pid;
308     PyObject *py_tuple = NULL;
309     PyObject *py_laddr = NULL;
310     PyObject *py_raddr = NULL;
311     PyObject *py_retlist = PyList_New(0);
312 
313     if (py_retlist == NULL)
314         return NULL;
315 
316     if (! PyArg_ParseTuple(args, "l", &pid))
317         return NULL;
318 
319     psutil_kiflist_init();
320     psutil_kpcblist_init();
321     if (psutil_get_files() != 0)
322         goto error;
323     if (psutil_get_info(ALL) != 0)
324         goto error;
325 
326     struct kif *k;
327     SLIST_FOREACH(k, &kihead, kifs) {
328         struct kpcb *kp;
329         if ((pid != -1) && (k->kif->ki_pid != (unsigned int)pid))
330             continue;
331         SLIST_FOREACH(kp, &kpcbhead, kpcbs) {
332             if (k->kif->ki_fdata != kp->kpcb->ki_sockaddr)
333                 continue;
334 
335             // IPv4 or IPv6
336             if ((kp->kpcb->ki_family == AF_INET) ||
337                     (kp->kpcb->ki_family == AF_INET6)) {
338 
339                 if (kp->kpcb->ki_family == AF_INET) {
340                     // IPv4
341                     struct sockaddr_in *sin_src =
342                         (struct sockaddr_in *)&kp->kpcb->ki_src;
343                     struct sockaddr_in *sin_dst =
344                         (struct sockaddr_in *)&kp->kpcb->ki_dst;
345                     // source addr and port
346                     inet_ntop(AF_INET, &sin_src->sin_addr, laddr,
347                               sizeof(laddr));
348                     lport = ntohs(sin_src->sin_port);
349                     // remote addr and port
350                     inet_ntop(AF_INET, &sin_dst->sin_addr, raddr,
351                               sizeof(raddr));
352                     rport = ntohs(sin_dst->sin_port);
353                 }
354                 else {
355                     // IPv6
356                     struct sockaddr_in6 *sin6_src =
357                         (struct sockaddr_in6 *)&kp->kpcb->ki_src;
358                     struct sockaddr_in6 *sin6_dst =
359                         (struct sockaddr_in6 *)&kp->kpcb->ki_dst;
360                     // local addr and port
361                     inet_ntop(AF_INET6, &sin6_src->sin6_addr, laddr,
362                               sizeof(laddr));
363                     lport = ntohs(sin6_src->sin6_port);
364                     // remote addr and port
365                     inet_ntop(AF_INET6, &sin6_dst->sin6_addr, raddr,
366                               sizeof(raddr));
367                     rport = ntohs(sin6_dst->sin6_port);
368                 }
369 
370                 // status
371                 if (kp->kpcb->ki_type == SOCK_STREAM)
372                     status = kp->kpcb->ki_tstate;
373                 else
374                     status = PSUTIL_CONN_NONE;
375 
376                 // build addr tuple
377                 py_laddr = Py_BuildValue("(si)", laddr, lport);
378                 if (! py_laddr)
379                     goto error;
380                 if (rport != 0)
381                     py_raddr = Py_BuildValue("(si)", raddr, rport);
382                 else
383                     py_raddr = Py_BuildValue("()");
384                 if (! py_raddr)
385                     goto error;
386             }
387             else if (kp->kpcb->ki_family == AF_UNIX) {
388                 // UNIX sockets
389                 struct sockaddr_un *sun_src =
390                     (struct sockaddr_un *)&kp->kpcb->ki_src;
391                 struct sockaddr_un *sun_dst =
392                     (struct sockaddr_un *)&kp->kpcb->ki_dst;
393                 strcpy(laddr, sun_src->sun_path);
394                 strcpy(raddr, sun_dst->sun_path);
395                 status = PSUTIL_CONN_NONE;
396                 py_laddr = PyUnicode_DecodeFSDefault(laddr);
397                 if (! py_laddr)
398                     goto error;
399                 py_raddr = PyUnicode_DecodeFSDefault(raddr);
400                 if (! py_raddr)
401                     goto error;
402             }
403             else {
404                 continue;
405             }
406 
407             // append tuple to list
408             py_tuple = Py_BuildValue(
409                 "(iiiOOii)",
410                 k->kif->ki_fd,
411                 kp->kpcb->ki_family,
412                 kp->kpcb->ki_type,
413                 py_laddr,
414                 py_raddr,
415                 status,
416                 k->kif->ki_pid);
417             if (! py_tuple)
418                 goto error;
419             if (PyList_Append(py_retlist, py_tuple))
420                 goto error;
421             Py_DECREF(py_laddr);
422             Py_DECREF(py_raddr);
423             Py_DECREF(py_tuple);
424         }
425     }
426 
427     psutil_kiflist_clear();
428     psutil_kpcblist_clear();
429     return py_retlist;
430 
431 error:
432     Py_XDECREF(py_tuple);
433     Py_XDECREF(py_laddr);
434     Py_XDECREF(py_raddr);
435     return 0;
436 }
437