home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.sbin / sendmail / src / srvrsmtp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-10  |  14.6 KB  |  659 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. All advertising materials mentioning features or use of this software
  15.  *    must display the following acknowledgement:
  16.  *    This product includes software developed by the University of
  17.  *    California, Berkeley and its contributors.
  18.  * 4. Neither the name of the University nor the names of its contributors
  19.  *    may be used to endorse or promote products derived from this software
  20.  *    without specific prior written permission.
  21.  *
  22.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32.  * SUCH DAMAGE.
  33.  */
  34.  
  35. # include "sendmail.h"
  36.  
  37. #ifndef lint
  38. #ifdef SMTP
  39. static char sccsid[] = "@(#)srvrsmtp.c    5.31 (Berkeley) 5/10/91 (with SMTP)";
  40. #else
  41. static char sccsid[] = "@(#)srvrsmtp.c    5.31 (Berkeley) 5/10/91 (without SMTP)";
  42. #endif
  43. #endif /* not lint */
  44.  
  45. # include <errno.h>
  46. # include <signal.h>
  47.  
  48. # ifdef SMTP
  49.  
  50. /*
  51. **  SMTP -- run the SMTP protocol.
  52. **
  53. **    Parameters:
  54. **        none.
  55. **
  56. **    Returns:
  57. **        never.
  58. **
  59. **    Side Effects:
  60. **        Reads commands from the input channel and processes
  61. **            them.
  62. */
  63.  
  64. struct cmd
  65. {
  66.     char    *cmdname;    /* command name */
  67.     int    cmdcode;    /* internal code, see below */
  68. };
  69.  
  70. /* values for cmdcode */
  71. # define CMDERROR    0    /* bad command */
  72. # define CMDMAIL    1    /* mail -- designate sender */
  73. # define CMDRCPT    2    /* rcpt -- designate recipient */
  74. # define CMDDATA    3    /* data -- send message text */
  75. # define CMDRSET    4    /* rset -- reset state */
  76. # define CMDVRFY    5    /* vrfy -- verify address */
  77. # define CMDHELP    6    /* help -- give usage info */
  78. # define CMDNOOP    7    /* noop -- do nothing */
  79. # define CMDQUIT    8    /* quit -- close connection and die */
  80. # define CMDHELO    9    /* helo -- be polite */
  81. # define CMDONEX    10    /* onex -- sending one transaction only */
  82. # define CMDVERB    11    /* verb -- go into verbose mode */
  83. /* debugging-only commands, only enabled if SMTPDEBUG is defined */
  84. # define CMDDBGQSHOW    12    /* showq -- show send queue */
  85. # define CMDDBGDEBUG    13    /* debug -- set debug mode */
  86.  
  87. static struct cmd    CmdTab[] =
  88. {
  89.     "mail",        CMDMAIL,
  90.     "rcpt",        CMDRCPT,
  91.     "data",        CMDDATA,
  92.     "rset",        CMDRSET,
  93.     "vrfy",        CMDVRFY,
  94.     "expn",        CMDVRFY,
  95.     "help",        CMDHELP,
  96.     "noop",        CMDNOOP,
  97.     "quit",        CMDQUIT,
  98.     "helo",        CMDHELO,
  99.     "verb",        CMDVERB,
  100.     "onex",        CMDONEX,
  101.     /*
  102.      * remaining commands are here only
  103.      * to trap and log attempts to use them
  104.      */
  105.     "showq",    CMDDBGQSHOW,
  106.     "debug",    CMDDBGDEBUG,
  107.     NULL,        CMDERROR,
  108. };
  109.  
  110. bool    InChild = FALSE;        /* true if running in a subprocess */
  111. bool    OneXact = FALSE;        /* one xaction only this run */
  112.  
  113. #define EX_QUIT        22        /* special code for QUIT command */
  114.  
  115. smtp()
  116. {
  117.     register char *p;
  118.     register struct cmd *c;
  119.     char *cmd;
  120.     static char *skipword();
  121.     bool hasmail;            /* mail command received */
  122.     auto ADDRESS *vrfyqueue;
  123.     ADDRESS *a;
  124.     char *sendinghost;
  125.     char inp[MAXLINE];
  126.     char cmdbuf[100];
  127.     extern char Version[];
  128.     extern char *macvalue();
  129.     extern ADDRESS *recipient();
  130.     extern ENVELOPE BlankEnvelope;
  131.     extern ENVELOPE *newenvelope();
  132.  
  133.     hasmail = FALSE;
  134.     if (OutChannel != stdout)
  135.     {
  136.         /* arrange for debugging output to go to remote host */
  137.         (void) close(1);
  138.         (void) dup(fileno(OutChannel));
  139.     }
  140.     settime();
  141.     if (RealHostName != NULL)
  142.     {
  143.         CurHostName = RealHostName;
  144.         setproctitle("srvrsmtp %s", CurHostName);
  145.     }
  146.     else
  147.     {
  148.         /* this must be us!! */
  149.         CurHostName = MyHostName;
  150.     }
  151.     expand("\001e", inp, &inp[sizeof inp], CurEnv);
  152.     message("220", inp);
  153.     SmtpPhase = "startup";
  154.     sendinghost = NULL;
  155.     for (;;)
  156.     {
  157.         /* arrange for backout */
  158.         if (setjmp(TopFrame) > 0 && InChild)
  159.             finis();
  160.         QuickAbort = FALSE;
  161.         HoldErrs = FALSE;
  162.  
  163.         /* setup for the read */
  164.         CurEnv->e_to = NULL;
  165.         Errors = 0;
  166.         (void) fflush(stdout);
  167.  
  168.         /* read the input line */
  169.         p = sfgets(inp, sizeof inp, InChannel);
  170.  
  171.         /* handle errors */
  172.         if (p == NULL)
  173.         {
  174.             /* end of file, just die */
  175.             message("421", "%s Lost input channel from %s",
  176.                 MyHostName, CurHostName);
  177.             finis();
  178.         }
  179.  
  180.         /* clean up end of line */
  181.         fixcrlf(inp, TRUE);
  182.  
  183.         /* echo command to transcript */
  184.         if (CurEnv->e_xfp != NULL)
  185.             fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
  186.  
  187.         /* break off command */
  188.         for (p = inp; isspace(*p); p++)
  189.             continue;
  190.         cmd = p;
  191.         for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
  192.             *cmd++ = *p++;
  193.         *cmd = '\0';
  194.  
  195.         /* throw away leading whitespace */
  196.         while (isspace(*p))
  197.             p++;
  198.  
  199.         /* decode command */
  200.         for (c = CmdTab; c->cmdname != NULL; c++)
  201.         {
  202.             if (!strcasecmp(c->cmdname, cmdbuf))
  203.                 break;
  204.         }
  205.  
  206.         /* process command */
  207.         switch (c->cmdcode)
  208.         {
  209.           case CMDHELO:        /* hello -- introduce yourself */
  210.             SmtpPhase = "HELO";
  211.             setproctitle("%s: %s", CurHostName, inp);
  212.             if (!strcasecmp(p, MyHostName))
  213.             {
  214.                 /*
  215.                  * didn't know about alias,
  216.                  * or connected to an echo server
  217.                  */
  218.                 message("553", "%s config error: mail loops back to myself",
  219.                     MyHostName);
  220.                 break;
  221.             }
  222.             if (RealHostName != NULL && strcasecmp(p, RealHostName))
  223.             {
  224.                 char hostbuf[MAXNAME];
  225.  
  226.                 (void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
  227.                 sendinghost = newstr(hostbuf);
  228.             }
  229.             else
  230.                 sendinghost = newstr(p);
  231.             message("250", "%s Hello %s, pleased to meet you",
  232.                 MyHostName, sendinghost);
  233.             break;
  234.  
  235.           case CMDMAIL:        /* mail -- designate sender */
  236.             SmtpPhase = "MAIL";
  237.  
  238.             /* force a sending host even if no HELO given */
  239.             if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
  240.                 sendinghost = RealHostName;
  241.  
  242.             /* check for validity of this command */
  243.             if (hasmail)
  244.             {
  245.                 message("503", "Sender already specified");
  246.                 break;
  247.             }
  248.             if (InChild)
  249.             {
  250.                 errno = 0;
  251.                 syserr("Nested MAIL command");
  252.                 exit(0);
  253.             }
  254.  
  255.             /* fork a subprocess to process this command */
  256.             if (runinchild("SMTP-MAIL") > 0)
  257.                 break;
  258.             define('s', sendinghost, CurEnv);
  259.             define('r', "SMTP", CurEnv);
  260.             initsys();
  261.             setproctitle("%s %s: %s", CurEnv->e_id,
  262.                 CurHostName, inp);
  263.  
  264.             /* child -- go do the processing */
  265.             p = skipword(p, "from");
  266.             if (p == NULL)
  267.                 break;
  268.             setsender(p);
  269.             if (Errors == 0)
  270.             {
  271.                 message("250", "Sender ok");
  272.                 hasmail = TRUE;
  273.             }
  274.             else if (InChild)
  275.                 finis();
  276.             break;
  277.  
  278.           case CMDRCPT:        /* rcpt -- designate recipient */
  279.             SmtpPhase = "RCPT";
  280.             setproctitle("%s %s: %s", CurEnv->e_id,
  281.                 CurHostName, inp);
  282.             if (setjmp(TopFrame) > 0)
  283.             {
  284.                 CurEnv->e_flags &= ~EF_FATALERRS;
  285.                 break;
  286.             }
  287.             QuickAbort = TRUE;
  288.             p = skipword(p, "to");
  289.             if (p == NULL)
  290.                 break;
  291.             a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
  292.             if (a == NULL)
  293.                 break;
  294.             a->q_flags |= QPRIMARY;
  295.             a = recipient(a, &CurEnv->e_sendqueue);
  296.             if (Errors != 0)
  297.                 break;
  298.  
  299.             /* no errors during parsing, but might be a duplicate */
  300.             CurEnv->e_to = p;
  301.             if (!bitset(QBADADDR, a->q_flags))
  302.                 message("250", "Recipient ok");
  303.             else
  304.             {
  305.                 /* punt -- should keep message in ADDRESS.... */
  306.                 message("550", "Addressee unknown");
  307.             }
  308.             CurEnv->e_to = NULL;
  309.             break;
  310.  
  311.           case CMDDATA:        /* data -- text of mail */
  312.             SmtpPhase = "DATA";
  313.             if (!hasmail)
  314.             {
  315.                 message("503", "Need MAIL command");
  316.                 break;
  317.             }
  318.             else if (CurEnv->e_nrcpts <= 0)
  319.             {
  320.                 message("503", "Need RCPT (recipient)");
  321.                 break;
  322.             }
  323.  
  324.             /* collect the text of the message */
  325.             SmtpPhase = "collect";
  326.             setproctitle("%s %s: %s", CurEnv->e_id,
  327.                 CurHostName, inp);
  328.             collect(TRUE);
  329.             if (Errors != 0)
  330.                 break;
  331.  
  332.             /*
  333.             **  Arrange to send to everyone.
  334.             **    If sending to multiple people, mail back
  335.             **        errors rather than reporting directly.
  336.             **    In any case, don't mail back errors for
  337.             **        anything that has happened up to
  338.             **        now (the other end will do this).
  339.             **    Truncate our transcript -- the mail has gotten
  340.             **        to us successfully, and if we have
  341.             **        to mail this back, it will be easier
  342.             **        on the reader.
  343.             **    Then send to everyone.
  344.             **    Finally give a reply code.  If an error has
  345.             **        already been given, don't mail a
  346.             **        message back.
  347.             **    We goose error returns by clearing error bit.
  348.             */
  349.  
  350.             SmtpPhase = "delivery";
  351.             if (CurEnv->e_nrcpts != 1)
  352.             {
  353.                 HoldErrs = TRUE;
  354.                 ErrorMode = EM_MAIL;
  355.             }
  356.             CurEnv->e_flags &= ~EF_FATALERRS;
  357.             CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
  358.  
  359.             /* send to all recipients */
  360.             sendall(CurEnv, SM_DEFAULT);
  361.             CurEnv->e_to = NULL;
  362.  
  363.             /* save statistics */
  364.             markstats(CurEnv, (ADDRESS *) NULL);
  365.  
  366.             /* issue success if appropriate and reset */
  367.             if (Errors == 0 || HoldErrs)
  368.                 message("250", "Ok");
  369.             else
  370.                 CurEnv->e_flags &= ~EF_FATALERRS;
  371.  
  372.             /* if in a child, pop back to our parent */
  373.             if (InChild)
  374.                 finis();
  375.  
  376.             /* clean up a bit */
  377.             hasmail = 0;
  378.             dropenvelope(CurEnv);
  379.             CurEnv = newenvelope(CurEnv);
  380.             CurEnv->e_flags = BlankEnvelope.e_flags;
  381.             break;
  382.  
  383.           case CMDRSET:        /* rset -- reset state */
  384.             message("250", "Reset state");
  385.             if (InChild)
  386.                 finis();
  387.             break;
  388.  
  389.           case CMDVRFY:        /* vrfy -- verify address */
  390.             if (runinchild("SMTP-VRFY") > 0)
  391.                 break;
  392.             setproctitle("%s: %s", CurHostName, inp);
  393.             vrfyqueue = NULL;
  394.             QuickAbort = TRUE;
  395.             sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
  396.             if (Errors != 0)
  397.             {
  398.                 if (InChild)
  399.                     finis();
  400.                 break;
  401.             }
  402.             while (vrfyqueue != NULL)
  403.             {
  404.                 register ADDRESS *a = vrfyqueue->q_next;
  405.                 char *code;
  406.  
  407.                 while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
  408.                     a = a->q_next;
  409.  
  410.                 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
  411.                 {
  412.                     if (a != NULL)
  413.                         code = "250-";
  414.                     else
  415.                         code = "250";
  416.                     if (vrfyqueue->q_fullname == NULL)
  417.                         message(code, "<%s>", vrfyqueue->q_paddr);
  418.                     else
  419.                         message(code, "%s <%s>",
  420.                             vrfyqueue->q_fullname, vrfyqueue->q_paddr);
  421.                 }
  422.                 else if (a == NULL)
  423.                     message("554", "Self destructive alias loop");
  424.                 vrfyqueue = a;
  425.             }
  426.             if (InChild)
  427.                 finis();
  428.             break;
  429.  
  430.           case CMDHELP:        /* help -- give user info */
  431.             help(p);
  432.             break;
  433.  
  434.           case CMDNOOP:        /* noop -- do nothing */
  435.             message("200", "OK");
  436.             break;
  437.  
  438.           case CMDQUIT:        /* quit -- leave mail */
  439.             message("221", "%s closing connection", MyHostName);
  440.             if (InChild)
  441.                 ExitStat = EX_QUIT;
  442.             finis();
  443.  
  444.           case CMDVERB:        /* set verbose mode */
  445.             Verbose = TRUE;
  446.             SendMode = SM_DELIVER;
  447.             message("200", "Verbose mode");
  448.             break;
  449.  
  450.           case CMDONEX:        /* doing one transaction only */
  451.             OneXact = TRUE;
  452.             message("200", "Only one transaction");
  453.             break;
  454.  
  455. # ifdef SMTPDEBUG
  456.           case CMDDBGQSHOW:    /* show queues */
  457.             printf("Send Queue=");
  458.             printaddr(CurEnv->e_sendqueue, TRUE);
  459.             break;
  460.  
  461.           case CMDDBGDEBUG:    /* set debug mode */
  462.             tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
  463.             tTflag(p);
  464.             message("200", "Debug set");
  465.             break;
  466.  
  467. # else /* not SMTPDEBUG */
  468.  
  469.           case CMDDBGQSHOW:    /* show queues */
  470.           case CMDDBGDEBUG:    /* set debug mode */
  471. # ifdef LOG
  472.             if (RealHostName != NULL && LogLevel > 0)
  473.                 syslog(LOG_NOTICE,
  474.                     "\"%s\" command from %s (%s)\n",
  475.                     c->cmdname, RealHostName,
  476.                     inet_ntoa(RealHostAddr.sin_addr));
  477. # endif
  478.             /* FALL THROUGH */
  479. # endif /* SMTPDEBUG */
  480.  
  481.           case CMDERROR:    /* unknown command */
  482.             message("500", "Command unrecognized");
  483.             break;
  484.  
  485.           default:
  486.             errno = 0;
  487.             syserr("smtp: unknown code %d", c->cmdcode);
  488.             break;
  489.         }
  490.     }
  491. }
  492. /*
  493. **  SKIPWORD -- skip a fixed word.
  494. **
  495. **    Parameters:
  496. **        p -- place to start looking.
  497. **        w -- word to skip.
  498. **
  499. **    Returns:
  500. **        p following w.
  501. **        NULL on error.
  502. **
  503. **    Side Effects:
  504. **        clobbers the p data area.
  505. */
  506.  
  507. static char *
  508. skipword(p, w)
  509.     register char *p;
  510.     char *w;
  511. {
  512.     register char *q;
  513.  
  514.     /* find beginning of word */
  515.     while (isspace(*p))
  516.         p++;
  517.     q = p;
  518.  
  519.     /* find end of word */
  520.     while (*p != '\0' && *p != ':' && !isspace(*p))
  521.         p++;
  522.     while (isspace(*p))
  523.         *p++ = '\0';
  524.     if (*p != ':')
  525.     {
  526.       syntax:
  527.         message("501", "Syntax error");
  528.         Errors++;
  529.         return (NULL);
  530.     }
  531.     *p++ = '\0';
  532.     while (isspace(*p))
  533.         p++;
  534.  
  535.     /* see if the input word matches desired word */
  536.     if (strcasecmp(q, w))
  537.         goto syntax;
  538.  
  539.     return (p);
  540. }
  541. /*
  542. **  HELP -- implement the HELP command.
  543. **
  544. **    Parameters:
  545. **        topic -- the topic we want help for.
  546. **
  547. **    Returns:
  548. **        none.
  549. **
  550. **    Side Effects:
  551. **        outputs the help file to message output.
  552. */
  553.  
  554. help(topic)
  555.     char *topic;
  556. {
  557.     register FILE *hf;
  558.     int len;
  559.     char buf[MAXLINE];
  560.     bool noinfo;
  561.  
  562.     if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
  563.     {
  564.         /* no help */
  565.         errno = 0;
  566.         message("502", "HELP not implemented");
  567.         return;
  568.     }
  569.  
  570.     if (topic == NULL || *topic == '\0')
  571.         topic = "smtp";
  572.     else
  573.         makelower(topic);
  574.  
  575.     len = strlen(topic);
  576.     noinfo = TRUE;
  577.  
  578.     while (fgets(buf, sizeof buf, hf) != NULL)
  579.     {
  580.         if (strncmp(buf, topic, len) == 0)
  581.         {
  582.             register char *p;
  583.  
  584.             p = index(buf, '\t');
  585.             if (p == NULL)
  586.                 p = buf;
  587.             else
  588.                 p++;
  589.             fixcrlf(p, TRUE);
  590.             message("214-", p);
  591.             noinfo = FALSE;
  592.         }
  593.     }
  594.  
  595.     if (noinfo)
  596.         message("504", "HELP topic unknown");
  597.     else
  598.         message("214", "End of HELP info");
  599.     (void) fclose(hf);
  600. }
  601. /*
  602. **  RUNINCHILD -- return twice -- once in the child, then in the parent again
  603. **
  604. **    Parameters:
  605. **        label -- a string used in error messages
  606. **
  607. **    Returns:
  608. **        zero in the child
  609. **        one in the parent
  610. **
  611. **    Side Effects:
  612. **        none.
  613. */
  614.  
  615. runinchild(label)
  616.     char *label;
  617. {
  618.     int childpid;
  619.  
  620.     if (!OneXact)
  621.     {
  622.         childpid = dofork();
  623.         if (childpid < 0)
  624.         {
  625.             syserr("%s: cannot fork", label);
  626.             return (1);
  627.         }
  628.         if (childpid > 0)
  629.         {
  630.             auto int st;
  631.  
  632.             /* parent -- wait for child to complete */
  633.             st = waitfor(childpid);
  634.             if (st == -1)
  635.                 syserr("%s: lost child", label);
  636.  
  637.             /* if we exited on a QUIT command, complete the process */
  638.             if (st == (EX_QUIT << 8))
  639.                 finis();
  640.  
  641.             return (1);
  642.         }
  643.         else
  644.         {
  645.             /* child */
  646.             InChild = TRUE;
  647.             QuickAbort = FALSE;
  648.             clearenvelope(CurEnv, FALSE);
  649.         }
  650.     }
  651.  
  652.     /* open alias database */
  653.     initaliases(AliasFile, FALSE);
  654.  
  655.     return (0);
  656. }
  657.  
  658. # endif SMTP
  659.