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