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