19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 311728Seric # include <signal.h> 44549Seric 55181Seric # ifndef SMTP 6*14785Seric SCCSID(@(#)srvrsmtp.c 4.2 08/21/83 (no SMTP)); 75181Seric # else SMTP 84556Seric 9*14785Seric SCCSID(@(#)srvrsmtp.c 4.2 08/21/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; 9412612Seric ADDRESS *a; 958544Seric char inp[MAXLINE]; 967124Seric extern char Version[]; 977356Seric extern tick(); 988544Seric extern bool iswiz(); 999349Seric extern char *arpadate(); 10011151Seric extern char *macvalue(); 10112612Seric extern ADDRESS *recipient(); 1024549Seric 1035003Seric hasmail = FALSE; 1044713Seric rcps = 0; 1057363Seric if (OutChannel != stdout) 1067363Seric { 1077363Seric /* arrange for debugging output to go to remote host */ 1087363Seric (void) close(1); 1097363Seric (void) dup(fileno(OutChannel)); 1107363Seric } 11111931Seric settime(); 11210708Seric expand("$e", inp, &inp[sizeof inp], CurEnv); 11310708Seric message("220", inp); 1144549Seric for (;;) 1154549Seric { 11612612Seric /* arrange for backout */ 11712612Seric if (setjmp(TopFrame) > 0 && InChild) 11812612Seric finis(); 11912612Seric QuickAbort = FALSE; 12012612Seric HoldErrs = FALSE; 12112612Seric 1227356Seric /* setup for the read */ 1236907Seric CurEnv->e_to = NULL; 1244577Seric Errors = 0; 1257275Seric (void) fflush(stdout); 1267356Seric 1277356Seric /* read the input line */ 1287685Seric p = sfgets(inp, sizeof inp, InChannel); 1297356Seric 1307685Seric /* handle errors */ 1317356Seric if (p == NULL) 1327356Seric { 1334549Seric /* end of file, just die */ 1344558Seric message("421", "%s Lost input channel", HostName); 1354549Seric finis(); 1364549Seric } 1374549Seric 1384549Seric /* clean up end of line */ 1394558Seric fixcrlf(inp, TRUE); 1404549Seric 1414713Seric /* echo command to transcript */ 1429545Seric if (CurEnv->e_xfp != NULL) 1439545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1444713Seric 1454549Seric /* break off command */ 1464549Seric for (p = inp; isspace(*p); p++) 1474549Seric continue; 1484549Seric cmd = p; 1494549Seric while (*++p != '\0' && !isspace(*p)) 1504549Seric continue; 1514549Seric if (*p != '\0') 1524549Seric *p++ = '\0'; 1534549Seric 1544549Seric /* decode command */ 1554549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1564549Seric { 1574549Seric if (sameword(c->cmdname, cmd)) 1584549Seric break; 1594549Seric } 1604549Seric 1614549Seric /* process command */ 1624549Seric switch (c->cmdcode) 1634549Seric { 1644976Seric case CMDHELO: /* hello -- introduce yourself */ 16511146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 16611146Seric { 16711146Seric char buf[MAXNAME]; 16811146Seric 16911146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 17011146Seric define('s', newstr(buf), CurEnv); 17111146Seric } 17211146Seric else 17311146Seric define('s', newstr(p), CurEnv); 1744997Seric message("250", "%s Hello %s, pleased to meet you", 1754997Seric HostName, p); 1764976Seric break; 1774976Seric 1784549Seric case CMDMAIL: /* mail -- designate sender */ 17911151Seric /* force a sending host even if no HELO given */ 18011151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 18111151Seric define('s', RealHostName, CurEnv); 18211151Seric 1839314Seric /* check for validity of this command */ 1844558Seric if (hasmail) 1854558Seric { 1864558Seric message("503", "Sender already specified"); 1874558Seric break; 1884558Seric } 1899339Seric if (InChild) 1909339Seric { 1919339Seric syserr("Nested MAIL command"); 1929339Seric exit(0); 1939339Seric } 1949339Seric 1959339Seric /* fork a subprocess to process this command */ 1969339Seric if (runinchild("SMTP-MAIL") > 0) 1979339Seric break; 1989339Seric initsys(); 1999339Seric 2009339Seric /* child -- go do the processing */ 2014549Seric p = skipword(p, "from"); 2024549Seric if (p == NULL) 2034549Seric break; 2044549Seric setsender(p); 2054577Seric if (Errors == 0) 2064549Seric { 2074549Seric message("250", "Sender ok"); 2084549Seric hasmail = TRUE; 2094549Seric } 2109339Seric else if (InChild) 2119339Seric finis(); 2124549Seric break; 2134549Seric 2144976Seric case CMDRCPT: /* rcpt -- designate recipient */ 21512612Seric if (setjmp(TopFrame) > 0) 216*14785Seric { 217*14785Seric CurEnv->e_flags &= ~EF_FATALERRS; 21812612Seric break; 219*14785Seric } 22012612Seric QuickAbort = TRUE; 2214549Seric p = skipword(p, "to"); 2224549Seric if (p == NULL) 2234549Seric break; 22412612Seric a = parseaddr(p, (ADDRESS *) NULL, 1); 22512612Seric if (a == NULL) 22612612Seric break; 22712612Seric a = recipient(a, &CurEnv->e_sendqueue); 22812612Seric if (Errors != 0) 22912612Seric break; 23012612Seric 23112612Seric /* no errors during parsing, but might be a duplicate */ 23212612Seric CurEnv->e_to = p; 23312612Seric if (!bitset(QBADADDR, a->q_flags)) 23412612Seric message("250", "Recipient ok"); 23512612Seric else 2364549Seric { 23712612Seric /* punt -- should keep message in ADDRESS.... */ 23812612Seric message("550", "Addressee unknown"); 2394549Seric } 24012612Seric CurEnv->e_to = NULL; 24112612Seric rcps++; 2424549Seric break; 2434549Seric 2444549Seric case CMDDATA: /* data -- text of mail */ 2454976Seric if (!hasmail) 2464549Seric { 2474976Seric message("503", "Need MAIL command"); 2484976Seric break; 2494549Seric } 2504713Seric else if (rcps <= 0) 2514549Seric { 2524976Seric message("503", "Need RCPT (recipient)"); 2534976Seric break; 2544549Seric } 2554976Seric 2564976Seric /* collect the text of the message */ 2574976Seric collect(TRUE); 2584976Seric if (Errors != 0) 2594976Seric break; 2604976Seric 2618238Seric /* 2628238Seric ** Arrange to send to everyone. 2638238Seric ** If sending to multiple people, mail back 2648238Seric ** errors rather than reporting directly. 2658238Seric ** In any case, don't mail back errors for 2668238Seric ** anything that has happened up to 2678238Seric ** now (the other end will do this). 26810197Seric ** Truncate our transcript -- the mail has gotten 26910197Seric ** to us successfully, and if we have 27010197Seric ** to mail this back, it will be easier 27110197Seric ** on the reader. 2728238Seric ** Then send to everyone. 2738238Seric ** Finally give a reply code. If an error has 2748238Seric ** already been given, don't mail a 2758238Seric ** message back. 2769339Seric ** We goose error returns by clearing error bit. 2778238Seric */ 2788238Seric 2794976Seric if (rcps != 1) 2809378Seric { 2819378Seric HoldErrs = TRUE; 2829378Seric ErrorMode == EM_MAIL; 2839378Seric } 2849339Seric CurEnv->e_flags &= ~EF_FATALERRS; 28510197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2864976Seric 2874976Seric /* send to all recipients */ 2889283Seric sendall(CurEnv, SendMode); 2896907Seric CurEnv->e_to = NULL; 2904976Seric 2918238Seric /* issue success if appropriate and reset */ 2928238Seric if (Errors == 0 || HoldErrs) 2939283Seric message("250", "Ok"); 2948238Seric else 2959339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2969339Seric 2979339Seric /* if in a child, pop back to our parent */ 2989339Seric if (InChild) 2999339Seric finis(); 3004549Seric break; 3014549Seric 3024549Seric case CMDRSET: /* rset -- reset state */ 3034549Seric message("250", "Reset state"); 3049339Seric if (InChild) 3059339Seric finis(); 3069339Seric break; 3074549Seric 3084549Seric case CMDVRFY: /* vrfy -- verify address */ 3099339Seric if (runinchild("SMTP-VRFY") > 0) 3109339Seric break; 3115003Seric vrfyqueue = NULL; 3127762Seric QuickAbort = TRUE; 3139619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3147762Seric if (Errors != 0) 3159339Seric { 3169339Seric if (InChild) 3179339Seric finis(); 3187762Seric break; 3199339Seric } 3205003Seric while (vrfyqueue != NULL) 3215003Seric { 3225003Seric register ADDRESS *a = vrfyqueue->q_next; 3235003Seric char *code; 3245003Seric 3257685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3265003Seric a = a->q_next; 3275003Seric 3287685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3295003Seric { 3305003Seric if (a != NULL) 3315003Seric code = "250-"; 3325003Seric else 3335003Seric code = "250"; 3345003Seric if (vrfyqueue->q_fullname == NULL) 3355003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3365003Seric else 3375003Seric message(code, "%s <%s>", 3385003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3395003Seric } 3405003Seric else if (a == NULL) 3415003Seric message("554", "Self destructive alias loop"); 3425003Seric vrfyqueue = a; 3435003Seric } 3449339Seric if (InChild) 3459339Seric finis(); 3464549Seric break; 3474549Seric 3484549Seric case CMDHELP: /* help -- give user info */ 3494577Seric if (*p == '\0') 3504577Seric p = "SMTP"; 3514577Seric help(p); 3524549Seric break; 3534549Seric 3544549Seric case CMDNOOP: /* noop -- do nothing */ 3554549Seric message("200", "OK"); 3564549Seric break; 3574549Seric 3584549Seric case CMDQUIT: /* quit -- leave mail */ 3594549Seric message("221", "%s closing connection", HostName); 3609339Seric if (InChild) 3619339Seric ExitStat = EX_QUIT; 3624549Seric finis(); 3634549Seric 3648544Seric case CMDVERB: /* set verbose mode */ 3658544Seric Verbose = TRUE; 3668544Seric message("200", "Verbose mode"); 3678544Seric break; 3688544Seric 3699314Seric case CMDONEX: /* doing one transaction only */ 3709378Seric OneXact = TRUE; 3719314Seric message("200", "Only one transaction"); 3729314Seric break; 3739314Seric 3745003Seric # ifdef DEBUG 3759339Seric case CMDDBGQSHOW: /* show queues */ 3766907Seric printf("Send Queue="); 3776907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3785003Seric break; 3797275Seric 3807275Seric case CMDDBGDEBUG: /* set debug mode */ 3817676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3827676Seric tTflag(p); 3837676Seric message("200", "Debug set"); 3847275Seric break; 3857275Seric 3867282Seric case CMDDBGKILL: /* kill the parent */ 3878544Seric if (!iswiz()) 3888544Seric break; 3897282Seric if (kill(MotherPid, SIGTERM) >= 0) 3907282Seric message("200", "Mother is dead"); 3917282Seric else 3927282Seric message("500", "Can't kill Mom"); 3937282Seric break; 3948544Seric 3959339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3969339Seric if (!iswiz()) 3979339Seric break; 3989378Seric if (fileno(InChannel) != 0) 3999378Seric { 4009378Seric (void) close(0); 4019378Seric (void) dup(fileno(InChannel)); 40210346Seric if (fileno(InChannel) != fileno(OutChannel)) 40310346Seric (void) fclose(InChannel); 4049378Seric InChannel = stdin; 4059378Seric } 4069378Seric if (fileno(OutChannel) != 1) 4079378Seric { 4089378Seric (void) close(1); 4099378Seric (void) dup(fileno(OutChannel)); 4109378Seric (void) fclose(OutChannel); 4119378Seric OutChannel = stdout; 4129378Seric } 41310346Seric (void) close(2); 41410346Seric (void) dup(1); 4159339Seric execl("/bin/csh", "sendmail", 0); 4169339Seric execl("/bin/sh", "sendmail", 0); 4179339Seric message("500", "Can't"); 4189378Seric exit(EX_UNAVAILABLE); 4199339Seric 4208544Seric case CMDDBGWIZ: /* become a wizard */ 4218544Seric if (WizWord != NULL) 4228544Seric { 4238544Seric char seed[3]; 4248544Seric extern char *crypt(); 4258544Seric 4268544Seric strncpy(seed, WizWord, 2); 4278544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 4288544Seric { 4298544Seric message("500", "You are no wizard!"); 4308544Seric break; 4318544Seric } 4328544Seric } 4338544Seric IsWiz = TRUE; 4348544Seric message("200", "Please pass, oh mighty wizard"); 4358544Seric break; 4365003Seric # endif DEBUG 4375003Seric 4384549Seric case CMDERROR: /* unknown command */ 4394549Seric message("500", "Command unrecognized"); 4404549Seric break; 4414549Seric 4424549Seric default: 4434549Seric syserr("smtp: unknown code %d", c->cmdcode); 4444549Seric break; 4454549Seric } 4464549Seric } 4474549Seric } 4484549Seric /* 4494549Seric ** SKIPWORD -- skip a fixed word. 4504549Seric ** 4514549Seric ** Parameters: 4524549Seric ** p -- place to start looking. 4534549Seric ** w -- word to skip. 4544549Seric ** 4554549Seric ** Returns: 4564549Seric ** p following w. 4574549Seric ** NULL on error. 4584549Seric ** 4594549Seric ** Side Effects: 4604549Seric ** clobbers the p data area. 4614549Seric */ 4624549Seric 4634549Seric static char * 4644549Seric skipword(p, w) 4654549Seric register char *p; 4664549Seric char *w; 4674549Seric { 4684549Seric register char *q; 4694549Seric extern bool sameword(); 4704549Seric 4714549Seric /* find beginning of word */ 4724549Seric while (isspace(*p)) 4734549Seric p++; 4744549Seric q = p; 4754549Seric 4764549Seric /* find end of word */ 4774549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4784549Seric p++; 4794549Seric while (isspace(*p)) 4804549Seric *p++ = '\0'; 4814549Seric if (*p != ':') 4824549Seric { 4834549Seric syntax: 4844549Seric message("501", "Syntax error"); 4854549Seric Errors++; 4864549Seric return (NULL); 4874549Seric } 4884549Seric *p++ = '\0'; 4894549Seric while (isspace(*p)) 4904549Seric p++; 4914549Seric 4924549Seric /* see if the input word matches desired word */ 4934549Seric if (!sameword(q, w)) 4944549Seric goto syntax; 4954549Seric 4964549Seric return (p); 4974549Seric } 4984577Seric /* 4994577Seric ** HELP -- implement the HELP command. 5004577Seric ** 5014577Seric ** Parameters: 5024577Seric ** topic -- the topic we want help for. 5034577Seric ** 5044577Seric ** Returns: 5054577Seric ** none. 5064577Seric ** 5074577Seric ** Side Effects: 5084577Seric ** outputs the help file to message output. 5094577Seric */ 5104577Seric 5114577Seric help(topic) 5124577Seric char *topic; 5134577Seric { 5144577Seric register FILE *hf; 5154577Seric int len; 5164577Seric char buf[MAXLINE]; 5174577Seric bool noinfo; 5184577Seric 5198269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5204577Seric { 5214577Seric /* no help */ 52211931Seric errno = 0; 5234577Seric message("502", "HELP not implemented"); 5244577Seric return; 5254577Seric } 5264577Seric 5274577Seric len = strlen(topic); 5284577Seric makelower(topic); 5294577Seric noinfo = TRUE; 5304577Seric 5314577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5324577Seric { 5334577Seric if (strncmp(buf, topic, len) == 0) 5344577Seric { 5354577Seric register char *p; 5364577Seric 5374577Seric p = index(buf, '\t'); 5384577Seric if (p == NULL) 5394577Seric p = buf; 5404577Seric else 5414577Seric p++; 5424577Seric fixcrlf(p, TRUE); 5434577Seric message("214-", p); 5444577Seric noinfo = FALSE; 5454577Seric } 5464577Seric } 5474577Seric 5484577Seric if (noinfo) 5494577Seric message("504", "HELP topic unknown"); 5504577Seric else 5514577Seric message("214", "End of HELP info"); 5524628Seric (void) fclose(hf); 5534577Seric } 5548544Seric /* 5558544Seric ** ISWIZ -- tell us if we are a wizard 5568544Seric ** 5578544Seric ** If not, print a nasty message. 5588544Seric ** 5598544Seric ** Parameters: 5608544Seric ** none. 5618544Seric ** 5628544Seric ** Returns: 5638544Seric ** TRUE if we are a wizard. 5648544Seric ** FALSE if we are not a wizard. 5658544Seric ** 5668544Seric ** Side Effects: 5678544Seric ** Prints a 500 exit stat if we are not a wizard. 5688544Seric */ 5695181Seric 5708544Seric bool 5718544Seric iswiz() 5728544Seric { 5738544Seric if (!IsWiz) 5748544Seric message("500", "Mere mortals musn't mutter that mantra"); 5758544Seric return (IsWiz); 5768544Seric } 5779339Seric /* 5789339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5799339Seric ** 5809339Seric ** Parameters: 5819339Seric ** label -- a string used in error messages 5829339Seric ** 5839339Seric ** Returns: 5849339Seric ** zero in the child 5859339Seric ** one in the parent 5869339Seric ** 5879339Seric ** Side Effects: 5889339Seric ** none. 5899339Seric */ 5908544Seric 5919339Seric runinchild(label) 5929339Seric char *label; 5939339Seric { 5949339Seric int childpid; 5959339Seric 5969378Seric if (OneXact) 5979378Seric return (0); 5989378Seric 5999339Seric childpid = dofork(); 6009339Seric if (childpid < 0) 6019339Seric { 6029339Seric syserr("%s: cannot fork", label); 6039339Seric return (1); 6049339Seric } 6059339Seric if (childpid > 0) 6069339Seric { 6079339Seric auto int st; 6089339Seric 6099378Seric /* parent -- wait for child to complete */ 6109378Seric st = waitfor(childpid); 6119378Seric if (st == -1) 6129339Seric syserr("%s: lost child", label); 6139339Seric 6149339Seric /* if we exited on a QUIT command, complete the process */ 6159339Seric if (st == (EX_QUIT << 8)) 6169339Seric finis(); 6179339Seric 6189339Seric return (1); 6199339Seric } 6209339Seric else 6219339Seric { 6229339Seric /* child */ 6239339Seric InChild = TRUE; 6249545Seric clearenvelope(CurEnv); 6259339Seric return (0); 6269339Seric } 6279339Seric } 6289339Seric 6295181Seric # endif SMTP 630