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
inet_send_query(const int fd,const int family,const int proto)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
inet_parse_response(const char * proto_name,const void * data,int data_len,const unsigned long inode)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
receive_responses(const int fd,const unsigned long inode,const char * proto_name,bool (* parser)(const char *,const void *,int,const unsigned long))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
inet_print(const int fd,const int family,const int protocol,const unsigned long inode,const char * proto_name)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
unix_send_query(const int fd,const unsigned long inode)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
unix_parse_response(const char * proto_name,const void * data,int data_len,const unsigned long inode)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 (diag_msg->udiag_ino != inode)
249 		return false;
250 	if (diag_msg->udiag_family != AF_UNIX)
251 		return false;
252 
253 	for (attr = (struct rtattr *) (diag_msg + 1);
254 	     RTA_OK(attr, rta_len);
255 	     attr = RTA_NEXT(attr, rta_len)) {
256 		switch (attr->rta_type) {
257 		case UNIX_DIAG_NAME:
258 			if (!path_len) {
259 				path_len = RTA_PAYLOAD(attr);
260 				if (path_len > UNIX_PATH_MAX)
261 					path_len = UNIX_PATH_MAX;
262 				memcpy(path, RTA_DATA(attr), path_len);
263 				path[path_len] = '\0';
264 			}
265 			break;
266 		case UNIX_DIAG_PEER:
267 			if (RTA_PAYLOAD(attr) >= 4)
268 				peer = *(uint32_t *)RTA_DATA(attr);
269 			break;
270 		}
271 	}
272 
273 	/*
274 	 * print obtained information in the following format:
275 	 * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]"
276 	 */
277 	if (peer || path_len) {
278 		tprintf("%s:[%lu", proto_name, inode);
279 		if (peer)
280 			tprintf("->%u", peer);
281 		if (path_len) {
282 			if (path[0] == '\0') {
283 				tprints(",@");
284 				print_quoted_string(path + 1, path_len,
285 						    QUOTE_0_TERMINATED);
286 			} else {
287 				tprints(",");
288 				print_quoted_string(path, path_len + 1,
289 						    QUOTE_0_TERMINATED);
290 			}
291 		}
292 		tprints("]");
293 		return true;
294 	}
295 	else
296 		return false;
297 }
298 
299 static bool
unix_print(int fd,const unsigned long inode)300 unix_print(int fd, const unsigned long inode)
301 {
302 	return unix_send_query(fd, inode)
303 		&& receive_responses(fd, inode, "UNIX", unix_parse_response);
304 }
305 
306 /* Given an inode number of a socket, print out the details
307  * of the ip address and port. */
308 bool
print_sockaddr_by_inode(const unsigned long inode,const char * proto_name)309 print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
310 {
311 	int fd;
312 	bool r = false;
313 
314 	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
315 	if (fd < 0)
316 		return false;
317 
318 	if (proto_name) {
319 		if (strcmp(proto_name, "TCP") == 0)
320 			r = inet_print(fd, AF_INET, IPPROTO_TCP, inode, "TCP");
321 		else if (strcmp(proto_name, "UDP") == 0)
322 			r = inet_print(fd, AF_INET, IPPROTO_UDP, inode, "UDP");
323 		else if (strcmp(proto_name, "TCPv6") == 0)
324 			r = inet_print(fd, AF_INET6, IPPROTO_TCP, inode, "TCPv6");
325 		else if (strcmp(proto_name, "UDPv6") == 0)
326 			r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode, "UDPv6");
327 		else if (strcmp(proto_name, "UNIX") == 0)
328 			r = unix_print(fd, inode);
329 	} else {
330 		const struct {
331 			const int family;
332 			const int protocol;
333 			const char *name;
334 		} protocols[] = {
335 			{ AF_INET, IPPROTO_TCP, "TCP" },
336 			{ AF_INET, IPPROTO_UDP, "UDP" },
337 			{ AF_INET6, IPPROTO_TCP, "TCPv6" },
338 			{ AF_INET6, IPPROTO_UDP, "UDPv6" }
339 		};
340 		size_t i;
341 
342 		for (i = 0; i < ARRAY_SIZE(protocols); ++i) {
343 			if ((r = inet_print(fd, protocols[i].family,
344 					    protocols[i].protocol, inode,
345 					    protocols[i].name)))
346 				break;
347 		}
348 	}
349 
350 	close(fd);
351 	return r;
352 }
353