19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 34549Seric 45181Seric # ifndef SMTP 5*11146Seric SCCSID(@(#)srvrsmtp.c 3.46 02/18/83 (no SMTP)); 65181Seric # else SMTP 74556Seric 8*11146Seric SCCSID(@(#)srvrsmtp.c 3.46 02/18/83); 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 */ 79*11146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 80*11146Seric 819339Seric #define EX_QUIT 22 /* special code for QUIT command */ 828544Seric 834549Seric smtp() 844549Seric { 854549Seric register char *p; 868544Seric register struct cmd *c; 874549Seric char *cmd; 884549Seric extern char *skipword(); 894549Seric extern bool sameword(); 904549Seric bool hasmail; /* mail command received */ 914713Seric int rcps; /* number of recipients */ 925003Seric auto ADDRESS *vrfyqueue; 938544Seric char inp[MAXLINE]; 947124Seric extern char Version[]; 957356Seric extern tick(); 968544Seric extern bool iswiz(); 979349Seric extern char *arpadate(); 984549Seric 995003Seric hasmail = FALSE; 1004713Seric rcps = 0; 1017363Seric if (OutChannel != stdout) 1027363Seric { 1037363Seric /* arrange for debugging output to go to remote host */ 1047363Seric (void) close(1); 1057363Seric (void) dup(fileno(OutChannel)); 1067363Seric } 10710708Seric expand("$e", inp, &inp[sizeof inp], CurEnv); 10810708Seric message("220", inp); 1097762Seric (void) setjmp(TopFrame); 1107762Seric QuickAbort = FALSE; 1119390Seric HoldErrs = FALSE; 1124549Seric for (;;) 1134549Seric { 1147356Seric /* setup for the read */ 1156907Seric CurEnv->e_to = NULL; 1164577Seric Errors = 0; 1177275Seric (void) fflush(stdout); 1187356Seric 1197356Seric /* read the input line */ 1207685Seric p = sfgets(inp, sizeof inp, InChannel); 1217356Seric 1227685Seric /* handle errors */ 1237356Seric if (p == NULL) 1247356Seric { 1254549Seric /* end of file, just die */ 1264558Seric message("421", "%s Lost input channel", HostName); 1274549Seric finis(); 1284549Seric } 1294549Seric 1304549Seric /* clean up end of line */ 1314558Seric fixcrlf(inp, TRUE); 1324549Seric 1334713Seric /* echo command to transcript */ 1349545Seric if (CurEnv->e_xfp != NULL) 1359545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1364713Seric 1374549Seric /* break off command */ 1384549Seric for (p = inp; isspace(*p); p++) 1394549Seric continue; 1404549Seric cmd = p; 1414549Seric while (*++p != '\0' && !isspace(*p)) 1424549Seric continue; 1434549Seric if (*p != '\0') 1444549Seric *p++ = '\0'; 1454549Seric 1464549Seric /* decode command */ 1474549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1484549Seric { 1494549Seric if (sameword(c->cmdname, cmd)) 1504549Seric break; 1514549Seric } 1524549Seric 1534549Seric /* process command */ 1544549Seric switch (c->cmdcode) 1554549Seric { 1564976Seric case CMDHELO: /* hello -- introduce yourself */ 157*11146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 158*11146Seric { 159*11146Seric char buf[MAXNAME]; 160*11146Seric 161*11146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 162*11146Seric define('s', newstr(buf), CurEnv); 163*11146Seric } 164*11146Seric else 165*11146Seric define('s', newstr(p), CurEnv); 1664997Seric message("250", "%s Hello %s, pleased to meet you", 1674997Seric HostName, p); 1684976Seric break; 1694976Seric 1704549Seric case CMDMAIL: /* mail -- designate sender */ 1719314Seric /* check for validity of this command */ 1724558Seric if (hasmail) 1734558Seric { 1744558Seric message("503", "Sender already specified"); 1754558Seric break; 1764558Seric } 1779339Seric if (InChild) 1789339Seric { 1799339Seric syserr("Nested MAIL command"); 1809339Seric exit(0); 1819339Seric } 1829339Seric 1839339Seric /* fork a subprocess to process this command */ 1849339Seric if (runinchild("SMTP-MAIL") > 0) 1859339Seric break; 1869339Seric initsys(); 1879339Seric 1889339Seric /* child -- go do the processing */ 1894549Seric p = skipword(p, "from"); 1904549Seric if (p == NULL) 1914549Seric break; 1924549Seric setsender(p); 1934577Seric if (Errors == 0) 1944549Seric { 1954549Seric message("250", "Sender ok"); 1964549Seric hasmail = TRUE; 1974549Seric } 1989339Seric else if (InChild) 1999339Seric finis(); 2004549Seric break; 2014549Seric 2024976Seric case CMDRCPT: /* rcpt -- designate recipient */ 2034549Seric p = skipword(p, "to"); 2044549Seric if (p == NULL) 2054549Seric break; 2069619Seric sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 2079390Seric CurEnv->e_flags &= ~EF_FATALERRS; 2084577Seric if (Errors == 0) 2094549Seric { 2106057Seric message("250", "%s... Recipient ok", p); 2114713Seric rcps++; 2124549Seric } 2134549Seric break; 2144549Seric 2154549Seric case CMDDATA: /* data -- text of mail */ 2164976Seric if (!hasmail) 2174549Seric { 2184976Seric message("503", "Need MAIL command"); 2194976Seric break; 2204549Seric } 2214713Seric else if (rcps <= 0) 2224549Seric { 2234976Seric message("503", "Need RCPT (recipient)"); 2244976Seric break; 2254549Seric } 2264976Seric 2274976Seric /* collect the text of the message */ 2284976Seric collect(TRUE); 2294976Seric if (Errors != 0) 2304976Seric break; 2314976Seric 2328238Seric /* 2338238Seric ** Arrange to send to everyone. 2348238Seric ** If sending to multiple people, mail back 2358238Seric ** errors rather than reporting directly. 2368238Seric ** In any case, don't mail back errors for 2378238Seric ** anything that has happened up to 2388238Seric ** now (the other end will do this). 23910197Seric ** Truncate our transcript -- the mail has gotten 24010197Seric ** to us successfully, and if we have 24110197Seric ** to mail this back, it will be easier 24210197Seric ** on the reader. 2438238Seric ** Then send to everyone. 2448238Seric ** Finally give a reply code. If an error has 2458238Seric ** already been given, don't mail a 2468238Seric ** message back. 2479339Seric ** We goose error returns by clearing error bit. 2488238Seric */ 2498238Seric 2504976Seric if (rcps != 1) 2519378Seric { 2529378Seric HoldErrs = TRUE; 2539378Seric ErrorMode == EM_MAIL; 2549378Seric } 2559339Seric CurEnv->e_flags &= ~EF_FATALERRS; 25610197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2574976Seric 2584976Seric /* send to all recipients */ 2599283Seric sendall(CurEnv, SendMode); 2606907Seric CurEnv->e_to = NULL; 2614976Seric 2628238Seric /* issue success if appropriate and reset */ 2638238Seric if (Errors == 0 || HoldErrs) 2649283Seric message("250", "Ok"); 2658238Seric else 2669339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2679339Seric 2689339Seric /* if in a child, pop back to our parent */ 2699339Seric if (InChild) 2709339Seric finis(); 2714549Seric break; 2724549Seric 2734549Seric case CMDRSET: /* rset -- reset state */ 2744549Seric message("250", "Reset state"); 2759339Seric if (InChild) 2769339Seric finis(); 2779339Seric break; 2784549Seric 2794549Seric case CMDVRFY: /* vrfy -- verify address */ 2809339Seric if (runinchild("SMTP-VRFY") > 0) 2819339Seric break; 2825003Seric vrfyqueue = NULL; 2837762Seric QuickAbort = TRUE; 2849619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 2857762Seric if (Errors != 0) 2869339Seric { 2879339Seric if (InChild) 2889339Seric finis(); 2897762Seric break; 2909339Seric } 2915003Seric while (vrfyqueue != NULL) 2925003Seric { 2935003Seric register ADDRESS *a = vrfyqueue->q_next; 2945003Seric char *code; 2955003Seric 2967685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2975003Seric a = a->q_next; 2985003Seric 2997685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3005003Seric { 3015003Seric if (a != NULL) 3025003Seric code = "250-"; 3035003Seric else 3045003Seric code = "250"; 3055003Seric if (vrfyqueue->q_fullname == NULL) 3065003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3075003Seric else 3085003Seric message(code, "%s <%s>", 3095003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3105003Seric } 3115003Seric else if (a == NULL) 3125003Seric message("554", "Self destructive alias loop"); 3135003Seric vrfyqueue = a; 3145003Seric } 3159339Seric if (InChild) 3169339Seric finis(); 3174549Seric break; 3184549Seric 3194549Seric case CMDHELP: /* help -- give user info */ 3204577Seric if (*p == '\0') 3214577Seric p = "SMTP"; 3224577Seric help(p); 3234549Seric break; 3244549Seric 3254549Seric case CMDNOOP: /* noop -- do nothing */ 3264549Seric message("200", "OK"); 3274549Seric break; 3284549Seric 3294549Seric case CMDQUIT: /* quit -- leave mail */ 3304549Seric message("221", "%s closing connection", HostName); 3319339Seric if (InChild) 3329339Seric ExitStat = EX_QUIT; 3334549Seric finis(); 3344549Seric 3358544Seric case CMDVERB: /* set verbose mode */ 3368544Seric Verbose = TRUE; 3378544Seric message("200", "Verbose mode"); 3388544Seric break; 3398544Seric 3409314Seric case CMDONEX: /* doing one transaction only */ 3419378Seric OneXact = TRUE; 3429314Seric message("200", "Only one transaction"); 3439314Seric break; 3449314Seric 3455003Seric # ifdef DEBUG 3469339Seric case CMDDBGQSHOW: /* show queues */ 3476907Seric printf("Send Queue="); 3486907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3495003Seric break; 3507275Seric 3517275Seric case CMDDBGDEBUG: /* set debug mode */ 3527676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3537676Seric tTflag(p); 3547676Seric message("200", "Debug set"); 3557275Seric break; 3567275Seric 3577282Seric case CMDDBGKILL: /* kill the parent */ 3588544Seric if (!iswiz()) 3598544Seric break; 3607282Seric if (kill(MotherPid, SIGTERM) >= 0) 3617282Seric message("200", "Mother is dead"); 3627282Seric else 3637282Seric message("500", "Can't kill Mom"); 3647282Seric break; 3658544Seric 3669339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3679339Seric if (!iswiz()) 3689339Seric break; 3699378Seric if (fileno(InChannel) != 0) 3709378Seric { 3719378Seric (void) close(0); 3729378Seric (void) dup(fileno(InChannel)); 37310346Seric if (fileno(InChannel) != fileno(OutChannel)) 37410346Seric (void) fclose(InChannel); 3759378Seric InChannel = stdin; 3769378Seric } 3779378Seric if (fileno(OutChannel) != 1) 3789378Seric { 3799378Seric (void) close(1); 3809378Seric (void) dup(fileno(OutChannel)); 3819378Seric (void) fclose(OutChannel); 3829378Seric OutChannel = stdout; 3839378Seric } 38410346Seric (void) close(2); 38510346Seric (void) dup(1); 3869339Seric execl("/bin/csh", "sendmail", 0); 3879339Seric execl("/bin/sh", "sendmail", 0); 3889339Seric message("500", "Can't"); 3899378Seric exit(EX_UNAVAILABLE); 3909339Seric 3918544Seric case CMDDBGWIZ: /* become a wizard */ 3928544Seric if (WizWord != NULL) 3938544Seric { 3948544Seric char seed[3]; 3958544Seric extern char *crypt(); 3968544Seric 3978544Seric strncpy(seed, WizWord, 2); 3988544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3998544Seric { 4008544Seric message("500", "You are no wizard!"); 4018544Seric break; 4028544Seric } 4038544Seric } 4048544Seric IsWiz = TRUE; 4058544Seric message("200", "Please pass, oh mighty wizard"); 4068544Seric break; 4075003Seric # endif DEBUG 4085003Seric 4094549Seric case CMDERROR: /* unknown command */ 4104549Seric message("500", "Command unrecognized"); 4114549Seric break; 4124549Seric 4134549Seric default: 4144549Seric syserr("smtp: unknown code %d", c->cmdcode); 4154549Seric break; 4164549Seric } 4174549Seric } 4184549Seric } 4194549Seric /* 4204549Seric ** SKIPWORD -- skip a fixed word. 4214549Seric ** 4224549Seric ** Parameters: 4234549Seric ** p -- place to start looking. 4244549Seric ** w -- word to skip. 4254549Seric ** 4264549Seric ** Returns: 4274549Seric ** p following w. 4284549Seric ** NULL on error. 4294549Seric ** 4304549Seric ** Side Effects: 4314549Seric ** clobbers the p data area. 4324549Seric */ 4334549Seric 4344549Seric static char * 4354549Seric skipword(p, w) 4364549Seric register char *p; 4374549Seric char *w; 4384549Seric { 4394549Seric register char *q; 4404549Seric extern bool sameword(); 4414549Seric 4424549Seric /* find beginning of word */ 4434549Seric while (isspace(*p)) 4444549Seric p++; 4454549Seric q = p; 4464549Seric 4474549Seric /* find end of word */ 4484549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4494549Seric p++; 4504549Seric while (isspace(*p)) 4514549Seric *p++ = '\0'; 4524549Seric if (*p != ':') 4534549Seric { 4544549Seric syntax: 4554549Seric message("501", "Syntax error"); 4564549Seric Errors++; 4574549Seric return (NULL); 4584549Seric } 4594549Seric *p++ = '\0'; 4604549Seric while (isspace(*p)) 4614549Seric p++; 4624549Seric 4634549Seric /* see if the input word matches desired word */ 4644549Seric if (!sameword(q, w)) 4654549Seric goto syntax; 4664549Seric 4674549Seric return (p); 4684549Seric } 4694577Seric /* 4704577Seric ** HELP -- implement the HELP command. 4714577Seric ** 4724577Seric ** Parameters: 4734577Seric ** topic -- the topic we want help for. 4744577Seric ** 4754577Seric ** Returns: 4764577Seric ** none. 4774577Seric ** 4784577Seric ** Side Effects: 4794577Seric ** outputs the help file to message output. 4804577Seric */ 4814577Seric 4824577Seric help(topic) 4834577Seric char *topic; 4844577Seric { 4854577Seric register FILE *hf; 4864577Seric int len; 4874577Seric char buf[MAXLINE]; 4884577Seric bool noinfo; 4894577Seric 4908269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4914577Seric { 4924577Seric /* no help */ 4934577Seric message("502", "HELP not implemented"); 4944577Seric return; 4954577Seric } 4964577Seric 4974577Seric len = strlen(topic); 4984577Seric makelower(topic); 4994577Seric noinfo = TRUE; 5004577Seric 5014577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5024577Seric { 5034577Seric if (strncmp(buf, topic, len) == 0) 5044577Seric { 5054577Seric register char *p; 5064577Seric 5074577Seric p = index(buf, '\t'); 5084577Seric if (p == NULL) 5094577Seric p = buf; 5104577Seric else 5114577Seric p++; 5124577Seric fixcrlf(p, TRUE); 5134577Seric message("214-", p); 5144577Seric noinfo = FALSE; 5154577Seric } 5164577Seric } 5174577Seric 5184577Seric if (noinfo) 5194577Seric message("504", "HELP topic unknown"); 5204577Seric else 5214577Seric message("214", "End of HELP info"); 5224628Seric (void) fclose(hf); 5234577Seric } 5248544Seric /* 5258544Seric ** ISWIZ -- tell us if we are a wizard 5268544Seric ** 5278544Seric ** If not, print a nasty message. 5288544Seric ** 5298544Seric ** Parameters: 5308544Seric ** none. 5318544Seric ** 5328544Seric ** Returns: 5338544Seric ** TRUE if we are a wizard. 5348544Seric ** FALSE if we are not a wizard. 5358544Seric ** 5368544Seric ** Side Effects: 5378544Seric ** Prints a 500 exit stat if we are not a wizard. 5388544Seric */ 5395181Seric 5408544Seric bool 5418544Seric iswiz() 5428544Seric { 5438544Seric if (!IsWiz) 5448544Seric message("500", "Mere mortals musn't mutter that mantra"); 5458544Seric return (IsWiz); 5468544Seric } 5479339Seric /* 5489339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5499339Seric ** 5509339Seric ** Parameters: 5519339Seric ** label -- a string used in error messages 5529339Seric ** 5539339Seric ** Returns: 5549339Seric ** zero in the child 5559339Seric ** one in the parent 5569339Seric ** 5579339Seric ** Side Effects: 5589339Seric ** none. 5599339Seric */ 5608544Seric 5619339Seric runinchild(label) 5629339Seric char *label; 5639339Seric { 5649339Seric int childpid; 5659339Seric 5669378Seric if (OneXact) 5679378Seric return (0); 5689378Seric 5699339Seric childpid = dofork(); 5709339Seric if (childpid < 0) 5719339Seric { 5729339Seric syserr("%s: cannot fork", label); 5739339Seric return (1); 5749339Seric } 5759339Seric if (childpid > 0) 5769339Seric { 5779339Seric auto int st; 5789339Seric 5799378Seric /* parent -- wait for child to complete */ 5809378Seric st = waitfor(childpid); 5819378Seric if (st == -1) 5829339Seric syserr("%s: lost child", label); 5839339Seric 5849339Seric /* if we exited on a QUIT command, complete the process */ 5859339Seric if (st == (EX_QUIT << 8)) 5869339Seric finis(); 5879339Seric 5889339Seric return (1); 5899339Seric } 5909339Seric else 5919339Seric { 5929339Seric /* child */ 5939339Seric InChild = TRUE; 5949545Seric clearenvelope(CurEnv); 5959339Seric return (0); 5969339Seric } 5979339Seric } 5989339Seric 5995181Seric # endif SMTP 600