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