122712Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362532Sbostic * Copyright (c) 1988, 1993 462532Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822712Sdist 933731Sbostic # include "sendmail.h" 1022712Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*68751Seric static char sccsid[] = "@(#)srvrsmtp.c 8.65 (Berkeley) 04/08/95 (with SMTP)"; 1433731Sbostic #else 15*68751Seric static char sccsid[] = "@(#)srvrsmtp.c 8.65 (Berkeley) 04/08/95 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 204549Seric 2133731Sbostic # ifdef SMTP 224556Seric 234549Seric /* 244549Seric ** SMTP -- run the SMTP protocol. 254549Seric ** 264549Seric ** Parameters: 274549Seric ** none. 284549Seric ** 294549Seric ** Returns: 304549Seric ** never. 314549Seric ** 324549Seric ** Side Effects: 334549Seric ** Reads commands from the input channel and processes 344549Seric ** them. 354549Seric */ 364549Seric 374549Seric struct cmd 384549Seric { 394549Seric char *cmdname; /* command name */ 404549Seric int cmdcode; /* internal code, see below */ 414549Seric }; 424549Seric 434549Seric /* values for cmdcode */ 444549Seric # define CMDERROR 0 /* bad command */ 454549Seric # define CMDMAIL 1 /* mail -- designate sender */ 464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 474549Seric # define CMDDATA 3 /* data -- send message text */ 489339Seric # define CMDRSET 4 /* rset -- reset state */ 499339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5058092Seric # define CMDEXPN 6 /* expn -- expand address */ 519339Seric # define CMDNOOP 7 /* noop -- do nothing */ 529339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 539339Seric # define CMDHELO 9 /* helo -- be polite */ 5458092Seric # define CMDHELP 10 /* help -- give usage info */ 5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 5964685Seric /* use this to catch and log "door handle" attempts on your system */ 6064685Seric # define CMDLOGBOGUS 23 /* bogus command that should be logged */ 6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6258092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6358092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 644549Seric 654549Seric static struct cmd CmdTab[] = 664549Seric { 674549Seric "mail", CMDMAIL, 684976Seric "rcpt", CMDRCPT, 694549Seric "data", CMDDATA, 704549Seric "rset", CMDRSET, 714549Seric "vrfy", CMDVRFY, 7258092Seric "expn", CMDEXPN, 734549Seric "help", CMDHELP, 744549Seric "noop", CMDNOOP, 754549Seric "quit", CMDQUIT, 764976Seric "helo", CMDHELO, 7758323Seric "ehlo", CMDEHLO, 788544Seric "verb", CMDVERB, 799314Seric "onex", CMDONEX, 8036230Skarels /* 8136230Skarels * remaining commands are here only 8236230Skarels * to trap and log attempts to use them 8336230Skarels */ 849339Seric "showq", CMDDBGQSHOW, 858544Seric "debug", CMDDBGDEBUG, 8664685Seric "wiz", CMDLOGBOGUS, 874549Seric NULL, CMDERROR, 884549Seric }; 894549Seric 909378Seric bool OneXact = FALSE; /* one xaction only this run */ 9165017Seric char *CurSmtpClient; /* who's at the other end of channel */ 9211146Seric 9363937Seric static char *skipword(); 9466325Seric extern char RealUserName[]; 9563937Seric 9666325Seric 9766283Seric #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 9866283Seric 9955012Seric smtp(e) 10055012Seric register ENVELOPE *e; 1014549Seric { 1024549Seric register char *p; 1038544Seric register struct cmd *c; 1044549Seric char *cmd; 1055003Seric auto ADDRESS *vrfyqueue; 10612612Seric ADDRESS *a; 10758109Seric bool gotmail; /* mail command received */ 10858092Seric bool gothello; /* helo command received */ 10958092Seric bool vrfy; /* set if this is a vrfy command */ 11058323Seric char *protocol; /* sending protocol */ 11159016Seric char *sendinghost; /* sending hostname */ 11266005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11358333Seric auto char *delimptr; 11458714Seric char *id; 11568433Seric int nrcpts = 0; /* number of RCPT commands */ 11663787Seric bool doublequeue; 11766283Seric int badcommands = 0; /* count of bad commands */ 1188544Seric char inp[MAXLINE]; 11957232Seric char cmdbuf[MAXLINE]; 1207124Seric extern char Version[]; 12124943Seric extern ENVELOPE BlankEnvelope; 1224549Seric 12359066Seric if (fileno(OutChannel) != fileno(stdout)) 1247363Seric { 1257363Seric /* arrange for debugging output to go to remote host */ 12659066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1277363Seric } 12855012Seric settime(e); 12966005Seric peerhostname = RealHostName; 13066005Seric if (peerhostname == NULL) 13166005Seric peerhostname = "localhost"; 13266005Seric CurHostName = peerhostname; 13365017Seric CurSmtpClient = macvalue('_', e); 13465017Seric if (CurSmtpClient == NULL) 13566003Seric CurSmtpClient = CurHostName; 13665017Seric 13765017Seric setproctitle("server %s startup", CurSmtpClient); 13868529Seric expand("\201e", inp, sizeof inp, e); 13964496Seric if (BrokenSmtpPeers) 14064496Seric { 14166762Seric p = strchr(inp, '\n'); 14266762Seric if (p != NULL) 14366762Seric *p = '\0'; 14464496Seric message("220 %s", inp); 14564496Seric } 14664496Seric else 14764496Seric { 14866745Seric char *q = inp; 14966745Seric 15066745Seric while (q != NULL) 15166745Seric { 15266762Seric p = strchr(q, '\n'); 15366762Seric if (p != NULL) 15466762Seric *p++ = '\0'; 15566745Seric message("220-%s", q); 15666745Seric q = p; 15766745Seric } 15864496Seric message("220 ESMTP spoken here"); 15964496Seric } 16058330Seric protocol = NULL; 16159016Seric sendinghost = macvalue('s', e); 16258082Seric gothello = FALSE; 16358330Seric gotmail = FALSE; 1644549Seric for (;;) 1654549Seric { 16612612Seric /* arrange for backout */ 16765751Seric if (setjmp(TopFrame) > 0) 16859058Seric { 16965751Seric /* if() nesting is necessary for Cray UNICOS */ 17065751Seric if (InChild) 17165751Seric { 17265751Seric QuickAbort = FALSE; 17365751Seric SuprErrs = TRUE; 17465751Seric finis(); 17565751Seric } 17659058Seric } 17712612Seric QuickAbort = FALSE; 17812612Seric HoldErrs = FALSE; 17951951Seric LogUsrErrs = FALSE; 18063843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 18112612Seric 1827356Seric /* setup for the read */ 18355012Seric e->e_to = NULL; 1844577Seric Errors = 0; 1857275Seric (void) fflush(stdout); 1867356Seric 1877356Seric /* read the input line */ 18861093Seric SmtpPhase = "server cmd read"; 18961093Seric setproctitle("server %s cmd read", CurHostName); 19061093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 19161093Seric SmtpPhase); 1927356Seric 1937685Seric /* handle errors */ 1947356Seric if (p == NULL) 1957356Seric { 1964549Seric /* end of file, just die */ 19766017Seric disconnect(1, e); 19858151Seric message("421 %s Lost input channel from %s", 19965017Seric MyHostName, CurSmtpClient); 20055464Seric #ifdef LOG 20163843Seric if (LogLevel > (gotmail ? 1 : 19)) 20255464Seric syslog(LOG_NOTICE, "lost input channel from %s", 20365017Seric CurSmtpClient); 20455464Seric #endif 20558069Seric if (InChild) 20658069Seric ExitStat = EX_QUIT; 2074549Seric finis(); 2084549Seric } 2094549Seric 2104549Seric /* clean up end of line */ 2114558Seric fixcrlf(inp, TRUE); 2124549Seric 2134713Seric /* echo command to transcript */ 21455012Seric if (e->e_xfp != NULL) 21555012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2164713Seric 21759060Seric if (e->e_id == NULL) 21865058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 21959060Seric else 22065058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 22159060Seric 2224549Seric /* break off command */ 22358050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2244549Seric continue; 22557232Seric cmd = cmdbuf; 22658050Seric while (*p != '\0' && 22758050Seric !(isascii(*p) && isspace(*p)) && 22858050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 22924981Seric *cmd++ = *p++; 23024981Seric *cmd = '\0'; 2314549Seric 23225691Seric /* throw away leading whitespace */ 23358050Seric while (isascii(*p) && isspace(*p)) 23425691Seric p++; 23525691Seric 2364549Seric /* decode command */ 2374549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2384549Seric { 23933725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2404549Seric break; 2414549Seric } 2424549Seric 24351954Seric /* reset errors */ 24451954Seric errno = 0; 24551954Seric 2464549Seric /* process command */ 2474549Seric switch (c->cmdcode) 2484549Seric { 2494976Seric case CMDHELO: /* hello -- introduce yourself */ 25058323Seric case CMDEHLO: /* extended hello */ 25158323Seric if (c->cmdcode == CMDEHLO) 25258323Seric { 25358323Seric protocol = "ESMTP"; 25461093Seric SmtpPhase = "server EHLO"; 25558323Seric } 25658323Seric else 25758323Seric { 25858323Seric protocol = "SMTP"; 25961093Seric SmtpPhase = "server HELO"; 26058323Seric } 26167445Seric 26267445Seric /* check for valid domain name (re 1123 5.2.5) */ 26367445Seric if (*p == '\0') 26467445Seric { 26567445Seric message("501 %s requires domain address", 26667445Seric cmdbuf); 26767445Seric break; 26867445Seric } 26967445Seric else 27067445Seric { 27167445Seric register char *q; 27267445Seric 27367445Seric for (q = p; *q != '\0'; q++) 27467445Seric { 27567445Seric if (!isascii(*q)) 27667445Seric break; 27767445Seric if (isalnum(*q)) 27867445Seric continue; 27967445Seric if (strchr("[].-_#", *q) == NULL) 28067445Seric break; 28167445Seric } 28267445Seric if (*q != '\0') 28367445Seric { 28467445Seric message("501 Invalid domain name"); 28567445Seric break; 28667445Seric } 28767445Seric } 28867445Seric 28959016Seric sendinghost = newstr(p); 29060210Seric gothello = TRUE; 29160210Seric if (c->cmdcode != CMDEHLO) 29260239Seric { 29360239Seric /* print old message and be done with it */ 29460239Seric message("250 %s Hello %s, pleased to meet you", 29565017Seric MyHostName, CurSmtpClient); 29660210Seric break; 29760239Seric } 29860239Seric 29960239Seric /* print extended message and brag */ 30060239Seric message("250-%s Hello %s, pleased to meet you", 30166760Seric MyHostName, CurSmtpClient); 30258323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 30358323Seric message("250-EXPN"); 30467417Seric message("250-8BITMIME"); 30564359Seric if (MaxMessageSize > 0) 30664359Seric message("250-SIZE %ld", MaxMessageSize); 30759271Seric else 30859271Seric message("250-SIZE"); 30968028Seric #ifdef DSN 31068606Seric message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)"); 31168028Seric #endif 31258323Seric message("250 HELP"); 3134976Seric break; 3144976Seric 3154549Seric case CMDMAIL: /* mail -- designate sender */ 31661093Seric SmtpPhase = "server MAIL"; 31724943Seric 3189314Seric /* check for validity of this command */ 31958789Seric if (!gothello) 32058082Seric { 32158957Seric /* set sending host to our known value */ 32259016Seric if (sendinghost == NULL) 32366005Seric sendinghost = peerhostname; 32458957Seric 32558789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 32658821Seric { 32758789Seric message("503 Polite people say HELO first"); 32858821Seric break; 32958821Seric } 33058082Seric } 33158109Seric if (gotmail) 3324558Seric { 33358151Seric message("503 Sender already specified"); 33463843Seric if (InChild) 33563843Seric finis(); 3364558Seric break; 3374558Seric } 3389339Seric if (InChild) 3399339Seric { 34036230Skarels errno = 0; 34158151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34258069Seric finis(); 3439339Seric } 3449339Seric 3459339Seric /* fork a subprocess to process this command */ 34655012Seric if (runinchild("SMTP-MAIL", e) > 0) 3479339Seric break; 34863753Seric if (!gothello) 34963753Seric { 35063753Seric auth_warning(e, 35166005Seric "Host %s didn't use HELO protocol", 35266005Seric peerhostname); 35363753Seric } 35465947Seric #ifdef PICKY_HELO_CHECK 35566005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 35666005Seric (strcasecmp(peerhostname, "localhost") != 0 || 35765823Seric strcasecmp(sendinghost, MyHostName) != 0)) 35865823Seric { 35965823Seric auth_warning(e, "Host %s claimed to be %s", 36066005Seric peerhostname, sendinghost); 36165823Seric } 36265947Seric #endif 36365823Seric 36458323Seric if (protocol == NULL) 36558323Seric protocol = "SMTP"; 36658323Seric define('r', protocol, e); 36759016Seric define('s', sendinghost, e); 36855012Seric initsys(e); 36959747Seric nrcpts = 0; 37068582Seric e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; 37165058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3729339Seric 3739339Seric /* child -- go do the processing */ 3744549Seric p = skipword(p, "from"); 3754549Seric if (p == NULL) 3764549Seric break; 37757977Seric if (setjmp(TopFrame) > 0) 37858147Seric { 37958147Seric /* this failed -- undo work */ 38058147Seric if (InChild) 38159058Seric { 38259058Seric QuickAbort = FALSE; 38359058Seric SuprErrs = TRUE; 38463787Seric e->e_flags &= ~EF_FATALERRS; 38558147Seric finis(); 38659058Seric } 38757977Seric break; 38858147Seric } 38957977Seric QuickAbort = TRUE; 39058333Seric 39158333Seric /* must parse sender first */ 39258333Seric delimptr = NULL; 39358704Seric setsender(p, e, &delimptr, FALSE); 39458333Seric p = delimptr; 39558333Seric if (p != NULL && *p != '\0') 39658333Seric *p++ = '\0'; 39758333Seric 39866325Seric /* check for possible spoofing */ 39966325Seric if (RealUid != 0 && OpMode == MD_SMTP && 40067473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40167473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40266325Seric { 40366325Seric auth_warning(e, "%s owned process doing -bs", 40466325Seric RealUserName); 40566325Seric } 40666325Seric 40758333Seric /* now parse ESMTP arguments */ 40868560Seric e->e_msgsize = 0; 40966764Seric while (p != NULL && *p != '\0') 41058333Seric { 41158333Seric char *kp; 41266304Seric char *vp = NULL; 41358333Seric 41458333Seric /* locate the beginning of the keyword */ 41558333Seric while (isascii(*p) && isspace(*p)) 41658333Seric p++; 41758333Seric if (*p == '\0') 41858333Seric break; 41958333Seric kp = p; 42058333Seric 42158333Seric /* skip to the value portion */ 42258333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42358333Seric p++; 42458333Seric if (*p == '=') 42558333Seric { 42658333Seric *p++ = '\0'; 42758333Seric vp = p; 42858333Seric 42958333Seric /* skip to the end of the value */ 43058333Seric while (*p != '\0' && *p != ' ' && 43158333Seric !(isascii(*p) && iscntrl(*p)) && 43258333Seric *p != '=') 43358333Seric p++; 43458333Seric } 43558333Seric 43658333Seric if (*p != '\0') 43758333Seric *p++ = '\0'; 43858333Seric 43958333Seric if (tTd(19, 1)) 44066764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 44158333Seric vp == NULL ? "<null>" : vp); 44258333Seric 44368559Seric mail_esmtp_args(kp, vp, e); 44458333Seric } 44559284Seric 44668560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 44759284Seric { 44859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 44959284Seric MaxMessageSize); 45059284Seric /* NOTREACHED */ 45159284Seric } 45258333Seric 45368560Seric if (!enoughspace(e->e_msgsize)) 45458333Seric { 45558333Seric message("452 Insufficient disk space; try again later"); 45658333Seric break; 45758333Seric } 45858151Seric message("250 Sender ok"); 45958147Seric gotmail = TRUE; 4604549Seric break; 4614549Seric 4624976Seric case CMDRCPT: /* rcpt -- designate recipient */ 46358850Seric if (!gotmail) 46458850Seric { 46558850Seric usrerr("503 Need MAIL before RCPT"); 46658850Seric break; 46758850Seric } 46861093Seric SmtpPhase = "server RCPT"; 46912612Seric if (setjmp(TopFrame) > 0) 47014785Seric { 47155012Seric e->e_flags &= ~EF_FATALERRS; 47212612Seric break; 47314785Seric } 47412612Seric QuickAbort = TRUE; 47551951Seric LogUsrErrs = TRUE; 47658093Seric 47759699Seric if (e->e_sendmode != SM_DELIVER) 47859699Seric e->e_flags |= EF_VRFYONLY; 47958919Seric 4804549Seric p = skipword(p, "to"); 4814549Seric if (p == NULL) 4824549Seric break; 48367880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 48412612Seric if (a == NULL) 48512612Seric break; 48667880Seric p = delimptr; 48767880Seric 48867880Seric /* now parse ESMTP arguments */ 48967880Seric while (p != NULL && *p != '\0') 49067880Seric { 49167880Seric char *kp; 49267880Seric char *vp = NULL; 49367880Seric 49467880Seric /* locate the beginning of the keyword */ 49567880Seric while (isascii(*p) && isspace(*p)) 49667880Seric p++; 49767880Seric if (*p == '\0') 49867880Seric break; 49967880Seric kp = p; 50067880Seric 50167880Seric /* skip to the value portion */ 50267880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 50367880Seric p++; 50467880Seric if (*p == '=') 50567880Seric { 50667880Seric *p++ = '\0'; 50767880Seric vp = p; 50867880Seric 50967880Seric /* skip to the end of the value */ 51067880Seric while (*p != '\0' && *p != ' ' && 51167880Seric !(isascii(*p) && iscntrl(*p)) && 51267880Seric *p != '=') 51367880Seric p++; 51467880Seric } 51567880Seric 51667880Seric if (*p != '\0') 51767880Seric *p++ = '\0'; 51867880Seric 51967880Seric if (tTd(19, 1)) 52067880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 52167880Seric vp == NULL ? "<null>" : vp); 52267880Seric 52367963Seric rcpt_esmtp_args(a, kp, vp, e); 52467963Seric 52567880Seric } 52667980Seric 52767980Seric /* save in recipient list after ESMTP mods */ 52867982Seric a = recipient(a, &e->e_sendqueue, 0, e); 52967980Seric 53012612Seric if (Errors != 0) 53112612Seric break; 53212612Seric 53312612Seric /* no errors during parsing, but might be a duplicate */ 53455012Seric e->e_to = p; 53512612Seric if (!bitset(QBADADDR, a->q_flags)) 53659747Seric { 53764718Seric message("250 Recipient ok%s", 53864718Seric bitset(QQUEUEUP, a->q_flags) ? 53964718Seric " (will queue)" : ""); 54059747Seric nrcpts++; 54159747Seric } 54212612Seric else 5434549Seric { 54412612Seric /* punt -- should keep message in ADDRESS.... */ 54558151Seric message("550 Addressee unknown"); 5464549Seric } 54755012Seric e->e_to = NULL; 5484549Seric break; 5494549Seric 5504549Seric case CMDDATA: /* data -- text of mail */ 55161093Seric SmtpPhase = "server DATA"; 55258109Seric if (!gotmail) 5534549Seric { 55458151Seric message("503 Need MAIL command"); 5554976Seric break; 5564549Seric } 55764718Seric else if (nrcpts <= 0) 5584549Seric { 55958151Seric message("503 Need RCPT (recipient)"); 5604976Seric break; 5614549Seric } 5624976Seric 56358929Seric /* check to see if we need to re-expand aliases */ 56463787Seric /* also reset QBADADDR on already-diagnosted addrs */ 56563787Seric doublequeue = FALSE; 56658929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 56758929Seric { 56858929Seric if (bitset(QVERIFIED, a->q_flags)) 56963787Seric { 57063787Seric /* need to re-expand aliases */ 57163787Seric doublequeue = TRUE; 57263787Seric } 57363787Seric if (bitset(QBADADDR, a->q_flags)) 57463787Seric { 57563787Seric /* make this "go away" */ 57663787Seric a->q_flags |= QDONTSEND; 57763787Seric a->q_flags &= ~QBADADDR; 57863787Seric } 57958929Seric } 58058929Seric 5814976Seric /* collect the text of the message */ 58224943Seric SmtpPhase = "collect"; 58368692Seric buffer_errors(); 58467546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 58568692Seric flush_errors(TRUE); 58664766Seric if (Errors != 0) 58764766Seric goto abortmessage; 58867131Seric 58968582Seric /* make sure we actually do delivery */ 59068582Seric e->e_flags &= ~EF_CLRQUEUE; 59168582Seric 59267131Seric /* from now on, we have to operate silently */ 59368692Seric buffer_errors(); 59467131Seric e->e_errormode = EM_MAIL; 5954976Seric 5968238Seric /* 5978238Seric ** Arrange to send to everyone. 5988238Seric ** If sending to multiple people, mail back 5998238Seric ** errors rather than reporting directly. 6008238Seric ** In any case, don't mail back errors for 6018238Seric ** anything that has happened up to 6028238Seric ** now (the other end will do this). 60310197Seric ** Truncate our transcript -- the mail has gotten 60410197Seric ** to us successfully, and if we have 60510197Seric ** to mail this back, it will be easier 60610197Seric ** on the reader. 6078238Seric ** Then send to everyone. 6088238Seric ** Finally give a reply code. If an error has 6098238Seric ** already been given, don't mail a 6108238Seric ** message back. 6119339Seric ** We goose error returns by clearing error bit. 6128238Seric */ 6138238Seric 61424943Seric SmtpPhase = "delivery"; 61555012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 61658714Seric id = e->e_id; 6174976Seric 61867131Seric if (doublequeue) 61959730Seric { 62067131Seric /* make sure it is in the queue */ 62167131Seric queueup(e, TRUE, FALSE); 62268035Seric if (e->e_sendmode == SM_QUEUE) 62368035Seric e->e_flags |= EF_KEEPQUEUE; 62459730Seric } 6258238Seric else 62658919Seric { 62767131Seric /* send to all recipients */ 62867131Seric sendall(e, SM_DEFAULT); 62967131Seric } 63067131Seric e->e_to = NULL; 63159747Seric 63267131Seric /* issue success message */ 63367131Seric message("250 %s Message accepted for delivery", id); 63464296Seric 63567131Seric /* if we just queued, poke it */ 63667131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 63767131Seric { 63867131Seric extern pid_t dowork(); 63967131Seric 64067131Seric unlockqueue(e); 64167131Seric (void) dowork(id, TRUE, TRUE, e); 64258919Seric } 64358883Seric 64459747Seric abortmessage: 6459339Seric /* if in a child, pop back to our parent */ 6469339Seric if (InChild) 6479339Seric finis(); 64824943Seric 64924943Seric /* clean up a bit */ 65058109Seric gotmail = FALSE; 65155012Seric dropenvelope(e); 65258179Seric CurEnv = e = newenvelope(e, CurEnv); 65355012Seric e->e_flags = BlankEnvelope.e_flags; 6544549Seric break; 6554549Seric 6564549Seric case CMDRSET: /* rset -- reset state */ 65758151Seric message("250 Reset state"); 65868603Seric 65968603Seric /* arrange to ignore any current send list */ 66068603Seric e->e_sendqueue = NULL; 66164359Seric e->e_flags |= EF_CLRQUEUE; 6629339Seric if (InChild) 6639339Seric finis(); 66458109Seric 66558109Seric /* clean up a bit */ 66658109Seric gotmail = FALSE; 66758109Seric dropenvelope(e); 66858179Seric CurEnv = e = newenvelope(e, CurEnv); 6699339Seric break; 6704549Seric 6714549Seric case CMDVRFY: /* vrfy -- verify address */ 67258092Seric case CMDEXPN: /* expn -- expand address */ 67358092Seric vrfy = c->cmdcode == CMDVRFY; 67458092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 67558092Seric PrivacyFlags)) 67658082Seric { 67758412Seric if (vrfy) 67867160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 67958412Seric else 68065192Seric message("502 Sorry, we do not allow this operation"); 68165017Seric #ifdef LOG 68265017Seric if (LogLevel > 5) 68365017Seric syslog(LOG_INFO, "%s: %s [rejected]", 68465017Seric CurSmtpClient, inp); 68565017Seric #endif 68658082Seric break; 68758082Seric } 68858082Seric else if (!gothello && 68958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 69058092Seric PrivacyFlags)) 69158082Seric { 69258151Seric message("503 I demand that you introduce yourself first"); 69358082Seric break; 69458082Seric } 69558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6969339Seric break; 69755173Seric #ifdef LOG 69858020Seric if (LogLevel > 5) 69965017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 70055173Seric #endif 7015003Seric vrfyqueue = NULL; 7027762Seric QuickAbort = TRUE; 70358092Seric if (vrfy) 70458092Seric e->e_flags |= EF_VRFYONLY; 70562373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 70667615Seric p++; 70762373Seric if (*p == '\0') 70862373Seric { 70962373Seric message("501 Argument required"); 71062373Seric Errors++; 71162373Seric } 71262373Seric else 71362373Seric { 71467990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 71562373Seric } 7167762Seric if (Errors != 0) 7179339Seric { 7189339Seric if (InChild) 7199339Seric finis(); 7207762Seric break; 7219339Seric } 72262373Seric if (vrfyqueue == NULL) 72362373Seric { 72462373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 72562373Seric } 7265003Seric while (vrfyqueue != NULL) 7275003Seric { 72863971Seric a = vrfyqueue; 72963971Seric while ((a = a->q_next) != NULL && 73063971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 73163971Seric continue; 7327685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 73358151Seric printvrfyaddr(vrfyqueue, a == NULL); 73463847Seric vrfyqueue = vrfyqueue->q_next; 7355003Seric } 7369339Seric if (InChild) 7379339Seric finis(); 7384549Seric break; 7394549Seric 7404549Seric case CMDHELP: /* help -- give user info */ 7414577Seric help(p); 7424549Seric break; 7434549Seric 7444549Seric case CMDNOOP: /* noop -- do nothing */ 74564122Seric message("250 OK"); 7464549Seric break; 7474549Seric 7484549Seric case CMDQUIT: /* quit -- leave mail */ 74958151Seric message("221 %s closing connection", MyHostName); 75061051Seric 75166283Seric doquit: 75268603Seric /* arrange to ignore any current send list */ 75368603Seric e->e_sendqueue = NULL; 75468603Seric 75561051Seric /* avoid future 050 messages */ 75666017Seric disconnect(1, e); 75761051Seric 7589339Seric if (InChild) 7599339Seric ExitStat = EX_QUIT; 7604549Seric finis(); 7614549Seric 7628544Seric case CMDVERB: /* set verbose mode */ 76359957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 76459957Seric { 76559957Seric /* this would give out the same info */ 76659957Seric message("502 Verbose unavailable"); 76759957Seric break; 76859957Seric } 7698544Seric Verbose = TRUE; 77058734Seric e->e_sendmode = SM_DELIVER; 77159957Seric message("250 Verbose mode"); 7728544Seric break; 7738544Seric 7749314Seric case CMDONEX: /* doing one transaction only */ 7759378Seric OneXact = TRUE; 77659957Seric message("250 Only one transaction"); 7779314Seric break; 7789314Seric 77936230Skarels # ifdef SMTPDEBUG 7809339Seric case CMDDBGQSHOW: /* show queues */ 7816907Seric printf("Send Queue="); 78255012Seric printaddr(e->e_sendqueue, TRUE); 7835003Seric break; 7847275Seric 7857275Seric case CMDDBGDEBUG: /* set debug mode */ 7867676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7877676Seric tTflag(p); 78858151Seric message("200 Debug set"); 7897275Seric break; 7907275Seric 79136230Skarels # else /* not SMTPDEBUG */ 79236230Skarels case CMDDBGQSHOW: /* show queues */ 79336230Skarels case CMDDBGDEBUG: /* set debug mode */ 79464685Seric # endif /* SMTPDEBUG */ 79564685Seric case CMDLOGBOGUS: /* bogus command */ 79636233Skarels # ifdef LOG 79758308Seric if (LogLevel > 0) 79864685Seric syslog(LOG_CRIT, 79958020Seric "\"%s\" command from %s (%s)", 80066005Seric c->cmdname, peerhostname, 80158755Seric anynet_ntoa(&RealHostAddr)); 80236233Skarels # endif 80336230Skarels /* FALL THROUGH */ 80436230Skarels 8054549Seric case CMDERROR: /* unknown command */ 80666283Seric if (++badcommands > MAXBADCOMMANDS) 80766283Seric { 80866283Seric message("421 %s Too many bad commands; closing connection", 80966283Seric MyHostName); 81066283Seric goto doquit; 81166283Seric } 81266283Seric 81358151Seric message("500 Command unrecognized"); 8144549Seric break; 8154549Seric 8164549Seric default: 81736230Skarels errno = 0; 81858151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8194549Seric break; 8204549Seric } 8214549Seric } 8224549Seric } 8234549Seric /* 8244549Seric ** SKIPWORD -- skip a fixed word. 8254549Seric ** 8264549Seric ** Parameters: 8274549Seric ** p -- place to start looking. 8284549Seric ** w -- word to skip. 8294549Seric ** 8304549Seric ** Returns: 8314549Seric ** p following w. 8324549Seric ** NULL on error. 8334549Seric ** 8344549Seric ** Side Effects: 8354549Seric ** clobbers the p data area. 8364549Seric */ 8374549Seric 8384549Seric static char * 8394549Seric skipword(p, w) 8404549Seric register char *p; 8414549Seric char *w; 8424549Seric { 8434549Seric register char *q; 84466005Seric char *firstp = p; 8454549Seric 8464549Seric /* find beginning of word */ 84758050Seric while (isascii(*p) && isspace(*p)) 8484549Seric p++; 8494549Seric q = p; 8504549Seric 8514549Seric /* find end of word */ 85258050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8534549Seric p++; 85458050Seric while (isascii(*p) && isspace(*p)) 8554549Seric *p++ = '\0'; 8564549Seric if (*p != ':') 8574549Seric { 8584549Seric syntax: 85966005Seric message("501 Syntax error in parameters scanning \"%s\"", 86066005Seric firstp); 8614549Seric Errors++; 8624549Seric return (NULL); 8634549Seric } 8644549Seric *p++ = '\0'; 86558050Seric while (isascii(*p) && isspace(*p)) 8664549Seric p++; 8674549Seric 86862373Seric if (*p == '\0') 86962373Seric goto syntax; 87062373Seric 8714549Seric /* see if the input word matches desired word */ 87233725Sbostic if (strcasecmp(q, w)) 8734549Seric goto syntax; 8744549Seric 8754549Seric return (p); 8764549Seric } 8774577Seric /* 87868559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 87968559Seric ** 88068559Seric ** Parameters: 88168559Seric ** kp -- the parameter key. 88268559Seric ** vp -- the value of that parameter. 88368559Seric ** e -- the envelope. 88468559Seric ** 88568559Seric ** Returns: 88668559Seric ** none. 88768559Seric */ 88868559Seric 88968559Seric mail_esmtp_args(kp, vp, e) 89068559Seric char *kp; 89168559Seric char *vp; 89268559Seric ENVELOPE *e; 89368559Seric { 89468559Seric if (strcasecmp(kp, "size") == 0) 89568559Seric { 89668559Seric if (vp == NULL) 89768559Seric { 89868559Seric usrerr("501 SIZE requires a value"); 89968559Seric /* NOTREACHED */ 90068559Seric } 90168559Seric # ifdef __STDC__ 90268560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10); 90368559Seric # else 90468560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10); 90568559Seric # endif 90668559Seric } 90768559Seric else if (strcasecmp(kp, "body") == 0) 90868559Seric { 90968559Seric if (vp == NULL) 91068559Seric { 91168559Seric usrerr("501 BODY requires a value"); 91268559Seric /* NOTREACHED */ 91368559Seric } 91468559Seric if (strcasecmp(vp, "8bitmime") == 0) 91568559Seric { 91668559Seric SevenBitInput = FALSE; 91768559Seric } 91868559Seric else if (strcasecmp(vp, "7bit") == 0) 91968559Seric { 92068559Seric SevenBitInput = TRUE; 92168559Seric } 92268559Seric else 92368559Seric { 92468559Seric usrerr("501 Unknown BODY type %s", 92568559Seric vp); 92668559Seric /* NOTREACHED */ 92768559Seric } 92868559Seric e->e_bodytype = newstr(vp); 92968559Seric } 93068559Seric else if (strcasecmp(kp, "envid") == 0) 93168559Seric { 93268559Seric if (vp == NULL) 93368559Seric { 93468559Seric usrerr("501 ENVID requires a value"); 93568559Seric /* NOTREACHED */ 93668559Seric } 93768583Seric if (!xtextok(vp)) 93868583Seric { 93968583Seric usrerr("501 Syntax error in ENVID parameter value"); 94068583Seric /* NOTREACHED */ 94168583Seric } 94268583Seric if (e->e_envid != NULL) 94368583Seric { 94468583Seric usrerr("501 Duplicate ENVID parameter"); 94568583Seric /* NOTREACHED */ 94668583Seric } 94768559Seric e->e_envid = newstr(vp); 94868559Seric } 94968559Seric else if (strcasecmp(kp, "ret") == 0) 95068559Seric { 95168559Seric if (vp == NULL) 95268559Seric { 95368559Seric usrerr("501 RET requires a value"); 95468559Seric /* NOTREACHED */ 95568559Seric } 95668583Seric if (bitset(EF_RET_PARAM, e->e_flags)) 95768583Seric { 95868583Seric usrerr("501 Duplicate RET parameter"); 95968583Seric /* NOTREACHED */ 96068583Seric } 96168559Seric e->e_flags |= EF_RET_PARAM; 96268559Seric if (strcasecmp(vp, "hdrs") == 0) 96368559Seric e->e_flags |= EF_NO_BODY_RETN; 96468559Seric else if (strcasecmp(vp, "full") != 0) 96568559Seric { 96668559Seric usrerr("501 Bad argument \"%s\" to RET", vp); 96768559Seric /* NOTREACHED */ 96868559Seric } 96968559Seric } 97068559Seric else 97168559Seric { 97268559Seric usrerr("501 %s parameter unrecognized", kp); 97368559Seric /* NOTREACHED */ 97468559Seric } 97568559Seric } 97668559Seric /* 97767963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 97867963Seric ** 97967963Seric ** Parameters: 98067963Seric ** a -- the address corresponding to the To: parameter. 98167963Seric ** kp -- the parameter key. 98267963Seric ** vp -- the value of that parameter. 98367963Seric ** e -- the envelope. 98467963Seric ** 98567963Seric ** Returns: 98667963Seric ** none. 98767963Seric */ 98867963Seric 98967963Seric rcpt_esmtp_args(a, kp, vp, e) 99067963Seric ADDRESS *a; 99167963Seric char *kp; 99267963Seric char *vp; 99367963Seric ENVELOPE *e; 99467963Seric { 99567963Seric if (strcasecmp(kp, "notify") == 0) 99667963Seric { 99767963Seric char *p; 99867963Seric 99967963Seric if (vp == NULL) 100067963Seric { 100167963Seric usrerr("501 NOTIFY requires a value"); 100267963Seric /* NOTREACHED */ 100367963Seric } 100467963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 100568595Seric a->q_flags |= QHASNOTIFY; 100667963Seric if (strcasecmp(vp, "never") == 0) 100767963Seric return; 100867963Seric for (p = vp; p != NULL; vp = p) 100967963Seric { 101067963Seric p = strchr(p, ','); 101167963Seric if (p != NULL) 101267963Seric *p++ = '\0'; 101367963Seric if (strcasecmp(vp, "success") == 0) 101467963Seric a->q_flags |= QPINGONSUCCESS; 101567963Seric else if (strcasecmp(vp, "failure") == 0) 101667963Seric a->q_flags |= QPINGONFAILURE; 101767963Seric else if (strcasecmp(vp, "delay") == 0) 101867963Seric a->q_flags |= QPINGONDELAY; 101967963Seric else 102067963Seric { 102167963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 102267963Seric vp); 102367963Seric /* NOTREACHED */ 102467963Seric } 102567963Seric } 102667963Seric } 102767963Seric else if (strcasecmp(kp, "orcpt") == 0) 102867963Seric { 102967963Seric if (vp == NULL) 103067963Seric { 103167963Seric usrerr("501 ORCPT requires a value"); 103267963Seric /* NOTREACHED */ 103367963Seric } 103468583Seric if (!xtextok(vp)) 103568583Seric { 103668583Seric usrerr("501 Syntax error in ORCPT parameter value"); 103768583Seric /* NOTREACHED */ 103868583Seric } 103968583Seric if (a->q_orcpt != NULL) 104068583Seric { 104168583Seric usrerr("501 Duplicate ORCPT parameter"); 104268583Seric /* NOTREACHED */ 104368583Seric } 104467963Seric a->q_orcpt = newstr(vp); 104567963Seric } 104667963Seric else 104767963Seric { 104867963Seric usrerr("501 %s parameter unrecognized", kp); 104967963Seric /* NOTREACHED */ 105067963Seric } 105167963Seric } 105267963Seric /* 105358151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 105458151Seric ** 105558151Seric ** Parameters: 105658151Seric ** a -- the address to print 105758151Seric ** last -- set if this is the last one. 105858151Seric ** 105958151Seric ** Returns: 106058151Seric ** none. 106158151Seric ** 106258151Seric ** Side Effects: 106358151Seric ** Prints the appropriate 250 codes. 106458151Seric */ 106558151Seric 106658151Seric printvrfyaddr(a, last) 106758151Seric register ADDRESS *a; 106858151Seric bool last; 106958151Seric { 107058151Seric char fmtbuf[20]; 107158151Seric 107258151Seric strcpy(fmtbuf, "250"); 107358151Seric fmtbuf[3] = last ? ' ' : '-'; 107458151Seric 107559746Seric if (a->q_fullname == NULL) 107659746Seric { 107759746Seric if (strchr(a->q_user, '@') == NULL) 107859746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 107959746Seric else 108059746Seric strcpy(&fmtbuf[4], "<%s>"); 108159746Seric message(fmtbuf, a->q_user, MyHostName); 108259746Seric } 108358151Seric else 108458151Seric { 108559746Seric if (strchr(a->q_user, '@') == NULL) 108659746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 108759746Seric else 108859746Seric strcpy(&fmtbuf[4], "%s <%s>"); 108959746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 109058151Seric } 109158151Seric } 109258151Seric /* 10934577Seric ** HELP -- implement the HELP command. 10944577Seric ** 10954577Seric ** Parameters: 10964577Seric ** topic -- the topic we want help for. 10974577Seric ** 10984577Seric ** Returns: 10994577Seric ** none. 11004577Seric ** 11014577Seric ** Side Effects: 11024577Seric ** outputs the help file to message output. 11034577Seric */ 11044577Seric 11054577Seric help(topic) 11064577Seric char *topic; 11074577Seric { 11084577Seric register FILE *hf; 11094577Seric int len; 1110*68751Seric bool noinfo; 11114577Seric char buf[MAXLINE]; 1112*68751Seric extern char Version[]; 11134577Seric 1114*68751Seric 11158269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 11164577Seric { 11174577Seric /* no help */ 111811931Seric errno = 0; 1119*68751Seric message("502 Sendmail %s -- HELP not implemented", Version); 11204577Seric return; 11214577Seric } 11224577Seric 112349669Seric if (topic == NULL || *topic == '\0') 1124*68751Seric { 112549669Seric topic = "smtp"; 1126*68751Seric message("214-This is Sendmail version %s", Version); 1127*68751Seric noinfo = FALSE; 1128*68751Seric } 112949669Seric else 1130*68751Seric { 113149669Seric makelower(topic); 1132*68751Seric noinfo = TRUE; 1133*68751Seric } 113449669Seric 11354577Seric len = strlen(topic); 11364577Seric 11374577Seric while (fgets(buf, sizeof buf, hf) != NULL) 11384577Seric { 11394577Seric if (strncmp(buf, topic, len) == 0) 11404577Seric { 11414577Seric register char *p; 11424577Seric 114356795Seric p = strchr(buf, '\t'); 11444577Seric if (p == NULL) 11454577Seric p = buf; 11464577Seric else 11474577Seric p++; 11484577Seric fixcrlf(p, TRUE); 114958151Seric message("214-%s", p); 11504577Seric noinfo = FALSE; 11514577Seric } 11524577Seric } 11534577Seric 11544577Seric if (noinfo) 115558151Seric message("504 HELP topic unknown"); 11564577Seric else 115758151Seric message("214 End of HELP info"); 11584628Seric (void) fclose(hf); 11594577Seric } 11608544Seric /* 11619339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11629339Seric ** 11639339Seric ** Parameters: 11649339Seric ** label -- a string used in error messages 11659339Seric ** 11669339Seric ** Returns: 11679339Seric ** zero in the child 11689339Seric ** one in the parent 11699339Seric ** 11709339Seric ** Side Effects: 11719339Seric ** none. 11729339Seric */ 11738544Seric 117455012Seric runinchild(label, e) 11759339Seric char *label; 117655012Seric register ENVELOPE *e; 11779339Seric { 11789339Seric int childpid; 11799339Seric 118016158Seric if (!OneXact) 11819339Seric { 118216158Seric childpid = dofork(); 118316158Seric if (childpid < 0) 118416158Seric { 118516158Seric syserr("%s: cannot fork", label); 118616158Seric return (1); 118716158Seric } 118816158Seric if (childpid > 0) 118916158Seric { 119016158Seric auto int st; 11919339Seric 119216158Seric /* parent -- wait for child to complete */ 119361093Seric setproctitle("server %s child wait", CurHostName); 119416158Seric st = waitfor(childpid); 119516158Seric if (st == -1) 119616158Seric syserr("%s: lost child", label); 119764948Seric else if (!WIFEXITED(st)) 119864948Seric syserr("%s: died on signal %d", 119964948Seric label, st & 0177); 12009339Seric 120116158Seric /* if we exited on a QUIT command, complete the process */ 120266017Seric if (WEXITSTATUS(st) == EX_QUIT) 120366017Seric { 120466017Seric disconnect(1, e); 120516158Seric finis(); 120666017Seric } 12079339Seric 120816158Seric return (1); 120916158Seric } 121016158Seric else 121116158Seric { 121216158Seric /* child */ 121316158Seric InChild = TRUE; 121425050Seric QuickAbort = FALSE; 121555012Seric clearenvelope(e, FALSE); 121616158Seric } 12179339Seric } 121815256Seric 121916158Seric /* open alias database */ 122060537Seric initmaps(FALSE, e); 122116158Seric 122216158Seric return (0); 12239339Seric } 12249339Seric 122556795Seric # endif /* SMTP */ 1226