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