--- inn-1.7.2.orig/samples/hosts.nntp
+++ inn-1.7.2/samples/hosts.nntp
@@ -3,6 +3,11 @@
 ##  Format
 ##	<host>:
 ##	<host>:<password>
+##	<host>:<password>:<permitted groups>
 ##  <host> can be a name or IP address; no wildcards.  Any hosts not
 ##  listed here are handed off to nnrpd.
-localhost:
+##  <host> can be followed by some parameters:
+##   /a IP address of the feeder should always be included in the Path: line
+##   /t IP address of the feeder should never be included in the Path: line
+##   /s the feeder is allowed to do streaming (please read hosts.nntp(5)!)
+localhost/t:
--- inn-1.7.2.orig/doc/hosts.nntp.5
+++ inn-1.7.2/doc/hosts.nntp.5
@@ -70,6 +70,24 @@
 .fi
 .RE
 .PP
+The first field may be suffixed by ``/a'' to indicate that the IP
+address of the feeding hosts allowed by this entry should always be
+included in the Path line of articles, or by ``/t'' to indicate that
+the address should not be included, or ``/a'' followed by a pathhost
+value to indicate that the IP address should be included if the most
+recent Path entry does not match the pathhost specified after ``/a''.
+The default is to log the address in articles whose most recent Path
+entry is not the same as the hostname in the hosts.nntp entry.
+.\" .PP
+.\" The first field may be suffixed by ``/i'' to indicate that the TCP
+.\" Ident information should be included in the Path lines of articles.
+.\" ``/i'' may be followed by one or more usernames separated by commas;
+.\" if so then those usernames will not be logged.  If ``/i'' is specified
+.\" then articles received over connections for which TCP ident
+.\" information was not available have ``?'' used for the username
+.\" information.  If ``/i'' implies the logging of a username then the IP
+.\" address will be logged even if this would not ordinarily be the case.
+.PP
 Since
 .I innd
 is usually started at system boot time, the local nameserver may not be
--- inn-1.7.2.orig/include/configdata.h
+++ inn-1.7.2/include/configdata.h
@@ -309,6 +309,12 @@
     /* Log by host IP address, rather than from Path line? */
     /* =()<#define @<IPADDR_LOG>@_IPADDR_LOG>()= */
 #define DO_IPADDR_LOG
+    /* Support putting feeding IP address in Paths (/a,/t hosts.nntp options)*/
+    /* =()<#define @<IPADDR_PATH>@_IPADDR_PATH>()= */
+#define DO_IPADDR_PATH
+    /* Support logging TCP Ident info in Paths (/i hosts.nntp option, requires IPADDR_PATH) */
+    /* =()<#define @<TCPIDENT_PATH>@_TCPIDENT_PATH>()= */
+#define DONT_TCPIDENT_PATH
     /* Log NNTP activity after this many articles. */
     /* =()<#define NNTP_ACTIVITY_SYNC	@<NNTP_ACTIVITY_SYNC>@>()= */
 #define NNTP_ACTIVITY_SYNC	200
--- inn-1.7.2.orig/innd/art.c
+++ inn-1.7.2/innd/art.c
@@ -559,7 +559,7 @@
     register IOVEC	*vp;
     register long	size;
     register char	*p;
-    IOVEC		iov[7];
+    IOVEC		iov[11];
     IOVEC		*end;
     char		bytesbuff[SMBUF];
     int			i;
@@ -593,6 +593,27 @@
         size += (vp++)->iov_len;
     }
         
+#if defined(DO_IPADDR_PATH)
+    if (Data->Feedaddress.s_addr) {
+#if defined(DO_TCPIDENT_PATH)
+	if (Data->Feedident) {
+	    vp->iov_base = Data->Feedident;
+	    vp->iov_len  = strlen(vp->iov_base);
+	    size += (vp++)->iov_len;
+	    vp->iov_base = "@";
+	    vp->iov_len  = 1;
+	    size += (vp++)->iov_len;
+	}
+#endif /* defined(DO_TCPIDENT_PATH) */
+	vp->iov_base = inet_ntoa(Data->Feedaddress);
+	vp->iov_len  = strlen(vp->iov_base);
+	size += (vp++)->iov_len;
+	vp->iov_base = "!";
+	vp->iov_len  = 1;
+	size += (vp++)->iov_len;
+    }
+#endif /* defined(DO_IPADDR_PATH) */
+
     vp->iov_base = p;
     vp->iov_len  = Data->Body - p;
     size += (vp++)->iov_len;
@@ -1833,6 +1854,23 @@
 #else
     Data.Feedsite = hops && hops[0] ? hops[0] : CHANname(cp);
 #endif	/* defined(DO_IPADDRLOG) */
+
+#if defined(DO_IPADDR_PATH)
+    if (cp->PathAddr ||
+#if defined(DO_TCPIDENT_PATH)
+	    cp->IdentForPath ||
+#endif /* defined(DO_TCPIDENT_PATH) */
+	    cp->RequirePathHostForPath &&
+	    (!hops || !hops[0] || strcmp(hops[0],cp->RequirePathHostForPath))){
+	Data.Feedaddress = cp->Address;
+	Data.Feedident = cp->IdentForPath;
+    } else
+#endif /* defined(DO_IPADDR_PATH) */
+    {
+	Data.Feedaddress.s_addr = 0;
+	Data.Feedident = NULL;
+    }
+
     Data.FeedsiteLength = strlen(Data.Feedsite);
     (void)sprintf(Data.TimeReceived, "%lu", Now.time);
     Data.TimeReceivedLength = strlen(Data.TimeReceived);
--- inn-1.7.2.orig/innd/chan.c
+++ inn-1.7.2/innd/chan.c
@@ -102,6 +102,8 @@
 	    (SIZE_T)(CHANtablesize * sizeof *CHANtable));
     CHANnull.NextLog = CHANNEL_INACTIVE_TIME;
     CHANnull.Address.s_addr = MyAddress.s_addr;
+    CHANnull.RequirePathHostForPath = NULL;
+    CHANnull.IdentForPath = NULL;
     for (cp = CHANtable; --i >= 0; cp++)
 	*cp = CHANnull;
 }
@@ -267,6 +269,14 @@
 	cp->Sendid.Size = 0;
 	DISPOSE(cp->Sendid.Data);
     }
+    if (cp->RequirePathHostForPath) {
+	DISPOSE(cp->RequirePathHostForPath);
+	cp->RequirePathHostForPath = NULL;
+    }
+    if (cp->IdentForPath) {
+	DISPOSE(cp->IdentForPath);
+	cp->IdentForPath = NULL;
+    }
 }
 
 
--- inn-1.7.2.orig/innd/innd.h
+++ inn-1.7.2/innd/innd.h
@@ -132,6 +132,7 @@
     CHANNELTYPE		Type;
     CHANNELSTATE	State;
     BOOL		Streaming ; /* had an '/s' on the end of hosts.nntp entry */
+    BOOL               PathAddr ; /* If TRUE then always add IP address info to Path */
     int			fd;
     int			Reported;
     long		Received;
@@ -144,6 +145,8 @@
     time_t		LastActive;
     time_t		NextLog;
     INADDR		Address;
+    char               *RequirePathHostForPath; /* If non-null then add Path addr info if proximate Path entry not equal to this; PathAddr will be FALSE and IdentForPath NULL */
+    char               *IdentForPath; /* If non-null then PathAddr will be TRUE */
     FUNCPTR		Reader;
     FUNCPTR		WriteDone;
     time_t		Waketime;
@@ -298,8 +301,12 @@
     int		DistributionLength;
     STRING	Feedsite;
     int		FeedsiteLength;
+    INADDR     Feedaddress; /* If Feedaddress is set then Feedident may be set. */
+    char       *Feedident; /* Feedident makes no sense on its own. */
     STRING	Replic;
     int		ReplicLength;
+    STRING	Path;
+    int		PathLength;
     BUFFER	*Headers;
     BUFFER	*Overview;
 } ARTDATA;
--- inn-1.7.2.orig/innd/rc.c
+++ inn-1.7.2/innd/rc.c
@@ -26,6 +26,10 @@
 #define COPYADDR(dest, src) \
 	    (void)memcpy((POINTER)dest, (POINTER)src, (SIZE_T)sizeof (INADDR))
 
+#if    defined(DO_TCPIDENT_PATH)
+#include <ident.h>
+#endif /* defined(DO_TCPIDENT_PATH) */
+
 /*
 **  A remote host has an address and a password.
 */
@@ -34,6 +38,10 @@
     INADDR	Address;
     char	*Password;
     BOOL	Streaming ;
+    BOOL       PathAddr ;
+    char       *RequirePathHostForPath ;
+    BOOL       PathIdent ;
+    char       **PathNoIdentIdents;
     char	**Patterns;
 } REMOTEHOST;
 
@@ -208,6 +216,86 @@
 
 
 /*
+**  Sets the Path logging on a new NNTP channel, according to the
+**  parameters given (which usually came from the hosts.nntp).
+*/
+STATIC FUNCTYPE
+RCsetpathlogging(cp,PathAddr,RequirePathHost,PathIdent,PathNoIdentIdents)
+    CHANNEL            *cp;
+    BOOL               PathAddr;
+    char               *RequirePathHost;
+    BOOL               PathIdent;
+    char               **PathNoIdentIdents;
+{
+    char               *ident;
+    char               *protectwalk;
+    char               *protectout;
+    char               **noidentswalk;
+    int                        c;
+    int                        protectedlen;
+
+#if    defined(DO_IPADDR_PATH)
+#if    defined(DO_TCPIDENT_PATH)
+    if (!PathIdent) {
+       cp->PathAddr= PathAddr;
+       cp->RequirePathHostForPath = RequirePathHost ? COPY(RequirePathHost) : NULL;
+       cp->IdentForPath = NULL;
+       return;
+    }
+    ident = ident_id(cp->fd,10);
+    if (!ident) {
+       cp->PathAddr = TRUE;
+       cp->RequirePathHostForPath = NULL;
+       cp->IdentForPath = NEW(char,2);
+       (void)strcpy(cp->IdentForPath,"?");
+       return;
+    }
+    for (noidentswalk = PathNoIdentIdents;
+        noidentswalk && *noidentswalk;
+        noidentswalk++) {
+       if (!strcmp(*noidentswalk,ident)) {
+           DISPOSE(ident);
+           cp->PathAddr = PathAddr;
+           cp->RequirePathHostForPath = RequirePathHost ? COPY(RequirePathHost) : NULL;
+           cp->IdentForPath = NULL;
+           return;
+       }
+    }
+    for (protectwalk = ident, protectedlen = 0;
+        c = *protectwalk;
+        protectwalk++)
+       protectedlen += c == '\\' ? 2 : (c > 32 && c <= 127) ? 1 : 4;
+    cp->PathAddr = TRUE;
+    cp->RequirePathHostForPath = NULL;
+    cp->IdentForPath = NEW(char,protectedlen+1);
+    for (protectwalk = ident, protectout = cp->IdentForPath;
+        c = *protectwalk;
+        protectwalk++) {
+       if (c == '\\') {
+           *protectout++ = '\\';
+           *protectout++ = '\\';
+       } else if (c > 32 && c <= 127) {
+           *protectout++ = c;
+       } else {
+           sprintf(protectout,"\\x%02x",c & 0x0ff);
+           protectout += 4;
+       }
+    }
+    *protectout = '\0';
+    return;
+#else
+    cp->PathAddr = PathAddr;
+    cp->RequirePathHostForPath = RequirePathHost ? COPY(RequirePathHost) : NULL;
+    cp->IdentForPath = NULL;
+#endif /* defined(DO_TCPIDENT_PATH) */
+#else
+    cp->PathAddr = FALSE;
+    cp->RequirePathHostForPath = NULL;
+    cp->IdentForPath = NULL;
+#endif /* defined(DO_IPADDR_PATH) */
+}
+
+/*
 **  Read function.  Accept the connection and either create an NNTP channel
 **  or spawn an nnrpd to handle it.
 */
@@ -353,8 +441,13 @@
     if (i >= 0) {
 	new = NCcreate(fd, rp->Password[0] != '\0', FALSE);
         new->Streaming = rp->Streaming ;
+       RCsetpathlogging(new,
+                        rp->PathAddr, rp->RequirePathHostForPath,
+                        rp->PathIdent, rp->PathNoIdentIdents);
     } else if (AnyIncoming) {
 	new = NCcreate(fd, FALSE, FALSE);
+       new->Streaming = FALSE ;
+       RCsetpathlogging(new, TRUE, NULL, TRUE, NULL);
     } else {
 	RChandoff(fd, HOnntpd);
 	if (close(fd) < 0)
@@ -364,8 +457,10 @@
 
     /* SUPPRESS 112 *//* Retrieving long where char is stored */
     new->Address.s_addr = remote.sin_addr.s_addr;
-    syslog(L_NOTICE, "%s connected %d streaming %s",
-           name ? name : inet_ntoa(new->Address), new->fd,
+    syslog(L_NOTICE, "%s connected %d%s%s streaming %s",
+          name ? name : inet_ntoa(new->Address), new->fd,
+          new->IdentForPath ? " ident " : "",
+          new->IdentForPath ? new->IdentForPath : "",
            (!StreamingOff || new->Streaming) ? "allowed" : "not allowed");
 }
 
@@ -403,6 +498,12 @@
     register REMOTEHOST	*rp;
     register int	j;
     int			k ;
+    BOOL               usedefaultrequirepathhost;
+    int                        exceptionscount;
+    char               *attrib;
+    char               *exceptionswalk;
+    char               **exceptionsdispose;
+    char               *delimiter;
     char		*pass;
     char		*pats;
     int			errors;
@@ -412,6 +513,15 @@
 	for (rp = *list, i = *count; --i >= 0; rp++) {
 	    DISPOSE(rp->Name);
 	    DISPOSE(rp->Password);
+           if (rp->RequirePathHostForPath)
+               DISPOSE(rp->RequirePathHostForPath);
+           if (rp->PathNoIdentIdents) {
+               for (exceptionsdispose= rp->PathNoIdentIdents;
+                    *exceptionsdispose;
+                    exceptionsdispose++)
+                   DISPOSE(*exceptionsdispose);
+               DISPOSE(rp->PathNoIdentIdents);
+           }
 	    if (rp->Patterns) {
 		DISPOSE(rp->Patterns[0]);
 		DISPOSE(rp->Patterns);
@@ -437,6 +547,9 @@
     rp->Name = COPY("localhost");
     rp->Password = COPY(NOPASS);
     rp->Patterns = NULL;
+    rp->PathAddr = FALSE;
+    rp->PathIdent = FALSE;
+    rp->PathNoIdentIdents = NULL;
     rp++;
 #endif	/* !defined(DO_HAVE_UNIX_DOMAIN) */
 
@@ -462,20 +575,107 @@
 	    pats = NULL;
 	}
 
-        /* Check if the host name ends with '/s' which means that streaming is
-           specifically permitted (as opposed to defaulted). The default
-           for the global StreamingOff is FALSE, meaning any host can use
-           streaming commands. If any entry is hosts.nntp has a suffix of
-           '/s' then StreamingOff is set to TRUE, and then only those
-           hosts.nntp entries with '/s' can use streaming commands. */
-        rp->Streaming = FALSE;
-        if ((k = strlen(buff)) > 2) {
-            if (buff[k - 1] == 's' && buff[k - 2] == '/') {
-                buff[k - 2] = '\0';
-                rp->Streaming = TRUE;
-                StreamingOff = TRUE ;
-            }
-        }
+       rp->Streaming = FALSE;
+       rp->PathAddr = FALSE;
+       rp->PathIdent = FALSE;
+       rp->PathNoIdentIdents = NULL;
+       rp->RequirePathHostForPath = NULL;
+       usedefaultrequirepathhost = TRUE;
+
+       /* Check for `attributes', ie things after the hostname separated
+          by a /.  This includes the following checks ... */
+
+       while ((attrib = strrchr(buff,'/')) != NULL) {
+           if (!strcmp(attrib,"/s")) {
+             /* Check if the host name ends with '/s' which means that streaming is
+                specifically permitted (as opposed to defaulted). The default
+                for the global StreamingOff is FALSE, meaning any host can use
+                streaming commands. If any entry is hosts.nntp has a suffix of
+                '/s' then StreamingOff is set to TRUE, and then only those
+                hosts.nntp entries with '/s' can use streaming commands. */
+
+               rp->Streaming = TRUE ;
+               StreamingOff = TRUE ;
+#if    defined(DO_IPADDR_PATH)
+           } else if (!strcmp(attrib,"/t")) {
+             /* Check if the host name ends with '/t' which means
+                that this entry's articles should never have the
+                calling IP address of the feeding host logged in the
+                Path: line of accepted articles. */
+
+               rp->PathAddr = FALSE ;
+               usedefaultrequirepathhost = FALSE;
+           } else if (!strcmp(attrib,"/a")) {
+             /* Check if the host name ends with '/a' which means that this
+                entry's articles should have the calling IP address of the feeding
+                host logged in the Path: line of all accepted articles. */
+
+               rp->PathAddr = TRUE ;
+               usedefaultrequirepathhost = FALSE;
+               if (rp->RequirePathHostForPath != NULL) {
+                   DISPOSE(rp->RequirePathHostForPath);
+                   rp->RequirePathHostForPath = NULL;
+               }
+           } else if (!strncmp(attrib,"/a",2)) {
+             /* Check if the host name ends with `/a<hostname>' which
+                means that this entry's articles should have the
+                calling IP address logged if it the most recent Path:
+                entry does not match the hostname supplied after `/a'. */
+
+               if (rp->RequirePathHostForPath != NULL)
+                   DISPOSE(rp->RequirePathHostForPath);
+               usedefaultrequirepathhost = FALSE;
+               rp->RequirePathHostForPath = COPY(attrib+2);
+#if    defined(DO_TCPIDENT_PATH)
+           } else if (!strncmp(attrib,"/i",2)) {
+             /* Check if the host name ends with
+                '/i[<username>,<username>,...]' which means that the
+                calling IP address and RFC1413 (TCP Remote Username
+                Service, aka 'ident') value should be logged, unless
+                the ident value is one of those listed. '/i' implies '/a'. */
+
+               rp->PathIdent = TRUE ;
+               if (attrib[2] == '\0') {
+                   rp->PathNoIdentIdents = NULL;
+               } else {
+                   exceptionswalk = attrib+2;
+                   exceptionscount = 0;
+                   for (;;) {
+                       exceptionscount++;
+                       exceptionswalk = strchr(exceptionswalk,',');
+                       if (!exceptionswalk) break;
+                       exceptionswalk++;
+                   }
+                   rp->PathNoIdentIdents = NEW(char*,exceptionscount++);
+                   exceptionscount = 0;
+                   exceptionswalk = attrib+2;
+                   for (;;) {
+                       delimiter = strchr(exceptionswalk,',');
+                       k = delimiter ? delimiter-exceptionswalk
+                                     : strlen(exceptionswalk);
+                       rp->PathNoIdentIdents[exceptionscount] = NEW(char,k+1);
+                       memcpy(rp->PathNoIdentIdents[exceptionscount],exceptionswalk,k);
+                       rp->PathNoIdentIdents[exceptionscount][k] = '\0';
+                       exceptionscount++;
+                       if (!delimiter) break;
+                       exceptionswalk = delimiter+1;
+                   }
+                   rp->PathNoIdentIdents[exceptionscount] = NULL;
+               }
+#endif /* defined(DO_TCPIDENT_PATH) */
+#endif /* defined(DO_IPADDR_PATH) */
+           } else {
+               syslog(L_NOTICE,"unknown attribute (ignored): %s",attrib);
+           }
+           *attrib = '\0';
+       }
+
+#if    defined(DO_IPADDR_PATH)
+       if (usedefaultrequirepathhost) {
+           rp->RequirePathHostForPath = COPY(buff);
+       }
+
+#endif /* defined(DO_IPADDR_PATH) */
 
 	/* Was host specified as as dotted quad? */
 	if ((rp->Address.s_addr = inet_addr(buff)) != (unsigned int) -1) {
