home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume3 / sm-smtp / dconverse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-02-03  |  12.3 KB  |  561 lines

  1. #ifndef lint
  2. static char *sccsid = "@(#)converse.c    1.8 87/05/14";
  3. #endif lint
  4. /*  Copyright 1984 Massachusetts Institute of Technology
  5.  
  6. Permission to use, copy, modify, and distribute this program
  7. for any purpose and without fee is hereby granted, provided
  8. that this copyright and permission notice appear on all copies
  9. and supporting documentation, the name of M.I.T. not be used
  10. in advertising or publicity pertaining to distribution of the
  11. program without specific prior permission, and notice be given
  12. in supporting documentation that copying and distribution is
  13. by permission of M.I.T.  M.I.T. makes no representations about
  14. the suitability of this software for any purpose.  It is pro-
  15. vided "as is" without express or implied warranty.        */
  16.  
  17. /*
  18.  * smtpd - World's most trivial SMTP server.  Only accepts the MAIL, FROM,
  19.  * RCPT, and DATA commands.  Generates a date file for the mail
  20.  * daemon and kicks the mail daemon off.
  21.  */
  22.  
  23. #include "smtp.h"
  24.  
  25. #ifdef BSD
  26. #include <sgtty.h>
  27. #endif
  28. /* #include <ioctl.h> */
  29. #include <signal.h>
  30. #include <sys/uio.h>
  31. #include <sys/socket.h>
  32. #include <netinet/in.h>
  33.  
  34. #include "cmds.h"
  35.  
  36. /* tunable constants */
  37.  
  38. #define SECONDS        1
  39. #define MINUTES        60
  40. #define HOURS        (60 * MINUTES)
  41.  
  42. #define    SHORTTIME    (5 * MINUTES)    /* enough time for easy stuff */
  43. #define    LONGTIME    (2 * HOURS)    /* max time, DATA to `.' */
  44.  
  45. #define    DATAMODE    0660        /* mode for data file */
  46.  
  47. #ifdef MAILER
  48. char *sigmaild = MAILER;
  49. #else
  50. char *sigmaild = "/bin/rmail";
  51. #endif
  52.  
  53. typedef long in_name;            /* internet host address */
  54.  
  55. int    buflen;                /* size of string in cmd buffer */
  56.  
  57. static char rcptlist[MAXSTR];        /* recipient list */
  58. static char *rcptlast;            /* end of rcptlist */
  59.  
  60. FILE    *datafd;            /* data file descriptor */
  61.  
  62. char    dataname[NAMSIZ];        /* data file name */
  63.  
  64. typedef int event;
  65.  
  66. extern int death();
  67. extern int alarmtr();
  68.  
  69. extern char *strcpy();
  70. extern char *index();
  71. extern char *rindex();
  72. extern char *strcpy(), *strcat();
  73.  
  74. extern char hostdomain[];
  75. extern char hostname[];
  76. extern char arpanows[];
  77.  
  78. #ifdef SIMPLELOG
  79. #include <sys/file.h>
  80. static char mailfrom[MAXSTR], rcptto[MAXSTR];
  81. #endif
  82.  
  83. /*
  84.  * This is the routine which processes incoming smtp commands from the
  85.  * user.  It goes to sleep awaiting network input.  When a complete
  86.  * command is received, the tcp receiver task awakens us to process it.
  87.  * Currently only the commands listed in the command table are accepted.
  88.  * This routine never returns.
  89.  */
  90. /* ARGSUSED from */
  91. converse(fi, fo, from)
  92. FILE *fi, *fo;
  93. struct sockaddr_in *from;
  94. {
  95.     char greeting[MAXSTR];
  96.  
  97.     (void) chdir("/tmp");        /* put temp files somewhere sensible */
  98.     (void) signal(SIGALRM, TYPESIG alarmtr);
  99.     (void) alarm(SHORTTIME);        /* make sure we eventually go away */
  100.     setdates ();
  101.     (void) sprintf(greeting, "220 %s SMTP server ready at %s\n",
  102.         hostdomain, arpanows);
  103.     (void) tputs(greeting, fo);
  104.     do_helo(fi, fo);        /* wait for the hello */
  105.     for (;;) {            /* until QUIT */
  106.         do_mail(fi, fo);    /* wait for the mail command */
  107.         while (do_rcpt(fi, fo))    /* do all the recipients */
  108.             ;
  109.         (void) alarm(LONGTIME);
  110.         do_data(fi, fo);    /* do the data */
  111.     }
  112. }
  113.  
  114. /*
  115.  * Wait for the user to send the HELO command.  Punt out if he sends
  116.  * QUIT or RSET.
  117.  */
  118. do_helo(fi, fo)
  119. FILE *fi, *fo;
  120. {
  121.     char    cmdbuf[MAXSTR];
  122.     char    greeting[MAXSTR];
  123.  
  124.     for (;;) {        /* until HELO, QUIT, or RSET */
  125.         buflen = tgets(cmdbuf, sizeof cmdbuf, fi);    /* wait for command */
  126.         switch (cmdparse(cmdbuf, buflen)) {
  127.         case QUIT:
  128.         case RSET:
  129.             quit(fi, fo);
  130.         case NOOP:
  131.             (void) tputs("250 OK\n", fo);
  132.             continue;
  133.         case HELO:
  134.             (void) sprintf(greeting, "250 %s Pleased to meet you\n", hostname);
  135.             (void) tputs(greeting, fo);
  136.             return;
  137.         case NONE:
  138.             bitch(cmdbuf, fo);
  139.             continue;
  140.         default:
  141.             (void) tputs("503 Expecting HELO\n", fo);
  142.             continue;
  143.         }
  144.     }
  145. }
  146.  
  147. /*
  148.  * Wait for the user to send the MAIL command.  Punt out if he sends
  149.  * QUIT or RSET.
  150.  */
  151. do_mail(fi, fo)
  152. FILE *fi, *fo;
  153. {
  154.     char    cmdbuf[MAXSTR];
  155.  
  156.     for (;;) {        /* until MAIL, QUIT, or RSET */
  157.         buflen = tgets(cmdbuf, sizeof cmdbuf, fi);    /* wait for command */
  158.         switch (cmdparse(cmdbuf, buflen)) {
  159.         case QUIT:
  160.         case RSET:
  161.             quit(fi, fo);
  162.         case NOOP:
  163.             (void) tputs("250 OK\n", fo);
  164.             continue;
  165.         case MAIL:
  166. #ifdef SIMPLELOG
  167.             strcpy(mailfrom, cmdbuf);
  168. #endif
  169.             (void) tputs("250 OK\n", fo);
  170.             return;
  171.         case NONE:
  172.             bitch(cmdbuf, fo);
  173.             continue;
  174.         default:
  175.             (void) tputs("503 Expecting MAIL\n", fo);
  176.             continue;
  177.         }
  178.     }
  179. }
  180.  
  181. /*
  182.  * Wait for the user to send the RCPT command.  Punt out if he sends
  183.  * QUIT or RSET.  Returns TRUE if a RCPT command was received, FALSE
  184.  * if a DATA command was received.
  185.  */
  186. do_rcpt(fi, fo)
  187. FILE *fi, *fo;
  188. {
  189.     char    cmdbuf[MAXSTR];
  190.  
  191.     for (;;) {        /* until RCPT, DATA, QUIT, or RSET */
  192.         buflen = tgets(cmdbuf, sizeof cmdbuf, fi);    /* wait for command */
  193.         switch (cmdparse(cmdbuf, buflen)) {
  194.         case QUIT:
  195.         case RSET:
  196.             quit(fi, fo);
  197.         case NOOP:
  198.             (void) tputs("250 OK\n", fo);
  199.             continue;
  200.         case RCPT:
  201. #ifdef SIMPLELOG
  202.             strcat(rcptto, cmdbuf);
  203. #endif
  204.             if (!parse_rcpt(cmdbuf, buflen)) {
  205.                 (void) tputs("501 Syntax error in recipient name\n", fo);
  206.                 continue;
  207.             }
  208.             (void) tputs("250 OK\n", fo);
  209.             return(TRUE);
  210.         case DATA:
  211.             if (*rcptlist == 0) {
  212.                 (void) tputs("503 Expecting RCPT\n", fo);
  213.                 continue;
  214.             }
  215.             if (!init_xfr()) {    /* set up data file */
  216.                 (void) tputs("451 Can't initialize transfer\n", fo);
  217.                 death(E_CANTOPEN);
  218.             }
  219.             (void) tputs("354 Start mail input; end with <CRLF>.<CRLF>\n", fo);
  220.             return(FALSE);
  221.         case NONE:
  222.             bitch(cmdbuf, fo);
  223.             continue;
  224.         default:
  225.             (void) tputs("503 Expecting RCPT or DATA\n", fo);
  226.             continue;
  227.         }
  228.     }
  229. }
  230.  
  231. do_data(fi, fo)
  232. FILE *fi, *fo;
  233. {
  234.     char cmd[MAXSTR];
  235.     register char *buf = cmd;
  236.     int sysret;
  237.  
  238.     setdates ();
  239.     fprintf (datafd, "Received: by %s with SMTP; %s\n",
  240.         hostdomain, arpanows);
  241.     for (;;) {
  242.         if (tgets(buf, sizeof cmd, fi) < 0)
  243.             death(E_IOERR);
  244.         if (*buf == '.') {
  245.             buf++;    /* hidden dot */
  246.             if (*buf == '\n')
  247.                 break;
  248.         }
  249.         (void) fputs(buf, datafd);
  250.     }
  251.  
  252.     (void) fclose(datafd);
  253.  
  254.     /* run mailer with rcptlist as args and message as input */
  255.     /* TODO: check system status to see if OK */
  256.     (void) sprintf(cmd, "%s %s <%s", sigmaild, rcptlist, dataname);
  257.     sysret = system(cmd);
  258.     if (sysret == 0)
  259.         (void) tputs("250 OK\n", fo);
  260.     else
  261.         (void) tputs("554 Transaction failed\n", fo);
  262.  
  263. #ifdef SIMPLELOG
  264.     simplelog(abs(sysret));
  265. #endif
  266.  
  267.     /* shouldn't leave it around, but ... */
  268.     if (sysret == 0)
  269.         (void) unlink(dataname);    /* remove temporaries */
  270.  
  271.     *dataname = *rcptlist = *rcptto = 0;
  272.     rcptlast = 0;
  273. }
  274.  
  275. /*
  276.  * Create the data file for the transfer.  Get unique
  277.  * names and create the files.
  278.  */
  279. init_xfr()
  280. {
  281.     int    dfd;            /* file desc. for data file */
  282.  
  283.     (void) tmpnam(dataname);
  284.     
  285.     if ((dfd = creat(dataname, DATAMODE)) < 0)
  286.         return FALSE;
  287.     datafd = fdopen(dfd, "w");    /* make stdio descriptor */
  288.     if (datafd == NULL)
  289.         return FALSE;
  290.  
  291.     
  292.     return TRUE;
  293. }
  294.  
  295. /*
  296.  * Give up on the transfer.  Unlink the data file (if any),
  297.  * close the tcp connection, and exit.
  298.  */
  299. quit(fi, fo)
  300. FILE *fi, *fo;
  301. {
  302.     char greeting[MAXSTR];
  303.  
  304.     (void) sprintf(greeting, "221 %s Terminating\n", hostname);
  305.     (void) tputs(greeting, fo);
  306.     (void) fclose(fi);
  307.     (void) fclose(fo);
  308.     exit(0);
  309. }
  310.  
  311. /*
  312.  * Parse the command part off the specified buffer.  Return the index
  313.  * of the command in the command table(or 0 if the command is not
  314.  * recognized).
  315.  * The commands and indices accepted are listed in the include file
  316.  * "cmds.h".
  317.  */
  318. cmdparse(buf, len)
  319. char *buf;
  320. int len;
  321. {
  322.     register char *cmdp, *bufp;    /* command, buffer ptrs. */
  323.     register struct    cmdtab    *ct;    /* cmd table ptr */
  324.     register int i;            /* index in cmd table */
  325.     int    clen;            /* length of this command */
  326.     
  327.     for (ct = &cmdtab[1], i = 1; ct->c_name != NULL; ct++, i++) {
  328.         clen = ct->c_len;
  329.         if (len < clen)        /* buffer shorter than command? */
  330.             continue;
  331.         /* case-insensitive matching of command names */
  332.         for (cmdp = ct->c_name, bufp = buf;
  333.              clen > 0 && *cmdp == toupper(*bufp);
  334.              cmdp++, bufp++, clen--)
  335.             ;
  336.         if (clen == 0) {        /* success */
  337.             /* sendmail compatibility */
  338.             if (i == ONEX || i == VERB)
  339.                 i = NOOP;
  340.             return i;
  341.         }
  342.     }
  343.     return 0;
  344. }
  345.  
  346. static    char    *to;            /* ptr. into request buffer */
  347.  
  348. /*
  349.  * Parse the recipient spec in the buffer.  Start by stripping the
  350.  * command off the front of the buffer.  Then call canon() to convert
  351.  * the recpient name into a format acceptable to the mailer daemon
  352.  * (ie. the original multiple-at-sign format).
  353.  * Returns TRUE if parsed successfully, FALSE otherwise.
  354.  */
  355. /* ARGSUSED len */
  356. parse_rcpt(buf, len)
  357. char *buf;                /* command buffer */
  358. int len;                /* size of buffer string */
  359. {
  360.     register char *from;        /* ptr to recipient name */
  361.     char *end;
  362.     
  363.     from = &buf[cmdtab[RCPT].c_len];
  364.     while (*from == ' ' || *from == '\t')
  365.         from++;
  366.     if (*from == '<') {
  367.         end = index(from++, '>');
  368.         if (end == 0) {
  369.             (void) printf("no > at end of string\n");
  370.             return FALSE;
  371.         }
  372.         *end = 0;
  373.     }
  374.     if (rcptlast) {
  375.         rcptlast += strlen(rcptlast);
  376.         *rcptlast++ = ' ';
  377.     } else
  378.         rcptlast = rcptlist;
  379.     /* NB: we use the canonical name even if `bad' */
  380.     if (canon(from, rcptlast))    /* canonicalize */
  381. #ifdef DEBUG
  382.         (void) printf("parsed ok: %s\n", rcptlast);
  383.     else
  384.         (void) printf("parsed bad: %s\n", rcptlast);
  385. #endif
  386.     ;
  387.     return TRUE;
  388. }
  389.  
  390. /*
  391.  * Canonicalize the smtp-style path pointed to by from into the buffer
  392.  * pointed to by the external static variable to.  The result will be
  393.  * a string containing the multiple-at-sign form, as desired by the
  394.  * mailer daemon.  Also removes the '\' escape characters.
  395.  * The procedure follwed is recursive: this routine is recursively
  396.  * called for each "@host" in the from string.
  397.  * Returns TRUE if successful, or FALSE if the format of the recipient
  398.  * name is bad.
  399.  */
  400. rcanon(from)
  401. register char    *from;            /* start of string to canonicalize */
  402. {
  403.     register char    *end;        /* end of this part of path */
  404.     register int    escseen;    /* escape character seen */
  405.     int    atseen;            /* '@' seen in mailbox */
  406.     
  407.     escseen = atseen = FALSE;
  408.     if (*from == '@') {        /* host name; find end */
  409.         for (end = from; *end != '\0'; end++) {
  410.             if (escseen)
  411.                 escseen = FALSE;
  412.             else if (*end == '\\') /* escape? */
  413.                 escseen = TRUE;
  414.             else if (*end == ',' || *end == ':')
  415.                 break;
  416.         }
  417.         if (*end == '\0' || !rcanon(end+1)) { /* bad format? */
  418. #ifdef PICKY /*{*/
  419.             (void) printf("no mailbox found\n");
  420.             return FALSE;
  421.         } else
  422. #else
  423.         }
  424. #endif                /* PICKY */
  425.         {
  426.             escseen = FALSE;
  427.             for (*from = '%'; from < end; from++) { /* copy into to buffer */
  428.                 if (escseen)
  429.                     escseen = FALSE;
  430.                 else if (*from == '\\') {
  431.                     escseen = TRUE;
  432.                     continue;
  433.                 }
  434.                 *to++ = *from;
  435.             }
  436.             *to = '\0';
  437.             return TRUE;
  438.         }
  439.     } else {
  440.         for (; *from; from++) {    /* copy mailbox */
  441.             if (escseen)
  442.                 escseen = FALSE;
  443.             else if (*from == '\\') {
  444.                 escseen = TRUE;
  445.                 continue;
  446.             } else if (*from == '@') { /* end of username? */
  447.                 (void) printf("found @ in mailbox\n");
  448.                 *from = '%';
  449.                 atseen = TRUE;
  450.             }
  451.             *to++ = *from;
  452.         }
  453.         *to = 0;
  454.         return atseen;
  455.     }
  456. }
  457.  
  458. /* Time to live elapsed or io error. */
  459. death(weapon)
  460. {
  461. #ifdef SIMPLELOG
  462.     simplelog(weapon);
  463. #endif
  464.     (void) printf("Time to die.\n");
  465.     /*(void) unlink(dataname);*/
  466.     exit(1);
  467. }
  468.  
  469. alarmtr()
  470. {
  471.     death(E_TEMPFAIL);
  472. }
  473.  
  474. canon(in, out)
  475. char    *in, *out;
  476. {
  477.     char *at;
  478.  
  479.     to = out;
  480.     if (funnychars(in) || !rcanon(in) || (at = rindex(out, '%')) == 0)
  481.         return(FALSE);
  482.     *at = '@';
  483.     return TRUE;
  484. }
  485.  
  486. funnychars(str)
  487. register char *str;
  488. {
  489.  
  490.     for (;;)
  491.         switch(*str++) {
  492.         case '^':
  493.         case '&':
  494.         case '>':
  495.         case '<':
  496.         case '`':
  497.         case '|':
  498.         case ';':
  499.         case '\'':
  500.             return TRUE;
  501.  
  502.         case 0:
  503.             return FALSE;
  504.         }
  505. }
  506.  
  507. #ifdef SIMPLELOG
  508. simplelog(retcode)
  509. {
  510.     char buf[1024], *bptr, *status;
  511.     int fd;
  512.     time_t t;
  513.     extern char *ctime();
  514.     extern time_t time();
  515.  
  516.     t = time(&t);
  517.     switch (retcode) {
  518.     case E_CANTOPEN:
  519.         status = "OPEN FAILED";
  520.         break;
  521.     case E_IOERR:
  522.         status = "IO ERROR";
  523.         break;
  524.     case E_TEMPFAIL:
  525.         status = "TIMED OUT";
  526.         break;
  527.     case 0:
  528.         status = "OK";
  529.         break;
  530.     default:
  531.         status = "DELIVERY FAILURE";
  532.         break;
  533.     }
  534.     if (*mailfrom == 0)
  535.         strcpy(mailfrom, "UNKNOWN");
  536.     if (*rcptto == 0)
  537.         strcpy(rcptto, "UNKNOWN");
  538.     (void) sprintf(buf, "%s %s %s %s", mailfrom, rcptto, status, ctime(&t));
  539.     for (bptr = buf; *bptr; bptr++)
  540.         if (*bptr == '\n' || *bptr == '\r')
  541.             *bptr = ' ';
  542.     strcat(bptr, "\n");
  543.     if ((fd = open("/tmp/smtpd.log", O_WRONLY|O_APPEND)) >= 0) {
  544.         (void) write(fd, buf, strlen(buf));
  545.         (void) close(fd);
  546.     }
  547. }
  548. #endif
  549.  
  550. bitch(buf, fo)
  551. char *buf;
  552. FILE *fo;
  553. {
  554.     char gripe[MAXSTR], *nlptr;
  555.  
  556.     if ((nlptr = index(buf, '\n')) != 0)
  557.         *nlptr = 0;
  558.     (void) sprintf(gripe, "502 %s ... Not recognized\n", buf);
  559.     (void) tputs(gripe, fo);
  560. }
  561.