19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 34549Seric 45181Seric # ifndef SMTP 5*11151Seric SCCSID(@(#)srvrsmtp.c 3.47 02/18/83 (no SMTP)); 65181Seric # else SMTP 74556Seric 8*11151Seric SCCSID(@(#)srvrsmtp.c 3.47 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 */ 7911146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 8011146Seric 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(); 98*11151Seric extern char *macvalue(); 994549Seric 1005003Seric hasmail = FALSE; 1014713Seric rcps = 0; 1027363Seric if (OutChannel != stdout) 1037363Seric { 1047363Seric /* arrange for debugging output to go to remote host */ 1057363Seric (void) close(1); 1067363Seric (void) dup(fileno(OutChannel)); 1077363Seric } 10810708Seric expand("$e", inp, &inp[sizeof inp], CurEnv); 10910708Seric message("220", inp); 1107762Seric (void) setjmp(TopFrame); 1117762Seric QuickAbort = FALSE; 1129390Seric HoldErrs = FALSE; 1134549Seric for (;;) 1144549Seric { 1157356Seric /* setup for the read */ 1166907Seric CurEnv->e_to = NULL; 1174577Seric Errors = 0; 1187275Seric (void) fflush(stdout); 1197356Seric 1207356Seric /* read the input line */ 1217685Seric p = sfgets(inp, sizeof inp, InChannel); 1227356Seric 1237685Seric /* handle errors */ 1247356Seric if (p == NULL) 1257356Seric { 1264549Seric /* end of file, just die */ 1274558Seric message("421", "%s Lost input channel", HostName); 1284549Seric finis(); 1294549Seric } 1304549Seric 1314549Seric /* clean up end of line */ 1324558Seric fixcrlf(inp, TRUE); 1334549Seric 1344713Seric /* echo command to transcript */ 1359545Seric if (CurEnv->e_xfp != NULL) 1369545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1374713Seric 1384549Seric /* break off command */ 1394549Seric for (p = inp; isspace(*p); p++) 1404549Seric continue; 1414549Seric cmd = p; 1424549Seric while (*++p != '\0' && !isspace(*p)) 1434549Seric continue; 1444549Seric if (*p != '\0') 1454549Seric *p++ = '\0'; 1464549Seric 1474549Seric /* decode command */ 1484549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1494549Seric { 1504549Seric if (sameword(c->cmdname, cmd)) 1514549Seric break; 1524549Seric } 1534549Seric 1544549Seric /* process command */ 1554549Seric switch (c->cmdcode) 1564549Seric { 1574976Seric case CMDHELO: /* hello -- introduce yourself */ 15811146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 15911146Seric { 16011146Seric char buf[MAXNAME]; 16111146Seric 16211146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 16311146Seric define('s', newstr(buf), CurEnv); 16411146Seric } 16511146Seric else 16611146Seric define('s', newstr(p), CurEnv); 1674997Seric message("250", "%s Hello %s, pleased to meet you", 1684997Seric HostName, p); 1694976Seric break; 1704976Seric 1714549Seric case CMDMAIL: /* mail -- designate sender */ 172*11151Seric /* force a sending host even if no HELO given */ 173*11151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 174*11151Seric define('s', RealHostName, CurEnv); 175*11151Seric 1769314Seric /* check for validity of this command */ 1774558Seric if (hasmail) 1784558Seric { 1794558Seric message("503", "Sender already specified"); 1804558Seric break; 1814558Seric } 1829339Seric if (InChild) 1839339Seric { 1849339Seric syserr("Nested MAIL command"); 1859339Seric exit(0); 1869339Seric } 1879339Seric 1889339Seric /* fork a subprocess to process this command */ 1899339Seric if (runinchild("SMTP-MAIL") > 0) 1909339Seric break; 1919339Seric initsys(); 1929339Seric 1939339Seric /* child -- go do the processing */ 1944549Seric p = skipword(p, "from"); 1954549Seric if (p == NULL) 1964549Seric break; 1974549Seric setsender(p); 1984577Seric if (Errors == 0) 1994549Seric { 2004549Seric message("250", "Sender ok"); 2014549Seric hasmail = TRUE; 2024549Seric } 2039339Seric else if (InChild) 2049339Seric finis(); 2054549Seric break; 2064549Seric 2074976Seric case CMDRCPT: /* rcpt -- designate recipient */ 2084549Seric p = skipword(p, "to"); 2094549Seric if (p == NULL) 2104549Seric break; 2119619Seric sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 2129390Seric CurEnv->e_flags &= ~EF_FATALERRS; 2134577Seric if (Errors == 0) 2144549Seric { 2156057Seric message("250", "%s... Recipient ok", p); 2164713Seric rcps++; 2174549Seric } 2184549Seric break; 2194549Seric 2204549Seric case CMDDATA: /* data -- text of mail */ 2214976Seric if (!hasmail) 2224549Seric { 2234976Seric message("503", "Need MAIL command"); 2244976Seric break; 2254549Seric } 2264713Seric else if (rcps <= 0) 2274549Seric { 2284976Seric message("503", "Need RCPT (recipient)"); 2294976Seric break; 2304549Seric } 2314976Seric 2324976Seric /* collect the text of the message */ 2334976Seric collect(TRUE); 2344976Seric if (Errors != 0) 2354976Seric break; 2364976Seric 2378238Seric /* 2388238Seric ** Arrange to send to everyone. 2398238Seric ** If sending to multiple people, mail back 2408238Seric ** errors rather than reporting directly. 2418238Seric ** In any case, don't mail back errors for 2428238Seric ** anything that has happened up to 2438238Seric ** now (the other end will do this). 24410197Seric ** Truncate our transcript -- the mail has gotten 24510197Seric ** to us successfully, and if we have 24610197Seric ** to mail this back, it will be easier 24710197Seric ** on the reader. 2488238Seric ** Then send to everyone. 2498238Seric ** Finally give a reply code. If an error has 2508238Seric ** already been given, don't mail a 2518238Seric ** message back. 2529339Seric ** We goose error returns by clearing error bit. 2538238Seric */ 2548238Seric 2554976Seric if (rcps != 1) 2569378Seric { 2579378Seric HoldErrs = TRUE; 2589378Seric ErrorMode == EM_MAIL; 2599378Seric } 2609339Seric CurEnv->e_flags &= ~EF_FATALERRS; 26110197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2624976Seric 2634976Seric /* send to all recipients */ 2649283Seric sendall(CurEnv, SendMode); 2656907Seric CurEnv->e_to = NULL; 2664976Seric 2678238Seric /* issue success if appropriate and reset */ 2688238Seric if (Errors == 0 || HoldErrs) 2699283Seric message("250", "Ok"); 2708238Seric else 2719339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2729339Seric 2739339Seric /* if in a child, pop back to our parent */ 2749339Seric if (InChild) 2759339Seric finis(); 2764549Seric break; 2774549Seric 2784549Seric case CMDRSET: /* rset -- reset state */ 2794549Seric message("250", "Reset state"); 2809339Seric if (InChild) 2819339Seric finis(); 2829339Seric break; 2834549Seric 2844549Seric case CMDVRFY: /* vrfy -- verify address */ 2859339Seric if (runinchild("SMTP-VRFY") > 0) 2869339Seric break; 2875003Seric vrfyqueue = NULL; 2887762Seric QuickAbort = TRUE; 2899619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 2907762Seric if (Errors != 0) 2919339Seric { 2929339Seric if (InChild) 2939339Seric finis(); 2947762Seric break; 2959339Seric } 2965003Seric while (vrfyqueue != NULL) 2975003Seric { 2985003Seric register ADDRESS *a = vrfyqueue->q_next; 2995003Seric char *code; 3005003Seric 3017685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3025003Seric a = a->q_next; 3035003Seric 3047685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3055003Seric { 3065003Seric if (a != NULL) 3075003Seric code = "250-"; 3085003Seric else 3095003Seric code = "250"; 3105003Seric if (vrfyqueue->q_fullname == NULL) 3115003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3125003Seric else 3135003Seric message(code, "%s <%s>", 3145003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3155003Seric } 3165003Seric else if (a == NULL) 3175003Seric message("554", "Self destructive alias loop"); 3185003Seric vrfyqueue = a; 3195003Seric } 3209339Seric if (InChild) 3219339Seric finis(); 3224549Seric break; 3234549Seric 3244549Seric case CMDHELP: /* help -- give user info */ 3254577Seric if (*p == '\0') 3264577Seric p = "SMTP"; 3274577Seric help(p); 3284549Seric break; 3294549Seric 3304549Seric case CMDNOOP: /* noop -- do nothing */ 3314549Seric message("200", "OK"); 3324549Seric break; 3334549Seric 3344549Seric case CMDQUIT: /* quit -- leave mail */ 3354549Seric message("221", "%s closing connection", HostName); 3369339Seric if (InChild) 3379339Seric ExitStat = EX_QUIT; 3384549Seric finis(); 3394549Seric 3408544Seric case CMDVERB: /* set verbose mode */ 3418544Seric Verbose = TRUE; 3428544Seric message("200", "Verbose mode"); 3438544Seric break; 3448544Seric 3459314Seric case CMDONEX: /* doing one transaction only */ 3469378Seric OneXact = TRUE; 3479314Seric message("200", "Only one transaction"); 3489314Seric break; 3499314Seric 3505003Seric # ifdef DEBUG 3519339Seric case CMDDBGQSHOW: /* show queues */ 3526907Seric printf("Send Queue="); 3536907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3545003Seric break; 3557275Seric 3567275Seric case CMDDBGDEBUG: /* set debug mode */ 3577676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3587676Seric tTflag(p); 3597676Seric message("200", "Debug set"); 3607275Seric break; 3617275Seric 3627282Seric case CMDDBGKILL: /* kill the parent */ 3638544Seric if (!iswiz()) 3648544Seric break; 3657282Seric if (kill(MotherPid, SIGTERM) >= 0) 3667282Seric message("200", "Mother is dead"); 3677282Seric else 3687282Seric message("500", "Can't kill Mom"); 3697282Seric break; 3708544Seric 3719339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3729339Seric if (!iswiz()) 3739339Seric break; 3749378Seric if (fileno(InChannel) != 0) 3759378Seric { 3769378Seric (void) close(0); 3779378Seric (void) dup(fileno(InChannel)); 37810346Seric if (fileno(InChannel) != fileno(OutChannel)) 37910346Seric (void) fclose(InChannel); 3809378Seric InChannel = stdin; 3819378Seric } 3829378Seric if (fileno(OutChannel) != 1) 3839378Seric { 3849378Seric (void) close(1); 3859378Seric (void) dup(fileno(OutChannel)); 3869378Seric (void) fclose(OutChannel); 3879378Seric OutChannel = stdout; 3889378Seric } 38910346Seric (void) close(2); 39010346Seric (void) dup(1); 3919339Seric execl("/bin/csh", "sendmail", 0); 3929339Seric execl("/bin/sh", "sendmail", 0); 3939339Seric message("500", "Can't"); 3949378Seric exit(EX_UNAVAILABLE); 3959339Seric 3968544Seric case CMDDBGWIZ: /* become a wizard */ 3978544Seric if (WizWord != NULL) 3988544Seric { 3998544Seric char seed[3]; 4008544Seric extern char *crypt(); 4018544Seric 4028544Seric strncpy(seed, WizWord, 2); 4038544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 4048544Seric { 4058544Seric message("500", "You are no wizard!"); 4068544Seric break; 4078544Seric } 4088544Seric } 4098544Seric IsWiz = TRUE; 4108544Seric message("200", "Please pass, oh mighty wizard"); 4118544Seric break; 4125003Seric # endif DEBUG 4135003Seric 4144549Seric case CMDERROR: /* unknown command */ 4154549Seric message("500", "Command unrecognized"); 4164549Seric break; 4174549Seric 4184549Seric default: 4194549Seric syserr("smtp: unknown code %d", c->cmdcode); 4204549Seric break; 4214549Seric } 4224549Seric } 4234549Seric } 4244549Seric /* 4254549Seric ** SKIPWORD -- skip a fixed word. 4264549Seric ** 4274549Seric ** Parameters: 4284549Seric ** p -- place to start looking. 4294549Seric ** w -- word to skip. 4304549Seric ** 4314549Seric ** Returns: 4324549Seric ** p following w. 4334549Seric ** NULL on error. 4344549Seric ** 4354549Seric ** Side Effects: 4364549Seric ** clobbers the p data area. 4374549Seric */ 4384549Seric 4394549Seric static char * 4404549Seric skipword(p, w) 4414549Seric register char *p; 4424549Seric char *w; 4434549Seric { 4444549Seric register char *q; 4454549Seric extern bool sameword(); 4464549Seric 4474549Seric /* find beginning of word */ 4484549Seric while (isspace(*p)) 4494549Seric p++; 4504549Seric q = p; 4514549Seric 4524549Seric /* find end of word */ 4534549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4544549Seric p++; 4554549Seric while (isspace(*p)) 4564549Seric *p++ = '\0'; 4574549Seric if (*p != ':') 4584549Seric { 4594549Seric syntax: 4604549Seric message("501", "Syntax error"); 4614549Seric Errors++; 4624549Seric return (NULL); 4634549Seric } 4644549Seric *p++ = '\0'; 4654549Seric while (isspace(*p)) 4664549Seric p++; 4674549Seric 4684549Seric /* see if the input word matches desired word */ 4694549Seric if (!sameword(q, w)) 4704549Seric goto syntax; 4714549Seric 4724549Seric return (p); 4734549Seric } 4744577Seric /* 4754577Seric ** HELP -- implement the HELP command. 4764577Seric ** 4774577Seric ** Parameters: 4784577Seric ** topic -- the topic we want help for. 4794577Seric ** 4804577Seric ** Returns: 4814577Seric ** none. 4824577Seric ** 4834577Seric ** Side Effects: 4844577Seric ** outputs the help file to message output. 4854577Seric */ 4864577Seric 4874577Seric help(topic) 4884577Seric char *topic; 4894577Seric { 4904577Seric register FILE *hf; 4914577Seric int len; 4924577Seric char buf[MAXLINE]; 4934577Seric bool noinfo; 4944577Seric 4958269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4964577Seric { 4974577Seric /* no help */ 4984577Seric message("502", "HELP not implemented"); 4994577Seric return; 5004577Seric } 5014577Seric 5024577Seric len = strlen(topic); 5034577Seric makelower(topic); 5044577Seric noinfo = TRUE; 5054577Seric 5064577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5074577Seric { 5084577Seric if (strncmp(buf, topic, len) == 0) 5094577Seric { 5104577Seric register char *p; 5114577Seric 5124577Seric p = index(buf, '\t'); 5134577Seric if (p == NULL) 5144577Seric p = buf; 5154577Seric else 5164577Seric p++; 5174577Seric fixcrlf(p, TRUE); 5184577Seric message("214-", p); 5194577Seric noinfo = FALSE; 5204577Seric } 5214577Seric } 5224577Seric 5234577Seric if (noinfo) 5244577Seric message("504", "HELP topic unknown"); 5254577Seric else 5264577Seric message("214", "End of HELP info"); 5274628Seric (void) fclose(hf); 5284577Seric } 5298544Seric /* 5308544Seric ** ISWIZ -- tell us if we are a wizard 5318544Seric ** 5328544Seric ** If not, print a nasty message. 5338544Seric ** 5348544Seric ** Parameters: 5358544Seric ** none. 5368544Seric ** 5378544Seric ** Returns: 5388544Seric ** TRUE if we are a wizard. 5398544Seric ** FALSE if we are not a wizard. 5408544Seric ** 5418544Seric ** Side Effects: 5428544Seric ** Prints a 500 exit stat if we are not a wizard. 5438544Seric */ 5445181Seric 5458544Seric bool 5468544Seric iswiz() 5478544Seric { 5488544Seric if (!IsWiz) 5498544Seric message("500", "Mere mortals musn't mutter that mantra"); 5508544Seric return (IsWiz); 5518544Seric } 5529339Seric /* 5539339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5549339Seric ** 5559339Seric ** Parameters: 5569339Seric ** label -- a string used in error messages 5579339Seric ** 5589339Seric ** Returns: 5599339Seric ** zero in the child 5609339Seric ** one in the parent 5619339Seric ** 5629339Seric ** Side Effects: 5639339Seric ** none. 5649339Seric */ 5658544Seric 5669339Seric runinchild(label) 5679339Seric char *label; 5689339Seric { 5699339Seric int childpid; 5709339Seric 5719378Seric if (OneXact) 5729378Seric return (0); 5739378Seric 5749339Seric childpid = dofork(); 5759339Seric if (childpid < 0) 5769339Seric { 5779339Seric syserr("%s: cannot fork", label); 5789339Seric return (1); 5799339Seric } 5809339Seric if (childpid > 0) 5819339Seric { 5829339Seric auto int st; 5839339Seric 5849378Seric /* parent -- wait for child to complete */ 5859378Seric st = waitfor(childpid); 5869378Seric if (st == -1) 5879339Seric syserr("%s: lost child", label); 5889339Seric 5899339Seric /* if we exited on a QUIT command, complete the process */ 5909339Seric if (st == (EX_QUIT << 8)) 5919339Seric finis(); 5929339Seric 5939339Seric return (1); 5949339Seric } 5959339Seric else 5969339Seric { 5979339Seric /* child */ 5989339Seric InChild = TRUE; 5999545Seric clearenvelope(CurEnv); 6009339Seric return (0); 6019339Seric } 6029339Seric } 6039339Seric 6045181Seric # endif SMTP 605