static int netlink_parse_response(const void *data, const int data_len, const unsigned long inode, void *opaque_data) { const char *proto_name = opaque_data; const struct netlink_diag_msg *const diag_msg = data; const char *netlink_proto; char *details; if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg))) return -1; if (diag_msg->ndiag_ino != inode) return 0; if (diag_msg->ndiag_family != AF_NETLINK) return -1; netlink_proto = xlookup(netlink_protocols, diag_msg->ndiag_protocol); if (netlink_proto) { netlink_proto = STR_STRIP_PREFIX(netlink_proto, "NETLINK_"); if (asprintf(&details, "%s:[%s:%u]", proto_name, netlink_proto, diag_msg->ndiag_portid) < 0) return -1; } else { if (asprintf(&details, "%s:[%u]", proto_name, (unsigned) diag_msg->ndiag_protocol) < 0) return -1; } return cache_inode_details(inode, details); }
static int inet_parse_response(const void *const data, const int data_len, const unsigned long inode, void *opaque_data) { const char *const proto_name = opaque_data; const struct inet_diag_msg *const diag_msg = data; static const char zero_addr[sizeof(struct in6_addr)]; socklen_t addr_size, text_size; if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg))) return -1; if (diag_msg->idiag_inode != inode) return 0; switch (diag_msg->idiag_family) { case AF_INET: addr_size = sizeof(struct in_addr); text_size = INET_ADDRSTRLEN; break; case AF_INET6: addr_size = sizeof(struct in6_addr); text_size = INET6_ADDRSTRLEN; break; default: return -1; } char src_buf[text_size]; char *details; /* open/closing brackets for IPv6 addresses */ const char *ob = diag_msg->idiag_family == AF_INET6 ? "[" : ""; const char *cb = diag_msg->idiag_family == AF_INET6 ? "]" : ""; if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src, src_buf, text_size)) return -1; if (diag_msg->id.idiag_dport || memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) { char dst_buf[text_size]; if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst, dst_buf, text_size)) return -1; if (asprintf(&details, "%s:[%s%s%s:%u->%s%s%s:%u]", proto_name, ob, src_buf, cb, ntohs(diag_msg->id.idiag_sport), ob, dst_buf, cb, ntohs(diag_msg->id.idiag_dport)) < 0) return false; } else { if (asprintf(&details, "%s:[%s%s%s:%u]", proto_name, ob, src_buf, cb, ntohs(diag_msg->id.idiag_sport)) < 0) return false; } return cache_inode_details(inode, details); }
static bool receive_responses(struct tcb *tcp, const int fd, const unsigned long inode, const unsigned long expected_msg_type, int (*parser)(const void *, int, unsigned long, void *), void *opaque_data) { static union { struct nlmsghdr hdr; long buf[8192 / sizeof(long)]; } hdr_buf; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct iovec iov = { .iov_base = hdr_buf.buf, .iov_len = sizeof(hdr_buf.buf) }; int flags = 0; for (;;) { struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1 }; ssize_t ret = recvmsg(fd, &msg, flags); if (ret < 0) { if (errno == EINTR) continue; return false; } const struct nlmsghdr *h = &hdr_buf.hdr; if (!NLMSG_OK(h, ret)) return false; for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) { if (h->nlmsg_type != expected_msg_type) return false; const int rc = parser(NLMSG_DATA(h), h->nlmsg_len, inode, opaque_data); if (rc > 0) return true; if (rc < 0) return false; } flags = MSG_DONTWAIT; } } static bool unix_send_query(struct tcb *tcp, const int fd, const unsigned long inode) { struct { const struct nlmsghdr nlh; const struct unix_diag_req udr; } req = { .nlh = { .nlmsg_len = sizeof(req), .nlmsg_type = SOCK_DIAG_BY_FAMILY, .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST }, .udr = { .sdiag_family = AF_UNIX, .udiag_ino = inode, .udiag_states = -1, .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER } }; return send_query(tcp, fd, &req, sizeof(req)); } static int unix_parse_response(const void *data, const int data_len, const unsigned long inode, void *opaque_data) { const char *proto_name = opaque_data; const struct unix_diag_msg *diag_msg = data; struct rtattr *attr; int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg)); uint32_t peer = 0; size_t path_len = 0; char path[UNIX_PATH_MAX + 1]; if (rta_len < 0) return -1; if (diag_msg->udiag_ino != inode) return 0; if (diag_msg->udiag_family != AF_UNIX) return -1; for (attr = (struct rtattr *) (diag_msg + 1); RTA_OK(attr, rta_len); attr = RTA_NEXT(attr, rta_len)) { switch (attr->rta_type) { case UNIX_DIAG_NAME: if (!path_len) { path_len = RTA_PAYLOAD(attr); if (path_len > UNIX_PATH_MAX) path_len = UNIX_PATH_MAX; memcpy(path, RTA_DATA(attr), path_len); path[path_len] = '\0'; } break; case UNIX_DIAG_PEER: if (RTA_PAYLOAD(attr) >= 4) peer = *(uint32_t *) RTA_DATA(attr); break; } } /* * print obtained information in the following format: * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]" */ if (!peer && !path_len) return -1; char peer_str[3 + sizeof(peer) * 3]; if (peer) xsprintf(peer_str, "->%u", peer); else peer_str[0] = '\0'; const char *path_str; if (path_len) { char *outstr = alloca(4 * path_len + 4); outstr[0] = ','; if (path[0] == '\0') { outstr[1] = '@'; string_quote(path + 1, outstr + 2, path_len - 1, QUOTE_0_TERMINATED); } else { string_quote(path, outstr + 1, path_len, QUOTE_0_TERMINATED); } path_str = outstr; } else { path_str = ""; } char *details; if (asprintf(&details, "%s:[%lu%s%s]", proto_name, inode, peer_str, path_str) < 0) return -1; return cache_inode_details(inode, details); } static bool netlink_send_query(struct tcb *tcp, const int fd, const unsigned long inode) { struct { const struct nlmsghdr nlh; const struct netlink_diag_req ndr; } req = { .nlh = { .nlmsg_len = sizeof(req), .nlmsg_type = SOCK_DIAG_BY_FAMILY, .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST }, .ndr = { .sdiag_family = AF_NETLINK, .sdiag_protocol = NDIAG_PROTO_ALL, .ndiag_show = NDIAG_SHOW_MEMINFO } }; return send_query(tcp, fd, &req, sizeof(req)); }