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