/* $Id: rc.c 7438 2005-12-11 21:22:34Z eagle $
**
** Routines for the remote connect channel. Create an Internet stream
** socket that processes connect to. If the incoming site is not one of
** our feeds, then we optionally pass the connection off to the standard
** NNTP daemon.
*/
#include "config.h"
#include "clibrary.h"
#include "portable/socket.h"
#include <errno.h>
#include <netdb.h>
#include "inn/innconf.h"
#include "innd.h"
#define TEST_CONFIG(a, b) \
{ \
b = ((peer_params.Keysetbit & (1 << a)) != 0) ? true : false; \
}
#define SET_CONFIG(a) \
{ \
peer_params.Keysetbit |= (1 << a); \
}
/*
** A remote host has an address and a password.
*/
typedef struct _REMOTEHOST {
char *Label; /* Peer label */
char *Name; /* Hostname */
struct sockaddr_storage Address; /* List of ip adresses */
char *Password; /* Optional password */
char *Identd; /* Optional identd */
bool Streaming; /* Streaming allowed ? */
bool Skip; /* Skip this peer ? */
bool NoResendId; /* Don't send RESEND responses ? */
bool Nolist; /* no list command allowed */
int MaxCnx; /* Max connections (per peer) */
char **Patterns; /* List of groups allowed */
char *Pattern; /* List of groups allowed (string) */
char *Email; /* Email(s) of contact */
char *Comment; /* Commentary [max size = MAXBUFF] */
int HoldTime; /* Hold time before disconnect over MaxCnx */
int Keysetbit; /* Bit to check duplicated key */
} REMOTEHOST;
typedef struct _REMOTEHOST_DATA {
int key; /* Key (as defined in the _Keywords enum) */
int type; /* Type of the value (see _Type enum) */
char *value; /* Value */
} REMOTEHOST_DATA;
typedef struct _REMOTETABLE {
struct sockaddr_storage Address;
time_t Expires;
} REMOTETABLE;
static char *RCslaveflag;
static char *RCnnrpd = NULL;
static char *RCnntpd = NULL;
static CHANNEL **RCchan;
static int chanlimit;
static REMOTEHOST_DATA *RCpeerlistfile;
static REMOTEHOST *RCpeerlist;
static int RCnpeerlist;
static char RCbuff[BIG_BUFFER];
#define PEER "peer"
#define GROUP "group"
#define HOSTNAME "hostname:"
#define STREAMING "streaming:"
#define MAX_CONN "max-connections:"
#define PASSWORD "password:"
#define IDENTD "identd:"
#define PATTERNS "patterns:"
#define EMAIL "email:"
#define COMMENT "comment:"
#define SKIP "skip:"
#define NORESENDID "noresendid:"
#define HOLD_TIME "hold-time:"
#define NOLIST "nolist:"
typedef enum {K_END, K_BEGIN_PEER, K_BEGIN_GROUP, K_END_PEER, K_END_GROUP,
K_STREAM, K_HOSTNAME, K_MAX_CONN, K_PASSWORD, K_IDENTD,
K_EMAIL, K_PATTERNS, K_COMMENT, K_SKIP, K_NORESENDID,
K_HOLD_TIME, K_NOLIST
} _Keywords;
typedef enum {T_STRING, T_BOOLEAN, T_INTEGER} _Types;
#define GROUP_NAME "%s can't get group name in %s line %d"
#define PEER_IN_PEER "%s peer can't contain peer in %s line %d"
#define PEER_NAME "%s can't get peer name in %s line %d"
#define LEFT_BRACE "%s '{' expected in %s line %d"
#define RIGHT_BRACE "%s '}' unexpected line %d in %s"
#define INCOMPLETE_PEER "%s incomplete peer (%s) in %s line %d"
#define INCOMPLETE_GROUP "%s incomplete group (%s) in %s line %d"
#define MUST_BE_BOOL "%s Must be 'true' or 'false' in %s line %d"
#define MUST_BE_INT "%s Must be an integer value in %s line %d"
#define HOST_NEEDED "%s 'hostname' needed in %s line %d"
#define DUPLICATE_KEY "%s duplicate key in %s line %d"
/*
** Stuff needed for limiting incoming connects.
*/
static char RCterm[] = "\r\n";
static REMOTETABLE remotetable[REMOTETABLESIZE];
static int remotecount;
static int remotefirst;
/*
* Check that the client has the right identd. Return true if is the
* case, false, if not.
*/
static bool
GoodIdent(int fd, char *identd)
{
#define PORT_IDENTD 113
char IDENTuser[80];
struct sockaddr_storage ss_local;
struct sockaddr_storage ss_distant;
struct sockaddr *s_local = (struct sockaddr *)&ss_local;
struct sockaddr *s_distant = (struct sockaddr *)&ss_distant;
int ident_fd;
socklen_t len;
int port1,port2;
ssize_t lu;
char buf[80], *buf2;
if(identd[0] == '\0') {
return true;
}
len = sizeof( ss_local );
if ((getsockname(fd,s_local,&len)) < 0) {
syslog(L_ERROR, "can't do getsockname for identd");
return false;
}
len = sizeof( ss_distant );
if ((getpeername(fd,s_distant,&len)) < 0) {
syslog(L_ERROR, "can't do getsockname for identd");
return false;
}
#ifdef HAVE_INET6
if( s_local->sa_family == AF_INET6 )
{
struct sockaddr_in6 *s_l6 = (struct sockaddr_in6 *)s_local;
struct sockaddr_in6 *s_d6 = (struct sockaddr_in6 *)s_distant;
port1=ntohs(s_l6->sin6_port);
port2=ntohs(s_d6->sin6_port);
s_l6->sin6_port = 0;
s_d6->sin6_port = htons( PORT_IDENTD );
ident_fd=socket(PF_INET6, SOCK_STREAM, 0);
} else
#endif
if( s_local->sa_family == AF_INET )
{
struct sockaddr_in *s_l = (struct sockaddr_in *)s_local;
struct sockaddr_in *s_d = (struct sockaddr_in *)s_distant;
port1=ntohs(s_l->sin_port);
port2=ntohs(s_d->sin_port);
s_l->sin_port = 0;
s_d->sin_port = htons( PORT_IDENTD );
ident_fd=socket(PF_INET, SOCK_STREAM, 0);
} else
{
syslog(L_ERROR, "Bad address family: %d\n", s_local->sa_family );
return false;
}
if (ident_fd < 0) {
syslog(L_ERROR, "can't open socket for identd (%m)");
return false;
}
if (bind(ident_fd,s_local,SA_LEN(s_local)) < 0) {
syslog(L_ERROR, "can't bind socket for identd (%m)");
close(ident_fd);
return false;
}
if (connect(ident_fd,s_distant,SA_LEN(s_distant)) < 0) {
syslog(L_ERROR, "can't connect to identd (%m)");
close(ident_fd);
return false;
}
snprintf(buf,sizeof(buf),"%d,%d\r\n",port2, port1);
write(ident_fd,buf, strlen(buf));
memset( buf, 0, 80 );
lu=read(ident_fd, buf, 79); /* pas encore parfait ("not yet perfect"?) */
if (lu<0)
{
syslog(L_ERROR, "error reading from ident server: %m" );
close( ident_fd );
return false;
}
buf[lu]='\0';
if ((lu>0) && (strstr(buf,"ERROR")==NULL)
&& ((buf2=strrchr(buf,':'))!=NULL))
{
buf2++;
while(*buf2 == ' ') buf2++;
strlcpy(IDENTuser, buf2, sizeof(IDENTuser));
buf2=strchr(IDENTuser,'\r');
if (!buf2) buf2=strchr(IDENTuser,'\n');
if (buf2) *buf2='\0';
} else
strlcpy(IDENTuser, "UNKNOWN", sizeof(IDENTuser));
close(ident_fd);
return strcmp(identd, IDENTuser) == 0;
}
/*
* Split text into comma-separated fields. Return an allocated
* NULL-terminated array of the fields within the modified argument that
* the caller is expected to save or free. We don't use strchr() since
* the text is expected to be either relatively short or "comma-dense."
* (This function is different from CommaSplit because spaces are allowed
* and removed here)
*/
static char **
RCCommaSplit(char *text)
{
int i;
char *p;
char *q;
char *r;
char **av;
char **save;
/* How much space do we need? */
for (i = 2, p = text, q = r = xstrdup(text); *p; p++) {
if (*p != ' ' && *p != '\t' && *p != '\n')
*q++ = *p;
if (*p == ',')
i++;
}
*q = '\0';
free (text);
for (text = r, av = save = xmalloc(i * sizeof(char *)), *av++ = p = text; *p; )
if (*p == ',') {
*p++ = '\0';
*av++ = p;
}
else
p++;
*av = NULL;
return save;
}
/*
* Routine to disable IP-level socket options. This code was taken from 4.4BSD
* rlogind source, but all mistakes in it are my fault.
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*
* 21-Jan-1997 smd
* Code copied again, and modified for INN, all new mistakes are mine.
*
*/
/* fix_options - get rid of IP-level socket options */
#ifndef IP_OPTIONS
#define IP_OPTIONS 1
#endif
static int
RCfix_options(int fd, struct sockaddr_storage *remote)
{
#if IP_OPTIONS
unsigned char optbuf[BUFSIZ / 3], *cp;
char lbuf[BUFSIZ], *lp;
socklen_t optsize = sizeof(optbuf);
int ipproto;
struct protoent *ip;
switch (remote->ss_family) {
case AF_INET:
if ((ip = getprotobyname("ip")) != 0)
ipproto = ip->p_proto;
else
ipproto = IPPROTO_IP;
break;
#ifdef HAVE_INET6
case AF_INET6:
if ((ip = getprotobyname("ipv6")) != 0)
ipproto = ip->p_proto;
else
ipproto = IPPROTO_IPV6;
break;
#endif
default:
syslog(LOG_ERR, "unknown address family: %d", remote->ss_family);
return -1;
}
if (getsockopt(fd, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0
&& optsize != 0) {
lp = lbuf;
for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
sprintf(lp, " %2.2x", *cp);
syslog(LOG_NOTICE,
"connect from %s with IP options (ignored):%s",
sprint_sockaddr((struct sockaddr *)remote), lbuf);
if (setsockopt(fd, ipproto, IP_OPTIONS, (char *) 0, optsize) != 0) {
syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
return -1;
}
}
#endif
return 0;
}
static bool
RCaddressmatch(const struct sockaddr_storage *cp, const struct sockaddr_storage *rp)
{
#ifdef HAVE_INET6
struct sockaddr_in *sin_cp, *sin_rp;
struct sockaddr_in6 *sin6_cp, *sin6_rp;
if (cp->ss_family == AF_INET6 && rp->ss_family == AF_INET) {
sin6_cp = (struct sockaddr_in6 *)cp;
sin_rp = (struct sockaddr_in *)rp;
if (IN6_IS_ADDR_V4MAPPED(&sin6_cp->sin6_addr) &&
memcmp(&sin6_cp->sin6_addr.s6_addr[12],
&sin_rp->sin_addr.s_addr, sizeof(struct in_addr)) == 0)
return true;
} else if (cp->ss_family == AF_INET && rp->ss_family == AF_INET6) {
sin_cp = (struct sockaddr_in *)cp;
sin6_rp = (struct sockaddr_in6 *)rp;
if (IN6_IS_ADDR_V4MAPPED(&sin6_rp->sin6_addr) &&
memcmp(&sin6_rp->sin6_addr.s6_addr[12],
&sin_cp->sin_addr.s_addr, sizeof(struct in_addr)) == 0)
return true;
} else if (cp->ss_family == AF_INET6 && rp->ss_family == AF_INET6) {
#ifdef HAVE_BROKEN_IN6_ARE_ADDR_EQUAL
if (!memcmp(&((struct sockaddr_in6 *)cp)->sin6_addr,
&((struct sockaddr_in6 *)rp)->sin6_addr,
sizeof(struct in6_addr)))
#else
if (IN6_ARE_ADDR_EQUAL( &((struct sockaddr_in6 *)cp)->sin6_addr,
&((struct sockaddr_in6 *)rp)->sin6_addr))
#endif
return true;
} else
#endif /* INET6 */
if (((struct sockaddr_in *)cp)->sin_addr.s_addr ==
((struct sockaddr_in *)rp)->sin_addr.s_addr)
return true;
return false;
}
/*
** See if the site properly entered the password.
*/
bool
RCauthorized(CHANNEL *cp, char *pass)
{
REMOTEHOST *rp;
int i;
for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++)
if (RCaddressmatch(&cp->Address, &rp->Address)) {
if (rp->Password[0] == '\0' || strcmp(pass, rp->Password) == 0)
return true;
syslog(L_ERROR, "%s (%s) bad_auth", rp->Label,
sprint_sockaddr((struct sockaddr *)&cp->Address));
return false;
}
if (!AnyIncoming)
/* Not found in our table; this can't happen. */
syslog(L_ERROR, "%s not_found", sprint_sockaddr((struct sockaddr *)&cp->Address));
/* Anonymous hosts should not authenticate. */
return false;
}
/*
** See if a host is limited or not.
*/
bool
RCnolimit(CHANNEL *cp)
{
REMOTEHOST *rp;
int i;
for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++)
if (RCaddressmatch(&cp->Address, &rp->Address))
return !rp->MaxCnx;
/* Not found in our table; this can't happen. */
return false;
}
/*
** Return the limit (max number of connections) for a host.
*/
int
RClimit(CHANNEL *cp)
{
REMOTEHOST *rp;
int i;
for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++)
if (RCaddressmatch(&cp->Address, &rp->Address))
return rp->MaxCnx;
/* Not found in our table; this can't happen. */
return RemoteLimit;
}
/*
** Called when input is ready to read. Shouldn't happen.
*/
static void
RCrejectreader(CHANNEL *cp)
{
syslog(L_ERROR, "%s internal RCrejectreader (%s)", LogName,
sprint_sockaddr((struct sockaddr *)&cp->Address));
}
/*
** Write-done function for rejects.
*/
static void
RCrejectwritedone(CHANNEL *cp)
{
switch (cp->State) {
default:
syslog(L_ERROR, "%s internal RCrejectwritedone state %d",
CHANname(cp), cp->State);
break;
case CSwritegoodbye:
CHANclose(cp, CHANname(cp));
break;
}
}
/*
** Hand off a descriptor to NNRPD.
*/
void
RChandoff(int fd, HANDOFF h)
{
const char *argv[6];
char buff[SMBUF];
int i;
if (RCnnrpd == NULL)
RCnnrpd = concatpath(innconf->pathbin, "nnrpd");
if (RCnntpd == NULL)
RCnntpd = concatpath(innconf->pathbin, "nnrpd");
#if defined(SOL_SOCKET) && defined(SO_KEEPALIVE)
/* Set KEEPALIVE to catch broken socket connections. */
i = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&i, sizeof i) < 0)
syslog(L_ERROR, "fd %d cant setsockopt(KEEPALIVE) %m", fd);
#endif /* defined(SOL_SOCKET) && defined(SO_KEEPALIVE) */
if (nonblocking(fd, false) < 0)
syslog(L_ERROR, "%s cant nonblock %d in RChandoff %m", LogName, fd);
switch (h) {
default:
syslog(L_ERROR, "%s internal RChandoff %d type %d", LogName, fd, h);
/* FALLTHROUGH */
case HOnnrpd: argv[0] = RCnnrpd; break;
case HOnntpd: argv[0] = RCnntpd; break;
}
argv[1] = "-s ";
i = 2;
if (NNRPReason) {
snprintf(buff, sizeof(buff), "-r%s", NNRPReason);
argv[i++] = buff;
}
if (NNRPTracing)
argv[i++] = "-t";
if (RCslaveflag)
argv[i++] = RCslaveflag;
argv[i] = NULL;
/* Call NNRP; don't send back a QUIT message if Spawn fails since
* that's a major error we want to find out about quickly. */
(void)Spawn(innconf->nicekids, fd, fd, fd, (char * const *)argv);
}
/*
** Read function. Accept the connection and either create an NNTP channel
** or spawn an nnrpd to handle it.
*/
static void
RCreader(CHANNEL *cp)
{
int fd;
struct sockaddr_storage remote;
socklen_t size;
int i;
REMOTEHOST *rp;
CHANNEL *new;
char *name;
long reject_val = 0;
const char *reject_message;
int count;
int found;
time_t now;
CHANNEL tempchan;
char buff[SMBUF];
for (i = 0 ; i < chanlimit ; i++) {
if (RCchan[i] == cp) {
break;
}
}
if (i == chanlimit) {
syslog(L_ERROR, "%s internal RCreader wrong channel 0x%p",
LogName, (void *)cp);
return;
}
/* Get the connection. */
size = sizeof remote;
if ((fd = accept(cp->fd, (struct sockaddr *)&remote, &size)) < 0) {
if (errno != EWOULDBLOCK && errno != EAGAIN)
syslog(L_ERROR, "%s cant accept RCreader %m", LogName);
return;
}
/*
** Clear any IP_OPTIONS, including source routing, on the socket
*/
/* FIXME RCfix_options breaks IPv6 sockets, at least on Linux -lutchann */
#ifndef HAVE_INET6
if (RCfix_options(fd, &remote) != 0) {
/* shouldn't happen, but we're bit paranoid at this point */
if (close(fd) < 0)
syslog(L_ERROR, "%s cant close %d %m", LogName, fd);
return;
}
#endif
/* If RemoteTimer is not zero, then check the limits on incoming
connections on a total and per host basis.
The incoming connection table is fixed at 128 entries to make
calculating the index easy (i + 1) & 7, and to be pretty sure that you
won't run out of space. The table is used as a ring with new entries
being added to the end (wrapping around) and expired entries being
deleted from the front (again wrapping around). It is doubtful that
you will ever use even half of the table.
There are three parameters controlling the use of the table not
counting the starting index and count:
H = per host incoming connects per X seconds allowed
T = total incoming connects per X seconds allowed
X = number of seconds to remember a successful connect
First, one pass is made over the live entries deleting any that are
over X seconds old. If the entry hasn't expired, compare the incoming
connection's host address with the entry's host address. If equal,
increment the "found" counter.
Second, if the number of entries now in the table is equal to the T
parameter, reject the connection with a message indicating that the
server is overloaded.
Third, if the number of entries now in the table which match the
incoming connection's host address is equal to the H parameter, reject
the connection.
Finally, if neither rejection happened, add the entry to the table, and
continue on as a normal connect. */
memcpy(&tempchan.Address, &remote, SA_LEN((struct sockaddr *)&remote));
reject_message = NULL;
if (RemoteTimer != 0) {
now = time(NULL);
i = remotefirst;
count = remotecount;
found = 0;
while (count--) {
if (remotetable[i].Expires < now) {
remotecount--;
remotefirst = (remotefirst + 1) & (REMOTETABLESIZE - 1);
i = (i + 1) & (REMOTETABLESIZE - 1);
continue;
}
if (RCaddressmatch(&remotetable[i].Address, &remote))
found++;
i = (i + 1) & (REMOTETABLESIZE - 1);
}
if (remotecount == RemoteTotal) {
reject_val = NNTP_GOODBYE_VAL;
reject_message = "400 Server overloaded, try later";
}
else if (found >= RemoteLimit && !RCnolimit(&tempchan)) {
reject_val = NNTP_GOODBYE_VAL;
reject_message = "400 Connection rejected, you're making too"
" many connects per minute";
}
else {
i = (remotefirst + remotecount) & (REMOTETABLESIZE - 1);
memcpy(&remotetable[i].Address, &remote, SA_LEN((struct sockaddr *)&remote));
remotetable[i].Expires = now + RemoteTimer;
remotecount++;
}
}
/*
** Create a reject channel to reject the connection. This is done
** to avoid a call to fork.
*/
if (reject_message) {
new = CHANcreate(fd, CTreject, CSwritegoodbye, RCrejectreader,
RCrejectwritedone);
memcpy(&remotetable[i].Address, &remote, SA_LEN((struct sockaddr *)&remote));
new->Rejected = reject_val;
RCHANremove(new);
WCHANset(new, reject_message, (int)strlen(reject_message));
WCHANappend(new, RCterm, strlen(RCterm));
WCHANadd(new);
return;
}
/* See if it's one of our servers. */
for (name = NULL, rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++)
if (RCaddressmatch(&rp->Address, &remote)) {
name = rp->Name;
break;
}
/* If not a server, and not allowing anyone, hand him off unless
not spawning nnrpd in which case we return an error. */
if ((i >= 0) && !rp->Skip) {
/* We check now the identd if we have to */
if(! GoodIdent(fd, rp->Identd))
{
if (!innconf->noreader) {
RChandoff(fd, HOnntpd);
if (close(fd) < 0)
syslog(L_ERROR, "%s cant close %d %m", LogName, fd);
return;
}
}
if ((new = NCcreate(fd, rp->Password[0] != '\0', false)) != NULL) {
new->Streaming = rp->Streaming;
new->Skip = rp->Skip;
new->NoResendId = rp->NoResendId;
new->Nolist = rp->Nolist;
new->MaxCnx = rp->MaxCnx;
new->HoldTime = rp->HoldTime;
memcpy(&new->Address, &remote, SA_LEN((struct sockaddr *)&remote));
if (new->MaxCnx > 0 && new->HoldTime == 0) {
CHANsetActiveCnx(new);
if((new->ActiveCnx > new->MaxCnx) && (new->fd > 0)) {
snprintf(buff, sizeof(buff),
"You are limited to %d connection%s",
new->MaxCnx, (new->MaxCnx != 1) ? "s" : "");
NCwriteshutdown(new, buff);
syslog(L_NOTICE, "too many connections from %s", rp->Label);
} else {
NCwritereply(new, (char *)NCgreeting);
}
} else {
NCwritereply(new, (char *)NCgreeting);
}
}
} else if (AnyIncoming && !rp->Skip) {
if ((new = NCcreate(fd, false, false)) != NULL) {
NCwritereply(new, (char *)NCgreeting);
}
} else if (!innconf->noreader) {
RChandoff(fd, HOnntpd);
if (close(fd) < 0)
syslog(L_ERROR, "%s cant close %d %m", LogName, fd);
return;
} else {
reject_val = NNTP_ACCESS_VAL;
reject_message = NNTP_ACCESS;
new = CHANcreate(fd, CTreject, CSwritegoodbye, RCrejectreader,
RCrejectwritedone);
memcpy(&new->Address, &remote, SA_LEN((struct sockaddr *)&remote));
new->Rejected = reject_val;
RCHANremove(new);
WCHANset(new, reject_message, (int)strlen(reject_message));
WCHANappend(new, RCterm, strlen(RCterm));
WCHANadd(new);
return;
}
if (new != NULL) {
memcpy(&new->Address, &remote, SA_LEN((struct sockaddr *)&remote));
syslog(L_NOTICE, "%s connected %d streaming %s",
name ? name : sprint_sockaddr((struct sockaddr *)&new->Address),
new->fd, (!StreamingOff && new->Streaming) ? "allowed" : "not allowed");
}
}
/*
** Write-done function. Shouldn't happen.
*/
static void
RCwritedone(CHANNEL *unused)
{
unused = unused; /* ARGSUSED */
syslog(L_ERROR, "%s internal RCwritedone", LogName);
}
/*
* New config file style. Old hosts.nntp and hosts.nntp.nolimit are merged
* into one file called incoming.conf (to avoid confusion).
* See ../samples/incoming.conf for the new syntax.
*
* Fabien Tassin <fta@sofaraway.org>, 21-Dec-1997.
*/
/*
* Read something (a word or a double quoted string) from a file.
*/
static char *
RCreaddata(int *num, FILE *F, bool *toolong)
{
char *p;
char *s;
char *t;
char *word;
bool flag;
*toolong = false;
if (*RCbuff == '\0') {
if (feof (F)) return (NULL);
fgets(RCbuff, sizeof RCbuff, F);
(*num)++;
if (strlen (RCbuff) == sizeof RCbuff) {
*toolong = true;
return (NULL); /* Line too long */
}
}
p = RCbuff;
do {
/* Ignore blank and comment lines. */
if ((p = strchr(RCbuff, '\n')) != NULL)
*p = '\0';
if ((p = strchr(RCbuff, '#')) != NULL) {
if (p == RCbuff || (p > RCbuff && *(p - 1) != '\\'))
*p = '\0';
}
for (p = RCbuff; *p == ' ' || *p == '\t' ; p++);
flag = true;
if (*p == '\0' && !feof (F)) {
flag = false;
fgets(RCbuff, sizeof RCbuff, F);
(*num)++;
if (strlen (RCbuff) == sizeof RCbuff) {
*toolong = true;
return (NULL); /* Line too long */
}
continue;
}
break;
} while (!feof (F) || !flag);
if (*p == '"') { /* double quoted string ? */
p++;
do {
for (t = p; (*t != '"' || (*t == '"' && *(t - 1) == '\\')) &&
*t != '\0'; t++);
if (*t == '\0') {
*t++ = '\n';
fgets(t, sizeof RCbuff - strlen (RCbuff), F);
(*num)++;
if (strlen (RCbuff) == sizeof RCbuff) {
*toolong = true;
return (NULL); /* Line too long */
}
if ((s = strchr(t, '\n')) != NULL)
*s = '\0';
}
else
break;
} while (!feof (F));
*t++ = '\0';
}
else {
for (t = p; *t != ' ' && *t != '\t' && *t != '\0'; t++);
if (*t != '\0')
*t++ = '\0';
}
if (*p == '\0' && feof (F)) return (NULL);
word = xstrdup (p);
for (p = RCbuff; *t != '\0'; t++)
*p++ = *t;
*p = '\0';
return (word);
}
/*
* Add all data into RCpeerlistfile.
*/
static void
RCadddata(REMOTEHOST_DATA **d, int *count, int Key, int Type, char* Value)
{
(*d)[*count].key = Key;
(*d)[*count].type = Type;
(*d)[*count].value = Value;
(*count)++;
*d = xrealloc(*d, (*count + 1) * sizeof(REMOTEHOST_DATA));
}
/*
** Read in the file listing the hosts we take news from, and fill in the
** global list of their Internet addresses. A host can have multiple
** addresses, so we take care to add all of them to the list.
*/
static void
RCreadfile (REMOTEHOST_DATA **data, REMOTEHOST **list, int *count,
char *filename)
{
static char NOPASS[] = "";
static char NOIDENTD[] = "";
static char NOEMAIL[] = "";
static char NOCOMMENT[] = "";
FILE *F;
char *p;
char **q;
char **r;
#if !defined( HAVE_INET6)
struct hostent *hp;
#endif
#if !defined(HAVE_UNIX_DOMAIN_SOCKETS) || !defined(HAVE_INET6)
struct in_addr addr;
#endif
int i;
int j;
int linecount;
int infocount;
int groupcount;
int maxgroup;
REMOTEHOST_DATA *dt;
REMOTEHOST *rp;
char *word;
REMOTEHOST *groups;
REMOTEHOST *group_params = NULL;
REMOTEHOST peer_params;
REMOTEHOST default_params;
bool flag, bit, toolong;
*RCbuff = '\0';
if (*list) {
for (rp = *list, i = *count; --i >= 0; rp++) {
free(rp->Name);
free(rp->Label);
free(rp->Email);
free(rp->Comment);
free(rp->Password);
free(rp->Identd);
if (rp->Patterns) {
free(rp->Patterns[0]);
free(rp->Patterns);
}
}
free(*list);
*list = NULL;
*count = 0;
}
if (*data) {
for (i = 0; (*data)[i].key != K_END; i++)
if ((*data)[i].value != NULL)
free((*data)[i].value);
free(*data);
*data = NULL;
}
*count = 0;
maxgroup = 0;
/* Open the server file. */
if ((F = Fopen(filename, "r", TEMPORARYOPEN)) == NULL) {
syslog(L_FATAL, "%s cant read %s: %m", LogName, filename);
exit(1);
}
dt = *data = xmalloc(sizeof(REMOTEHOST_DATA));
rp = *list = xmalloc(sizeof(REMOTEHOST));
#if !defined(HAVE_UNIX_DOMAIN_SOCKETS)
addr.s_addr = INADDR_LOOPBACK;
make_sin( (struct sockaddr_in *)&rp->Address, &addr );
rp->Name = xstrdup("localhost");
rp->Label = xstrdup("localhost");
rp->Email = xstrdup(NOEMAIL);
rp->Comment = xstrdup(NOCOMMENT);
rp->Password = xstrdup(NOPASS);
rp->Identd = xstrdup(NOIDENTD);
rp->Patterns = NULL;
rp->MaxCnx = 0;
rp->Streaming = true;
rp->Skip = false;
rp->NoResendId = false;
rp->Nolist = false;
rp->HoldTime = 0;
rp++;
(*count)++;
#endif /* !defined(HAVE_UNIX_DOMAIN_SOCKETS) */
linecount = 0;
infocount = 0;
groupcount = 0; /* no group defined yet */
groups = 0;
peer_params.Label = NULL;
default_params.Streaming = true;
default_params.Skip = false;
default_params.NoResendId = false;
default_params.Nolist = false;
default_params.MaxCnx = 0;
default_params.HoldTime = 0;
default_params.Password = xstrdup(NOPASS);
default_params.Identd = xstrdup(NOIDENTD);
default_params.Email = xstrdup(NOEMAIL);
default_params.Comment = xstrdup(NOCOMMENT);
default_params.Pattern = NULL;
peer_params.Keysetbit = 0;
/* Read the file to add all the hosts. */
while ((word = RCreaddata (&linecount, F, &toolong)) != NULL) {
/* group */
if (!strncmp (word, GROUP, sizeof GROUP)) {
free(word);
/* name of the group */
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
syslog(L_ERROR, GROUP_NAME, LogName, filename, linecount);
break;
}
RCadddata(data, &infocount, K_BEGIN_GROUP, T_STRING, word);
groupcount++;
if (groupcount == 1) {
group_params = groups = xmalloc(sizeof(REMOTEHOST));
}
else if (groupcount >= maxgroup) {
/* alloc 5 groups */
groups = xrealloc(groups, (groupcount + 4) * sizeof(REMOTEHOST));
maxgroup += 5;
group_params = groups + groupcount - 1;
}
group_params->Label = word;
group_params->Skip = groupcount > 1 ?
groups[groupcount - 2].Skip : default_params.Skip;
group_params->Streaming = groupcount > 1 ?
groups[groupcount - 2].Streaming : default_params.Streaming;
group_params->NoResendId = groupcount > 1 ?
groups[groupcount - 2].NoResendId : default_params.NoResendId;
group_params->Nolist = groupcount > 1 ?
groups[groupcount - 2].Nolist : default_params.Nolist;
group_params->Email = groupcount > 1 ?
groups[groupcount - 2].Email : default_params.Email;
group_params->Comment = groupcount > 1 ?
groups[groupcount - 2].Comment : default_params.Comment;
group_params->Pattern = groupcount > 1 ?
groups[groupcount - 2].Pattern : default_params.Pattern;
group_params->Password = groupcount > 1 ?
groups[groupcount - 2].Password : default_params.Password;
group_params->Identd = groupcount > 1 ?
groups[groupcount - 2].Identd : default_params.Identd;
group_params->MaxCnx = groupcount > 1 ?
groups[groupcount - 2].MaxCnx : default_params.MaxCnx;
group_params->HoldTime = groupcount > 1 ?
groups[groupcount - 2].HoldTime : default_params.HoldTime;
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
syslog(L_ERROR, LEFT_BRACE, LogName, filename, linecount);
break;
}
/* left brace */
if (strncmp (word, "{", 1)) {
free(word);
syslog(L_ERROR, LEFT_BRACE, LogName, filename, linecount);
break;
}
else
free(word);
peer_params.Keysetbit = 0;
continue;
}
/* peer */
if (!strncmp (word, PEER, sizeof PEER)) {
free(word);
if (peer_params.Label != NULL) {
/* peer can't contain peer */
syslog(L_ERROR, PEER_IN_PEER, LogName,
filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL)
{
syslog(L_ERROR, PEER_NAME, LogName, filename, linecount);
break;
}
RCadddata(data, &infocount, K_BEGIN_PEER, T_STRING, word);
/* name of the peer */
peer_params.Label = word;
peer_params.Name = NULL;
peer_params.Skip = groupcount > 0 ?
group_params->Skip : default_params.Skip;
peer_params.Streaming = groupcount > 0 ?
group_params->Streaming : default_params.Streaming;
peer_params.NoResendId = groupcount > 0 ?
group_params->NoResendId : default_params.NoResendId;
peer_params.Nolist = groupcount > 0 ?
group_params->Nolist : default_params.Nolist;
peer_params.Email = groupcount > 0 ?
group_params->Email : default_params.Email;
peer_params.Comment = groupcount > 0 ?
group_params->Comment : default_params.Comment;
peer_params.Pattern = groupcount > 0 ?
group_params->Pattern : default_params.Pattern;
peer_params.Password = groupcount > 0 ?
group_params->Password : default_params.Password;
peer_params.Identd = groupcount > 0 ?
group_params->Identd : default_params.Identd;
peer_params.MaxCnx = groupcount > 0 ?
group_params->MaxCnx : default_params.MaxCnx;
peer_params.HoldTime = groupcount > 0 ?
group_params->HoldTime : default_params.HoldTime;
peer_params.Keysetbit = 0;
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL)
{
syslog(L_ERROR, LEFT_BRACE, LogName, filename, linecount);
break;
}
/* left brace */
if (strncmp (word, "{", 1)) {
syslog(L_ERROR, LEFT_BRACE, LogName, filename, linecount);
free(word);
break;
}
else
free(word);
continue;
}
/* right brace */
if (!strncmp (word, "}", 1)) {
free(word);
if (peer_params.Label != NULL) {
RCadddata(data, &infocount, K_END_PEER, T_STRING, NULL);
/* Hostname defaults to label if not given */
if (peer_params.Name == NULL)
peer_params.Name = xstrdup(peer_params.Label);
for(r = q = RCCommaSplit(xstrdup(peer_params.Name)); *q != NULL; q++) {
#ifdef HAVE_INET6
struct addrinfo *res, *res0, hints;
int gai_ret;
#endif
(*count)++;
/* Grow the array */
j = rp - *list;
*list = xrealloc(*list, *count * sizeof(REMOTEHOST));
rp = *list + j;
#ifdef HAVE_INET6
memset( &hints, 0, sizeof( hints ) );
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
if ((gai_ret = getaddrinfo(*q, NULL, &hints, &res0)) != 0) {
syslog(L_ERROR, "%s cant getaddrinfo %s %s", LogName, *q,
gai_strerror( gai_ret ) );
/* decrement *count, since we never got to add this record. */
(*count)--;
continue;
}
/* Count the addresses and see if we have to grow the list */
i = 0;
for (res = res0; res != NULL; res = res->ai_next)
i++;
/* Grow the array */
j = rp - *list;
*count += i - 1;
*list = xrealloc(*list, *count * sizeof(REMOTEHOST));
rp = *list + j;
/* Add all hosts */
for (res = res0; res != NULL; res = res->ai_next) {
(void)memcpy(&rp->Address, res->ai_addr, res->ai_addrlen);
rp->Name = xstrdup (*q);
rp->Label = xstrdup (peer_params.Label);
rp->Email = xstrdup(peer_params.Email);
rp->Comment = xstrdup(peer_params.Comment);
rp->Streaming = peer_params.Streaming;
rp->Skip = peer_params.Skip;
rp->NoResendId = peer_params.NoResendId;
rp->Nolist = peer_params.Nolist;
rp->Password = xstrdup(peer_params.Password);
rp->Identd = xstrdup(peer_params.Identd);
rp->Patterns = peer_params.Pattern != NULL ?
RCCommaSplit(xstrdup(peer_params.Pattern)) : NULL;
rp->MaxCnx = peer_params.MaxCnx;
rp->HoldTime = peer_params.HoldTime;
rp++;
}
freeaddrinfo(res0);
#else /* HAVE_INET6 */
/* Was host specified as a dotted quad ? */
if (inet_aton(*q, &addr)) {
make_sin( (struct sockaddr_in *)&rp->Address, &addr );
rp->Name = xstrdup (*q);
rp->Label = xstrdup (peer_params.Label);
rp->Password = xstrdup(peer_params.Password);
rp->Identd = xstrdup(peer_params.Identd);
rp->Skip = peer_params.Skip;
rp->Streaming = peer_params.Streaming;
rp->NoResendId = peer_params.NoResendId;
rp->Nolist = peer_params.Nolist;
rp->Email = xstrdup(peer_params.Email);
rp->Comment = xstrdup(peer_params.Comment);
rp->Patterns = peer_params.Pattern != NULL ?
RCCommaSplit(xstrdup(peer_params.Pattern)) : NULL;
rp->MaxCnx = peer_params.MaxCnx;
rp->HoldTime = peer_params.HoldTime;
rp++;
continue;
}
/* Host specified as a text name ? */
if ((hp = gethostbyname(*q)) == NULL) {
syslog(L_ERROR, "%s cant gethostbyname %s %m", LogName, *q);
/* decrement *count, since we never got to add this record. */
(*count)--;
continue;
}
/* Count the adresses and see if we have to grow the list */
for (i = 0; hp->h_addr_list[i]; i++)
continue;
if (i == 0) {
syslog(L_ERROR, "%s no_address %s %m", LogName, *q);
continue;
}
if (i == 1) {
char **rr;
int t = 0;
/* Strange DNS ? try this.. */
for (rr = hp->h_aliases; *rr != 0; rr++) {
if (!inet_aton(*rr, &addr))
continue;
(*count)++;
/* Grow the array */
j = rp - *list;
*list = xrealloc(*list, *count * sizeof(REMOTEHOST));
rp = *list + j;
make_sin( (struct sockaddr_in *)&rp->Address, &addr );
rp->Name = xstrdup (*q);
rp->Label = xstrdup (peer_params.Label);
rp->Email = xstrdup(peer_params.Email);
rp->Comment = xstrdup(peer_params.Comment);
rp->Streaming = peer_params.Streaming;
rp->Skip = peer_params.Skip;
rp->NoResendId = peer_params.NoResendId;
rp->Nolist = peer_params.Nolist;
rp->Password = xstrdup(peer_params.Password);
rp->Identd = xstrdup(peer_params.Identd);
rp->Patterns = peer_params.Pattern != NULL ?
RCCommaSplit(xstrdup(peer_params.Pattern)) : NULL;
rp->MaxCnx = peer_params.MaxCnx;
rp->HoldTime = peer_params.HoldTime;
rp++;
t++;
}
if (t == 0) {
/* Just one, no need to grow. */
make_sin( (struct sockaddr_in *)&rp->Address,
(struct in_addr *)hp->h_addr_list[0] );
rp->Name = xstrdup (*q);
rp->Label = xstrdup (peer_params.Label);
rp->Email = xstrdup(peer_params.Email);
rp->Comment = xstrdup(peer_params.Comment);
rp->Streaming = peer_params.Streaming;
rp->Skip = peer_params.Skip;
rp->NoResendId = peer_params.NoResendId;
rp->Nolist = peer_params.Nolist;
rp->Password = xstrdup(peer_params.Password);
rp->Identd = xstrdup(peer_params.Identd);
rp->Patterns = peer_params.Pattern != NULL ?
RCCommaSplit(xstrdup(peer_params.Pattern)) : NULL;
rp->MaxCnx = peer_params.MaxCnx;
rp->HoldTime = peer_params.HoldTime;
rp++;
continue;
}
}
/* Grow the array */
j = rp - *list;
*count += i - 1;
*list = xrealloc(*list, *count * sizeof(REMOTEHOST));
rp = *list + j;
/* Add all the hosts. */
for (i = 0; hp->h_addr_list[i]; i++) {
make_sin( (struct sockaddr_in *)&rp->Address,
(struct in_addr *)hp->h_addr_list[i] );
rp->Name = xstrdup (*q);
rp->Label = xstrdup (peer_params.Label);
rp->Email = xstrdup(peer_params.Email);
rp->Comment = xstrdup(peer_params.Comment);
rp->Streaming = peer_params.Streaming;
rp->Skip = peer_params.Skip;
rp->NoResendId = peer_params.NoResendId;
rp->Nolist = peer_params.Nolist;
rp->Password = xstrdup(peer_params.Password);
rp->Identd = xstrdup(peer_params.Identd);
rp->Patterns = peer_params.Pattern != NULL ?
RCCommaSplit(xstrdup(peer_params.Pattern)) : NULL;
rp->MaxCnx = peer_params.MaxCnx;
rp->HoldTime = peer_params.HoldTime;
rp++;
}
#endif /* HAVE_INET6 */
}
free(r[0]);
free(r);
peer_params.Label = NULL;
}
else if (groupcount > 0 && group_params->Label != NULL) {
RCadddata(data, &infocount, K_END_GROUP, T_STRING, NULL);
group_params->Label = NULL;
groupcount--;
if (groupcount == 0)
free(groups);
else
group_params--;
}
else {
syslog(L_ERROR, RIGHT_BRACE, LogName, linecount, filename);
}
continue;
}
/* streaming */
if (!strncmp (word, STREAMING, sizeof STREAMING)) {
free(word);
TEST_CONFIG(K_STREAM, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
if (!strcmp (word, "true"))
flag = true;
else
if (!strcmp (word, "false"))
flag = false;
else {
syslog(L_ERROR, MUST_BE_BOOL, LogName, filename, linecount);
break;
}
RCadddata(data, &infocount, K_STREAM, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Streaming = flag;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Streaming = flag;
else
default_params.Streaming = flag;
SET_CONFIG(K_STREAM);
continue;
}
/* skip */
if (!strncmp (word, SKIP, sizeof SKIP)) {
free(word);
TEST_CONFIG(K_SKIP, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
if (!strcmp (word, "true"))
flag = true;
else
if (!strcmp (word, "false"))
flag = false;
else {
syslog(L_ERROR, MUST_BE_BOOL, LogName, filename, linecount);
break;
}
RCadddata(data, &infocount, K_SKIP, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Skip = flag;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Skip = flag;
else
default_params.Skip = flag;
SET_CONFIG(K_SKIP);
continue;
}
/* noresendid */
if (!strncmp (word, NORESENDID, sizeof NORESENDID)) {
free(word);
TEST_CONFIG(K_NORESENDID, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
if (!strcmp (word, "true"))
flag = true;
else
if (!strcmp (word, "false"))
flag = false;
else {
syslog(L_ERROR, MUST_BE_BOOL, LogName, filename, linecount);
break;
}
RCadddata(data, &infocount, K_NORESENDID, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.NoResendId = flag;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->NoResendId = flag;
else
default_params.NoResendId = flag;
SET_CONFIG(K_NORESENDID);
continue;
}
/* nolist */
if (!strncmp (word, NOLIST, sizeof NOLIST)) {
free(word);
TEST_CONFIG(K_NOLIST, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
if (!strcmp (word, "true"))
flag = true;
else
if (!strcmp (word, "false"))
flag = false;
else {
syslog(L_ERROR, MUST_BE_BOOL, LogName, filename, linecount);
break;
}
RCadddata(data, &infocount, K_NOLIST, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Nolist = flag;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Nolist = flag;
else
default_params.Nolist = flag;
SET_CONFIG(K_NOLIST);
continue;
}
/* max-connections */
if (!strncmp (word, MAX_CONN, sizeof MAX_CONN)) {
int max;
free(word);
TEST_CONFIG(K_MAX_CONN, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_MAX_CONN, T_STRING, word);
for (p = word; CTYPE(isdigit, *p) && *p != '\0'; p++);
if (!strcmp (word, "none") || !strcmp (word, "unlimited")) {
max = 0;
} else {
if (*p != '\0') {
syslog(L_ERROR, MUST_BE_INT, LogName, filename, linecount);
break;
}
max = atoi(word);
}
if (peer_params.Label != NULL)
peer_params.MaxCnx = max;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->MaxCnx = max;
else
default_params.MaxCnx = max;
SET_CONFIG(K_MAX_CONN);
continue;
}
/* hold-time */
if (!strncmp (word, HOLD_TIME, sizeof HOLD_TIME)) {
free(word);
TEST_CONFIG(K_HOLD_TIME, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_HOLD_TIME, T_STRING, word);
for (p = word; CTYPE(isdigit, *p) && *p != '\0'; p++);
if (*p != '\0') {
syslog(L_ERROR, MUST_BE_INT, LogName, filename, linecount);
break;
}
if (peer_params.Label != NULL)
peer_params.HoldTime = atoi(word);
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->HoldTime = atoi(word);
else
default_params.HoldTime = atoi(word);
SET_CONFIG(K_HOLD_TIME);
continue;
}
/* hostname */
if (!strncmp (word, HOSTNAME, sizeof HOSTNAME)) {
free(word);
TEST_CONFIG(K_HOSTNAME, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_HOSTNAME, T_STRING, word);
peer_params.Name = word;
SET_CONFIG(K_HOSTNAME);
continue;
}
/* password */
if (!strncmp (word, PASSWORD, sizeof PASSWORD)) {
free(word);
TEST_CONFIG(K_PASSWORD, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_PASSWORD, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Password = word;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Password = word;
else
default_params.Password = word;
SET_CONFIG(K_PASSWORD);
continue;
}
/* identd */
if (!strncmp (word, IDENTD, sizeof IDENTD)) {
free(word);
TEST_CONFIG(K_IDENTD, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_IDENTD, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Identd = word;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Identd = word;
else
default_params.Identd = word;
SET_CONFIG(K_IDENTD);
continue;
}
/* patterns */
if (!strncmp (word, PATTERNS, sizeof PATTERNS)) {
TEST_CONFIG(K_PATTERNS, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
free(word);
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_PATTERNS, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Pattern = word;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Pattern = word;
else
default_params.Pattern = word;
SET_CONFIG(K_PATTERNS);
continue;
}
/* email */
if (!strncmp (word, EMAIL, sizeof EMAIL)) {
free(word);
TEST_CONFIG(K_EMAIL, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_EMAIL, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Email = word;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Email = word;
else
default_params.Email = word;
SET_CONFIG(K_EMAIL);
continue;
}
/* comment */
if (!strncmp (word, COMMENT, sizeof COMMENT)) {
free(word);
TEST_CONFIG(K_COMMENT, bit);
if (bit) {
syslog(L_ERROR, DUPLICATE_KEY, LogName, filename, linecount);
break;
}
if ((word = RCreaddata (&linecount, F, &toolong)) == NULL) {
break;
}
RCadddata(data, &infocount, K_COMMENT, T_STRING, word);
if (peer_params.Label != NULL)
peer_params.Comment = word;
else
if (groupcount > 0 && group_params->Label != NULL)
group_params->Comment = word;
else
default_params.Comment = word;
SET_CONFIG(K_COMMENT);
continue;
}
if (toolong)
syslog(L_ERROR, "%s line too long at %d: %s",
LogName, --linecount, filename);
else
syslog(L_ERROR, "%s Unknown value line %d: %s",
LogName, linecount, filename);
free(word);
break;
}
free(default_params.Email);
free(default_params.Comment);
RCadddata(data, &infocount, K_END, T_STRING, NULL);
if (feof (F)) {
if (peer_params.Label != NULL)
syslog(L_ERROR, INCOMPLETE_PEER, LogName, peer_params.Label,
filename, linecount);
if (groupcount > 0 && group_params->Label != NULL)
syslog(L_ERROR, INCOMPLETE_GROUP, LogName, group_params->Label,
filename, linecount);
}
else
syslog(L_ERROR, "%s Syntax error in %s at or before line %d", LogName,
filename, linecount);
if (Fclose(F) == EOF)
syslog(L_ERROR, "%s cant fclose %s %m", LogName, filename);
free(default_params.Password);
free(default_params.Identd);
}
/*
** Indent a line with 3 * c blanks.
** Used by RCwritelist().
*/
static void
RCwritelistindent(FILE *F, int c)
{
int i;
for (i = 0; i < c; i++)
fprintf(F, " ");
}
/*
** Add double quotes around a string, if needed.
** Used by RCwritelist().
*/
static void
RCwritelistvalue(FILE *F, char *value)
{
if (*value == '\0' || strchr (value, '\n') ||
strchr (value, ' ') || strchr (value, '\t'))
fprintf(F, "\"%s\"", value);
else
fprintf(F, "%s", value);
}
/*
** Write the incoming configuration (memory->disk)
*/
static void UNUSED
RCwritelist(char *filename)
{
FILE *F;
int i;
int inc;
char *p;
char *q;
char *r;
if ((F = Fopen(filename, "w", TEMPORARYOPEN)) == NULL) {
syslog(L_FATAL, "%s cant write %s: %m", LogName, filename);
return;
}
/* Write a standard header.. */
/* Find the filename */
p = concatpath(innconf->pathetc, _PATH_INNDHOSTS);
for (r = q = p; *p; p++)
if (*p == '/')
q = p + 1;
fprintf (F, "## $Revision: 7438 $\n");
fprintf (F, "## %s - names and addresses that feed us news\n", q);
free(r);
fprintf (F, "##\n\n");
/* ... */
inc = 0;
for (i = 0; RCpeerlistfile[i].key != K_END; i++) {
switch (RCpeerlistfile[i].key) {
case K_BEGIN_PEER:
fputc ('\n', F);
RCwritelistindent (F, inc);
fprintf(F, "%s %s {\n", PEER, RCpeerlistfile[i].value);
inc++;
break;
case K_BEGIN_GROUP:
fputc ('\n', F);
RCwritelistindent (F, inc);
fprintf(F, "%s %s {\n", GROUP, RCpeerlistfile[i].value);
inc++;
break;
case K_END_PEER:
case K_END_GROUP:
inc--;
RCwritelistindent (F, inc);
fprintf(F, "}\n");
break;
case K_STREAM:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", STREAMING);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_SKIP:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", SKIP);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_NORESENDID:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", NORESENDID);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_NOLIST:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", NOLIST);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_HOSTNAME:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", HOSTNAME);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_MAX_CONN:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", MAX_CONN);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_HOLD_TIME:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", HOLD_TIME);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_PASSWORD:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", PASSWORD);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_IDENTD:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", IDENTD);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_EMAIL:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", EMAIL);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_PATTERNS:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", PATTERNS);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
case K_COMMENT:
RCwritelistindent (F, inc);
fprintf(F, "%s\t", COMMENT);
RCwritelistvalue (F, RCpeerlistfile[i].value);
fputc ('\n', F);
break;
default:
fprintf(F, "# ***ERROR***\n");
}
}
if (Fclose(F) == EOF)
syslog(L_ERROR, "%s cant fclose %s %m", LogName, filename);
}
void
RCreadlist(void)
{
static char *INNDHOSTS = NULL;
if (INNDHOSTS == NULL)
INNDHOSTS = concatpath(innconf->pathetc, _PATH_INNDHOSTS);
StreamingOff = false;
RCreadfile(&RCpeerlistfile, &RCpeerlist, &RCnpeerlist, INNDHOSTS);
/* RCwritelist("/tmp/incoming.conf.new"); */
}
/*
** Find the name of a remote host we've connected to.
*/
char *
RChostname(const CHANNEL *cp)
{
static char buff[SMBUF];
REMOTEHOST *rp;
int i;
for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++)
if (RCaddressmatch(&cp->Address, &rp->Address))
return rp->Name;
strlcpy(buff, sprint_sockaddr((struct sockaddr *)&cp->Address),
sizeof(buff));
return buff;
}
/*
** Find the label name of a remote host we've connected to.
*/
char *
RClabelname(CHANNEL *cp) {
REMOTEHOST *rp;
int i;
for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) {
if (RCaddressmatch(&cp->Address, &rp->Address))
return rp->Label;
}
return NULL;
}
/*
** Is the remote site allowed to post to this group?
*/
int
RCcanpost(CHANNEL *cp, char *group)
{
REMOTEHOST *rp;
char match;
char subvalue;
char **argv;
char *pat;
int i;
/* Connections from lc.c are from local nnrpd and should always work */
if (cp->Address.ss_family == 0)
return 1;
for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) {
if (!RCaddressmatch(&cp->Address, &rp->Address))
continue;
if (rp->Patterns == NULL)
break;
for (match = 0, argv = rp->Patterns; (pat = *argv++) != NULL; ) {
subvalue = (*pat != SUB_NEGATE) && (*pat != SUB_POISON) ?
0 : *pat;
if (subvalue)
pat++;
if ((match != subvalue) && uwildmat(group, pat)) {
if (subvalue == SUB_POISON)
return -1;
match = subvalue;
}
}
return !match;
}
return 1;
}
/*
** Create the channel.
*/
void
RCsetup(int i)
{
#if defined(SO_REUSEADDR)
int on;
#endif /* defined(SO_REUSEADDR) */
int j;
CHANNEL *rcchan;
/* This code is called only when inndstart is not being used */
if (i < 0) {
#ifdef HAVE_INET6
syslog(L_FATAL, "%s innd MUST be started with inndstart", LogName);
exit(1);
#else
/* Create a socket and name it. */
struct sockaddr_in server;
if ((i = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
syslog(L_FATAL, "%s cant socket RCreader %m", LogName);
exit(1);
}
#if defined(SO_REUSEADDR)
on = 1;
if (setsockopt(i, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
syslog(L_ERROR, "%s cant setsockopt RCreader %m", LogName);
#endif /* defined(SO_REUSEADDR) */
memset(&server, 0, sizeof server);
server.sin_port = htons(innconf->port);
server.sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_LEN
server.sin_len = sizeof( struct sockaddr_in );
#endif
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (innconf->bindaddress) {
if (!inet_aton(innconf->bindaddress, &server.sin_addr)) {
syslog(L_FATAL, "unable to determine bind ip (%s) %m",
innconf->bindaddress);
exit(1);
}
}
if (bind(i, (struct sockaddr *)&server, sizeof server) < 0) {
syslog(L_FATAL, "%s cant bind RCreader %m", LogName);
exit(1);
}
#endif /* HAVE_INET6 */
}
/* Set it up to wait for connections. */
if (listen(i, MAXLISTEN) < 0) {
j = errno;
syslog(L_FATAL, "%s cant listen RCreader %m", LogName);
/* some IPv6 systems already listening on any address will
return EADDRINUSE when trying to listen on the IPv4 socket */
if (j == EADDRINUSE)
return;
exit(1);
}
rcchan = CHANcreate(i, CTremconn, CSwaiting, RCreader, RCwritedone);
syslog(L_NOTICE, "%s rcsetup %s", LogName, CHANname(rcchan));
RCHANadd(rcchan);
for (j = 0 ; j < chanlimit ; j++ ) {
if (RCchan[j] == NULL) {
break;
}
}
if (j < chanlimit) {
RCchan[j] = rcchan;
} else if (chanlimit == 0) {
/* assuming two file descriptors(AF_INET and AF_INET6) */
chanlimit = 2;
RCchan = xmalloc(chanlimit * sizeof(CHANNEL **));
for (j = 0 ; j < chanlimit ; j++ ) {
RCchan[j] = NULL;
}
RCchan[0] = rcchan;
} else {
/* extend to double size */
RCchan = xrealloc(RCchan, chanlimit * 2 * sizeof(CHANNEL **));
for (j = chanlimit ; j < chanlimit * 2 ; j++ ) {
RCchan[j] = NULL;
}
RCchan[chanlimit] = rcchan;
chanlimit *= 2;
}
/* Get the list of hosts we handle. */
RCreadlist();
}
/*
** Cleanly shut down the channel.
*/
void
RCclose(void)
{
REMOTEHOST *rp;
int i;
for (i = 0 ; i < chanlimit ; i++) {
if (RCchan[i] != NULL) {
CHANclose(RCchan[i], CHANname(RCchan[i]));
} else {
break;
}
}
if (chanlimit != 0)
free(RCchan);
RCchan = NULL;
chanlimit = 0;
if (RCpeerlist) {
for (rp = RCpeerlist, i = RCnpeerlist; --i >= 0; rp++) {
free(rp->Name);
free(rp->Label);
free(rp->Email);
free(rp->Password);
free(rp->Identd);
free(rp->Comment);
if (rp->Patterns) {
free(rp->Patterns[0]);
free(rp->Patterns);
}
}
free(RCpeerlist);
RCpeerlist = NULL;
RCnpeerlist = 0;
}
if (RCpeerlistfile) {
for (i = 0; RCpeerlistfile[i].key != K_END; i++)
if (RCpeerlistfile[i].value != NULL)
free(RCpeerlistfile[i].value);
free(RCpeerlistfile);
RCpeerlistfile = NULL;
}
}
syntax highlighted by Code2HTML, v. 0.9.1