19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 3*11728Seric # include <signal.h> 44549Seric 55181Seric # ifndef SMTP 6*11728Seric SCCSID(@(#)srvrsmtp.c 3.48 03/26/83 (no SMTP)); 75181Seric # else SMTP 84556Seric 9*11728Seric SCCSID(@(#)srvrsmtp.c 3.48 03/26/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 } 10910708Seric expand("$e", inp, &inp[sizeof inp], CurEnv); 11010708Seric message("220", inp); 1117762Seric (void) setjmp(TopFrame); 1127762Seric QuickAbort = FALSE; 1139390Seric HoldErrs = FALSE; 1144549Seric for (;;) 1154549Seric { 1167356Seric /* setup for the read */ 1176907Seric CurEnv->e_to = NULL; 1184577Seric Errors = 0; 1197275Seric (void) fflush(stdout); 1207356Seric 1217356Seric /* read the input line */ 1227685Seric p = sfgets(inp, sizeof inp, InChannel); 1237356Seric 1247685Seric /* handle errors */ 1257356Seric if (p == NULL) 1267356Seric { 1274549Seric /* end of file, just die */ 1284558Seric message("421", "%s Lost input channel", HostName); 1294549Seric finis(); 1304549Seric } 1314549Seric 1324549Seric /* clean up end of line */ 1334558Seric fixcrlf(inp, TRUE); 1344549Seric 1354713Seric /* echo command to transcript */ 1369545Seric if (CurEnv->e_xfp != NULL) 1379545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1384713Seric 1394549Seric /* break off command */ 1404549Seric for (p = inp; isspace(*p); p++) 1414549Seric continue; 1424549Seric cmd = p; 1434549Seric while (*++p != '\0' && !isspace(*p)) 1444549Seric continue; 1454549Seric if (*p != '\0') 1464549Seric *p++ = '\0'; 1474549Seric 1484549Seric /* decode command */ 1494549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1504549Seric { 1514549Seric if (sameword(c->cmdname, cmd)) 1524549Seric break; 1534549Seric } 1544549Seric 1554549Seric /* process command */ 1564549Seric switch (c->cmdcode) 1574549Seric { 1584976Seric case CMDHELO: /* hello -- introduce yourself */ 15911146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 16011146Seric { 16111146Seric char buf[MAXNAME]; 16211146Seric 16311146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 16411146Seric define('s', newstr(buf), CurEnv); 16511146Seric } 16611146Seric else 16711146Seric define('s', newstr(p), CurEnv); 1684997Seric message("250", "%s Hello %s, pleased to meet you", 1694997Seric HostName, p); 1704976Seric break; 1714976Seric 1724549Seric case CMDMAIL: /* mail -- designate sender */ 17311151Seric /* force a sending host even if no HELO given */ 17411151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 17511151Seric define('s', RealHostName, CurEnv); 17611151Seric 1779314Seric /* check for validity of this command */ 1784558Seric if (hasmail) 1794558Seric { 1804558Seric message("503", "Sender already specified"); 1814558Seric break; 1824558Seric } 1839339Seric if (InChild) 1849339Seric { 1859339Seric syserr("Nested MAIL command"); 1869339Seric exit(0); 1879339Seric } 1889339Seric 1899339Seric /* fork a subprocess to process this command */ 1909339Seric if (runinchild("SMTP-MAIL") > 0) 1919339Seric break; 1929339Seric initsys(); 1939339Seric 1949339Seric /* child -- go do the processing */ 1954549Seric p = skipword(p, "from"); 1964549Seric if (p == NULL) 1974549Seric break; 1984549Seric setsender(p); 1994577Seric if (Errors == 0) 2004549Seric { 2014549Seric message("250", "Sender ok"); 2024549Seric hasmail = TRUE; 2034549Seric } 2049339Seric else if (InChild) 2059339Seric finis(); 2064549Seric break; 2074549Seric 2084976Seric case CMDRCPT: /* rcpt -- designate recipient */ 2094549Seric p = skipword(p, "to"); 2104549Seric if (p == NULL) 2114549Seric break; 2129619Seric sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 2139390Seric CurEnv->e_flags &= ~EF_FATALERRS; 2144577Seric if (Errors == 0) 2154549Seric { 2166057Seric message("250", "%s... Recipient ok", p); 2174713Seric rcps++; 2184549Seric } 2194549Seric break; 2204549Seric 2214549Seric case CMDDATA: /* data -- text of mail */ 2224976Seric if (!hasmail) 2234549Seric { 2244976Seric message("503", "Need MAIL command"); 2254976Seric break; 2264549Seric } 2274713Seric else if (rcps <= 0) 2284549Seric { 2294976Seric message("503", "Need RCPT (recipient)"); 2304976Seric break; 2314549Seric } 2324976Seric 2334976Seric /* collect the text of the message */ 2344976Seric collect(TRUE); 2354976Seric if (Errors != 0) 2364976Seric break; 2374976Seric 2388238Seric /* 2398238Seric ** Arrange to send to everyone. 2408238Seric ** If sending to multiple people, mail back 2418238Seric ** errors rather than reporting directly. 2428238Seric ** In any case, don't mail back errors for 2438238Seric ** anything that has happened up to 2448238Seric ** now (the other end will do this). 24510197Seric ** Truncate our transcript -- the mail has gotten 24610197Seric ** to us successfully, and if we have 24710197Seric ** to mail this back, it will be easier 24810197Seric ** on the reader. 2498238Seric ** Then send to everyone. 2508238Seric ** Finally give a reply code. If an error has 2518238Seric ** already been given, don't mail a 2528238Seric ** message back. 2539339Seric ** We goose error returns by clearing error bit. 2548238Seric */ 2558238Seric 2564976Seric if (rcps != 1) 2579378Seric { 2589378Seric HoldErrs = TRUE; 2599378Seric ErrorMode == EM_MAIL; 2609378Seric } 2619339Seric CurEnv->e_flags &= ~EF_FATALERRS; 26210197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2634976Seric 2644976Seric /* send to all recipients */ 2659283Seric sendall(CurEnv, SendMode); 2666907Seric CurEnv->e_to = NULL; 2674976Seric 2688238Seric /* issue success if appropriate and reset */ 2698238Seric if (Errors == 0 || HoldErrs) 2709283Seric message("250", "Ok"); 2718238Seric else 2729339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2739339Seric 2749339Seric /* if in a child, pop back to our parent */ 2759339Seric if (InChild) 2769339Seric finis(); 2774549Seric break; 2784549Seric 2794549Seric case CMDRSET: /* rset -- reset state */ 2804549Seric message("250", "Reset state"); 2819339Seric if (InChild) 2829339Seric finis(); 2839339Seric break; 2844549Seric 2854549Seric case CMDVRFY: /* vrfy -- verify address */ 2869339Seric if (runinchild("SMTP-VRFY") > 0) 2879339Seric break; 2885003Seric vrfyqueue = NULL; 2897762Seric QuickAbort = TRUE; 2909619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 2917762Seric if (Errors != 0) 2929339Seric { 2939339Seric if (InChild) 2949339Seric finis(); 2957762Seric break; 2969339Seric } 2975003Seric while (vrfyqueue != NULL) 2985003Seric { 2995003Seric register ADDRESS *a = vrfyqueue->q_next; 3005003Seric char *code; 3015003Seric 3027685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3035003Seric a = a->q_next; 3045003Seric 3057685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3065003Seric { 3075003Seric if (a != NULL) 3085003Seric code = "250-"; 3095003Seric else 3105003Seric code = "250"; 3115003Seric if (vrfyqueue->q_fullname == NULL) 3125003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3135003Seric else 3145003Seric message(code, "%s <%s>", 3155003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3165003Seric } 3175003Seric else if (a == NULL) 3185003Seric message("554", "Self destructive alias loop"); 3195003Seric vrfyqueue = a; 3205003Seric } 3219339Seric if (InChild) 3229339Seric finis(); 3234549Seric break; 3244549Seric 3254549Seric case CMDHELP: /* help -- give user info */ 3264577Seric if (*p == '\0') 3274577Seric p = "SMTP"; 3284577Seric help(p); 3294549Seric break; 3304549Seric 3314549Seric case CMDNOOP: /* noop -- do nothing */ 3324549Seric message("200", "OK"); 3334549Seric break; 3344549Seric 3354549Seric case CMDQUIT: /* quit -- leave mail */ 3364549Seric message("221", "%s closing connection", HostName); 3379339Seric if (InChild) 3389339Seric ExitStat = EX_QUIT; 3394549Seric finis(); 3404549Seric 3418544Seric case CMDVERB: /* set verbose mode */ 3428544Seric Verbose = TRUE; 3438544Seric message("200", "Verbose mode"); 3448544Seric break; 3458544Seric 3469314Seric case CMDONEX: /* doing one transaction only */ 3479378Seric OneXact = TRUE; 3489314Seric message("200", "Only one transaction"); 3499314Seric break; 3509314Seric 3515003Seric # ifdef DEBUG 3529339Seric case CMDDBGQSHOW: /* show queues */ 3536907Seric printf("Send Queue="); 3546907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3555003Seric break; 3567275Seric 3577275Seric case CMDDBGDEBUG: /* set debug mode */ 3587676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3597676Seric tTflag(p); 3607676Seric message("200", "Debug set"); 3617275Seric break; 3627275Seric 3637282Seric case CMDDBGKILL: /* kill the parent */ 3648544Seric if (!iswiz()) 3658544Seric break; 3667282Seric if (kill(MotherPid, SIGTERM) >= 0) 3677282Seric message("200", "Mother is dead"); 3687282Seric else 3697282Seric message("500", "Can't kill Mom"); 3707282Seric break; 3718544Seric 3729339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3739339Seric if (!iswiz()) 3749339Seric break; 3759378Seric if (fileno(InChannel) != 0) 3769378Seric { 3779378Seric (void) close(0); 3789378Seric (void) dup(fileno(InChannel)); 37910346Seric if (fileno(InChannel) != fileno(OutChannel)) 38010346Seric (void) fclose(InChannel); 3819378Seric InChannel = stdin; 3829378Seric } 3839378Seric if (fileno(OutChannel) != 1) 3849378Seric { 3859378Seric (void) close(1); 3869378Seric (void) dup(fileno(OutChannel)); 3879378Seric (void) fclose(OutChannel); 3889378Seric OutChannel = stdout; 3899378Seric } 39010346Seric (void) close(2); 39110346Seric (void) dup(1); 3929339Seric execl("/bin/csh", "sendmail", 0); 3939339Seric execl("/bin/sh", "sendmail", 0); 3949339Seric message("500", "Can't"); 3959378Seric exit(EX_UNAVAILABLE); 3969339Seric 3978544Seric case CMDDBGWIZ: /* become a wizard */ 3988544Seric if (WizWord != NULL) 3998544Seric { 4008544Seric char seed[3]; 4018544Seric extern char *crypt(); 4028544Seric 4038544Seric strncpy(seed, WizWord, 2); 4048544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 4058544Seric { 4068544Seric message("500", "You are no wizard!"); 4078544Seric break; 4088544Seric } 4098544Seric } 4108544Seric IsWiz = TRUE; 4118544Seric message("200", "Please pass, oh mighty wizard"); 4128544Seric break; 4135003Seric # endif DEBUG 4145003Seric 4154549Seric case CMDERROR: /* unknown command */ 4164549Seric message("500", "Command unrecognized"); 4174549Seric break; 4184549Seric 4194549Seric default: 4204549Seric syserr("smtp: unknown code %d", c->cmdcode); 4214549Seric break; 4224549Seric } 4234549Seric } 4244549Seric } 4254549Seric /* 4264549Seric ** SKIPWORD -- skip a fixed word. 4274549Seric ** 4284549Seric ** Parameters: 4294549Seric ** p -- place to start looking. 4304549Seric ** w -- word to skip. 4314549Seric ** 4324549Seric ** Returns: 4334549Seric ** p following w. 4344549Seric ** NULL on error. 4354549Seric ** 4364549Seric ** Side Effects: 4374549Seric ** clobbers the p data area. 4384549Seric */ 4394549Seric 4404549Seric static char * 4414549Seric skipword(p, w) 4424549Seric register char *p; 4434549Seric char *w; 4444549Seric { 4454549Seric register char *q; 4464549Seric extern bool sameword(); 4474549Seric 4484549Seric /* find beginning of word */ 4494549Seric while (isspace(*p)) 4504549Seric p++; 4514549Seric q = p; 4524549Seric 4534549Seric /* find end of word */ 4544549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4554549Seric p++; 4564549Seric while (isspace(*p)) 4574549Seric *p++ = '\0'; 4584549Seric if (*p != ':') 4594549Seric { 4604549Seric syntax: 4614549Seric message("501", "Syntax error"); 4624549Seric Errors++; 4634549Seric return (NULL); 4644549Seric } 4654549Seric *p++ = '\0'; 4664549Seric while (isspace(*p)) 4674549Seric p++; 4684549Seric 4694549Seric /* see if the input word matches desired word */ 4704549Seric if (!sameword(q, w)) 4714549Seric goto syntax; 4724549Seric 4734549Seric return (p); 4744549Seric } 4754577Seric /* 4764577Seric ** HELP -- implement the HELP command. 4774577Seric ** 4784577Seric ** Parameters: 4794577Seric ** topic -- the topic we want help for. 4804577Seric ** 4814577Seric ** Returns: 4824577Seric ** none. 4834577Seric ** 4844577Seric ** Side Effects: 4854577Seric ** outputs the help file to message output. 4864577Seric */ 4874577Seric 4884577Seric help(topic) 4894577Seric char *topic; 4904577Seric { 4914577Seric register FILE *hf; 4924577Seric int len; 4934577Seric char buf[MAXLINE]; 4944577Seric bool noinfo; 4954577Seric 4968269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4974577Seric { 4984577Seric /* no help */ 4994577Seric message("502", "HELP not implemented"); 5004577Seric return; 5014577Seric } 5024577Seric 5034577Seric len = strlen(topic); 5044577Seric makelower(topic); 5054577Seric noinfo = TRUE; 5064577Seric 5074577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5084577Seric { 5094577Seric if (strncmp(buf, topic, len) == 0) 5104577Seric { 5114577Seric register char *p; 5124577Seric 5134577Seric p = index(buf, '\t'); 5144577Seric if (p == NULL) 5154577Seric p = buf; 5164577Seric else 5174577Seric p++; 5184577Seric fixcrlf(p, TRUE); 5194577Seric message("214-", p); 5204577Seric noinfo = FALSE; 5214577Seric } 5224577Seric } 5234577Seric 5244577Seric if (noinfo) 5254577Seric message("504", "HELP topic unknown"); 5264577Seric else 5274577Seric message("214", "End of HELP info"); 5284628Seric (void) fclose(hf); 5294577Seric } 5308544Seric /* 5318544Seric ** ISWIZ -- tell us if we are a wizard 5328544Seric ** 5338544Seric ** If not, print a nasty message. 5348544Seric ** 5358544Seric ** Parameters: 5368544Seric ** none. 5378544Seric ** 5388544Seric ** Returns: 5398544Seric ** TRUE if we are a wizard. 5408544Seric ** FALSE if we are not a wizard. 5418544Seric ** 5428544Seric ** Side Effects: 5438544Seric ** Prints a 500 exit stat if we are not a wizard. 5448544Seric */ 5455181Seric 5468544Seric bool 5478544Seric iswiz() 5488544Seric { 5498544Seric if (!IsWiz) 5508544Seric message("500", "Mere mortals musn't mutter that mantra"); 5518544Seric return (IsWiz); 5528544Seric } 5539339Seric /* 5549339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5559339Seric ** 5569339Seric ** Parameters: 5579339Seric ** label -- a string used in error messages 5589339Seric ** 5599339Seric ** Returns: 5609339Seric ** zero in the child 5619339Seric ** one in the parent 5629339Seric ** 5639339Seric ** Side Effects: 5649339Seric ** none. 5659339Seric */ 5668544Seric 5679339Seric runinchild(label) 5689339Seric char *label; 5699339Seric { 5709339Seric int childpid; 5719339Seric 5729378Seric if (OneXact) 5739378Seric return (0); 5749378Seric 5759339Seric childpid = dofork(); 5769339Seric if (childpid < 0) 5779339Seric { 5789339Seric syserr("%s: cannot fork", label); 5799339Seric return (1); 5809339Seric } 5819339Seric if (childpid > 0) 5829339Seric { 5839339Seric auto int st; 5849339Seric 5859378Seric /* parent -- wait for child to complete */ 5869378Seric st = waitfor(childpid); 5879378Seric if (st == -1) 5889339Seric syserr("%s: lost child", label); 5899339Seric 5909339Seric /* if we exited on a QUIT command, complete the process */ 5919339Seric if (st == (EX_QUIT << 8)) 5929339Seric finis(); 5939339Seric 5949339Seric return (1); 5959339Seric } 5969339Seric else 5979339Seric { 5989339Seric /* child */ 5999339Seric InChild = TRUE; 6009545Seric clearenvelope(CurEnv); 6019339Seric return (0); 6029339Seric } 6039339Seric } 6049339Seric 6055181Seric # endif SMTP 606