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