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

Defined functions

help defined in line 555; used 60 times
iswiz defined in line 616; used 2 times
runinchild defined in line 639; used 2 times
skipword defined in line 507; 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 92; 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: 1986-01-06
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2569
Valid CSS Valid XHTML 1.0 Strict