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