/*
nntpd -- the NNTP server
Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
22646949.
Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
and Randolf Skerka <Randolf.Skerka@gmx.de>.
Copyright of the modifications 1997.
Modified by Kent Robotti <robotti@erols.com>. Copyright of the
modifications 1998.
Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
Copyright of the modifications 1998.
Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
and Kazushi (Jam) Marukawa <jam@pobox.com>.
Copyright of the modifications 1998, 1999.
Modified by Matthias Andree <matthias.andree@gmx.de>
Copyright of the modifications 2000 - 2002.
Modified by Ralf Wildenhues <ralf.wildenhues@gmx.de>
Copyright of the modifications 2002.
Modified by Jonathan Larmour <jifl@jifvik.org>
Copyright of the modifications 2002.
See file COPYING for restrictions on the use of this software.
*/
#include "leafnode.h"
#include "masock.h"
#include "mastring.h"
#include "validatefqdn.h"
#include "strlcpy.h"
#include "ln_log.h"
#include "nntpd.h"
#ifdef SOCKS
#include <socks.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#ifndef __LCLINT__
#include <arpa/inet.h>
#endif
#include <ctype.h>
#include "system.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <syslog.h>
#include <unistd.h>
#include <utime.h>
#define MAXLINELENGTH 1000
#ifdef HAVE_IPV6
/*
* * local union struct
*/
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
};
#endif
/*@null@*/ static struct newsgroup *group; /* current group, initially none */
/*@null@*/ static struct newsgroup *xovergroup = NULL;
static unsigned long artno; /* current article number */
/*@dependent@*/ /*@null@*/ static char *cmd; /* current command line */
static time_t activetime;
int debug = 0;
int verbose = 0; /* verbose doesn't count here */
static void
fatal_write(void)
{
/*@observer@*/ static const char *const e = "Write error on stdout.";
syslog(LOG_CRIT, "%s", e);
fprintf(stderr, "%s\n", e);
exit(EXIT_FAILURE);
}
static void
rereadactive(void)
{
struct stat st;
char s[SIZE_s+1];
(void)xsnprintf(s, SIZE_s, "%s/leaf.node/groupinfo", spooldir);
if ((!stat(s, &st) && (st.st_mtime > activetime)) || (active == NULL)) {
char *grouptmp = NULL;
if (debugmode)
syslog(LOG_DEBUG, "rereading %s", s);
if (group) {
grouptmp = critstrdup(group->name, "rereadactive");
}
readactive();
if (activesize == 0)
fakeactive();
else
activetime = st.st_mtime;
if (grouptmp) {
group = findgroup(grouptmp);
xovergroup = NULL;
free(grouptmp);
}
}
}
/*
* pseudo article stuff
*/
/* build and return an open fd to pseudoart in group */
static FILE *
buildpseudoart(const char *grp)
{
FILE *f;
int fd;
mastr *n = mastr_new(PATH_MAX);
(void)mastr_vcat(n, spooldir, "/temp.files/pseudo.XXXXXXXXXX", NULL);
fd = safe_mkstemp(mastr_modifyable_str(n));
if (fd < 0) {
syslog(LOG_ERR, "Could not create pseudoarticle: mkstemp failed: %m");
mastr_delete(n);
return NULL;
}
(void)unlink(mastr_str(n));
f = fdopen(fd, "w+b");
if (f == NULL) {
syslog(LOG_ERR, "Could not create pseudoarticle: fdopen failed: %m");
(void)close(fd);
mastr_delete(n);
return NULL;
}
fprintf(f, "Path: %s\n", fqdn);
fprintf(f, "Newsgroups: %s\n", grp);
fprintf(f, "From: Leafnode <%s>\n", newsadmin);
fprintf(f, "Subject: Leafnode placeholder for group %s\n", grp);
fprintf(f, "Date: %s\n", rfctime());
fprintf(f, "Message-ID: <leafnode:placeholder:%s@%s>\n", grp, fqdn);
fprintf(f, "\n");
fprintf(f,
"This server is running leafnode, which is a dynamic NNTP proxy.\n"
"This means that it does not retrieve newsgroups unless someone is\n"
"actively reading them.\n"
"\n"
"If you do an operation on a group - such as reading an article,\n"
"looking at the group table of contents or similar, then leafnode\n"
"will go and fetch articles from that group when it next updates.\n\n");
fprintf(f,
"Since you have read this dummy article, leafnode will retrieve\n"
"the newsgroup %s when fetchnews is run\n"
"the next time. If you'll look into this group a little later, you\n"
"will see real articles.\n\n", grp);
fprintf(f,
"If you see articles in groups you do not read, that is almost\n"
"always because of cross-posting. These articles do not occupy any\n"
"more space - they are hard-linked into each newsgroup directory.\n"
"\n"
"If you do not understand this, please talk to your newsmaster.\n"
"\n"
"Leafnode can be found at\n" "\thttp://www.leafnode.org/\n\n");
if (ferror(f)) {
(void)fclose(f);
f = NULL;
} else {
rewind(f);
}
mastr_delete(n);
return f;
}
static int
parserange(char *arg, unsigned long *a, unsigned long *b)
{
char *l = arg;
/* no argument */
if (!*l) return 0;
/* parse */
if (*l != '-')
*a = strtoul(arg, &l, 10);
SKIPLWS(l);
if (*l == '-') {
++l;
SKIPLWS(l);
if (isdigit((unsigned char)*l))
*b = strtoul(l, &l, 10);
} else {
*b = *a;
}
SKIPLWS(l);
/* trailing garbage */
if (l && *l) {
return 0;
}
return 1;
}
static int
is_pseudogroup(/*@null@*/ const struct newsgroup *g)
{
if (!g) return 0;
if (!chdirgroup(g->name, FALSE)) return 1;
/* if (isinteresting(g->name)) return 0; */
if (g->last < g->first) return 1;
if (g->last <= 1) return 1;
return 0;
}
/*
* XXX FIXME: an option is needed if the administrator
* locks the interesting.group permissions in order to have a fixed set
* of subscriptions that are always available, or if the subscription
* is handled outside leafnode.
*/
static void
markinterest(const char *ng)
{
struct stat st;
struct utimbuf buf;
int err;
time_t now;
FILE *f;
char s[SIZE_s+1];
/* OK, the user may be setting the file to root owned with group
* write permission. This is a problem when trying to set the ctime
* without setting the mtime, because we would need privileges to do
* that. Instead, we'll bump both times and warn the user. */
err = 0;
(void)xsnprintf(s, SIZE_s, "%s/interesting.groups/%s", spooldir, ng);
if (stat(s, &st) == 0) {
const char *touched = "ctime";
/* group was read before: keep mtime, but bump up ctime */
now = time(NULL);
buf.actime = (now < st.st_atime) ? st.st_atime : now;
/* now < update may happen through HW failures */
buf.modtime = st.st_mtime;
if (utime(s, &buf)) {
int oe = errno;
/* cannot set time - the file's user-id must be the NEWS_USER's
* 2nd best - we'll try to bump both times, but this means
* timeout_short applies, so warn the user. */
if (utime(s, NULL)) {
/* ok, didn't work either. complain and give up.
* We used to fall through to the code below, but that
* may truncate the file and thus lose delaybody article
* numbers. */
syslog(LOG_ERR, "Error: cannot set ctime/mtime timestamp of %s: %s.",
s, strerror(errno));
return;
} else {
/* complain that timeout_short applies, so that the user
* is not surprised by premature group unsubscription. */
touched = "ctime and mtime";
if (timeout_short < timeout_long) {
syslog(LOG_WARNING, "Warning: cannot set the ctime timestamp of %s without resetting mtime (%s). "
"This means that timeout_short will apply rather than timeout_long. "
"Either reset file ownership to " NEWS_USER " or set timeout_short and timeout_long to the same value to suppress this warning.",
s, strerror(oe));
}
}
}
if (debugmode && !err)
syslog(LOG_DEBUG, "markinterest: %s touched %s", ng, touched);
} else {
err = 1;
}
if (err) {
f = fopen(s, "w"); /* truncating sets ctime and mtime */
if (f == NULL || fclose(f) == EOF) {
syslog(LOG_ERR, "Could not create %s: %m", s);
} else {
if (debugmode)
syslog(LOG_DEBUG, "markinterest: %s new", ng);
}
} else {
int fd = open(".", O_RDONLY);
(void)chdirgroup(ng, FALSE);
if (fd >= 0) {
(void)fchdir(fd);
(void)close(fd);
}
}
}
/* open a pseudo art */
/* WARNING: article_num MUST be 0 for selection based on Message-ID */
static FILE *
fopenpseudoart(const char *arg, const unsigned long article_num)
{
FILE *f = NULL;
char *c;
struct newsgroup *g;
if (group && (article_num && ((article_num == group->first &&
group->first >= group->last) || is_pseudogroup(group)))) {
f = buildpseudoart(group->name);
} else if (!article_num) {
if (!strncmp(arg, "<leafnode:placeholder:", 22)) {
mastr *msgidbuf = mastr_new(1024);
(void)mastr_cpy(msgidbuf, arg + 22);
if ((c = strchr(mastr_modifyable_str(msgidbuf), '@')) != NULL) {
*c++ = '\0';
(void)strtok(c, ">");
if (0 == strcasecmp(c, fqdn)) {
g = findgroup(mastr_str(msgidbuf));
if (g && (g->last <= 1 || g->first >= g->last)) {
markinterest(g->name);
f = buildpseudoart(g->name);
}
}
}
mastr_delete(msgidbuf);
}
}
return f;
}
/* open an article by number or message-id */
static FILE *
fopenart(const char *arg)
{
unsigned long int a;
FILE *f;
char *t;
struct stat st;
/*@temp@*/ char buf[32];
t = NULL;
a = strtoul(arg, &t, 10);
if (arg && *arg == '<') {
/* message ID given */
f = fopen(lookup(arg), "r");
if (!f) f = fopenpseudoart(arg, 0);
} else if (t && !*t && group != NULL) {
const char *ptr = arg;
/* number not given -> take current article pointer */
if (!a) {
a = artno;
sprintf(buf, "%lu", artno); /* RATS: ignore */
ptr = buf;
}
if (is_pseudogroup(group)) {
f = fopenpseudoart(ptr, a);
} else {
f = fopen(ptr, "r");
}
if (f != NULL)
artno = a;
markinterest(group->name);
} else {
f = NULL;
}
if (f != NULL && (fstat(fileno(f), &st) || st.st_size == 0)) {
(void)fclose(f);
f = NULL;
}
return f;
}
/*
* Mark an article for download by appending its number to the
* corresponding file in interesting.groups
*/
static int
markdownload(const char *ng, unsigned long id)
{
int i, e = 0;
unsigned long n;
FILE *f;
char *t;
char s[SIZE_s+1];
(void)xsnprintf(s, SIZE_s, "%s/interesting.groups/%s", spooldir, ng);
if ((f = fopen(s, "r+"))) {
i = 0;
while ((t = getaline(f))) {
if (sscanf(t, "%lu", &n) == 1 && n == id) {
(void)fclose(f); /* we only read from the file */
return 0; /* already marked */
}
if (ferror(f))
e = errno;
++i;
}
if (i < BODY_DOWNLOAD_LIMIT) {
(void)fprintf(f, "%lu\n", id);
if (ferror(f))
e = errno;
if (debugmode)
syslog(LOG_DEBUG, "Marking %s %lu for download", ng, id);
} else {
syslog(LOG_ERR, "Too many bodies marked in %s", ng);
}
if (fclose(f))
e = errno;
}
if (e) {
syslog(LOG_ERR, "I/O error handling \"%s\": %s", s, strerror(e));
return -1;
}
return 1;
}
static void
nogroup(void)
{
printf("412 Use the GROUP command first\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">412 Use the GROUP command first");
return;
}
/* display an article or somesuch */
/* DOARTICLE */
static void
doarticle(const char *arg, int what)
{
FILE *f;
char *p = NULL;
char *q = NULL;
char *t;
unsigned long localartno;
unsigned long replyartno;
char *localmsgid, *xref = NULL;
char *markgroup = NULL;
char s[SIZE_s+1];
f = fopenart(arg);
if (!f) {
if (arg && *arg != '<' && !group) {
nogroup();
} else if (strlen(arg)) {
printf("430 No such article: %s\r\n", arg);
if (debugmode)
syslog(LOG_DEBUG, ">430 No such article: %s", arg);
} else {
printf("423 No such article: %lu\r\n", artno);
if (debugmode)
syslog(LOG_DEBUG, ">423 No such article: %lu", artno);
}
return;
}
if (!*arg) {
/* no. implicit */
localartno = artno;
localmsgid = fgetheader(f, "Message-ID:");
} else if (*arg == '<') {
/* message-id -- do not modify artno */
localartno = 0;
localmsgid = critstrdup(arg, "doarticle");
} else {
/* no. explicit */
localartno = strtoul(arg, NULL, 10);
localmsgid = fgetheader(f, "Message-ID:");
}
replyartno = localartno;
if (!localartno) {
/* we have to extract the article number and the newsgroup from
* the Xref: header */
xref = fgetheader(f, "Xref:");
p = xref;
if (p) {
/* skip host name */
while (*p && !isspace((unsigned char)*p)) {
p++;
}
while (isspace((unsigned char)*p)) {
p++;
}
}
if (p) {
/* search article number of current group in Xref: */
if (group) {
while ((q = strstr(p, group->name)) != NULL) {
q += strlen(group->name);
if (*q++ == ':') {
localartno = strtoul(q, NULL, 10);
markgroup = group->name;
break;
}
p = q + strcspn(q, " \t");
}
}
/* if we don't have a localartno, then we need to mark this
* article in a different news group */
if (!localartno) {
char *r = p;
while ((q = strchr(r, ':'))) {
*q++ = '\0';
if (isinteresting(p)) {
/* got one we can mark */
markgroup = r;
localartno = strtoul(q, NULL, 10);
break;
}
while (isdigit((unsigned char)*q)) {
q++;
}
while (isspace((unsigned char)*q)) {
q++;
}
r = q;
}
}
}
} else if (group) {
markgroup = group->name;
}
if (!localmsgid) {
const char *tmp = "423 Corrupt article.";
printf("%s\r\n", tmp);
syslog(LOG_WARNING, ">%s", tmp);
if (replyartno) {
(void)xsnprintf(s, SIZE_s, "%lu", replyartno);
(void)log_unlink(s, 0);
}
(void)fclose(f);
if (xref)
free(xref);
return;
}
(void)xsnprintf(s, SIZE_s - 24, "%3d %lu %s article retrieved - ",
223 - what, replyartno, localmsgid);
free(localmsgid);
if (what == 0)
strcat(s, "request text separately");
else if (what == 1)
strcat(s, "body follows");
else if (what == 2)
strcat(s, "head follows");
else
strcat(s, "text follows");
printf("%s\r\n", s);
if (debugmode)
syslog(LOG_DEBUG, ">%s", s);
while ((t = getaline(f)) && *t) {
if (what & 2) {
if (*t == '.')
(void)fputc('.', stdout);
(void)fputs(t, stdout);
(void)fputs("\r\n", stdout);
if (ferror(stdout))
fatal_write();
}
}
/* Matthias Andree, 2002-03-05:
* t == NULL or *t == '\0' makes a difference here.
* - t == NULL means we ran into EOF and don't have a body in delaybody mode.
* - t != NULL but *t == '\0' means we just read the empty separator line between header and body.
*/
if (what == 3)
printf("\r\n"); /* empty separator line */
if (what & 1) {
/* delaybody:
t == NULL: body is missing, mark for download
t != NULL: body is present */
if (t == NULL) {
if (localartno && markgroup != NULL) {
switch (markdownload(markgroup, localartno)) {
case 0:
printf("\r\n\r\n"
"\t[ Leafnode: ]\r\n"
"\t[ This message has already been "
"marked for download. ]\r\n");
break;
case 1:
printf("\r\n\r\n"
"\t[ Leafnode: ]\r\n"
"\t[ Message %lu of %s ]\r\n"
"\t[ has been marked for download. ]\r\n",
localartno, markgroup);
break;
default:
printf("\r\n\r\n"
"\t[ Leafnode: ]\r\n"
"\t[ Message %lu of %s ]\r\n"
"\t[ cannot be marked for download. ]\r\n"
"\t[ (Check the server's syslog "
"for information). ]\r\n", localartno, markgroup);
break;
}
} else {
/* did not figure a group for which to mark this article */
syslog(LOG_ERR,
"cannot mark for body download: arg=\"%s\" "
"localartno=%lu markgroup=\"%s\" group=\"%s\"",
arg, localartno, markgroup ? markgroup : "(null)",
group ? group->name : "(null)");
printf("\r\n\r\n"
"\t[ Leafnode: ]\r\n"
"\t[ I cannot figure out a newsgroup for which to download ]\r\n"
"\t[ this article. Please report this condition to the ]\r\n"
"\t[ leafnode mailing list, with leafnode version, the name ]\r\n"
"\t[ and the version of your news reader and a log excerpt. ]\r\n");
}
} else { /* immediate body */
while ((t = getaline(f))) {
if (*t == '.')
(void)fputc('.', stdout);
(void)fputs(t, stdout);
(void)fputs("\r\n", stdout);
if (ferror(stdout))
fatal_write();
}
}
}
if (what)
printf(".\r\n");
(void)fclose(f);
if (xref)
free(xref);
return; /* OF COURSE there were no errors */
}
/* change to group - note no checks on group name */
static int
dogroup(const char *arg)
{
struct newsgroup *g;
g = findgroup(arg);
if (g) {
group = g; /* global */
if (isinteresting(arg)) {
if (debugmode)
syslog(LOG_DEBUG, "marked group %s interesting", arg);
markinterest(arg);
}
if (!is_pseudogroup(g)) {
/* regular news group */
if (debugmode)
syslog(LOG_DEBUG, ">211 %lu %lu %lu %s group selected",
g->last >= g->first ? g->last - g->first + 1 : 0,
g->first, g->last, g->name);
printf("211 %lu %lu %lu %s group selected\r\n",
g->last >= g->first ? g->last - g->first + 1 : 0,
g->first, g->last, g->name);
} else {
/* pseudo news group */
if (debugmode)
syslog(LOG_DEBUG,
">211 %lu %lu %lu %s group selected (pseudo article)",
1lu, g->first, g->first, g->name);
printf("211 %lu %lu %lu %s group selected (pseudo article)\r\n",
1lu, g->first, g->first, g->name);
}
artno = g->first;
} else {
if (debugmode)
syslog(LOG_DEBUG, ">411 No such group");
printf("411 No such group\r\n");
}
if (fflush(stdout)) return -1;
return 0;
}
static void
dohelp(void)
{
fputs("100 Legal commands on THIS server:\r\n"
" ARTICLE [<Message-ID>|<Number>]\r\n"
" BODY [<Message-ID>|<Number>]\r\n"
" DATE\r\n"
" GROUP <Newsgroup>\r\n"
" HDR <Header> <Message-ID>|<Range>\r\n"
" HEAD [<Message-ID>|<Number>]\r\n"
" HELP\r\n"
" LAST\r\n"
" LIST [ACTIVE|NEWSGROUPS] [<Wildmat>]]\r\n"
" LIST [ACTIVE.TIMES|EXTENSIONS|OVERVIEW.FMT]\r\n"
" LISTGROUP <Newsgroup>\r\n"
" MODE READER\r\n"
" NEWGROUPS <yymmdd> <hhmmss> [GMT]\r\n"
" NEXT\r\n"
" POST\r\n"
" OVER <Range>\r\n"
" SLAVE\r\n"
" STAT [<Message-ID>|<Number>]\r\n"
" XHDR <Header> <Message-ID>|<Range>\r\n"
" XOVER <Range>\r\n"
".\r\n", stdout);
}
static void
domove(int by)
{
char *msgid;
char s[SIZE_s+1];
by = (by < 0) ? -1 : 1;
if (group) {
if (artno) {
artno += by;
do {
sprintf(s, "%lu", artno);
msgid = getheader(s, "Message-ID:");
if (!msgid)
artno += by;
} while (msgid == NULL && artno >= group->first && artno <= group->last);
if (msgid && (artno > group->last || artno < group->first)) {
free(msgid);
msgid = NULL;
}
if (msgid == NULL) {
if (by > 0) {
artno = group->last;
printf("421 There is no next article\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">421 There is no next article");
} else {
artno = group->first;
printf("422 There is no previous article\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">422 There is no previous article");
}
} else {
printf("223 %lu %s article retrieved\r\n", artno, msgid);
if (debugmode)
syslog(LOG_DEBUG, ">223 %lu %s article retrieved",
artno, msgid);
free(msgid);
}
} else {
printf("420 There is no current article\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">420 There is no current article");
}
} else {
nogroup();
}
}
static int is_pattern(const char *s) {
return s ? strcspn(s, "?*[") < strlen(s) : 1;
}
/* LIST ACTIVE if what==0,
* LIST NEWSGROUPS if what==1
* LIST ACTIVE.TIMES if what==2
*/
static void
printlist(const struct newsgroup *g, const int what)
{
switch(what) {
case 0:
if (is_pseudogroup(g))
printf("%s %lu %lu y\r\n", g->name, g->first, g->first);
else
printf("%s %lu %lu y\r\n", g->name, g->last, g->first);
break;
case 1:
printf("%s\t%s\r\n", g->name, g->desc ? g->desc : "-x-");
break;
case 2:
printf("%s %lu %s\r\n", g->name, (unsigned long)g->age, newsadmin);
break;
default:
abort();
break;
}
}
/* LIST ACTIVE if what==0,
* LIST NEWSGROUPS if what==1
* LIST ACTIVE.TIMES if what==2
*/
static void
list(struct newsgroup *g, const int what, const char *pattern)
{
if (is_pattern(pattern)) {
while(g->name) {
if (!pattern || !ngmatch(pattern, g->name)) {
printlist(g, what);
}
g++;
}
} else {
/* single group */
g = findgroup(pattern);
if (g) {
printlist(g, what);
if (what == 0 && isinteresting(pattern))
markinterest(pattern);
}
}
}
static void
dolist(char *arg)
{
if (!strcasecmp(arg, "extensions")) {
printf("202 extensions supported follow\r\n"
"HDR\r\n"
"LISTGROUP\r\n"
"OVER\r\n"
"XHDR\r\n"
"XOVER\r\n"
".\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">202 extensions supported follow");
} else if (!strcasecmp(arg, "overview.fmt")) {
printf("215 information follows\r\n"
"Subject:\r\n"
"From:\r\n"
"Date:\r\n"
"Message-ID:\r\n"
"References:\r\n"
"Bytes:\r\n"
"Lines:\r\n"
"Xref:full\r\n"
".\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">215 information follows");
} else if (!strncasecmp(arg, "active.times", 12)) {
if (active) {
const char m[] = "215 Note that leafnode will fetch groups on demand.";
printf("%s\r\n", m);
if (debugmode)
syslog(LOG_DEBUG, ">%s", m);
list(active, 2, NULL);
printf(".\r\n");
} else {
const char e[] = "503 Active file has not been read.";
printf("%s\r\n", e);
if (debugmode)
syslog(LOG_DEBUG, ">%s", e);
}
} else {
if (!active) {
printf("503 Group information file does not exist!\r\n");
syslog(LOG_ERR, ">503 Group information file does not exist!");
} else if (!*arg || !strncasecmp(arg, "active", 6)) {
printf("215 Newsgroups in form \"group high low flags\".\r\n");
if (debugmode)
syslog(LOG_DEBUG,
">215 Newsgroups in form \"group high low flags\".");
if (active) {
if (!*arg || strlen(arg) == 6)
list(active, 0, NULL);
else {
while (*arg && (!isspace((unsigned char)*arg)))
arg++;
while (*arg && isspace((unsigned char)*arg))
arg++;
list(active, 0, arg);
}
}
printf(".\r\n");
} else if (!strncasecmp(arg, "newsgroups", 10)) {
printf("215 Descriptions in form \"group description\".\r\n");
if (debugmode)
syslog(LOG_DEBUG,
">215 Descriptions in form \"group description\".");
if (active) {
if (strlen(arg) == 10)
list(active, 1, NULL);
else {
while (*arg && (!isspace((unsigned char)*arg)))
arg++;
while (*arg && isspace((unsigned char)*arg))
arg++;
list(active, 1, arg);
}
}
printf(".\r\n");
} else {
printf("503 Syntax error\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">503 Syntax error");
}
}
}
static void
donewgroups(const char *arg)
{
struct tm timearray;
struct tm *ltime;
time_t age;
time_t now;
int year, century;
char *l;
long a;
long b;
struct newsgroup *ng;
now = time(NULL);
ltime = localtime(&now);
if (ltime == NULL) {
syslog(LOG_CRIT, "fatal: localtime returned NULL. abort.");
abort();
}
year = ltime->tm_year % 100;
century = ltime->tm_year / 100; /* 0 for 1900-1999, 1 for 2000-2099 etc */
memset(&timearray, 0, sizeof(timearray));
l = NULL;
a = (int)strtol(arg, &l, 10);
/* NEWGROUPS may have the form YYMMDD or YYYYMMDD.
Distinguish between the two */
b = a / 10000;
if (b < 100) {
/* YYMMDD */
if (b <= year)
timearray.tm_year = (int)(b + (century * 100));
else
timearray.tm_year = (int)(b + (century - 1) * 100);
} else if (b < 1000) {
/* YYYMMDD - happens with buggy newsreaders */
/* In these readers, YYY=100 is equivalent to YY=00 or YYYY=2000 */
syslog(LOG_NOTICE,
"NEWGROUPS year is %ld: please update your newsreader", b);
timearray.tm_year = (int)b;
} else {
/* YYYYMMDD */
timearray.tm_year = (int)b - 1900;
}
timearray.tm_mon = (int)(a % 10000 / 100) - 1;
timearray.tm_mday = (int)(a % 100);
while (*l && isspace((unsigned char)*l))
l++;
a = strtol(l, &l, 10); /* we don't care about the rest of the line */
while (*l && isspace((unsigned char)*l))
l++;
timearray.tm_hour = (int)(a / 10000);
timearray.tm_min = (int)(a % 10000 / 100);
timearray.tm_sec = (int)(a % 100);
/* mktime() shall guess correct value of tm_isdst (0 or 1) */
timearray.tm_isdst = -1;
if (0 == strncasecmp(l, "gmt", 3))
age = timegm(&timearray);
else
age = mktime(&timearray);
printf("231 List of new newsgroups since %ld follows\r\n", (long)age);
if (debugmode)
syslog(LOG_DEBUG, "231 List of new newsgroups since %ld follows",
(long)age);
ng = active;
if (ng != NULL)
while (ng->name) {
if (ng->age >= age)
printf("%s %lu %lu y\r\n", ng->name, ng->last, ng->first);
ng++;
}
printf(".\r\n");
}
/* next bit is copied from INN 1.4 and modified ("broken") by agulbra
mail to Rich $alz <rsalz@uunet.uu.net> bounced */
/* Scale time back a bit, for shorter Message-ID's. */
#define OFFSET (time_t)1026380000L
/*@observer@*/ static char *
generateMessageID(void)
{
static char ALPHABET[] = "0123456789abcdefghijklmnopqrstuv";
static char buff[1000];
static time_t then;
static unsigned int fudge;
time_t now;
char *p;
unsigned long n;
now = time(NULL); /* might be 0, in which case fudge
will almost fix it */
if (now < OFFSET) {
ln_log(LNLOG_SCRIT, LNLOG_CTOP,
"your system clock cannot be right. abort.");
abort();
}
if (now != then)
fudge = 0;
else
fudge++;
then = now;
p = buff;
*p++ = '<';
n = (unsigned long)now - OFFSET;
while (n) {
*p++ = ALPHABET[(int)(n & 31)];
n >>= 5;
}
*p++ = '-';
n = fudge * 32768 + (int)getpid();
while (n) {
*p++ = ALPHABET[(int)(n & 31)];
n >>= 5;
}
sprintf(p, ".ln1@%-.256s>", fqdn);
return buff;
}
/* the end of what I stole from rsalz and then mangled */
static int
dopost(void)
{
char *line;
int havefrom = 0;
int havepath = 0;
int havedate = 0;
int havenewsgroups = 0;
int havemessageid = 0;
int havesubject = 0;
int err = 0, ferr = 0;
/*@observer@*/ const char *ferrstr = NULL;
int o;
size_t len;
FILE *out;
char outname[1000];
static int postingno; /* starts as 0 */
char *suggmid;
/*@observer@*/ const char *appendheader = NULL;
if (getenv("LN_REJECT_POST_PRE")) {
printf("400 Posting rejected - debug variable LN_REJECT_POST_PRE exists\r\n");
return 0;
}
do {
(void)xsnprintf(outname, sizeof(outname), "%s/out.going/%d-%d-%d",
spooldir, (int)getpid(), (int)time(NULL), ++postingno);
o = open(outname, O_WRONLY | O_EXCL | O_CREAT, 0244);
if (o < 0 && errno != EEXIST) {
char *errmsg = strerror(errno);
printf("441 Unable to open spool file %s: %s\r\n", outname, errmsg);
syslog(LOG_ERR, ">441 Unable to open spool file %s: %s", outname,
errmsg);
return 0;
}
} while (o < 0);
out = fdopen(o, "w");
if (out == NULL) {
char *errmsg = strerror(errno);
printf("441 Unable to fdopen(%d): %s\r\n", o, errmsg);
syslog(LOG_ERR, ">441 Unable to fdopen(%d): %s", o, errmsg);
return 0;
}
suggmid = generateMessageID();
printf("340 Ok, recommended ID %s\r\n", suggmid);
if (debugmode)
syslog(LOG_DEBUG, ">340 Go ahead.");
if (fflush(stdout)) return -1;
/* get headers */
do {
debug = 0;
line = getaline(stdin);
if (!line) { /* timeout */
unlink(outname);
exit(0);
}
if (0 == strcmp(line, ".")) {
ferr = TRUE;
ferrstr = "No body found.";
break;
}
debug = debugmode;
if (!strncasecmp(line, "From: ", 6)) {
if (havefrom)
ferr = TRUE, ferrstr = "Duplicate From: header";
else
havefrom = TRUE;
}
if (!strncasecmp(line, "Path: ", 6)) {
if (havepath)
ferr = TRUE, ferrstr = "Duplicate Path: header";
else
havepath = TRUE;
}
if (!strncasecmp(line, "Message-ID: ", 12)) {
if (havemessageid)
ferr = TRUE, ferrstr = "Duplicate Message-ID: header";
else {
char *vec[2]; /* RATS: ignore */
int rc;
havemessageid = TRUE;
if (debugmode)
syslog(LOG_DEBUG, "debug header: %s", line);
if (2 != (rc = pcre_extract(line, "Message-ID:\\s+<(?:[^>]+)@([^@>]+)>\\s*$", vec, 2))
|| vec[1] == NULL) {
ferr = TRUE, ferrstr = "Malformatted Message-ID: header.";
} else if (!strchr(vec[1], '.')) {
ferr = TRUE, ferrstr = "Message-ID: header does not have domain name part.";
} else if (!is_validfqdn(vec[1])) {
ferr = TRUE, ferrstr = "Message-ID: header contains invalid domain name part.";
}
pcre_extract_free(vec, rc);
}
}
if (!strncasecmp(line, "Subject: ", 9)) {
if (havesubject)
ferr = TRUE, ferrstr = "Duplicate Subject: header";
else
havesubject = TRUE;
}
if (!strncasecmp(line, "Newsgroups: ", 12)) {
if (havenewsgroups)
ferr = TRUE, ferrstr = "Duplicate Newsgroups: header";
else
havenewsgroups = TRUE;
}
if (!strncasecmp(line, "Date: ", 6)) {
if (havedate)
ferr = TRUE, ferrstr = "Duplicate Date: header";
else
havedate = TRUE;
}
len = strlen(line);
/* check for illegal 8bit/control stuff in header */
{
char *t;
for (t = line; *t; t++) {
if (*t & 0x80) {
if (allow_8bit_headers) {
appendheader = "X-Leafnode-Warning: administrator "
"allowed illegal use of 8-bit data in header.\r\n";
} else {
ferr = TRUE;
ferrstr = "Illegal use of 8-bit data in header.";
break;
}
}
if ((unsigned char)*t < (unsigned char)0x20u && *t != '\t') {
ferr = TRUE;
ferrstr = "Illegal use of control data in header.";
break;
}
}
}
/* checks for non-folded lines */
if (*line && *line != ' ' && *line != '\t') {
if (strchr(line, ':') == NULL) {
/* must have a colon */
ferr = TRUE;
ferrstr = "Header tag not found.";
} else if (strcspn(line, " \t") < strcspn(line, ":")) {
/* must not have space before colon */
ferr = TRUE;
ferrstr = "Whitespace in header tag is not allowed.";
}
}
if (len) {
if (fwrite(line, 1, len, out) != (size_t) len)
err = 1;
} else {
if (!havepath) {
if (fputs("Path: ", out) == EOF)
err = 1;
if (fputs(fqdn, out) == EOF)
err = 1;
if (fprintf(out, "!%s\r\n", NEWS_USER) < 0)
err = 1;
}
if (!havedate) {
const char *l = rfctime();
if (fputs("Date: ", out) == EOF)
err = 1;
if (fputs(l, out) == EOF)
err = 1;
if (fputs("\r\n", out) == EOF)
err = 1;
}
if (!havemessageid) {
if (fputs("Message-ID: ", out) == EOF)
err = 1;
if (fputs(suggmid, out) == EOF)
err = 1;
if (fputs("\r\n", out) == EOF)
err = 1;
}
if (appendheader) {
if (fputs(appendheader, out) == EOF)
err = 1;
}
}
if (fputs("\r\n", out) == EOF)
err = 1;
} while (*line);
/* get bodies */
if (strcmp(line, "."))
do {
debug = 0;
line = getaline(stdin);
debug = debugmode;
if (!line) {
(void)unlink(outname);
exit(1);
}
len = strlen(line);
if (line[0] == '.') {
if (len > 1) {
if (fputs(line + 1, out) == EOF)
err = 1;
if (fputs("\r\n", out) == EOF)
err = 1;
}
} else {
if (fputs(line, out) == EOF)
err = 1;
if (fputs("\r\n", out) == EOF)
err = 1;
}
} while (line[0] != '.' || line[1] != '\0');
if (fflush(out))
err = 1;
if (fsync(fileno(out)))
err = 1;
if (fclose(out))
err = 1;
if (!havenewsgroups)
ferrstr = "Missing Newsgroups: header";
if (!havesubject)
ferrstr = "Missing Subject: header";
if (!havefrom)
ferrstr = "Missing From: header";
if (getenv("LN_REJECT_POST_POST"))
ferr = 1;
if (havefrom && havesubject && havenewsgroups && !ferr) {
if (!err && 0 == chmod(outname, 0644)) {
printf("240 Article posted, now be patient\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">240 Article posted, now be patient");
return 0;
} else {
(void)unlink(outname);
printf("441 I/O error, article not posted\r\n");
syslog(LOG_INFO, ">441 I/O error, article not posted");
return 0;
}
}
(void)unlink(outname);
if (getenv("LN_REJECT_POST_POST")) {
printf("400 Posting rejected - debug variable LN_REJECT_POST_POST exists\r\n");
syslog(LOG_INFO, ">400 Posting rejected - debug variable LN_REJECT_POST_POST exists\r\n");
return 0;
}
if (ferrstr) {
printf("441 Post rejected, formatting error: %s\r\n", ferrstr);
syslog(LOG_INFO, ">441 Post rejected, formatting error: %s", ferrstr);
} else {
printf("441 Post rejected, formatting error\r\n");
syslog(LOG_INFO, ">441 Post rejected, formatting error");
}
return 0;
}
static void invalidrange(void)
{
printf("420 No articles in specified range.\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">420 No articles in specified range.");
}
/* check if a - b is a valid range for the current group.
* If it's not, print a 420 error and return 0.
* If it is, do not print anything and return 1.
* group must not be NULL!
*/
static int checkrange(const struct newsgroup *g,
unsigned long a, unsigned long b)
{
if ((a > b) || (g->first <= g->last
? (a > g->last) || (b < g->first)
: (a > g->first) || (b < g->first))) {
invalidrange();
return 0;
}
return 1;
}
static void
doxhdr(char *arg)
{
static const char *h[] = { "Subject", "From", "Date", "Message-ID",
"References", "Bytes", "Lines"
};
int n = 7;
size_t i;
char *l;
char *buf;
unsigned long a, b = 0, c;
char s[SIZE_s+1];
if (!arg || !*arg) {
if (debugmode)
syslog(LOG_DEBUG,
">502 Usage: HDR header first[-last] or "
"HDR header message-id");
printf("502 Usage: HDR header first[-last] or "
"HDR header message-id\r\n");
return;
}
/* go figure header */
l = arg;
while (l && *l && !isspace((unsigned char)*l))
l++;
if (l && *l)
*l++ = '\0';
SKIPLWS(l);
buf = critmalloc((i = strlen(arg)) + 2, "doxhdr");
strcpy(buf, arg); /* RATS: ignore */
if (buf[i - 1] != ':')
strcpy(buf + i, ":");
if (l && *l == '<') { /* handle message-id form (well) */
FILE *f;
char *m = critstrdup(l, "doxhdr");
f = fopenart(l);
if (!f) {
printf("430 No such article\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">430 No such article");
free(buf);
free(m);
return;
}
l = fgetheader(f, buf);
if (debugmode) {
syslog(LOG_DEBUG, ">221 %s header of %s follows:", buf, m);
if (l) syslog(LOG_DEBUG, ">%s %s", m, l);
syslog(LOG_DEBUG, ">.");
}
printf("221 %s header of %s follows:\r\n", buf, m);
if (l) printf("%s %s\r\n", m, l);
printf(".\r\n");
free(m);
(void)fclose(f);
free(buf);
if (l) free(l);
return;
}
if (!group) {
nogroup();
free(buf);
return;
}
markinterest(group->name);
a = group->first;
b = group->last;
if (b < a) b = a;
if (!parserange(l, &a, &b)) {
if (debugmode)
syslog(LOG_DEBUG, ">502 Usage: XHDR header first[-last] "
"or XHDR header message-id");
printf("502 Usage: XHDR header first[-last] "
"or XHDR header message-id\r\n");
free(buf);
return;
}
if (!checkrange(group, a, b)) {
free(buf);
return;
}
if (!is_pseudogroup(group)) {
if (xovergroup != group && chdirgroup(group->name, FALSE))
if (getxover())
xovergroup = group;
}
if (is_pseudogroup(group)) {
do {
n--;
} while (n >= 0 && strncasecmp(h[n], buf, strlen(h[n])) != 0);
if ((n < 0) && strncasecmp("Newsgroups", buf, 10)) {
printf("430 No such header: %s\r\n", buf);
if (debugmode)
syslog(LOG_DEBUG, ">430 No such header: %s", buf);
free(buf);
return;
}
if (debugmode)
syslog(LOG_DEBUG,
">221 First line of %s pseudo-header follows:", buf);
printf("221 First line of %s pseudo-header follows:\r\n", buf);
if (a <= b && a <= group->first && b >= group->last) {
printf("%lu ", group->first);
if (n == 0) /* Subject: */
printf("Leafnode placeholder for group %s\r\n", group->name);
else if (n == 1) /* From: */
printf("Leafnode <%s>\r\n", newsadmin);
else if (n == 2) /* Date: */
printf("%s\r\n", rfctime());
else if (n == 3) /* Message-ID: */
printf("<leafnode:placeholder:%s@%s>\r\n", group->name, fqdn);
else if (n == 4) /* References */
printf("(none)\r\n");
else if (n == 5) /* Bytes */
printf("%d\r\n", 1024); /* FIXME: just a guess */
else if (n == 6) /* Lines */
printf("%d\r\n", 22); /* FIXME: from buildpseudoart() */
else /* Newsgroups */
printf("%s\r\n", group->name);
}
printf(".\r\n");
free(buf);
return;
}
do {
n--;
} while (n > -1 && strncasecmp(buf, h[n], strlen(h[n])));
if (a < group->first)
a = group->first;
if (b > group->last)
b = group->last;
if (n >= 0) {
if (debugmode)
syslog(LOG_DEBUG, "221 %s header (from overview) "
"for postings %lu-%lu:", h[n], a, b);
printf("221 %s header (from overview) for postings %lu-%lu:\r\n",
h[n], a, b);
s[sizeof(s)-1] = '\0';
for (c = a; c <= b; c++) {
if (xoverinfo &&
c >= xfirst && c <= xlast && xoverinfo[c - xfirst].text) {
char *l2 = xoverinfo[c - xfirst].text;
int d;
for (d = 0; l2 && d <= n; d++)
l2 = strchr(l2 + 1, '\t');
if (l2) {
char *p;
(void)strlcpy(s, ++l2, sizeof(s));
p = strchr(s, '\t');
if (p)
*p = '\0';
}
if (l2 && *l2) printf("%lu %s\r\n", c, s);
}
}
} else {
if (debugmode)
syslog(LOG_DEBUG, ">221 %s header (from article files) "
"for postings %lu-%lu:", buf, a, b);
printf("221 %s header (from article files) for postings %lu-%lu:\r\n",
buf, a, b);
for (c = a; c <= b; c++) {
sprintf(s, "%lu", c);
l = getheader(s, buf);
if (l) {
printf("%lu %s\r\n", c, l); /* (l && *l) ? l : "(none)" ); */
free(l);
}
}
}
free(buf);
printf(".\r\n");
return;
}
static void
doxover(char *arg)
{
unsigned long a, b, art;
if (!group) {
nogroup();
return;
}
markinterest(group->name);
a = group->first;
b = group->last;
if (b < a) b = a;
if (!arg || !*arg)
a = b = artno;
else if (!parserange(arg, &a, &b)) {
printf("502 Usage: OVER first[-[last]]\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">502 Usage: OVER first[-[last]]");
return;
}
if (!checkrange(group, a, b))
return;
if (!is_pseudogroup(group)) {
if (xovergroup != group && chdirgroup(group->name, FALSE))
if (getxover()) xovergroup = group;
if (NULL == xoverinfo) {
invalidrange();
return;
}
if (b > xlast)
b = xlast;
if (a < xfirst)
a = xfirst;
printf("224 Overview information for postings %lu-%lu:\r\n", a, b);
if (debugmode)
syslog(LOG_DEBUG, ">224 Overview information for postings %lu-%lu:",
a, b);
for (art = a; art <= b; art++) {
if (xoverinfo[art - xfirst].text)
printf("%s\r\n", xoverinfo[art - xfirst].text);
}
printf(".\r\n");
} else {
if ((a > b) || (group->first <= group->last
? (a > group->last) || (b < group->first)
: (a > group->first) || (b < group->first))) {
printf("420 No articles in specified range.\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">420 No articles in specified range.");
return;
}
printf("224 Overview information (pseudo) for postings %lu-%lu:\r\n",
group->first, group->first);
if (debugmode)
syslog(LOG_DEBUG, ">224 Overview information (pseudo) for "
"postings %lu-%lu:", group->first, group->first);
printf("%lu\t"
"Leafnode placeholder for group %s\t"
"%s (Leafnode)\t%s\t"
"<leafnode:placeholder:%s@%s>\t\t1000\t40\r\n", group->first,
group->name, newsadmin, rfctime(), group->name, fqdn);
printf(".\r\n");
if (debugmode)
syslog(LOG_DEBUG, ">%lu\tLeafnode placeholder for group %s\t"
"%s (Leafnode)\t%s\t<leafnode:placeholder:%s@%s>\t\t1000\t40",
group->first, group->name, newsadmin, rfctime(), group->name, fqdn);
}
}
static void
dolistgroup(const char *arg)
{
unsigned long art;
if (arg && *(arg)) {
struct newsgroup *g;
g = findgroup(arg);
if (g) {
group = g;
artno = g->first;
} else {
printf("411 No such group: %s\r\n", arg);
if (debugmode)
syslog(LOG_DEBUG, ">411 No such group: %s", arg);
return;
}
}
if (!group) {
nogroup();
return;
}
/* group = g; */
markinterest(group->name);
if ((NULL == xovergroup || xovergroup != group)
&& chdirgroup(group->name, FALSE))
if (getxover()) xovergroup = group;
if (is_pseudogroup(group)) {
printf("211 Article list for %s follows (pseudo)\r\n", group->name);
if (debugmode)
syslog(LOG_DEBUG,
">211 Article list for %s follows (pseudo)", group->name);
printf("%lu\r\n", group->first ? group->first : 1);
} else {
printf("211 Article list for %s follows\r\n", group->name);
if (debugmode)
syslog(LOG_DEBUG, ">211 Article list for %s follows", group->name);
if (xoverinfo)
for (art = xfirst; art <= xlast; art++) {
if (xoverinfo[art - xfirst].text)
printf("%lu\r\n", art);
}
}
printf(".\r\n");
}
static void
parser(void)
{
char *arg;
int n;
size_t size;
mgetaline_settimeout(timeout_client);
while ((cmd = mgetaline(stdin))) {
if (debug == 1)
syslog(LOG_DEBUG, "<%s", cmd);
size = strlen(cmd);
if (size == 0)
continue; /* ignore */
if (size > MAXLINELENGTH || (long)size > (long)INT_MAX) {
/* ignore attempts at buffer overflow */
if (debugmode)
syslog(LOG_DEBUG, ">500 Dazed and confused");
printf("500 Dazed and confused\r\n");
continue;
}
/* parse command line */
n = 0;
while (isalpha((unsigned char)cmd[n]))
n++;
while (isspace((unsigned char)cmd[n]))
cmd[n++] = '\0';
arg = cmd + n;
while (cmd[n])
n++;
n--;
while (n >= 0 && isspace((unsigned char)cmd[n]))
cmd[n--] = '\0';
if (!strcasecmp(cmd, "quit")) {
if (debugmode)
syslog(LOG_DEBUG, ">205 Always happy to serve!");
printf("205 Always happy to serve!\r\n");
return;
}
rereadactive();
if (!strcasecmp(cmd, "article")) {
doarticle(arg, 3);
} else if (!strcasecmp(cmd, "head")) {
doarticle(arg, 2);
} else if (!strcasecmp(cmd, "body")) {
doarticle(arg, 1);
} else if (!strcasecmp(cmd, "stat")) {
doarticle(arg, 0);
} else if (!strcasecmp(cmd, "help")) {
dohelp();
} else if (!strcasecmp(cmd, "last")) {
domove(-1);
} else if (!strcasecmp(cmd, "next")) {
domove(1);
} else if (!strcasecmp(cmd, "list")) {
dolist(arg);
} else if (!strcasecmp(cmd, "date")) {
dodate();
} else if (!strcasecmp(cmd, "mode")) {
if (debugmode)
syslog(LOG_DEBUG, ">200 Leafnode %s, pleased to meet you!",
version);
printf("200 Leafnode %s, pleased to meet you!\r\n", version);
} else if (!strcasecmp(cmd, "newgroups")) {
donewgroups(arg);
} else if (!strcasecmp(cmd, "newnews")) {
if (debugmode)
syslog(LOG_DEBUG,
">500 NEWNEWS is meaningless for this server");
printf("500 NEWNEWS is meaningless for this server\r\n");
} else if (!strcasecmp(cmd, "post")) {
if (dopost()) break;
} else if (!strcasecmp(cmd, "slave")) {
if (debugmode)
syslog(LOG_DEBUG, ">202 Cool - I always wanted a slave");
printf("202 Cool - I always wanted a slave\r\n");
} else if (!strcasecmp(cmd, "xhdr")) {
doxhdr(arg);
} else if (!strcasecmp(cmd, "hdr")) {
doxhdr(arg);
} else if (!strcasecmp(cmd, "xover")) {
doxover(arg);
} else if (!strcasecmp(cmd, "over")) {
doxover(arg);
} else if (!strcasecmp(cmd, "listgroup")) {
dolistgroup(arg);
} else if (!strcasecmp(cmd, "group")) {
if (dogroup(arg)) break;
} else {
if (debugmode)
syslog(LOG_DEBUG, ">500 Unknown command");
printf("500 Unknown command\r\n");
}
if (ferror(stdout) || fflush(stdout)) {
syslog(LOG_ERR, "Cannot write to client.");
break;
}
}
if (debugmode)
syslog(LOG_DEBUG, "Client timeout, disconnecting.");
/* There was once a 400 error message here. It confused broken
* clients, most notably, tin.
* Future NNTP drafts command that we don't send stuff back on
* timeout, so we anticipate these. */
}
int
main(int argc, char **argv)
{
socklen_t fodder;
char peername[256]; /* RATS: ignore */
#ifdef HAVE_IPV6
char *st;
int h_err;
#define ADDRLEN INET6_ADDRSTRLEN
union sockaddr_union su;
#else
struct hostent *he;
#ifdef INET_ADDRSTRLEN
#define ADDRLEN INET_ADDRSTRLEN
#else
#define ADDRLEN 16
#endif
struct sockaddr_in sa;
#endif
char peerip[ADDRLEN]; /* RATS: ignore */
char ownip[ADDRLEN]; /* RATS: ignore */
char origfqdn[FQDNLEN + 1]; /* RATS: ignore */
ln_log_use_console(0); /* disable console logging */
(void)argc; /* quiet compiler warning */
myopenlog("leafnode");
/* this gets the actual hostname */
if (!initvars(argv[0]))
exit(1);
artno = 0;
verbose = 0;
(void)umask(2);
/* this reads the host name from the config file */
if (!readconfig(1)) {
const char *m = "503 Unable to read configuration file, exiting; the server's syslog should have more information.";
printf("%s\r\n", m);
syslog(LOG_ERR, "%s", m);
exit(1);
}
freeservers();
strcpy(origfqdn, fqdn); /* same size buffer */ /* RATS: ignore */
/* get own name */
#ifdef HAVE_IPV6
fodder = sizeof(union sockaddr_union);
if (0 == getsockname(0, (struct sockaddr *)&su, &fodder)) {
if (su.sin.sin_family == AF_INET6)
inet_ntop(AF_INET6, &su.sin6.sin6_addr, ownip, sizeof(ownip));
else
inet_ntop(AF_INET, &su.sin.sin_addr, ownip, sizeof(ownip));
if ((st = masock_sa2name((struct sockaddr *)&su, &h_err))) {
xstrlcpy(fqdn, st, sizeof(fqdn));
free(st);
}
}
#else
fodder = sizeof(struct sockaddr_in);
if (0 == getsockname(0, (struct sockaddr *)&sa, &fodder)) {
he = gethostbyaddr((char *)&sa.sin_addr.s_addr,
sizeof(sa.sin_addr.s_addr), AF_INET);
*fqdn = '\0';
(void)xstrlcpy(fqdn,
he && he->h_name ? he->h_name : inet_ntoa(sa.sin_addr),
sizeof(fqdn));
strcpy(ownip, inet_ntoa(sa.sin_addr));
}
#endif
else {
strcpy(ownip, "no IP");
}
/* get remote name */
#ifdef HAVE_IPV6
fodder = sizeof(union sockaddr_union);
if (0 == getpeername(0, (struct sockaddr *)&su, &fodder)) {
if (su.sa.sa_family == AF_INET6)
inet_ntop(AF_INET6, &su.sin6.sin6_addr, peername, sizeof(peername));
else
inet_ntop(AF_INET, &su.sin.sin_addr, peername, sizeof(peername));
strcpy(peerip, peername);
if ((st = masock_sa2name((struct sockaddr *)&su, &h_err))) {
xstrlcpy(peername, st, sizeof(peername));
free(st);
}
}
#else
fodder = sizeof(struct sockaddr_in);
if (0 == getpeername(0, (struct sockaddr *)&sa, &fodder)) {
he = gethostbyaddr((char *)&sa.sin_addr.s_addr,
sizeof(sa.sin_addr.s_addr), AF_INET);
(void)xstrlcpy(peername,
he && he->h_name ? he->h_name : inet_ntoa(sa.sin_addr),
sizeof(peername));
strcpy(peerip, inet_ntoa(sa.sin_addr));
}
#endif
else {
if (errno == ENOTSOCK) {
strcpy(peername, "(local file)");
strcpy(peerip, "no IP");
} else {
strcpy(peerip, "unknown");
strcpy(peername, "(unknown)");
}
}
syslog(LOG_INFO, "connect from %s (%s) to %s (%s) (my fqdn: %s)",
peername, peerip, fqdn, ownip, origfqdn);
if (allowstrangers == 0 && checkpeerlocal(0) != 1) {
unsigned int i = 5;
syslog(LOG_NOTICE, "Denying access from address outside the local networks. (Check config.example.)");
while (i) i = sleep(i);
printf("502 Remote access denied.\n");
exit(0);
}
printf("200 Leafnode NNTP Daemon, version %s "
"running at %s (my fqdn: %s)\r\n",
version, fqdn, origfqdn);
if (fflush(stdout)) exit(0);
strcpy(fqdn, origfqdn);
rereadactive();
parser();
(void)fflush(stdout);
freeactive(active);
freexover();
freeconfig();
sleep(1); /* protect against process ID induced file name collisions */
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1