Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2014 Zubin Mithra <zubin.mithra@gmail.com>
3 : : * Copyright (c) 2014-2016 Dmitry V. Levin <ldv@altlinux.org>
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions
8 : : * are met:
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. The name of the author may not be used to endorse or promote products
15 : : * derived from this software without specific prior written permission.
16 : : *
17 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : : */
28 : :
29 : : #include "defs.h"
30 : : #include <netinet/in.h>
31 : : #include <sys/socket.h>
32 : : #include <arpa/inet.h>
33 : : #include <linux/netlink.h>
34 : : #include <linux/sock_diag.h>
35 : : #include <linux/inet_diag.h>
36 : : #include <linux/unix_diag.h>
37 : : #include <linux/netlink_diag.h>
38 : : #include <linux/rtnetlink.h>
39 : : #include "xlat/netlink_protocols.h"
40 : :
41 : : #include <sys/un.h>
42 : : #ifndef UNIX_PATH_MAX
43 : : # define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) 0)->sun_path)
44 : : #endif
45 : :
46 : : typedef struct {
47 : : unsigned long inode;
48 : : char *details;
49 : : } cache_entry;
50 : :
51 : : #define CACHE_SIZE 1024U
52 : : static cache_entry cache[CACHE_SIZE];
53 : : #define CACHE_MASK (CACHE_SIZE - 1)
54 : :
55 : : static int
56 : 45 : cache_and_print_inode_details(const unsigned long inode, char *const details)
57 : : {
58 : 45 : cache_entry *e = &cache[inode & CACHE_MASK];
59 : 45 : free(e->details);
60 : 45 : e->inode = inode;
61 : 45 : e->details = details;
62 : :
63 : 45 : tprints(details);
64 : 45 : return 1;
65 : : }
66 : :
67 : : bool
68 : 235 : print_sockaddr_by_inode_cached(const unsigned long inode)
69 : : {
70 : 235 : const cache_entry *const e = &cache[inode & CACHE_MASK];
71 [ + - ][ + + ]: 235 : if (e && inode == e->inode) {
72 : 120 : tprints(e->details);
73 : 120 : return true;
74 : : }
75 : 115 : return false;
76 : : }
77 : :
78 : : static bool
79 : 115 : send_query(const int fd, void *req, size_t req_size)
80 : : {
81 : 115 : struct sockaddr_nl nladdr = {
82 : : .nl_family = AF_NETLINK
83 : : };
84 : 115 : struct iovec iov = {
85 : : .iov_base = req,
86 : : .iov_len = req_size
87 : : };
88 : 115 : const struct msghdr msg = {
89 : : .msg_name = &nladdr,
90 : : .msg_namelen = sizeof(nladdr),
91 : : .msg_iov = &iov,
92 : : .msg_iovlen = 1
93 : : };
94 : :
95 : : for (;;) {
96 [ - + ]: 115 : if (sendmsg(fd, &msg, 0) < 0) {
97 [ # # ]: 0 : if (errno == EINTR)
98 : 0 : continue;
99 : 0 : return false;
100 : : }
101 : 115 : return true;
102 : : }
103 : : }
104 : :
105 : : static bool
106 : 40 : inet_send_query(const int fd, const int family, const int proto)
107 : : {
108 : : struct {
109 : : const struct nlmsghdr nlh;
110 : : const struct inet_diag_req_v2 idr;
111 : 40 : } req = {
112 : : .nlh = {
113 : : .nlmsg_len = sizeof(req),
114 : : .nlmsg_type = SOCK_DIAG_BY_FAMILY,
115 : : .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
116 : : },
117 : : .idr = {
118 : : .sdiag_family = family,
119 : : .sdiag_protocol = proto,
120 : : .idiag_states = -1
121 : : }
122 : : };
123 : 40 : return send_query(fd, &req, sizeof(req));
124 : : }
125 : :
126 : : static int
127 : 190 : inet_parse_response(const char *const proto_name, const void *const data,
128 : : const int data_len, const unsigned long inode)
129 : : {
130 : 190 : const struct inet_diag_msg *const diag_msg = data;
131 : : static const char zero_addr[sizeof(struct in6_addr)];
132 : : socklen_t addr_size, text_size;
133 : :
134 [ - + ]: 190 : if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg)))
135 : 0 : return -1;
136 [ + + ]: 190 : if (diag_msg->idiag_inode != inode)
137 : 175 : return 0;
138 : :
139 [ + - - ]: 15 : switch(diag_msg->idiag_family) {
140 : : case AF_INET:
141 : 15 : addr_size = sizeof(struct in_addr);
142 : 15 : text_size = INET_ADDRSTRLEN;
143 : 15 : break;
144 : : case AF_INET6:
145 : 0 : addr_size = sizeof(struct in6_addr);
146 : 0 : text_size = INET6_ADDRSTRLEN;
147 : 0 : break;
148 : : default:
149 : 0 : return -1;
150 : : }
151 : :
152 : 15 : char src_buf[text_size];
153 : : char *details;
154 : :
155 [ - + ]: 15 : if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
156 : : src_buf, text_size))
157 : 0 : return -1;
158 : :
159 [ + + ][ - + ]: 15 : if (diag_msg->id.idiag_dport ||
160 : 10 : memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
161 : 10 : char dst_buf[text_size];
162 : :
163 [ - + ]: 10 : if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
164 : : dst_buf, text_size))
165 : 0 : return -1;
166 : :
167 [ - + ]: 10 : if (asprintf(&details, "%s:[%s:%u->%s:%u]", proto_name,
168 : 10 : src_buf, ntohs(diag_msg->id.idiag_sport),
169 : 10 : dst_buf, ntohs(diag_msg->id.idiag_dport)) < 0)
170 : 10 : return false;
171 : : } else {
172 [ - + ]: 5 : if (asprintf(&details, "%s:[%s:%u]", proto_name, src_buf,
173 : 5 : ntohs(diag_msg->id.idiag_sport)) < 0)
174 : 0 : return false;
175 : : }
176 : :
177 : 190 : return cache_and_print_inode_details(inode, details);
178 : : }
179 : :
180 : : static bool
181 : 115 : receive_responses(const int fd, const unsigned long inode,
182 : : const char *proto_name,
183 : : int (* parser) (const char *, const void *,
184 : : int, unsigned long))
185 : : {
186 : : static union {
187 : : struct nlmsghdr hdr;
188 : : long buf[8192 / sizeof(long)];
189 : : } hdr_buf;
190 : :
191 : 115 : struct sockaddr_nl nladdr = {
192 : : .nl_family = AF_NETLINK
193 : : };
194 : 115 : struct iovec iov = {
195 : : .iov_base = hdr_buf.buf,
196 : : .iov_len = sizeof(hdr_buf.buf)
197 : : };
198 : 115 : int flags = 0;
199 : :
200 : 51 : for (;;) {
201 : 166 : struct msghdr msg = {
202 : : .msg_name = &nladdr,
203 : : .msg_namelen = sizeof(nladdr),
204 : : .msg_iov = &iov,
205 : : .msg_iovlen = 1
206 : : };
207 : :
208 : 166 : ssize_t ret = recvmsg(fd, &msg, flags);
209 [ - + ]: 166 : if (ret < 0) {
210 [ # # ]: 0 : if (errno == EINTR)
211 : 0 : continue;
212 : 115 : return false;
213 : : }
214 : :
215 : 166 : const struct nlmsghdr *h = &hdr_buf.hdr;
216 [ + - ][ + - ]: 166 : if (!NLMSG_OK(h, ret))
[ - + ]
217 : 0 : return false;
218 [ + + ][ + - ]: 3460 : for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
[ + - ]
219 [ + + ]: 3409 : if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
220 : 35 : return false;
221 : 3374 : const int rc = parser(proto_name, NLMSG_DATA(h),
222 : 3374 : h->nlmsg_len, inode);
223 [ + + ]: 3374 : if (rc > 0)
224 : 45 : return true;
225 [ + + ]: 3329 : if (rc < 0)
226 : 35 : return false;
227 : : }
228 : 51 : flags = MSG_DONTWAIT;
229 : : }
230 : : }
231 : :
232 : : static bool
233 : 40 : inet_print(const int fd, const int family, const int protocol,
234 : : const unsigned long inode, const char *proto_name)
235 : : {
236 : 40 : return inet_send_query(fd, family, protocol)
237 [ + - ][ + + ]: 40 : && receive_responses(fd, inode, proto_name, inet_parse_response);
238 : : }
239 : :
240 : : static bool
241 : 60 : unix_send_query(const int fd, const unsigned long inode)
242 : : {
243 : : struct {
244 : : const struct nlmsghdr nlh;
245 : : const struct unix_diag_req udr;
246 : 60 : } req = {
247 : : .nlh = {
248 : : .nlmsg_len = sizeof(req),
249 : : .nlmsg_type = SOCK_DIAG_BY_FAMILY,
250 : : .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
251 : : },
252 : : .udr = {
253 : : .sdiag_family = AF_UNIX,
254 : : .udiag_ino = inode,
255 : : .udiag_states = -1,
256 : : .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
257 : : }
258 : : };
259 : 60 : return send_query(fd, &req, sizeof(req));
260 : : }
261 : :
262 : : static int
263 : 2724 : unix_parse_response(const char *proto_name, const void *data,
264 : : const int data_len, const unsigned long inode)
265 : : {
266 : 2724 : const struct unix_diag_msg *diag_msg = data;
267 : : struct rtattr *attr;
268 : 2724 : int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg));
269 : 2724 : uint32_t peer = 0;
270 : 2724 : size_t path_len = 0;
271 : : char path[UNIX_PATH_MAX + 1];
272 : :
273 [ - + ]: 2724 : if (rta_len < 0)
274 : 0 : return -1;
275 [ + + ]: 2724 : if (diag_msg->udiag_ino != inode)
276 : 2664 : return 0;
277 [ - + ]: 60 : if (diag_msg->udiag_family != AF_UNIX)
278 : 0 : return -1;
279 : :
280 [ + + ]: 160 : for (attr = (struct rtattr *) (diag_msg + 1);
281 [ + - ][ + - ]: 100 : RTA_OK(attr, rta_len);
282 : 100 : attr = RTA_NEXT(attr, rta_len)) {
283 [ + + + ]: 100 : switch (attr->rta_type) {
284 : : case UNIX_DIAG_NAME:
285 [ + - ]: 20 : if (!path_len) {
286 : 20 : path_len = RTA_PAYLOAD(attr);
287 [ - + ]: 20 : if (path_len > UNIX_PATH_MAX)
288 : 0 : path_len = UNIX_PATH_MAX;
289 : 20 : memcpy(path, RTA_DATA(attr), path_len);
290 : 20 : path[path_len] = '\0';
291 : : }
292 : 20 : break;
293 : : case UNIX_DIAG_PEER:
294 [ + - ]: 20 : if (RTA_PAYLOAD(attr) >= 4)
295 : 20 : peer = *(uint32_t *) RTA_DATA(attr);
296 : 20 : break;
297 : : }
298 : : }
299 : :
300 : : /*
301 : : * print obtained information in the following format:
302 : : * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]"
303 : : */
304 [ + + ][ + + ]: 60 : if (!peer && !path_len)
305 : 35 : return -1;
306 : :
307 : : char peer_str[3 + sizeof(peer) * 3];
308 [ + + ]: 25 : if (peer)
309 : 20 : snprintf(peer_str, sizeof(peer_str), "->%u", peer);
310 : : else
311 : 5 : peer_str[0] = '\0';
312 : :
313 : : const char *path_str;
314 [ + + ]: 25 : if (path_len) {
315 : 20 : char *outstr = alloca(4 * path_len + 4);
316 : :
317 : 20 : outstr[0] = ',';
318 [ + + ]: 20 : if (path[0] == '\0') {
319 : 5 : outstr[1] = '@';
320 : 5 : string_quote(path + 1, outstr + 2,
321 : : path_len - 1, QUOTE_0_TERMINATED);
322 : : } else {
323 : 15 : string_quote(path, outstr + 1,
324 : : path_len, QUOTE_0_TERMINATED);
325 : : }
326 : 20 : path_str = outstr;
327 : : } else {
328 : 5 : path_str = "";
329 : : }
330 : :
331 : : char *details;
332 [ - + ]: 25 : if (asprintf(&details, "%s:[%lu%s%s]", proto_name, inode,
333 : : peer_str, path_str) < 0)
334 : 0 : return -1;
335 : :
336 : 2724 : return cache_and_print_inode_details(inode, details);
337 : : }
338 : :
339 : : static bool
340 : 15 : netlink_send_query(const int fd, const unsigned long inode)
341 : : {
342 : : struct {
343 : : const struct nlmsghdr nlh;
344 : : const struct netlink_diag_req ndr;
345 : 15 : } req = {
346 : : .nlh = {
347 : : .nlmsg_len = sizeof(req),
348 : : .nlmsg_type = SOCK_DIAG_BY_FAMILY,
349 : : .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
350 : : },
351 : : .ndr = {
352 : : .sdiag_family = AF_NETLINK,
353 : : .sdiag_protocol = NDIAG_PROTO_ALL,
354 : : .ndiag_show = NDIAG_SHOW_MEMINFO
355 : : }
356 : : };
357 : 15 : return send_query(fd, &req, sizeof(req));
358 : : }
359 : :
360 : : static int
361 : 460 : netlink_parse_response(const char *proto_name, const void *data,
362 : : const int data_len, const unsigned long inode)
363 : : {
364 : 460 : const struct netlink_diag_msg *const diag_msg = data;
365 : : const char *netlink_proto;
366 : : char *details;
367 : :
368 [ - + ]: 460 : if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg)))
369 : 0 : return -1;
370 [ + + ]: 460 : if (diag_msg->ndiag_ino != inode)
371 : 455 : return 0;
372 : :
373 [ - + ]: 5 : if (diag_msg->ndiag_family != AF_NETLINK)
374 : 0 : return -1;
375 : :
376 : 5 : netlink_proto = xlookup(netlink_protocols,
377 : 5 : diag_msg->ndiag_protocol);
378 : :
379 [ + - ]: 5 : if (netlink_proto) {
380 : : static const char netlink_prefix[] = "NETLINK_";
381 : 5 : const size_t netlink_prefix_len =
382 : : sizeof(netlink_prefix) -1;
383 [ + - ]: 5 : if (strncmp(netlink_proto, netlink_prefix,
384 : : netlink_prefix_len) == 0)
385 : 5 : netlink_proto += netlink_prefix_len;
386 [ - + ]: 5 : if (asprintf(&details, "%s:[%s:%u]", proto_name,
387 : : netlink_proto, diag_msg->ndiag_portid) < 0)
388 : 5 : return -1;
389 : : } else {
390 [ # # ]: 0 : if (asprintf(&details, "%s:[%u]", proto_name,
391 : 0 : (unsigned) diag_msg->ndiag_protocol) < 0)
392 : 0 : return -1;
393 : : }
394 : :
395 : 460 : return cache_and_print_inode_details(inode, details);
396 : : }
397 : :
398 : : static bool
399 : 60 : unix_print(const int fd, const unsigned long inode)
400 : : {
401 : 60 : return unix_send_query(fd, inode)
402 [ + - ][ + + ]: 60 : && receive_responses(fd, inode, "UNIX", unix_parse_response);
403 : : }
404 : :
405 : : static bool
406 : 40 : tcp_v4_print(const int fd, const unsigned long inode)
407 : : {
408 : 40 : return inet_print(fd, AF_INET, IPPROTO_TCP, inode, "TCP");
409 : : }
410 : :
411 : : static bool
412 : 0 : udp_v4_print(const int fd, const unsigned long inode)
413 : : {
414 : 0 : return inet_print(fd, AF_INET, IPPROTO_UDP, inode, "UDP");
415 : : }
416 : :
417 : : static bool
418 : 0 : tcp_v6_print(const int fd, const unsigned long inode)
419 : : {
420 : 0 : return inet_print(fd, AF_INET6, IPPROTO_TCP, inode, "TCPv6");
421 : : }
422 : :
423 : : static bool
424 : 0 : udp_v6_print(const int fd, const unsigned long inode)
425 : : {
426 : 0 : return inet_print(fd, AF_INET6, IPPROTO_UDP, inode, "UDPv6");
427 : : }
428 : :
429 : : static bool
430 : 15 : netlink_print(const int fd, const unsigned long inode)
431 : : {
432 : 15 : return netlink_send_query(fd, inode)
433 [ + - ][ + + ]: 15 : && receive_responses(fd, inode, "NETLINK",
434 : : netlink_parse_response);
435 : : }
436 : :
437 : : static const struct {
438 : : const char *const name;
439 : : bool (*const print)(int, unsigned long);
440 : : } protocols[] = {
441 : : [SOCK_PROTO_UNIX] = { "UNIX", unix_print },
442 : : [SOCK_PROTO_TCP] = { "TCP", tcp_v4_print },
443 : : [SOCK_PROTO_UDP] = { "UDP", udp_v4_print },
444 : : [SOCK_PROTO_TCPv6] = { "TCPv6", tcp_v6_print },
445 : : [SOCK_PROTO_UDPv6] = { "UDPv6", udp_v6_print },
446 : : [SOCK_PROTO_NETLINK] = { "NETLINK", netlink_print }
447 : : };
448 : :
449 : : enum sock_proto
450 : 279 : get_proto_by_name(const char *const name)
451 : : {
452 : : unsigned int i;
453 [ + - ]: 754 : for (i = (unsigned int) SOCK_PROTO_UNKNOWN + 1;
454 : 475 : i < ARRAY_SIZE(protocols); ++i) {
455 [ + - ][ + + ]: 754 : if (protocols[i].name && !strcmp(name, protocols[i].name))
456 : 279 : return (enum sock_proto) i;
457 : : }
458 : 0 : return SOCK_PROTO_UNKNOWN;
459 : : }
460 : :
461 : : /* Given an inode number of a socket, print out the details
462 : : * of the ip address and port. */
463 : :
464 : : bool
465 : 115 : print_sockaddr_by_inode(const unsigned long inode, const enum sock_proto proto)
466 : : {
467 [ + - ][ + - ]: 115 : if ((unsigned int) proto >= ARRAY_SIZE(protocols) ||
468 [ - + ]: 115 : (proto != SOCK_PROTO_UNKNOWN && !protocols[proto].print))
469 : 0 : return false;
470 : :
471 : 115 : const int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
472 [ - + ]: 115 : if (fd < 0)
473 : 0 : return false;
474 : 115 : bool r = false;
475 : :
476 [ + - ]: 115 : if (proto != SOCK_PROTO_UNKNOWN) {
477 : 115 : r = protocols[proto].print(fd, inode);
478 [ + + ]: 115 : if (!r) {
479 : 70 : tprintf("%s:[%lu]", protocols[proto].name, inode);
480 : 115 : r = true;
481 : : }
482 : : } else {
483 : : unsigned int i;
484 [ # # ]: 0 : for (i = (unsigned int) SOCK_PROTO_UNKNOWN + 1;
485 : 0 : i < ARRAY_SIZE(protocols); ++i) {
486 [ # # ]: 0 : if (!protocols[i].print)
487 : 0 : continue;
488 : 0 : r = protocols[i].print(fd, inode);
489 [ # # ]: 0 : if (r)
490 : 0 : break;
491 : : }
492 : : }
493 : :
494 : 115 : close(fd);
495 : 115 : return r;
496 : : }
|