]> granicus.if.org Git - strace/blob - socketutils.c
Support unix domain sockets in -yy option
[strace] / socketutils.c
1 #include "defs.h"
2 #include <netinet/in.h>
3 #include <sys/socket.h>
4 #include <arpa/inet.h>
5 #include <linux/netlink.h>
6 #include <linux/sock_diag.h>
7 #include <linux/inet_diag.h>
8 #include <linux/unix_diag.h>
9 #include <linux/rtnetlink.h>
10
11 #include <sys/un.h>
12 #ifndef UNIX_PATH_MAX
13 # define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) 0)->sun_path)
14 #endif
15
16 static bool
17 inet_send_query(const int fd, const int family, const int proto)
18 {
19         struct sockaddr_nl nladdr = {
20                 .nl_family = AF_NETLINK
21         };
22         struct {
23                 struct nlmsghdr nlh;
24                 struct inet_diag_req_v2 idr;
25         } req = {
26                 .nlh = {
27                         .nlmsg_len = sizeof(req),
28                         .nlmsg_type = SOCK_DIAG_BY_FAMILY,
29                         .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
30                 },
31                 .idr = {
32                         .sdiag_family = family,
33                         .sdiag_protocol = proto,
34                         .idiag_states = -1
35                 }
36         };
37         struct iovec iov = {
38                 .iov_base = &req,
39                 .iov_len = sizeof(req)
40         };
41         struct msghdr msg = {
42                 .msg_name = (void*)&nladdr,
43                 .msg_namelen = sizeof(nladdr),
44                 .msg_iov = &iov,
45                 .msg_iovlen = 1
46         };
47
48         for (;;) {
49                 if (sendmsg(fd, &msg, 0) < 0) {
50                         if (errno == EINTR)
51                                 continue;
52                         return false;
53                 }
54                 return true;
55         }
56 }
57
58 static bool
59 inet_parse_response(const void *data, int data_len, const unsigned long inode)
60 {
61         const struct inet_diag_msg *diag_msg = data;
62         static const char zero_addr[sizeof(struct in6_addr)];
63         socklen_t addr_size, text_size;
64
65         if (diag_msg->idiag_inode != inode)
66                 return false;
67
68         switch(diag_msg->idiag_family) {
69                 case AF_INET:
70                         addr_size = sizeof(struct in_addr);
71                         text_size = INET_ADDRSTRLEN;
72                         break;
73                 case AF_INET6:
74                         addr_size = sizeof(struct in6_addr);
75                         text_size = INET6_ADDRSTRLEN;
76                         break;
77                 default:
78                         return false;
79         }
80
81         char src_buf[text_size];
82
83         if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
84                        src_buf, text_size))
85                 return false;
86
87         if (diag_msg->id.idiag_dport ||
88             memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
89                 char dst_buf[text_size];
90
91                 if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
92                                dst_buf, text_size))
93                         return false;
94
95                 tprintf("%s:%u->%s:%u",
96                         src_buf, ntohs(diag_msg->id.idiag_sport),
97                         dst_buf, ntohs(diag_msg->id.idiag_dport));
98         } else {
99                 tprintf("%s:%u", src_buf, ntohs(diag_msg->id.idiag_sport));
100         }
101
102         return true;
103 }
104
105 static bool
106 receive_responses(const int fd, const unsigned long inode,
107                   bool (* parser) (const void*, int, const unsigned long))
108 {
109         static char buf[8192];
110         struct sockaddr_nl nladdr = {
111                 .nl_family = AF_NETLINK
112         };
113         struct iovec iov = {
114                 .iov_base = buf,
115                 .iov_len = sizeof(buf)
116         };
117
118         for (;;) {
119                 ssize_t ret;
120                 struct nlmsghdr *h;
121                 struct msghdr msg = {
122                         .msg_name = (void*)&nladdr,
123                         .msg_namelen = sizeof(nladdr),
124                         .msg_iov = &iov,
125                         .msg_iovlen = 1
126                 };
127
128                 ret = recvmsg(fd, &msg, 0);
129                 if (ret < 0) {
130                         if (errno == EINTR)
131                                 continue;
132                         return false;
133                 }
134                 if (!ret)
135                         return false;
136                 for (h = (struct nlmsghdr*)buf;
137                      NLMSG_OK(h, ret);
138                      h = NLMSG_NEXT(h, ret)) {
139                         switch (h->nlmsg_type) {
140                                 case NLMSG_DONE:
141                                 case NLMSG_ERROR:
142                                         return false;
143                         }
144                         if (parser(NLMSG_DATA(h), h->nlmsg_len, inode))
145                                 return true;
146                 }
147         }
148 }
149
150 static bool
151 inet_print(int fd, int family, int protocol, const unsigned long inode)
152 {
153         return inet_send_query(fd, family, protocol)
154                 && receive_responses(fd, inode, inet_parse_response);
155 }
156
157 static bool
158 unix_send_query(const int fd, const unsigned long inode)
159 {
160         struct sockaddr_nl nladdr = {
161                 .nl_family = AF_NETLINK
162         };
163         struct {
164                 struct nlmsghdr nlh;
165                 struct unix_diag_req udr;
166         } req = {
167                 .nlh = {
168                         .nlmsg_len = sizeof(req),
169                         .nlmsg_type = SOCK_DIAG_BY_FAMILY,
170                         .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
171                 },
172                 .udr = {
173                         .sdiag_family = AF_UNIX,
174                         .udiag_ino = inode,
175                         .udiag_states = -1,
176                         .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
177                 }
178         };
179         struct iovec iov = {
180                 .iov_base = &req,
181                 .iov_len = sizeof(req)
182         };
183         struct msghdr msg = {
184                 .msg_name = (void*)&nladdr,
185                 .msg_namelen = sizeof(nladdr),
186                 .msg_iov = &iov,
187                 .msg_iovlen = 1
188         };
189
190         for (;;) {
191                 if (sendmsg(fd, &msg, 0) < 0) {
192                         if (errno == EINTR)
193                                 continue;
194                         return false;
195                 }
196                 return true;
197         }
198 }
199
200 static bool
201 unix_parse_response(const void *data, int data_len, const unsigned long inode)
202 {
203         const struct unix_diag_msg *diag_msg = data;
204         struct rtattr *attr;
205         int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg));
206         uint32_t peer = 0;
207         size_t path_len = 0;
208         char path[UNIX_PATH_MAX + 1];
209
210         if (diag_msg->udiag_ino != inode)
211                 return false;
212         if (diag_msg->udiag_family != AF_UNIX)
213                 return false;
214
215         for (attr = (struct rtattr *) (diag_msg + 1);
216              RTA_OK(attr, rta_len);
217              attr = RTA_NEXT(attr, rta_len)) {
218                 switch (attr->rta_type) {
219                 case UNIX_DIAG_NAME:
220                         if (!path_len) {
221                                 path_len = RTA_PAYLOAD(attr);
222                                 if (path_len > UNIX_PATH_MAX)
223                                         path_len = UNIX_PATH_MAX;
224                                 memcpy(path, RTA_DATA(attr), path_len);
225                                 path[path_len] = '\0';
226                         }
227                         break;
228                 case UNIX_DIAG_PEER:
229                         if (RTA_PAYLOAD(attr) >= 4)
230                                 peer = *(uint32_t *)RTA_DATA(attr);
231                         break;
232                 }
233         }
234
235         /*
236          * print obtained information in the following format:
237          * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]"
238          */
239         if (peer || path_len) {
240                 tprintf("UNIX:[%lu", inode);
241                 if (peer)
242                         tprintf("->%u", peer);
243                 if (path_len) {
244                         if (path[0] == '\0') {
245                                 char *outstr = alloca(4 * path_len - 1);
246                                 string_quote(path + 1, outstr, -1, path_len);
247                                 tprintf(",@%s", outstr);
248                         } else {
249                                 char *outstr = alloca(4 * path_len + 3);
250                                 string_quote(path, outstr, -1, path_len + 1);
251                                 tprintf(",%s", outstr);
252                         }
253                 }
254                 tprints("]");
255                 return true;
256         }
257         else
258                 return false;
259 }
260
261 static bool
262 unix_print(int fd, const unsigned long inode)
263 {
264         return unix_send_query(fd, inode)
265                 && receive_responses(fd, inode, unix_parse_response);
266 }
267
268 /* Given an inode number of a socket, print out the details
269  * of the ip address and port. */
270 bool
271 print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
272 {
273         int fd;
274         bool r = false;
275
276         fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
277         if (fd < 0)
278                 return false;
279
280         if (proto_name) {
281                 if (strcmp(proto_name, "TCP") == 0)
282                         r = inet_print(fd, AF_INET, IPPROTO_TCP, inode);
283                 else if (strcmp(proto_name, "UDP") == 0)
284                         r = inet_print(fd, AF_INET, IPPROTO_UDP, inode);
285                 else if (strcmp(proto_name, "TCPv6") == 0)
286                         r = inet_print(fd, AF_INET6, IPPROTO_TCP, inode);
287                 else if (strcmp(proto_name, "UDPv6") == 0)
288                         r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode);
289                 else if (strcmp(proto_name, "UNIX") == 0)
290                         r = unix_print(fd, inode);
291         } else {
292                 const int families[] = {AF_INET, AF_INET6};
293                 const int protocols[] = {IPPROTO_TCP, IPPROTO_UDP};
294                 const size_t flen = ARRAY_SIZE(families);
295                 const size_t plen = ARRAY_SIZE(protocols);
296                 size_t fi, pi;
297
298                 for (fi = 0; fi < flen; ++fi) {
299                         for (pi = 0; pi < plen; ++pi) {
300                                 if ((r = inet_print(fd, families[fi], protocols[pi], inode)))
301                                         goto out;
302                         }
303                 }
304         }
305 out:
306         close(fd);
307         return r;
308 }