home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
300-399
/
ff319.lzh
/
CNewsSrc
/
cnews.orig.lzh
/
relay
/
relaynews.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-27
|
12KB
|
468 lines
/*
* relaynews - relay Usenet news (version C)
* See the file COPYRIGHT for the copyright notice.
*
* relaynews should be setuid-news, setgid-news. You'll need to install
* setnewsids setuid-root if setuid(geteuid()) doesn't work on your
* machine (e.g. on V7 and possibly SystemIII).
*
* Written by Geoff Collyer, 15-20 November 1985 and revised periodically
* since.
*
* relaynews parses article headers, rejects articles by newsgroup &
* message-id, files articles, updates the active & history files,
* transmits articles, and honours (infrequent) control messages, which do
* all sorts of varied and rococo things. Control messages are implemented
* by separate programs. relaynews reads a "sys" file to control the
* transmission of articles but can function as a promiscuous leaf node
* without one. See ARPA Internet RFC 1036 nee 850 for the whole story.
*
* A truly radical notion: people may over-ride via environment variables
* the compiled-in default directories so IHCC kludges are not needed and
* testing is possible (and encouraged) in alternate directories. This
* does cause a loss of privilege, to avoid spoofing.
*
* The disused old ihave/sendme protocol is dead; it's been broken in
* B news for ages but no one has noticed because it's essentially
* useless on the uucp network, especially when batching news articles.
* It is also only semi-documented, very wasteful and kludgey in the extreme.
* It is unreasonable to expect an ill-documented protocol to be implemented
* in new software, particularly one as kludgey as this one.
* The new ihave sys flag ("I") for NNTP is implemented (trivially).
*
* Portability vs SystemV. relaynews uses dbm(3) and makes no apologies
* for so doing. Imitation UNIX (registered trademark of AT&T in the
* United States) brand operating systems that lack dbm are going to
* have to use my incredibly slow dbm simulation, or another.
*/
#include <stdio.h>
#include <ctype.h>
#include <signal.h> /* to make locking safe */
#include <sys/types.h>
#include "libc.h"
#include "news.h"
#include "config.h"
#include "fgetmfs.h"
#include "active.h"
#include "caches.h"
#include "cpu.h"
#include "fileart.h"
#include "headers.h"
#include "history.h"
#include "transmit.h"
/*
* setuid-root program to set ids to news/news & rexec rnews with
* NEWSPERMS in the environment to break loops.
*/
#ifndef SETNEWSIDS
#define SETNEWSIDS "setnewsids"
#endif
/* exports */
char *progname;
boolean okrefusal = YES; /* okay to refuse articles? */
char *exclude = NULL; /* site to exclude, for erik */
boolean histreject = NO; /* keep history of rejects? */
/* internal */
static boolean userealids = NO;
/* imports */
extern int optind; /* set by getopt */
extern char *optarg;
extern statust cpinsart(); /* from procart.c */
/* forwards */
extern void prelude(), setids(), procopts(), redirectlogs(), logfile();
extern void getwdandcd();
extern statust procargs(), relnmprocess(), process(), unbatch();
extern boolean batchln();
FORWARD boolean debugon();
/*
* main - take setuid precautions, switch to "news" ids, ignore signals,
* handle options, lock news system, process files & unlock news system.
*/
int
main(argc, argv)
int argc;
char *argv[];
{
statust status = ST_OKAY;
int redirlogs = 0; /* redirect n std output streams to logs */
char *origdir = NULL; /* current directory at start */
progname = argv[0];
#ifdef CSRIMALLOC
mal_debug(0); /* was 2; 3 is too slow */
mal_leaktrace(0); /* was 1 */
#endif
prelude(argv); /* various precautions; switch to "news" */
/* ignore signals (for locking). relaynews runs quickly, so don't worry. */
(void) signal(SIGINT, (sigarg_t)SIG_IGN);
(void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
(void) signal(SIGHUP, (sigarg_t)SIG_IGN);
(void) signal(SIGTERM, (sigarg_t)SIG_IGN);
procopts(argc, argv, &redirlogs, &okrefusal);
newslock(); /* done here due to dbm internal cacheing */
if (redirlogs > 0) {
redirectlogs(redirlogs); /* redirect std output streams to logs */
#ifdef MANYERRORS
(void) putc('\n', stderr); /* leave a blank line */
/* prints "Jun 5 12:34:56" */
timestamp(stderr, (time_t *)NULL);
(void) putc('\n', stderr);
#endif
}
getwdandcd(argc, argv, &origdir);
status |= procargs(argc, argv, &origdir);
status |= synccaches(); /* being cautious: write & close caches */
(void) fflush(stdout); /* log file */
(void) fflush(stderr); /* errlog file */
#ifdef notdef
#ifdef CSRIMALLOC
mal_dumpleaktrace(fileno(stderr));
#endif
#endif
newsunlock();
exit(status);
/* NOTREACHED */
}
/*
* reset various environmental things for safety: umask, alarm,
* environment variables (PATH, IFS), standard file descriptors,
* user & group ids.
*/
void
prelude(argv) /* setuid daemon prelude */
char **argv;
{
register char *newpath;
(void) umask(2); /* undo silly umasks, ignore newsumask() */
(void) alarm(0); /* cancel any pending alarm */
newpath = malloc(STRLEN("PATH=") + strlen(newspath()) + 1);
if (newpath == NULL)
exit(1); /* no chatter until stdfdopen */
(void) strcpy(newpath, "PATH=");
(void) strcat(newpath, newspath());
if (putenv(newpath) ||
putenv("IFS= \t\n"))
exit(1); /* no chatter until stdfdopen */
closeall(1); /* closes all but std descriptors */
stdfdopen(); /* ensure standard descriptors are open */
setids(argv); /* change of real and effective ids */
}
/*
* change real and effective ids to real ids if unprivileged() is called,
* else to effective ("news") ids. ctlfile((char *)0) will trigger a call
* to unprivileged() if any environment variables override the default
* path names. unprivileged() in turn sets userealids.
*
* If setuid(geteuid()) fails, try execing a small, setuid-root program
* to just do "getpwnam(), getgrnam() (with NEWSPERMS set), setgid(),
* setuid()," and exec this program again. If NEWSPERMS is set,
* the failure is a fatal error (recursive loop).
* This program (relaynews) can be setuid-news.
*/
void
setids(argv)
char **argv;
{
int newsuid, newsgid;
(void) ctlfile((char *)NULL);
if (userealids)
newsuid = getuid(), newsgid = getgid();
else
newsuid = geteuid(), newsgid = getegid();
if (setgid(newsgid) < 0 || setuid(newsuid) < 0) {
if (getenv("NEWSPERMS") != 0)
error("recursive loop setting ids", "");
execv(ctlfile(SETNEWSIDS), argv);
error("can't exec `%s' to set ids", ctlfile(SETNEWSIDS));
/* NOTREACHED */
}
/* we are now running as news, so you can all relax */
}
/*
* parse options and set flags
*/
void
procopts(argc, argv, redirlogsp, okrefusalp)
int argc;
char **argv;
int *redirlogsp;
boolean *okrefusalp;
{
int c, errflg = 0;
while ((c = getopt(argc, argv, "d:inrsx:")) != EOF)
switch (c) {
case 'd': /* -d debug-options; thanks, henry */
if (!debugon(optarg))
errflg++; /* debugon has complained */
break;
case 'i': /* redirect stdout to log (inews) */
*redirlogsp = 1; /* just stdout */
break;
case 'n': /* nntp mode: keep history of rejects */
histreject = YES;
break;
case 'r': /* redirect std. ostreams to logs (rnews) */
*redirlogsp = 2; /* stdout & stderr */
break;
case 's': /* dropping input is serious (inews) */
*okrefusalp = NO;
break;
case 'x': /* -x site: don't send to site */
/* you're welcome, erik */
/* erik says he only needs one -x per inews */
if (exclude != NULL) {
(void) fprintf(stderr,
"%s: more than one -x site (%s)\n",
progname, optarg);
errflg++;
} else
exclude = optarg;
break;
default:
errflg++;
break;
}
if (errflg) {
(void) fprintf(stderr, "usage: %s [-inrs][-d fhlmt][-x site]\n",
progname);
exit(2);
}
}
void
unprivileged() /* called if NEWSARTS, NEWSCTL or NEWSBIN present */
{
userealids = YES;
}
STATIC boolean
debugon(dbopt)
register char *dbopt;
{
statust status = YES;
for (; *dbopt != '\0'; dbopt++)
switch (*dbopt) {
case 'f':
filedebug(YES);
break;
case 'h':
hdrdebug(YES);
break;
case 'l':
lockdebug(YES);
break;
case 'm':
matchdebug(YES);
break;
case 't':
transdebug(YES);
break;
default:
status = NO; /* unknown debugging option */
(void) fprintf(stderr, "%s: bad -d %c\n",
progname, *dbopt);
break;
}
return status;
}
/*
* Redirect stdout or stderr into log files at known locations.
*/
void
redirectlogs(count)
int count;
{
if (count > 0)
logfile(stdout, ctlfile("log"));
if (count > 1)
logfile(stderr, ctlfile("errlog"));
}
void
logfile(stream, name) /* redirect stream into name */
FILE *stream;
char *name;
{
if (freopen(name, "a", stream) == NULL)
errunlock("can't redirect standard stream to `%s'", name);
}
/*
* if argv contains relative file name arguments, save current directory name
* in malloced memory, through origdirp.
* then change directory to the spool directory ($NEWSARTS).
*/
void
getwdandcd(argc, argv, origdirp)
int argc;
char **argv;
char **origdirp;
{
register int argind;
boolean needpwd = NO;
char dirtmp[MAXPATH]; /* much bigger than needed */
for (argind = optind; argind < argc; argind++)
if (argv[argind][0] != FNDELIM)
needpwd = YES;
*origdirp = "/???"; /* pessimism */
if (needpwd && getcwd(dirtmp, sizeof dirtmp) != 0)
*origdirp = dirtmp;
*origdirp = strsave(*origdirp); /* save a smaller copy */
cd(fullartfile((char *)NULL)); /* move to spool directory */
}
/*
* process files named as arguments (or implied)
*/
statust
procargs(argc, argv, origdirp)
int argc;
char **argv;
char **origdirp;
{
register statust status = ST_OKAY;
if (optind == argc)
status |= process(stdin, "stdin");
else
for (; optind < argc; optind++)
status |= relnmprocess(argv[optind], *origdirp);
nnfree(origdirp);
return status;
}
statust
relnmprocess(name, origdir) /* process a (relative) file name */
char *name, *origdir;
{
register statust status = ST_OKAY;
register FILE *in;
register char *fullname;
fullname = nemalloc((unsigned)strlen(origdir) + STRLEN(SFNDELIM) +
strlen(name) + 1);
fullname[0] = '\0';
if (name[0] != FNDELIM) { /* relative path */
(void) strcat(fullname, origdir);
(void) strcat(fullname, SFNDELIM);
}
(void) strcat(fullname, name);
in = fopenwclex(fullname, "r");
if (in != NULL) {
status |= process(in, fullname);
(void) nfclose(in);
}
free(fullname);
return status;
}
/*
* process - process input file
* If it starts with '#', assume it's a batch and unravel it,
* else it's a single article, so just inject it.
*/
statust
process(in, inname)
FILE *in;
char *inname;
{
register int c;
if ((c = getc(in)) == EOF)
return ST_OKAY; /* normal EOF */
(void) ungetc(c, in);
if (c == '#')
return unbatch(in, inname);
else
return cpinsart(in, inname, MAXLONG, NO);
}
/*
* Unwind "in" and insert each article.
* For each article, call cpinsart to copy the article from "in" into
* a (temporary) file in the news spool directory and rename the temp file
* to the correct final name if it isn't right already.
*
* If the unbatcher gets out of sync with the input batch, the unbatcher
* will print and discard each input line until it gets back in sync.
*/
statust
unbatch(in, inname)
register FILE *in;
char *inname;
{
register int c;
/* register */ char *line;
register statust status = ST_OKAY;
long charcnt;
while (!(status&ST_DISKFULL) && (c = getc(in)) != EOF) {
(void) ungetc(c, in);
while ((line = fgetms(in)) != NULL &&
!batchln(line, &charcnt)) { /* returns charcnt */
status |= ST_DROPPED;
(void) fprintf(stderr,
"%s: unbatcher out of synch, tossing: ",
progname);
(void) fputs(line, stderr);
free(line);
}
nnfree(&line); /* free "#! rnews n" */
if (!feof(in))
status |= cpinsart(in, inname, charcnt, YES);
}
if (ferror(in))
errunlock("error reading `%s'", inname);
return status;
}
/*
* Is line a batcher-produced line (#! rnews count)?
* If so, return the count through charcntp.
* This is slightly less convenient than sscanf, but a lot smaller.
*/
boolean
batchln(line, charcntp)
register char *line;
register long *charcntp;
{
register char *countp;
static char batchtext[] = "#! rnews ";
countp = line + STRLEN(batchtext);
if (STREQN(line, batchtext, STRLEN(batchtext)) &&
isascii(*countp) && isdigit(*countp)) {
*charcntp = atol(countp);
return YES;
} else {
*charcntp = 0;
return NO;
}
}