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*23106Seric static char SccsId[] = "@(#)srvrsmtp.c 5.3 (Berkeley) 06/08/85 (no SMTP)"; 185181Seric # else SMTP 194556Seric 20*23106Seric static char SccsId[] = "@(#)srvrsmtp.c 5.3 (Berkeley) 06/08/85"; 215181Seric 224549Seric /* 234549Seric ** SMTP -- run the SMTP protocol. 244549Seric ** 254549Seric ** Parameters: 264549Seric ** none. 274549Seric ** 284549Seric ** Returns: 294549Seric ** never. 304549Seric ** 314549Seric ** Side Effects: 324549Seric ** Reads commands from the input channel and processes 334549Seric ** them. 344549Seric */ 354549Seric 364549Seric struct cmd 374549Seric { 384549Seric char *cmdname; /* command name */ 394549Seric int cmdcode; /* internal code, see below */ 404549Seric }; 414549Seric 424549Seric /* values for cmdcode */ 434549Seric # define CMDERROR 0 /* bad command */ 444549Seric # define CMDMAIL 1 /* mail -- designate sender */ 454976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 464549Seric # define CMDDATA 3 /* data -- send message text */ 479339Seric # define CMDRSET 4 /* rset -- reset state */ 489339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 499339Seric # define CMDHELP 6 /* help -- give usage info */ 509339Seric # define CMDNOOP 7 /* noop -- do nothing */ 519339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 529339Seric # define CMDHELO 9 /* helo -- be polite */ 539339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 549339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 559339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 569339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 579339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 589339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 594549Seric 604549Seric static struct cmd CmdTab[] = 614549Seric { 624549Seric "mail", CMDMAIL, 634976Seric "rcpt", CMDRCPT, 644549Seric "data", CMDDATA, 654549Seric "rset", CMDRSET, 664549Seric "vrfy", CMDVRFY, 677762Seric "expn", CMDVRFY, 684549Seric "help", CMDHELP, 694549Seric "noop", CMDNOOP, 704549Seric "quit", CMDQUIT, 714976Seric "helo", CMDHELO, 728544Seric "verb", CMDVERB, 739314Seric "onex", CMDONEX, 745003Seric # ifdef DEBUG 759339Seric "showq", CMDDBGQSHOW, 768544Seric "debug", CMDDBGDEBUG, 778544Seric "kill", CMDDBGKILL, 788544Seric "wiz", CMDDBGWIZ, 795003Seric # endif DEBUG 804549Seric NULL, CMDERROR, 814549Seric }; 824549Seric 838544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 8415596Seric char *WizWord; /* the wizard word to compare against */ 859339Seric bool InChild = FALSE; /* true if running in a subprocess */ 869378Seric bool OneXact = FALSE; /* one xaction only this run */ 8711146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 8811146Seric 899339Seric #define EX_QUIT 22 /* special code for QUIT command */ 908544Seric 914549Seric smtp() 924549Seric { 934549Seric register char *p; 948544Seric register struct cmd *c; 954549Seric char *cmd; 964549Seric extern char *skipword(); 974549Seric extern bool sameword(); 984549Seric bool hasmail; /* mail command received */ 994713Seric int rcps; /* number of recipients */ 1005003Seric auto ADDRESS *vrfyqueue; 10112612Seric ADDRESS *a; 1028544Seric char inp[MAXLINE]; 1037124Seric extern char Version[]; 1047356Seric extern tick(); 1058544Seric extern bool iswiz(); 1069349Seric extern char *arpadate(); 10711151Seric extern char *macvalue(); 10812612Seric extern ADDRESS *recipient(); 1094549Seric 1105003Seric hasmail = FALSE; 1114713Seric rcps = 0; 1127363Seric if (OutChannel != stdout) 1137363Seric { 1147363Seric /* arrange for debugging output to go to remote host */ 1157363Seric (void) close(1); 1167363Seric (void) dup(fileno(OutChannel)); 1177363Seric } 11811931Seric settime(); 11916153Seric expand("\001e", inp, &inp[sizeof inp], CurEnv); 12010708Seric message("220", inp); 1214549Seric for (;;) 1224549Seric { 12312612Seric /* arrange for backout */ 12412612Seric if (setjmp(TopFrame) > 0 && InChild) 12512612Seric finis(); 12612612Seric QuickAbort = FALSE; 12712612Seric HoldErrs = FALSE; 12812612Seric 1297356Seric /* setup for the read */ 1306907Seric CurEnv->e_to = NULL; 1314577Seric Errors = 0; 1327275Seric (void) fflush(stdout); 1337356Seric 1347356Seric /* read the input line */ 1357685Seric p = sfgets(inp, sizeof inp, InChannel); 1367356Seric 1377685Seric /* handle errors */ 1387356Seric if (p == NULL) 1397356Seric { 1404549Seric /* end of file, just die */ 1414558Seric message("421", "%s Lost input channel", HostName); 1424549Seric finis(); 1434549Seric } 1444549Seric 1454549Seric /* clean up end of line */ 1464558Seric fixcrlf(inp, TRUE); 1474549Seric 1484713Seric /* echo command to transcript */ 1499545Seric if (CurEnv->e_xfp != NULL) 1509545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1514713Seric 1524549Seric /* break off command */ 1534549Seric for (p = inp; isspace(*p); p++) 1544549Seric continue; 1554549Seric cmd = p; 1564549Seric while (*++p != '\0' && !isspace(*p)) 1574549Seric continue; 1584549Seric if (*p != '\0') 1594549Seric *p++ = '\0'; 1604549Seric 1614549Seric /* decode command */ 1624549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1634549Seric { 1644549Seric if (sameword(c->cmdname, cmd)) 1654549Seric break; 1664549Seric } 1674549Seric 1684549Seric /* process command */ 1694549Seric switch (c->cmdcode) 1704549Seric { 1714976Seric case CMDHELO: /* hello -- introduce yourself */ 17214877Seric if (sameword(p, HostName)) 17314877Seric { 17414877Seric /* connected to an echo server */ 17514877Seric message("553", "%s I refuse to talk to myself", 17614877Seric HostName); 17714877Seric break; 17814877Seric } 17911146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 18011146Seric { 18111146Seric char buf[MAXNAME]; 18211146Seric 18311146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 18411146Seric define('s', newstr(buf), CurEnv); 18511146Seric } 18611146Seric else 18711146Seric define('s', newstr(p), CurEnv); 1884997Seric message("250", "%s Hello %s, pleased to meet you", 1894997Seric HostName, p); 1904976Seric break; 1914976Seric 1924549Seric case CMDMAIL: /* mail -- designate sender */ 19311151Seric /* force a sending host even if no HELO given */ 19411151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 19511151Seric define('s', RealHostName, CurEnv); 19611151Seric 1979314Seric /* check for validity of this command */ 1984558Seric if (hasmail) 1994558Seric { 2004558Seric message("503", "Sender already specified"); 2014558Seric break; 2024558Seric } 2039339Seric if (InChild) 2049339Seric { 2059339Seric syserr("Nested MAIL command"); 2069339Seric exit(0); 2079339Seric } 2089339Seric 2099339Seric /* fork a subprocess to process this command */ 2109339Seric if (runinchild("SMTP-MAIL") > 0) 2119339Seric break; 2129339Seric initsys(); 2139339Seric 2149339Seric /* child -- go do the processing */ 2154549Seric p = skipword(p, "from"); 2164549Seric if (p == NULL) 2174549Seric break; 2184549Seric setsender(p); 2194577Seric if (Errors == 0) 2204549Seric { 2214549Seric message("250", "Sender ok"); 2224549Seric hasmail = TRUE; 2234549Seric } 2249339Seric else if (InChild) 2259339Seric finis(); 2264549Seric break; 2274549Seric 2284976Seric case CMDRCPT: /* rcpt -- designate recipient */ 22912612Seric if (setjmp(TopFrame) > 0) 23014785Seric { 23114785Seric CurEnv->e_flags &= ~EF_FATALERRS; 23212612Seric break; 23314785Seric } 23412612Seric QuickAbort = TRUE; 2354549Seric p = skipword(p, "to"); 2364549Seric if (p == NULL) 2374549Seric break; 23816140Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0'); 23912612Seric if (a == NULL) 24012612Seric break; 24116886Seric a->q_flags |= QPRIMARY; 24212612Seric a = recipient(a, &CurEnv->e_sendqueue); 24312612Seric if (Errors != 0) 24412612Seric break; 24512612Seric 24612612Seric /* no errors during parsing, but might be a duplicate */ 24712612Seric CurEnv->e_to = p; 24812612Seric if (!bitset(QBADADDR, a->q_flags)) 24912612Seric message("250", "Recipient ok"); 25012612Seric else 2514549Seric { 25212612Seric /* punt -- should keep message in ADDRESS.... */ 25312612Seric message("550", "Addressee unknown"); 2544549Seric } 25512612Seric CurEnv->e_to = NULL; 25612612Seric rcps++; 2574549Seric break; 2584549Seric 2594549Seric case CMDDATA: /* data -- text of mail */ 2604976Seric if (!hasmail) 2614549Seric { 2624976Seric message("503", "Need MAIL command"); 2634976Seric break; 2644549Seric } 2654713Seric else if (rcps <= 0) 2664549Seric { 2674976Seric message("503", "Need RCPT (recipient)"); 2684976Seric break; 2694549Seric } 2704976Seric 2714976Seric /* collect the text of the message */ 2724976Seric collect(TRUE); 2734976Seric if (Errors != 0) 2744976Seric break; 2754976Seric 2768238Seric /* 2778238Seric ** Arrange to send to everyone. 2788238Seric ** If sending to multiple people, mail back 2798238Seric ** errors rather than reporting directly. 2808238Seric ** In any case, don't mail back errors for 2818238Seric ** anything that has happened up to 2828238Seric ** now (the other end will do this). 28310197Seric ** Truncate our transcript -- the mail has gotten 28410197Seric ** to us successfully, and if we have 28510197Seric ** to mail this back, it will be easier 28610197Seric ** on the reader. 2878238Seric ** Then send to everyone. 2888238Seric ** Finally give a reply code. If an error has 2898238Seric ** already been given, don't mail a 2908238Seric ** message back. 2919339Seric ** We goose error returns by clearing error bit. 2928238Seric */ 2938238Seric 2944976Seric if (rcps != 1) 2959378Seric { 2969378Seric HoldErrs = TRUE; 29716886Seric ErrorMode = EM_MAIL; 2989378Seric } 2999339Seric CurEnv->e_flags &= ~EF_FATALERRS; 30010197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 3014976Seric 3024976Seric /* send to all recipients */ 30314877Seric sendall(CurEnv, SM_DEFAULT); 3046907Seric CurEnv->e_to = NULL; 3054976Seric 3068238Seric /* issue success if appropriate and reset */ 3078238Seric if (Errors == 0 || HoldErrs) 3089283Seric message("250", "Ok"); 3098238Seric else 3109339Seric CurEnv->e_flags &= ~EF_FATALERRS; 3119339Seric 3129339Seric /* if in a child, pop back to our parent */ 3139339Seric if (InChild) 3149339Seric finis(); 3154549Seric break; 3164549Seric 3174549Seric case CMDRSET: /* rset -- reset state */ 3184549Seric message("250", "Reset state"); 3199339Seric if (InChild) 3209339Seric finis(); 3219339Seric break; 3224549Seric 3234549Seric case CMDVRFY: /* vrfy -- verify address */ 3249339Seric if (runinchild("SMTP-VRFY") > 0) 3259339Seric break; 3265003Seric vrfyqueue = NULL; 3277762Seric QuickAbort = TRUE; 3289619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3297762Seric if (Errors != 0) 3309339Seric { 3319339Seric if (InChild) 3329339Seric finis(); 3337762Seric break; 3349339Seric } 3355003Seric while (vrfyqueue != NULL) 3365003Seric { 3375003Seric register ADDRESS *a = vrfyqueue->q_next; 3385003Seric char *code; 3395003Seric 3407685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3415003Seric a = a->q_next; 3425003Seric 3437685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3445003Seric { 3455003Seric if (a != NULL) 3465003Seric code = "250-"; 3475003Seric else 3485003Seric code = "250"; 3495003Seric if (vrfyqueue->q_fullname == NULL) 3505003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3515003Seric else 3525003Seric message(code, "%s <%s>", 3535003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3545003Seric } 3555003Seric else if (a == NULL) 3565003Seric message("554", "Self destructive alias loop"); 3575003Seric vrfyqueue = a; 3585003Seric } 3599339Seric if (InChild) 3609339Seric finis(); 3614549Seric break; 3624549Seric 3634549Seric case CMDHELP: /* help -- give user info */ 3644577Seric if (*p == '\0') 3654577Seric p = "SMTP"; 3664577Seric help(p); 3674549Seric break; 3684549Seric 3694549Seric case CMDNOOP: /* noop -- do nothing */ 3704549Seric message("200", "OK"); 3714549Seric break; 3724549Seric 3734549Seric case CMDQUIT: /* quit -- leave mail */ 3744549Seric message("221", "%s closing connection", HostName); 3759339Seric if (InChild) 3769339Seric ExitStat = EX_QUIT; 3774549Seric finis(); 3784549Seric 3798544Seric case CMDVERB: /* set verbose mode */ 3808544Seric Verbose = TRUE; 3818544Seric message("200", "Verbose mode"); 3828544Seric break; 3838544Seric 3849314Seric case CMDONEX: /* doing one transaction only */ 3859378Seric OneXact = TRUE; 3869314Seric message("200", "Only one transaction"); 3879314Seric break; 3889314Seric 3895003Seric # ifdef DEBUG 3909339Seric case CMDDBGQSHOW: /* show queues */ 3916907Seric printf("Send Queue="); 3926907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3935003Seric break; 3947275Seric 3957275Seric case CMDDBGDEBUG: /* set debug mode */ 3967676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3977676Seric tTflag(p); 3987676Seric message("200", "Debug set"); 3997275Seric break; 4007275Seric 4017282Seric case CMDDBGKILL: /* kill the parent */ 4028544Seric if (!iswiz()) 4038544Seric break; 4047282Seric if (kill(MotherPid, SIGTERM) >= 0) 4057282Seric message("200", "Mother is dead"); 4067282Seric else 4077282Seric message("500", "Can't kill Mom"); 4087282Seric break; 4098544Seric 4108544Seric case CMDDBGWIZ: /* become a wizard */ 4118544Seric if (WizWord != NULL) 4128544Seric { 4138544Seric char seed[3]; 4148544Seric extern char *crypt(); 4158544Seric 416*23106Seric (void) strncpy(seed, WizWord, 2); 41715596Seric if (strcmp(WizWord, crypt(p, seed)) == 0) 4188544Seric { 41915596Seric IsWiz = TRUE; 42015596Seric message("200", "Please pass, oh mighty wizard"); 4218544Seric break; 4228544Seric } 4238544Seric } 42415596Seric message("500", "You are no wizard!"); 4258544Seric break; 4265003Seric # endif DEBUG 4275003Seric 4284549Seric case CMDERROR: /* unknown command */ 4294549Seric message("500", "Command unrecognized"); 4304549Seric break; 4314549Seric 4324549Seric default: 4334549Seric syserr("smtp: unknown code %d", c->cmdcode); 4344549Seric break; 4354549Seric } 4364549Seric } 4374549Seric } 4384549Seric /* 4394549Seric ** SKIPWORD -- skip a fixed word. 4404549Seric ** 4414549Seric ** Parameters: 4424549Seric ** p -- place to start looking. 4434549Seric ** w -- word to skip. 4444549Seric ** 4454549Seric ** Returns: 4464549Seric ** p following w. 4474549Seric ** NULL on error. 4484549Seric ** 4494549Seric ** Side Effects: 4504549Seric ** clobbers the p data area. 4514549Seric */ 4524549Seric 4534549Seric static char * 4544549Seric skipword(p, w) 4554549Seric register char *p; 4564549Seric char *w; 4574549Seric { 4584549Seric register char *q; 4594549Seric extern bool sameword(); 4604549Seric 4614549Seric /* find beginning of word */ 4624549Seric while (isspace(*p)) 4634549Seric p++; 4644549Seric q = p; 4654549Seric 4664549Seric /* find end of word */ 4674549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4684549Seric p++; 4694549Seric while (isspace(*p)) 4704549Seric *p++ = '\0'; 4714549Seric if (*p != ':') 4724549Seric { 4734549Seric syntax: 4744549Seric message("501", "Syntax error"); 4754549Seric Errors++; 4764549Seric return (NULL); 4774549Seric } 4784549Seric *p++ = '\0'; 4794549Seric while (isspace(*p)) 4804549Seric p++; 4814549Seric 4824549Seric /* see if the input word matches desired word */ 4834549Seric if (!sameword(q, w)) 4844549Seric goto syntax; 4854549Seric 4864549Seric return (p); 4874549Seric } 4884577Seric /* 4894577Seric ** HELP -- implement the HELP command. 4904577Seric ** 4914577Seric ** Parameters: 4924577Seric ** topic -- the topic we want help for. 4934577Seric ** 4944577Seric ** Returns: 4954577Seric ** none. 4964577Seric ** 4974577Seric ** Side Effects: 4984577Seric ** outputs the help file to message output. 4994577Seric */ 5004577Seric 5014577Seric help(topic) 5024577Seric char *topic; 5034577Seric { 5044577Seric register FILE *hf; 5054577Seric int len; 5064577Seric char buf[MAXLINE]; 5074577Seric bool noinfo; 5084577Seric 5098269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5104577Seric { 5114577Seric /* no help */ 51211931Seric errno = 0; 5134577Seric message("502", "HELP not implemented"); 5144577Seric return; 5154577Seric } 5164577Seric 5174577Seric len = strlen(topic); 5184577Seric makelower(topic); 5194577Seric noinfo = TRUE; 5204577Seric 5214577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5224577Seric { 5234577Seric if (strncmp(buf, topic, len) == 0) 5244577Seric { 5254577Seric register char *p; 5264577Seric 5274577Seric p = index(buf, '\t'); 5284577Seric if (p == NULL) 5294577Seric p = buf; 5304577Seric else 5314577Seric p++; 5324577Seric fixcrlf(p, TRUE); 5334577Seric message("214-", p); 5344577Seric noinfo = FALSE; 5354577Seric } 5364577Seric } 5374577Seric 5384577Seric if (noinfo) 5394577Seric message("504", "HELP topic unknown"); 5404577Seric else 5414577Seric message("214", "End of HELP info"); 5424628Seric (void) fclose(hf); 5434577Seric } 5448544Seric /* 5458544Seric ** ISWIZ -- tell us if we are a wizard 5468544Seric ** 5478544Seric ** If not, print a nasty message. 5488544Seric ** 5498544Seric ** Parameters: 5508544Seric ** none. 5518544Seric ** 5528544Seric ** Returns: 5538544Seric ** TRUE if we are a wizard. 5548544Seric ** FALSE if we are not a wizard. 5558544Seric ** 5568544Seric ** Side Effects: 5578544Seric ** Prints a 500 exit stat if we are not a wizard. 5588544Seric */ 5595181Seric 56019038Seric #ifdef DEBUG 56119038Seric 5628544Seric bool 5638544Seric iswiz() 5648544Seric { 5658544Seric if (!IsWiz) 5668544Seric message("500", "Mere mortals musn't mutter that mantra"); 5678544Seric return (IsWiz); 5688544Seric } 56919038Seric 57019038Seric #endif DEBUG 5719339Seric /* 5729339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5739339Seric ** 5749339Seric ** Parameters: 5759339Seric ** label -- a string used in error messages 5769339Seric ** 5779339Seric ** Returns: 5789339Seric ** zero in the child 5799339Seric ** one in the parent 5809339Seric ** 5819339Seric ** Side Effects: 5829339Seric ** none. 5839339Seric */ 5848544Seric 5859339Seric runinchild(label) 5869339Seric char *label; 5879339Seric { 5889339Seric int childpid; 5899339Seric 59016158Seric if (!OneXact) 5919339Seric { 59216158Seric childpid = dofork(); 59316158Seric if (childpid < 0) 59416158Seric { 59516158Seric syserr("%s: cannot fork", label); 59616158Seric return (1); 59716158Seric } 59816158Seric if (childpid > 0) 59916158Seric { 60016158Seric auto int st; 6019339Seric 60216158Seric /* parent -- wait for child to complete */ 60316158Seric st = waitfor(childpid); 60416158Seric if (st == -1) 60516158Seric syserr("%s: lost child", label); 6069339Seric 60716158Seric /* if we exited on a QUIT command, complete the process */ 60816158Seric if (st == (EX_QUIT << 8)) 60916158Seric finis(); 6109339Seric 61116158Seric return (1); 61216158Seric } 61316158Seric else 61416158Seric { 61516158Seric /* child */ 61616158Seric InChild = TRUE; 61716158Seric } 6189339Seric } 61915256Seric 62016158Seric /* child (or ONEX command specified) */ 62116158Seric clearenvelope(CurEnv); 62215256Seric 62316158Seric /* open alias database */ 62416158Seric initaliases(AliasFile, FALSE); 62516158Seric 62616158Seric return (0); 6279339Seric } 6289339Seric 6295181Seric # endif SMTP 630