Use the protocol name of a socket as a hint for peer address resolution

To resolve the peer address of socket, all combinations of families
(AF_INET, AF_INET6) and protocols(IPPROTO_TCP, IPPROTO_UDP) were tried.
This change utilizes the protocol name obtained via getxattr to specify
the right combination.

* socketutils.c (inet_print): New helper function.
(print_sockaddr_by_inode): Use it.  Utilize the protocol name
associated with the given inode for resolving the peer socket
address.  If the protocol name is NULL, resolve the address
by trying combinations of families and protocols as before.
* defs.h (print_sockaddr_by_inode): Update prototype.
* util.c (printfd): Pass the protocol name associated with
the given path to print_sockaddr_by_inode as the 2nd argument.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Dmitry V. Levin <ldv@altlinux.org>
diff --git a/socketutils.c b/socketutils.c
index 80abed5..f57c304 100644
--- a/socketutils.c
+++ b/socketutils.c
@@ -138,33 +138,49 @@
 	}
 }
 
+static bool
+inet_print(int fd, int family, int protocol, const unsigned long inode)
+{
+	return send_query(fd, family, protocol)
+		&& receive_responses(fd, inode);
+}
+
 /* Given an inode number of a socket, print out the details
  * of the ip address and port. */
 bool
-print_sockaddr_by_inode(const unsigned long inode)
+print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
 {
-	const int families[] = {AF_INET, AF_INET6};
-	const int protocols[] = {IPPROTO_TCP, IPPROTO_UDP};
-	const size_t flen = ARRAY_SIZE(families);
-	const size_t plen = ARRAY_SIZE(protocols);
-	size_t fi, pi;
 	int fd;
+	bool r = false;
 
 	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG);
 	if (fd < 0)
 		return false;
 
-	for (fi = 0; fi < flen; ++fi) {
-		for (pi = 0; pi < plen; ++pi) {
-			if (!send_query(fd, families[fi], protocols[pi]))
-				continue;
-			if (receive_responses(fd, inode)) {
-				close(fd);
-				return true;
+	if (proto_name) {
+		if (strcmp(proto_name, "TCP") == 0)
+			r = inet_print(fd, AF_INET, IPPROTO_TCP, inode);
+		else if (strcmp(proto_name, "UDP") == 0)
+			r = inet_print(fd, AF_INET, IPPROTO_UDP, inode);
+		else if (strcmp(proto_name, "TCPv6") == 0)
+			r = inet_print(fd, AF_INET6, IPPROTO_TCP, inode);
+		else if (strcmp(proto_name, "UDPv6") == 0)
+			r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode);
+	} else {
+		const int families[] = {AF_INET, AF_INET6};
+		const int protocols[] = {IPPROTO_TCP, IPPROTO_UDP};
+		const size_t flen = ARRAY_SIZE(families);
+		const size_t plen = ARRAY_SIZE(protocols);
+		size_t fi, pi;
+
+		for (fi = 0; fi < flen; ++fi) {
+			for (pi = 0; pi < plen; ++pi) {
+				if ((r = inet_print(fd, families[fi], protocols[pi], inode)))
+					goto out;
 			}
 		}
 	}
-
+out:
 	close(fd);
-	return false;
+	return r;
 }