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*69757Seric static char sccsid[] = "@(#)srvrsmtp.c 8.76 (Berkeley) 05/29/95 (with SMTP)"; 1433731Sbostic #else 15*69757Seric static char sccsid[] = "@(#)srvrsmtp.c 8.76 (Berkeley) 05/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 9969748Seric 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; 12269748Seric 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 && 408*69757Seric !wordinclass(RealUserName, 't') && 40967473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 41067473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 41166325Seric { 41266325Seric auth_warning(e, "%s owned process doing -bs", 41366325Seric RealUserName); 41466325Seric } 41566325Seric 41658333Seric /* now parse ESMTP arguments */ 41768560Seric e->e_msgsize = 0; 41866764Seric while (p != NULL && *p != '\0') 41958333Seric { 42058333Seric char *kp; 42166304Seric char *vp = NULL; 42269748Seric extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); 42358333Seric 42458333Seric /* locate the beginning of the keyword */ 42558333Seric while (isascii(*p) && isspace(*p)) 42658333Seric p++; 42758333Seric if (*p == '\0') 42858333Seric break; 42958333Seric kp = p; 43058333Seric 43158333Seric /* skip to the value portion */ 43258333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 43358333Seric p++; 43458333Seric if (*p == '=') 43558333Seric { 43658333Seric *p++ = '\0'; 43758333Seric vp = p; 43858333Seric 43958333Seric /* skip to the end of the value */ 44058333Seric while (*p != '\0' && *p != ' ' && 44158333Seric !(isascii(*p) && iscntrl(*p)) && 44258333Seric *p != '=') 44358333Seric p++; 44458333Seric } 44558333Seric 44658333Seric if (*p != '\0') 44758333Seric *p++ = '\0'; 44858333Seric 44958333Seric if (tTd(19, 1)) 45066764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 45158333Seric vp == NULL ? "<null>" : vp); 45258333Seric 45368559Seric mail_esmtp_args(kp, vp, e); 45458333Seric } 45559284Seric 45668560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 45759284Seric { 45859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 45959284Seric MaxMessageSize); 46059284Seric /* NOTREACHED */ 46159284Seric } 46258333Seric 46368560Seric if (!enoughspace(e->e_msgsize)) 46458333Seric { 46558333Seric message("452 Insufficient disk space; try again later"); 46658333Seric break; 46758333Seric } 46858151Seric message("250 Sender ok"); 46958147Seric gotmail = TRUE; 4704549Seric break; 4714549Seric 4724976Seric case CMDRCPT: /* rcpt -- designate recipient */ 47358850Seric if (!gotmail) 47458850Seric { 47558850Seric usrerr("503 Need MAIL before RCPT"); 47658850Seric break; 47758850Seric } 47861093Seric SmtpPhase = "server RCPT"; 47912612Seric if (setjmp(TopFrame) > 0) 48014785Seric { 48155012Seric e->e_flags &= ~EF_FATALERRS; 48212612Seric break; 48314785Seric } 48412612Seric QuickAbort = TRUE; 48551951Seric LogUsrErrs = TRUE; 48658093Seric 48759699Seric if (e->e_sendmode != SM_DELIVER) 48859699Seric e->e_flags |= EF_VRFYONLY; 48958919Seric 4904549Seric p = skipword(p, "to"); 4914549Seric if (p == NULL) 4924549Seric break; 49367880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 49412612Seric if (a == NULL) 49512612Seric break; 49667880Seric p = delimptr; 49767880Seric 49867880Seric /* now parse ESMTP arguments */ 49967880Seric while (p != NULL && *p != '\0') 50067880Seric { 50167880Seric char *kp; 50267880Seric char *vp = NULL; 50369748Seric extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); 50467880Seric 50567880Seric /* locate the beginning of the keyword */ 50667880Seric while (isascii(*p) && isspace(*p)) 50767880Seric p++; 50867880Seric if (*p == '\0') 50967880Seric break; 51067880Seric kp = p; 51167880Seric 51267880Seric /* skip to the value portion */ 51367880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 51467880Seric p++; 51567880Seric if (*p == '=') 51667880Seric { 51767880Seric *p++ = '\0'; 51867880Seric vp = p; 51967880Seric 52067880Seric /* skip to the end of the value */ 52167880Seric while (*p != '\0' && *p != ' ' && 52267880Seric !(isascii(*p) && iscntrl(*p)) && 52367880Seric *p != '=') 52467880Seric p++; 52567880Seric } 52667880Seric 52767880Seric if (*p != '\0') 52867880Seric *p++ = '\0'; 52967880Seric 53067880Seric if (tTd(19, 1)) 53167880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 53267880Seric vp == NULL ? "<null>" : vp); 53367880Seric 53467963Seric rcpt_esmtp_args(a, kp, vp, e); 53567880Seric } 53667980Seric 53767980Seric /* save in recipient list after ESMTP mods */ 53867982Seric a = recipient(a, &e->e_sendqueue, 0, e); 53967980Seric 54012612Seric if (Errors != 0) 54112612Seric break; 54212612Seric 54312612Seric /* no errors during parsing, but might be a duplicate */ 54455012Seric e->e_to = p; 54512612Seric if (!bitset(QBADADDR, a->q_flags)) 54659747Seric { 54764718Seric message("250 Recipient ok%s", 54864718Seric bitset(QQUEUEUP, a->q_flags) ? 54964718Seric " (will queue)" : ""); 55059747Seric nrcpts++; 55159747Seric } 55212612Seric else 5534549Seric { 55412612Seric /* punt -- should keep message in ADDRESS.... */ 55558151Seric message("550 Addressee unknown"); 5564549Seric } 55755012Seric e->e_to = NULL; 5584549Seric break; 5594549Seric 5604549Seric case CMDDATA: /* data -- text of mail */ 56161093Seric SmtpPhase = "server DATA"; 56258109Seric if (!gotmail) 5634549Seric { 56458151Seric message("503 Need MAIL command"); 5654976Seric break; 5664549Seric } 56764718Seric else if (nrcpts <= 0) 5684549Seric { 56958151Seric message("503 Need RCPT (recipient)"); 5704976Seric break; 5714549Seric } 5724976Seric 57358929Seric /* check to see if we need to re-expand aliases */ 57463787Seric /* also reset QBADADDR on already-diagnosted addrs */ 57563787Seric doublequeue = FALSE; 57658929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 57758929Seric { 57858929Seric if (bitset(QVERIFIED, a->q_flags)) 57963787Seric { 58063787Seric /* need to re-expand aliases */ 58163787Seric doublequeue = TRUE; 58263787Seric } 58363787Seric if (bitset(QBADADDR, a->q_flags)) 58463787Seric { 58563787Seric /* make this "go away" */ 58663787Seric a->q_flags |= QDONTSEND; 58763787Seric a->q_flags &= ~QBADADDR; 58863787Seric } 58958929Seric } 59058929Seric 5914976Seric /* collect the text of the message */ 59224943Seric SmtpPhase = "collect"; 59368692Seric buffer_errors(); 59467546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 59568692Seric flush_errors(TRUE); 59664766Seric if (Errors != 0) 59764766Seric goto abortmessage; 59867131Seric 59968582Seric /* make sure we actually do delivery */ 60068582Seric e->e_flags &= ~EF_CLRQUEUE; 60168582Seric 60267131Seric /* from now on, we have to operate silently */ 60368692Seric buffer_errors(); 60467131Seric e->e_errormode = EM_MAIL; 6054976Seric 6068238Seric /* 6078238Seric ** Arrange to send to everyone. 6088238Seric ** If sending to multiple people, mail back 6098238Seric ** errors rather than reporting directly. 6108238Seric ** In any case, don't mail back errors for 6118238Seric ** anything that has happened up to 6128238Seric ** now (the other end will do this). 61310197Seric ** Truncate our transcript -- the mail has gotten 61410197Seric ** to us successfully, and if we have 61510197Seric ** to mail this back, it will be easier 61610197Seric ** on the reader. 6178238Seric ** Then send to everyone. 6188238Seric ** Finally give a reply code. If an error has 6198238Seric ** already been given, don't mail a 6208238Seric ** message back. 6219339Seric ** We goose error returns by clearing error bit. 6228238Seric */ 6238238Seric 62424943Seric SmtpPhase = "delivery"; 62555012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 62658714Seric id = e->e_id; 6274976Seric 62867131Seric if (doublequeue) 62959730Seric { 63067131Seric /* make sure it is in the queue */ 63167131Seric queueup(e, TRUE, FALSE); 63268035Seric if (e->e_sendmode == SM_QUEUE) 63368035Seric e->e_flags |= EF_KEEPQUEUE; 63459730Seric } 6358238Seric else 63658919Seric { 63767131Seric /* send to all recipients */ 63867131Seric sendall(e, SM_DEFAULT); 63967131Seric } 64067131Seric e->e_to = NULL; 64159747Seric 64267131Seric /* issue success message */ 64367131Seric message("250 %s Message accepted for delivery", id); 64464296Seric 64567131Seric /* if we just queued, poke it */ 64667131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 64767131Seric { 64867131Seric extern pid_t dowork(); 64967131Seric 65067131Seric unlockqueue(e); 65167131Seric (void) dowork(id, TRUE, TRUE, e); 65258919Seric } 65358883Seric 65459747Seric abortmessage: 6559339Seric /* if in a child, pop back to our parent */ 6569339Seric if (InChild) 6579339Seric finis(); 65824943Seric 65924943Seric /* clean up a bit */ 66058109Seric gotmail = FALSE; 66155012Seric dropenvelope(e); 66258179Seric CurEnv = e = newenvelope(e, CurEnv); 66355012Seric e->e_flags = BlankEnvelope.e_flags; 6644549Seric break; 6654549Seric 6664549Seric case CMDRSET: /* rset -- reset state */ 66758151Seric message("250 Reset state"); 66868603Seric 66968603Seric /* arrange to ignore any current send list */ 67068603Seric e->e_sendqueue = NULL; 67164359Seric e->e_flags |= EF_CLRQUEUE; 6729339Seric if (InChild) 6739339Seric finis(); 67458109Seric 67558109Seric /* clean up a bit */ 67658109Seric gotmail = FALSE; 67758109Seric dropenvelope(e); 67858179Seric CurEnv = e = newenvelope(e, CurEnv); 6799339Seric break; 6804549Seric 6814549Seric case CMDVRFY: /* vrfy -- verify address */ 68258092Seric case CMDEXPN: /* expn -- expand address */ 68358092Seric vrfy = c->cmdcode == CMDVRFY; 68458092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 68558092Seric PrivacyFlags)) 68658082Seric { 68758412Seric if (vrfy) 68867160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 68958412Seric else 69065192Seric message("502 Sorry, we do not allow this operation"); 69165017Seric #ifdef LOG 69265017Seric if (LogLevel > 5) 69365017Seric syslog(LOG_INFO, "%s: %s [rejected]", 69465017Seric CurSmtpClient, inp); 69565017Seric #endif 69658082Seric break; 69758082Seric } 69858082Seric else if (!gothello && 69958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 70058092Seric PrivacyFlags)) 70158082Seric { 70258151Seric message("503 I demand that you introduce yourself first"); 70358082Seric break; 70458082Seric } 70558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7069339Seric break; 70755173Seric #ifdef LOG 70858020Seric if (LogLevel > 5) 70965017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 71055173Seric #endif 7115003Seric vrfyqueue = NULL; 7127762Seric QuickAbort = TRUE; 71358092Seric if (vrfy) 71458092Seric e->e_flags |= EF_VRFYONLY; 71562373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 71667615Seric p++; 71762373Seric if (*p == '\0') 71862373Seric { 71962373Seric message("501 Argument required"); 72062373Seric Errors++; 72162373Seric } 72262373Seric else 72362373Seric { 72467990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 72562373Seric } 7267762Seric if (Errors != 0) 7279339Seric { 7289339Seric if (InChild) 7299339Seric finis(); 7307762Seric break; 7319339Seric } 73262373Seric if (vrfyqueue == NULL) 73362373Seric { 73462373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 73562373Seric } 7365003Seric while (vrfyqueue != NULL) 7375003Seric { 73869748Seric extern void printvrfyaddr __P((ADDRESS *, bool)); 73969748Seric 74063971Seric a = vrfyqueue; 74163971Seric while ((a = a->q_next) != NULL && 74263971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 74363971Seric continue; 7447685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 74558151Seric printvrfyaddr(vrfyqueue, a == NULL); 74663847Seric vrfyqueue = vrfyqueue->q_next; 7475003Seric } 7489339Seric if (InChild) 7499339Seric finis(); 7504549Seric break; 7514549Seric 7524549Seric case CMDHELP: /* help -- give user info */ 7534577Seric help(p); 7544549Seric break; 7554549Seric 7564549Seric case CMDNOOP: /* noop -- do nothing */ 75764122Seric message("250 OK"); 7584549Seric break; 7594549Seric 7604549Seric case CMDQUIT: /* quit -- leave mail */ 76158151Seric message("221 %s closing connection", MyHostName); 76261051Seric 76366283Seric doquit: 76468603Seric /* arrange to ignore any current send list */ 76568603Seric e->e_sendqueue = NULL; 76668603Seric 76761051Seric /* avoid future 050 messages */ 76866017Seric disconnect(1, e); 76961051Seric 7709339Seric if (InChild) 7719339Seric ExitStat = EX_QUIT; 7724549Seric finis(); 7734549Seric 7748544Seric case CMDVERB: /* set verbose mode */ 77559957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 77659957Seric { 77759957Seric /* this would give out the same info */ 77859957Seric message("502 Verbose unavailable"); 77959957Seric break; 78059957Seric } 7818544Seric Verbose = TRUE; 78258734Seric e->e_sendmode = SM_DELIVER; 78359957Seric message("250 Verbose mode"); 7848544Seric break; 7858544Seric 7869314Seric case CMDONEX: /* doing one transaction only */ 7879378Seric OneXact = TRUE; 78859957Seric message("250 Only one transaction"); 7899314Seric break; 7909314Seric 79136230Skarels # ifdef SMTPDEBUG 7929339Seric case CMDDBGQSHOW: /* show queues */ 7936907Seric printf("Send Queue="); 79455012Seric printaddr(e->e_sendqueue, TRUE); 7955003Seric break; 7967275Seric 7977275Seric case CMDDBGDEBUG: /* set debug mode */ 7987676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7997676Seric tTflag(p); 80058151Seric message("200 Debug set"); 8017275Seric break; 8027275Seric 80336230Skarels # else /* not SMTPDEBUG */ 80436230Skarels case CMDDBGQSHOW: /* show queues */ 80536230Skarels case CMDDBGDEBUG: /* set debug mode */ 80664685Seric # endif /* SMTPDEBUG */ 80764685Seric case CMDLOGBOGUS: /* bogus command */ 80836233Skarels # ifdef LOG 80958308Seric if (LogLevel > 0) 81064685Seric syslog(LOG_CRIT, 81158020Seric "\"%s\" command from %s (%s)", 81266005Seric c->cmdname, peerhostname, 81358755Seric anynet_ntoa(&RealHostAddr)); 81436233Skarels # endif 81536230Skarels /* FALL THROUGH */ 81636230Skarels 8174549Seric case CMDERROR: /* unknown command */ 81866283Seric if (++badcommands > MAXBADCOMMANDS) 81966283Seric { 82066283Seric message("421 %s Too many bad commands; closing connection", 82166283Seric MyHostName); 82266283Seric goto doquit; 82366283Seric } 82466283Seric 82558151Seric message("500 Command unrecognized"); 8264549Seric break; 8274549Seric 8284549Seric default: 82936230Skarels errno = 0; 83058151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8314549Seric break; 8324549Seric } 8334549Seric } 8344549Seric } 8354549Seric /* 8364549Seric ** SKIPWORD -- skip a fixed word. 8374549Seric ** 8384549Seric ** Parameters: 8394549Seric ** p -- place to start looking. 8404549Seric ** w -- word to skip. 8414549Seric ** 8424549Seric ** Returns: 8434549Seric ** p following w. 8444549Seric ** NULL on error. 8454549Seric ** 8464549Seric ** Side Effects: 8474549Seric ** clobbers the p data area. 8484549Seric */ 8494549Seric 8504549Seric static char * 8514549Seric skipword(p, w) 8524549Seric register char *p; 8534549Seric char *w; 8544549Seric { 8554549Seric register char *q; 85666005Seric char *firstp = p; 8574549Seric 8584549Seric /* find beginning of word */ 85958050Seric while (isascii(*p) && isspace(*p)) 8604549Seric p++; 8614549Seric q = p; 8624549Seric 8634549Seric /* find end of word */ 86458050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8654549Seric p++; 86658050Seric while (isascii(*p) && isspace(*p)) 8674549Seric *p++ = '\0'; 8684549Seric if (*p != ':') 8694549Seric { 8704549Seric syntax: 87166005Seric message("501 Syntax error in parameters scanning \"%s\"", 87266005Seric firstp); 8734549Seric Errors++; 8744549Seric return (NULL); 8754549Seric } 8764549Seric *p++ = '\0'; 87758050Seric while (isascii(*p) && isspace(*p)) 8784549Seric p++; 8794549Seric 88062373Seric if (*p == '\0') 88162373Seric goto syntax; 88262373Seric 8834549Seric /* see if the input word matches desired word */ 88433725Sbostic if (strcasecmp(q, w)) 8854549Seric goto syntax; 8864549Seric 8874549Seric return (p); 8884549Seric } 8894577Seric /* 89068559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 89168559Seric ** 89268559Seric ** Parameters: 89368559Seric ** kp -- the parameter key. 89468559Seric ** vp -- the value of that parameter. 89568559Seric ** e -- the envelope. 89668559Seric ** 89768559Seric ** Returns: 89868559Seric ** none. 89968559Seric */ 90068559Seric 90169748Seric void 90268559Seric mail_esmtp_args(kp, vp, e) 90368559Seric char *kp; 90468559Seric char *vp; 90568559Seric ENVELOPE *e; 90668559Seric { 90768559Seric if (strcasecmp(kp, "size") == 0) 90868559Seric { 90968559Seric if (vp == NULL) 91068559Seric { 91168559Seric usrerr("501 SIZE requires a value"); 91268559Seric /* NOTREACHED */ 91368559Seric } 91468890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) 91568560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10); 91668559Seric # else 91768560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10); 91868559Seric # endif 91968559Seric } 92068559Seric else if (strcasecmp(kp, "body") == 0) 92168559Seric { 92268559Seric if (vp == NULL) 92368559Seric { 92468559Seric usrerr("501 BODY requires a value"); 92568559Seric /* NOTREACHED */ 92668559Seric } 92768559Seric if (strcasecmp(vp, "8bitmime") == 0) 92868559Seric { 92968559Seric SevenBitInput = FALSE; 93068883Seric e->e_flags |= EF_NL_NOT_EOL; 93168559Seric } 93268559Seric else if (strcasecmp(vp, "7bit") == 0) 93368559Seric { 93468559Seric SevenBitInput = TRUE; 93568559Seric } 93668559Seric else 93768559Seric { 93868559Seric usrerr("501 Unknown BODY type %s", 93968559Seric vp); 94068559Seric /* NOTREACHED */ 94168559Seric } 94268559Seric e->e_bodytype = newstr(vp); 94368559Seric } 94468559Seric else if (strcasecmp(kp, "envid") == 0) 94568559Seric { 94668559Seric if (vp == NULL) 94768559Seric { 94868559Seric usrerr("501 ENVID requires a value"); 94968559Seric /* NOTREACHED */ 95068559Seric } 95168583Seric if (!xtextok(vp)) 95268583Seric { 95368583Seric usrerr("501 Syntax error in ENVID parameter value"); 95468583Seric /* NOTREACHED */ 95568583Seric } 95668583Seric if (e->e_envid != NULL) 95768583Seric { 95868583Seric usrerr("501 Duplicate ENVID parameter"); 95968583Seric /* NOTREACHED */ 96068583Seric } 96168559Seric e->e_envid = newstr(vp); 96268559Seric } 96368559Seric else if (strcasecmp(kp, "ret") == 0) 96468559Seric { 96568559Seric if (vp == NULL) 96668559Seric { 96768559Seric usrerr("501 RET requires a value"); 96868559Seric /* NOTREACHED */ 96968559Seric } 97068583Seric if (bitset(EF_RET_PARAM, e->e_flags)) 97168583Seric { 97268583Seric usrerr("501 Duplicate RET parameter"); 97368583Seric /* NOTREACHED */ 97468583Seric } 97568559Seric e->e_flags |= EF_RET_PARAM; 97668559Seric if (strcasecmp(vp, "hdrs") == 0) 97768559Seric e->e_flags |= EF_NO_BODY_RETN; 97868559Seric else if (strcasecmp(vp, "full") != 0) 97968559Seric { 98068559Seric usrerr("501 Bad argument \"%s\" to RET", vp); 98168559Seric /* NOTREACHED */ 98268559Seric } 98368559Seric } 98468559Seric else 98568559Seric { 98668559Seric usrerr("501 %s parameter unrecognized", kp); 98768559Seric /* NOTREACHED */ 98868559Seric } 98968559Seric } 99068559Seric /* 99167963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 99267963Seric ** 99367963Seric ** Parameters: 99467963Seric ** a -- the address corresponding to the To: parameter. 99567963Seric ** kp -- the parameter key. 99667963Seric ** vp -- the value of that parameter. 99767963Seric ** e -- the envelope. 99867963Seric ** 99967963Seric ** Returns: 100067963Seric ** none. 100167963Seric */ 100267963Seric 100369748Seric void 100467963Seric rcpt_esmtp_args(a, kp, vp, e) 100567963Seric ADDRESS *a; 100667963Seric char *kp; 100767963Seric char *vp; 100867963Seric ENVELOPE *e; 100967963Seric { 101067963Seric if (strcasecmp(kp, "notify") == 0) 101167963Seric { 101267963Seric char *p; 101367963Seric 101467963Seric if (vp == NULL) 101567963Seric { 101667963Seric usrerr("501 NOTIFY requires a value"); 101767963Seric /* NOTREACHED */ 101867963Seric } 101967963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 102068595Seric a->q_flags |= QHASNOTIFY; 102167963Seric if (strcasecmp(vp, "never") == 0) 102267963Seric return; 102367963Seric for (p = vp; p != NULL; vp = p) 102467963Seric { 102567963Seric p = strchr(p, ','); 102667963Seric if (p != NULL) 102767963Seric *p++ = '\0'; 102867963Seric if (strcasecmp(vp, "success") == 0) 102967963Seric a->q_flags |= QPINGONSUCCESS; 103067963Seric else if (strcasecmp(vp, "failure") == 0) 103167963Seric a->q_flags |= QPINGONFAILURE; 103267963Seric else if (strcasecmp(vp, "delay") == 0) 103367963Seric a->q_flags |= QPINGONDELAY; 103467963Seric else 103567963Seric { 103667963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 103767963Seric vp); 103867963Seric /* NOTREACHED */ 103967963Seric } 104067963Seric } 104167963Seric } 104267963Seric else if (strcasecmp(kp, "orcpt") == 0) 104367963Seric { 104467963Seric if (vp == NULL) 104567963Seric { 104667963Seric usrerr("501 ORCPT requires a value"); 104767963Seric /* NOTREACHED */ 104867963Seric } 104968583Seric if (!xtextok(vp)) 105068583Seric { 105168583Seric usrerr("501 Syntax error in ORCPT parameter value"); 105268583Seric /* NOTREACHED */ 105368583Seric } 105468583Seric if (a->q_orcpt != NULL) 105568583Seric { 105668583Seric usrerr("501 Duplicate ORCPT parameter"); 105768583Seric /* NOTREACHED */ 105868583Seric } 105967963Seric a->q_orcpt = newstr(vp); 106067963Seric } 106167963Seric else 106267963Seric { 106367963Seric usrerr("501 %s parameter unrecognized", kp); 106467963Seric /* NOTREACHED */ 106567963Seric } 106667963Seric } 106767963Seric /* 106858151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 106958151Seric ** 107058151Seric ** Parameters: 107158151Seric ** a -- the address to print 107258151Seric ** last -- set if this is the last one. 107358151Seric ** 107458151Seric ** Returns: 107558151Seric ** none. 107658151Seric ** 107758151Seric ** Side Effects: 107858151Seric ** Prints the appropriate 250 codes. 107958151Seric */ 108058151Seric 108169748Seric void 108258151Seric printvrfyaddr(a, last) 108358151Seric register ADDRESS *a; 108458151Seric bool last; 108558151Seric { 108658151Seric char fmtbuf[20]; 108758151Seric 108858151Seric strcpy(fmtbuf, "250"); 108958151Seric fmtbuf[3] = last ? ' ' : '-'; 109058151Seric 109159746Seric if (a->q_fullname == NULL) 109259746Seric { 109359746Seric if (strchr(a->q_user, '@') == NULL) 109459746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 109559746Seric else 109659746Seric strcpy(&fmtbuf[4], "<%s>"); 109759746Seric message(fmtbuf, a->q_user, MyHostName); 109859746Seric } 109958151Seric else 110058151Seric { 110159746Seric if (strchr(a->q_user, '@') == NULL) 110259746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 110359746Seric else 110459746Seric strcpy(&fmtbuf[4], "%s <%s>"); 110559746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 110658151Seric } 110758151Seric } 110858151Seric /* 11094577Seric ** HELP -- implement the HELP command. 11104577Seric ** 11114577Seric ** Parameters: 11124577Seric ** topic -- the topic we want help for. 11134577Seric ** 11144577Seric ** Returns: 11154577Seric ** none. 11164577Seric ** 11174577Seric ** Side Effects: 11184577Seric ** outputs the help file to message output. 11194577Seric */ 11204577Seric 112169748Seric void 11224577Seric help(topic) 11234577Seric char *topic; 11244577Seric { 11254577Seric register FILE *hf; 11264577Seric int len; 112768751Seric bool noinfo; 11284577Seric char buf[MAXLINE]; 112968751Seric extern char Version[]; 11304577Seric 113168751Seric 11328269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 11334577Seric { 11344577Seric /* no help */ 113511931Seric errno = 0; 113668751Seric message("502 Sendmail %s -- HELP not implemented", Version); 11374577Seric return; 11384577Seric } 11394577Seric 114049669Seric if (topic == NULL || *topic == '\0') 114168751Seric { 114249669Seric topic = "smtp"; 114368751Seric message("214-This is Sendmail version %s", Version); 114468751Seric noinfo = FALSE; 114568751Seric } 114649669Seric else 114768751Seric { 114849669Seric makelower(topic); 114968751Seric noinfo = TRUE; 115068751Seric } 115149669Seric 11524577Seric len = strlen(topic); 11534577Seric 11544577Seric while (fgets(buf, sizeof buf, hf) != NULL) 11554577Seric { 11564577Seric if (strncmp(buf, topic, len) == 0) 11574577Seric { 11584577Seric register char *p; 11594577Seric 116056795Seric p = strchr(buf, '\t'); 11614577Seric if (p == NULL) 11624577Seric p = buf; 11634577Seric else 11644577Seric p++; 11654577Seric fixcrlf(p, TRUE); 116658151Seric message("214-%s", p); 11674577Seric noinfo = FALSE; 11684577Seric } 11694577Seric } 11704577Seric 11714577Seric if (noinfo) 117258151Seric message("504 HELP topic unknown"); 11734577Seric else 117458151Seric message("214 End of HELP info"); 11754628Seric (void) fclose(hf); 11764577Seric } 11778544Seric /* 11789339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11799339Seric ** 11809339Seric ** Parameters: 11819339Seric ** label -- a string used in error messages 11829339Seric ** 11839339Seric ** Returns: 11849339Seric ** zero in the child 11859339Seric ** one in the parent 11869339Seric ** 11879339Seric ** Side Effects: 11889339Seric ** none. 11899339Seric */ 11908544Seric 119169748Seric int 119255012Seric runinchild(label, e) 11939339Seric char *label; 119455012Seric register ENVELOPE *e; 11959339Seric { 11969339Seric int childpid; 11979339Seric 119816158Seric if (!OneXact) 11999339Seric { 120016158Seric childpid = dofork(); 120116158Seric if (childpid < 0) 120216158Seric { 120369716Seric syserr("451 %s: cannot fork", label); 120416158Seric return (1); 120516158Seric } 120616158Seric if (childpid > 0) 120716158Seric { 120816158Seric auto int st; 12099339Seric 121016158Seric /* parent -- wait for child to complete */ 121161093Seric setproctitle("server %s child wait", CurHostName); 121216158Seric st = waitfor(childpid); 121316158Seric if (st == -1) 121469716Seric syserr("451 %s: lost child", label); 121564948Seric else if (!WIFEXITED(st)) 121669716Seric syserr("451 %s: died on signal %d", 121764948Seric label, st & 0177); 12189339Seric 121916158Seric /* if we exited on a QUIT command, complete the process */ 122066017Seric if (WEXITSTATUS(st) == EX_QUIT) 122166017Seric { 122266017Seric disconnect(1, e); 122316158Seric finis(); 122466017Seric } 12259339Seric 122616158Seric return (1); 122716158Seric } 122816158Seric else 122916158Seric { 123016158Seric /* child */ 123116158Seric InChild = TRUE; 123225050Seric QuickAbort = FALSE; 123355012Seric clearenvelope(e, FALSE); 123416158Seric } 12359339Seric } 123615256Seric 123716158Seric /* open alias database */ 123860537Seric initmaps(FALSE, e); 123916158Seric 124016158Seric return (0); 12419339Seric } 12429339Seric 124356795Seric # endif /* SMTP */ 1244