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, '\0'); 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