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

Defined functions

help defined in line 554; used 1 times
iswiz defined in line 615; used 2 times
runinchild defined in line 638; used 2 times
skipword defined in line 506; used 3 times
smtp defined in line 98; used 1 times

Defined variables

CmdTab defined in line 64; used 1 times
SccsId defined in line 23; never used
WizWord defined in line 91; used 4 times

Defined struct's

cmd defined in line 40; used 4 times

Defined macros

CMDDATA defined in line 50; used 1 times
  • in line 68
CMDDBGDEBUG defined in line 58; used 1 times
  • in line 80
CMDDBGKILL defined in line 60; used 1 times
  • in line 83
CMDDBGQSHOW defined in line 57; used 1 times
  • in line 79
CMDDBGWIZ defined in line 61; used 1 times
  • in line 85
CMDERROR defined in line 47; used 1 times
  • in line 86
CMDHELO defined in line 56; used 1 times
  • in line 75
CMDHELP defined in line 53; used 1 times
  • in line 72
CMDMAIL defined in line 48; used 1 times
  • in line 66
CMDNOOP defined in line 54; used 1 times
  • in line 73
CMDONEX defined in line 62; used 1 times
  • in line 77
CMDQUIT defined in line 55; used 1 times
  • in line 74
CMDRCPT defined in line 49; used 1 times
  • in line 67
CMDRSET defined in line 51; used 1 times
  • in line 69
CMDVERB defined in line 59; used 1 times
  • in line 76
CMDVRFY defined in line 52; used 2 times
EX_QUIT defined in line 96; used 2 times
Last modified: 1994-07-16
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 5980
Valid CSS Valid XHTML 1.0 Strict