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*69748Seric static char sccsid[] = "@(#)srvrsmtp.c 8.75 (Berkeley) 05/28/95 (with SMTP)"; 1433731Sbostic #else 15*69748Seric static char sccsid[] = "@(#)srvrsmtp.c 8.75 (Berkeley) 05/28/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 99*69748Seric void 10055012Seric smtp(e) 10155012Seric register ENVELOPE *e; 1024549Seric { 1034549Seric register char *p; 1048544Seric register struct cmd *c; 1054549Seric char *cmd; 1065003Seric auto ADDRESS *vrfyqueue; 10712612Seric ADDRESS *a; 10858109Seric bool gotmail; /* mail command received */ 10958092Seric bool gothello; /* helo command received */ 11058092Seric bool vrfy; /* set if this is a vrfy command */ 11158323Seric char *protocol; /* sending protocol */ 11259016Seric char *sendinghost; /* sending hostname */ 11366005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11458333Seric auto char *delimptr; 11558714Seric char *id; 11668433Seric int nrcpts = 0; /* number of RCPT commands */ 11763787Seric bool doublequeue; 11866283Seric int badcommands = 0; /* count of bad commands */ 1198544Seric char inp[MAXLINE]; 12057232Seric char cmdbuf[MAXLINE]; 12124943Seric extern ENVELOPE BlankEnvelope; 122*69748Seric extern void help __P((char *)); 1234549Seric 12459066Seric if (fileno(OutChannel) != fileno(stdout)) 1257363Seric { 1267363Seric /* arrange for debugging output to go to remote host */ 12759066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1287363Seric } 12955012Seric settime(e); 13066005Seric peerhostname = RealHostName; 13166005Seric if (peerhostname == NULL) 13266005Seric peerhostname = "localhost"; 13366005Seric CurHostName = peerhostname; 13465017Seric CurSmtpClient = macvalue('_', e); 13565017Seric if (CurSmtpClient == NULL) 13666003Seric CurSmtpClient = CurHostName; 13765017Seric 13865017Seric setproctitle("server %s startup", CurSmtpClient); 13968529Seric expand("\201e", inp, sizeof inp, e); 14068769Seric 14168769Seric /* output the first line, inserting "ESMTP" as second word */ 14268769Seric p = strchr(inp, '\n'); 14368769Seric if (p != NULL) 14468769Seric *p++ = '\0'; 14568769Seric id = strchr(inp, ' '); 14668769Seric if (id == NULL) 14768769Seric id = &inp[strlen(inp)]; 14868769Seric cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; 14968769Seric message(cmd, id - inp, inp, id); 15068769Seric 15168769Seric /* output remaining lines */ 15268769Seric while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 15364496Seric { 15468769Seric *p++ = '\0'; 15569106Seric if (isascii(*id) && isspace(*id)) 15669106Seric id++; 15768769Seric message("220-%s", id); 15864496Seric } 15968769Seric if (id != NULL) 16069106Seric { 16169106Seric if (isascii(*id) && isspace(*id)) 16269106Seric id++; 16368769Seric message("220 %s", id); 16469106Seric } 16566745Seric 16658330Seric protocol = NULL; 16759016Seric sendinghost = macvalue('s', e); 16858082Seric gothello = FALSE; 16958330Seric gotmail = FALSE; 1704549Seric for (;;) 1714549Seric { 17212612Seric /* arrange for backout */ 17365751Seric if (setjmp(TopFrame) > 0) 17459058Seric { 17565751Seric /* if() nesting is necessary for Cray UNICOS */ 17665751Seric if (InChild) 17765751Seric { 17865751Seric QuickAbort = FALSE; 17965751Seric SuprErrs = TRUE; 18065751Seric finis(); 18165751Seric } 18259058Seric } 18312612Seric QuickAbort = FALSE; 18412612Seric HoldErrs = FALSE; 18551951Seric LogUsrErrs = FALSE; 18663843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 18712612Seric 1887356Seric /* setup for the read */ 18955012Seric e->e_to = NULL; 1904577Seric Errors = 0; 1917275Seric (void) fflush(stdout); 1927356Seric 1937356Seric /* read the input line */ 19461093Seric SmtpPhase = "server cmd read"; 19561093Seric setproctitle("server %s cmd read", CurHostName); 19661093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 19761093Seric SmtpPhase); 1987356Seric 1997685Seric /* handle errors */ 2007356Seric if (p == NULL) 2017356Seric { 2024549Seric /* end of file, just die */ 20366017Seric disconnect(1, e); 20458151Seric message("421 %s Lost input channel from %s", 20565017Seric MyHostName, CurSmtpClient); 20655464Seric #ifdef LOG 20763843Seric if (LogLevel > (gotmail ? 1 : 19)) 20855464Seric syslog(LOG_NOTICE, "lost input channel from %s", 20965017Seric CurSmtpClient); 21055464Seric #endif 21158069Seric if (InChild) 21258069Seric ExitStat = EX_QUIT; 2134549Seric finis(); 2144549Seric } 2154549Seric 2164549Seric /* clean up end of line */ 2174558Seric fixcrlf(inp, TRUE); 2184549Seric 2194713Seric /* echo command to transcript */ 22055012Seric if (e->e_xfp != NULL) 22155012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2224713Seric 22359060Seric if (e->e_id == NULL) 22465058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 22559060Seric else 22665058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 22759060Seric 2284549Seric /* break off command */ 22958050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2304549Seric continue; 23157232Seric cmd = cmdbuf; 23258050Seric while (*p != '\0' && 23358050Seric !(isascii(*p) && isspace(*p)) && 23458050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 23524981Seric *cmd++ = *p++; 23624981Seric *cmd = '\0'; 2374549Seric 23825691Seric /* throw away leading whitespace */ 23958050Seric while (isascii(*p) && isspace(*p)) 24025691Seric p++; 24125691Seric 2424549Seric /* decode command */ 2434549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2444549Seric { 24533725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2464549Seric break; 2474549Seric } 2484549Seric 24951954Seric /* reset errors */ 25051954Seric errno = 0; 25151954Seric 2524549Seric /* process command */ 2534549Seric switch (c->cmdcode) 2544549Seric { 2554976Seric case CMDHELO: /* hello -- introduce yourself */ 25658323Seric case CMDEHLO: /* extended hello */ 25758323Seric if (c->cmdcode == CMDEHLO) 25858323Seric { 25958323Seric protocol = "ESMTP"; 26061093Seric SmtpPhase = "server EHLO"; 26158323Seric } 26258323Seric else 26358323Seric { 26458323Seric protocol = "SMTP"; 26561093Seric SmtpPhase = "server HELO"; 26658323Seric } 26767445Seric 26867445Seric /* check for valid domain name (re 1123 5.2.5) */ 26967445Seric if (*p == '\0') 27067445Seric { 27167445Seric message("501 %s requires domain address", 27267445Seric cmdbuf); 27367445Seric break; 27467445Seric } 27567445Seric else 27667445Seric { 27767445Seric register char *q; 27867445Seric 27967445Seric for (q = p; *q != '\0'; q++) 28067445Seric { 28167445Seric if (!isascii(*q)) 28267445Seric break; 28367445Seric if (isalnum(*q)) 28467445Seric continue; 28567445Seric if (strchr("[].-_#", *q) == NULL) 28667445Seric break; 28767445Seric } 28867445Seric if (*q != '\0') 28967445Seric { 29067445Seric message("501 Invalid domain name"); 29167445Seric break; 29267445Seric } 29367445Seric } 29467445Seric 29559016Seric sendinghost = newstr(p); 29660210Seric gothello = TRUE; 29760210Seric if (c->cmdcode != CMDEHLO) 29860239Seric { 29960239Seric /* print old message and be done with it */ 30060239Seric message("250 %s Hello %s, pleased to meet you", 30165017Seric MyHostName, CurSmtpClient); 30260210Seric break; 30360239Seric } 30460239Seric 30560239Seric /* print extended message and brag */ 30660239Seric message("250-%s Hello %s, pleased to meet you", 30766760Seric MyHostName, CurSmtpClient); 30858323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 30958323Seric message("250-EXPN"); 31069480Seric #if MIME8TO7 31167417Seric message("250-8BITMIME"); 31269480Seric #endif 31364359Seric if (MaxMessageSize > 0) 31464359Seric message("250-SIZE %ld", MaxMessageSize); 31559271Seric else 31659271Seric message("250-SIZE"); 31768848Seric #if DSN 31869545Seric message("250-X-DSN-03 (Draft of 12 Mar 1995)"); 31968028Seric #endif 32058323Seric message("250 HELP"); 3214976Seric break; 3224976Seric 3234549Seric case CMDMAIL: /* mail -- designate sender */ 32461093Seric SmtpPhase = "server MAIL"; 32524943Seric 3269314Seric /* check for validity of this command */ 32758789Seric if (!gothello) 32858082Seric { 32958957Seric /* set sending host to our known value */ 33059016Seric if (sendinghost == NULL) 33166005Seric sendinghost = peerhostname; 33258957Seric 33358789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 33458821Seric { 33558789Seric message("503 Polite people say HELO first"); 33658821Seric break; 33758821Seric } 33858082Seric } 33958109Seric if (gotmail) 3404558Seric { 34158151Seric message("503 Sender already specified"); 34263843Seric if (InChild) 34363843Seric finis(); 3444558Seric break; 3454558Seric } 3469339Seric if (InChild) 3479339Seric { 34836230Skarels errno = 0; 34958151Seric syserr("503 Nested MAIL command: MAIL %s", p); 35058069Seric finis(); 3519339Seric } 3529339Seric 3539339Seric /* fork a subprocess to process this command */ 35455012Seric if (runinchild("SMTP-MAIL", e) > 0) 3559339Seric break; 35663753Seric if (!gothello) 35763753Seric { 35863753Seric auth_warning(e, 35966005Seric "Host %s didn't use HELO protocol", 36066005Seric peerhostname); 36163753Seric } 36265947Seric #ifdef PICKY_HELO_CHECK 36366005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 36466005Seric (strcasecmp(peerhostname, "localhost") != 0 || 36565823Seric strcasecmp(sendinghost, MyHostName) != 0)) 36665823Seric { 36765823Seric auth_warning(e, "Host %s claimed to be %s", 36866005Seric peerhostname, sendinghost); 36965823Seric } 37065947Seric #endif 37165823Seric 37258323Seric if (protocol == NULL) 37358323Seric protocol = "SMTP"; 37458323Seric define('r', protocol, e); 37559016Seric define('s', sendinghost, e); 37655012Seric initsys(e); 37759747Seric nrcpts = 0; 37868582Seric e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; 37965058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3809339Seric 3819339Seric /* child -- go do the processing */ 3824549Seric p = skipword(p, "from"); 3834549Seric if (p == NULL) 3844549Seric break; 38557977Seric if (setjmp(TopFrame) > 0) 38658147Seric { 38758147Seric /* this failed -- undo work */ 38858147Seric if (InChild) 38959058Seric { 39059058Seric QuickAbort = FALSE; 39159058Seric SuprErrs = TRUE; 39263787Seric e->e_flags &= ~EF_FATALERRS; 39358147Seric finis(); 39459058Seric } 39557977Seric break; 39658147Seric } 39757977Seric QuickAbort = TRUE; 39858333Seric 39958333Seric /* must parse sender first */ 40058333Seric delimptr = NULL; 40158704Seric setsender(p, e, &delimptr, FALSE); 40258333Seric p = delimptr; 40358333Seric if (p != NULL && *p != '\0') 40458333Seric *p++ = '\0'; 40558333Seric 40666325Seric /* check for possible spoofing */ 40766325Seric if (RealUid != 0 && OpMode == MD_SMTP && 40867473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40967473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 41066325Seric { 41166325Seric auth_warning(e, "%s owned process doing -bs", 41266325Seric RealUserName); 41366325Seric } 41466325Seric 41558333Seric /* now parse ESMTP arguments */ 41668560Seric e->e_msgsize = 0; 41766764Seric while (p != NULL && *p != '\0') 41858333Seric { 41958333Seric char *kp; 42066304Seric char *vp = NULL; 421*69748Seric extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); 42258333Seric 42358333Seric /* locate the beginning of the keyword */ 42458333Seric while (isascii(*p) && isspace(*p)) 42558333Seric p++; 42658333Seric if (*p == '\0') 42758333Seric break; 42858333Seric kp = p; 42958333Seric 43058333Seric /* skip to the value portion */ 43158333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 43258333Seric p++; 43358333Seric if (*p == '=') 43458333Seric { 43558333Seric *p++ = '\0'; 43658333Seric vp = p; 43758333Seric 43858333Seric /* skip to the end of the value */ 43958333Seric while (*p != '\0' && *p != ' ' && 44058333Seric !(isascii(*p) && iscntrl(*p)) && 44158333Seric *p != '=') 44258333Seric p++; 44358333Seric } 44458333Seric 44558333Seric if (*p != '\0') 44658333Seric *p++ = '\0'; 44758333Seric 44858333Seric if (tTd(19, 1)) 44966764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 45058333Seric vp == NULL ? "<null>" : vp); 45158333Seric 45268559Seric mail_esmtp_args(kp, vp, e); 45358333Seric } 45459284Seric 45568560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 45659284Seric { 45759284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 45859284Seric MaxMessageSize); 45959284Seric /* NOTREACHED */ 46059284Seric } 46158333Seric 46268560Seric if (!enoughspace(e->e_msgsize)) 46358333Seric { 46458333Seric message("452 Insufficient disk space; try again later"); 46558333Seric break; 46658333Seric } 46758151Seric message("250 Sender ok"); 46858147Seric gotmail = TRUE; 4694549Seric break; 4704549Seric 4714976Seric case CMDRCPT: /* rcpt -- designate recipient */ 47258850Seric if (!gotmail) 47358850Seric { 47458850Seric usrerr("503 Need MAIL before RCPT"); 47558850Seric break; 47658850Seric } 47761093Seric SmtpPhase = "server RCPT"; 47812612Seric if (setjmp(TopFrame) > 0) 47914785Seric { 48055012Seric e->e_flags &= ~EF_FATALERRS; 48112612Seric break; 48214785Seric } 48312612Seric QuickAbort = TRUE; 48451951Seric LogUsrErrs = TRUE; 48558093Seric 48659699Seric if (e->e_sendmode != SM_DELIVER) 48759699Seric e->e_flags |= EF_VRFYONLY; 48858919Seric 4894549Seric p = skipword(p, "to"); 4904549Seric if (p == NULL) 4914549Seric break; 49267880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 49312612Seric if (a == NULL) 49412612Seric break; 49567880Seric p = delimptr; 49667880Seric 49767880Seric /* now parse ESMTP arguments */ 49867880Seric while (p != NULL && *p != '\0') 49967880Seric { 50067880Seric char *kp; 50167880Seric char *vp = NULL; 502*69748Seric extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); 50367880Seric 50467880Seric /* locate the beginning of the keyword */ 50567880Seric while (isascii(*p) && isspace(*p)) 50667880Seric p++; 50767880Seric if (*p == '\0') 50867880Seric break; 50967880Seric kp = p; 51067880Seric 51167880Seric /* skip to the value portion */ 51267880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 51367880Seric p++; 51467880Seric if (*p == '=') 51567880Seric { 51667880Seric *p++ = '\0'; 51767880Seric vp = p; 51867880Seric 51967880Seric /* skip to the end of the value */ 52067880Seric while (*p != '\0' && *p != ' ' && 52167880Seric !(isascii(*p) && iscntrl(*p)) && 52267880Seric *p != '=') 52367880Seric p++; 52467880Seric } 52567880Seric 52667880Seric if (*p != '\0') 52767880Seric *p++ = '\0'; 52867880Seric 52967880Seric if (tTd(19, 1)) 53067880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 53167880Seric vp == NULL ? "<null>" : vp); 53267880Seric 53367963Seric rcpt_esmtp_args(a, kp, vp, e); 53467880Seric } 53567980Seric 53667980Seric /* save in recipient list after ESMTP mods */ 53767982Seric a = recipient(a, &e->e_sendqueue, 0, e); 53867980Seric 53912612Seric if (Errors != 0) 54012612Seric break; 54112612Seric 54212612Seric /* no errors during parsing, but might be a duplicate */ 54355012Seric e->e_to = p; 54412612Seric if (!bitset(QBADADDR, a->q_flags)) 54559747Seric { 54664718Seric message("250 Recipient ok%s", 54764718Seric bitset(QQUEUEUP, a->q_flags) ? 54864718Seric " (will queue)" : ""); 54959747Seric nrcpts++; 55059747Seric } 55112612Seric else 5524549Seric { 55312612Seric /* punt -- should keep message in ADDRESS.... */ 55458151Seric message("550 Addressee unknown"); 5554549Seric } 55655012Seric e->e_to = NULL; 5574549Seric break; 5584549Seric 5594549Seric case CMDDATA: /* data -- text of mail */ 56061093Seric SmtpPhase = "server DATA"; 56158109Seric if (!gotmail) 5624549Seric { 56358151Seric message("503 Need MAIL command"); 5644976Seric break; 5654549Seric } 56664718Seric else if (nrcpts <= 0) 5674549Seric { 56858151Seric message("503 Need RCPT (recipient)"); 5694976Seric break; 5704549Seric } 5714976Seric 57258929Seric /* check to see if we need to re-expand aliases */ 57363787Seric /* also reset QBADADDR on already-diagnosted addrs */ 57463787Seric doublequeue = FALSE; 57558929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 57658929Seric { 57758929Seric if (bitset(QVERIFIED, a->q_flags)) 57863787Seric { 57963787Seric /* need to re-expand aliases */ 58063787Seric doublequeue = TRUE; 58163787Seric } 58263787Seric if (bitset(QBADADDR, a->q_flags)) 58363787Seric { 58463787Seric /* make this "go away" */ 58563787Seric a->q_flags |= QDONTSEND; 58663787Seric a->q_flags &= ~QBADADDR; 58763787Seric } 58858929Seric } 58958929Seric 5904976Seric /* collect the text of the message */ 59124943Seric SmtpPhase = "collect"; 59268692Seric buffer_errors(); 59367546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 59468692Seric flush_errors(TRUE); 59564766Seric if (Errors != 0) 59664766Seric goto abortmessage; 59767131Seric 59868582Seric /* make sure we actually do delivery */ 59968582Seric e->e_flags &= ~EF_CLRQUEUE; 60068582Seric 60167131Seric /* from now on, we have to operate silently */ 60268692Seric buffer_errors(); 60367131Seric e->e_errormode = EM_MAIL; 6044976Seric 6058238Seric /* 6068238Seric ** Arrange to send to everyone. 6078238Seric ** If sending to multiple people, mail back 6088238Seric ** errors rather than reporting directly. 6098238Seric ** In any case, don't mail back errors for 6108238Seric ** anything that has happened up to 6118238Seric ** now (the other end will do this). 61210197Seric ** Truncate our transcript -- the mail has gotten 61310197Seric ** to us successfully, and if we have 61410197Seric ** to mail this back, it will be easier 61510197Seric ** on the reader. 6168238Seric ** Then send to everyone. 6178238Seric ** Finally give a reply code. If an error has 6188238Seric ** already been given, don't mail a 6198238Seric ** message back. 6209339Seric ** We goose error returns by clearing error bit. 6218238Seric */ 6228238Seric 62324943Seric SmtpPhase = "delivery"; 62455012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 62558714Seric id = e->e_id; 6264976Seric 62767131Seric if (doublequeue) 62859730Seric { 62967131Seric /* make sure it is in the queue */ 63067131Seric queueup(e, TRUE, FALSE); 63168035Seric if (e->e_sendmode == SM_QUEUE) 63268035Seric e->e_flags |= EF_KEEPQUEUE; 63359730Seric } 6348238Seric else 63558919Seric { 63667131Seric /* send to all recipients */ 63767131Seric sendall(e, SM_DEFAULT); 63867131Seric } 63967131Seric e->e_to = NULL; 64059747Seric 64167131Seric /* issue success message */ 64267131Seric message("250 %s Message accepted for delivery", id); 64364296Seric 64467131Seric /* if we just queued, poke it */ 64567131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 64667131Seric { 64767131Seric extern pid_t dowork(); 64867131Seric 64967131Seric unlockqueue(e); 65067131Seric (void) dowork(id, TRUE, TRUE, e); 65158919Seric } 65258883Seric 65359747Seric abortmessage: 6549339Seric /* if in a child, pop back to our parent */ 6559339Seric if (InChild) 6569339Seric finis(); 65724943Seric 65824943Seric /* clean up a bit */ 65958109Seric gotmail = FALSE; 66055012Seric dropenvelope(e); 66158179Seric CurEnv = e = newenvelope(e, CurEnv); 66255012Seric e->e_flags = BlankEnvelope.e_flags; 6634549Seric break; 6644549Seric 6654549Seric case CMDRSET: /* rset -- reset state */ 66658151Seric message("250 Reset state"); 66768603Seric 66868603Seric /* arrange to ignore any current send list */ 66968603Seric e->e_sendqueue = NULL; 67064359Seric e->e_flags |= EF_CLRQUEUE; 6719339Seric if (InChild) 6729339Seric finis(); 67358109Seric 67458109Seric /* clean up a bit */ 67558109Seric gotmail = FALSE; 67658109Seric dropenvelope(e); 67758179Seric CurEnv = e = newenvelope(e, CurEnv); 6789339Seric break; 6794549Seric 6804549Seric case CMDVRFY: /* vrfy -- verify address */ 68158092Seric case CMDEXPN: /* expn -- expand address */ 68258092Seric vrfy = c->cmdcode == CMDVRFY; 68358092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 68458092Seric PrivacyFlags)) 68558082Seric { 68658412Seric if (vrfy) 68767160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 68858412Seric else 68965192Seric message("502 Sorry, we do not allow this operation"); 69065017Seric #ifdef LOG 69165017Seric if (LogLevel > 5) 69265017Seric syslog(LOG_INFO, "%s: %s [rejected]", 69365017Seric CurSmtpClient, inp); 69465017Seric #endif 69558082Seric break; 69658082Seric } 69758082Seric else if (!gothello && 69858092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 69958092Seric PrivacyFlags)) 70058082Seric { 70158151Seric message("503 I demand that you introduce yourself first"); 70258082Seric break; 70358082Seric } 70458092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7059339Seric break; 70655173Seric #ifdef LOG 70758020Seric if (LogLevel > 5) 70865017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 70955173Seric #endif 7105003Seric vrfyqueue = NULL; 7117762Seric QuickAbort = TRUE; 71258092Seric if (vrfy) 71358092Seric e->e_flags |= EF_VRFYONLY; 71462373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 71567615Seric p++; 71662373Seric if (*p == '\0') 71762373Seric { 71862373Seric message("501 Argument required"); 71962373Seric Errors++; 72062373Seric } 72162373Seric else 72262373Seric { 72367990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 72462373Seric } 7257762Seric if (Errors != 0) 7269339Seric { 7279339Seric if (InChild) 7289339Seric finis(); 7297762Seric break; 7309339Seric } 73162373Seric if (vrfyqueue == NULL) 73262373Seric { 73362373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 73462373Seric } 7355003Seric while (vrfyqueue != NULL) 7365003Seric { 737*69748Seric extern void printvrfyaddr __P((ADDRESS *, bool)); 738*69748Seric 73963971Seric a = vrfyqueue; 74063971Seric while ((a = a->q_next) != NULL && 74163971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 74263971Seric continue; 7437685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 74458151Seric printvrfyaddr(vrfyqueue, a == NULL); 74563847Seric vrfyqueue = vrfyqueue->q_next; 7465003Seric } 7479339Seric if (InChild) 7489339Seric finis(); 7494549Seric break; 7504549Seric 7514549Seric case CMDHELP: /* help -- give user info */ 7524577Seric help(p); 7534549Seric break; 7544549Seric 7554549Seric case CMDNOOP: /* noop -- do nothing */ 75664122Seric message("250 OK"); 7574549Seric break; 7584549Seric 7594549Seric case CMDQUIT: /* quit -- leave mail */ 76058151Seric message("221 %s closing connection", MyHostName); 76161051Seric 76266283Seric doquit: 76368603Seric /* arrange to ignore any current send list */ 76468603Seric e->e_sendqueue = NULL; 76568603Seric 76661051Seric /* avoid future 050 messages */ 76766017Seric disconnect(1, e); 76861051Seric 7699339Seric if (InChild) 7709339Seric ExitStat = EX_QUIT; 7714549Seric finis(); 7724549Seric 7738544Seric case CMDVERB: /* set verbose mode */ 77459957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 77559957Seric { 77659957Seric /* this would give out the same info */ 77759957Seric message("502 Verbose unavailable"); 77859957Seric break; 77959957Seric } 7808544Seric Verbose = TRUE; 78158734Seric e->e_sendmode = SM_DELIVER; 78259957Seric message("250 Verbose mode"); 7838544Seric break; 7848544Seric 7859314Seric case CMDONEX: /* doing one transaction only */ 7869378Seric OneXact = TRUE; 78759957Seric message("250 Only one transaction"); 7889314Seric break; 7899314Seric 79036230Skarels # ifdef SMTPDEBUG 7919339Seric case CMDDBGQSHOW: /* show queues */ 7926907Seric printf("Send Queue="); 79355012Seric printaddr(e->e_sendqueue, TRUE); 7945003Seric break; 7957275Seric 7967275Seric case CMDDBGDEBUG: /* set debug mode */ 7977676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7987676Seric tTflag(p); 79958151Seric message("200 Debug set"); 8007275Seric break; 8017275Seric 80236230Skarels # else /* not SMTPDEBUG */ 80336230Skarels case CMDDBGQSHOW: /* show queues */ 80436230Skarels case CMDDBGDEBUG: /* set debug mode */ 80564685Seric # endif /* SMTPDEBUG */ 80664685Seric case CMDLOGBOGUS: /* bogus command */ 80736233Skarels # ifdef LOG 80858308Seric if (LogLevel > 0) 80964685Seric syslog(LOG_CRIT, 81058020Seric "\"%s\" command from %s (%s)", 81166005Seric c->cmdname, peerhostname, 81258755Seric anynet_ntoa(&RealHostAddr)); 81336233Skarels # endif 81436230Skarels /* FALL THROUGH */ 81536230Skarels 8164549Seric case CMDERROR: /* unknown command */ 81766283Seric if (++badcommands > MAXBADCOMMANDS) 81866283Seric { 81966283Seric message("421 %s Too many bad commands; closing connection", 82066283Seric MyHostName); 82166283Seric goto doquit; 82266283Seric } 82366283Seric 82458151Seric message("500 Command unrecognized"); 8254549Seric break; 8264549Seric 8274549Seric default: 82836230Skarels errno = 0; 82958151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8304549Seric break; 8314549Seric } 8324549Seric } 8334549Seric } 8344549Seric /* 8354549Seric ** SKIPWORD -- skip a fixed word. 8364549Seric ** 8374549Seric ** Parameters: 8384549Seric ** p -- place to start looking. 8394549Seric ** w -- word to skip. 8404549Seric ** 8414549Seric ** Returns: 8424549Seric ** p following w. 8434549Seric ** NULL on error. 8444549Seric ** 8454549Seric ** Side Effects: 8464549Seric ** clobbers the p data area. 8474549Seric */ 8484549Seric 8494549Seric static char * 8504549Seric skipword(p, w) 8514549Seric register char *p; 8524549Seric char *w; 8534549Seric { 8544549Seric register char *q; 85566005Seric char *firstp = p; 8564549Seric 8574549Seric /* find beginning of word */ 85858050Seric while (isascii(*p) && isspace(*p)) 8594549Seric p++; 8604549Seric q = p; 8614549Seric 8624549Seric /* find end of word */ 86358050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8644549Seric p++; 86558050Seric while (isascii(*p) && isspace(*p)) 8664549Seric *p++ = '\0'; 8674549Seric if (*p != ':') 8684549Seric { 8694549Seric syntax: 87066005Seric message("501 Syntax error in parameters scanning \"%s\"", 87166005Seric firstp); 8724549Seric Errors++; 8734549Seric return (NULL); 8744549Seric } 8754549Seric *p++ = '\0'; 87658050Seric while (isascii(*p) && isspace(*p)) 8774549Seric p++; 8784549Seric 87962373Seric if (*p == '\0') 88062373Seric goto syntax; 88162373Seric 8824549Seric /* see if the input word matches desired word */ 88333725Sbostic if (strcasecmp(q, w)) 8844549Seric goto syntax; 8854549Seric 8864549Seric return (p); 8874549Seric } 8884577Seric /* 88968559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 89068559Seric ** 89168559Seric ** Parameters: 89268559Seric ** kp -- the parameter key. 89368559Seric ** vp -- the value of that parameter. 89468559Seric ** e -- the envelope. 89568559Seric ** 89668559Seric ** Returns: 89768559Seric ** none. 89868559Seric */ 89968559Seric 900*69748Seric void 90168559Seric mail_esmtp_args(kp, vp, e) 90268559Seric char *kp; 90368559Seric char *vp; 90468559Seric ENVELOPE *e; 90568559Seric { 90668559Seric if (strcasecmp(kp, "size") == 0) 90768559Seric { 90868559Seric if (vp == NULL) 90968559Seric { 91068559Seric usrerr("501 SIZE requires a value"); 91168559Seric /* NOTREACHED */ 91268559Seric } 91368890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) 91468560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10); 91568559Seric # else 91668560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10); 91768559Seric # endif 91868559Seric } 91968559Seric else if (strcasecmp(kp, "body") == 0) 92068559Seric { 92168559Seric if (vp == NULL) 92268559Seric { 92368559Seric usrerr("501 BODY requires a value"); 92468559Seric /* NOTREACHED */ 92568559Seric } 92668559Seric if (strcasecmp(vp, "8bitmime") == 0) 92768559Seric { 92868559Seric SevenBitInput = FALSE; 92968883Seric e->e_flags |= EF_NL_NOT_EOL; 93068559Seric } 93168559Seric else if (strcasecmp(vp, "7bit") == 0) 93268559Seric { 93368559Seric SevenBitInput = TRUE; 93468559Seric } 93568559Seric else 93668559Seric { 93768559Seric usrerr("501 Unknown BODY type %s", 93868559Seric vp); 93968559Seric /* NOTREACHED */ 94068559Seric } 94168559Seric e->e_bodytype = newstr(vp); 94268559Seric } 94368559Seric else if (strcasecmp(kp, "envid") == 0) 94468559Seric { 94568559Seric if (vp == NULL) 94668559Seric { 94768559Seric usrerr("501 ENVID requires a value"); 94868559Seric /* NOTREACHED */ 94968559Seric } 95068583Seric if (!xtextok(vp)) 95168583Seric { 95268583Seric usrerr("501 Syntax error in ENVID parameter value"); 95368583Seric /* NOTREACHED */ 95468583Seric } 95568583Seric if (e->e_envid != NULL) 95668583Seric { 95768583Seric usrerr("501 Duplicate ENVID parameter"); 95868583Seric /* NOTREACHED */ 95968583Seric } 96068559Seric e->e_envid = newstr(vp); 96168559Seric } 96268559Seric else if (strcasecmp(kp, "ret") == 0) 96368559Seric { 96468559Seric if (vp == NULL) 96568559Seric { 96668559Seric usrerr("501 RET requires a value"); 96768559Seric /* NOTREACHED */ 96868559Seric } 96968583Seric if (bitset(EF_RET_PARAM, e->e_flags)) 97068583Seric { 97168583Seric usrerr("501 Duplicate RET parameter"); 97268583Seric /* NOTREACHED */ 97368583Seric } 97468559Seric e->e_flags |= EF_RET_PARAM; 97568559Seric if (strcasecmp(vp, "hdrs") == 0) 97668559Seric e->e_flags |= EF_NO_BODY_RETN; 97768559Seric else if (strcasecmp(vp, "full") != 0) 97868559Seric { 97968559Seric usrerr("501 Bad argument \"%s\" to RET", vp); 98068559Seric /* NOTREACHED */ 98168559Seric } 98268559Seric } 98368559Seric else 98468559Seric { 98568559Seric usrerr("501 %s parameter unrecognized", kp); 98668559Seric /* NOTREACHED */ 98768559Seric } 98868559Seric } 98968559Seric /* 99067963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 99167963Seric ** 99267963Seric ** Parameters: 99367963Seric ** a -- the address corresponding to the To: parameter. 99467963Seric ** kp -- the parameter key. 99567963Seric ** vp -- the value of that parameter. 99667963Seric ** e -- the envelope. 99767963Seric ** 99867963Seric ** Returns: 99967963Seric ** none. 100067963Seric */ 100167963Seric 1002*69748Seric void 100367963Seric rcpt_esmtp_args(a, kp, vp, e) 100467963Seric ADDRESS *a; 100567963Seric char *kp; 100667963Seric char *vp; 100767963Seric ENVELOPE *e; 100867963Seric { 100967963Seric if (strcasecmp(kp, "notify") == 0) 101067963Seric { 101167963Seric char *p; 101267963Seric 101367963Seric if (vp == NULL) 101467963Seric { 101567963Seric usrerr("501 NOTIFY requires a value"); 101667963Seric /* NOTREACHED */ 101767963Seric } 101867963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 101968595Seric a->q_flags |= QHASNOTIFY; 102067963Seric if (strcasecmp(vp, "never") == 0) 102167963Seric return; 102267963Seric for (p = vp; p != NULL; vp = p) 102367963Seric { 102467963Seric p = strchr(p, ','); 102567963Seric if (p != NULL) 102667963Seric *p++ = '\0'; 102767963Seric if (strcasecmp(vp, "success") == 0) 102867963Seric a->q_flags |= QPINGONSUCCESS; 102967963Seric else if (strcasecmp(vp, "failure") == 0) 103067963Seric a->q_flags |= QPINGONFAILURE; 103167963Seric else if (strcasecmp(vp, "delay") == 0) 103267963Seric a->q_flags |= QPINGONDELAY; 103367963Seric else 103467963Seric { 103567963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 103667963Seric vp); 103767963Seric /* NOTREACHED */ 103867963Seric } 103967963Seric } 104067963Seric } 104167963Seric else if (strcasecmp(kp, "orcpt") == 0) 104267963Seric { 104367963Seric if (vp == NULL) 104467963Seric { 104567963Seric usrerr("501 ORCPT requires a value"); 104667963Seric /* NOTREACHED */ 104767963Seric } 104868583Seric if (!xtextok(vp)) 104968583Seric { 105068583Seric usrerr("501 Syntax error in ORCPT parameter value"); 105168583Seric /* NOTREACHED */ 105268583Seric } 105368583Seric if (a->q_orcpt != NULL) 105468583Seric { 105568583Seric usrerr("501 Duplicate ORCPT parameter"); 105668583Seric /* NOTREACHED */ 105768583Seric } 105867963Seric a->q_orcpt = newstr(vp); 105967963Seric } 106067963Seric else 106167963Seric { 106267963Seric usrerr("501 %s parameter unrecognized", kp); 106367963Seric /* NOTREACHED */ 106467963Seric } 106567963Seric } 106667963Seric /* 106758151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 106858151Seric ** 106958151Seric ** Parameters: 107058151Seric ** a -- the address to print 107158151Seric ** last -- set if this is the last one. 107258151Seric ** 107358151Seric ** Returns: 107458151Seric ** none. 107558151Seric ** 107658151Seric ** Side Effects: 107758151Seric ** Prints the appropriate 250 codes. 107858151Seric */ 107958151Seric 1080*69748Seric void 108158151Seric printvrfyaddr(a, last) 108258151Seric register ADDRESS *a; 108358151Seric bool last; 108458151Seric { 108558151Seric char fmtbuf[20]; 108658151Seric 108758151Seric strcpy(fmtbuf, "250"); 108858151Seric fmtbuf[3] = last ? ' ' : '-'; 108958151Seric 109059746Seric if (a->q_fullname == NULL) 109159746Seric { 109259746Seric if (strchr(a->q_user, '@') == NULL) 109359746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 109459746Seric else 109559746Seric strcpy(&fmtbuf[4], "<%s>"); 109659746Seric message(fmtbuf, a->q_user, MyHostName); 109759746Seric } 109858151Seric else 109958151Seric { 110059746Seric if (strchr(a->q_user, '@') == NULL) 110159746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 110259746Seric else 110359746Seric strcpy(&fmtbuf[4], "%s <%s>"); 110459746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 110558151Seric } 110658151Seric } 110758151Seric /* 11084577Seric ** HELP -- implement the HELP command. 11094577Seric ** 11104577Seric ** Parameters: 11114577Seric ** topic -- the topic we want help for. 11124577Seric ** 11134577Seric ** Returns: 11144577Seric ** none. 11154577Seric ** 11164577Seric ** Side Effects: 11174577Seric ** outputs the help file to message output. 11184577Seric */ 11194577Seric 1120*69748Seric void 11214577Seric help(topic) 11224577Seric char *topic; 11234577Seric { 11244577Seric register FILE *hf; 11254577Seric int len; 112668751Seric bool noinfo; 11274577Seric char buf[MAXLINE]; 112868751Seric extern char Version[]; 11294577Seric 113068751Seric 11318269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 11324577Seric { 11334577Seric /* no help */ 113411931Seric errno = 0; 113568751Seric message("502 Sendmail %s -- HELP not implemented", Version); 11364577Seric return; 11374577Seric } 11384577Seric 113949669Seric if (topic == NULL || *topic == '\0') 114068751Seric { 114149669Seric topic = "smtp"; 114268751Seric message("214-This is Sendmail version %s", Version); 114368751Seric noinfo = FALSE; 114468751Seric } 114549669Seric else 114668751Seric { 114749669Seric makelower(topic); 114868751Seric noinfo = TRUE; 114968751Seric } 115049669Seric 11514577Seric len = strlen(topic); 11524577Seric 11534577Seric while (fgets(buf, sizeof buf, hf) != NULL) 11544577Seric { 11554577Seric if (strncmp(buf, topic, len) == 0) 11564577Seric { 11574577Seric register char *p; 11584577Seric 115956795Seric p = strchr(buf, '\t'); 11604577Seric if (p == NULL) 11614577Seric p = buf; 11624577Seric else 11634577Seric p++; 11644577Seric fixcrlf(p, TRUE); 116558151Seric message("214-%s", p); 11664577Seric noinfo = FALSE; 11674577Seric } 11684577Seric } 11694577Seric 11704577Seric if (noinfo) 117158151Seric message("504 HELP topic unknown"); 11724577Seric else 117358151Seric message("214 End of HELP info"); 11744628Seric (void) fclose(hf); 11754577Seric } 11768544Seric /* 11779339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11789339Seric ** 11799339Seric ** Parameters: 11809339Seric ** label -- a string used in error messages 11819339Seric ** 11829339Seric ** Returns: 11839339Seric ** zero in the child 11849339Seric ** one in the parent 11859339Seric ** 11869339Seric ** Side Effects: 11879339Seric ** none. 11889339Seric */ 11898544Seric 1190*69748Seric int 119155012Seric runinchild(label, e) 11929339Seric char *label; 119355012Seric register ENVELOPE *e; 11949339Seric { 11959339Seric int childpid; 11969339Seric 119716158Seric if (!OneXact) 11989339Seric { 119916158Seric childpid = dofork(); 120016158Seric if (childpid < 0) 120116158Seric { 120269716Seric syserr("451 %s: cannot fork", label); 120316158Seric return (1); 120416158Seric } 120516158Seric if (childpid > 0) 120616158Seric { 120716158Seric auto int st; 12089339Seric 120916158Seric /* parent -- wait for child to complete */ 121061093Seric setproctitle("server %s child wait", CurHostName); 121116158Seric st = waitfor(childpid); 121216158Seric if (st == -1) 121369716Seric syserr("451 %s: lost child", label); 121464948Seric else if (!WIFEXITED(st)) 121569716Seric syserr("451 %s: died on signal %d", 121664948Seric label, st & 0177); 12179339Seric 121816158Seric /* if we exited on a QUIT command, complete the process */ 121966017Seric if (WEXITSTATUS(st) == EX_QUIT) 122066017Seric { 122166017Seric disconnect(1, e); 122216158Seric finis(); 122366017Seric } 12249339Seric 122516158Seric return (1); 122616158Seric } 122716158Seric else 122816158Seric { 122916158Seric /* child */ 123016158Seric InChild = TRUE; 123125050Seric QuickAbort = FALSE; 123255012Seric clearenvelope(e, FALSE); 123316158Seric } 12349339Seric } 123515256Seric 123616158Seric /* open alias database */ 123760537Seric initmaps(FALSE, e); 123816158Seric 123916158Seric return (0); 12409339Seric } 12419339Seric 124256795Seric # endif /* SMTP */ 1243