1 /*
2 * Copyright (c) 2009, Giampaolo Rodola'.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 *
7 * Retrieves system-wide open socket connections. This is based off of
8 * sockstat utility source code:
9 * https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c
10 */
11
12 #include <Python.h>
13 #include <sys/user.h>
14 #include <sys/file.h>
15 #include <sys/socketvar.h> // for struct xsocket
16 #include <sys/un.h>
17 #include <sys/unpcb.h>
18 #include <sys/sysctl.h>
19 #if defined(__FreeBSD_version) && __FreeBSD_version < 800000
20 #include <netinet/in_systm.h>
21 #endif
22 #include <netinet/in.h> // for xinpcb struct
23 #include <netinet/ip.h>
24 #include <netinet/in_pcb.h>
25 #include <netinet/tcp_var.h> // for struct xtcpcb
26 #include <arpa/inet.h> // for inet_ntop()
27
28 #include "../../_psutil_common.h"
29 #include "../../_psutil_posix.h"
30
31 static struct xfile *psutil_xfiles;
32 static int psutil_nxfiles;
33
34
35 int
psutil_populate_xfiles(void)36 psutil_populate_xfiles(void) {
37 size_t len;
38
39 if ((psutil_xfiles = malloc(len = sizeof *psutil_xfiles)) == NULL) {
40 PyErr_NoMemory();
41 return 0;
42 }
43 while (sysctlbyname("kern.file", psutil_xfiles, &len, 0, 0) == -1) {
44 if (errno != ENOMEM) {
45 PyErr_SetFromErrno(0);
46 return 0;
47 }
48 len *= 2;
49 if ((psutil_xfiles = realloc(psutil_xfiles, len)) == NULL) {
50 PyErr_NoMemory();
51 return 0;
52 }
53 }
54 if (len > 0 && psutil_xfiles->xf_size != sizeof *psutil_xfiles) {
55 PyErr_Format(PyExc_RuntimeError, "struct xfile size mismatch");
56 return 0;
57 }
58 psutil_nxfiles = len / sizeof *psutil_xfiles;
59 return 1;
60 }
61
62
63 struct xfile *
psutil_get_file_from_sock(void * sock)64 psutil_get_file_from_sock(void *sock) {
65 struct xfile *xf;
66 int n;
67
68 for (xf = psutil_xfiles, n = 0; n < psutil_nxfiles; ++n, ++xf) {
69 if (xf->xf_data == sock)
70 return xf;
71 }
72 return NULL;
73 }
74
75
76 // Reference:
77 // https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c
psutil_gather_inet(int proto,PyObject * py_retlist)78 int psutil_gather_inet(int proto, PyObject *py_retlist) {
79 struct xinpgen *xig, *exig;
80 struct xinpcb *xip;
81 struct xtcpcb *xtp;
82 #if __FreeBSD_version >= 1200026
83 struct xinpcb *inp;
84 #else
85 struct inpcb *inp;
86 #endif
87 struct xsocket *so;
88 const char *varname = NULL;
89 size_t len, bufsize;
90 void *buf;
91 int retry;
92 int type;
93
94 PyObject *py_tuple = NULL;
95 PyObject *py_laddr = NULL;
96 PyObject *py_raddr = NULL;
97
98 switch (proto) {
99 case IPPROTO_TCP:
100 varname = "net.inet.tcp.pcblist";
101 type = SOCK_STREAM;
102 break;
103 case IPPROTO_UDP:
104 varname = "net.inet.udp.pcblist";
105 type = SOCK_DGRAM;
106 break;
107 }
108
109 buf = NULL;
110 bufsize = 8192;
111 retry = 5;
112 do {
113 for (;;) {
114 buf = realloc(buf, bufsize);
115 if (buf == NULL)
116 continue; // XXX
117 len = bufsize;
118 if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
119 break;
120 if (errno != ENOMEM) {
121 PyErr_SetFromErrno(0);
122 goto error;
123 }
124 bufsize *= 2;
125 }
126 xig = (struct xinpgen *)buf;
127 exig = (struct xinpgen *)(void *)((char *)buf + len - sizeof *exig);
128 if (xig->xig_len != sizeof *xig || exig->xig_len != sizeof *exig) {
129 PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
130 goto error;
131 }
132 } while (xig->xig_gen != exig->xig_gen && retry--);
133
134 for (;;) {
135 struct xfile *xf;
136 int lport, rport, status, family;
137
138 xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
139 if (xig >= exig)
140 break;
141
142 switch (proto) {
143 case IPPROTO_TCP:
144 xtp = (struct xtcpcb *)xig;
145 if (xtp->xt_len != sizeof *xtp) {
146 PyErr_Format(PyExc_RuntimeError,
147 "struct xtcpcb size mismatch");
148 goto error;
149 }
150 inp = &xtp->xt_inp;
151 #if __FreeBSD_version >= 1200026
152 so = &inp->xi_socket;
153 status = xtp->t_state;
154 #else
155 so = &xtp->xt_socket;
156 status = xtp->xt_tp.t_state;
157 #endif
158 break;
159 case IPPROTO_UDP:
160 xip = (struct xinpcb *)xig;
161 if (xip->xi_len != sizeof *xip) {
162 PyErr_Format(PyExc_RuntimeError,
163 "struct xinpcb size mismatch");
164 goto error;
165 }
166 #if __FreeBSD_version >= 1200026
167 inp = xip;
168 #else
169 inp = &xip->xi_inp;
170 #endif
171 so = &xip->xi_socket;
172 status = PSUTIL_CONN_NONE;
173 break;
174 default:
175 PyErr_Format(PyExc_RuntimeError, "invalid proto");
176 goto error;
177 }
178
179 char lip[200], rip[200];
180
181 xf = psutil_get_file_from_sock(so->xso_so);
182 if (xf == NULL)
183 continue;
184 lport = ntohs(inp->inp_lport);
185 rport = ntohs(inp->inp_fport);
186
187 if (inp->inp_vflag & INP_IPV4) {
188 family = AF_INET;
189 inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip));
190 inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip));
191 }
192 else if (inp->inp_vflag & INP_IPV6) {
193 family = AF_INET6;
194 inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip));
195 inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip));
196 }
197
198 // construct python tuple/list
199 py_laddr = Py_BuildValue("(si)", lip, lport);
200 if (!py_laddr)
201 goto error;
202 if (rport != 0)
203 py_raddr = Py_BuildValue("(si)", rip, rport);
204 else
205 py_raddr = Py_BuildValue("()");
206 if (!py_raddr)
207 goto error;
208 py_tuple = Py_BuildValue(
209 "iiiNNi" _Py_PARSE_PID,
210 xf->xf_fd, // fd
211 family, // family
212 type, // type
213 py_laddr, // laddr
214 py_raddr, // raddr
215 status, // status
216 xf->xf_pid // pid
217 );
218 if (!py_tuple)
219 goto error;
220 if (PyList_Append(py_retlist, py_tuple))
221 goto error;
222 Py_CLEAR(py_tuple);
223 }
224
225 free(buf);
226 return 1;
227
228 error:
229 Py_XDECREF(py_tuple);
230 Py_XDECREF(py_laddr);
231 Py_XDECREF(py_raddr);
232 free(buf);
233 return 0;
234 }
235
236
psutil_gather_unix(int proto,PyObject * py_retlist)237 int psutil_gather_unix(int proto, PyObject *py_retlist) {
238 struct xunpgen *xug, *exug;
239 struct xunpcb *xup;
240 const char *varname = NULL;
241 const char *protoname = NULL;
242 size_t len;
243 size_t bufsize;
244 void *buf;
245 int retry;
246 struct sockaddr_un *sun;
247 char path[PATH_MAX];
248
249 PyObject *py_tuple = NULL;
250 PyObject *py_lpath = NULL;
251
252 switch (proto) {
253 case SOCK_STREAM:
254 varname = "net.local.stream.pcblist";
255 protoname = "stream";
256 break;
257 case SOCK_DGRAM:
258 varname = "net.local.dgram.pcblist";
259 protoname = "dgram";
260 break;
261 }
262
263 buf = NULL;
264 bufsize = 8192;
265 retry = 5;
266
267 do {
268 for (;;) {
269 buf = realloc(buf, bufsize);
270 if (buf == NULL) {
271 PyErr_NoMemory();
272 goto error;
273 }
274 len = bufsize;
275 if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
276 break;
277 if (errno != ENOMEM) {
278 PyErr_SetFromErrno(0);
279 goto error;
280 }
281 bufsize *= 2;
282 }
283 xug = (struct xunpgen *)buf;
284 exug = (struct xunpgen *)(void *)
285 ((char *)buf + len - sizeof *exug);
286 if (xug->xug_len != sizeof *xug || exug->xug_len != sizeof *exug) {
287 PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
288 goto error;
289 }
290 } while (xug->xug_gen != exug->xug_gen && retry--);
291
292 for (;;) {
293 struct xfile *xf;
294
295 xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
296 if (xug >= exug)
297 break;
298 xup = (struct xunpcb *)xug;
299 if (xup->xu_len != sizeof *xup)
300 goto error;
301
302 xf = psutil_get_file_from_sock(xup->xu_socket.xso_so);
303 if (xf == NULL)
304 continue;
305
306 sun = (struct sockaddr_un *)&xup->xu_addr;
307 snprintf(path, sizeof(path), "%.*s",
308 (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
309 sun->sun_path);
310 py_lpath = PyUnicode_DecodeFSDefault(path);
311 if (! py_lpath)
312 goto error;
313
314 py_tuple = Py_BuildValue("(iiiOsii)",
315 xf->xf_fd, // fd
316 AF_UNIX, // family
317 proto, // type
318 py_lpath, // lpath
319 "", // rath
320 PSUTIL_CONN_NONE, // status
321 xf->xf_pid); // pid
322 if (!py_tuple)
323 goto error;
324 if (PyList_Append(py_retlist, py_tuple))
325 goto error;
326 Py_DECREF(py_lpath);
327 Py_DECREF(py_tuple);
328 }
329
330 free(buf);
331 return 1;
332
333 error:
334 Py_XDECREF(py_tuple);
335 Py_XDECREF(py_lpath);
336 free(buf);
337 return 0;
338 }
339
340
341 PyObject*
psutil_net_connections(PyObject * self,PyObject * args)342 psutil_net_connections(PyObject* self, PyObject* args) {
343 // Return system-wide open connections.
344 PyObject *py_retlist = PyList_New(0);
345
346 if (py_retlist == NULL)
347 return NULL;
348 if (psutil_populate_xfiles() != 1)
349 goto error;
350 if (psutil_gather_inet(IPPROTO_TCP, py_retlist) == 0)
351 goto error;
352 if (psutil_gather_inet(IPPROTO_UDP, py_retlist) == 0)
353 goto error;
354 if (psutil_gather_unix(SOCK_STREAM, py_retlist) == 0)
355 goto error;
356 if (psutil_gather_unix(SOCK_DGRAM, py_retlist) == 0)
357 goto error;
358
359 free(psutil_xfiles);
360 return py_retlist;
361
362 error:
363 Py_DECREF(py_retlist);
364 free(psutil_xfiles);
365 return NULL;
366 }
367