122712Sdist /* 268839Seric * Copyright (c) 1983, 1995 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*69106Seric static char sccsid[] = "@(#)srvrsmtp.c 8.71 (Berkeley) 04/29/95 (with SMTP)"; 1433731Sbostic #else 15*69106Seric static char sccsid[] = "@(#)srvrsmtp.c 8.71 (Berkeley) 04/29/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]; 12024943Seric extern ENVELOPE BlankEnvelope; 1214549Seric 12259066Seric if (fileno(OutChannel) != fileno(stdout)) 1237363Seric { 1247363Seric /* arrange for debugging output to go to remote host */ 12559066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1267363Seric } 12755012Seric settime(e); 12866005Seric peerhostname = RealHostName; 12966005Seric if (peerhostname == NULL) 13066005Seric peerhostname = "localhost"; 13166005Seric CurHostName = peerhostname; 13265017Seric CurSmtpClient = macvalue('_', e); 13365017Seric if (CurSmtpClient == NULL) 13466003Seric CurSmtpClient = CurHostName; 13565017Seric 13665017Seric setproctitle("server %s startup", CurSmtpClient); 13768529Seric expand("\201e", inp, sizeof inp, e); 13868769Seric 13968769Seric /* output the first line, inserting "ESMTP" as second word */ 14068769Seric p = strchr(inp, '\n'); 14168769Seric if (p != NULL) 14268769Seric *p++ = '\0'; 14368769Seric id = strchr(inp, ' '); 14468769Seric if (id == NULL) 14568769Seric id = &inp[strlen(inp)]; 14668769Seric cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; 14768769Seric message(cmd, id - inp, inp, id); 14868769Seric 14968769Seric /* output remaining lines */ 15068769Seric while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 15164496Seric { 15268769Seric *p++ = '\0'; 153*69106Seric if (isascii(*id) && isspace(*id)) 154*69106Seric id++; 15568769Seric message("220-%s", id); 15664496Seric } 15768769Seric if (id != NULL) 158*69106Seric { 159*69106Seric if (isascii(*id) && isspace(*id)) 160*69106Seric id++; 16168769Seric message("220 %s", id); 162*69106Seric } 16366745Seric 16458330Seric protocol = NULL; 16559016Seric sendinghost = macvalue('s', e); 16658082Seric gothello = FALSE; 16758330Seric gotmail = FALSE; 1684549Seric for (;;) 1694549Seric { 17012612Seric /* arrange for backout */ 17165751Seric if (setjmp(TopFrame) > 0) 17259058Seric { 17365751Seric /* if() nesting is necessary for Cray UNICOS */ 17465751Seric if (InChild) 17565751Seric { 17665751Seric QuickAbort = FALSE; 17765751Seric SuprErrs = TRUE; 17865751Seric finis(); 17965751Seric } 18059058Seric } 18112612Seric QuickAbort = FALSE; 18212612Seric HoldErrs = FALSE; 18351951Seric LogUsrErrs = FALSE; 18463843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 18512612Seric 1867356Seric /* setup for the read */ 18755012Seric e->e_to = NULL; 1884577Seric Errors = 0; 1897275Seric (void) fflush(stdout); 1907356Seric 1917356Seric /* read the input line */ 19261093Seric SmtpPhase = "server cmd read"; 19361093Seric setproctitle("server %s cmd read", CurHostName); 19461093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 19561093Seric SmtpPhase); 1967356Seric 1977685Seric /* handle errors */ 1987356Seric if (p == NULL) 1997356Seric { 2004549Seric /* end of file, just die */ 20166017Seric disconnect(1, e); 20258151Seric message("421 %s Lost input channel from %s", 20365017Seric MyHostName, CurSmtpClient); 20455464Seric #ifdef LOG 20563843Seric if (LogLevel > (gotmail ? 1 : 19)) 20655464Seric syslog(LOG_NOTICE, "lost input channel from %s", 20765017Seric CurSmtpClient); 20855464Seric #endif 20958069Seric if (InChild) 21058069Seric ExitStat = EX_QUIT; 2114549Seric finis(); 2124549Seric } 2134549Seric 2144549Seric /* clean up end of line */ 2154558Seric fixcrlf(inp, TRUE); 2164549Seric 2174713Seric /* echo command to transcript */ 21855012Seric if (e->e_xfp != NULL) 21955012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2204713Seric 22159060Seric if (e->e_id == NULL) 22265058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 22359060Seric else 22465058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 22559060Seric 2264549Seric /* break off command */ 22758050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2284549Seric continue; 22957232Seric cmd = cmdbuf; 23058050Seric while (*p != '\0' && 23158050Seric !(isascii(*p) && isspace(*p)) && 23258050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 23324981Seric *cmd++ = *p++; 23424981Seric *cmd = '\0'; 2354549Seric 23625691Seric /* throw away leading whitespace */ 23758050Seric while (isascii(*p) && isspace(*p)) 23825691Seric p++; 23925691Seric 2404549Seric /* decode command */ 2414549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2424549Seric { 24333725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2444549Seric break; 2454549Seric } 2464549Seric 24751954Seric /* reset errors */ 24851954Seric errno = 0; 24951954Seric 2504549Seric /* process command */ 2514549Seric switch (c->cmdcode) 2524549Seric { 2534976Seric case CMDHELO: /* hello -- introduce yourself */ 25458323Seric case CMDEHLO: /* extended hello */ 25558323Seric if (c->cmdcode == CMDEHLO) 25658323Seric { 25758323Seric protocol = "ESMTP"; 25861093Seric SmtpPhase = "server EHLO"; 25958323Seric } 26058323Seric else 26158323Seric { 26258323Seric protocol = "SMTP"; 26361093Seric SmtpPhase = "server HELO"; 26458323Seric } 26567445Seric 26667445Seric /* check for valid domain name (re 1123 5.2.5) */ 26767445Seric if (*p == '\0') 26867445Seric { 26967445Seric message("501 %s requires domain address", 27067445Seric cmdbuf); 27167445Seric break; 27267445Seric } 27367445Seric else 27467445Seric { 27567445Seric register char *q; 27667445Seric 27767445Seric for (q = p; *q != '\0'; q++) 27867445Seric { 27967445Seric if (!isascii(*q)) 28067445Seric break; 28167445Seric if (isalnum(*q)) 28267445Seric continue; 28367445Seric if (strchr("[].-_#", *q) == NULL) 28467445Seric break; 28567445Seric } 28667445Seric if (*q != '\0') 28767445Seric { 28867445Seric message("501 Invalid domain name"); 28967445Seric break; 29067445Seric } 29167445Seric } 29267445Seric 29359016Seric sendinghost = newstr(p); 29460210Seric gothello = TRUE; 29560210Seric if (c->cmdcode != CMDEHLO) 29660239Seric { 29760239Seric /* print old message and be done with it */ 29860239Seric message("250 %s Hello %s, pleased to meet you", 29965017Seric MyHostName, CurSmtpClient); 30060210Seric break; 30160239Seric } 30260239Seric 30360239Seric /* print extended message and brag */ 30460239Seric message("250-%s Hello %s, pleased to meet you", 30566760Seric MyHostName, CurSmtpClient); 30658323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 30758323Seric message("250-EXPN"); 30867417Seric message("250-8BITMIME"); 30964359Seric if (MaxMessageSize > 0) 31064359Seric message("250-SIZE %ld", MaxMessageSize); 31159271Seric else 31259271Seric message("250-SIZE"); 31368848Seric #if DSN 31468606Seric message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)"); 31568028Seric #endif 31658323Seric message("250 HELP"); 3174976Seric break; 3184976Seric 3194549Seric case CMDMAIL: /* mail -- designate sender */ 32061093Seric SmtpPhase = "server MAIL"; 32124943Seric 3229314Seric /* check for validity of this command */ 32358789Seric if (!gothello) 32458082Seric { 32558957Seric /* set sending host to our known value */ 32659016Seric if (sendinghost == NULL) 32766005Seric sendinghost = peerhostname; 32858957Seric 32958789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 33058821Seric { 33158789Seric message("503 Polite people say HELO first"); 33258821Seric break; 33358821Seric } 33458082Seric } 33558109Seric if (gotmail) 3364558Seric { 33758151Seric message("503 Sender already specified"); 33863843Seric if (InChild) 33963843Seric finis(); 3404558Seric break; 3414558Seric } 3429339Seric if (InChild) 3439339Seric { 34436230Skarels errno = 0; 34558151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34658069Seric finis(); 3479339Seric } 3489339Seric 3499339Seric /* fork a subprocess to process this command */ 35055012Seric if (runinchild("SMTP-MAIL", e) > 0) 3519339Seric break; 35263753Seric if (!gothello) 35363753Seric { 35463753Seric auth_warning(e, 35566005Seric "Host %s didn't use HELO protocol", 35666005Seric peerhostname); 35763753Seric } 35865947Seric #ifdef PICKY_HELO_CHECK 35966005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 36066005Seric (strcasecmp(peerhostname, "localhost") != 0 || 36165823Seric strcasecmp(sendinghost, MyHostName) != 0)) 36265823Seric { 36365823Seric auth_warning(e, "Host %s claimed to be %s", 36466005Seric peerhostname, sendinghost); 36565823Seric } 36665947Seric #endif 36765823Seric 36858323Seric if (protocol == NULL) 36958323Seric protocol = "SMTP"; 37058323Seric define('r', protocol, e); 37159016Seric define('s', sendinghost, e); 37255012Seric initsys(e); 37359747Seric nrcpts = 0; 37468582Seric e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; 37565058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3769339Seric 3779339Seric /* child -- go do the processing */ 3784549Seric p = skipword(p, "from"); 3794549Seric if (p == NULL) 3804549Seric break; 38157977Seric if (setjmp(TopFrame) > 0) 38258147Seric { 38358147Seric /* this failed -- undo work */ 38458147Seric if (InChild) 38559058Seric { 38659058Seric QuickAbort = FALSE; 38759058Seric SuprErrs = TRUE; 38863787Seric e->e_flags &= ~EF_FATALERRS; 38958147Seric finis(); 39059058Seric } 39157977Seric break; 39258147Seric } 39357977Seric QuickAbort = TRUE; 39458333Seric 39558333Seric /* must parse sender first */ 39658333Seric delimptr = NULL; 39758704Seric setsender(p, e, &delimptr, FALSE); 39858333Seric p = delimptr; 39958333Seric if (p != NULL && *p != '\0') 40058333Seric *p++ = '\0'; 40158333Seric 40266325Seric /* check for possible spoofing */ 40366325Seric if (RealUid != 0 && OpMode == MD_SMTP && 40467473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40567473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40666325Seric { 40766325Seric auth_warning(e, "%s owned process doing -bs", 40866325Seric RealUserName); 40966325Seric } 41066325Seric 41158333Seric /* now parse ESMTP arguments */ 41268560Seric e->e_msgsize = 0; 41366764Seric while (p != NULL && *p != '\0') 41458333Seric { 41558333Seric char *kp; 41666304Seric char *vp = NULL; 41758333Seric 41858333Seric /* locate the beginning of the keyword */ 41958333Seric while (isascii(*p) && isspace(*p)) 42058333Seric p++; 42158333Seric if (*p == '\0') 42258333Seric break; 42358333Seric kp = p; 42458333Seric 42558333Seric /* skip to the value portion */ 42658333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42758333Seric p++; 42858333Seric if (*p == '=') 42958333Seric { 43058333Seric *p++ = '\0'; 43158333Seric vp = p; 43258333Seric 43358333Seric /* skip to the end of the value */ 43458333Seric while (*p != '\0' && *p != ' ' && 43558333Seric !(isascii(*p) && iscntrl(*p)) && 43658333Seric *p != '=') 43758333Seric p++; 43858333Seric } 43958333Seric 44058333Seric if (*p != '\0') 44158333Seric *p++ = '\0'; 44258333Seric 44358333Seric if (tTd(19, 1)) 44466764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 44558333Seric vp == NULL ? "<null>" : vp); 44658333Seric 44768559Seric mail_esmtp_args(kp, vp, e); 44858333Seric } 44959284Seric 45068560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 45159284Seric { 45259284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 45359284Seric MaxMessageSize); 45459284Seric /* NOTREACHED */ 45559284Seric } 45658333Seric 45768560Seric if (!enoughspace(e->e_msgsize)) 45858333Seric { 45958333Seric message("452 Insufficient disk space; try again later"); 46058333Seric break; 46158333Seric } 46258151Seric message("250 Sender ok"); 46358147Seric gotmail = TRUE; 4644549Seric break; 4654549Seric 4664976Seric case CMDRCPT: /* rcpt -- designate recipient */ 46758850Seric if (!gotmail) 46858850Seric { 46958850Seric usrerr("503 Need MAIL before RCPT"); 47058850Seric break; 47158850Seric } 47261093Seric SmtpPhase = "server RCPT"; 47312612Seric if (setjmp(TopFrame) > 0) 47414785Seric { 47555012Seric e->e_flags &= ~EF_FATALERRS; 47612612Seric break; 47714785Seric } 47812612Seric QuickAbort = TRUE; 47951951Seric LogUsrErrs = TRUE; 48058093Seric 48159699Seric if (e->e_sendmode != SM_DELIVER) 48259699Seric e->e_flags |= EF_VRFYONLY; 48358919Seric 4844549Seric p = skipword(p, "to"); 4854549Seric if (p == NULL) 4864549Seric break; 48767880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 48812612Seric if (a == NULL) 48912612Seric break; 49067880Seric p = delimptr; 49167880Seric 49267880Seric /* now parse ESMTP arguments */ 49367880Seric while (p != NULL && *p != '\0') 49467880Seric { 49567880Seric char *kp; 49667880Seric char *vp = NULL; 49767880Seric 49867880Seric /* locate the beginning of the keyword */ 49967880Seric while (isascii(*p) && isspace(*p)) 50067880Seric p++; 50167880Seric if (*p == '\0') 50267880Seric break; 50367880Seric kp = p; 50467880Seric 50567880Seric /* skip to the value portion */ 50667880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 50767880Seric p++; 50867880Seric if (*p == '=') 50967880Seric { 51067880Seric *p++ = '\0'; 51167880Seric vp = p; 51267880Seric 51367880Seric /* skip to the end of the value */ 51467880Seric while (*p != '\0' && *p != ' ' && 51567880Seric !(isascii(*p) && iscntrl(*p)) && 51667880Seric *p != '=') 51767880Seric p++; 51867880Seric } 51967880Seric 52067880Seric if (*p != '\0') 52167880Seric *p++ = '\0'; 52267880Seric 52367880Seric if (tTd(19, 1)) 52467880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 52567880Seric vp == NULL ? "<null>" : vp); 52667880Seric 52767963Seric rcpt_esmtp_args(a, kp, vp, e); 52867963Seric 52967880Seric } 53067980Seric 53167980Seric /* save in recipient list after ESMTP mods */ 53267982Seric a = recipient(a, &e->e_sendqueue, 0, e); 53367980Seric 53412612Seric if (Errors != 0) 53512612Seric break; 53612612Seric 53712612Seric /* no errors during parsing, but might be a duplicate */ 53855012Seric e->e_to = p; 53912612Seric if (!bitset(QBADADDR, a->q_flags)) 54059747Seric { 54164718Seric message("250 Recipient ok%s", 54264718Seric bitset(QQUEUEUP, a->q_flags) ? 54364718Seric " (will queue)" : ""); 54459747Seric nrcpts++; 54559747Seric } 54612612Seric else 5474549Seric { 54812612Seric /* punt -- should keep message in ADDRESS.... */ 54958151Seric message("550 Addressee unknown"); 5504549Seric } 55155012Seric e->e_to = NULL; 5524549Seric break; 5534549Seric 5544549Seric case CMDDATA: /* data -- text of mail */ 55561093Seric SmtpPhase = "server DATA"; 55658109Seric if (!gotmail) 5574549Seric { 55858151Seric message("503 Need MAIL command"); 5594976Seric break; 5604549Seric } 56164718Seric else if (nrcpts <= 0) 5624549Seric { 56358151Seric message("503 Need RCPT (recipient)"); 5644976Seric break; 5654549Seric } 5664976Seric 56758929Seric /* check to see if we need to re-expand aliases */ 56863787Seric /* also reset QBADADDR on already-diagnosted addrs */ 56963787Seric doublequeue = FALSE; 57058929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 57158929Seric { 57258929Seric if (bitset(QVERIFIED, a->q_flags)) 57363787Seric { 57463787Seric /* need to re-expand aliases */ 57563787Seric doublequeue = TRUE; 57663787Seric } 57763787Seric if (bitset(QBADADDR, a->q_flags)) 57863787Seric { 57963787Seric /* make this "go away" */ 58063787Seric a->q_flags |= QDONTSEND; 58163787Seric a->q_flags &= ~QBADADDR; 58263787Seric } 58358929Seric } 58458929Seric 5854976Seric /* collect the text of the message */ 58624943Seric SmtpPhase = "collect"; 58768692Seric buffer_errors(); 58867546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 58968692Seric flush_errors(TRUE); 59064766Seric if (Errors != 0) 59164766Seric goto abortmessage; 59267131Seric 59368582Seric /* make sure we actually do delivery */ 59468582Seric e->e_flags &= ~EF_CLRQUEUE; 59568582Seric 59667131Seric /* from now on, we have to operate silently */ 59768692Seric buffer_errors(); 59867131Seric e->e_errormode = EM_MAIL; 5994976Seric 6008238Seric /* 6018238Seric ** Arrange to send to everyone. 6028238Seric ** If sending to multiple people, mail back 6038238Seric ** errors rather than reporting directly. 6048238Seric ** In any case, don't mail back errors for 6058238Seric ** anything that has happened up to 6068238Seric ** now (the other end will do this). 60710197Seric ** Truncate our transcript -- the mail has gotten 60810197Seric ** to us successfully, and if we have 60910197Seric ** to mail this back, it will be easier 61010197Seric ** on the reader. 6118238Seric ** Then send to everyone. 6128238Seric ** Finally give a reply code. If an error has 6138238Seric ** already been given, don't mail a 6148238Seric ** message back. 6159339Seric ** We goose error returns by clearing error bit. 6168238Seric */ 6178238Seric 61824943Seric SmtpPhase = "delivery"; 61955012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 62058714Seric id = e->e_id; 6214976Seric 62267131Seric if (doublequeue) 62359730Seric { 62467131Seric /* make sure it is in the queue */ 62567131Seric queueup(e, TRUE, FALSE); 62668035Seric if (e->e_sendmode == SM_QUEUE) 62768035Seric e->e_flags |= EF_KEEPQUEUE; 62859730Seric } 6298238Seric else 63058919Seric { 63167131Seric /* send to all recipients */ 63267131Seric sendall(e, SM_DEFAULT); 63367131Seric } 63467131Seric e->e_to = NULL; 63559747Seric 63667131Seric /* issue success message */ 63767131Seric message("250 %s Message accepted for delivery", id); 63864296Seric 63967131Seric /* if we just queued, poke it */ 64067131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 64167131Seric { 64267131Seric extern pid_t dowork(); 64367131Seric 64467131Seric unlockqueue(e); 64567131Seric (void) dowork(id, TRUE, TRUE, e); 64658919Seric } 64758883Seric 64859747Seric abortmessage: 6499339Seric /* if in a child, pop back to our parent */ 6509339Seric if (InChild) 6519339Seric finis(); 65224943Seric 65324943Seric /* clean up a bit */ 65458109Seric gotmail = FALSE; 65555012Seric dropenvelope(e); 65658179Seric CurEnv = e = newenvelope(e, CurEnv); 65755012Seric e->e_flags = BlankEnvelope.e_flags; 6584549Seric break; 6594549Seric 6604549Seric case CMDRSET: /* rset -- reset state */ 66158151Seric message("250 Reset state"); 66268603Seric 66368603Seric /* arrange to ignore any current send list */ 66468603Seric e->e_sendqueue = NULL; 66564359Seric e->e_flags |= EF_CLRQUEUE; 6669339Seric if (InChild) 6679339Seric finis(); 66858109Seric 66958109Seric /* clean up a bit */ 67058109Seric gotmail = FALSE; 67158109Seric dropenvelope(e); 67258179Seric CurEnv = e = newenvelope(e, CurEnv); 6739339Seric break; 6744549Seric 6754549Seric case CMDVRFY: /* vrfy -- verify address */ 67658092Seric case CMDEXPN: /* expn -- expand address */ 67758092Seric vrfy = c->cmdcode == CMDVRFY; 67858092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 67958092Seric PrivacyFlags)) 68058082Seric { 68158412Seric if (vrfy) 68267160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 68358412Seric else 68465192Seric message("502 Sorry, we do not allow this operation"); 68565017Seric #ifdef LOG 68665017Seric if (LogLevel > 5) 68765017Seric syslog(LOG_INFO, "%s: %s [rejected]", 68865017Seric CurSmtpClient, inp); 68965017Seric #endif 69058082Seric break; 69158082Seric } 69258082Seric else if (!gothello && 69358092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 69458092Seric PrivacyFlags)) 69558082Seric { 69658151Seric message("503 I demand that you introduce yourself first"); 69758082Seric break; 69858082Seric } 69958092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7009339Seric break; 70155173Seric #ifdef LOG 70258020Seric if (LogLevel > 5) 70365017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 70455173Seric #endif 7055003Seric vrfyqueue = NULL; 7067762Seric QuickAbort = TRUE; 70758092Seric if (vrfy) 70858092Seric e->e_flags |= EF_VRFYONLY; 70962373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 71067615Seric p++; 71162373Seric if (*p == '\0') 71262373Seric { 71362373Seric message("501 Argument required"); 71462373Seric Errors++; 71562373Seric } 71662373Seric else 71762373Seric { 71867990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 71962373Seric } 7207762Seric if (Errors != 0) 7219339Seric { 7229339Seric if (InChild) 7239339Seric finis(); 7247762Seric break; 7259339Seric } 72662373Seric if (vrfyqueue == NULL) 72762373Seric { 72862373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 72962373Seric } 7305003Seric while (vrfyqueue != NULL) 7315003Seric { 73263971Seric a = vrfyqueue; 73363971Seric while ((a = a->q_next) != NULL && 73463971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 73563971Seric continue; 7367685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 73758151Seric printvrfyaddr(vrfyqueue, a == NULL); 73863847Seric vrfyqueue = vrfyqueue->q_next; 7395003Seric } 7409339Seric if (InChild) 7419339Seric finis(); 7424549Seric break; 7434549Seric 7444549Seric case CMDHELP: /* help -- give user info */ 7454577Seric help(p); 7464549Seric break; 7474549Seric 7484549Seric case CMDNOOP: /* noop -- do nothing */ 74964122Seric message("250 OK"); 7504549Seric break; 7514549Seric 7524549Seric case CMDQUIT: /* quit -- leave mail */ 75358151Seric message("221 %s closing connection", MyHostName); 75461051Seric 75566283Seric doquit: 75668603Seric /* arrange to ignore any current send list */ 75768603Seric e->e_sendqueue = NULL; 75868603Seric 75961051Seric /* avoid future 050 messages */ 76066017Seric disconnect(1, e); 76161051Seric 7629339Seric if (InChild) 7639339Seric ExitStat = EX_QUIT; 7644549Seric finis(); 7654549Seric 7668544Seric case CMDVERB: /* set verbose mode */ 76759957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 76859957Seric { 76959957Seric /* this would give out the same info */ 77059957Seric message("502 Verbose unavailable"); 77159957Seric break; 77259957Seric } 7738544Seric Verbose = TRUE; 77458734Seric e->e_sendmode = SM_DELIVER; 77559957Seric message("250 Verbose mode"); 7768544Seric break; 7778544Seric 7789314Seric case CMDONEX: /* doing one transaction only */ 7799378Seric OneXact = TRUE; 78059957Seric message("250 Only one transaction"); 7819314Seric break; 7829314Seric 78336230Skarels # ifdef SMTPDEBUG 7849339Seric case CMDDBGQSHOW: /* show queues */ 7856907Seric printf("Send Queue="); 78655012Seric printaddr(e->e_sendqueue, TRUE); 7875003Seric break; 7887275Seric 7897275Seric case CMDDBGDEBUG: /* set debug mode */ 7907676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7917676Seric tTflag(p); 79258151Seric message("200 Debug set"); 7937275Seric break; 7947275Seric 79536230Skarels # else /* not SMTPDEBUG */ 79636230Skarels case CMDDBGQSHOW: /* show queues */ 79736230Skarels case CMDDBGDEBUG: /* set debug mode */ 79864685Seric # endif /* SMTPDEBUG */ 79964685Seric case CMDLOGBOGUS: /* bogus command */ 80036233Skarels # ifdef LOG 80158308Seric if (LogLevel > 0) 80264685Seric syslog(LOG_CRIT, 80358020Seric "\"%s\" command from %s (%s)", 80466005Seric c->cmdname, peerhostname, 80558755Seric anynet_ntoa(&RealHostAddr)); 80636233Skarels # endif 80736230Skarels /* FALL THROUGH */ 80836230Skarels 8094549Seric case CMDERROR: /* unknown command */ 81066283Seric if (++badcommands > MAXBADCOMMANDS) 81166283Seric { 81266283Seric message("421 %s Too many bad commands; closing connection", 81366283Seric MyHostName); 81466283Seric goto doquit; 81566283Seric } 81666283Seric 81758151Seric message("500 Command unrecognized"); 8184549Seric break; 8194549Seric 8204549Seric default: 82136230Skarels errno = 0; 82258151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8234549Seric break; 8244549Seric } 8254549Seric } 8264549Seric } 8274549Seric /* 8284549Seric ** SKIPWORD -- skip a fixed word. 8294549Seric ** 8304549Seric ** Parameters: 8314549Seric ** p -- place to start looking. 8324549Seric ** w -- word to skip. 8334549Seric ** 8344549Seric ** Returns: 8354549Seric ** p following w. 8364549Seric ** NULL on error. 8374549Seric ** 8384549Seric ** Side Effects: 8394549Seric ** clobbers the p data area. 8404549Seric */ 8414549Seric 8424549Seric static char * 8434549Seric skipword(p, w) 8444549Seric register char *p; 8454549Seric char *w; 8464549Seric { 8474549Seric register char *q; 84866005Seric char *firstp = p; 8494549Seric 8504549Seric /* find beginning of word */ 85158050Seric while (isascii(*p) && isspace(*p)) 8524549Seric p++; 8534549Seric q = p; 8544549Seric 8554549Seric /* find end of word */ 85658050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8574549Seric p++; 85858050Seric while (isascii(*p) && isspace(*p)) 8594549Seric *p++ = '\0'; 8604549Seric if (*p != ':') 8614549Seric { 8624549Seric syntax: 86366005Seric message("501 Syntax error in parameters scanning \"%s\"", 86466005Seric firstp); 8654549Seric Errors++; 8664549Seric return (NULL); 8674549Seric } 8684549Seric *p++ = '\0'; 86958050Seric while (isascii(*p) && isspace(*p)) 8704549Seric p++; 8714549Seric 87262373Seric if (*p == '\0') 87362373Seric goto syntax; 87462373Seric 8754549Seric /* see if the input word matches desired word */ 87633725Sbostic if (strcasecmp(q, w)) 8774549Seric goto syntax; 8784549Seric 8794549Seric return (p); 8804549Seric } 8814577Seric /* 88268559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 88368559Seric ** 88468559Seric ** Parameters: 88568559Seric ** kp -- the parameter key. 88668559Seric ** vp -- the value of that parameter. 88768559Seric ** e -- the envelope. 88868559Seric ** 88968559Seric ** Returns: 89068559Seric ** none. 89168559Seric */ 89268559Seric 89368559Seric mail_esmtp_args(kp, vp, e) 89468559Seric char *kp; 89568559Seric char *vp; 89668559Seric ENVELOPE *e; 89768559Seric { 89868559Seric if (strcasecmp(kp, "size") == 0) 89968559Seric { 90068559Seric if (vp == NULL) 90168559Seric { 90268559Seric usrerr("501 SIZE requires a value"); 90368559Seric /* NOTREACHED */ 90468559Seric } 90568890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) 90668560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10); 90768559Seric # else 90868560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10); 90968559Seric # endif 91068559Seric } 91168559Seric else if (strcasecmp(kp, "body") == 0) 91268559Seric { 91368559Seric if (vp == NULL) 91468559Seric { 91568559Seric usrerr("501 BODY requires a value"); 91668559Seric /* NOTREACHED */ 91768559Seric } 91868559Seric if (strcasecmp(vp, "8bitmime") == 0) 91968559Seric { 92068559Seric SevenBitInput = FALSE; 92168883Seric e->e_flags |= EF_NL_NOT_EOL; 92268559Seric } 92368559Seric else if (strcasecmp(vp, "7bit") == 0) 92468559Seric { 92568559Seric SevenBitInput = TRUE; 92668559Seric } 92768559Seric else 92868559Seric { 92968559Seric usrerr("501 Unknown BODY type %s", 93068559Seric vp); 93168559Seric /* NOTREACHED */ 93268559Seric } 93368559Seric e->e_bodytype = newstr(vp); 93468559Seric } 93568559Seric else if (strcasecmp(kp, "envid") == 0) 93668559Seric { 93768559Seric if (vp == NULL) 93868559Seric { 93968559Seric usrerr("501 ENVID requires a value"); 94068559Seric /* NOTREACHED */ 94168559Seric } 94268583Seric if (!xtextok(vp)) 94368583Seric { 94468583Seric usrerr("501 Syntax error in ENVID parameter value"); 94568583Seric /* NOTREACHED */ 94668583Seric } 94768583Seric if (e->e_envid != NULL) 94868583Seric { 94968583Seric usrerr("501 Duplicate ENVID parameter"); 95068583Seric /* NOTREACHED */ 95168583Seric } 95268559Seric e->e_envid = newstr(vp); 95368559Seric } 95468559Seric else if (strcasecmp(kp, "ret") == 0) 95568559Seric { 95668559Seric if (vp == NULL) 95768559Seric { 95868559Seric usrerr("501 RET requires a value"); 95968559Seric /* NOTREACHED */ 96068559Seric } 96168583Seric if (bitset(EF_RET_PARAM, e->e_flags)) 96268583Seric { 96368583Seric usrerr("501 Duplicate RET parameter"); 96468583Seric /* NOTREACHED */ 96568583Seric } 96668559Seric e->e_flags |= EF_RET_PARAM; 96768559Seric if (strcasecmp(vp, "hdrs") == 0) 96868559Seric e->e_flags |= EF_NO_BODY_RETN; 96968559Seric else if (strcasecmp(vp, "full") != 0) 97068559Seric { 97168559Seric usrerr("501 Bad argument \"%s\" to RET", vp); 97268559Seric /* NOTREACHED */ 97368559Seric } 97468559Seric } 97568559Seric else 97668559Seric { 97768559Seric usrerr("501 %s parameter unrecognized", kp); 97868559Seric /* NOTREACHED */ 97968559Seric } 98068559Seric } 98168559Seric /* 98267963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 98367963Seric ** 98467963Seric ** Parameters: 98567963Seric ** a -- the address corresponding to the To: parameter. 98667963Seric ** kp -- the parameter key. 98767963Seric ** vp -- the value of that parameter. 98867963Seric ** e -- the envelope. 98967963Seric ** 99067963Seric ** Returns: 99167963Seric ** none. 99267963Seric */ 99367963Seric 99467963Seric rcpt_esmtp_args(a, kp, vp, e) 99567963Seric ADDRESS *a; 99667963Seric char *kp; 99767963Seric char *vp; 99867963Seric ENVELOPE *e; 99967963Seric { 100067963Seric if (strcasecmp(kp, "notify") == 0) 100167963Seric { 100267963Seric char *p; 100367963Seric 100467963Seric if (vp == NULL) 100567963Seric { 100667963Seric usrerr("501 NOTIFY requires a value"); 100767963Seric /* NOTREACHED */ 100867963Seric } 100967963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 101068595Seric a->q_flags |= QHASNOTIFY; 101167963Seric if (strcasecmp(vp, "never") == 0) 101267963Seric return; 101367963Seric for (p = vp; p != NULL; vp = p) 101467963Seric { 101567963Seric p = strchr(p, ','); 101667963Seric if (p != NULL) 101767963Seric *p++ = '\0'; 101867963Seric if (strcasecmp(vp, "success") == 0) 101967963Seric a->q_flags |= QPINGONSUCCESS; 102067963Seric else if (strcasecmp(vp, "failure") == 0) 102167963Seric a->q_flags |= QPINGONFAILURE; 102267963Seric else if (strcasecmp(vp, "delay") == 0) 102367963Seric a->q_flags |= QPINGONDELAY; 102467963Seric else 102567963Seric { 102667963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 102767963Seric vp); 102867963Seric /* NOTREACHED */ 102967963Seric } 103067963Seric } 103167963Seric } 103267963Seric else if (strcasecmp(kp, "orcpt") == 0) 103367963Seric { 103467963Seric if (vp == NULL) 103567963Seric { 103667963Seric usrerr("501 ORCPT requires a value"); 103767963Seric /* NOTREACHED */ 103867963Seric } 103968583Seric if (!xtextok(vp)) 104068583Seric { 104168583Seric usrerr("501 Syntax error in ORCPT parameter value"); 104268583Seric /* NOTREACHED */ 104368583Seric } 104468583Seric if (a->q_orcpt != NULL) 104568583Seric { 104668583Seric usrerr("501 Duplicate ORCPT parameter"); 104768583Seric /* NOTREACHED */ 104868583Seric } 104967963Seric a->q_orcpt = newstr(vp); 105067963Seric } 105167963Seric else 105267963Seric { 105367963Seric usrerr("501 %s parameter unrecognized", kp); 105467963Seric /* NOTREACHED */ 105567963Seric } 105667963Seric } 105767963Seric /* 105858151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 105958151Seric ** 106058151Seric ** Parameters: 106158151Seric ** a -- the address to print 106258151Seric ** last -- set if this is the last one. 106358151Seric ** 106458151Seric ** Returns: 106558151Seric ** none. 106658151Seric ** 106758151Seric ** Side Effects: 106858151Seric ** Prints the appropriate 250 codes. 106958151Seric */ 107058151Seric 107158151Seric printvrfyaddr(a, last) 107258151Seric register ADDRESS *a; 107358151Seric bool last; 107458151Seric { 107558151Seric char fmtbuf[20]; 107658151Seric 107758151Seric strcpy(fmtbuf, "250"); 107858151Seric fmtbuf[3] = last ? ' ' : '-'; 107958151Seric 108059746Seric if (a->q_fullname == NULL) 108159746Seric { 108259746Seric if (strchr(a->q_user, '@') == NULL) 108359746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 108459746Seric else 108559746Seric strcpy(&fmtbuf[4], "<%s>"); 108659746Seric message(fmtbuf, a->q_user, MyHostName); 108759746Seric } 108858151Seric else 108958151Seric { 109059746Seric if (strchr(a->q_user, '@') == NULL) 109159746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 109259746Seric else 109359746Seric strcpy(&fmtbuf[4], "%s <%s>"); 109459746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 109558151Seric } 109658151Seric } 109758151Seric /* 10984577Seric ** HELP -- implement the HELP command. 10994577Seric ** 11004577Seric ** Parameters: 11014577Seric ** topic -- the topic we want help for. 11024577Seric ** 11034577Seric ** Returns: 11044577Seric ** none. 11054577Seric ** 11064577Seric ** Side Effects: 11074577Seric ** outputs the help file to message output. 11084577Seric */ 11094577Seric 11104577Seric help(topic) 11114577Seric char *topic; 11124577Seric { 11134577Seric register FILE *hf; 11144577Seric int len; 111568751Seric bool noinfo; 11164577Seric char buf[MAXLINE]; 111768751Seric extern char Version[]; 11184577Seric 111968751Seric 11208269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 11214577Seric { 11224577Seric /* no help */ 112311931Seric errno = 0; 112468751Seric message("502 Sendmail %s -- HELP not implemented", Version); 11254577Seric return; 11264577Seric } 11274577Seric 112849669Seric if (topic == NULL || *topic == '\0') 112968751Seric { 113049669Seric topic = "smtp"; 113168751Seric message("214-This is Sendmail version %s", Version); 113268751Seric noinfo = FALSE; 113368751Seric } 113449669Seric else 113568751Seric { 113649669Seric makelower(topic); 113768751Seric noinfo = TRUE; 113868751Seric } 113949669Seric 11404577Seric len = strlen(topic); 11414577Seric 11424577Seric while (fgets(buf, sizeof buf, hf) != NULL) 11434577Seric { 11444577Seric if (strncmp(buf, topic, len) == 0) 11454577Seric { 11464577Seric register char *p; 11474577Seric 114856795Seric p = strchr(buf, '\t'); 11494577Seric if (p == NULL) 11504577Seric p = buf; 11514577Seric else 11524577Seric p++; 11534577Seric fixcrlf(p, TRUE); 115458151Seric message("214-%s", p); 11554577Seric noinfo = FALSE; 11564577Seric } 11574577Seric } 11584577Seric 11594577Seric if (noinfo) 116058151Seric message("504 HELP topic unknown"); 11614577Seric else 116258151Seric message("214 End of HELP info"); 11634628Seric (void) fclose(hf); 11644577Seric } 11658544Seric /* 11669339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11679339Seric ** 11689339Seric ** Parameters: 11699339Seric ** label -- a string used in error messages 11709339Seric ** 11719339Seric ** Returns: 11729339Seric ** zero in the child 11739339Seric ** one in the parent 11749339Seric ** 11759339Seric ** Side Effects: 11769339Seric ** none. 11779339Seric */ 11788544Seric 117955012Seric runinchild(label, e) 11809339Seric char *label; 118155012Seric register ENVELOPE *e; 11829339Seric { 11839339Seric int childpid; 11849339Seric 118516158Seric if (!OneXact) 11869339Seric { 118716158Seric childpid = dofork(); 118816158Seric if (childpid < 0) 118916158Seric { 119016158Seric syserr("%s: cannot fork", label); 119116158Seric return (1); 119216158Seric } 119316158Seric if (childpid > 0) 119416158Seric { 119516158Seric auto int st; 11969339Seric 119716158Seric /* parent -- wait for child to complete */ 119861093Seric setproctitle("server %s child wait", CurHostName); 119916158Seric st = waitfor(childpid); 120016158Seric if (st == -1) 120116158Seric syserr("%s: lost child", label); 120264948Seric else if (!WIFEXITED(st)) 120364948Seric syserr("%s: died on signal %d", 120464948Seric label, st & 0177); 12059339Seric 120616158Seric /* if we exited on a QUIT command, complete the process */ 120766017Seric if (WEXITSTATUS(st) == EX_QUIT) 120866017Seric { 120966017Seric disconnect(1, e); 121016158Seric finis(); 121166017Seric } 12129339Seric 121316158Seric return (1); 121416158Seric } 121516158Seric else 121616158Seric { 121716158Seric /* child */ 121816158Seric InChild = TRUE; 121925050Seric QuickAbort = FALSE; 122055012Seric clearenvelope(e, FALSE); 122116158Seric } 12229339Seric } 122315256Seric 122416158Seric /* open alias database */ 122560537Seric initmaps(FALSE, e); 122616158Seric 122716158Seric return (0); 12289339Seric } 12299339Seric 123056795Seric # endif /* SMTP */ 1231