1*22712Sdist /* 2*22712Sdist ** Sendmail 3*22712Sdist ** Copyright (c) 1983 Eric P. Allman 4*22712Sdist ** Berkeley, California 5*22712Sdist ** 6*22712Sdist ** Copyright (c) 1983 Regents of the University of California. 7*22712Sdist ** All rights reserved. The Berkeley software License Agreement 8*22712Sdist ** specifies the terms and conditions for redistribution. 9*22712Sdist */ 10*22712Sdist 11*22712Sdist #ifndef lint 12*22712Sdist static char SccsId[] = "@(#)srvrsmtp.c 5.1 (Berkeley) 06/07/85"; 13*22712Sdist #endif not lint 14*22712Sdist 159339Seric # include <errno.h> 164549Seric # include "sendmail.h" 1711728Seric # include <signal.h> 184549Seric 195181Seric # ifndef SMTP 20*22712Sdist SCCSID(@(#)srvrsmtp.c 5.1 06/07/85 (no SMTP)); 215181Seric # else SMTP 224556Seric 23*22712Sdist SCCSID(@(#)srvrsmtp.c 5.1 06/07/85); 245181Seric 254549Seric /* 264549Seric ** SMTP -- run the SMTP protocol. 274549Seric ** 284549Seric ** Parameters: 294549Seric ** none. 304549Seric ** 314549Seric ** Returns: 324549Seric ** never. 334549Seric ** 344549Seric ** Side Effects: 354549Seric ** Reads commands from the input channel and processes 364549Seric ** them. 374549Seric */ 384549Seric 394549Seric struct cmd 404549Seric { 414549Seric char *cmdname; /* command name */ 424549Seric int cmdcode; /* internal code, see below */ 434549Seric }; 444549Seric 454549Seric /* values for cmdcode */ 464549Seric # define CMDERROR 0 /* bad command */ 474549Seric # define CMDMAIL 1 /* mail -- designate sender */ 484976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 494549Seric # define CMDDATA 3 /* data -- send message text */ 509339Seric # define CMDRSET 4 /* rset -- reset state */ 519339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 529339Seric # define CMDHELP 6 /* help -- give usage info */ 539339Seric # define CMDNOOP 7 /* noop -- do nothing */ 549339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 559339Seric # define CMDHELO 9 /* helo -- be polite */ 569339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 579339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 589339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 599339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 609339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 619339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 624549Seric 634549Seric static struct cmd CmdTab[] = 644549Seric { 654549Seric "mail", CMDMAIL, 664976Seric "rcpt", CMDRCPT, 674549Seric "data", CMDDATA, 684549Seric "rset", CMDRSET, 694549Seric "vrfy", CMDVRFY, 707762Seric "expn", CMDVRFY, 714549Seric "help", CMDHELP, 724549Seric "noop", CMDNOOP, 734549Seric "quit", CMDQUIT, 744976Seric "helo", CMDHELO, 758544Seric "verb", CMDVERB, 769314Seric "onex", CMDONEX, 775003Seric # ifdef DEBUG 789339Seric "showq", CMDDBGQSHOW, 798544Seric "debug", CMDDBGDEBUG, 808544Seric "kill", CMDDBGKILL, 818544Seric "wiz", CMDDBGWIZ, 825003Seric # endif DEBUG 834549Seric NULL, CMDERROR, 844549Seric }; 854549Seric 868544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 8715596Seric char *WizWord; /* the wizard word to compare against */ 889339Seric bool InChild = FALSE; /* true if running in a subprocess */ 899378Seric bool OneXact = FALSE; /* one xaction only this run */ 9011146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 9111146Seric 929339Seric #define EX_QUIT 22 /* special code for QUIT command */ 938544Seric 944549Seric smtp() 954549Seric { 964549Seric register char *p; 978544Seric register struct cmd *c; 984549Seric char *cmd; 994549Seric extern char *skipword(); 1004549Seric extern bool sameword(); 1014549Seric bool hasmail; /* mail command received */ 1024713Seric int rcps; /* number of recipients */ 1035003Seric auto ADDRESS *vrfyqueue; 10412612Seric ADDRESS *a; 1058544Seric char inp[MAXLINE]; 1067124Seric extern char Version[]; 1077356Seric extern tick(); 1088544Seric extern bool iswiz(); 1099349Seric extern char *arpadate(); 11011151Seric extern char *macvalue(); 11112612Seric extern ADDRESS *recipient(); 1124549Seric 1135003Seric hasmail = FALSE; 1144713Seric rcps = 0; 1157363Seric if (OutChannel != stdout) 1167363Seric { 1177363Seric /* arrange for debugging output to go to remote host */ 1187363Seric (void) close(1); 1197363Seric (void) dup(fileno(OutChannel)); 1207363Seric } 12111931Seric settime(); 12216153Seric expand("\001e", inp, &inp[sizeof inp], CurEnv); 12310708Seric message("220", inp); 1244549Seric for (;;) 1254549Seric { 12612612Seric /* arrange for backout */ 12712612Seric if (setjmp(TopFrame) > 0 && InChild) 12812612Seric finis(); 12912612Seric QuickAbort = FALSE; 13012612Seric HoldErrs = FALSE; 13112612Seric 1327356Seric /* setup for the read */ 1336907Seric CurEnv->e_to = NULL; 1344577Seric Errors = 0; 1357275Seric (void) fflush(stdout); 1367356Seric 1377356Seric /* read the input line */ 1387685Seric p = sfgets(inp, sizeof inp, InChannel); 1397356Seric 1407685Seric /* handle errors */ 1417356Seric if (p == NULL) 1427356Seric { 1434549Seric /* end of file, just die */ 1444558Seric message("421", "%s Lost input channel", HostName); 1454549Seric finis(); 1464549Seric } 1474549Seric 1484549Seric /* clean up end of line */ 1494558Seric fixcrlf(inp, TRUE); 1504549Seric 1514713Seric /* echo command to transcript */ 1529545Seric if (CurEnv->e_xfp != NULL) 1539545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1544713Seric 1554549Seric /* break off command */ 1564549Seric for (p = inp; isspace(*p); p++) 1574549Seric continue; 1584549Seric cmd = p; 1594549Seric while (*++p != '\0' && !isspace(*p)) 1604549Seric continue; 1614549Seric if (*p != '\0') 1624549Seric *p++ = '\0'; 1634549Seric 1644549Seric /* decode command */ 1654549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1664549Seric { 1674549Seric if (sameword(c->cmdname, cmd)) 1684549Seric break; 1694549Seric } 1704549Seric 1714549Seric /* process command */ 1724549Seric switch (c->cmdcode) 1734549Seric { 1744976Seric case CMDHELO: /* hello -- introduce yourself */ 17514877Seric if (sameword(p, HostName)) 17614877Seric { 17714877Seric /* connected to an echo server */ 17814877Seric message("553", "%s I refuse to talk to myself", 17914877Seric HostName); 18014877Seric break; 18114877Seric } 18211146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 18311146Seric { 18411146Seric char buf[MAXNAME]; 18511146Seric 18611146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 18711146Seric define('s', newstr(buf), CurEnv); 18811146Seric } 18911146Seric else 19011146Seric define('s', newstr(p), CurEnv); 1914997Seric message("250", "%s Hello %s, pleased to meet you", 1924997Seric HostName, p); 1934976Seric break; 1944976Seric 1954549Seric case CMDMAIL: /* mail -- designate sender */ 19611151Seric /* force a sending host even if no HELO given */ 19711151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 19811151Seric define('s', RealHostName, CurEnv); 19911151Seric 2009314Seric /* check for validity of this command */ 2014558Seric if (hasmail) 2024558Seric { 2034558Seric message("503", "Sender already specified"); 2044558Seric break; 2054558Seric } 2069339Seric if (InChild) 2079339Seric { 2089339Seric syserr("Nested MAIL command"); 2099339Seric exit(0); 2109339Seric } 2119339Seric 2129339Seric /* fork a subprocess to process this command */ 2139339Seric if (runinchild("SMTP-MAIL") > 0) 2149339Seric break; 2159339Seric initsys(); 2169339Seric 2179339Seric /* child -- go do the processing */ 2184549Seric p = skipword(p, "from"); 2194549Seric if (p == NULL) 2204549Seric break; 2214549Seric setsender(p); 2224577Seric if (Errors == 0) 2234549Seric { 2244549Seric message("250", "Sender ok"); 2254549Seric hasmail = TRUE; 2264549Seric } 2279339Seric else if (InChild) 2289339Seric finis(); 2294549Seric break; 2304549Seric 2314976Seric case CMDRCPT: /* rcpt -- designate recipient */ 23212612Seric if (setjmp(TopFrame) > 0) 23314785Seric { 23414785Seric CurEnv->e_flags &= ~EF_FATALERRS; 23512612Seric break; 23614785Seric } 23712612Seric QuickAbort = TRUE; 2384549Seric p = skipword(p, "to"); 2394549Seric if (p == NULL) 2404549Seric break; 24116140Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0'); 24212612Seric if (a == NULL) 24312612Seric break; 24416886Seric a->q_flags |= QPRIMARY; 24512612Seric a = recipient(a, &CurEnv->e_sendqueue); 24612612Seric if (Errors != 0) 24712612Seric break; 24812612Seric 24912612Seric /* no errors during parsing, but might be a duplicate */ 25012612Seric CurEnv->e_to = p; 25112612Seric if (!bitset(QBADADDR, a->q_flags)) 25212612Seric message("250", "Recipient ok"); 25312612Seric else 2544549Seric { 25512612Seric /* punt -- should keep message in ADDRESS.... */ 25612612Seric message("550", "Addressee unknown"); 2574549Seric } 25812612Seric CurEnv->e_to = NULL; 25912612Seric rcps++; 2604549Seric break; 2614549Seric 2624549Seric case CMDDATA: /* data -- text of mail */ 2634976Seric if (!hasmail) 2644549Seric { 2654976Seric message("503", "Need MAIL command"); 2664976Seric break; 2674549Seric } 2684713Seric else if (rcps <= 0) 2694549Seric { 2704976Seric message("503", "Need RCPT (recipient)"); 2714976Seric break; 2724549Seric } 2734976Seric 2744976Seric /* collect the text of the message */ 2754976Seric collect(TRUE); 2764976Seric if (Errors != 0) 2774976Seric break; 2784976Seric 2798238Seric /* 2808238Seric ** Arrange to send to everyone. 2818238Seric ** If sending to multiple people, mail back 2828238Seric ** errors rather than reporting directly. 2838238Seric ** In any case, don't mail back errors for 2848238Seric ** anything that has happened up to 2858238Seric ** now (the other end will do this). 28610197Seric ** Truncate our transcript -- the mail has gotten 28710197Seric ** to us successfully, and if we have 28810197Seric ** to mail this back, it will be easier 28910197Seric ** on the reader. 2908238Seric ** Then send to everyone. 2918238Seric ** Finally give a reply code. If an error has 2928238Seric ** already been given, don't mail a 2938238Seric ** message back. 2949339Seric ** We goose error returns by clearing error bit. 2958238Seric */ 2968238Seric 2974976Seric if (rcps != 1) 2989378Seric { 2999378Seric HoldErrs = TRUE; 30016886Seric ErrorMode = EM_MAIL; 3019378Seric } 3029339Seric CurEnv->e_flags &= ~EF_FATALERRS; 30310197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 3044976Seric 3054976Seric /* send to all recipients */ 30614877Seric sendall(CurEnv, SM_DEFAULT); 3076907Seric CurEnv->e_to = NULL; 3084976Seric 3098238Seric /* issue success if appropriate and reset */ 3108238Seric if (Errors == 0 || HoldErrs) 3119283Seric message("250", "Ok"); 3128238Seric else 3139339Seric CurEnv->e_flags &= ~EF_FATALERRS; 3149339Seric 3159339Seric /* if in a child, pop back to our parent */ 3169339Seric if (InChild) 3179339Seric finis(); 3184549Seric break; 3194549Seric 3204549Seric case CMDRSET: /* rset -- reset state */ 3214549Seric message("250", "Reset state"); 3229339Seric if (InChild) 3239339Seric finis(); 3249339Seric break; 3254549Seric 3264549Seric case CMDVRFY: /* vrfy -- verify address */ 3279339Seric if (runinchild("SMTP-VRFY") > 0) 3289339Seric break; 3295003Seric vrfyqueue = NULL; 3307762Seric QuickAbort = TRUE; 3319619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3327762Seric if (Errors != 0) 3339339Seric { 3349339Seric if (InChild) 3359339Seric finis(); 3367762Seric break; 3379339Seric } 3385003Seric while (vrfyqueue != NULL) 3395003Seric { 3405003Seric register ADDRESS *a = vrfyqueue->q_next; 3415003Seric char *code; 3425003Seric 3437685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3445003Seric a = a->q_next; 3455003Seric 3467685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3475003Seric { 3485003Seric if (a != NULL) 3495003Seric code = "250-"; 3505003Seric else 3515003Seric code = "250"; 3525003Seric if (vrfyqueue->q_fullname == NULL) 3535003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3545003Seric else 3555003Seric message(code, "%s <%s>", 3565003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3575003Seric } 3585003Seric else if (a == NULL) 3595003Seric message("554", "Self destructive alias loop"); 3605003Seric vrfyqueue = a; 3615003Seric } 3629339Seric if (InChild) 3639339Seric finis(); 3644549Seric break; 3654549Seric 3664549Seric case CMDHELP: /* help -- give user info */ 3674577Seric if (*p == '\0') 3684577Seric p = "SMTP"; 3694577Seric help(p); 3704549Seric break; 3714549Seric 3724549Seric case CMDNOOP: /* noop -- do nothing */ 3734549Seric message("200", "OK"); 3744549Seric break; 3754549Seric 3764549Seric case CMDQUIT: /* quit -- leave mail */ 3774549Seric message("221", "%s closing connection", HostName); 3789339Seric if (InChild) 3799339Seric ExitStat = EX_QUIT; 3804549Seric finis(); 3814549Seric 3828544Seric case CMDVERB: /* set verbose mode */ 3838544Seric Verbose = TRUE; 3848544Seric message("200", "Verbose mode"); 3858544Seric break; 3868544Seric 3879314Seric case CMDONEX: /* doing one transaction only */ 3889378Seric OneXact = TRUE; 3899314Seric message("200", "Only one transaction"); 3909314Seric break; 3919314Seric 3925003Seric # ifdef DEBUG 3939339Seric case CMDDBGQSHOW: /* show queues */ 3946907Seric printf("Send Queue="); 3956907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3965003Seric break; 3977275Seric 3987275Seric case CMDDBGDEBUG: /* set debug mode */ 3997676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4007676Seric tTflag(p); 4017676Seric message("200", "Debug set"); 4027275Seric break; 4037275Seric 4047282Seric case CMDDBGKILL: /* kill the parent */ 4058544Seric if (!iswiz()) 4068544Seric break; 4077282Seric if (kill(MotherPid, SIGTERM) >= 0) 4087282Seric message("200", "Mother is dead"); 4097282Seric else 4107282Seric message("500", "Can't kill Mom"); 4117282Seric break; 4128544Seric 4138544Seric case CMDDBGWIZ: /* become a wizard */ 4148544Seric if (WizWord != NULL) 4158544Seric { 4168544Seric char seed[3]; 4178544Seric extern char *crypt(); 4188544Seric 4198544Seric strncpy(seed, WizWord, 2); 42015596Seric if (strcmp(WizWord, crypt(p, seed)) == 0) 4218544Seric { 42215596Seric IsWiz = TRUE; 42315596Seric message("200", "Please pass, oh mighty wizard"); 4248544Seric break; 4258544Seric } 4268544Seric } 42715596Seric message("500", "You are no wizard!"); 4288544Seric break; 4295003Seric # endif DEBUG 4305003Seric 4314549Seric case CMDERROR: /* unknown command */ 4324549Seric message("500", "Command unrecognized"); 4334549Seric break; 4344549Seric 4354549Seric default: 4364549Seric syserr("smtp: unknown code %d", c->cmdcode); 4374549Seric break; 4384549Seric } 4394549Seric } 4404549Seric } 4414549Seric /* 4424549Seric ** SKIPWORD -- skip a fixed word. 4434549Seric ** 4444549Seric ** Parameters: 4454549Seric ** p -- place to start looking. 4464549Seric ** w -- word to skip. 4474549Seric ** 4484549Seric ** Returns: 4494549Seric ** p following w. 4504549Seric ** NULL on error. 4514549Seric ** 4524549Seric ** Side Effects: 4534549Seric ** clobbers the p data area. 4544549Seric */ 4554549Seric 4564549Seric static char * 4574549Seric skipword(p, w) 4584549Seric register char *p; 4594549Seric char *w; 4604549Seric { 4614549Seric register char *q; 4624549Seric extern bool sameword(); 4634549Seric 4644549Seric /* find beginning of word */ 4654549Seric while (isspace(*p)) 4664549Seric p++; 4674549Seric q = p; 4684549Seric 4694549Seric /* find end of word */ 4704549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4714549Seric p++; 4724549Seric while (isspace(*p)) 4734549Seric *p++ = '\0'; 4744549Seric if (*p != ':') 4754549Seric { 4764549Seric syntax: 4774549Seric message("501", "Syntax error"); 4784549Seric Errors++; 4794549Seric return (NULL); 4804549Seric } 4814549Seric *p++ = '\0'; 4824549Seric while (isspace(*p)) 4834549Seric p++; 4844549Seric 4854549Seric /* see if the input word matches desired word */ 4864549Seric if (!sameword(q, w)) 4874549Seric goto syntax; 4884549Seric 4894549Seric return (p); 4904549Seric } 4914577Seric /* 4924577Seric ** HELP -- implement the HELP command. 4934577Seric ** 4944577Seric ** Parameters: 4954577Seric ** topic -- the topic we want help for. 4964577Seric ** 4974577Seric ** Returns: 4984577Seric ** none. 4994577Seric ** 5004577Seric ** Side Effects: 5014577Seric ** outputs the help file to message output. 5024577Seric */ 5034577Seric 5044577Seric help(topic) 5054577Seric char *topic; 5064577Seric { 5074577Seric register FILE *hf; 5084577Seric int len; 5094577Seric char buf[MAXLINE]; 5104577Seric bool noinfo; 5114577Seric 5128269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5134577Seric { 5144577Seric /* no help */ 51511931Seric errno = 0; 5164577Seric message("502", "HELP not implemented"); 5174577Seric return; 5184577Seric } 5194577Seric 5204577Seric len = strlen(topic); 5214577Seric makelower(topic); 5224577Seric noinfo = TRUE; 5234577Seric 5244577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5254577Seric { 5264577Seric if (strncmp(buf, topic, len) == 0) 5274577Seric { 5284577Seric register char *p; 5294577Seric 5304577Seric p = index(buf, '\t'); 5314577Seric if (p == NULL) 5324577Seric p = buf; 5334577Seric else 5344577Seric p++; 5354577Seric fixcrlf(p, TRUE); 5364577Seric message("214-", p); 5374577Seric noinfo = FALSE; 5384577Seric } 5394577Seric } 5404577Seric 5414577Seric if (noinfo) 5424577Seric message("504", "HELP topic unknown"); 5434577Seric else 5444577Seric message("214", "End of HELP info"); 5454628Seric (void) fclose(hf); 5464577Seric } 5478544Seric /* 5488544Seric ** ISWIZ -- tell us if we are a wizard 5498544Seric ** 5508544Seric ** If not, print a nasty message. 5518544Seric ** 5528544Seric ** Parameters: 5538544Seric ** none. 5548544Seric ** 5558544Seric ** Returns: 5568544Seric ** TRUE if we are a wizard. 5578544Seric ** FALSE if we are not a wizard. 5588544Seric ** 5598544Seric ** Side Effects: 5608544Seric ** Prints a 500 exit stat if we are not a wizard. 5618544Seric */ 5625181Seric 56319038Seric #ifdef DEBUG 56419038Seric 5658544Seric bool 5668544Seric iswiz() 5678544Seric { 5688544Seric if (!IsWiz) 5698544Seric message("500", "Mere mortals musn't mutter that mantra"); 5708544Seric return (IsWiz); 5718544Seric } 57219038Seric 57319038Seric #endif DEBUG 5749339Seric /* 5759339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5769339Seric ** 5779339Seric ** Parameters: 5789339Seric ** label -- a string used in error messages 5799339Seric ** 5809339Seric ** Returns: 5819339Seric ** zero in the child 5829339Seric ** one in the parent 5839339Seric ** 5849339Seric ** Side Effects: 5859339Seric ** none. 5869339Seric */ 5878544Seric 5889339Seric runinchild(label) 5899339Seric char *label; 5909339Seric { 5919339Seric int childpid; 5929339Seric 59316158Seric if (!OneXact) 5949339Seric { 59516158Seric childpid = dofork(); 59616158Seric if (childpid < 0) 59716158Seric { 59816158Seric syserr("%s: cannot fork", label); 59916158Seric return (1); 60016158Seric } 60116158Seric if (childpid > 0) 60216158Seric { 60316158Seric auto int st; 6049339Seric 60516158Seric /* parent -- wait for child to complete */ 60616158Seric st = waitfor(childpid); 60716158Seric if (st == -1) 60816158Seric syserr("%s: lost child", label); 6099339Seric 61016158Seric /* if we exited on a QUIT command, complete the process */ 61116158Seric if (st == (EX_QUIT << 8)) 61216158Seric finis(); 6139339Seric 61416158Seric return (1); 61516158Seric } 61616158Seric else 61716158Seric { 61816158Seric /* child */ 61916158Seric InChild = TRUE; 62016158Seric } 6219339Seric } 62215256Seric 62316158Seric /* child (or ONEX command specified) */ 62416158Seric clearenvelope(CurEnv); 62515256Seric 62616158Seric /* open alias database */ 62716158Seric initaliases(AliasFile, FALSE); 62816158Seric 62916158Seric return (0); 6309339Seric } 6319339Seric 6325181Seric # endif SMTP 633