1 /*
2  * Copyright (c) 2017, Arnon Yaari
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 
8 /* Baded on code from lsof:
9  * http://www.ibm.com/developerworks/aix/library/au-lsof.html
10  * - dialects/aix/dproc.c:gather_proc_info
11  * - lib/prfp.c:process_file
12  * - dialects/aix/dsock.c:process_socket
13  * - dialects/aix/dproc.c:get_kernel_access
14 */
15 
16 #include <Python.h>
17 #include <stdlib.h>
18 #include <fcntl.h>
19 #define _KERNEL
20 #include <sys/file.h>
21 #undef _KERNEL
22 #include <sys/types.h>
23 #include <sys/core.h>
24 #include <sys/domain.h>
25 #include <sys/un.h>
26 #include <netinet/in_pcb.h>
27 #include <arpa/inet.h>
28 
29 #include "../../_psutil_common.h"
30 #include "net_kernel_structs.h"
31 #include "net_connections.h"
32 #include "common.h"
33 
34 #define NO_SOCKET       (PyObject *)(-1)
35 
36 static int
read_unp_addr(int Kd,KA_T unp_addr,char * buf,size_t buflen)37 read_unp_addr(
38     int Kd,
39     KA_T unp_addr,
40     char *buf,
41     size_t buflen
42 ) {
43     struct sockaddr_un *ua = (struct sockaddr_un *)NULL;
44     struct sockaddr_un un;
45     struct mbuf64 mb;
46     int uo;
47 
48     if (psutil_kread(Kd, unp_addr, (char *)&mb, sizeof(mb))) {
49         return 1;
50     }
51 
52     uo = (int)(mb.m_hdr.mh_data - unp_addr);
53     if ((uo + sizeof(struct sockaddr)) <= sizeof(mb))
54         ua = (struct sockaddr_un *)((char *)&mb + uo);
55     else {
56         if (psutil_kread(Kd, (KA_T)mb.m_hdr.mh_data,
57                          (char *)&un, sizeof(un))) {
58             return 1;
59         }
60         ua = &un;
61     }
62     if (ua && ua->sun_path[0]) {
63         if (mb.m_len > sizeof(struct sockaddr_un))
64             mb.m_len = sizeof(struct sockaddr_un);
65         *((char *)ua + mb.m_len - 1) = '\0';
66         snprintf(buf, buflen, "%s", ua->sun_path);
67     }
68     return 0;
69 }
70 
71 static PyObject *
process_file(int Kd,pid32_t pid,int fd,KA_T fp)72 process_file(int Kd, pid32_t pid, int fd, KA_T fp) {
73     struct file64 f;
74     struct socket64 s;
75     struct protosw64 p;
76     struct domain d;
77     struct inpcb64 inp;
78     int fam;
79     struct tcpcb64 t;
80     int state = PSUTIL_CONN_NONE;
81     unsigned char *laddr = (unsigned char *)NULL;
82     unsigned char *raddr = (unsigned char *)NULL;
83     int rport, lport;
84     char laddr_str[INET6_ADDRSTRLEN];
85     char raddr_str[INET6_ADDRSTRLEN];
86     struct unpcb64 unp;
87     char unix_laddr_str[PATH_MAX] = { 0 };
88     char unix_raddr_str[PATH_MAX] = { 0 };
89 
90     /* Read file structure */
91     if (psutil_kread(Kd, fp, (char *)&f, sizeof(f))) {
92         return NULL;
93     }
94     if (!f.f_count || f.f_type != DTYPE_SOCKET) {
95         return NO_SOCKET;
96     }
97 
98     if (psutil_kread(Kd, (KA_T) f.f_data, (char *) &s, sizeof(s))) {
99         return NULL;
100     }
101 
102     if (!s.so_type) {
103         return NO_SOCKET;
104     }
105 
106     if (!s.so_proto) {
107         PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol handle");
108         return NULL;
109     }
110     if (psutil_kread(Kd, (KA_T)s.so_proto, (char *)&p, sizeof(p))) {
111         return NULL;
112     }
113 
114     if (!p.pr_domain) {
115         PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol domain");
116         return NULL;
117     }
118     if (psutil_kread(Kd, (KA_T)p.pr_domain, (char *)&d, sizeof(d))) {
119         return NULL;
120     }
121 
122     fam = d.dom_family;
123     if (fam == AF_INET || fam == AF_INET6) {
124         /* Read protocol control block */
125         if (!s.so_pcb) {
126             PyErr_SetString(PyExc_RuntimeError, "invalid socket PCB");
127             return NULL;
128         }
129         if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *) &inp, sizeof(inp))) {
130             return NULL;
131         }
132 
133         if (p.pr_protocol == IPPROTO_TCP) {
134             /* If this is a TCP socket, read its control block */
135             if (inp.inp_ppcb
136                 &&  !psutil_kread(Kd, (KA_T)inp.inp_ppcb,
137                                   (char *)&t, sizeof(t)))
138                 state = t.t_state;
139         }
140 
141         if (fam == AF_INET6) {
142             laddr = (unsigned char *)&inp.inp_laddr6;
143             if (!IN6_IS_ADDR_UNSPECIFIED(&inp.inp_faddr6)) {
144                 raddr = (unsigned char *)&inp.inp_faddr6;
145                 rport = (int)ntohs(inp.inp_fport);
146             }
147         }
148         if (fam == AF_INET) {
149             laddr = (unsigned char *)&inp.inp_laddr;
150             if (inp.inp_faddr.s_addr != INADDR_ANY || inp.inp_fport != 0) {
151                 raddr =  (unsigned char *)&inp.inp_faddr;
152                 rport = (int)ntohs(inp.inp_fport);
153             }
154         }
155         lport = (int)ntohs(inp.inp_lport);
156 
157         inet_ntop(fam, laddr, laddr_str, sizeof(laddr_str));
158 
159         if (raddr != NULL) {
160             inet_ntop(fam, raddr, raddr_str, sizeof(raddr_str));
161             return Py_BuildValue("(iii(si)(si)ii)", fd, fam,
162                                  s.so_type, laddr_str, lport, raddr_str,
163                                  rport, state, pid);
164         }
165         else {
166             return Py_BuildValue("(iii(si)()ii)", fd, fam,
167                                  s.so_type, laddr_str, lport, state,
168                                  pid);
169         }
170     }
171 
172 
173     if (fam == AF_UNIX) {
174         if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *)&unp, sizeof(unp))) {
175             return NULL;
176         }
177         if ((KA_T) f.f_data != (KA_T) unp.unp_socket) {
178             PyErr_SetString(PyExc_RuntimeError, "unp_socket mismatch");
179             return NULL;
180         }
181 
182         if (unp.unp_addr) {
183             if (read_unp_addr(Kd, unp.unp_addr, unix_laddr_str,
184                               sizeof(unix_laddr_str))) {
185                 return NULL;
186             }
187         }
188 
189         if (unp.unp_conn) {
190             if (psutil_kread(Kd, (KA_T) unp.unp_conn, (char *)&unp,
191                 sizeof(unp))) {
192                 return NULL;
193             }
194             if (read_unp_addr(Kd, unp.unp_addr, unix_raddr_str,
195                               sizeof(unix_raddr_str))) {
196                 return NULL;
197             }
198         }
199 
200         return Py_BuildValue("(iiissii)", fd, d.dom_family,
201                  s.so_type, unix_laddr_str, unix_raddr_str, PSUTIL_CONN_NONE,
202                  pid);
203     }
204     return NO_SOCKET;
205 }
206 
207 PyObject *
psutil_net_connections(PyObject * self,PyObject * args)208 psutil_net_connections(PyObject *self, PyObject *args) {
209     PyObject *py_retlist = PyList_New(0);
210     PyObject *py_tuple = NULL;
211     KA_T fp;
212     int Kd = -1;
213     int i, np;
214     struct procentry64 *p;
215     struct fdsinfo64 *fds = (struct fdsinfo64 *)NULL;
216     pid32_t requested_pid;
217     pid32_t pid;
218     struct procentry64 *processes = (struct procentry64 *)NULL;
219                     /* the process table */
220 
221     if (py_retlist == NULL)
222         goto error;
223     if (! PyArg_ParseTuple(args, "i", &requested_pid))
224         goto error;
225 
226     Kd = open(KMEM, O_RDONLY, 0);
227     if (Kd < 0) {
228         PyErr_SetFromErrnoWithFilename(PyExc_OSError, KMEM);
229         goto error;
230     }
231 
232     processes = psutil_read_process_table(&np);
233     if (!processes)
234         goto error;
235 
236     /* Loop through processes */
237     for (p = processes; np > 0; np--, p++) {
238         pid = p->pi_pid;
239         if (requested_pid != -1 && requested_pid != pid)
240             continue;
241         if (p->pi_state == 0 || p->pi_state == SZOMB)
242             continue;
243 
244         if (!fds) {
245             fds = (struct fdsinfo64 *)malloc((size_t)FDSINFOSIZE);
246             if (!fds) {
247                 PyErr_NoMemory();
248                 goto error;
249             }
250         }
251         if (getprocs64((struct procentry64 *)NULL, PROCSIZE, fds, FDSINFOSIZE,
252               &pid, 1)
253         != 1)
254             continue;
255 
256         /* loop over file descriptors */
257         for (i = 0; i < p->pi_maxofile; i++) {
258             fp = (KA_T)fds->pi_ufd[i].fp;
259             if (fp) {
260                 py_tuple = process_file(Kd, p->pi_pid, i, fp);
261                 if (py_tuple == NULL)
262                     goto error;
263                 if (py_tuple != NO_SOCKET) {
264                     if (PyList_Append(py_retlist, py_tuple))
265                         goto error;
266                     Py_DECREF(py_tuple);
267                 }
268             }
269         }
270     }
271     close(Kd);
272     free(processes);
273     if (fds != NULL)
274         free(fds);
275     return py_retlist;
276 
277 error:
278     Py_XDECREF(py_tuple);
279     Py_DECREF(py_retlist);
280     if (Kd > 0)
281         close(Kd);
282     if (processes != NULL)
283         free(processes);
284     if (fds != NULL)
285         free(fds);
286     return NULL;
287 }
288