home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / contrib / snntpd / snntpd.c < prev    next >
C/C++ Source or Header  |  1993-02-07  |  8KB  |  365 lines

  1. /*
  2.  * snntpd - barebones nntpd that only understands transport reception.
  3.  *    Mark Moraes, University of Toronto
  4.  *
  5.  * "NNTP newsreaders make me and my machine ill"
  6.  *                - Ian Dickinson, vato@csv.warwick.ac.uk
  7.  *
  8.  * TODO:
  9.  *    Timeouts.
  10.  *    No options supported yet, don't bother editing config.h.
  11.  *
  12.  * Access control could be elaborated endlessly, but it's only netnews.
  13.  */
  14.  
  15. #include <stdio.h>
  16. #include <ctype.h>
  17. #include <string.h>
  18. #include <signal.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21.  
  22. #include "msgid.h"
  23.  
  24. #include "libcnews.h"
  25. #include "config.h"
  26. #include "fgetfln.h"
  27. #include "history.h"
  28.  
  29. #include "batch.h"
  30. #include "msgs.h"
  31. #include "debug.h"
  32. #include "netdata.h"
  33. #include "log.h"
  34.  
  35. #ifndef F_OK
  36. #define F_OK 0
  37. #endif
  38. #ifndef W_OK
  39. #define W_OK 2        /* not actually used */
  40. #endif
  41.  
  42. #define NULLSTR        ((char *) NULL)
  43. #define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
  44. #define CISTREQN(a,b,n)    (cistrncmp((a), (b), (n)) == 0)
  45.  
  46. /* NOTE: x *MUST* be an array or you'll only get 3 chars */
  47. #define SAY(x, y) \
  48.     if (net_ackwrite((x), sizeof (x) - 1, (y), stdout) == 0) \
  49.         ; \
  50.     else \
  51.         unlock(), error("net_ackwrite(%s) failed", (x))
  52.  
  53. /* imports */
  54. extern char *mktemp();
  55. void postarticle(/* FILE *fp,  struct netdata *ndp */);
  56.  
  57. /* forwards */
  58. static void process(/* FILE *in */);
  59. static char *munge_msgid();
  60. static void readarticle(/* FILE *fp, char *msgid, struct netdata *ndp */);
  61. static void sendarticle(/* char *msgid */);
  62.  
  63. /* for getopt */
  64. extern int optind;
  65. extern char *optarg;
  66. extern int errno;
  67.  
  68. extern char *getrealpeername();
  69.  
  70. char *progname;
  71. unsigned int debug;
  72. FILE *dfp;
  73. long artmax = ARTMAX;    /* toss articles bigger than this size */
  74.  
  75. int sendarts, sendnope, postarts, postfail;
  76. static char *hostlock;
  77.  
  78. /*
  79.  * main - parse arguments and handle options
  80.  */
  81. int
  82. main(argc, argv)
  83. int argc;
  84. char *argv[];
  85. {
  86.     int c, errflg = 0;
  87.     char *hname;
  88.     extern long spacefor();
  89.  
  90.     progname = argv[0];
  91.     dfp = stderr;
  92.     while ((c = getopt(argc, argv, "dD:")) != EOF) {
  93.         switch (c) {
  94.         case 'd':
  95.             debug++;
  96.             break;
  97.         case 'D':
  98.             dfp = efopen(optarg, "a+");
  99.             break;
  100.         default:
  101.             errflg++;
  102.             break;
  103.         }
  104.     }
  105.  
  106.     sendarts = postarts = sendnope = postfail = 0;
  107.     hname = getrealpeername(0);
  108.     if (hname != NULL)
  109.         hostsetup(hname);
  110.     if (errflg || optind < argc) {
  111.         (void) fprintf(stderr, "usage: %s [-d] [-D debugfile]\n",
  112.                    progname);
  113.         unlock();
  114.         exit(1);
  115.     }
  116.  
  117.     (void) signal(SIGPIPE, SIG_IGN);    /* we check write returns */
  118.     if (access(ctlfile("LOCKnntp"), F_OK) == 0) {
  119.         dprintf(dfp, "%s seen\n", ctlfile("LOCKnntp"));
  120.         SAY(NNTP_DENIED, "NNTP reception temporarily disabled.");
  121.         unlock();
  122.         exit(0);
  123.     }
  124.  
  125.     ddprintf(dfp, "chdir(%s)\n", artfile(NULLSTR));
  126.     cd(artfile(NULLSTR));
  127.     if (spacefor(1L, artfile("in.coming"), 5000L, 1000L, 250000L) <= 0) {
  128.         sleep(10);        /* defeat rabid feeders */
  129.         SAY(NNTP_DENIED, "out of space; try again later");
  130.         unlock();
  131.         exit(0);
  132.     }
  133.  
  134.     SAY(NNTP_HI, NULLSTR);
  135.     process(stdin);
  136.     batchend();
  137.     if (sendarts > 0 || sendnope > 0)
  138.         log3int("sent %d missed %d articles", sendarts, sendnope, 0);
  139.     if (postarts > 0 || postfail > 0)
  140.         log3int("posted %d failed %d postings", postarts, postfail, 0);
  141.     unlock();
  142.     return 0;
  143. }
  144.  
  145. hostsetup(hname)
  146. char *hname;
  147. {
  148.     int fd, ret, ok = 1;
  149.     char *temp, *line = NULL;
  150.     FILE *fp;
  151.  
  152.     /* must be a socket, so we syslog and errlog */
  153.     loginit("snntpd");
  154.     logstr = strsave(hname);
  155.     if (freopen(ctlfile(LOGFILE), "a+", stderr) == NULL) {
  156.         SAY(NNTP_EH, "failed to open error log file");
  157.         exit(1);
  158.     }
  159.  
  160.     /* see if this host is allowed to send us netnews */
  161.     fp = fopen(ctlfile("nntp.allow"), "r");
  162.     if (fp != NULL) {
  163.         ok = 0;
  164.         while ((line = fgetln(fp)) != NULL) {
  165.             trim(line);
  166.             if (STREQ(line, hname) || domainmatch(line, hname)) {
  167.                 ok++;
  168.                 break;
  169.             }
  170.         }
  171.         (void) fclose(fp);
  172.     }
  173.     if (!ok) {            /* don't know this guy? */
  174.         /* 
  175.          * some scurvy vermin is trying to read news with NNTP,
  176.          * so kick off the reference nntpd (if any) to deal with him;
  177.          * we don't dirty our hands with such nonsense.
  178.          */
  179.         (void) execl("in.nntpd", "/usr/etc/in.nntpd", (char *)NULL);
  180.         (void) execl("nntpd", "/usr/etc/nntpd", (char *)NULL);
  181.         SAY(NNTP_EH, "sorry, we don't allow news reading via NNTP.");
  182.         exit(1);
  183.     }
  184.  
  185.     /* see if this host is already connected for transfer */
  186.     temp = strsave(ctlfile("L.XXXXXX"));
  187.     (void) mktemp(temp);
  188.     fd = creat(temp, 0444);
  189.     if (fd < 0) {
  190.         SAY(NNTP_EH, "can't make temporary");
  191.         exit(1);
  192.     }
  193.     (void) close(fd);
  194.  
  195.     hostlock = str3save(ctlfile("LOCK."), "", hname);
  196.     ret = link(temp, hostlock);
  197.     (void) unlink(temp);
  198.     if (ret < 0) {
  199.         sleep(10);        /* slow down rabid feeders */
  200.         SAY(NNTP_DENIED,    /* piss off, ya tit */
  201.     "NNTP transfer from your machine already in progress, greedy guts.");
  202.         exit(1);
  203.     }
  204.     free(temp);
  205.     /* we have the lock for this host now; remove at exit */
  206. }
  207.  
  208. static int
  209. domainmatch(dom, host)        /* is dom (.dom.ain) the end of host? */
  210. register char *dom, *host;
  211. {
  212.     register int domlen, hostlen;
  213.  
  214.     if (dom[0] != '.')
  215.         return 0;
  216.     domlen = strlen(dom);
  217.     hostlen = strlen(host);
  218.     if (hostlen <= domlen)
  219.         return 0;
  220.     return STREQ(dom, &host[hostlen-domlen]);
  221. }
  222.  
  223. unlock()
  224. {
  225.     if (hostlock != NULL)
  226.         (void) unlink(hostlock);
  227. }
  228.  
  229. /*
  230.  * process - process input file
  231.  */
  232. static void
  233. process(in)
  234. FILE *in;
  235. {
  236.     register char *line;
  237.     sizeint len;
  238.     struct netdata *ndp;
  239.  
  240.     ndp = net_new();
  241.     while ((line = net_cmdread(in, &len)) != NULL)
  242.         if (CISTREQN(line, "ihave", 5)) {
  243.             line = munge_msgid(line + 5);
  244.             readarticle(in, line, ndp);
  245.         } else if (CISTREQN(line, "quit", 4)) {
  246.             SAY(NNTP_BYE, NULLSTR);
  247.             return;
  248.         }
  249. #ifdef READING
  250.         else if (CISTREQN(line, "article", 7)) {
  251.             line = munge_msgid(line + 7);
  252.             sendarticle(line);
  253.         } else if (CISTREQN(line, "post", 4)) {
  254.             if (line[4] != '\0')
  255.                 SAY(NNTP_EH, NULLSTR);
  256.             else
  257.                 postarticle(in, ndp);
  258.         }
  259. #endif                        /* READING */
  260.         else
  261.             SAY(NNTP_EH, NULLSTR);
  262.     /* only get here if we hit an error on stdin or EOF */
  263.     unlock();
  264.     error("abruptly terminated", "");
  265. }
  266.  
  267. /*
  268.  * Skip leading whitespace, lowercase the hostpart.
  269.  */
  270. static char *
  271. munge_msgid(s)
  272. register char *s;
  273. {
  274.     while (isascii(*s) && isspace(*s))
  275.         s++;
  276.     s = rfc822ize(s);
  277.     ddprintf(dfp, "message-id is `%s'\n", s);
  278.     return s;
  279. }
  280.  
  281. static void
  282. readarticle(fp, msgid, ndp)
  283. FILE *fp;
  284. char *msgid;
  285. struct netdata *ndp;
  286. {
  287.     char *histent = histlook(msgid);
  288.  
  289.     if (histent != NULL || msgidseen(msgid)) {    /* got msgid already? */
  290. /*        ddprintf(dfp, "histent = `%s'\n", histent);    */
  291.         SAY(NNTP_NEXT, NULLSTR);
  292.         bt_rej++;
  293.     } else {
  294.         SAY(NNTP_WANT, NULLSTR);
  295.         if (net_getdata(fp, ndp) != 0 ||
  296.             batchadd(ndp->nd_buf, ndp->nd_bufcnt, ndp->nd_bytes,
  297.                  ndp->nd_fp, ndp->nd_spilled) != 0) {
  298.             SAY(NNTP_FAIL, NULLSTR);
  299.             bt_fail++;
  300.         } else
  301.             SAY(NNTP_OK, NULLSTR);
  302.     }
  303. }
  304.  
  305. int
  306. msgidseen(id)        /* has msgidd seen this id? */
  307. char *id;
  308. {
  309.     char *idcopy = strsave(id);
  310.     int ret = msgid(idcopy, MADD) != 0;
  311.  
  312.     free(idcopy);
  313.     return ret;
  314. }
  315.  
  316. static void
  317. sendarticle(msgid)
  318. char *msgid;
  319. {
  320.     char *histent = histlook(msgid), *cp;
  321. #define MAXFIELDS 3
  322.     char *fields[MAXFIELDS];
  323.     FILE *fp;
  324.     int len, nf;
  325.  
  326.     if (histent == NULL) {
  327.         SAY(NNTP_NO, NULLSTR);
  328.         sendnope++;
  329.         return;
  330.     } else {
  331.         nf = split(histent, fields, MAXFIELDS, "\t");
  332.         if (nf < 3) {
  333.             ddprintf(dfp, "history entry has %d fields\n", nf);
  334.             SAY(NNTP_NO, NULLSTR);
  335.             sendnope++;
  336.             return;
  337.         }
  338.         ddprintf(dfp, "files = `%s'\n", fields[2]);
  339.         cp = histslash(strtok(fields[2], " "));
  340.         ddprintf(dfp, "first file = `%s'\n", cp);
  341.         if (cp == NULL || *cp == '\0' ||(fp = fopen(cp, "r")) == NULL) {
  342.             SAY(NNTP_NO, NULLSTR);
  343.             sendnope++;
  344.             return;
  345.         }
  346.         SAY(NNTP_HERE, fields[0]);
  347.         while ((cp = fgetline(fp, &len)) != NULL) {
  348.             if (cp[--len] == '\n')
  349.                 cp[len] = '\0';
  350.             else
  351.                 len++;    /* shouldn't happen */
  352.             if (net_writeline(cp, len, stdout) != 0) {
  353.                 unlock();
  354.                 error("net_writeline(%s) failed", cp);
  355.             }
  356.         }
  357.         if (net_endwrite(stdout) != 0) {
  358.             unlock();
  359.             error("net_endwrite(stdout) failed", "");
  360.         }
  361.         sendarts++;
  362.         (void) fclose(fp);
  363.     }
  364. }
  365.