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

Defined functions

help defined in line 563; used 1 times
iswiz defined in line 624; used 2 times
runinchild defined in line 647; used 2 times
skipword defined in line 516; used 3 times
smtp defined in line 106; used 1 times

Defined variables

CmdTab defined in line 70; used 1 times
WizWord defined in line 100; used 4 times
sccsid defined in line 23; never used

Defined struct's

cmd defined in line 46; used 4 times

Defined macros

CMDDATA defined in line 56; used 1 times
  • in line 74
CMDDBGDEBUG defined in line 64; used 1 times
  • in line 87
CMDDBGKILL defined in line 66; used 1 times
  • in line 91
CMDDBGQSHOW defined in line 63; used 1 times
  • in line 85
CMDDBGWIZ defined in line 67; used 1 times
  • in line 93
CMDERROR defined in line 53; used 1 times
  • in line 94
CMDHELO defined in line 62; used 1 times
  • in line 81
CMDHELP defined in line 59; used 1 times
  • in line 78
CMDMAIL defined in line 54; used 1 times
  • in line 72
CMDNOOP defined in line 60; used 1 times
  • in line 79
CMDONEX defined in line 68; used 1 times
  • in line 83
CMDQUIT defined in line 61; used 1 times
  • in line 80
CMDRCPT defined in line 55; used 1 times
  • in line 73
CMDRSET defined in line 57; used 1 times
  • in line 75
CMDVERB defined in line 65; used 1 times
  • in line 82
CMDVRFY defined in line 58; used 2 times
EX_QUIT defined in line 104; used 2 times
Last modified: 1997-10-03
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4918
Valid CSS Valid XHTML 1.0 Strict