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