19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 34549Seric 45181Seric # ifndef SMTP 5*9545Seric SCCSID(@(#)srvrsmtp.c 3.41 12/05/82 (no SMTP)); 65181Seric # else SMTP 74556Seric 8*9545Seric SCCSID(@(#)srvrsmtp.c 3.41 12/05/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 */ 789378Seric 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; 1099390Seric HoldErrs = FALSE; 1104549Seric for (;;) 1114549Seric { 1127356Seric /* setup for the read */ 1136907Seric CurEnv->e_to = NULL; 1144577Seric Errors = 0; 1157275Seric (void) fflush(stdout); 1167356Seric 1177356Seric /* read the input line */ 1187685Seric p = sfgets(inp, sizeof inp, InChannel); 1197356Seric 1207685Seric /* handle errors */ 1217356Seric if (p == NULL) 1227356Seric { 1234549Seric /* end of file, just die */ 1244558Seric message("421", "%s Lost input channel", HostName); 1254549Seric finis(); 1264549Seric } 1274549Seric 1284549Seric /* clean up end of line */ 1294558Seric fixcrlf(inp, TRUE); 1304549Seric 1314713Seric /* echo command to transcript */ 132*9545Seric if (CurEnv->e_xfp != NULL) 133*9545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1344713Seric 1354549Seric /* break off command */ 1364549Seric for (p = inp; isspace(*p); p++) 1374549Seric continue; 1384549Seric cmd = p; 1394549Seric while (*++p != '\0' && !isspace(*p)) 1404549Seric continue; 1414549Seric if (*p != '\0') 1424549Seric *p++ = '\0'; 1434549Seric 1444549Seric /* decode command */ 1454549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1464549Seric { 1474549Seric if (sameword(c->cmdname, cmd)) 1484549Seric break; 1494549Seric } 1504549Seric 1514549Seric /* process command */ 1524549Seric switch (c->cmdcode) 1534549Seric { 1544976Seric case CMDHELO: /* hello -- introduce yourself */ 1559378Seric define('s', newstr(p), CurEnv); 1564997Seric message("250", "%s Hello %s, pleased to meet you", 1574997Seric HostName, p); 1584976Seric break; 1594976Seric 1604549Seric case CMDMAIL: /* mail -- designate sender */ 1619314Seric /* check for validity of this command */ 1624558Seric if (hasmail) 1634558Seric { 1644558Seric message("503", "Sender already specified"); 1654558Seric break; 1664558Seric } 1679339Seric if (InChild) 1689339Seric { 1699339Seric syserr("Nested MAIL command"); 1709339Seric exit(0); 1719339Seric } 1729339Seric 1739339Seric /* fork a subprocess to process this command */ 1749339Seric if (runinchild("SMTP-MAIL") > 0) 1759339Seric break; 1769339Seric initsys(); 1779339Seric 1789339Seric /* child -- go do the processing */ 1794549Seric p = skipword(p, "from"); 1804549Seric if (p == NULL) 1814549Seric break; 1824549Seric setsender(p); 1834577Seric if (Errors == 0) 1844549Seric { 1854549Seric message("250", "Sender ok"); 1864549Seric hasmail = TRUE; 1874549Seric } 1889339Seric else if (InChild) 1899339Seric finis(); 1904549Seric break; 1914549Seric 1924976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1934549Seric p = skipword(p, "to"); 1944549Seric if (p == NULL) 1954549Seric break; 1968081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1979390Seric CurEnv->e_flags &= ~EF_FATALERRS; 1984577Seric if (Errors == 0) 1994549Seric { 2006057Seric message("250", "%s... Recipient ok", p); 2014713Seric rcps++; 2024549Seric } 2034549Seric break; 2044549Seric 2054549Seric case CMDDATA: /* data -- text of mail */ 2064976Seric if (!hasmail) 2074549Seric { 2084976Seric message("503", "Need MAIL command"); 2094976Seric break; 2104549Seric } 2114713Seric else if (rcps <= 0) 2124549Seric { 2134976Seric message("503", "Need RCPT (recipient)"); 2144976Seric break; 2154549Seric } 2164976Seric 2174976Seric /* collect the text of the message */ 2184976Seric collect(TRUE); 2194976Seric if (Errors != 0) 2204976Seric break; 2214976Seric 2228238Seric /* 2238238Seric ** Arrange to send to everyone. 2248238Seric ** If sending to multiple people, mail back 2258238Seric ** errors rather than reporting directly. 2268238Seric ** In any case, don't mail back errors for 2278238Seric ** anything that has happened up to 2288238Seric ** now (the other end will do this). 2298238Seric ** Then send to everyone. 2308238Seric ** Finally give a reply code. If an error has 2318238Seric ** already been given, don't mail a 2328238Seric ** message back. 2339339Seric ** We goose error returns by clearing error bit. 2348238Seric */ 2358238Seric 2364976Seric if (rcps != 1) 2379378Seric { 2389378Seric HoldErrs = TRUE; 2399378Seric ErrorMode == EM_MAIL; 2409378Seric } 2419339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2424976Seric 2434976Seric /* send to all recipients */ 2449283Seric sendall(CurEnv, SendMode); 2456907Seric CurEnv->e_to = NULL; 2464976Seric 2478238Seric /* issue success if appropriate and reset */ 2488238Seric if (Errors == 0 || HoldErrs) 2499283Seric message("250", "Ok"); 2508238Seric else 2519339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2529339Seric 2539339Seric /* if in a child, pop back to our parent */ 2549339Seric if (InChild) 2559339Seric finis(); 2564549Seric break; 2574549Seric 2584549Seric case CMDRSET: /* rset -- reset state */ 2594549Seric message("250", "Reset state"); 2609339Seric if (InChild) 2619339Seric finis(); 2629339Seric break; 2634549Seric 2644549Seric case CMDVRFY: /* vrfy -- verify address */ 2659339Seric if (runinchild("SMTP-VRFY") > 0) 2669339Seric break; 2675003Seric vrfyqueue = NULL; 2687762Seric QuickAbort = TRUE; 2698081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2707762Seric if (Errors != 0) 2719339Seric { 2729339Seric if (InChild) 2739339Seric finis(); 2747762Seric break; 2759339Seric } 2765003Seric while (vrfyqueue != NULL) 2775003Seric { 2785003Seric register ADDRESS *a = vrfyqueue->q_next; 2795003Seric char *code; 2805003Seric 2817685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2825003Seric a = a->q_next; 2835003Seric 2847685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2855003Seric { 2865003Seric if (a != NULL) 2875003Seric code = "250-"; 2885003Seric else 2895003Seric code = "250"; 2905003Seric if (vrfyqueue->q_fullname == NULL) 2915003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2925003Seric else 2935003Seric message(code, "%s <%s>", 2945003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2955003Seric } 2965003Seric else if (a == NULL) 2975003Seric message("554", "Self destructive alias loop"); 2985003Seric vrfyqueue = a; 2995003Seric } 3009339Seric if (InChild) 3019339Seric finis(); 3024549Seric break; 3034549Seric 3044549Seric case CMDHELP: /* help -- give user info */ 3054577Seric if (*p == '\0') 3064577Seric p = "SMTP"; 3074577Seric help(p); 3084549Seric break; 3094549Seric 3104549Seric case CMDNOOP: /* noop -- do nothing */ 3114549Seric message("200", "OK"); 3124549Seric break; 3134549Seric 3144549Seric case CMDQUIT: /* quit -- leave mail */ 3154549Seric message("221", "%s closing connection", HostName); 3169339Seric if (InChild) 3179339Seric ExitStat = EX_QUIT; 3184549Seric finis(); 3194549Seric 3208544Seric case CMDVERB: /* set verbose mode */ 3218544Seric Verbose = TRUE; 3228544Seric message("200", "Verbose mode"); 3238544Seric break; 3248544Seric 3259314Seric case CMDONEX: /* doing one transaction only */ 3269378Seric OneXact = TRUE; 3279314Seric message("200", "Only one transaction"); 3289314Seric break; 3299314Seric 3305003Seric # ifdef DEBUG 3319339Seric case CMDDBGQSHOW: /* show queues */ 3326907Seric printf("Send Queue="); 3336907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3345003Seric break; 3357275Seric 3367275Seric case CMDDBGDEBUG: /* set debug mode */ 3377676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3387676Seric tTflag(p); 3397676Seric message("200", "Debug set"); 3407275Seric break; 3417275Seric 3427282Seric case CMDDBGKILL: /* kill the parent */ 3438544Seric if (!iswiz()) 3448544Seric break; 3457282Seric if (kill(MotherPid, SIGTERM) >= 0) 3467282Seric message("200", "Mother is dead"); 3477282Seric else 3487282Seric message("500", "Can't kill Mom"); 3497282Seric break; 3508544Seric 3519339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3529339Seric if (!iswiz()) 3539339Seric break; 3549378Seric if (fileno(InChannel) != 0) 3559378Seric { 3569378Seric (void) close(0); 3579378Seric (void) dup(fileno(InChannel)); 3589378Seric (void) fclose(InChannel); 3599378Seric InChannel = stdin; 3609378Seric } 3619378Seric if (fileno(OutChannel) != 1) 3629378Seric { 3639378Seric (void) close(1); 3649378Seric (void) dup(fileno(OutChannel)); 3659378Seric (void) fclose(OutChannel); 3669378Seric OutChannel = stdout; 3679378Seric } 3689339Seric execl("/bin/csh", "sendmail", 0); 3699339Seric execl("/bin/sh", "sendmail", 0); 3709339Seric message("500", "Can't"); 3719378Seric exit(EX_UNAVAILABLE); 3729339Seric 3738544Seric case CMDDBGWIZ: /* become a wizard */ 3748544Seric if (WizWord != NULL) 3758544Seric { 3768544Seric char seed[3]; 3778544Seric extern char *crypt(); 3788544Seric 3798544Seric strncpy(seed, WizWord, 2); 3808544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3818544Seric { 3828544Seric message("500", "You are no wizard!"); 3838544Seric break; 3848544Seric } 3858544Seric } 3868544Seric IsWiz = TRUE; 3878544Seric message("200", "Please pass, oh mighty wizard"); 3888544Seric break; 3895003Seric # endif DEBUG 3905003Seric 3914549Seric case CMDERROR: /* unknown command */ 3924549Seric message("500", "Command unrecognized"); 3934549Seric break; 3944549Seric 3954549Seric default: 3964549Seric syserr("smtp: unknown code %d", c->cmdcode); 3974549Seric break; 3984549Seric } 3994549Seric } 4004549Seric } 4014549Seric /* 4024549Seric ** SKIPWORD -- skip a fixed word. 4034549Seric ** 4044549Seric ** Parameters: 4054549Seric ** p -- place to start looking. 4064549Seric ** w -- word to skip. 4074549Seric ** 4084549Seric ** Returns: 4094549Seric ** p following w. 4104549Seric ** NULL on error. 4114549Seric ** 4124549Seric ** Side Effects: 4134549Seric ** clobbers the p data area. 4144549Seric */ 4154549Seric 4164549Seric static char * 4174549Seric skipword(p, w) 4184549Seric register char *p; 4194549Seric char *w; 4204549Seric { 4214549Seric register char *q; 4224549Seric extern bool sameword(); 4234549Seric 4244549Seric /* find beginning of word */ 4254549Seric while (isspace(*p)) 4264549Seric p++; 4274549Seric q = p; 4284549Seric 4294549Seric /* find end of word */ 4304549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4314549Seric p++; 4324549Seric while (isspace(*p)) 4334549Seric *p++ = '\0'; 4344549Seric if (*p != ':') 4354549Seric { 4364549Seric syntax: 4374549Seric message("501", "Syntax error"); 4384549Seric Errors++; 4394549Seric return (NULL); 4404549Seric } 4414549Seric *p++ = '\0'; 4424549Seric while (isspace(*p)) 4434549Seric p++; 4444549Seric 4454549Seric /* see if the input word matches desired word */ 4464549Seric if (!sameword(q, w)) 4474549Seric goto syntax; 4484549Seric 4494549Seric return (p); 4504549Seric } 4514577Seric /* 4524577Seric ** HELP -- implement the HELP command. 4534577Seric ** 4544577Seric ** Parameters: 4554577Seric ** topic -- the topic we want help for. 4564577Seric ** 4574577Seric ** Returns: 4584577Seric ** none. 4594577Seric ** 4604577Seric ** Side Effects: 4614577Seric ** outputs the help file to message output. 4624577Seric */ 4634577Seric 4644577Seric help(topic) 4654577Seric char *topic; 4664577Seric { 4674577Seric register FILE *hf; 4684577Seric int len; 4694577Seric char buf[MAXLINE]; 4704577Seric bool noinfo; 4714577Seric 4728269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4734577Seric { 4744577Seric /* no help */ 4754577Seric message("502", "HELP not implemented"); 4764577Seric return; 4774577Seric } 4784577Seric 4794577Seric len = strlen(topic); 4804577Seric makelower(topic); 4814577Seric noinfo = TRUE; 4824577Seric 4834577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4844577Seric { 4854577Seric if (strncmp(buf, topic, len) == 0) 4864577Seric { 4874577Seric register char *p; 4884577Seric 4894577Seric p = index(buf, '\t'); 4904577Seric if (p == NULL) 4914577Seric p = buf; 4924577Seric else 4934577Seric p++; 4944577Seric fixcrlf(p, TRUE); 4954577Seric message("214-", p); 4964577Seric noinfo = FALSE; 4974577Seric } 4984577Seric } 4994577Seric 5004577Seric if (noinfo) 5014577Seric message("504", "HELP topic unknown"); 5024577Seric else 5034577Seric message("214", "End of HELP info"); 5044628Seric (void) fclose(hf); 5054577Seric } 5068544Seric /* 5078544Seric ** ISWIZ -- tell us if we are a wizard 5088544Seric ** 5098544Seric ** If not, print a nasty message. 5108544Seric ** 5118544Seric ** Parameters: 5128544Seric ** none. 5138544Seric ** 5148544Seric ** Returns: 5158544Seric ** TRUE if we are a wizard. 5168544Seric ** FALSE if we are not a wizard. 5178544Seric ** 5188544Seric ** Side Effects: 5198544Seric ** Prints a 500 exit stat if we are not a wizard. 5208544Seric */ 5215181Seric 5228544Seric bool 5238544Seric iswiz() 5248544Seric { 5258544Seric if (!IsWiz) 5268544Seric message("500", "Mere mortals musn't mutter that mantra"); 5278544Seric return (IsWiz); 5288544Seric } 5299339Seric /* 5309339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5319339Seric ** 5329339Seric ** Parameters: 5339339Seric ** label -- a string used in error messages 5349339Seric ** 5359339Seric ** Returns: 5369339Seric ** zero in the child 5379339Seric ** one in the parent 5389339Seric ** 5399339Seric ** Side Effects: 5409339Seric ** none. 5419339Seric */ 5428544Seric 5439339Seric runinchild(label) 5449339Seric char *label; 5459339Seric { 5469339Seric int childpid; 5479339Seric 5489378Seric if (OneXact) 5499378Seric return (0); 5509378Seric 5519339Seric childpid = dofork(); 5529339Seric if (childpid < 0) 5539339Seric { 5549339Seric syserr("%s: cannot fork", label); 5559339Seric return (1); 5569339Seric } 5579339Seric if (childpid > 0) 5589339Seric { 5599339Seric auto int st; 5609339Seric 5619378Seric /* parent -- wait for child to complete */ 5629378Seric st = waitfor(childpid); 5639378Seric if (st == -1) 5649339Seric syserr("%s: lost child", label); 5659339Seric 5669339Seric /* if we exited on a QUIT command, complete the process */ 5679339Seric if (st == (EX_QUIT << 8)) 5689339Seric finis(); 5699339Seric 5709339Seric return (1); 5719339Seric } 5729339Seric else 5739339Seric { 5749339Seric /* child */ 5759339Seric InChild = TRUE; 576*9545Seric clearenvelope(CurEnv); 5779339Seric return (0); 5789339Seric } 5799339Seric } 5809339Seric 5815181Seric # endif SMTP 582