Annotation of 42BSD/usr.lib/sendmail/src/srvrsmtp.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.