pidentd (3.0.19.ds1-5) src/k_linux.c

Summary

 src/k_linux.c |  110 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 97 insertions(+), 13 deletions(-)

    
download this patch

Patch contents

--- pidentd-3.0.19.ds1.orig/src/k_linux.c
+++ pidentd-3.0.19.ds1/src/k_linux.c
@@ -17,15 +17,28 @@
 #include <stdio.h>
 #include <syslog.h>
 #include <errno.h>
+#include <unistd.h>
 
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <arpa/inet.h>
 
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/inet_diag.h>
+
 #include "pidentd.h"
 
+struct kainfo
+{
+    int nlfd;
+    __u32 seq;
+    FILE *proc_net_tcp;
+};
+
 /*
 ** Make sure we are running on a supported OS version
 */
@@ -39,23 +52,85 @@
 int
 ka_open(void **misc)
 {
-    FILE *fp;
+    struct kainfo *kp;
+
+    kp = s_malloc(sizeof(*kp));
 
+    kp->seq = 0;
+    kp->nlfd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_INET_DIAG);
+    if (kp->nlfd >= 0)
+	goto out;
     
-    while ((fp = fopen("/proc/net/tcp", "r")) == NULL && errno == EINTR)
-	;
+    syslog(LOG_INFO, "netlink failed, fallback to /proc/net/tcp: %m");
+    kp->proc_net_tcp = fopen("/proc/net/tcp", "r");
     
-    if (fp == NULL)
+    if (kp->proc_net_tcp == NULL)
     {
 	syslog(LOG_ERR, "fopen(\"/proc/net/tcp\", \"r\"): %m");
 	return -1;
     }
 
-    *misc = (void *) fp;
+out:
+    *misc = (void *) kp;
     return 0;
 }
 
 
+static int
+netlink_lookup(struct kainfo *kip, struct kernel *kp)
+{
+    int status;
+    struct {
+	struct nlmsghdr nlh;
+	union {
+		struct inet_diag_req req;
+		struct inet_diag_msg rsp;
+	} u;
+    } buf;
+    struct sockaddr_nl addr;
+
+    memset(&buf, 0, sizeof(buf));
+    buf.nlh.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(buf.u.req)));
+    buf.nlh.nlmsg_type = TCPDIAG_GETSOCK;
+    buf.nlh.nlmsg_flags = NLM_F_REQUEST;
+    buf.nlh.nlmsg_seq = ++kip->seq;
+    buf.u.req.idiag_family = AF_INET;
+
+    buf.u.req.id.idiag_dport = kp->remote.sin_port;
+    buf.u.req.id.idiag_sport = kp->local.sin_port;
+    buf.u.req.id.idiag_dst[0] = kp->remote.sin_addr.s_addr;
+    buf.u.req.id.idiag_src[0] = kp->local.sin_addr.s_addr;
+    buf.u.req.id.idiag_cookie[0] = INET_DIAG_NOCOOKIE;
+    buf.u.req.id.idiag_cookie[1] = INET_DIAG_NOCOOKIE;
+
+    status = write(kip->nlfd, &buf, buf.nlh.nlmsg_len);
+    if (status < 0) {
+	syslog(LOG_ERR, "netlink_lookup: write failed: %m");
+	return 3;
+    }
+
+    do {
+	socklen_t alen = sizeof(addr);
+	status = recvfrom(kip->nlfd, &buf, sizeof(buf), 0,
+	 			(void *)&addr, &alen);
+	if (status < 0) {
+	    if (errno == ENOBUFS)
+		return -1;
+	    syslog(LOG_ERR, "netlink_lookup: recvfrom failed: %m");
+	    return 3;
+	}
+    } while (addr.nl_pid || buf.nlh.nlmsg_seq != kip->seq);
+
+    if (buf.nlh.nlmsg_type != TCPDIAG_GETSOCK)
+	return 0;
+    if (buf.u.rsp.idiag_state != TCP_ESTABLISHED)
+	return 0;
+
+    kp->euid = buf.u.rsp.idiag_uid;
+    return 1;
+}
+
+
 int 
 ka_lookup(void *vp, struct kernel *kp)
 {
@@ -66,6 +141,9 @@
     int r_lport, r_rport, mylport, myrport;
     int euid;
     int nra;
+    int status;
+    unsigned long ino;
+    struct kainfo *kip;
     
 /*
  * PSz 11 Dec 02
@@ -94,23 +172,28 @@
  * 
  * Should we skip lines with just ino, or both uid and ino, zero?
  */
-    unsigned long int ino;
 
-    
+
+    kip = (struct kainfo *)vp;
+    kp->ruid = NO_UID;
+
+    if (kip->nlfd >= 0)
+	return netlink_lookup(kip, kp);
+
     r_rport = ntohs(kp->remote.sin_port);
     r_lport = ntohs(kp->local.sin_port);
     r_raddr = kp->remote.sin_addr.s_addr;
     r_laddr = kp->local.sin_addr.s_addr;
 
-    fp = (FILE *) vp;
+    fp = kip->proc_net_tcp;
 
-    kp->ruid = NO_UID;
     rewind(fp);
 
     /* eat header */
     if (fgets(buf, sizeof(buf)-1,fp) == NULL)
 	return -1;
 
+    status = 0;
     while (fgets(buf, sizeof(buf)-1, fp) != NULL)
     {
 	nra = sscanf(buf, "%d: %lX:%x %lX:%x %x %lX:%lX %x:%lX %lx %d %ld %lu",
@@ -119,9 +202,10 @@
 		     &euid, &dummy, &ino);
 	if (nra >= 12)
 	{
-	    if (myladdr == r_laddr && mylport == r_lport &&
-		myraddr == r_raddr && myrport == r_rport)
+	    if (myladdr == r_laddr && mylport == r_lport)
 	    {
+		if (myraddr != r_raddr || myrport != r_rport || !ino)
+		    continue;
 		if (nra >= 14 && euid == 0 && ino == 0) {
 		  /*
 		   * Both uid and ino are zero: not even a socket?
@@ -131,11 +215,11 @@
 		  continue;
 		}
 		kp->euid = euid;
-		return 1;
+		status = 1;
 	    }
 	}
     }
 
-    return -1;
+    return status;
 }