]> granicus.if.org Git - strace/blob - socketutils.c
inet_parse_response: add a safety check
[strace] / socketutils.c
1 /*
2  * Copyright (c) 2014 Zubin Mithra <zubin.mithra@gmail.com>
3  * Copyright (c) 2014-2015 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/rtnetlink.h>
38
39 #if !defined NETLINK_SOCK_DIAG && defined NETLINK_INET_DIAG
40 # define NETLINK_SOCK_DIAG NETLINK_INET_DIAG
41 #endif
42
43 #include <sys/un.h>
44 #ifndef UNIX_PATH_MAX
45 # define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) 0)->sun_path)
46 #endif
47
48 static bool
49 inet_send_query(const int fd, const int family, const int proto)
50 {
51         struct sockaddr_nl nladdr = {
52                 .nl_family = AF_NETLINK
53         };
54         struct {
55                 struct nlmsghdr nlh;
56                 struct inet_diag_req_v2 idr;
57         } req = {
58                 .nlh = {
59                         .nlmsg_len = sizeof(req),
60                         .nlmsg_type = SOCK_DIAG_BY_FAMILY,
61                         .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
62                 },
63                 .idr = {
64                         .sdiag_family = family,
65                         .sdiag_protocol = proto,
66                         .idiag_states = -1
67                 }
68         };
69         struct iovec iov = {
70                 .iov_base = &req,
71                 .iov_len = sizeof(req)
72         };
73         struct msghdr msg = {
74                 .msg_name = (void*)&nladdr,
75                 .msg_namelen = sizeof(nladdr),
76                 .msg_iov = &iov,
77                 .msg_iovlen = 1
78         };
79
80         for (;;) {
81                 if (sendmsg(fd, &msg, 0) < 0) {
82                         if (errno == EINTR)
83                                 continue;
84                         return false;
85                 }
86                 return true;
87         }
88 }
89
90 static bool
91 inet_parse_response(const char *proto_name, const void *data, int data_len,
92                     const unsigned long inode)
93 {
94         const struct inet_diag_msg *diag_msg = data;
95         static const char zero_addr[sizeof(struct in6_addr)];
96         socklen_t addr_size, text_size;
97
98         if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg)))
99                 return false;
100         if (diag_msg->idiag_inode != inode)
101                 return false;
102
103         switch(diag_msg->idiag_family) {
104                 case AF_INET:
105                         addr_size = sizeof(struct in_addr);
106                         text_size = INET_ADDRSTRLEN;
107                         break;
108                 case AF_INET6:
109                         addr_size = sizeof(struct in6_addr);
110                         text_size = INET6_ADDRSTRLEN;
111                         break;
112                 default:
113                         return false;
114         }
115
116         char src_buf[text_size];
117
118         if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
119                        src_buf, text_size))
120                 return false;
121
122         if (diag_msg->id.idiag_dport ||
123             memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
124                 char dst_buf[text_size];
125
126                 if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
127                                dst_buf, text_size))
128                         return false;
129
130                 tprintf("%s:[%s:%u->%s:%u]",
131                         proto_name,
132                         src_buf, ntohs(diag_msg->id.idiag_sport),
133                         dst_buf, ntohs(diag_msg->id.idiag_dport));
134         } else {
135                 tprintf("%s:[%s:%u]", proto_name, src_buf,
136                         ntohs(diag_msg->id.idiag_sport));
137         }
138
139         return true;
140 }
141
142 static bool
143 receive_responses(const int fd, const unsigned long inode,
144                   const char *proto_name,
145                   bool (* parser) (const char *, const void *, int, const unsigned long))
146 {
147         static long buf[8192 / sizeof(long)];
148         struct sockaddr_nl nladdr = {
149                 .nl_family = AF_NETLINK
150         };
151         struct iovec iov = {
152                 .iov_base = buf,
153                 .iov_len = sizeof(buf)
154         };
155         int flags = 0;
156
157         for (;;) {
158                 ssize_t ret;
159                 struct nlmsghdr *h;
160                 struct msghdr msg = {
161                         .msg_name = (void*)&nladdr,
162                         .msg_namelen = sizeof(nladdr),
163                         .msg_iov = &iov,
164                         .msg_iovlen = 1
165                 };
166
167                 ret = recvmsg(fd, &msg, flags);
168                 if (ret < 0) {
169                         if (errno == EINTR)
170                                 continue;
171                         return false;
172                 }
173                 if (!ret)
174                         return false;
175                 for (h = (struct nlmsghdr*)buf;
176                      NLMSG_OK(h, ret);
177                      h = NLMSG_NEXT(h, ret)) {
178                         switch (h->nlmsg_type) {
179                                 case NLMSG_DONE:
180                                 case NLMSG_ERROR:
181                                         return false;
182                         }
183                         if (parser(proto_name, NLMSG_DATA(h), h->nlmsg_len, inode))
184                                 return true;
185                 }
186                 flags = MSG_DONTWAIT;
187         }
188 }
189
190 static bool
191 inet_print(const int fd, const int family, const int protocol,
192            const unsigned long inode, const char *proto_name)
193 {
194         return inet_send_query(fd, family, protocol)
195                 && receive_responses(fd, inode, proto_name, inet_parse_response);
196 }
197
198 static bool
199 unix_send_query(const int fd, const unsigned long inode)
200 {
201         struct sockaddr_nl nladdr = {
202                 .nl_family = AF_NETLINK
203         };
204         struct {
205                 struct nlmsghdr nlh;
206                 struct unix_diag_req udr;
207         } req = {
208                 .nlh = {
209                         .nlmsg_len = sizeof(req),
210                         .nlmsg_type = SOCK_DIAG_BY_FAMILY,
211                         .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
212                 },
213                 .udr = {
214                         .sdiag_family = AF_UNIX,
215                         .udiag_ino = inode,
216                         .udiag_states = -1,
217                         .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
218                 }
219         };
220         struct iovec iov = {
221                 .iov_base = &req,
222                 .iov_len = sizeof(req)
223         };
224         struct msghdr msg = {
225                 .msg_name = (void*)&nladdr,
226                 .msg_namelen = sizeof(nladdr),
227                 .msg_iov = &iov,
228                 .msg_iovlen = 1
229         };
230
231         for (;;) {
232                 if (sendmsg(fd, &msg, 0) < 0) {
233                         if (errno == EINTR)
234                                 continue;
235                         return false;
236                 }
237                 return true;
238         }
239 }
240
241 static bool
242 unix_parse_response(const char *proto_name, const void *data, int data_len,
243                     const unsigned long inode)
244 {
245         const struct unix_diag_msg *diag_msg = data;
246         struct rtattr *attr;
247         int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg));
248         uint32_t peer = 0;
249         size_t path_len = 0;
250         char path[UNIX_PATH_MAX + 1];
251
252         if (rta_len < 0)
253                 return false;
254         if (diag_msg->udiag_ino != inode)
255                 return false;
256         if (diag_msg->udiag_family != AF_UNIX)
257                 return false;
258
259         for (attr = (struct rtattr *) (diag_msg + 1);
260              RTA_OK(attr, rta_len);
261              attr = RTA_NEXT(attr, rta_len)) {
262                 switch (attr->rta_type) {
263                 case UNIX_DIAG_NAME:
264                         if (!path_len) {
265                                 path_len = RTA_PAYLOAD(attr);
266                                 if (path_len > UNIX_PATH_MAX)
267                                         path_len = UNIX_PATH_MAX;
268                                 memcpy(path, RTA_DATA(attr), path_len);
269                                 path[path_len] = '\0';
270                         }
271                         break;
272                 case UNIX_DIAG_PEER:
273                         if (RTA_PAYLOAD(attr) >= 4)
274                                 peer = *(uint32_t *)RTA_DATA(attr);
275                         break;
276                 }
277         }
278
279         /*
280          * print obtained information in the following format:
281          * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]"
282          */
283         if (peer || path_len) {
284                 tprintf("%s:[%lu", proto_name, inode);
285                 if (peer)
286                         tprintf("->%u", peer);
287                 if (path_len) {
288                         if (path[0] == '\0') {
289                                 tprints(",@");
290                                 print_quoted_string(path + 1, path_len,
291                                                     QUOTE_0_TERMINATED);
292                         } else {
293                                 tprints(",");
294                                 print_quoted_string(path, path_len + 1,
295                                                     QUOTE_0_TERMINATED);
296                         }
297                 }
298                 tprints("]");
299                 return true;
300         }
301         else
302                 return false;
303 }
304
305 static bool
306 unix_print(int fd, const unsigned long inode)
307 {
308         return unix_send_query(fd, inode)
309                 && receive_responses(fd, inode, "UNIX", unix_parse_response);
310 }
311
312 /* Given an inode number of a socket, print out the details
313  * of the ip address and port. */
314 bool
315 print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
316 {
317         int fd;
318         bool r = false;
319
320         fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
321         if (fd < 0)
322                 return false;
323
324         if (proto_name) {
325                 if (strcmp(proto_name, "TCP") == 0)
326                         r = inet_print(fd, AF_INET, IPPROTO_TCP, inode, "TCP");
327                 else if (strcmp(proto_name, "UDP") == 0)
328                         r = inet_print(fd, AF_INET, IPPROTO_UDP, inode, "UDP");
329                 else if (strcmp(proto_name, "TCPv6") == 0)
330                         r = inet_print(fd, AF_INET6, IPPROTO_TCP, inode, "TCPv6");
331                 else if (strcmp(proto_name, "UDPv6") == 0)
332                         r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode, "UDPv6");
333                 else if (strcmp(proto_name, "UNIX") == 0)
334                         r = unix_print(fd, inode);
335
336                 if (!r) {
337                         tprintf("%s:[%lu]", proto_name, inode);
338                         r = true;
339                 }
340         } else {
341                 const struct {
342                         const int family;
343                         const int protocol;
344                         const char *name;
345                 } protocols[] = {
346                         { AF_INET, IPPROTO_TCP, "TCP" },
347                         { AF_INET, IPPROTO_UDP, "UDP" },
348                         { AF_INET6, IPPROTO_TCP, "TCPv6" },
349                         { AF_INET6, IPPROTO_UDP, "UDPv6" }
350                 };
351                 size_t i;
352
353                 for (i = 0; i < ARRAY_SIZE(protocols); ++i) {
354                         if ((r = inet_print(fd, protocols[i].family,
355                                             protocols[i].protocol, inode,
356                                             protocols[i].name)))
357                                 break;
358                 }
359         }
360
361         close(fd);
362         return r;
363 }