home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / relay / relaynews.c < prev    next >
C/C++ Source or Header  |  1995-04-27  |  11KB  |  450 lines

  1. /*
  2.  * relaynews - relay Usenet news (version C)
  3.  * See the file COPYRIGHT for the copyright notice.
  4.  *
  5.  * Written by Geoff Collyer, 15-20 November 1985 and revised periodically
  6.  * since.
  7.  *
  8.  * relaynews parses article headers, rejects articles by newsgroup &
  9.  * message-id, files articles, updates the active & history files,
  10.  * transmits articles, and honours (infrequent) control messages, which do
  11.  * all sorts of varied and rococo things.  Most control messages are
  12.  * implemented by separate programs.  relaynews reads a "sys" file to
  13.  * control the transmission of articles but can function as a promiscuous
  14.  * leaf node without one.  See Internet RFC 1036 for the whole story and
  15.  * RFC 850 for background.
  16.  *
  17.  * relaynews must be invoked under the news user and group ids,
  18.  * typically from cron.  It must *not* be made setuid nor setgid.
  19.  *
  20.  * A truly radical notion: people may over-ride via environment variables
  21.  * the compiled-in default directories so IHCC kludges are not needed and
  22.  * testing is possible (and encouraged) in alternate directories.  This
  23.  * does cause a loss of privilege, to avoid spoofing.
  24.  *
  25.  * The disused old unbatched ihave/sendme protocol is gone because it was
  26.  * too wasteful; use the batched form instead (see the ihave sys flag
  27.  * ("I") instead).
  28.  */
  29.  
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <ctype.h>
  33. #include <string.h>
  34. #include <signal.h>        /* to make locking safe */
  35. #include <errno.h>
  36. #include "fixerrno.h"
  37. #include <sys/types.h>
  38. #include <sys/stat.h>
  39.  
  40. #include "libc.h"
  41. #include "news.h"
  42. #include "config.h"
  43. #include "fgetfln.h"
  44. #include "active.h"
  45. #include "headers.h"
  46. #include "relay.h"
  47. #include "history.h"
  48.  
  49. /* imports */
  50. extern int optind;            /* set by getopt */
  51. extern char *optarg;
  52. extern statust cpinsart();        /* from procart.c */
  53. extern statust clshdrstrm();
  54.  
  55. /* exports */
  56. char *progname = "relaynews";
  57. struct options opts;
  58. struct newsconf newsconf;
  59. char *slinkfile = NULL;
  60.  
  61. /* privates */
  62. static boolean uunlink = NO;
  63.  
  64. /*
  65.  * SystemV getcwd simulation, courtesy peter honeyman
  66.  */
  67. static char *
  68. getpwd(path, size)
  69. register char *path;
  70. int size;
  71. {
  72.     register FILE *fp;
  73.  
  74.     fp = popen("PATH=/bin:/usr/bin pwd 2>/dev/null", "r");
  75.     if (fp == NULL)
  76.         return NULL;
  77.     if (fgets(path, size, fp) == NULL)
  78.         path = NULL;            /* signal failure */
  79.     else
  80.         trim(path);
  81.     return (pclose(fp) != 0? NULL: path);
  82. }
  83.  
  84. /*
  85.  * if argv contains relative file name arguments, save current directory name
  86.  * in malloced memory, in opts.currdir.
  87.  * then change directory to the spool directory ($NEWSARTS).
  88.  */
  89. void
  90. getwdandcd(argc, argv)
  91. int argc;
  92. char **argv;
  93. {
  94.     register int argind;
  95.     boolean needpwd = NO;
  96.     char dirtmp[MAXPATH];            /* much bigger than needed */
  97.  
  98.     if (opts.currdir == NULL) {            /* not yet set? */
  99.         for (argind = optind; argind < argc; argind++)
  100.             if (argv[argind][0] != FNDELIM)
  101.                 needpwd = YES;
  102.  
  103.         opts.currdir = "/???";        /* pessimism */
  104.         if (needpwd && getpwd(dirtmp, sizeof dirtmp) != 0)
  105.             opts.currdir = dirtmp;
  106.         opts.currdir = strsave(opts.currdir); /* save a smaller copy */
  107.     }
  108.     cd(fullartfile((char *)NULL));        /* move to spool directory */
  109. }
  110.  
  111. /*
  112.  * reset various environmental things for safety: umask, alarm,
  113.  * environment variables (PATH, IFS), standard file descriptors,
  114.  * user & group ids.  May exit, so call before locking the news system.
  115.  */
  116. /* ARGSUSED argv */
  117. void
  118. prelude(argv)                /* setuid daemon prelude */
  119. char **argv;
  120. {
  121.     register char *newpath;
  122.  
  123.     (void) umask(newsumask());
  124.     (void) alarm(0);        /* cancel any pending alarm */
  125.     /* TODO: suppress chatter on failure here */
  126.     newpath = str3save("PATH=", newspath(), "");
  127.     if (newpath == NULL)
  128.         exit(1);        /* no chatter until stdfdopen */
  129.     if (putenv(newpath) ||
  130.         putenv("SHELL=/bin/sh") ||
  131.         putenv("IFS= \t\n"))
  132.         exit(1);        /* no chatter until stdfdopen */
  133.     closeall(1);            /* closes all but std descriptors */
  134.     stdfdopen();            /* ensure open standard descriptors */
  135. }
  136.  
  137. STATIC boolean
  138. debugon(dbopt)
  139. register char *dbopt;
  140. {
  141.     statust status = YES;
  142.  
  143.     for (; *dbopt != '\0'; dbopt++)
  144.         switch (*dbopt) {
  145.         case 'f':
  146.             filedebug(YES);
  147.             break;
  148.         case 'h':
  149.             hdrdebug(YES);
  150.             break;
  151.         case 'l':
  152.             lockdebug(YES);
  153.             break;
  154.         case 'm':
  155.             matchdebug(YES);
  156.             break;
  157.         case 't':
  158.             transdebug(YES);
  159.             break;
  160.         default:
  161.             status = NO;    /* unknown debugging option */
  162.             (void) fprintf(stderr, "%s: bad -d %c\n",
  163.                 progname, *dbopt);
  164.             break;
  165.         }
  166.     return status;
  167. }
  168.  
  169. /*
  170.  * parse options and set flags
  171.  */
  172. void
  173. procopts(argc, argv, optsp)
  174. int argc;
  175. char **argv;
  176. register struct options *optsp;
  177. {
  178.     int c, errflg = 0;
  179.  
  180.     while ((c = getopt(argc, argv, "a:b:c:d:hn:o:sux")) != EOF)
  181.         switch (c) {
  182.         case 'a':
  183.             optsp->dupsokay = YES;
  184.             optsp->dupsite = optarg;
  185.             break;
  186.         case 'b':
  187.             optsp->blvxref = YES;
  188.             optsp->blvsite = optarg;
  189.             break;
  190.         case 'c':
  191.             if (optarg[0] != FNDELIM) {
  192.                 (void) fprintf(stderr,
  193.                     "%s: %s: must be absolute path\n",
  194.                     progname, optarg);
  195.                 exit(1);
  196.             }
  197.             optsp->currdir = optarg;
  198.             break;
  199.         case 'd':        /* -d debug-options; thanks, henry */
  200.             if (!debugon(optarg))
  201.                 errflg++;    /* debugon has complained */
  202.             break;
  203.         case 'h':        /* keep no history of rejects */
  204.             optsp->histreject = NO;
  205.             break;
  206.         case 'n':        /* note use of symlinks in this file */
  207.             slinkfile = optarg;
  208.             break;
  209.         case 'o':
  210.             /* "oldness": drop articles older than this many days */
  211.             optsp->staledays = atol(optarg);
  212.             break;
  213.         case 's':        /* dropping input is serious (inews) */
  214.             optsp->okrefusal = NO;
  215.             break;
  216.         case 'u':        /* unlink good batches when done */
  217.             uunlink = YES;
  218.             break;
  219.         case 'x':
  220.             optsp->genxref = NO;
  221.             break;
  222.         default:
  223.             errflg++;
  224.             break;
  225.         }
  226.     if (errflg) {
  227.         (void) fprintf(stderr,
  228. "usage: %s [-c currdir][-hsux][-d fhlmt][-o days][-b xrefsite][-a dupsite]\n",
  229.             progname);
  230.         exit(1);
  231.     }
  232. }
  233.  
  234. /*
  235.  * Is line a batcher-produced line (#! rnews count)?
  236.  * If so, return the count through charcntp.
  237.  * This is slightly less convenient than sscanf, but a lot smaller.
  238.  */
  239. boolean
  240. batchln(line, charcntp)
  241. register char *line;
  242. register long *charcntp;
  243. {
  244.     static char numbang[] = "#!";
  245.     static char rnews[] = "rnews";
  246.  
  247.     *charcntp = 0;
  248.     if (!STREQN(line, numbang, STRLEN(numbang)))
  249.         return NO;
  250.     line = skipsp(line + STRLEN(numbang));
  251.     if (!STREQN(line, rnews, STRLEN(rnews)))
  252.         return NO;
  253.     line = skipsp(line + STRLEN(rnews));
  254.     if (isascii(*line) && isdigit(*line)) {
  255.         *charcntp = atol(line);
  256.         return YES;
  257.     } else
  258.         return NO;
  259. }
  260.  
  261. /*
  262.  * Unwind "in" and insert each article.
  263.  * For each article, call cpinsart to copy the article from "in" into
  264.  * a (temporary) file in the news spool directory and rename the temp file
  265.  * to the correct final name if it isn't right already.
  266.  *
  267.  * If the unbatcher gets out of sync with the input batch, the unbatcher
  268.  * will print and discard each input line until it gets back in sync.
  269.  */
  270. statust
  271. unbatch(in, inname)
  272. register FILE *in;
  273. char *inname;
  274. {
  275.     register int c;
  276.     /* register */ char *line;
  277.     register statust status = ST_OKAY;
  278.     int insynch = YES;
  279.     long charcnt;
  280.  
  281.     while (!(status&ST_NEEDATTN) && (c = getc(in)) != EOF) {
  282.         (void) ungetc(c, in);
  283.         while ((line = fgetln(in)) != NULL &&
  284.             !batchln(line, &charcnt)) {        /* returns charcnt */
  285.             status |= ST_DROPPED|ST_NEEDATTN;    /* save batch */
  286.             if (insynch)
  287.                 persistent(NOART, 'b',
  288.                        "unbatcher out of synch in file %s",
  289.                        inname);
  290.             insynch = NO;
  291.         }
  292.         if (!feof(in) && charcnt > 0)    /* anything to do? */
  293.             status |= cpinsart(in, inname, charcnt, YES);
  294.     }
  295.     if (ferror(in))
  296.         errunlock("error reading `%s'", inname);
  297.     return status;
  298. }
  299.  
  300. /*
  301.  * compute the largest number that can be stored in a long.  in theory, 
  302.  * #define MAXLONG ((long)(~(unsigned long)0 >> 1))
  303.  * will do the job, but old compilers don't have "unsigned long", don't
  304.  * like casts in initialisers, or otherwise miscompute.
  305.  */
  306. STATIC long
  307. maxlong()
  308. {
  309.     register int bits = 0;
  310.     register unsigned word = 1;        /* "unsigned" avoids overflow */
  311.     static long savemaxlong = 0;
  312.  
  313.     if (savemaxlong > 0)
  314.         return savemaxlong;
  315.     for (bits = 0, word = 1; word != 0; word <<= 1)
  316.         bits++;
  317.     /* bits/sizeof word = bits per char; all bits on but the sign bit */
  318.     savemaxlong = ~(1L << (bits/sizeof word * sizeof savemaxlong - 1));
  319.     if (savemaxlong <= 0) {            /* sanity check */
  320.         errno = 0;
  321.         errunlock("maxlong is non-positive; your compiler is broken", "");
  322.     }
  323.     return savemaxlong;
  324. }
  325.  
  326. /*
  327.  * process - process input file
  328.  * If it starts with '#', assume it's a batch and unravel it,
  329.  * else it's a single article, so just inject it.
  330.  */
  331. statust
  332. process(in, inname)
  333. FILE *in;
  334. char *inname;
  335. {
  336.     register int c;
  337.  
  338.     if ((c = getc(in)) == EOF)
  339.         return ST_OKAY;         /* normal EOF */
  340.     (void) ungetc(c, in);
  341.     if (c == '#')
  342.         return unbatch(in, inname);
  343.     else
  344.         /* -SIZENUL is to avoid overflow later during +SIZENUL */
  345.         return cpinsart(in, inname, maxlong() - SIZENUL, NO);
  346. }
  347.  
  348. /*
  349.  * process a (relative) file name.
  350.  * in unlink mode, try to unlink good batches, but not very hard.
  351.  */
  352. statust
  353. relnmprocess(name)
  354. char *name;
  355. {
  356.     register statust status = ST_OKAY;
  357.     register FILE *in;
  358.     register char *fullname = (name[0] != FNDELIM?
  359.         str3save(opts.currdir, SFNDELIM, name): strsave(name));
  360.  
  361.     in = fopenwclex(fullname, "r");
  362.     if (in != NULL) {
  363.         status |= process(in, fullname);
  364.         (void) nfclose(in);
  365.         if (uunlink && !(status&(ST_DROPPED|ST_SHORT)))
  366.             (void) unlink(fullname);
  367.     }
  368.     free(fullname);
  369.     return status;
  370. }
  371.  
  372. /*
  373.  * process files named as arguments (or implied)
  374.  */
  375. statust
  376. procargs(argc, argv)
  377. int argc;
  378. char **argv;
  379. {
  380.     register statust status = ST_OKAY;
  381.  
  382.     if (optind == argc)
  383.         status |= process(stdin, "(stdin)");
  384.     else
  385.         for (; optind < argc; optind++)
  386.             status |= relnmprocess(argv[optind]);
  387.     return status;
  388. }
  389.  
  390. /*
  391.  * main - take setuid precautions, switch to "news" ids, ignore signals,
  392.  * handle options, lock news system, process files & unlock news system.
  393.  */
  394. int
  395. main(argc, argv)
  396. int argc;
  397. char *argv[];
  398. {
  399.     statust status = ST_OKAY;
  400.  
  401.     if (argc > 0)
  402.         progname = argv[0];
  403.     opts.okrefusal = YES;
  404.     opts.genxref = YES;        /* needed for overview data */
  405.     opts.histreject = YES;        /* retain history of rejects normally */
  406.     opts.currdir = NULL;        /* pessimism */
  407. #ifdef CSRIMALLOC
  408.     {
  409.         char *maldebug = getenv("MALDEBUG");
  410.  
  411.         if (maldebug != NULL)
  412.             mal_debug(atoi(maldebug));
  413.         mal_leaktrace(0);    /* was 1 */
  414.     }
  415. #endif
  416.     prelude(argv);        /* various precautions; switch to "news" */
  417.  
  418.     /* ignore signals (for locking). relaynews runs quickly, so don't worry. */
  419.     (void) signal(SIGINT, SIG_IGN);
  420.     (void) signal(SIGQUIT, SIG_IGN);
  421.     (void) signal(SIGHUP, SIG_IGN);
  422.     (void) signal(SIGTERM, SIG_IGN);
  423.     (void) signal(SIGPIPE, SIG_IGN);    /* we check write returns */
  424.  
  425.     newsconf.nc_link = YES;        /* HACK */
  426.     newsconf.nc_symlink = YES;    /* HACK */
  427.     procopts(argc, argv, &opts);
  428.  
  429.     (void) morefds();        /* ask Unix for more descriptors */
  430.     newslock();            /* done here due to dbm internal cacheing */
  431.  
  432.     getwdandcd(argc, argv);
  433.     status |= procargs(argc, argv);
  434.  
  435.     status |= synccaches();        /* being cautious: write & close caches */
  436.     status |= closehist();
  437.     status |= clshdrstrm();
  438.     (void) fflush(stdout);        /* log file */
  439.     (void) fflush(stderr);        /* errlog file */
  440.  
  441. #ifdef notdef
  442. #ifdef CSRIMALLOC
  443.     mal_dumpleaktrace(fileno(stderr));
  444. #endif
  445. #endif
  446.     newsunlock();
  447.     exit((status&ST_NEEDATTN)? 2: (status != ST_OKAY? 1: 0));
  448.     /* NOTREACHED */
  449. }
  450.