Home | History | Annotate | Download | only in strace
      1 /*
      2  * Copyright (c) 2014 Zubin Mithra <zubin.mithra (at) gmail.com>
      3  * Copyright (c) 2014-2015 Dmitry V. Levin <ldv (at) 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 (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
    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
    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