This patch to INN 1.6b3 allows for "virtual news servers", i.e. one server on a multihomed host pretending to be several servers. For details consult the README.Virtual file. =================================================================== Index: ./README.Virtual diff -u /dev/null ./README.Virtual:1.1 --- /dev/null +++ ./README.Virtual Sat Aug 9 20:09:14 1997 @@ -0,0 +1,101 @@ +How to use the Virtual news servers patch + +A multi-homed news server can now use separate config files for each +of its addresses. This way, you can pretend that your news server +living on more than one address is in fact several news servers. +Useful for separating out special interest groups servers, internal +vs. external servers, etc. + +Each one of the following config files can be "virtualized": inn.conf, +hosts.nntp, hosts.nntp.nolimit, nnrp.access. If you suffix any of +these files with a dotquad IP address, e.g. inn.conf.10.0.0.1, this +file is used in place of the default (original file name) for any +client that connects to this IP address. This is called a virtual +config file. If the extended file is not available, the default is +used. + +Only the actual available file names matter. No attempt is made to +list or verify which virtual addresses a host has. + +- inn.conf + +The innd itself uses only one configuration item, i.e. "pathhost". +This is taken from the default inn.conf and can not be virtualized. +All items that apply to nnrpd are taken by nnrpd from the +virtualized inn.conf file at the time nnrpd starts up. This does +include "pathhost", which is the name in the 200 message and the +X-Trace header. + +inn.conf has an additional item for nnrpd: "pathdefault". This item, +if present, contains the default for an empty Path line, which is then +taken instead of the default from config.data. For a useful example, +see below. + +- hosts.nntp, hosts.nntp.nolimit + +On startup and with "ctlinnd reload hosts.nntp", innd loads all +available virtual hosts.nntp files (including the default, which still +must be present). The nolimit files are virtualized too. The default +for whether streaming is allowed remains global - streaming is denied +by default if the /s suffix appears in _any_ virtual hosts.nntp. + +- nnrp.access + +nnrpd loads on startup the matching virtual nnrp.access, as usual. + +** Usage example ** + +Suppose Blurf, Inc. runs a full news server for its employees called +"news.blurf.com" on the internal address 10.0.0.1. It provides limited +access to customers on the external address 200.1.2.3. Also Blurf, +Inc. provides a set of special interest newsgroups "roller.*" under +the address "news.roller.org" as 200.1.2.99. + +Then it could be set up like this: + +inn.conf: + pathhost: blurf.com + pathdefault: not-for-mail + organization: Blurf, Inc., Staff + +inn.conf.200.1.2.3: + pathhost: blurf.com + pathdefault: dialup.blurf.com!not-for-mail + organization: Blurf, Inc., Dialup Division + complaints: newsmaster@blurf.com + +inn.conf.200.1.2.99: + pathhost: news.roller.org + pathdefault: news.roller.org!not-for-mail + organization: The Roller Club + complaints: newsmaster@albuquerque.roller.org + +hosts.nntp: + howland.ans.net: + # backup feed + santa-monica.roller.org: + localhost: + +hosts.nntp.200.1.2.99: + santa-monica.roller.org::roller.* + flagstaff.roller.org::roller.* + localhost: + +nnrp.access: + *:: no: no:!* + *.blurf.com:RP:::* + +nnrp.access.200.1.2.3: + *:: no: no:!* + *.blurf.com:RP:::* + *.dialup.blurf.com:RP:::*,!blurf.intern.* + +nnrp.access.200.1.2.99: + *:RP:::roller.* + + + +Any bugs in the virtual server code are due to +Also look at the patches for innfeed that provide virtual addresses to +connect _from_. + Index: ./include/libinn.h diff -u ./include/libinn.h:1.1.1.1 ./include/libinn.h:1.3 --- ./include/libinn.h:1.1.1.1 Fri Aug 1 14:55:36 1997 +++ ./include/libinn.h Wed Aug 6 14:42:44 1997 @@ -67,3 +67,7 @@ extern FILE *xfopena(); extern BOOL HiLowWater(); extern char *identInfo(); +extern void VIRThost(); +extern FILE *VIRTfopen(); +extern void VIRTsearch(); +extern struct in_addr *VIRTnext(); Index: ./innd/innd.h diff -u ./innd/innd.h:1.1.1.1 ./innd/innd.h:1.2 --- ./innd/innd.h:1.1.1.1 Fri Aug 1 14:55:37 1997 +++ ./innd/innd.h Sat Aug 9 03:52:47 1997 @@ -146,6 +146,7 @@ time_t LastActive; time_t NextLog; INADDR Address; + INADDR Virtaddr; FUNCPTR Reader; FUNCPTR WriteDone; time_t Waketime; Index: ./innd/nc.c diff -u ./innd/nc.c:1.1.1.2 ./innd/nc.c:1.2 --- ./innd/nc.c:1.1.1.2 Wed Aug 6 14:47:25 1997 +++ ./innd/nc.c Sat Aug 9 03:52:49 1997 @@ -1538,6 +1538,9 @@ wherefroml = sizeof wherefrom; getpeername(cp->fd, (struct sockaddr *)&wherefrom, &wherefroml); cp->Address = wherefrom.sin_addr; + wherefroml = sizeof wherefrom; + getsockname(cp->fd, (struct sockaddr *)&wherefrom, &wherefroml); + cp->Virtaddr = wherefrom.sin_addr; /* See if we have too many channels. */ if (!IsLocal && MaxIncoming && NCcount >= MaxIncoming && !RCnolimit(cp)) { Index: ./innd/rc.c diff -u ./innd/rc.c:1.1.1.1 ./innd/rc.c:1.4 --- ./innd/rc.c:1.1.1.1 Fri Aug 1 14:55:38 1997 +++ ./innd/rc.c Sat Aug 9 03:52:51 1997 @@ -37,6 +37,14 @@ char **Patterns; } REMOTEHOST; +typedef struct _VIRTUALHOST { + INADDR Virtaddr; + REMOTEHOST *peerlist; + int npeerlist; + REMOTEHOST *nolimitlist; + int nnolimitlist; +} VIRTUALHOST; + typedef struct _REMOTETABLE { INADDR Address; time_t Expires; @@ -49,10 +57,8 @@ STATIC char RCnnrqd[] = _PATH_NNQRD; STATIC char RCnntpd[] = _PATH_NNTPD; STATIC CHANNEL *RCchan; -STATIC REMOTEHOST *RCpeerlist; -STATIC int RCnpeerlist; -STATIC REMOTEHOST *RCnolimitlist; -STATIC int RCnnolimitlist; +STATIC VIRTUALHOST *RCvirtpeers; +STATIC int RCnvirtpeers; /* ** Stuff needed for limiting incoming connects. @@ -74,7 +80,13 @@ register REMOTEHOST *rp; register int i; - for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) + for (i = 0; i < RCnvirtpeers; i++) { + if (cp->Virtaddr.s_addr == RCvirtpeers[i].Virtaddr.s_addr || + RCvirtpeers[i].Virtaddr.s_addr == 0) + break; + } + for (rp = RCvirtpeers[i].peerlist, i = RCvirtpeers[i].npeerlist; + --i >= 0; rp++) /* SUPPRESS 112 *//* Retrieving long where char is stored */ if (cp->Address.s_addr == rp->Address.s_addr) { if (rp->Password[0] == '\0' || EQ(pass, rp->Password)) @@ -102,7 +114,13 @@ register REMOTEHOST *rp; register int i; - for (rp = RCnolimitlist, i = RCnnolimitlist; --i >= 0; rp++) + for (i = 0; i < RCnvirtpeers; i++) { + if (cp->Virtaddr.s_addr == RCvirtpeers[i].Virtaddr.s_addr || + RCvirtpeers[i].Virtaddr.s_addr == 0) + break; + } + for (rp = RCvirtpeers[i].nolimitlist, i = RCvirtpeers[i].nnolimitlist; + --i >= 0; rp++) /* SUPPRESS 112 *//* Retrieving long where char is stored */ if (cp->Address.s_addr == rp->Address.s_addr) return TRUE; @@ -216,7 +234,7 @@ CHANNEL *cp; { int fd; - struct sockaddr_in remote; + struct sockaddr_in local, remote; int size; register int i; register REMOTEHOST *rp; @@ -251,6 +269,9 @@ syslog(L_ERROR, "%s cant close %d %m", LogName, fd); return; } + size = sizeof local; + if (getsockname(fd, (struct sockaddr *)&local, &size)==0) + VIRThost(&local.sin_addr); /* ** If RemoteTimer is not zero, then check the limits on incoming @@ -342,7 +363,14 @@ } /* See if it's one of our servers. */ - for (name = NULL, rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) + for (i = 0; i < RCnvirtpeers; i++) { + if (local.sin_addr.s_addr == RCvirtpeers[i].Virtaddr.s_addr || + RCvirtpeers[i].Virtaddr.s_addr == 0) + break; + } + for (name = NULL, + rp = RCvirtpeers[i].peerlist, i = RCvirtpeers[i].npeerlist; + --i >= 0; rp++) /* SUPPRESS 112 *//* Retrieving long where char is stored */ if (rp->Address.s_addr == remote.sin_addr.s_addr) { name = rp->Name; @@ -389,7 +417,7 @@ ** name in old ones. */ STATIC void -RCreadfile(list, count, filename) +RCreadfile(list, count, filename, need) REMOTEHOST **list; int *count; char *filename; @@ -406,6 +434,7 @@ char *pass; char *pats; int errors; + struct stat Sb; /* Free anything that might have been there. */ if (*list) { @@ -422,8 +451,12 @@ *count = 0; } + if (!need) { + if (stat(filename, &Sb) < 0) + return; + } /* Open the server file, count the lines. */ - if ((F = fopen(filename, "r")) == NULL) { + if ((F = VIRTfopen(filename, "r")) == NULL) { syslog(L_FATAL, "%s cant read %s %m", LogName, filename); exit(1); } @@ -554,13 +587,31 @@ { static char INNDHOSTS[] = _PATH_INNDHOSTS; char name[sizeof _PATH_INNDHOSTS + sizeof ".nolimit"]; - struct stat Sb; + static INADDR nulladdr; + INADDR *addr; + int i; StreamingOff = FALSE ; - RCreadfile(&RCpeerlist, &RCnpeerlist, INNDHOSTS); FileGlue(name, INNDHOSTS, '.', "nolimit", sizeof(name)); - if (stat(name, &Sb) >= 0) - RCreadfile(&RCnolimitlist, &RCnnolimitlist, name); + nulladdr.s_addr = 0; + + VIRTsearch(INNDHOSTS); + i = 0; + do { + addr=VIRTnext(); + if (i>=RCnvirtpeers) { + RENEW(RCvirtpeers, VIRTUALHOST, i+1); + RCvirtpeers[i].Virtaddr = addr ? *addr : nulladdr; + RCvirtpeers[i].peerlist = RCvirtpeers[i].nolimitlist = NULL; + RCnvirtpeers=i+1; + } + VIRThost(addr); + RCreadfile(&RCvirtpeers[i].peerlist, &RCvirtpeers[i].npeerlist, + INNDHOSTS, 1); + RCreadfile(&RCvirtpeers[i].nolimitlist, &RCvirtpeers[i].nnolimitlist, + name, 0); + i++; + } while (addr); } @@ -576,7 +627,13 @@ register REMOTEHOST *rp; register int i; - for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) + for (i = 0; i < RCnvirtpeers; i++) { + if (cp->Virtaddr.s_addr == RCvirtpeers[i].Virtaddr.s_addr || + RCvirtpeers[i].Virtaddr.s_addr == 0) + break; + } + for (rp = RCvirtpeers[i].peerlist, i = RCvirtpeers[i].npeerlist; + --i >= 0; rp++) /* SUPPRESS 112 *//* Retrieving long where char is stored */ if (cp->Address.s_addr == rp->Address.s_addr) return rp->Name; @@ -600,7 +657,13 @@ register char *pat; register int i; - for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) { + for (i = 0; i < RCnvirtpeers; i++) { + if (cp->Virtaddr.s_addr == RCvirtpeers[i].Virtaddr.s_addr || + RCvirtpeers[i].Virtaddr.s_addr == 0) + break; + } + for (rp = RCvirtpeers[i].peerlist, i = RCvirtpeers[i].npeerlist; + --i >= 0; rp++) { /* SUPPRESS 112 *//* Retrieving long where char is stored */ if (cp->Address.s_addr != rp->Address.s_addr) continue; @@ -667,6 +730,8 @@ RCHANadd(RCchan); /* Get the list of hosts we handle. */ + RCvirtpeers = NULL; + RCnvirtpeers = 0; RCreadlist(); /* If we have a master, get all his addresses. */ @@ -717,21 +782,24 @@ RCclose() { register REMOTEHOST *rp; - register int i; + register int i, j; CHANclose(RCchan, CHANname(RCchan)); RCchan = NULL; - if (RCpeerlist) { - for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) { - DISPOSE(rp->Name); - DISPOSE(rp->Password); - if (rp->Patterns) - DISPOSE(rp->Patterns); + for (j = 0; j < RCnvirtpeers; j++) + if (RCvirtpeers[j].peerlist) { + for (rp = RCvirtpeers[j].peerlist, i = RCvirtpeers[j].npeerlist; + --i >= 0; rp++) { + DISPOSE(rp->Name); + DISPOSE(rp->Password); + if (rp->Patterns) + DISPOSE(rp->Patterns); + } + DISPOSE(RCvirtpeers[j].peerlist); } - DISPOSE(RCpeerlist); - RCpeerlist = NULL; - RCnpeerlist = 0; - } + DISPOSE(RCvirtpeers); + RCvirtpeers = NULL; + RCnvirtpeers = 0; if (RCmaster) { DISPOSE(RCmaster); Index: ./lib/Makefile diff -u ./lib/Makefile:1.1.1.1 ./lib/Makefile:1.2 --- ./lib/Makefile:1.1.1.1 Fri Aug 1 14:55:38 1997 +++ ./lib/Makefile Fri Aug 1 16:04:19 1997 @@ -55,7 +55,8 @@ lockfile.c nonblocking.c parsedate.c perl.c qio.c radix32.c readin.c \ remopen.c resource.c sendarticle.c sendpass.c tempname.c \ waitnb.c wildmat.c \ - xfopena.c xmalloc.c xmemerr.c xrealloc.c xwrite.c xwritev.c hilow.c + xfopena.c xmalloc.c xmemerr.c xrealloc.c xwrite.c xwritev.c hilow.c \ + virtual.c OBJECTS = $(MISSING_OBJ) \ checkart.o cleanfrom.o clientactive.o clientlib.o closeonexec.o \ @@ -65,7 +66,8 @@ lockfile.o nonblocking.o parsedate.o perl.o qio.o radix32.o readin.o \ remopen.o resource.o sendarticle.o sendpass.o tempname.o \ waitnb.o wildmat.o \ - xfopena.o xmalloc.o xmemerr.o xrealloc.o xwrite.o xwritev.o hilow.o + xfopena.o xmalloc.o xmemerr.o xrealloc.o xwrite.o xwritev.o hilow.o \ + virtual.o all: libinn.a Index: ./lib/getconfig.c diff -u ./lib/getconfig.c:1.1.1.1 ./lib/getconfig.c:1.3 --- ./lib/getconfig.c:1.1.1.1 Fri Aug 1 14:55:40 1997 +++ ./lib/getconfig.c Wed Aug 6 14:42:52 1997 @@ -24,7 +24,7 @@ register char c; /* Read the config file. */ - if ((F = fopen(_PATH_CONFIG, "r")) != NULL) { + if ((F = VIRTfopen(_PATH_CONFIG, "r")) != NULL) { c = *value; i = strlen(value); while (fgets(ConfigBuff, sizeof ConfigBuff, F) != NULL) { Index: ./lib/virtual.c diff -u /dev/null ./lib/virtual.c:1.3 --- /dev/null +++ ./lib/virtual.c Sat Aug 9 03:52:57 1997 @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include "configdata.h" +#include "clibrary.h" +#include "libinn.h" +#include "logging.h" +#include "mydir.h" + +STATIC char VIRTnambuf[20] = ""; +STATIC char VIRTglobbuf[SPOOLNAMEBUFF] = ""; +STATIC DIR *VIRTdir = NULL; + + +/* Set the virtual host the next VIRTfopen should use. */ +void +VIRThost(host) + struct in_addr *host; +{ + if (host) { + sprintf(VIRTnambuf, ".%s", inet_ntoa(*host)); + syslog(L_TRACE, "VIRThost: %s", VIRTnambuf); + } else { + VIRTnambuf[0] = '\0'; + } +} + +/* Open a file "path.host", or if not available, "path". + "host" is the dotquad notation of the IP address set with VIRThost. */ +FILE * +VIRTfopen(path, mode) + const char *path; + const char *mode; +{ + char buf[SPOOLNAMEBUFF]; /* correct?? */ + register FILE *F; + + if (VIRTnambuf[0]) { + strncpy(buf, path, sizeof buf-sizeof VIRTnambuf-1); + strcat(buf, VIRTnambuf); + F = fopen(buf, mode); + if (F) { + syslog(L_TRACE, "VIRTfopen: using %s", buf); + return F; + } else { + syslog(L_TRACE, "VIRTfopen: using %s", path); + } + } + return fopen(path, mode); +} + +/* Prepare to find files "path.dotquad". */ +void +VIRTsearch(path) + const char *path; +{ + char buf[SPOOLNAMEBUFF]; + register char *p; + + strncpy(buf, path, sizeof buf); + buf[sizeof buf - 1] = '\0'; + p = strrchr(buf, '/'); + if (p) { + *p++ = '\0'; + VIRTdir = opendir(buf); + } else { + p = buf; + VIRTdir = opendir("."); /* should not happen */ + } + strncpy(VIRTglobbuf, p, sizeof VIRTglobbuf - 8); + VIRTglobbuf[sizeof VIRTglobbuf - 9] = '\0'; + syslog(L_TRACE, "VIRTsearch: %s", VIRTglobbuf); + strcat(VIRTglobbuf, ".[1-9]*"); +} + +/* Return next address for which file as specified by VIRTsearch is + available. */ +struct in_addr * +VIRTnext() +{ + register DIRENTRY *ep; + STATIC struct in_addr ia; + register char *p; + register int i; + + if (!VIRTdir) + return NULL; + while ((ep = readdir(VIRTdir)) != NULL) { + p = ep->d_name; + if (!wildmat(p, VIRTglobbuf)) + continue; + p += strlen(p); + i = 0; + while (--p > ep->d_name) { + if (*p == '.') + if (++i > 3) + if (inet_aton(p+1, &ia)) { + syslog(L_TRACE, "VIRTnext: found %s", p); + return &ia; + } else { + syslog(L_TRACE, "VIRTnext: bogus %s", p); + break; + } + } + } + syslog(L_TRACE, "VIRTnext: EOF"); + (void)closedir(VIRTdir); + VIRTdir = NULL; + return NULL; +} Index: ./nnrpd/nnrpd.c diff -u ./nnrpd/nnrpd.c:1.1.1.2 ./nnrpd/nnrpd.c:1.5 --- ./nnrpd/nnrpd.c:1.1.1.2 Wed Aug 6 14:47:56 1997 +++ ./nnrpd/nnrpd.c Sat Aug 9 03:52:05 1997 @@ -348,7 +348,7 @@ char buff[BIG_BUFFER]; char *fields[5]; - if ((F = fopen(ACCESS, "r")) == NULL) { + if ((F = VIRTfopen(ACCESS, "r")) == NULL) { syslog(L_ERROR, "%s cant fopen %s %m", ClientHost, ACCESS); return FALSE; } @@ -406,6 +406,20 @@ return found; } +STATIC char ServAddr[SMBUF] = "default"; + +STATIC void +SetupVirtual() +{ + struct sockaddr_in sin; + int length; + + length = sizeof sin; + if (getsockname(STDIN, (struct sockaddr *)&sin, &length)==0) { + VIRThost(&sin.sin_addr); + strcpy(ServAddr, inet_ntoa(sin.sin_addr)); + } +} /* ** Determine access rights of the client. @@ -478,7 +492,7 @@ strncpy (LogName,ClientHost,sizeof(LogName) - 1) ; LogName[sizeof(LogName) - 1] = '\0'; - syslog(L_NOTICE, "%s connect", ClientHost); + syslog(L_NOTICE, "%s connect to %s", ClientHost, ServAddr); if (!PERMinfile(ClientHost, ClientAddr, (char *)NULL, (char *)NULL, accesslist)) { syslog(L_NOTICE, "%s no_access", ClientHost); @@ -650,6 +664,7 @@ } STATstart = TIMEINFOasDOUBLE(Now); + SetupVirtual(); if ((MyHostName = GetConfigValue(_CONF_PATHHOST)) == NULL) { syslog(L_FATAL, "cant getconfigvalue %m"); ExitWithStats(1); Index: ./nnrpd/post.c diff -u ./nnrpd/post.c:1.1.1.2 ./nnrpd/post.c:1.3 --- ./nnrpd/post.c:1.1.1.2 Wed Aug 6 14:47:57 1997 +++ ./nnrpd/post.c Wed Aug 6 15:11:47 1997 @@ -264,6 +264,7 @@ { static char MONTHS[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static char datebuff[40]; + static char pathbuff[SMBUF]; static char orgbuff[SMBUF]; static char linebuff[40]; static char tracebuff[SMBUF]; @@ -375,9 +376,15 @@ /* Set Path */ if (HDR(_path) == NULL) { - /* Note that innd will put host name here for us. */ - HDR(_path) = PATHMASTER; - } else { + if ((p = GetConfigValue("pathdefault")) != NULL) { + (void)strncpy(pathbuff, p, sizeof(pathbuff) - 1); + pathbuff[sizeof(pathbuff) - 1] = '\0'; + HDR(_path) = pathbuff; + } else { + /* Note that innd will put host name here for us. */ + HDR(_path) = PATHMASTER; + } + } else { #if 0 /* Here's where to do Path changes for new Posts. */ char *newpath; =================================================================== End of patch