Subject: forward/proxy support for identd(8)
To: None <tech-userlevel@NetBSD.org>
From: Peter Postma <peter@pointless.nl>
List: tech-userlevel
Date: 03/24/2005 16:26:24
--sm4nu43k4a2Rpi4c
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi folks,

I've implemented forward/proxy support in identd(8).

Forward mode (-f filter):

Enables forwarding of ident queries. If an ident lookup fails, then
identd will look if the query was destinated for a machine on the local
network. If that's true then identd will send the query to that machine,
but using the local port on the NATed machine in the query. It then waits
for reply and sends the query back to the requesting host with the
original ports. This port swapping is needed to do a succesful lookup on
the proxy host. The filter argument can be either `pf' or `ipfilter'.
IPv4 and IPv6 are both supported, but only IPv4 is tested and ipfilter
can only do IPv4. There is also small limitation in ipfilters natlookup,
so the patch in PR 29757 should be applied first.


Proxy mode (-P address):

Enables receiving of proxied ident queries. The address argument should
be set to the proxy server. If an ident lookup fails, then identd
will try to find a match if we got it from the proxy server. We don't know
who has sent the request so only the ports (from the modified query) and
the local addresses are matched. If this succeeds then we know the
foreign address and we can lookup the UID for the connection. The code
is a bit complicated and this would be much simpler if we would allow
wildcarded lookups in sys/netinet/in_pcb.c:in_pcblookup_connect, but I'm
not sure if that a good thing to do...


I've tested the patch with pkgsrc/net/oidentd which also supports
forward and proxy mode and my patch is fully compatible it.

Comments are welcome.

-- 
Peter Postma

--sm4nu43k4a2Rpi4c
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="identd.diff"

Index: Makefile
===================================================================
RCS file: /cvsroot/src/libexec/identd/Makefile,v
retrieving revision 1.10
diff -u -r1.10 Makefile
--- Makefile	31 Jan 2004 21:47:17 -0000	1.10
+++ Makefile	24 Mar 2005 12:39:04 -0000
@@ -1,7 +1,22 @@
 # $NetBSD: Makefile,v 1.10 2004/01/31 21:47:17 christos Exp $
 
-WARNS=3
+.include <bsd.own.mk>
+
+WARNS=	4
 PROG=	identd
+SRCS=	identd.c
 MAN=	identd.8
 
+# Build with IP Filter support?
+.if (${MKIPFILTER} != "no")
+SRCS+=	ipf.c
+CPPFLAGS+=-I${NETBSDSRCDIR}/sys/dist/ipf -DWITH_IPF
+.endif
+
+# Build with pf support?
+.if (${MKPF} != "no")
+SRCS+=	pf.c
+CPPFLAGS+=-DWITH_PF
+.endif
+
 .include <bsd.prog.mk>
Index: identd.8
===================================================================
RCS file: /cvsroot/src/libexec/identd/identd.8,v
retrieving revision 1.18
diff -u -r1.18 identd.8
--- identd.8	31 Jan 2004 22:39:24 -0000	1.18
+++ identd.8	24 Mar 2005 12:39:04 -0000
@@ -1,9 +1,9 @@
 .\" $NetBSD: identd.8,v 1.18 2004/01/31 22:39:24 wiz Exp $
 .\"
 .\" This software is in the public domain.
-.\" Written by Peter Postma <peter@pointless.nl>
+.\" Written by Peter Postma <peter@NetBSD.org>
 .\"
-.Dd January 31, 2004
+.Dd March 22, 2005
 .Dt IDENTD 8
 .Os
 .Sh NAME
@@ -18,7 +18,9 @@
 .Op Fl f Ar fallback
 .Op Fl g Ar uid
 .Op Fl L Ar username
+.Op Fl m Ar filter
 .Op Fl o Ar osname
+.Op Fl P Ar address
 .Op Fl p Ar portno
 .Op Fl t Ar seconds
 .Op Fl u Ar uid
@@ -95,7 +97,8 @@
 .Ar fallback
 username.
 If the lookup fails then this username will be returned.
-This can be useful for when running this service on a NAT host.
+This can be useful for when running this service on a NAT host and
+not using the forward/proxy functionality.
 .It Fl g Ar gid
 Specify the group id number or name which the server should switch to after
 binding itself to the TCP/IP port.
@@ -122,6 +125,40 @@
 Use
 .Xr syslogd 8
 for logging.
+.It Fl m Ar filter
+Enables forwarding of ident queries.
+The
+.Ar filter
+argument specifies which packet filter should be used to lookup the
+connections, currently
+.Sq pf
+and
+.Sq ipfilter
+are supported packet filters.
+Note that
+.Nm
+changes the ident queries to use the local port on the NAT host instead of
+the local port on the forwarding host.
+This is needed because otherwise we can't do a lookup on the proxy host.
+On the proxy host,
+.Dq proxy mode
+should be enabled with the
+.Fl P
+flag or
+.Dq lying mode
+with the
+.Fl L
+flag.
+.Nm
+will need access to
+.Pa /etc/pf
+or
+.Pa /etc/ipnat
+so you either need to run
+.Nm
+under root or adjust group owner/permissions to the device(s) and run
+.Nm
+under that group.
 .It Fl N
 Enable
 .Pa .noident
@@ -136,6 +173,12 @@
 .Ar osname
 instead of the default
 .Dq UNIX .
+.It Fl P Ar address
+Specify a proxy server which will be used to receive proxied ident
+queries from.
+See also the
+.Fl m
+flag how this operates.
 .It Fl p Ar portno
 Specify an alternative port number under which the server should run.
 The default is port 113.
Index: identd.c
===================================================================
RCS file: /cvsroot/src/libexec/identd/identd.c,v
retrieving revision 1.25
diff -u -r1.25 identd.c
--- identd.c	11 Mar 2005 15:49:52 -0000	1.25
+++ identd.c	24 Mar 2005 12:39:04 -0000
@@ -4,7 +4,7 @@
  * identd.c - TCP/IP Ident protocol server.
  *
  * This software is in the public domain.
- * Written by Peter Postma <peter@pointless.nl>
+ * Written by Peter Postma <peter@NetBSD.org>
  */
 
 #include <sys/types.h>
@@ -38,6 +38,8 @@
 #include <syslog.h>
 #include <unistd.h>
 
+#include "identd.h"
+
 __RCSID("$NetBSD: identd.c,v 1.25 2005/03/11 15:49:52 peter Exp $");
 
 #define OPSYS_NAME	"UNIX"
@@ -45,12 +47,17 @@
 #define TIMEOUT		30	/* seconds */
 
 static int   idhandle(int, const char *, const char *, const char *,
-		const char *, int);
+		const char *, struct sockaddr *, int);
 static void  idparse(int, int, int, const char *, const char *, const char *);
 static void  iderror(int, int, int, const char *);
-static const char *gethost(struct sockaddr_storage *);
+static const char *gethost(struct sockaddr *);
 static int  *socketsetup(const char *, const char *, int);
+static int   ident_getuid(struct sockaddr_storage *, socklen_t,
+		struct sockaddr *, uid_t *);
 static int   sysctl_getuid(struct sockaddr_storage *, socklen_t, uid_t *);
+static int   sysctl_proxy_getuid(struct sockaddr_storage *,
+		struct sockaddr *, uid_t *);
+static int   forward(int, struct sockaddr *, int, int, int);
 static int   check_noident(const char *);
 static int   check_userident(const char *, char *, size_t);
 static void  random_string(char *, size_t);
@@ -58,18 +65,36 @@
 static void  timeout_handler(int);
 static void  waitchild(int);
 static void  fatal(const char *);
-static void  maybe_syslog(int, const char *, ...);
 
 static int   bflag, eflag, fflag, Fflag, iflag, Iflag;
 static int   lflag, Lflag, nflag, Nflag, rflag;
 
+/* NAT lookup function pointer. */
+static int  (*nat_lookup)(struct sockaddr_storage *, struct sockaddr *, int *);
+
+/* Packet filters. */
+static const struct {
+	const char *name;
+	int (*fn)(struct sockaddr_storage *, struct sockaddr *, int *);
+} filters[] = {
+#ifdef WITH_PF
+	{ "pf", pf_natlookup },
+#endif
+#ifdef WITH_IPF
+	{ "ipfilter", ipf_natlookup },
+#endif
+	{ NULL, NULL }
+};
+
 int
 main(int argc, char *argv[])
 {
-	int IPv4or6, ch, *socks, timeout;
+	int IPv4or6, i, ch, *socks, timeout;
 	char *address, *charset, *fmt, *p;
-	const char *osname, *portno;
+	const char *osname, *portno, *proxy, *filter;
 	char user[LOGIN_NAME_MAX];
+	struct addrinfo *ai, hints;
+	struct sockaddr *proxy_addr;
 	struct group *grp;
 	struct passwd *pw;
 	gid_t gid;
@@ -79,6 +104,9 @@
 	osname = OPSYS_NAME;
 	portno = IDENT_SERVICE;
 	timeout = TIMEOUT;
+	nat_lookup = NULL;
+	proxy_addr = NULL;
+	filter = proxy = NULL;
 	address = charset = fmt = NULL;
 	uid = gid = 0;
 	bflag = eflag = fflag = Fflag = iflag = Iflag = 0;
@@ -89,7 +117,8 @@
 		bflag = 1;
 
 	/* Parse command line arguments. */
-	while ((ch = getopt(argc, argv, "46a:bceF:f:g:IiL:lNno:p:rt:u:")) != -1)
+	while ((ch = getopt(argc, argv,
+	    "46a:bceF:f:g:IiL:lm:Nno:P:p:rt:u:")) != -1) {
 		switch (ch) {
 		case '4':
 			IPv4or6 = AF_INET;
@@ -140,6 +169,9 @@
 		case 'l':
 			lflag = 1;
 			break;
+		case 'm':
+			filter = optarg;
+			break;
 		case 'N':
 			Nflag = 1;
 			break;
@@ -149,6 +181,9 @@
 		case 'o':
 			osname = optarg;
 			break;
+		case 'P':
+			proxy = optarg;
+			break;
 		case 'p':
 			portno = optarg;
 			break;
@@ -175,10 +210,54 @@
 		default:
 			exit(EXIT_FAILURE);
 		}
+	}
 
 	if (lflag)
 		openlog("identd", LOG_PID, LOG_DAEMON);
 
+	/* Verify proxy address, if enabled. */
+	if (proxy != NULL) {
+		(void)memset(&hints, 0, sizeof(hints));
+		hints.ai_family = IPv4or6;
+		hints.ai_socktype = SOCK_STREAM;
+		if (getaddrinfo(proxy, NULL, &hints, &ai) != 0) {
+			const char *msg = "Invalid proxy `%s'";
+
+			maybe_syslog(LOG_ERR, msg, proxy);
+			if (bflag)
+				warnx(msg, proxy);
+			exit(EXIT_FAILURE);
+		}
+		if (ai->ai_next != NULL) {
+			const char *msg = "Invalid proxy `%s', "
+			    "resolves to multiple addresses";
+
+			maybe_syslog(LOG_ERR, msg, proxy);
+			if (bflag)
+				warnx(msg, proxy);
+			exit(EXIT_FAILURE);
+		} else
+			proxy_addr = ai->ai_addr;
+	}
+
+	/* Verify filter, if enabled. */
+	if (filter != NULL) {
+		for (i = 0; filters[i].name != NULL; i++) {
+			if (strcasecmp(filter, filters[i].name) == 0) {
+				nat_lookup = filters[i].fn;
+				break;
+			}
+		}
+		if (nat_lookup == NULL) {
+			const char *msg = "Packet filter `%s' not supported";
+
+			maybe_syslog(LOG_ERR, msg, filter);
+			if (bflag)
+				warnx(msg, filter);
+			exit(EXIT_FAILURE);
+		}
+	}
+
 	/* Setup sockets when running in the background. */
 	if (bflag)
 		socks = socketsetup(address, portno, IPv4or6);
@@ -203,7 +282,7 @@
 	 * let inetd handle the sockets.
 	 */
 	if (bflag) {
-		int fd, i, nfds, rv;
+		int fd, nfds, rv;
 		struct pollfd *rfds;
 
 		(void)signal(SIGCHLD, waitchild);
@@ -243,7 +322,8 @@
 						break;
 					case 0:		/* child */
 						(void)idhandle(fd, charset,
-						    fmt, osname, user, timeout);
+						    fmt, osname, user,
+						    proxy_addr, timeout);
 						_exit(EXIT_SUCCESS);
 					default:	/* parent */
 						(void)close(fd);
@@ -253,14 +333,14 @@
 		}
 	} else
 		(void)idhandle(STDIN_FILENO, charset, fmt, osname, user,
-		    timeout);
+		    proxy_addr, timeout);
 
 	return 0;
 }
 
 static int
 idhandle(int fd, const char *charset, const char *fmt, const char *osname,
-    const char *user, int timeout)
+    const char *user, struct sockaddr *proxy, int timeout)
 {
 	struct sockaddr_storage ss[2];
 	char userbuf[LOGIN_NAME_MAX];	/* actual user name (or numeric uid) */
@@ -282,7 +362,8 @@
 	if (getpeername(fd, (struct sockaddr *)&ss[0], &len) < 0)
 		fatal("getpeername");
 
-	maybe_syslog(LOG_INFO, "Connection from %s", gethost(&ss[0]));
+	maybe_syslog(LOG_INFO, "Connection from %s",
+	    gethost((struct sockaddr *)&ss[0]));
 
 	/* Get local internet address. */
 	len = sizeof(ss[1]);
@@ -318,7 +399,7 @@
 	/* Are the ports valid? */
 	if (lport < 1 || lport > 65535 || fport < 1 || fport > 65535) {
 		maybe_syslog(LOG_NOTICE, "Invalid port(s): %d, %d from %s",
-		    lport, fport, gethost(&ss[0]));
+		    lport, fport, gethost((struct sockaddr *)&ss[0]));
 		iderror(fd, 0, 0, eflag ? "UNKNOWN-ERROR" : "INVALID-PORT");
 		return 1;
 	}
@@ -326,7 +407,7 @@
 	/* If there is a 'lie' user enabled, then handle it now and stop. */
 	if (Lflag) {
 		maybe_syslog(LOG_NOTICE, "Lying with name %s to %s",
-		    idbuf, gethost(&ss[0]));
+		    idbuf, gethost((struct sockaddr *)&ss[0]));
 		idparse(fd, lport, fport, charset, osname, idbuf);
 		return 0;
 	}
@@ -334,12 +415,12 @@
 	/* Protocol dependent stuff. */
 	switch (ss[0].ss_family) {
 	case AF_INET:
-		((struct sockaddr_in *)&ss[0])->sin_port = htons(fport);
-		((struct sockaddr_in *)&ss[1])->sin_port = htons(lport);
+		satosin(&ss[0])->sin_port = htons(fport);
+		satosin(&ss[1])->sin_port = htons(lport);
 		break;
 	case AF_INET6:
-		((struct sockaddr_in6 *)&ss[0])->sin6_port = htons(fport);
-		((struct sockaddr_in6 *)&ss[1])->sin6_port = htons(lport);
+		satosin6(&ss[0])->sin6_port = htons(fport);
+		satosin6(&ss[1])->sin6_port = htons(lport);
 		break;
 	default:
 		maybe_syslog(LOG_ERR, "Unsupported protocol (no. %d)",
@@ -348,14 +429,31 @@
 	}
 
 	/* Try to get the UID of the connection owner using sysctl. */
-	if (sysctl_getuid(ss, sizeof(ss), &uid) == -1) {
-		maybe_syslog(LOG_ERR, "sysctl: %m");
+	if (ident_getuid(ss, sizeof(ss), proxy, &uid) == -1) {
+		/* Lookup failed, try to forward if enabled. */
+		if (nat_lookup != NULL) {
+			struct sockaddr nat_addr;
+			int nat_lport;
+
+			(void)memset(&nat_addr, 0, sizeof(nat_addr));
+
+			if ((*nat_lookup)(ss, &nat_addr, &nat_lport) &&
+			    forward(fd, &nat_addr, nat_lport, fport, lport)) {
+				maybe_syslog(LOG_INFO,
+				    "Succesfully forwarded the request to %s",
+				    gethost(&nat_addr));
+				return 0;
+			}
+		}
+		/* Fall back to a default name? */
 		if (fflag) {
 			maybe_syslog(LOG_NOTICE, "Using fallback name %s to %s",
-			    idbuf, gethost(&ss[0]));
+			    idbuf, gethost((struct sockaddr *)&ss[0]));
 			idparse(fd, lport, fport, charset, osname, idbuf);
 			return 0;
 		}
+		maybe_syslog(LOG_ERR, "Lookup failed, returning error to %s",
+		    gethost((struct sockaddr *)&ss[0]));
 		iderror(fd, lport, fport, eflag ? "UNKNOWN-ERROR" : "NO-USER");
 		return 1;
 	}
@@ -366,14 +464,15 @@
 		(void)snprintf(userbuf, sizeof(userbuf), "%u", uid);
 	} else {
 		maybe_syslog(LOG_INFO, "Successful lookup: %d, %d: %s for %s",
-		    lport, fport, pw->pw_name, gethost(&ss[0]));
+		    lport, fport, pw->pw_name,
+		    gethost((struct sockaddr *)&ss[0]));
 		(void)strlcpy(userbuf, pw->pw_name, sizeof(userbuf));
 	}
 
 	/* No ident enabled? */
 	if (Nflag && pw && check_noident(pw->pw_dir)) {
 		maybe_syslog(LOG_NOTICE, "Returning HIDDEN-USER for user %s"
-		    " to %s", pw->pw_name, gethost(&ss[0]));
+		    " to %s", pw->pw_name, gethost((struct sockaddr *)&ss[0]));
 		iderror(fd, lport, fport, "HIDDEN-USER");
 		return 1;
 	}
@@ -390,8 +489,9 @@
 				(void)strlcpy(idbuf, userbuf, sizeof(idbuf));
 			}
 		}
-		maybe_syslog(LOG_NOTICE, "Returning user-specified '%s' for "
-		    "user %s to %s", idbuf, userbuf, gethost(&ss[0]));
+		maybe_syslog(LOG_NOTICE,
+		    "Returning user-specified '%s' for user %s to %s",
+		    idbuf, userbuf, gethost((struct sockaddr *)&ss[0]));
 		idparse(fd, lport, fport, charset, osname, idbuf);
 		return 0;
 	}
@@ -405,8 +505,9 @@
 		else
 			random_string(idbuf, sizeof(idbuf));
 
-		maybe_syslog(LOG_NOTICE, "Returning random '%s' for user %s"
-		    " to %s", idbuf, userbuf, gethost(&ss[0]));
+		maybe_syslog(LOG_NOTICE,
+		    "Returning random '%s' for user %s to %s",
+		    idbuf, userbuf, gethost((struct sockaddr *)&ss[0]));
 		idparse(fd, lport, fport, charset, osname, idbuf);
 		return 0;
 	}
@@ -463,12 +564,12 @@
 
 /* Return the IP address of the connecting host. */
 static const char *
-gethost(struct sockaddr_storage *ss)
+gethost(struct sockaddr *sa)
 {
 	static char host[NI_MAXHOST];
 
-	if (getnameinfo((struct sockaddr *)ss, ss->ss_len, host,
-	    sizeof(host), NULL, 0, NI_NUMERICHOST) == 0)
+	if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
+	    NULL, 0, NI_NUMERICHOST) == 0)
 		return host;
 
 	return "UNKNOWN";
@@ -539,6 +640,20 @@
 	return socks;
 }
 
+/* UID lookup wrapper. */
+static int
+ident_getuid(struct sockaddr_storage *ss, socklen_t len,
+    struct sockaddr *proxy, uid_t *uid)
+{
+	int rc;
+
+	rc = sysctl_getuid(ss, len, uid);
+	if (rc == -1 && proxy != NULL)
+		rc = sysctl_proxy_getuid(ss, proxy, uid);
+
+	return rc;
+}
+
 /* Try to get the UID of the connection owner using sysctl. */
 static int
 sysctl_getuid(struct sockaddr_storage *ss, socklen_t len, uid_t *uid)
@@ -561,6 +676,190 @@
 	return 0;
 }
 
+/* Try to get the UID of the connection owner using sysctl (proxy version). */
+static int
+sysctl_proxy_getuid(struct sockaddr_storage *ss, struct sockaddr *proxy,
+    uid_t *uid)
+{
+	struct sockaddr_storage new[2];
+	int i, rc, name[CTL_MAXNAME];
+	struct kinfo_pcb *kp;
+	size_t sz, len;
+	const char *list;
+
+	rc = -1;
+	sz = CTL_MAXNAME;
+	list = NULL;
+
+	/* Retrieve a list of sockets. */
+	switch (ss[0].ss_family) {
+	case AF_INET:
+		/* We only accept queries from the proxy. */
+		if (in_hosteq(satosin(&ss[0])->sin_addr,
+		    satosin(proxy)->sin_addr))
+			list = "net.inet.tcp.pcblist";
+		break;
+	case AF_INET6:
+		/* We only accept queries from the proxy. */
+		if (IN6_ARE_ADDR_EQUAL(&satosin6(&ss[0])->sin6_addr,
+		    &satosin6(proxy)->sin6_addr))
+			list = "net.inet6.tcp.pcblist";	
+		break;
+	default:
+		maybe_syslog(LOG_ERR, "Unsupported protocol for proxy (no. %d)",
+		    ss[0].ss_family);
+	}
+	if (list != NULL)
+		rc = sysctlnametomib(list, &name[0], &sz);
+	if (rc == -1)
+		return -1;
+	len = sz;
+
+	name[len++] = PCB_ALL;
+	name[len++] = 0;
+	name[len++] = sizeof(struct kinfo_pcb);
+	name[len++] = INT_MAX;
+
+	kp = NULL;
+	sz = 0;
+	do {
+		rc = sysctl(&name[0], len, kp, &sz, NULL, 0);
+		if (rc == -1 && errno != ENOMEM)
+			return -1;
+		if (kp == NULL) {
+			kp = malloc(sz);
+			rc = -1;
+		}
+		if (kp == NULL)
+			return -1;
+	} while (rc == -1);
+
+	rc = -1;
+	/*
+	 * Walk through the list of sockets and try to find a match.
+	 * We don't know who has sent the query (we only know that the
+	 * proxy has forwarded to us) so just try to match the ports and
+	 * the local address.
+	 */
+	for (i = 0; i < sz / sizeof(struct kinfo_pcb); i++) {
+		switch (ss[0].ss_family) {
+		case AF_INET:
+			/* Foreign and local ports must match. */
+			if (satosin(&ss[0])->sin_port != 
+			    satosin(&kp[i].ki_src)->sin_port)
+				continue;
+			if (satosin(&ss[1])->sin_port !=
+			    satosin(&kp[i].ki_dst)->sin_port)
+				continue;
+			/* Foreign address may not match proxy address. */
+			if (in_hosteq(satosin(proxy)->sin_addr,
+			    satosin(&kp[i].ki_dst)->sin_addr))
+				continue;
+			/* Local addresses must match. */
+			if (!in_hosteq(satosin(&ss[1])->sin_addr,
+			    satosin(&kp[i].ki_src)->sin_addr))
+				continue;
+			break;
+		case AF_INET6:
+			/* Foreign and local ports must match. */
+			if (satosin6(&ss[0])->sin6_port !=
+			    satosin6(&kp[i].ki_src)->sin6_port)
+				continue;
+			if (satosin6(&ss[1])->sin6_port !=
+			    satosin6(&kp[i].ki_dst)->sin6_port)
+				continue;
+			/* Foreign address may not match proxy address. */
+			if (IN6_ARE_ADDR_EQUAL(&satosin6(proxy)->sin6_addr,
+			    &satosin6(&kp[i].ki_dst)->sin6_addr))
+				continue;
+			/* Local addresses must match. */
+			if (!IN6_ARE_ADDR_EQUAL(&satosin6(&ss[1])->sin6_addr,
+			    &satosin6(&kp[i].ki_src)->sin6_addr))
+				continue;
+			break;
+		}
+
+		/*
+		 * We have found the foreign address, copy it to a new
+		 * struct and retrieve the UID of the connection owner.
+		 */
+		memcpy(&new[0], &kp[i].ki_dst, kp[i].ki_dst.sa_len);
+		memcpy(&new[1], &kp[i].ki_src, kp[i].ki_src.sa_len);
+
+		rc = sysctl_getuid(new, sizeof(new), uid);
+
+		/* Done. */
+		break;
+	}
+
+	free(kp);
+	return rc;
+}
+
+/* Forward ident queries. Returns 1 when succesful, or zero if not. */
+static int
+forward(int fd, struct sockaddr *nat_addr, int nat_lport, int fport, int lport)
+{
+	char buf[BUFSIZ], reply[BUFSIZ], *p;
+	int sock, n;
+
+	/* Connect to the NAT host. */
+	sock = socket(nat_addr->sa_family, SOCK_STREAM, 0);
+	if (sock < 0) {
+		maybe_syslog(LOG_ERR, "socket: %m");
+		return 0;
+	}
+	if (connect(sock, nat_addr, nat_addr->sa_len) < 0) {
+		maybe_syslog(LOG_ERR, "Can't connect to %s: %m",
+		    gethost(nat_addr));
+		(void)close(sock);
+		return 0;
+	}
+
+	/*
+	 * Send the ident query to the NAT host, but use as local port
+	 * the port of the NAT host.
+	 */
+	(void)snprintf(buf, sizeof(buf), "%d , %d\r\n", nat_lport, fport);
+	if (send(sock, buf, strlen(buf), 0) < 0) {
+		maybe_syslog(LOG_ERR, "send: %m");
+		(void)close(sock);
+		return 0;
+	}
+
+	/* Read the reply from the NAT host. */
+	if ((n = recv(sock, reply, sizeof(reply) - 1, 0)) < 0) {
+		maybe_syslog(LOG_ERR, "recv: %m");
+		(void)close(sock);
+		return 0;
+	} else if (n == 0) {
+		maybe_syslog(LOG_NOTICE, "recv: EOF");
+		(void)close(sock);
+		return 0;
+	}
+	reply[n] = '\0';
+	(void)close(sock);
+
+	/* Extract everything after the port specs from the ident reply. */
+	for (p = reply; *p != '\0' && *p != ':'; p++)
+		continue;
+	if (*p == '\0' || *++p == '\0') {
+		maybe_syslog(LOG_ERR, "Malformed ident reply from %s",
+		    gethost(nat_addr));
+		return 0;
+	}
+	/* Build reply for the requesting host, use the original local port. */
+	(void)snprintf(buf, sizeof(buf), "%d,%d:%s", lport, fport, p);
+
+	/* Send the reply from the NAT host back to the requesting host. */
+	if (send(fd, buf, strlen(buf), 0) < 0) {
+		maybe_syslog(LOG_ERR, "send: %m");
+		return 0;
+	}
+
+	return 1;
+}
+
 /* Check if a .noident file exists in the user home directory. */
 static int
 check_noident(const char *homedir)
@@ -754,7 +1053,7 @@
 }
 
 /* Log using syslog, but only if enabled with the -l flag. */
-static void
+void
 maybe_syslog(int priority, const char *message, ...)
 {
 	va_list ap;
Index: identd.h
===================================================================
RCS file: identd.h
diff -N identd.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ identd.h	24 Mar 2005 12:39:04 -0000
@@ -0,0 +1,27 @@
+/* $NetBSD$ */
+
+/*
+ * identd.h - TCP/IP Ident protocol server.
+ *
+ * This software is in the public domain.
+ * Written by Peter Postma <peter@NetBSD.org>
+ */
+
+#ifndef _IDENTD_H_
+#define _IDENTD_H_
+
+#define satosin(sa)	((struct sockaddr_in *)(sa))
+#define satosin6(sa)	((struct sockaddr_in6 *)(sa))
+#define in_hosteq(s,t)	((s).s_addr == (t).s_addr)
+
+void maybe_syslog(int, const char *, ...);
+
+#ifdef WITH_PF
+int pf_natlookup(struct sockaddr_storage *, struct sockaddr *, int *);
+#endif
+
+#ifdef WITH_IPF
+int ipf_natlookup(struct sockaddr_storage *, struct sockaddr *, int *);
+#endif
+
+#endif /* !_IDENTD_H_ */
Index: ipf.c
===================================================================
RCS file: ipf.c
diff -N ipf.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ipf.c	24 Mar 2005 12:39:05 -0000
@@ -0,0 +1,96 @@
+/* $NetBSD$ */
+
+/*
+ * ipf.c - NAT lookup code for IP Filter.
+ *
+ * This software is in the public domain.
+ * Written by Peter Postma <peter@NetBSD.org>
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ipl.h>
+#include <netinet/ip_compat.h>
+#include <netinet/ip_fil.h>
+#include <netinet/ip_nat.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "identd.h"
+
+int
+ipf_natlookup(struct sockaddr_storage *ss, struct sockaddr *nat_addr,
+    int *nat_lport)
+{
+	natlookup_t nl;
+	ipfobj_t obj;
+	int dev;
+
+	/* Open the ipnat device. */
+	if ((dev = open(IPNAT_NAME, O_RDONLY)) == -1) {
+		maybe_syslog(LOG_ERR, "Cannot open %s: %m", IPNAT_NAME);
+		return 0;
+	}
+
+	(void)memset(&obj, 0, sizeof(obj));
+	(void)memset(&nl, 0, sizeof(nl));
+
+        /* Build the ipf object description structure. */
+	obj.ipfo_rev = IPFILTER_VERSION;
+	obj.ipfo_size = sizeof(nl);
+	obj.ipfo_ptr = &nl;
+	obj.ipfo_type = IPFOBJ_NATLOOKUP;
+
+	/* Build the ipf natlook structure. */
+	switch (ss[0].ss_family) {
+	case AF_INET:
+		nl.nl_flags = IPN_TCP|IPN_IN;
+		nl.nl_realip = satosin(&ss[0])->sin_addr;
+		nl.nl_outip = satosin(&ss[1])->sin_addr;
+		nl.nl_realport = ntohs(satosin(&ss[0])->sin_port);
+		nl.nl_outport = ntohs(satosin(&ss[1])->sin_port);
+		break;
+	case AF_INET6:
+		/* XXX IP Filter doesn't support IPv6 NAT yet. */
+	default:
+		maybe_syslog(LOG_ERR, "Unsupported protocol for NAT lookup "
+		    "(no. %d)", ss[0].ss_family);
+		(void)close(dev);
+		return 0;
+	}
+
+	if (ioctl(dev, SIOCGNATL, &obj) == -1) {
+		maybe_syslog(LOG_ERR, "NAT lookup failure: %m");
+		(void)close(dev);
+		return 0;
+	}
+	(void)close(dev);
+
+	/*
+	 * Put the originating address into nat_addr and fill
+	 * the port with the ident port, 113.
+	 */
+	switch (ss[0].ss_family) {
+	case AF_INET:
+		satosin(nat_addr)->sin_addr = nl.nl_inip;
+		satosin(nat_addr)->sin_port = htons(113);
+		satosin(nat_addr)->sin_len = sizeof(struct sockaddr_in);
+		satosin(nat_addr)->sin_family = AF_INET;
+		break;
+	case AF_INET6:
+		break;
+	}
+	/* Put the originating port info nat_lport. */
+	*nat_lport = nl.nl_inport;
+
+	return 1;
+}
Index: pf.c
===================================================================
RCS file: pf.c
diff -N pf.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ pf.c	24 Mar 2005 12:39:05 -0000
@@ -0,0 +1,97 @@
+/* $NetBSD$ */
+
+/*
+ * pf.c - NAT lookup code for pf.
+ *
+ * This software is in the public domain.
+ * Written by Peter Postma <peter@NetBSD.org>
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "identd.h"
+
+int
+pf_natlookup(struct sockaddr_storage *ss, struct sockaddr *nat_addr,
+    int *nat_lport)
+{
+	struct pfioc_natlook nl;
+	int dev;
+
+	/* Open the /dev/pf device. */
+	if ((dev = open("/dev/pf", O_RDWR)) == -1) {
+		maybe_syslog(LOG_ERR, "Cannot open /dev/pf: %m");
+		return 0;
+	}
+
+	(void)memset(&nl, 0, sizeof(nl));
+
+	/* Build the pf natlook structure. */
+	switch (ss[0].ss_family) {
+	case AF_INET:
+		nl.daddr.v4 = satosin(&ss[0])->sin_addr;
+		nl.saddr.v4 = satosin(&ss[1])->sin_addr;
+		nl.dport = satosin(&ss[0])->sin_port;
+		nl.sport = satosin(&ss[1])->sin_port;
+		nl.af = AF_INET;
+		nl.proto = IPPROTO_TCP;
+		nl.direction = PF_IN;
+		break;
+	case AF_INET6:
+		nl.daddr.v6 = satosin6(&ss[0])->sin6_addr;
+		nl.saddr.v6 = satosin6(&ss[1])->sin6_addr;
+		nl.dport = satosin6(&ss[0])->sin6_port;
+		nl.sport = satosin6(&ss[1])->sin6_port;
+		nl.af = AF_INET6;
+		nl.proto = IPPROTO_TCP;
+		nl.direction = PF_IN;
+		break;
+	default:
+		maybe_syslog(LOG_ERR, "Unsupported protocol for NAT lookup "
+		    "(no. %d)", ss[0].ss_family);
+		(void)close(dev);
+		return 0;
+	}
+
+	if (ioctl(dev, DIOCNATLOOK, &nl) == -1) {
+		maybe_syslog(LOG_ERR, "NAT lookup failure: %m");
+		(void)close(dev);
+		return 0;
+	}
+	(void)close(dev);
+
+	/*
+	 * Put the originating address into nat_addr and fill
+	 * the port with the ident port, 113.
+	 */
+	switch (ss[0].ss_family) {
+	case AF_INET:
+		satosin(nat_addr)->sin_addr = nl.rsaddr.v4;
+		satosin(nat_addr)->sin_port = htons(113);
+		satosin(nat_addr)->sin_len = sizeof(struct sockaddr_in);
+		satosin(nat_addr)->sin_family = AF_INET;
+		break;
+	case AF_INET6:
+		satosin6(nat_addr)->sin6_addr = nl.rsaddr.v6;
+		satosin6(nat_addr)->sin6_port = htons(113);
+		satosin6(nat_addr)->sin6_len = sizeof(struct sockaddr_in6);
+		satosin6(nat_addr)->sin6_family = AF_INET6;
+		break;
+	}
+	/* Put the originating port info nat_lport. */
+	*nat_lport = nl.rsport;
+
+	return 1;
+}

--sm4nu43k4a2Rpi4c--