19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 34549Seric 45181Seric # ifndef SMTP 5*9378Seric SCCSID(@(#)srvrsmtp.c 3.39 11/28/82 (no SMTP)); 65181Seric # else SMTP 74556Seric 8*9378Seric SCCSID(@(#)srvrsmtp.c 3.39 11/28/82); 95181Seric 104549Seric /* 114549Seric ** SMTP -- run the SMTP protocol. 124549Seric ** 134549Seric ** Parameters: 144549Seric ** none. 154549Seric ** 164549Seric ** Returns: 174549Seric ** never. 184549Seric ** 194549Seric ** Side Effects: 204549Seric ** Reads commands from the input channel and processes 214549Seric ** them. 224549Seric */ 234549Seric 244549Seric struct cmd 254549Seric { 264549Seric char *cmdname; /* command name */ 274549Seric int cmdcode; /* internal code, see below */ 284549Seric }; 294549Seric 304549Seric /* values for cmdcode */ 314549Seric # define CMDERROR 0 /* bad command */ 324549Seric # define CMDMAIL 1 /* mail -- designate sender */ 334976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 344549Seric # define CMDDATA 3 /* data -- send message text */ 359339Seric # define CMDRSET 4 /* rset -- reset state */ 369339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 379339Seric # define CMDHELP 6 /* help -- give usage info */ 389339Seric # define CMDNOOP 7 /* noop -- do nothing */ 399339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 409339Seric # define CMDHELO 9 /* helo -- be polite */ 419339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 429339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 439339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 449339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 459339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 469339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 479339Seric # define CMDDBGSHELL 16 /* shell -- give us a shell */ 484549Seric 494549Seric static struct cmd CmdTab[] = 504549Seric { 514549Seric "mail", CMDMAIL, 524976Seric "rcpt", CMDRCPT, 534549Seric "data", CMDDATA, 544549Seric "rset", CMDRSET, 554549Seric "vrfy", CMDVRFY, 567762Seric "expn", CMDVRFY, 574549Seric "help", CMDHELP, 584549Seric "noop", CMDNOOP, 594549Seric "quit", CMDQUIT, 604976Seric "helo", CMDHELO, 618544Seric "verb", CMDVERB, 629314Seric "onex", CMDONEX, 635003Seric # ifdef DEBUG 649339Seric "showq", CMDDBGQSHOW, 658544Seric "debug", CMDDBGDEBUG, 668544Seric "kill", CMDDBGKILL, 678544Seric "wiz", CMDDBGWIZ, 689339Seric "shell", CMDDBGSHELL, 695003Seric # endif DEBUG 704549Seric NULL, CMDERROR, 714549Seric }; 724549Seric 738544Seric # ifdef DEBUG 748544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 758544Seric char *WizWord = NULL; /* the wizard word to compare against */ 768544Seric # endif DEBUG 779339Seric bool InChild = FALSE; /* true if running in a subprocess */ 78*9378Seric bool OneXact = FALSE; /* one xaction only this run */ 799339Seric #define EX_QUIT 22 /* special code for QUIT command */ 808544Seric 814549Seric smtp() 824549Seric { 834549Seric register char *p; 848544Seric register struct cmd *c; 854549Seric char *cmd; 864549Seric extern char *skipword(); 874549Seric extern bool sameword(); 884549Seric bool hasmail; /* mail command received */ 894713Seric int rcps; /* number of recipients */ 905003Seric auto ADDRESS *vrfyqueue; 918544Seric char inp[MAXLINE]; 927124Seric extern char Version[]; 937356Seric extern tick(); 948544Seric extern bool iswiz(); 959349Seric extern char *arpadate(); 964549Seric 975003Seric hasmail = FALSE; 984713Seric rcps = 0; 997363Seric if (OutChannel != stdout) 1007363Seric { 1017363Seric /* arrange for debugging output to go to remote host */ 1027363Seric (void) close(1); 1037363Seric (void) dup(fileno(OutChannel)); 1047363Seric } 1057851Seric message("220", "%s Sendmail %s ready at %s", HostName, 1069349Seric Version, arpadate((char *) NULL)); 1077762Seric (void) setjmp(TopFrame); 1087762Seric QuickAbort = FALSE; 1094549Seric for (;;) 1104549Seric { 1117356Seric /* setup for the read */ 1126907Seric CurEnv->e_to = NULL; 1134577Seric Errors = 0; 1147275Seric (void) fflush(stdout); 1157356Seric 1167356Seric /* read the input line */ 1177685Seric p = sfgets(inp, sizeof inp, InChannel); 1187356Seric 1197685Seric /* handle errors */ 1207356Seric if (p == NULL) 1217356Seric { 1224549Seric /* end of file, just die */ 1234558Seric message("421", "%s Lost input channel", HostName); 1244549Seric finis(); 1254549Seric } 1264549Seric 1274549Seric /* clean up end of line */ 1284558Seric fixcrlf(inp, TRUE); 1294549Seric 1304713Seric /* echo command to transcript */ 1319339Seric if (Xscript != NULL) 1329339Seric fprintf(Xscript, "<<< %s\n", inp); 1334713Seric 1344549Seric /* break off command */ 1354549Seric for (p = inp; isspace(*p); p++) 1364549Seric continue; 1374549Seric cmd = p; 1384549Seric while (*++p != '\0' && !isspace(*p)) 1394549Seric continue; 1404549Seric if (*p != '\0') 1414549Seric *p++ = '\0'; 1424549Seric 1434549Seric /* decode command */ 1444549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1454549Seric { 1464549Seric if (sameword(c->cmdname, cmd)) 1474549Seric break; 1484549Seric } 1494549Seric 1504549Seric /* process command */ 1514549Seric switch (c->cmdcode) 1524549Seric { 1534976Seric case CMDHELO: /* hello -- introduce yourself */ 154*9378Seric define('s', newstr(p), CurEnv); 1554997Seric message("250", "%s Hello %s, pleased to meet you", 1564997Seric HostName, p); 1574976Seric break; 1584976Seric 1594549Seric case CMDMAIL: /* mail -- designate sender */ 1609314Seric /* check for validity of this command */ 1614558Seric if (hasmail) 1624558Seric { 1634558Seric message("503", "Sender already specified"); 1644558Seric break; 1654558Seric } 1669339Seric if (InChild) 1679339Seric { 1689339Seric syserr("Nested MAIL command"); 1699339Seric exit(0); 1709339Seric } 1719339Seric 1729339Seric /* fork a subprocess to process this command */ 1739339Seric if (runinchild("SMTP-MAIL") > 0) 1749339Seric break; 1759339Seric initsys(); 1769339Seric 1779339Seric /* child -- go do the processing */ 1784549Seric p = skipword(p, "from"); 1794549Seric if (p == NULL) 1804549Seric break; 1814549Seric setsender(p); 1824577Seric if (Errors == 0) 1834549Seric { 1844549Seric message("250", "Sender ok"); 1854549Seric hasmail = TRUE; 1864549Seric } 1879339Seric else if (InChild) 1889339Seric finis(); 1894549Seric break; 1904549Seric 1914976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1924549Seric p = skipword(p, "to"); 1934549Seric if (p == NULL) 1944549Seric break; 1958081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1964577Seric if (Errors == 0) 1974549Seric { 1986057Seric message("250", "%s... Recipient ok", p); 1994713Seric rcps++; 2004549Seric } 2014549Seric break; 2024549Seric 2034549Seric case CMDDATA: /* data -- text of mail */ 2044976Seric if (!hasmail) 2054549Seric { 2064976Seric message("503", "Need MAIL command"); 2074976Seric break; 2084549Seric } 2094713Seric else if (rcps <= 0) 2104549Seric { 2114976Seric message("503", "Need RCPT (recipient)"); 2124976Seric break; 2134549Seric } 2144976Seric 2154976Seric /* collect the text of the message */ 2164976Seric collect(TRUE); 2174976Seric if (Errors != 0) 2184976Seric break; 2194976Seric 2208238Seric /* 2218238Seric ** Arrange to send to everyone. 2228238Seric ** If sending to multiple people, mail back 2238238Seric ** errors rather than reporting directly. 2248238Seric ** In any case, don't mail back errors for 2258238Seric ** anything that has happened up to 2268238Seric ** now (the other end will do this). 2278238Seric ** Then send to everyone. 2288238Seric ** Finally give a reply code. If an error has 2298238Seric ** already been given, don't mail a 2308238Seric ** message back. 2319339Seric ** We goose error returns by clearing error bit. 2328238Seric */ 2338238Seric 2344976Seric if (rcps != 1) 235*9378Seric { 236*9378Seric HoldErrs = TRUE; 237*9378Seric ErrorMode == EM_MAIL; 238*9378Seric } 2399339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2404976Seric 2414976Seric /* send to all recipients */ 2429283Seric sendall(CurEnv, SendMode); 2436907Seric CurEnv->e_to = NULL; 2444976Seric 2458238Seric /* issue success if appropriate and reset */ 2468238Seric if (Errors == 0 || HoldErrs) 2478238Seric { 2488238Seric HoldErrs = FALSE; 2499283Seric message("250", "Ok"); 2508238Seric } 2518238Seric else 2529339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2539339Seric 2549339Seric /* if in a child, pop back to our parent */ 2559339Seric if (InChild) 2569339Seric finis(); 2574549Seric break; 2584549Seric 2594549Seric case CMDRSET: /* rset -- reset state */ 2604549Seric message("250", "Reset state"); 2619339Seric if (InChild) 2629339Seric finis(); 2639339Seric break; 2644549Seric 2654549Seric case CMDVRFY: /* vrfy -- verify address */ 2669339Seric if (runinchild("SMTP-VRFY") > 0) 2679339Seric break; 2685003Seric vrfyqueue = NULL; 2697762Seric QuickAbort = TRUE; 2708081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2717762Seric if (Errors != 0) 2729339Seric { 2739339Seric if (InChild) 2749339Seric finis(); 2757762Seric break; 2769339Seric } 2775003Seric while (vrfyqueue != NULL) 2785003Seric { 2795003Seric register ADDRESS *a = vrfyqueue->q_next; 2805003Seric char *code; 2815003Seric 2827685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2835003Seric a = a->q_next; 2845003Seric 2857685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2865003Seric { 2875003Seric if (a != NULL) 2885003Seric code = "250-"; 2895003Seric else 2905003Seric code = "250"; 2915003Seric if (vrfyqueue->q_fullname == NULL) 2925003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2935003Seric else 2945003Seric message(code, "%s <%s>", 2955003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2965003Seric } 2975003Seric else if (a == NULL) 2985003Seric message("554", "Self destructive alias loop"); 2995003Seric vrfyqueue = a; 3005003Seric } 3019339Seric if (InChild) 3029339Seric finis(); 3034549Seric break; 3044549Seric 3054549Seric case CMDHELP: /* help -- give user info */ 3064577Seric if (*p == '\0') 3074577Seric p = "SMTP"; 3084577Seric help(p); 3094549Seric break; 3104549Seric 3114549Seric case CMDNOOP: /* noop -- do nothing */ 3124549Seric message("200", "OK"); 3134549Seric break; 3144549Seric 3154549Seric case CMDQUIT: /* quit -- leave mail */ 3164549Seric message("221", "%s closing connection", HostName); 3179339Seric if (InChild) 3189339Seric ExitStat = EX_QUIT; 3194549Seric finis(); 3204549Seric 3218544Seric case CMDVERB: /* set verbose mode */ 3228544Seric Verbose = TRUE; 3238544Seric message("200", "Verbose mode"); 3248544Seric break; 3258544Seric 3269314Seric case CMDONEX: /* doing one transaction only */ 327*9378Seric OneXact = TRUE; 3289314Seric message("200", "Only one transaction"); 3299314Seric break; 3309314Seric 3315003Seric # ifdef DEBUG 3329339Seric case CMDDBGQSHOW: /* show queues */ 3336907Seric printf("Send Queue="); 3346907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3355003Seric break; 3367275Seric 3377275Seric case CMDDBGDEBUG: /* set debug mode */ 3387676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3397676Seric tTflag(p); 3407676Seric message("200", "Debug set"); 3417275Seric break; 3427275Seric 3437282Seric case CMDDBGKILL: /* kill the parent */ 3448544Seric if (!iswiz()) 3458544Seric break; 3467282Seric if (kill(MotherPid, SIGTERM) >= 0) 3477282Seric message("200", "Mother is dead"); 3487282Seric else 3497282Seric message("500", "Can't kill Mom"); 3507282Seric break; 3518544Seric 3529339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3539339Seric if (!iswiz()) 3549339Seric break; 355*9378Seric if (fileno(InChannel) != 0) 356*9378Seric { 357*9378Seric (void) close(0); 358*9378Seric (void) dup(fileno(InChannel)); 359*9378Seric (void) fclose(InChannel); 360*9378Seric InChannel = stdin; 361*9378Seric } 362*9378Seric if (fileno(OutChannel) != 1) 363*9378Seric { 364*9378Seric (void) close(1); 365*9378Seric (void) dup(fileno(OutChannel)); 366*9378Seric (void) fclose(OutChannel); 367*9378Seric OutChannel = stdout; 368*9378Seric } 3699339Seric execl("/bin/csh", "sendmail", 0); 3709339Seric execl("/bin/sh", "sendmail", 0); 3719339Seric message("500", "Can't"); 372*9378Seric exit(EX_UNAVAILABLE); 3739339Seric 3748544Seric case CMDDBGWIZ: /* become a wizard */ 3758544Seric if (WizWord != NULL) 3768544Seric { 3778544Seric char seed[3]; 3788544Seric extern char *crypt(); 3798544Seric 3808544Seric strncpy(seed, WizWord, 2); 3818544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3828544Seric { 3838544Seric message("500", "You are no wizard!"); 3848544Seric break; 3858544Seric } 3868544Seric } 3878544Seric IsWiz = TRUE; 3888544Seric message("200", "Please pass, oh mighty wizard"); 3898544Seric break; 3905003Seric # endif DEBUG 3915003Seric 3924549Seric case CMDERROR: /* unknown command */ 3934549Seric message("500", "Command unrecognized"); 3944549Seric break; 3954549Seric 3964549Seric default: 3974549Seric syserr("smtp: unknown code %d", c->cmdcode); 3984549Seric break; 3994549Seric } 4004549Seric } 4014549Seric } 4024549Seric /* 4034549Seric ** SKIPWORD -- skip a fixed word. 4044549Seric ** 4054549Seric ** Parameters: 4064549Seric ** p -- place to start looking. 4074549Seric ** w -- word to skip. 4084549Seric ** 4094549Seric ** Returns: 4104549Seric ** p following w. 4114549Seric ** NULL on error. 4124549Seric ** 4134549Seric ** Side Effects: 4144549Seric ** clobbers the p data area. 4154549Seric */ 4164549Seric 4174549Seric static char * 4184549Seric skipword(p, w) 4194549Seric register char *p; 4204549Seric char *w; 4214549Seric { 4224549Seric register char *q; 4234549Seric extern bool sameword(); 4244549Seric 4254549Seric /* find beginning of word */ 4264549Seric while (isspace(*p)) 4274549Seric p++; 4284549Seric q = p; 4294549Seric 4304549Seric /* find end of word */ 4314549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4324549Seric p++; 4334549Seric while (isspace(*p)) 4344549Seric *p++ = '\0'; 4354549Seric if (*p != ':') 4364549Seric { 4374549Seric syntax: 4384549Seric message("501", "Syntax error"); 4394549Seric Errors++; 4404549Seric return (NULL); 4414549Seric } 4424549Seric *p++ = '\0'; 4434549Seric while (isspace(*p)) 4444549Seric p++; 4454549Seric 4464549Seric /* see if the input word matches desired word */ 4474549Seric if (!sameword(q, w)) 4484549Seric goto syntax; 4494549Seric 4504549Seric return (p); 4514549Seric } 4524577Seric /* 4534577Seric ** HELP -- implement the HELP command. 4544577Seric ** 4554577Seric ** Parameters: 4564577Seric ** topic -- the topic we want help for. 4574577Seric ** 4584577Seric ** Returns: 4594577Seric ** none. 4604577Seric ** 4614577Seric ** Side Effects: 4624577Seric ** outputs the help file to message output. 4634577Seric */ 4644577Seric 4654577Seric help(topic) 4664577Seric char *topic; 4674577Seric { 4684577Seric register FILE *hf; 4694577Seric int len; 4704577Seric char buf[MAXLINE]; 4714577Seric bool noinfo; 4724577Seric 4738269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4744577Seric { 4754577Seric /* no help */ 4764577Seric message("502", "HELP not implemented"); 4774577Seric return; 4784577Seric } 4794577Seric 4804577Seric len = strlen(topic); 4814577Seric makelower(topic); 4824577Seric noinfo = TRUE; 4834577Seric 4844577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4854577Seric { 4864577Seric if (strncmp(buf, topic, len) == 0) 4874577Seric { 4884577Seric register char *p; 4894577Seric 4904577Seric p = index(buf, '\t'); 4914577Seric if (p == NULL) 4924577Seric p = buf; 4934577Seric else 4944577Seric p++; 4954577Seric fixcrlf(p, TRUE); 4964577Seric message("214-", p); 4974577Seric noinfo = FALSE; 4984577Seric } 4994577Seric } 5004577Seric 5014577Seric if (noinfo) 5024577Seric message("504", "HELP topic unknown"); 5034577Seric else 5044577Seric message("214", "End of HELP info"); 5054628Seric (void) fclose(hf); 5064577Seric } 5078544Seric /* 5088544Seric ** ISWIZ -- tell us if we are a wizard 5098544Seric ** 5108544Seric ** If not, print a nasty message. 5118544Seric ** 5128544Seric ** Parameters: 5138544Seric ** none. 5148544Seric ** 5158544Seric ** Returns: 5168544Seric ** TRUE if we are a wizard. 5178544Seric ** FALSE if we are not a wizard. 5188544Seric ** 5198544Seric ** Side Effects: 5208544Seric ** Prints a 500 exit stat if we are not a wizard. 5218544Seric */ 5225181Seric 5238544Seric bool 5248544Seric iswiz() 5258544Seric { 5268544Seric if (!IsWiz) 5278544Seric message("500", "Mere mortals musn't mutter that mantra"); 5288544Seric return (IsWiz); 5298544Seric } 5309339Seric /* 5319339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5329339Seric ** 5339339Seric ** Parameters: 5349339Seric ** label -- a string used in error messages 5359339Seric ** 5369339Seric ** Returns: 5379339Seric ** zero in the child 5389339Seric ** one in the parent 5399339Seric ** 5409339Seric ** Side Effects: 5419339Seric ** none. 5429339Seric */ 5438544Seric 5449339Seric runinchild(label) 5459339Seric char *label; 5469339Seric { 5479339Seric int childpid; 5489339Seric 549*9378Seric if (OneXact) 550*9378Seric return (0); 551*9378Seric 5529339Seric childpid = dofork(); 5539339Seric if (childpid < 0) 5549339Seric { 5559339Seric syserr("%s: cannot fork", label); 5569339Seric return (1); 5579339Seric } 5589339Seric if (childpid > 0) 5599339Seric { 5609339Seric auto int st; 5619339Seric 562*9378Seric /* parent -- wait for child to complete */ 563*9378Seric st = waitfor(childpid); 564*9378Seric if (st == -1) 5659339Seric syserr("%s: lost child", label); 5669339Seric 5679339Seric /* if we exited on a QUIT command, complete the process */ 5689339Seric if (st == (EX_QUIT << 8)) 5699339Seric finis(); 5709339Seric 5719339Seric return (1); 5729339Seric } 5739339Seric else 5749339Seric { 5759339Seric /* child */ 5769339Seric InChild = TRUE; 5779339Seric return (0); 5789339Seric } 5799339Seric } 5809339Seric 5815181Seric # endif SMTP 582