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