19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 311728Seric # include <signal.h> 44549Seric 55181Seric # ifndef SMTP 6*11931Seric SCCSID(@(#)srvrsmtp.c 3.49 04/17/83 (no SMTP)); 75181Seric # else SMTP 84556Seric 9*11931Seric SCCSID(@(#)srvrsmtp.c 3.49 04/17/83); 105181Seric 114549Seric /* 124549Seric ** SMTP -- run the SMTP protocol. 134549Seric ** 144549Seric ** Parameters: 154549Seric ** none. 164549Seric ** 174549Seric ** Returns: 184549Seric ** never. 194549Seric ** 204549Seric ** Side Effects: 214549Seric ** Reads commands from the input channel and processes 224549Seric ** them. 234549Seric */ 244549Seric 254549Seric struct cmd 264549Seric { 274549Seric char *cmdname; /* command name */ 284549Seric int cmdcode; /* internal code, see below */ 294549Seric }; 304549Seric 314549Seric /* values for cmdcode */ 324549Seric # define CMDERROR 0 /* bad command */ 334549Seric # define CMDMAIL 1 /* mail -- designate sender */ 344976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 354549Seric # define CMDDATA 3 /* data -- send message text */ 369339Seric # define CMDRSET 4 /* rset -- reset state */ 379339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 389339Seric # define CMDHELP 6 /* help -- give usage info */ 399339Seric # define CMDNOOP 7 /* noop -- do nothing */ 409339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 419339Seric # define CMDHELO 9 /* helo -- be polite */ 429339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 439339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 449339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 459339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 469339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 479339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 489339Seric # define CMDDBGSHELL 16 /* shell -- give us a shell */ 494549Seric 504549Seric static struct cmd CmdTab[] = 514549Seric { 524549Seric "mail", CMDMAIL, 534976Seric "rcpt", CMDRCPT, 544549Seric "data", CMDDATA, 554549Seric "rset", CMDRSET, 564549Seric "vrfy", CMDVRFY, 577762Seric "expn", CMDVRFY, 584549Seric "help", CMDHELP, 594549Seric "noop", CMDNOOP, 604549Seric "quit", CMDQUIT, 614976Seric "helo", CMDHELO, 628544Seric "verb", CMDVERB, 639314Seric "onex", CMDONEX, 645003Seric # ifdef DEBUG 659339Seric "showq", CMDDBGQSHOW, 668544Seric "debug", CMDDBGDEBUG, 678544Seric "kill", CMDDBGKILL, 688544Seric "wiz", CMDDBGWIZ, 699339Seric "shell", CMDDBGSHELL, 705003Seric # endif DEBUG 714549Seric NULL, CMDERROR, 724549Seric }; 734549Seric 748544Seric # ifdef DEBUG 758544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 768544Seric char *WizWord = NULL; /* the wizard word to compare against */ 778544Seric # endif DEBUG 789339Seric bool InChild = FALSE; /* true if running in a subprocess */ 799378Seric bool OneXact = FALSE; /* one xaction only this run */ 8011146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 8111146Seric 829339Seric #define EX_QUIT 22 /* special code for QUIT command */ 838544Seric 844549Seric smtp() 854549Seric { 864549Seric register char *p; 878544Seric register struct cmd *c; 884549Seric char *cmd; 894549Seric extern char *skipword(); 904549Seric extern bool sameword(); 914549Seric bool hasmail; /* mail command received */ 924713Seric int rcps; /* number of recipients */ 935003Seric auto ADDRESS *vrfyqueue; 948544Seric char inp[MAXLINE]; 957124Seric extern char Version[]; 967356Seric extern tick(); 978544Seric extern bool iswiz(); 989349Seric extern char *arpadate(); 9911151Seric extern char *macvalue(); 1004549Seric 1015003Seric hasmail = FALSE; 1024713Seric rcps = 0; 1037363Seric if (OutChannel != stdout) 1047363Seric { 1057363Seric /* arrange for debugging output to go to remote host */ 1067363Seric (void) close(1); 1077363Seric (void) dup(fileno(OutChannel)); 1087363Seric } 109*11931Seric settime(); 11010708Seric expand("$e", inp, &inp[sizeof inp], CurEnv); 11110708Seric message("220", inp); 1127762Seric (void) setjmp(TopFrame); 1137762Seric QuickAbort = FALSE; 1149390Seric HoldErrs = FALSE; 1154549Seric for (;;) 1164549Seric { 1177356Seric /* setup for the read */ 1186907Seric CurEnv->e_to = NULL; 1194577Seric Errors = 0; 1207275Seric (void) fflush(stdout); 1217356Seric 1227356Seric /* read the input line */ 1237685Seric p = sfgets(inp, sizeof inp, InChannel); 1247356Seric 1257685Seric /* handle errors */ 1267356Seric if (p == NULL) 1277356Seric { 1284549Seric /* end of file, just die */ 1294558Seric message("421", "%s Lost input channel", HostName); 1304549Seric finis(); 1314549Seric } 1324549Seric 1334549Seric /* clean up end of line */ 1344558Seric fixcrlf(inp, TRUE); 1354549Seric 1364713Seric /* echo command to transcript */ 1379545Seric if (CurEnv->e_xfp != NULL) 1389545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1394713Seric 1404549Seric /* break off command */ 1414549Seric for (p = inp; isspace(*p); p++) 1424549Seric continue; 1434549Seric cmd = p; 1444549Seric while (*++p != '\0' && !isspace(*p)) 1454549Seric continue; 1464549Seric if (*p != '\0') 1474549Seric *p++ = '\0'; 1484549Seric 1494549Seric /* decode command */ 1504549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1514549Seric { 1524549Seric if (sameword(c->cmdname, cmd)) 1534549Seric break; 1544549Seric } 1554549Seric 1564549Seric /* process command */ 1574549Seric switch (c->cmdcode) 1584549Seric { 1594976Seric case CMDHELO: /* hello -- introduce yourself */ 16011146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 16111146Seric { 16211146Seric char buf[MAXNAME]; 16311146Seric 16411146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 16511146Seric define('s', newstr(buf), CurEnv); 16611146Seric } 16711146Seric else 16811146Seric define('s', newstr(p), CurEnv); 1694997Seric message("250", "%s Hello %s, pleased to meet you", 1704997Seric HostName, p); 1714976Seric break; 1724976Seric 1734549Seric case CMDMAIL: /* mail -- designate sender */ 17411151Seric /* force a sending host even if no HELO given */ 17511151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 17611151Seric define('s', RealHostName, CurEnv); 17711151Seric 1789314Seric /* check for validity of this command */ 1794558Seric if (hasmail) 1804558Seric { 1814558Seric message("503", "Sender already specified"); 1824558Seric break; 1834558Seric } 1849339Seric if (InChild) 1859339Seric { 1869339Seric syserr("Nested MAIL command"); 1879339Seric exit(0); 1889339Seric } 1899339Seric 1909339Seric /* fork a subprocess to process this command */ 1919339Seric if (runinchild("SMTP-MAIL") > 0) 1929339Seric break; 1939339Seric initsys(); 1949339Seric 1959339Seric /* child -- go do the processing */ 1964549Seric p = skipword(p, "from"); 1974549Seric if (p == NULL) 1984549Seric break; 1994549Seric setsender(p); 2004577Seric if (Errors == 0) 2014549Seric { 2024549Seric message("250", "Sender ok"); 2034549Seric hasmail = TRUE; 2044549Seric } 2059339Seric else if (InChild) 2069339Seric finis(); 2074549Seric break; 2084549Seric 2094976Seric case CMDRCPT: /* rcpt -- designate recipient */ 2104549Seric p = skipword(p, "to"); 2114549Seric if (p == NULL) 2124549Seric break; 2139619Seric sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 2149390Seric CurEnv->e_flags &= ~EF_FATALERRS; 2154577Seric if (Errors == 0) 2164549Seric { 2176057Seric message("250", "%s... Recipient ok", p); 2184713Seric rcps++; 2194549Seric } 2204549Seric break; 2214549Seric 2224549Seric case CMDDATA: /* data -- text of mail */ 2234976Seric if (!hasmail) 2244549Seric { 2254976Seric message("503", "Need MAIL command"); 2264976Seric break; 2274549Seric } 2284713Seric else if (rcps <= 0) 2294549Seric { 2304976Seric message("503", "Need RCPT (recipient)"); 2314976Seric break; 2324549Seric } 2334976Seric 2344976Seric /* collect the text of the message */ 2354976Seric collect(TRUE); 2364976Seric if (Errors != 0) 2374976Seric break; 2384976Seric 2398238Seric /* 2408238Seric ** Arrange to send to everyone. 2418238Seric ** If sending to multiple people, mail back 2428238Seric ** errors rather than reporting directly. 2438238Seric ** In any case, don't mail back errors for 2448238Seric ** anything that has happened up to 2458238Seric ** now (the other end will do this). 24610197Seric ** Truncate our transcript -- the mail has gotten 24710197Seric ** to us successfully, and if we have 24810197Seric ** to mail this back, it will be easier 24910197Seric ** on the reader. 2508238Seric ** Then send to everyone. 2518238Seric ** Finally give a reply code. If an error has 2528238Seric ** already been given, don't mail a 2538238Seric ** message back. 2549339Seric ** We goose error returns by clearing error bit. 2558238Seric */ 2568238Seric 2574976Seric if (rcps != 1) 2589378Seric { 2599378Seric HoldErrs = TRUE; 2609378Seric ErrorMode == EM_MAIL; 2619378Seric } 2629339Seric CurEnv->e_flags &= ~EF_FATALERRS; 26310197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2644976Seric 2654976Seric /* send to all recipients */ 2669283Seric sendall(CurEnv, SendMode); 2676907Seric CurEnv->e_to = NULL; 2684976Seric 2698238Seric /* issue success if appropriate and reset */ 2708238Seric if (Errors == 0 || HoldErrs) 2719283Seric message("250", "Ok"); 2728238Seric else 2739339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2749339Seric 2759339Seric /* if in a child, pop back to our parent */ 2769339Seric if (InChild) 2779339Seric finis(); 2784549Seric break; 2794549Seric 2804549Seric case CMDRSET: /* rset -- reset state */ 2814549Seric message("250", "Reset state"); 2829339Seric if (InChild) 2839339Seric finis(); 2849339Seric break; 2854549Seric 2864549Seric case CMDVRFY: /* vrfy -- verify address */ 2879339Seric if (runinchild("SMTP-VRFY") > 0) 2889339Seric break; 2895003Seric vrfyqueue = NULL; 2907762Seric QuickAbort = TRUE; 2919619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 2927762Seric if (Errors != 0) 2939339Seric { 2949339Seric if (InChild) 2959339Seric finis(); 2967762Seric break; 2979339Seric } 2985003Seric while (vrfyqueue != NULL) 2995003Seric { 3005003Seric register ADDRESS *a = vrfyqueue->q_next; 3015003Seric char *code; 3025003Seric 3037685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3045003Seric a = a->q_next; 3055003Seric 3067685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3075003Seric { 3085003Seric if (a != NULL) 3095003Seric code = "250-"; 3105003Seric else 3115003Seric code = "250"; 3125003Seric if (vrfyqueue->q_fullname == NULL) 3135003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3145003Seric else 3155003Seric message(code, "%s <%s>", 3165003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3175003Seric } 3185003Seric else if (a == NULL) 3195003Seric message("554", "Self destructive alias loop"); 3205003Seric vrfyqueue = a; 3215003Seric } 3229339Seric if (InChild) 3239339Seric finis(); 3244549Seric break; 3254549Seric 3264549Seric case CMDHELP: /* help -- give user info */ 3274577Seric if (*p == '\0') 3284577Seric p = "SMTP"; 3294577Seric help(p); 3304549Seric break; 3314549Seric 3324549Seric case CMDNOOP: /* noop -- do nothing */ 3334549Seric message("200", "OK"); 3344549Seric break; 3354549Seric 3364549Seric case CMDQUIT: /* quit -- leave mail */ 3374549Seric message("221", "%s closing connection", HostName); 3389339Seric if (InChild) 3399339Seric ExitStat = EX_QUIT; 3404549Seric finis(); 3414549Seric 3428544Seric case CMDVERB: /* set verbose mode */ 3438544Seric Verbose = TRUE; 3448544Seric message("200", "Verbose mode"); 3458544Seric break; 3468544Seric 3479314Seric case CMDONEX: /* doing one transaction only */ 3489378Seric OneXact = TRUE; 3499314Seric message("200", "Only one transaction"); 3509314Seric break; 3519314Seric 3525003Seric # ifdef DEBUG 3539339Seric case CMDDBGQSHOW: /* show queues */ 3546907Seric printf("Send Queue="); 3556907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3565003Seric break; 3577275Seric 3587275Seric case CMDDBGDEBUG: /* set debug mode */ 3597676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3607676Seric tTflag(p); 3617676Seric message("200", "Debug set"); 3627275Seric break; 3637275Seric 3647282Seric case CMDDBGKILL: /* kill the parent */ 3658544Seric if (!iswiz()) 3668544Seric break; 3677282Seric if (kill(MotherPid, SIGTERM) >= 0) 3687282Seric message("200", "Mother is dead"); 3697282Seric else 3707282Seric message("500", "Can't kill Mom"); 3717282Seric break; 3728544Seric 3739339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3749339Seric if (!iswiz()) 3759339Seric break; 3769378Seric if (fileno(InChannel) != 0) 3779378Seric { 3789378Seric (void) close(0); 3799378Seric (void) dup(fileno(InChannel)); 38010346Seric if (fileno(InChannel) != fileno(OutChannel)) 38110346Seric (void) fclose(InChannel); 3829378Seric InChannel = stdin; 3839378Seric } 3849378Seric if (fileno(OutChannel) != 1) 3859378Seric { 3869378Seric (void) close(1); 3879378Seric (void) dup(fileno(OutChannel)); 3889378Seric (void) fclose(OutChannel); 3899378Seric OutChannel = stdout; 3909378Seric } 39110346Seric (void) close(2); 39210346Seric (void) dup(1); 3939339Seric execl("/bin/csh", "sendmail", 0); 3949339Seric execl("/bin/sh", "sendmail", 0); 3959339Seric message("500", "Can't"); 3969378Seric exit(EX_UNAVAILABLE); 3979339Seric 3988544Seric case CMDDBGWIZ: /* become a wizard */ 3998544Seric if (WizWord != NULL) 4008544Seric { 4018544Seric char seed[3]; 4028544Seric extern char *crypt(); 4038544Seric 4048544Seric strncpy(seed, WizWord, 2); 4058544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 4068544Seric { 4078544Seric message("500", "You are no wizard!"); 4088544Seric break; 4098544Seric } 4108544Seric } 4118544Seric IsWiz = TRUE; 4128544Seric message("200", "Please pass, oh mighty wizard"); 4138544Seric break; 4145003Seric # endif DEBUG 4155003Seric 4164549Seric case CMDERROR: /* unknown command */ 4174549Seric message("500", "Command unrecognized"); 4184549Seric break; 4194549Seric 4204549Seric default: 4214549Seric syserr("smtp: unknown code %d", c->cmdcode); 4224549Seric break; 4234549Seric } 4244549Seric } 4254549Seric } 4264549Seric /* 4274549Seric ** SKIPWORD -- skip a fixed word. 4284549Seric ** 4294549Seric ** Parameters: 4304549Seric ** p -- place to start looking. 4314549Seric ** w -- word to skip. 4324549Seric ** 4334549Seric ** Returns: 4344549Seric ** p following w. 4354549Seric ** NULL on error. 4364549Seric ** 4374549Seric ** Side Effects: 4384549Seric ** clobbers the p data area. 4394549Seric */ 4404549Seric 4414549Seric static char * 4424549Seric skipword(p, w) 4434549Seric register char *p; 4444549Seric char *w; 4454549Seric { 4464549Seric register char *q; 4474549Seric extern bool sameword(); 4484549Seric 4494549Seric /* find beginning of word */ 4504549Seric while (isspace(*p)) 4514549Seric p++; 4524549Seric q = p; 4534549Seric 4544549Seric /* find end of word */ 4554549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4564549Seric p++; 4574549Seric while (isspace(*p)) 4584549Seric *p++ = '\0'; 4594549Seric if (*p != ':') 4604549Seric { 4614549Seric syntax: 4624549Seric message("501", "Syntax error"); 4634549Seric Errors++; 4644549Seric return (NULL); 4654549Seric } 4664549Seric *p++ = '\0'; 4674549Seric while (isspace(*p)) 4684549Seric p++; 4694549Seric 4704549Seric /* see if the input word matches desired word */ 4714549Seric if (!sameword(q, w)) 4724549Seric goto syntax; 4734549Seric 4744549Seric return (p); 4754549Seric } 4764577Seric /* 4774577Seric ** HELP -- implement the HELP command. 4784577Seric ** 4794577Seric ** Parameters: 4804577Seric ** topic -- the topic we want help for. 4814577Seric ** 4824577Seric ** Returns: 4834577Seric ** none. 4844577Seric ** 4854577Seric ** Side Effects: 4864577Seric ** outputs the help file to message output. 4874577Seric */ 4884577Seric 4894577Seric help(topic) 4904577Seric char *topic; 4914577Seric { 4924577Seric register FILE *hf; 4934577Seric int len; 4944577Seric char buf[MAXLINE]; 4954577Seric bool noinfo; 4964577Seric 4978269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4984577Seric { 4994577Seric /* no help */ 500*11931Seric errno = 0; 5014577Seric message("502", "HELP not implemented"); 5024577Seric return; 5034577Seric } 5044577Seric 5054577Seric len = strlen(topic); 5064577Seric makelower(topic); 5074577Seric noinfo = TRUE; 5084577Seric 5094577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5104577Seric { 5114577Seric if (strncmp(buf, topic, len) == 0) 5124577Seric { 5134577Seric register char *p; 5144577Seric 5154577Seric p = index(buf, '\t'); 5164577Seric if (p == NULL) 5174577Seric p = buf; 5184577Seric else 5194577Seric p++; 5204577Seric fixcrlf(p, TRUE); 5214577Seric message("214-", p); 5224577Seric noinfo = FALSE; 5234577Seric } 5244577Seric } 5254577Seric 5264577Seric if (noinfo) 5274577Seric message("504", "HELP topic unknown"); 5284577Seric else 5294577Seric message("214", "End of HELP info"); 5304628Seric (void) fclose(hf); 5314577Seric } 5328544Seric /* 5338544Seric ** ISWIZ -- tell us if we are a wizard 5348544Seric ** 5358544Seric ** If not, print a nasty message. 5368544Seric ** 5378544Seric ** Parameters: 5388544Seric ** none. 5398544Seric ** 5408544Seric ** Returns: 5418544Seric ** TRUE if we are a wizard. 5428544Seric ** FALSE if we are not a wizard. 5438544Seric ** 5448544Seric ** Side Effects: 5458544Seric ** Prints a 500 exit stat if we are not a wizard. 5468544Seric */ 5475181Seric 5488544Seric bool 5498544Seric iswiz() 5508544Seric { 5518544Seric if (!IsWiz) 5528544Seric message("500", "Mere mortals musn't mutter that mantra"); 5538544Seric return (IsWiz); 5548544Seric } 5559339Seric /* 5569339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5579339Seric ** 5589339Seric ** Parameters: 5599339Seric ** label -- a string used in error messages 5609339Seric ** 5619339Seric ** Returns: 5629339Seric ** zero in the child 5639339Seric ** one in the parent 5649339Seric ** 5659339Seric ** Side Effects: 5669339Seric ** none. 5679339Seric */ 5688544Seric 5699339Seric runinchild(label) 5709339Seric char *label; 5719339Seric { 5729339Seric int childpid; 5739339Seric 5749378Seric if (OneXact) 5759378Seric return (0); 5769378Seric 5779339Seric childpid = dofork(); 5789339Seric if (childpid < 0) 5799339Seric { 5809339Seric syserr("%s: cannot fork", label); 5819339Seric return (1); 5829339Seric } 5839339Seric if (childpid > 0) 5849339Seric { 5859339Seric auto int st; 5869339Seric 5879378Seric /* parent -- wait for child to complete */ 5889378Seric st = waitfor(childpid); 5899378Seric if (st == -1) 5909339Seric syserr("%s: lost child", label); 5919339Seric 5929339Seric /* if we exited on a QUIT command, complete the process */ 5939339Seric if (st == (EX_QUIT << 8)) 5949339Seric finis(); 5959339Seric 5969339Seric return (1); 5979339Seric } 5989339Seric else 5999339Seric { 6009339Seric /* child */ 6019339Seric InChild = TRUE; 6029545Seric clearenvelope(CurEnv); 6039339Seric return (0); 6049339Seric } 6059339Seric } 6069339Seric 6075181Seric # endif SMTP 608