|
|
1.1 ! root 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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.