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