122712Sdist /* 222712Sdist ** Sendmail 322712Sdist ** Copyright (c) 1983 Eric P. Allman 422712Sdist ** Berkeley, California 522712Sdist ** 622712Sdist ** Copyright (c) 1983 Regents of the University of California. 722712Sdist ** All rights reserved. The Berkeley software License Agreement 822712Sdist ** specifies the terms and conditions for redistribution. 922712Sdist */ 1022712Sdist 1122712Sdist 129339Seric # include <errno.h> 134549Seric # include "sendmail.h" 1411728Seric # include <signal.h> 154549Seric 165181Seric # ifndef SMTP 17*23122Seric # ifndef lint 18*23122Seric static char SccsId[] = "@(#)srvrsmtp.c 5.4 (Berkeley) 06/08/85 (no SMTP)"; 19*23122Seric # endif not lint 205181Seric # else SMTP 214556Seric 22*23122Seric # ifndef lint 23*23122Seric static char SccsId[] = "@(#)srvrsmtp.c 5.4 (Berkeley) 06/08/85"; 24*23122Seric # endif not lint 255181Seric 264549Seric /* 274549Seric ** SMTP -- run the SMTP protocol. 284549Seric ** 294549Seric ** Parameters: 304549Seric ** none. 314549Seric ** 324549Seric ** Returns: 334549Seric ** never. 344549Seric ** 354549Seric ** Side Effects: 364549Seric ** Reads commands from the input channel and processes 374549Seric ** them. 384549Seric */ 394549Seric 404549Seric struct cmd 414549Seric { 424549Seric char *cmdname; /* command name */ 434549Seric int cmdcode; /* internal code, see below */ 444549Seric }; 454549Seric 464549Seric /* values for cmdcode */ 474549Seric # define CMDERROR 0 /* bad command */ 484549Seric # define CMDMAIL 1 /* mail -- designate sender */ 494976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 504549Seric # define CMDDATA 3 /* data -- send message text */ 519339Seric # define CMDRSET 4 /* rset -- reset state */ 529339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 539339Seric # define CMDHELP 6 /* help -- give usage info */ 549339Seric # define CMDNOOP 7 /* noop -- do nothing */ 559339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 569339Seric # define CMDHELO 9 /* helo -- be polite */ 579339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 589339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 599339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 609339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 619339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 629339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 634549Seric 644549Seric static struct cmd CmdTab[] = 654549Seric { 664549Seric "mail", CMDMAIL, 674976Seric "rcpt", CMDRCPT, 684549Seric "data", CMDDATA, 694549Seric "rset", CMDRSET, 704549Seric "vrfy", CMDVRFY, 717762Seric "expn", CMDVRFY, 724549Seric "help", CMDHELP, 734549Seric "noop", CMDNOOP, 744549Seric "quit", CMDQUIT, 754976Seric "helo", CMDHELO, 768544Seric "verb", CMDVERB, 779314Seric "onex", CMDONEX, 785003Seric # ifdef DEBUG 799339Seric "showq", CMDDBGQSHOW, 808544Seric "debug", CMDDBGDEBUG, 818544Seric "kill", CMDDBGKILL, 828544Seric "wiz", CMDDBGWIZ, 835003Seric # endif DEBUG 844549Seric NULL, CMDERROR, 854549Seric }; 864549Seric 878544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 8815596Seric char *WizWord; /* the wizard word to compare against */ 899339Seric bool InChild = FALSE; /* true if running in a subprocess */ 909378Seric bool OneXact = FALSE; /* one xaction only this run */ 9111146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 9211146Seric 939339Seric #define EX_QUIT 22 /* special code for QUIT command */ 948544Seric 954549Seric smtp() 964549Seric { 974549Seric register char *p; 988544Seric register struct cmd *c; 994549Seric char *cmd; 1004549Seric extern char *skipword(); 1014549Seric extern bool sameword(); 1024549Seric bool hasmail; /* mail command received */ 1034713Seric int rcps; /* number of recipients */ 1045003Seric auto ADDRESS *vrfyqueue; 10512612Seric ADDRESS *a; 1068544Seric char inp[MAXLINE]; 1077124Seric extern char Version[]; 1087356Seric extern tick(); 1098544Seric extern bool iswiz(); 1109349Seric extern char *arpadate(); 11111151Seric extern char *macvalue(); 11212612Seric extern ADDRESS *recipient(); 1134549Seric 1145003Seric hasmail = FALSE; 1154713Seric rcps = 0; 1167363Seric if (OutChannel != stdout) 1177363Seric { 1187363Seric /* arrange for debugging output to go to remote host */ 1197363Seric (void) close(1); 1207363Seric (void) dup(fileno(OutChannel)); 1217363Seric } 12211931Seric settime(); 12316153Seric expand("\001e", inp, &inp[sizeof inp], CurEnv); 12410708Seric message("220", inp); 1254549Seric for (;;) 1264549Seric { 12712612Seric /* arrange for backout */ 12812612Seric if (setjmp(TopFrame) > 0 && InChild) 12912612Seric finis(); 13012612Seric QuickAbort = FALSE; 13112612Seric HoldErrs = FALSE; 13212612Seric 1337356Seric /* setup for the read */ 1346907Seric CurEnv->e_to = NULL; 1354577Seric Errors = 0; 1367275Seric (void) fflush(stdout); 1377356Seric 1387356Seric /* read the input line */ 1397685Seric p = sfgets(inp, sizeof inp, InChannel); 1407356Seric 1417685Seric /* handle errors */ 1427356Seric if (p == NULL) 1437356Seric { 1444549Seric /* end of file, just die */ 1454558Seric message("421", "%s Lost input channel", HostName); 1464549Seric finis(); 1474549Seric } 1484549Seric 1494549Seric /* clean up end of line */ 1504558Seric fixcrlf(inp, TRUE); 1514549Seric 1524713Seric /* echo command to transcript */ 1539545Seric if (CurEnv->e_xfp != NULL) 1549545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1554713Seric 1564549Seric /* break off command */ 1574549Seric for (p = inp; isspace(*p); p++) 1584549Seric continue; 1594549Seric cmd = p; 1604549Seric while (*++p != '\0' && !isspace(*p)) 1614549Seric continue; 1624549Seric if (*p != '\0') 1634549Seric *p++ = '\0'; 1644549Seric 1654549Seric /* decode command */ 1664549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1674549Seric { 1684549Seric if (sameword(c->cmdname, cmd)) 1694549Seric break; 1704549Seric } 1714549Seric 1724549Seric /* process command */ 1734549Seric switch (c->cmdcode) 1744549Seric { 1754976Seric case CMDHELO: /* hello -- introduce yourself */ 17614877Seric if (sameword(p, HostName)) 17714877Seric { 17814877Seric /* connected to an echo server */ 17914877Seric message("553", "%s I refuse to talk to myself", 18014877Seric HostName); 18114877Seric break; 18214877Seric } 18311146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 18411146Seric { 18511146Seric char buf[MAXNAME]; 18611146Seric 18711146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 18811146Seric define('s', newstr(buf), CurEnv); 18911146Seric } 19011146Seric else 19111146Seric define('s', newstr(p), CurEnv); 1924997Seric message("250", "%s Hello %s, pleased to meet you", 1934997Seric HostName, p); 1944976Seric break; 1954976Seric 1964549Seric case CMDMAIL: /* mail -- designate sender */ 19711151Seric /* force a sending host even if no HELO given */ 19811151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 19911151Seric define('s', RealHostName, CurEnv); 20011151Seric 2019314Seric /* check for validity of this command */ 2024558Seric if (hasmail) 2034558Seric { 2044558Seric message("503", "Sender already specified"); 2054558Seric break; 2064558Seric } 2079339Seric if (InChild) 2089339Seric { 2099339Seric syserr("Nested MAIL command"); 2109339Seric exit(0); 2119339Seric } 2129339Seric 2139339Seric /* fork a subprocess to process this command */ 2149339Seric if (runinchild("SMTP-MAIL") > 0) 2159339Seric break; 2169339Seric initsys(); 2179339Seric 2189339Seric /* child -- go do the processing */ 2194549Seric p = skipword(p, "from"); 2204549Seric if (p == NULL) 2214549Seric break; 2224549Seric setsender(p); 2234577Seric if (Errors == 0) 2244549Seric { 2254549Seric message("250", "Sender ok"); 2264549Seric hasmail = TRUE; 2274549Seric } 2289339Seric else if (InChild) 2299339Seric finis(); 2304549Seric break; 2314549Seric 2324976Seric case CMDRCPT: /* rcpt -- designate recipient */ 23312612Seric if (setjmp(TopFrame) > 0) 23414785Seric { 23514785Seric CurEnv->e_flags &= ~EF_FATALERRS; 23612612Seric break; 23714785Seric } 23812612Seric QuickAbort = TRUE; 2394549Seric p = skipword(p, "to"); 2404549Seric if (p == NULL) 2414549Seric break; 24216140Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0'); 24312612Seric if (a == NULL) 24412612Seric break; 24516886Seric a->q_flags |= QPRIMARY; 24612612Seric a = recipient(a, &CurEnv->e_sendqueue); 24712612Seric if (Errors != 0) 24812612Seric break; 24912612Seric 25012612Seric /* no errors during parsing, but might be a duplicate */ 25112612Seric CurEnv->e_to = p; 25212612Seric if (!bitset(QBADADDR, a->q_flags)) 25312612Seric message("250", "Recipient ok"); 25412612Seric else 2554549Seric { 25612612Seric /* punt -- should keep message in ADDRESS.... */ 25712612Seric message("550", "Addressee unknown"); 2584549Seric } 25912612Seric CurEnv->e_to = NULL; 26012612Seric rcps++; 2614549Seric break; 2624549Seric 2634549Seric case CMDDATA: /* data -- text of mail */ 2644976Seric if (!hasmail) 2654549Seric { 2664976Seric message("503", "Need MAIL command"); 2674976Seric break; 2684549Seric } 2694713Seric else if (rcps <= 0) 2704549Seric { 2714976Seric message("503", "Need RCPT (recipient)"); 2724976Seric break; 2734549Seric } 2744976Seric 2754976Seric /* collect the text of the message */ 2764976Seric collect(TRUE); 2774976Seric if (Errors != 0) 2784976Seric break; 2794976Seric 2808238Seric /* 2818238Seric ** Arrange to send to everyone. 2828238Seric ** If sending to multiple people, mail back 2838238Seric ** errors rather than reporting directly. 2848238Seric ** In any case, don't mail back errors for 2858238Seric ** anything that has happened up to 2868238Seric ** now (the other end will do this). 28710197Seric ** Truncate our transcript -- the mail has gotten 28810197Seric ** to us successfully, and if we have 28910197Seric ** to mail this back, it will be easier 29010197Seric ** on the reader. 2918238Seric ** Then send to everyone. 2928238Seric ** Finally give a reply code. If an error has 2938238Seric ** already been given, don't mail a 2948238Seric ** message back. 2959339Seric ** We goose error returns by clearing error bit. 2968238Seric */ 2978238Seric 2984976Seric if (rcps != 1) 2999378Seric { 3009378Seric HoldErrs = TRUE; 30116886Seric ErrorMode = EM_MAIL; 3029378Seric } 3039339Seric CurEnv->e_flags &= ~EF_FATALERRS; 30410197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 3054976Seric 3064976Seric /* send to all recipients */ 30714877Seric sendall(CurEnv, SM_DEFAULT); 3086907Seric CurEnv->e_to = NULL; 3094976Seric 3108238Seric /* issue success if appropriate and reset */ 3118238Seric if (Errors == 0 || HoldErrs) 3129283Seric message("250", "Ok"); 3138238Seric else 3149339Seric CurEnv->e_flags &= ~EF_FATALERRS; 3159339Seric 3169339Seric /* if in a child, pop back to our parent */ 3179339Seric if (InChild) 3189339Seric finis(); 3194549Seric break; 3204549Seric 3214549Seric case CMDRSET: /* rset -- reset state */ 3224549Seric message("250", "Reset state"); 3239339Seric if (InChild) 3249339Seric finis(); 3259339Seric break; 3264549Seric 3274549Seric case CMDVRFY: /* vrfy -- verify address */ 3289339Seric if (runinchild("SMTP-VRFY") > 0) 3299339Seric break; 3305003Seric vrfyqueue = NULL; 3317762Seric QuickAbort = TRUE; 3329619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3337762Seric if (Errors != 0) 3349339Seric { 3359339Seric if (InChild) 3369339Seric finis(); 3377762Seric break; 3389339Seric } 3395003Seric while (vrfyqueue != NULL) 3405003Seric { 3415003Seric register ADDRESS *a = vrfyqueue->q_next; 3425003Seric char *code; 3435003Seric 3447685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3455003Seric a = a->q_next; 3465003Seric 3477685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3485003Seric { 3495003Seric if (a != NULL) 3505003Seric code = "250-"; 3515003Seric else 3525003Seric code = "250"; 3535003Seric if (vrfyqueue->q_fullname == NULL) 3545003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3555003Seric else 3565003Seric message(code, "%s <%s>", 3575003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3585003Seric } 3595003Seric else if (a == NULL) 3605003Seric message("554", "Self destructive alias loop"); 3615003Seric vrfyqueue = a; 3625003Seric } 3639339Seric if (InChild) 3649339Seric finis(); 3654549Seric break; 3664549Seric 3674549Seric case CMDHELP: /* help -- give user info */ 3684577Seric if (*p == '\0') 3694577Seric p = "SMTP"; 3704577Seric help(p); 3714549Seric break; 3724549Seric 3734549Seric case CMDNOOP: /* noop -- do nothing */ 3744549Seric message("200", "OK"); 3754549Seric break; 3764549Seric 3774549Seric case CMDQUIT: /* quit -- leave mail */ 3784549Seric message("221", "%s closing connection", HostName); 3799339Seric if (InChild) 3809339Seric ExitStat = EX_QUIT; 3814549Seric finis(); 3824549Seric 3838544Seric case CMDVERB: /* set verbose mode */ 3848544Seric Verbose = TRUE; 3858544Seric message("200", "Verbose mode"); 3868544Seric break; 3878544Seric 3889314Seric case CMDONEX: /* doing one transaction only */ 3899378Seric OneXact = TRUE; 3909314Seric message("200", "Only one transaction"); 3919314Seric break; 3929314Seric 3935003Seric # ifdef DEBUG 3949339Seric case CMDDBGQSHOW: /* show queues */ 3956907Seric printf("Send Queue="); 3966907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3975003Seric break; 3987275Seric 3997275Seric case CMDDBGDEBUG: /* set debug mode */ 4007676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4017676Seric tTflag(p); 4027676Seric message("200", "Debug set"); 4037275Seric break; 4047275Seric 4057282Seric case CMDDBGKILL: /* kill the parent */ 4068544Seric if (!iswiz()) 4078544Seric break; 4087282Seric if (kill(MotherPid, SIGTERM) >= 0) 4097282Seric message("200", "Mother is dead"); 4107282Seric else 4117282Seric message("500", "Can't kill Mom"); 4127282Seric break; 4138544Seric 4148544Seric case CMDDBGWIZ: /* become a wizard */ 4158544Seric if (WizWord != NULL) 4168544Seric { 4178544Seric char seed[3]; 4188544Seric extern char *crypt(); 4198544Seric 42023106Seric (void) strncpy(seed, WizWord, 2); 42115596Seric if (strcmp(WizWord, crypt(p, seed)) == 0) 4228544Seric { 42315596Seric IsWiz = TRUE; 42415596Seric message("200", "Please pass, oh mighty wizard"); 4258544Seric break; 4268544Seric } 4278544Seric } 42815596Seric message("500", "You are no wizard!"); 4298544Seric break; 4305003Seric # endif DEBUG 4315003Seric 4324549Seric case CMDERROR: /* unknown command */ 4334549Seric message("500", "Command unrecognized"); 4344549Seric break; 4354549Seric 4364549Seric default: 4374549Seric syserr("smtp: unknown code %d", c->cmdcode); 4384549Seric break; 4394549Seric } 4404549Seric } 4414549Seric } 4424549Seric /* 4434549Seric ** SKIPWORD -- skip a fixed word. 4444549Seric ** 4454549Seric ** Parameters: 4464549Seric ** p -- place to start looking. 4474549Seric ** w -- word to skip. 4484549Seric ** 4494549Seric ** Returns: 4504549Seric ** p following w. 4514549Seric ** NULL on error. 4524549Seric ** 4534549Seric ** Side Effects: 4544549Seric ** clobbers the p data area. 4554549Seric */ 4564549Seric 4574549Seric static char * 4584549Seric skipword(p, w) 4594549Seric register char *p; 4604549Seric char *w; 4614549Seric { 4624549Seric register char *q; 4634549Seric extern bool sameword(); 4644549Seric 4654549Seric /* find beginning of word */ 4664549Seric while (isspace(*p)) 4674549Seric p++; 4684549Seric q = p; 4694549Seric 4704549Seric /* find end of word */ 4714549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4724549Seric p++; 4734549Seric while (isspace(*p)) 4744549Seric *p++ = '\0'; 4754549Seric if (*p != ':') 4764549Seric { 4774549Seric syntax: 4784549Seric message("501", "Syntax error"); 4794549Seric Errors++; 4804549Seric return (NULL); 4814549Seric } 4824549Seric *p++ = '\0'; 4834549Seric while (isspace(*p)) 4844549Seric p++; 4854549Seric 4864549Seric /* see if the input word matches desired word */ 4874549Seric if (!sameword(q, w)) 4884549Seric goto syntax; 4894549Seric 4904549Seric return (p); 4914549Seric } 4924577Seric /* 4934577Seric ** HELP -- implement the HELP command. 4944577Seric ** 4954577Seric ** Parameters: 4964577Seric ** topic -- the topic we want help for. 4974577Seric ** 4984577Seric ** Returns: 4994577Seric ** none. 5004577Seric ** 5014577Seric ** Side Effects: 5024577Seric ** outputs the help file to message output. 5034577Seric */ 5044577Seric 5054577Seric help(topic) 5064577Seric char *topic; 5074577Seric { 5084577Seric register FILE *hf; 5094577Seric int len; 5104577Seric char buf[MAXLINE]; 5114577Seric bool noinfo; 5124577Seric 5138269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5144577Seric { 5154577Seric /* no help */ 51611931Seric errno = 0; 5174577Seric message("502", "HELP not implemented"); 5184577Seric return; 5194577Seric } 5204577Seric 5214577Seric len = strlen(topic); 5224577Seric makelower(topic); 5234577Seric noinfo = TRUE; 5244577Seric 5254577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5264577Seric { 5274577Seric if (strncmp(buf, topic, len) == 0) 5284577Seric { 5294577Seric register char *p; 5304577Seric 5314577Seric p = index(buf, '\t'); 5324577Seric if (p == NULL) 5334577Seric p = buf; 5344577Seric else 5354577Seric p++; 5364577Seric fixcrlf(p, TRUE); 5374577Seric message("214-", p); 5384577Seric noinfo = FALSE; 5394577Seric } 5404577Seric } 5414577Seric 5424577Seric if (noinfo) 5434577Seric message("504", "HELP topic unknown"); 5444577Seric else 5454577Seric message("214", "End of HELP info"); 5464628Seric (void) fclose(hf); 5474577Seric } 5488544Seric /* 5498544Seric ** ISWIZ -- tell us if we are a wizard 5508544Seric ** 5518544Seric ** If not, print a nasty message. 5528544Seric ** 5538544Seric ** Parameters: 5548544Seric ** none. 5558544Seric ** 5568544Seric ** Returns: 5578544Seric ** TRUE if we are a wizard. 5588544Seric ** FALSE if we are not a wizard. 5598544Seric ** 5608544Seric ** Side Effects: 5618544Seric ** Prints a 500 exit stat if we are not a wizard. 5628544Seric */ 5635181Seric 56419038Seric #ifdef DEBUG 56519038Seric 5668544Seric bool 5678544Seric iswiz() 5688544Seric { 5698544Seric if (!IsWiz) 5708544Seric message("500", "Mere mortals musn't mutter that mantra"); 5718544Seric return (IsWiz); 5728544Seric } 57319038Seric 57419038Seric #endif DEBUG 5759339Seric /* 5769339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5779339Seric ** 5789339Seric ** Parameters: 5799339Seric ** label -- a string used in error messages 5809339Seric ** 5819339Seric ** Returns: 5829339Seric ** zero in the child 5839339Seric ** one in the parent 5849339Seric ** 5859339Seric ** Side Effects: 5869339Seric ** none. 5879339Seric */ 5888544Seric 5899339Seric runinchild(label) 5909339Seric char *label; 5919339Seric { 5929339Seric int childpid; 5939339Seric 59416158Seric if (!OneXact) 5959339Seric { 59616158Seric childpid = dofork(); 59716158Seric if (childpid < 0) 59816158Seric { 59916158Seric syserr("%s: cannot fork", label); 60016158Seric return (1); 60116158Seric } 60216158Seric if (childpid > 0) 60316158Seric { 60416158Seric auto int st; 6059339Seric 60616158Seric /* parent -- wait for child to complete */ 60716158Seric st = waitfor(childpid); 60816158Seric if (st == -1) 60916158Seric syserr("%s: lost child", label); 6109339Seric 61116158Seric /* if we exited on a QUIT command, complete the process */ 61216158Seric if (st == (EX_QUIT << 8)) 61316158Seric finis(); 6149339Seric 61516158Seric return (1); 61616158Seric } 61716158Seric else 61816158Seric { 61916158Seric /* child */ 62016158Seric InChild = TRUE; 62116158Seric } 6229339Seric } 62315256Seric 62416158Seric /* child (or ONEX command specified) */ 62516158Seric clearenvelope(CurEnv); 62615256Seric 62716158Seric /* open alias database */ 62816158Seric initaliases(AliasFile, FALSE); 62916158Seric 63016158Seric return (0); 6319339Seric } 6329339Seric 6335181Seric # endif SMTP 634