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 1723122Seric # ifndef lint 18*23516Seric static char SccsId[] = "@(#)srvrsmtp.c 5.5 (Berkeley) 06/17/85 (no SMTP)"; 1923122Seric # endif not lint 205181Seric # else SMTP 214556Seric 2223122Seric # ifndef lint 23*23516Seric static char SccsId[] = "@(#)srvrsmtp.c 5.5 (Berkeley) 06/17/85"; 2423122Seric # 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 310*23516Seric /* save statistics */ 311*23516Seric markstats(CurEnv, (ADDRESS *) NULL); 312*23516Seric 3138238Seric /* issue success if appropriate and reset */ 3148238Seric if (Errors == 0 || HoldErrs) 3159283Seric message("250", "Ok"); 3168238Seric else 3179339Seric CurEnv->e_flags &= ~EF_FATALERRS; 3189339Seric 3199339Seric /* if in a child, pop back to our parent */ 3209339Seric if (InChild) 3219339Seric finis(); 3224549Seric break; 3234549Seric 3244549Seric case CMDRSET: /* rset -- reset state */ 3254549Seric message("250", "Reset state"); 3269339Seric if (InChild) 3279339Seric finis(); 3289339Seric break; 3294549Seric 3304549Seric case CMDVRFY: /* vrfy -- verify address */ 3319339Seric if (runinchild("SMTP-VRFY") > 0) 3329339Seric break; 3335003Seric vrfyqueue = NULL; 3347762Seric QuickAbort = TRUE; 3359619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3367762Seric if (Errors != 0) 3379339Seric { 3389339Seric if (InChild) 3399339Seric finis(); 3407762Seric break; 3419339Seric } 3425003Seric while (vrfyqueue != NULL) 3435003Seric { 3445003Seric register ADDRESS *a = vrfyqueue->q_next; 3455003Seric char *code; 3465003Seric 3477685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3485003Seric a = a->q_next; 3495003Seric 3507685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3515003Seric { 3525003Seric if (a != NULL) 3535003Seric code = "250-"; 3545003Seric else 3555003Seric code = "250"; 3565003Seric if (vrfyqueue->q_fullname == NULL) 3575003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3585003Seric else 3595003Seric message(code, "%s <%s>", 3605003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3615003Seric } 3625003Seric else if (a == NULL) 3635003Seric message("554", "Self destructive alias loop"); 3645003Seric vrfyqueue = a; 3655003Seric } 3669339Seric if (InChild) 3679339Seric finis(); 3684549Seric break; 3694549Seric 3704549Seric case CMDHELP: /* help -- give user info */ 3714577Seric if (*p == '\0') 3724577Seric p = "SMTP"; 3734577Seric help(p); 3744549Seric break; 3754549Seric 3764549Seric case CMDNOOP: /* noop -- do nothing */ 3774549Seric message("200", "OK"); 3784549Seric break; 3794549Seric 3804549Seric case CMDQUIT: /* quit -- leave mail */ 3814549Seric message("221", "%s closing connection", HostName); 3829339Seric if (InChild) 3839339Seric ExitStat = EX_QUIT; 3844549Seric finis(); 3854549Seric 3868544Seric case CMDVERB: /* set verbose mode */ 3878544Seric Verbose = TRUE; 3888544Seric message("200", "Verbose mode"); 3898544Seric break; 3908544Seric 3919314Seric case CMDONEX: /* doing one transaction only */ 3929378Seric OneXact = TRUE; 3939314Seric message("200", "Only one transaction"); 3949314Seric break; 3959314Seric 3965003Seric # ifdef DEBUG 3979339Seric case CMDDBGQSHOW: /* show queues */ 3986907Seric printf("Send Queue="); 3996907Seric printaddr(CurEnv->e_sendqueue, TRUE); 4005003Seric break; 4017275Seric 4027275Seric case CMDDBGDEBUG: /* set debug mode */ 4037676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4047676Seric tTflag(p); 4057676Seric message("200", "Debug set"); 4067275Seric break; 4077275Seric 4087282Seric case CMDDBGKILL: /* kill the parent */ 4098544Seric if (!iswiz()) 4108544Seric break; 4117282Seric if (kill(MotherPid, SIGTERM) >= 0) 4127282Seric message("200", "Mother is dead"); 4137282Seric else 4147282Seric message("500", "Can't kill Mom"); 4157282Seric break; 4168544Seric 4178544Seric case CMDDBGWIZ: /* become a wizard */ 4188544Seric if (WizWord != NULL) 4198544Seric { 4208544Seric char seed[3]; 4218544Seric extern char *crypt(); 4228544Seric 42323106Seric (void) strncpy(seed, WizWord, 2); 42415596Seric if (strcmp(WizWord, crypt(p, seed)) == 0) 4258544Seric { 42615596Seric IsWiz = TRUE; 42715596Seric message("200", "Please pass, oh mighty wizard"); 4288544Seric break; 4298544Seric } 4308544Seric } 43115596Seric message("500", "You are no wizard!"); 4328544Seric break; 4335003Seric # endif DEBUG 4345003Seric 4354549Seric case CMDERROR: /* unknown command */ 4364549Seric message("500", "Command unrecognized"); 4374549Seric break; 4384549Seric 4394549Seric default: 4404549Seric syserr("smtp: unknown code %d", c->cmdcode); 4414549Seric break; 4424549Seric } 4434549Seric } 4444549Seric } 4454549Seric /* 4464549Seric ** SKIPWORD -- skip a fixed word. 4474549Seric ** 4484549Seric ** Parameters: 4494549Seric ** p -- place to start looking. 4504549Seric ** w -- word to skip. 4514549Seric ** 4524549Seric ** Returns: 4534549Seric ** p following w. 4544549Seric ** NULL on error. 4554549Seric ** 4564549Seric ** Side Effects: 4574549Seric ** clobbers the p data area. 4584549Seric */ 4594549Seric 4604549Seric static char * 4614549Seric skipword(p, w) 4624549Seric register char *p; 4634549Seric char *w; 4644549Seric { 4654549Seric register char *q; 4664549Seric extern bool sameword(); 4674549Seric 4684549Seric /* find beginning of word */ 4694549Seric while (isspace(*p)) 4704549Seric p++; 4714549Seric q = p; 4724549Seric 4734549Seric /* find end of word */ 4744549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4754549Seric p++; 4764549Seric while (isspace(*p)) 4774549Seric *p++ = '\0'; 4784549Seric if (*p != ':') 4794549Seric { 4804549Seric syntax: 4814549Seric message("501", "Syntax error"); 4824549Seric Errors++; 4834549Seric return (NULL); 4844549Seric } 4854549Seric *p++ = '\0'; 4864549Seric while (isspace(*p)) 4874549Seric p++; 4884549Seric 4894549Seric /* see if the input word matches desired word */ 4904549Seric if (!sameword(q, w)) 4914549Seric goto syntax; 4924549Seric 4934549Seric return (p); 4944549Seric } 4954577Seric /* 4964577Seric ** HELP -- implement the HELP command. 4974577Seric ** 4984577Seric ** Parameters: 4994577Seric ** topic -- the topic we want help for. 5004577Seric ** 5014577Seric ** Returns: 5024577Seric ** none. 5034577Seric ** 5044577Seric ** Side Effects: 5054577Seric ** outputs the help file to message output. 5064577Seric */ 5074577Seric 5084577Seric help(topic) 5094577Seric char *topic; 5104577Seric { 5114577Seric register FILE *hf; 5124577Seric int len; 5134577Seric char buf[MAXLINE]; 5144577Seric bool noinfo; 5154577Seric 5168269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5174577Seric { 5184577Seric /* no help */ 51911931Seric errno = 0; 5204577Seric message("502", "HELP not implemented"); 5214577Seric return; 5224577Seric } 5234577Seric 5244577Seric len = strlen(topic); 5254577Seric makelower(topic); 5264577Seric noinfo = TRUE; 5274577Seric 5284577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5294577Seric { 5304577Seric if (strncmp(buf, topic, len) == 0) 5314577Seric { 5324577Seric register char *p; 5334577Seric 5344577Seric p = index(buf, '\t'); 5354577Seric if (p == NULL) 5364577Seric p = buf; 5374577Seric else 5384577Seric p++; 5394577Seric fixcrlf(p, TRUE); 5404577Seric message("214-", p); 5414577Seric noinfo = FALSE; 5424577Seric } 5434577Seric } 5444577Seric 5454577Seric if (noinfo) 5464577Seric message("504", "HELP topic unknown"); 5474577Seric else 5484577Seric message("214", "End of HELP info"); 5494628Seric (void) fclose(hf); 5504577Seric } 5518544Seric /* 5528544Seric ** ISWIZ -- tell us if we are a wizard 5538544Seric ** 5548544Seric ** If not, print a nasty message. 5558544Seric ** 5568544Seric ** Parameters: 5578544Seric ** none. 5588544Seric ** 5598544Seric ** Returns: 5608544Seric ** TRUE if we are a wizard. 5618544Seric ** FALSE if we are not a wizard. 5628544Seric ** 5638544Seric ** Side Effects: 5648544Seric ** Prints a 500 exit stat if we are not a wizard. 5658544Seric */ 5665181Seric 56719038Seric #ifdef DEBUG 56819038Seric 5698544Seric bool 5708544Seric iswiz() 5718544Seric { 5728544Seric if (!IsWiz) 5738544Seric message("500", "Mere mortals musn't mutter that mantra"); 5748544Seric return (IsWiz); 5758544Seric } 57619038Seric 57719038Seric #endif DEBUG 5789339Seric /* 5799339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5809339Seric ** 5819339Seric ** Parameters: 5829339Seric ** label -- a string used in error messages 5839339Seric ** 5849339Seric ** Returns: 5859339Seric ** zero in the child 5869339Seric ** one in the parent 5879339Seric ** 5889339Seric ** Side Effects: 5899339Seric ** none. 5909339Seric */ 5918544Seric 5929339Seric runinchild(label) 5939339Seric char *label; 5949339Seric { 5959339Seric int childpid; 5969339Seric 59716158Seric if (!OneXact) 5989339Seric { 59916158Seric childpid = dofork(); 60016158Seric if (childpid < 0) 60116158Seric { 60216158Seric syserr("%s: cannot fork", label); 60316158Seric return (1); 60416158Seric } 60516158Seric if (childpid > 0) 60616158Seric { 60716158Seric auto int st; 6089339Seric 60916158Seric /* parent -- wait for child to complete */ 61016158Seric st = waitfor(childpid); 61116158Seric if (st == -1) 61216158Seric syserr("%s: lost child", label); 6139339Seric 61416158Seric /* if we exited on a QUIT command, complete the process */ 61516158Seric if (st == (EX_QUIT << 8)) 61616158Seric finis(); 6179339Seric 61816158Seric return (1); 61916158Seric } 62016158Seric else 62116158Seric { 62216158Seric /* child */ 62316158Seric InChild = TRUE; 62416158Seric } 6259339Seric } 62615256Seric 62716158Seric /* child (or ONEX command specified) */ 62816158Seric clearenvelope(CurEnv); 62915256Seric 63016158Seric /* open alias database */ 63116158Seric initaliases(AliasFile, FALSE); 63216158Seric 63316158Seric return (0); 6349339Seric } 6359339Seric 6365181Seric # endif SMTP 637