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*69480Seric static char sccsid[] = "@(#)srvrsmtp.c 8.72 (Berkeley) 05/15/95 (with SMTP)"; 1433731Sbostic #else 15*69480Seric static char sccsid[] = "@(#)srvrsmtp.c 8.72 (Berkeley) 05/15/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'; 15369106Seric if (isascii(*id) && isspace(*id)) 15469106Seric id++; 15568769Seric message("220-%s", id); 15664496Seric } 15768769Seric if (id != NULL) 15869106Seric { 15969106Seric if (isascii(*id) && isspace(*id)) 16069106Seric id++; 16168769Seric message("220 %s", id); 16269106Seric } 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"); 308*69480Seric #if MIME8TO7 30967417Seric message("250-8BITMIME"); 310*69480Seric #endif 31164359Seric if (MaxMessageSize > 0) 31264359Seric message("250-SIZE %ld", MaxMessageSize); 31359271Seric else 31459271Seric message("250-SIZE"); 31568848Seric #if DSN 31668606Seric message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)"); 31768028Seric #endif 31858323Seric message("250 HELP"); 3194976Seric break; 3204976Seric 3214549Seric case CMDMAIL: /* mail -- designate sender */ 32261093Seric SmtpPhase = "server MAIL"; 32324943Seric 3249314Seric /* check for validity of this command */ 32558789Seric if (!gothello) 32658082Seric { 32758957Seric /* set sending host to our known value */ 32859016Seric if (sendinghost == NULL) 32966005Seric sendinghost = peerhostname; 33058957Seric 33158789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 33258821Seric { 33358789Seric message("503 Polite people say HELO first"); 33458821Seric break; 33558821Seric } 33658082Seric } 33758109Seric if (gotmail) 3384558Seric { 33958151Seric message("503 Sender already specified"); 34063843Seric if (InChild) 34163843Seric finis(); 3424558Seric break; 3434558Seric } 3449339Seric if (InChild) 3459339Seric { 34636230Skarels errno = 0; 34758151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34858069Seric finis(); 3499339Seric } 3509339Seric 3519339Seric /* fork a subprocess to process this command */ 35255012Seric if (runinchild("SMTP-MAIL", e) > 0) 3539339Seric break; 35463753Seric if (!gothello) 35563753Seric { 35663753Seric auth_warning(e, 35766005Seric "Host %s didn't use HELO protocol", 35866005Seric peerhostname); 35963753Seric } 36065947Seric #ifdef PICKY_HELO_CHECK 36166005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 36266005Seric (strcasecmp(peerhostname, "localhost") != 0 || 36365823Seric strcasecmp(sendinghost, MyHostName) != 0)) 36465823Seric { 36565823Seric auth_warning(e, "Host %s claimed to be %s", 36666005Seric peerhostname, sendinghost); 36765823Seric } 36865947Seric #endif 36965823Seric 37058323Seric if (protocol == NULL) 37158323Seric protocol = "SMTP"; 37258323Seric define('r', protocol, e); 37359016Seric define('s', sendinghost, e); 37455012Seric initsys(e); 37559747Seric nrcpts = 0; 37668582Seric e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; 37765058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3789339Seric 3799339Seric /* child -- go do the processing */ 3804549Seric p = skipword(p, "from"); 3814549Seric if (p == NULL) 3824549Seric break; 38357977Seric if (setjmp(TopFrame) > 0) 38458147Seric { 38558147Seric /* this failed -- undo work */ 38658147Seric if (InChild) 38759058Seric { 38859058Seric QuickAbort = FALSE; 38959058Seric SuprErrs = TRUE; 39063787Seric e->e_flags &= ~EF_FATALERRS; 39158147Seric finis(); 39259058Seric } 39357977Seric break; 39458147Seric } 39557977Seric QuickAbort = TRUE; 39658333Seric 39758333Seric /* must parse sender first */ 39858333Seric delimptr = NULL; 39958704Seric setsender(p, e, &delimptr, FALSE); 40058333Seric p = delimptr; 40158333Seric if (p != NULL && *p != '\0') 40258333Seric *p++ = '\0'; 40358333Seric 40466325Seric /* check for possible spoofing */ 40566325Seric if (RealUid != 0 && OpMode == MD_SMTP && 40667473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40767473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40866325Seric { 40966325Seric auth_warning(e, "%s owned process doing -bs", 41066325Seric RealUserName); 41166325Seric } 41266325Seric 41358333Seric /* now parse ESMTP arguments */ 41468560Seric e->e_msgsize = 0; 41566764Seric while (p != NULL && *p != '\0') 41658333Seric { 41758333Seric char *kp; 41866304Seric char *vp = NULL; 41958333Seric 42058333Seric /* locate the beginning of the keyword */ 42158333Seric while (isascii(*p) && isspace(*p)) 42258333Seric p++; 42358333Seric if (*p == '\0') 42458333Seric break; 42558333Seric kp = p; 42658333Seric 42758333Seric /* skip to the value portion */ 42858333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42958333Seric p++; 43058333Seric if (*p == '=') 43158333Seric { 43258333Seric *p++ = '\0'; 43358333Seric vp = p; 43458333Seric 43558333Seric /* skip to the end of the value */ 43658333Seric while (*p != '\0' && *p != ' ' && 43758333Seric !(isascii(*p) && iscntrl(*p)) && 43858333Seric *p != '=') 43958333Seric p++; 44058333Seric } 44158333Seric 44258333Seric if (*p != '\0') 44358333Seric *p++ = '\0'; 44458333Seric 44558333Seric if (tTd(19, 1)) 44666764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 44758333Seric vp == NULL ? "<null>" : vp); 44858333Seric 44968559Seric mail_esmtp_args(kp, vp, e); 45058333Seric } 45159284Seric 45268560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 45359284Seric { 45459284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 45559284Seric MaxMessageSize); 45659284Seric /* NOTREACHED */ 45759284Seric } 45858333Seric 45968560Seric if (!enoughspace(e->e_msgsize)) 46058333Seric { 46158333Seric message("452 Insufficient disk space; try again later"); 46258333Seric break; 46358333Seric } 46458151Seric message("250 Sender ok"); 46558147Seric gotmail = TRUE; 4664549Seric break; 4674549Seric 4684976Seric case CMDRCPT: /* rcpt -- designate recipient */ 46958850Seric if (!gotmail) 47058850Seric { 47158850Seric usrerr("503 Need MAIL before RCPT"); 47258850Seric break; 47358850Seric } 47461093Seric SmtpPhase = "server RCPT"; 47512612Seric if (setjmp(TopFrame) > 0) 47614785Seric { 47755012Seric e->e_flags &= ~EF_FATALERRS; 47812612Seric break; 47914785Seric } 48012612Seric QuickAbort = TRUE; 48151951Seric LogUsrErrs = TRUE; 48258093Seric 48359699Seric if (e->e_sendmode != SM_DELIVER) 48459699Seric e->e_flags |= EF_VRFYONLY; 48558919Seric 4864549Seric p = skipword(p, "to"); 4874549Seric if (p == NULL) 4884549Seric break; 48967880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 49012612Seric if (a == NULL) 49112612Seric break; 49267880Seric p = delimptr; 49367880Seric 49467880Seric /* now parse ESMTP arguments */ 49567880Seric while (p != NULL && *p != '\0') 49667880Seric { 49767880Seric char *kp; 49867880Seric char *vp = NULL; 49967880Seric 50067880Seric /* locate the beginning of the keyword */ 50167880Seric while (isascii(*p) && isspace(*p)) 50267880Seric p++; 50367880Seric if (*p == '\0') 50467880Seric break; 50567880Seric kp = p; 50667880Seric 50767880Seric /* skip to the value portion */ 50867880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 50967880Seric p++; 51067880Seric if (*p == '=') 51167880Seric { 51267880Seric *p++ = '\0'; 51367880Seric vp = p; 51467880Seric 51567880Seric /* skip to the end of the value */ 51667880Seric while (*p != '\0' && *p != ' ' && 51767880Seric !(isascii(*p) && iscntrl(*p)) && 51867880Seric *p != '=') 51967880Seric p++; 52067880Seric } 52167880Seric 52267880Seric if (*p != '\0') 52367880Seric *p++ = '\0'; 52467880Seric 52567880Seric if (tTd(19, 1)) 52667880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 52767880Seric vp == NULL ? "<null>" : vp); 52867880Seric 52967963Seric rcpt_esmtp_args(a, kp, vp, e); 53067963Seric 53167880Seric } 53267980Seric 53367980Seric /* save in recipient list after ESMTP mods */ 53467982Seric a = recipient(a, &e->e_sendqueue, 0, e); 53567980Seric 53612612Seric if (Errors != 0) 53712612Seric break; 53812612Seric 53912612Seric /* no errors during parsing, but might be a duplicate */ 54055012Seric e->e_to = p; 54112612Seric if (!bitset(QBADADDR, a->q_flags)) 54259747Seric { 54364718Seric message("250 Recipient ok%s", 54464718Seric bitset(QQUEUEUP, a->q_flags) ? 54564718Seric " (will queue)" : ""); 54659747Seric nrcpts++; 54759747Seric } 54812612Seric else 5494549Seric { 55012612Seric /* punt -- should keep message in ADDRESS.... */ 55158151Seric message("550 Addressee unknown"); 5524549Seric } 55355012Seric e->e_to = NULL; 5544549Seric break; 5554549Seric 5564549Seric case CMDDATA: /* data -- text of mail */ 55761093Seric SmtpPhase = "server DATA"; 55858109Seric if (!gotmail) 5594549Seric { 56058151Seric message("503 Need MAIL command"); 5614976Seric break; 5624549Seric } 56364718Seric else if (nrcpts <= 0) 5644549Seric { 56558151Seric message("503 Need RCPT (recipient)"); 5664976Seric break; 5674549Seric } 5684976Seric 56958929Seric /* check to see if we need to re-expand aliases */ 57063787Seric /* also reset QBADADDR on already-diagnosted addrs */ 57163787Seric doublequeue = FALSE; 57258929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 57358929Seric { 57458929Seric if (bitset(QVERIFIED, a->q_flags)) 57563787Seric { 57663787Seric /* need to re-expand aliases */ 57763787Seric doublequeue = TRUE; 57863787Seric } 57963787Seric if (bitset(QBADADDR, a->q_flags)) 58063787Seric { 58163787Seric /* make this "go away" */ 58263787Seric a->q_flags |= QDONTSEND; 58363787Seric a->q_flags &= ~QBADADDR; 58463787Seric } 58558929Seric } 58658929Seric 5874976Seric /* collect the text of the message */ 58824943Seric SmtpPhase = "collect"; 58968692Seric buffer_errors(); 59067546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 59168692Seric flush_errors(TRUE); 59264766Seric if (Errors != 0) 59364766Seric goto abortmessage; 59467131Seric 59568582Seric /* make sure we actually do delivery */ 59668582Seric e->e_flags &= ~EF_CLRQUEUE; 59768582Seric 59867131Seric /* from now on, we have to operate silently */ 59968692Seric buffer_errors(); 60067131Seric e->e_errormode = EM_MAIL; 6014976Seric 6028238Seric /* 6038238Seric ** Arrange to send to everyone. 6048238Seric ** If sending to multiple people, mail back 6058238Seric ** errors rather than reporting directly. 6068238Seric ** In any case, don't mail back errors for 6078238Seric ** anything that has happened up to 6088238Seric ** now (the other end will do this). 60910197Seric ** Truncate our transcript -- the mail has gotten 61010197Seric ** to us successfully, and if we have 61110197Seric ** to mail this back, it will be easier 61210197Seric ** on the reader. 6138238Seric ** Then send to everyone. 6148238Seric ** Finally give a reply code. If an error has 6158238Seric ** already been given, don't mail a 6168238Seric ** message back. 6179339Seric ** We goose error returns by clearing error bit. 6188238Seric */ 6198238Seric 62024943Seric SmtpPhase = "delivery"; 62155012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 62258714Seric id = e->e_id; 6234976Seric 62467131Seric if (doublequeue) 62559730Seric { 62667131Seric /* make sure it is in the queue */ 62767131Seric queueup(e, TRUE, FALSE); 62868035Seric if (e->e_sendmode == SM_QUEUE) 62968035Seric e->e_flags |= EF_KEEPQUEUE; 63059730Seric } 6318238Seric else 63258919Seric { 63367131Seric /* send to all recipients */ 63467131Seric sendall(e, SM_DEFAULT); 63567131Seric } 63667131Seric e->e_to = NULL; 63759747Seric 63867131Seric /* issue success message */ 63967131Seric message("250 %s Message accepted for delivery", id); 64064296Seric 64167131Seric /* if we just queued, poke it */ 64267131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 64367131Seric { 64467131Seric extern pid_t dowork(); 64567131Seric 64667131Seric unlockqueue(e); 64767131Seric (void) dowork(id, TRUE, TRUE, e); 64858919Seric } 64958883Seric 65059747Seric abortmessage: 6519339Seric /* if in a child, pop back to our parent */ 6529339Seric if (InChild) 6539339Seric finis(); 65424943Seric 65524943Seric /* clean up a bit */ 65658109Seric gotmail = FALSE; 65755012Seric dropenvelope(e); 65858179Seric CurEnv = e = newenvelope(e, CurEnv); 65955012Seric e->e_flags = BlankEnvelope.e_flags; 6604549Seric break; 6614549Seric 6624549Seric case CMDRSET: /* rset -- reset state */ 66358151Seric message("250 Reset state"); 66468603Seric 66568603Seric /* arrange to ignore any current send list */ 66668603Seric e->e_sendqueue = NULL; 66764359Seric e->e_flags |= EF_CLRQUEUE; 6689339Seric if (InChild) 6699339Seric finis(); 67058109Seric 67158109Seric /* clean up a bit */ 67258109Seric gotmail = FALSE; 67358109Seric dropenvelope(e); 67458179Seric CurEnv = e = newenvelope(e, CurEnv); 6759339Seric break; 6764549Seric 6774549Seric case CMDVRFY: /* vrfy -- verify address */ 67858092Seric case CMDEXPN: /* expn -- expand address */ 67958092Seric vrfy = c->cmdcode == CMDVRFY; 68058092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 68158092Seric PrivacyFlags)) 68258082Seric { 68358412Seric if (vrfy) 68467160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 68558412Seric else 68665192Seric message("502 Sorry, we do not allow this operation"); 68765017Seric #ifdef LOG 68865017Seric if (LogLevel > 5) 68965017Seric syslog(LOG_INFO, "%s: %s [rejected]", 69065017Seric CurSmtpClient, inp); 69165017Seric #endif 69258082Seric break; 69358082Seric } 69458082Seric else if (!gothello && 69558092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 69658092Seric PrivacyFlags)) 69758082Seric { 69858151Seric message("503 I demand that you introduce yourself first"); 69958082Seric break; 70058082Seric } 70158092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7029339Seric break; 70355173Seric #ifdef LOG 70458020Seric if (LogLevel > 5) 70565017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 70655173Seric #endif 7075003Seric vrfyqueue = NULL; 7087762Seric QuickAbort = TRUE; 70958092Seric if (vrfy) 71058092Seric e->e_flags |= EF_VRFYONLY; 71162373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 71267615Seric p++; 71362373Seric if (*p == '\0') 71462373Seric { 71562373Seric message("501 Argument required"); 71662373Seric Errors++; 71762373Seric } 71862373Seric else 71962373Seric { 72067990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 72162373Seric } 7227762Seric if (Errors != 0) 7239339Seric { 7249339Seric if (InChild) 7259339Seric finis(); 7267762Seric break; 7279339Seric } 72862373Seric if (vrfyqueue == NULL) 72962373Seric { 73062373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 73162373Seric } 7325003Seric while (vrfyqueue != NULL) 7335003Seric { 73463971Seric a = vrfyqueue; 73563971Seric while ((a = a->q_next) != NULL && 73663971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 73763971Seric continue; 7387685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 73958151Seric printvrfyaddr(vrfyqueue, a == NULL); 74063847Seric vrfyqueue = vrfyqueue->q_next; 7415003Seric } 7429339Seric if (InChild) 7439339Seric finis(); 7444549Seric break; 7454549Seric 7464549Seric case CMDHELP: /* help -- give user info */ 7474577Seric help(p); 7484549Seric break; 7494549Seric 7504549Seric case CMDNOOP: /* noop -- do nothing */ 75164122Seric message("250 OK"); 7524549Seric break; 7534549Seric 7544549Seric case CMDQUIT: /* quit -- leave mail */ 75558151Seric message("221 %s closing connection", MyHostName); 75661051Seric 75766283Seric doquit: 75868603Seric /* arrange to ignore any current send list */ 75968603Seric e->e_sendqueue = NULL; 76068603Seric 76161051Seric /* avoid future 050 messages */ 76266017Seric disconnect(1, e); 76361051Seric 7649339Seric if (InChild) 7659339Seric ExitStat = EX_QUIT; 7664549Seric finis(); 7674549Seric 7688544Seric case CMDVERB: /* set verbose mode */ 76959957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 77059957Seric { 77159957Seric /* this would give out the same info */ 77259957Seric message("502 Verbose unavailable"); 77359957Seric break; 77459957Seric } 7758544Seric Verbose = TRUE; 77658734Seric e->e_sendmode = SM_DELIVER; 77759957Seric message("250 Verbose mode"); 7788544Seric break; 7798544Seric 7809314Seric case CMDONEX: /* doing one transaction only */ 7819378Seric OneXact = TRUE; 78259957Seric message("250 Only one transaction"); 7839314Seric break; 7849314Seric 78536230Skarels # ifdef SMTPDEBUG 7869339Seric case CMDDBGQSHOW: /* show queues */ 7876907Seric printf("Send Queue="); 78855012Seric printaddr(e->e_sendqueue, TRUE); 7895003Seric break; 7907275Seric 7917275Seric case CMDDBGDEBUG: /* set debug mode */ 7927676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7937676Seric tTflag(p); 79458151Seric message("200 Debug set"); 7957275Seric break; 7967275Seric 79736230Skarels # else /* not SMTPDEBUG */ 79836230Skarels case CMDDBGQSHOW: /* show queues */ 79936230Skarels case CMDDBGDEBUG: /* set debug mode */ 80064685Seric # endif /* SMTPDEBUG */ 80164685Seric case CMDLOGBOGUS: /* bogus command */ 80236233Skarels # ifdef LOG 80358308Seric if (LogLevel > 0) 80464685Seric syslog(LOG_CRIT, 80558020Seric "\"%s\" command from %s (%s)", 80666005Seric c->cmdname, peerhostname, 80758755Seric anynet_ntoa(&RealHostAddr)); 80836233Skarels # endif 80936230Skarels /* FALL THROUGH */ 81036230Skarels 8114549Seric case CMDERROR: /* unknown command */ 81266283Seric if (++badcommands > MAXBADCOMMANDS) 81366283Seric { 81466283Seric message("421 %s Too many bad commands; closing connection", 81566283Seric MyHostName); 81666283Seric goto doquit; 81766283Seric } 81866283Seric 81958151Seric message("500 Command unrecognized"); 8204549Seric break; 8214549Seric 8224549Seric default: 82336230Skarels errno = 0; 82458151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8254549Seric break; 8264549Seric } 8274549Seric } 8284549Seric } 8294549Seric /* 8304549Seric ** SKIPWORD -- skip a fixed word. 8314549Seric ** 8324549Seric ** Parameters: 8334549Seric ** p -- place to start looking. 8344549Seric ** w -- word to skip. 8354549Seric ** 8364549Seric ** Returns: 8374549Seric ** p following w. 8384549Seric ** NULL on error. 8394549Seric ** 8404549Seric ** Side Effects: 8414549Seric ** clobbers the p data area. 8424549Seric */ 8434549Seric 8444549Seric static char * 8454549Seric skipword(p, w) 8464549Seric register char *p; 8474549Seric char *w; 8484549Seric { 8494549Seric register char *q; 85066005Seric char *firstp = p; 8514549Seric 8524549Seric /* find beginning of word */ 85358050Seric while (isascii(*p) && isspace(*p)) 8544549Seric p++; 8554549Seric q = p; 8564549Seric 8574549Seric /* find end of word */ 85858050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8594549Seric p++; 86058050Seric while (isascii(*p) && isspace(*p)) 8614549Seric *p++ = '\0'; 8624549Seric if (*p != ':') 8634549Seric { 8644549Seric syntax: 86566005Seric message("501 Syntax error in parameters scanning \"%s\"", 86666005Seric firstp); 8674549Seric Errors++; 8684549Seric return (NULL); 8694549Seric } 8704549Seric *p++ = '\0'; 87158050Seric while (isascii(*p) && isspace(*p)) 8724549Seric p++; 8734549Seric 87462373Seric if (*p == '\0') 87562373Seric goto syntax; 87662373Seric 8774549Seric /* see if the input word matches desired word */ 87833725Sbostic if (strcasecmp(q, w)) 8794549Seric goto syntax; 8804549Seric 8814549Seric return (p); 8824549Seric } 8834577Seric /* 88468559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 88568559Seric ** 88668559Seric ** Parameters: 88768559Seric ** kp -- the parameter key. 88868559Seric ** vp -- the value of that parameter. 88968559Seric ** e -- the envelope. 89068559Seric ** 89168559Seric ** Returns: 89268559Seric ** none. 89368559Seric */ 89468559Seric 89568559Seric mail_esmtp_args(kp, vp, e) 89668559Seric char *kp; 89768559Seric char *vp; 89868559Seric ENVELOPE *e; 89968559Seric { 90068559Seric if (strcasecmp(kp, "size") == 0) 90168559Seric { 90268559Seric if (vp == NULL) 90368559Seric { 90468559Seric usrerr("501 SIZE requires a value"); 90568559Seric /* NOTREACHED */ 90668559Seric } 90768890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) 90868560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10); 90968559Seric # else 91068560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10); 91168559Seric # endif 91268559Seric } 91368559Seric else if (strcasecmp(kp, "body") == 0) 91468559Seric { 91568559Seric if (vp == NULL) 91668559Seric { 91768559Seric usrerr("501 BODY requires a value"); 91868559Seric /* NOTREACHED */ 91968559Seric } 92068559Seric if (strcasecmp(vp, "8bitmime") == 0) 92168559Seric { 92268559Seric SevenBitInput = FALSE; 92368883Seric e->e_flags |= EF_NL_NOT_EOL; 92468559Seric } 92568559Seric else if (strcasecmp(vp, "7bit") == 0) 92668559Seric { 92768559Seric SevenBitInput = TRUE; 92868559Seric } 92968559Seric else 93068559Seric { 93168559Seric usrerr("501 Unknown BODY type %s", 93268559Seric vp); 93368559Seric /* NOTREACHED */ 93468559Seric } 93568559Seric e->e_bodytype = newstr(vp); 93668559Seric } 93768559Seric else if (strcasecmp(kp, "envid") == 0) 93868559Seric { 93968559Seric if (vp == NULL) 94068559Seric { 94168559Seric usrerr("501 ENVID requires a value"); 94268559Seric /* NOTREACHED */ 94368559Seric } 94468583Seric if (!xtextok(vp)) 94568583Seric { 94668583Seric usrerr("501 Syntax error in ENVID parameter value"); 94768583Seric /* NOTREACHED */ 94868583Seric } 94968583Seric if (e->e_envid != NULL) 95068583Seric { 95168583Seric usrerr("501 Duplicate ENVID parameter"); 95268583Seric /* NOTREACHED */ 95368583Seric } 95468559Seric e->e_envid = newstr(vp); 95568559Seric } 95668559Seric else if (strcasecmp(kp, "ret") == 0) 95768559Seric { 95868559Seric if (vp == NULL) 95968559Seric { 96068559Seric usrerr("501 RET requires a value"); 96168559Seric /* NOTREACHED */ 96268559Seric } 96368583Seric if (bitset(EF_RET_PARAM, e->e_flags)) 96468583Seric { 96568583Seric usrerr("501 Duplicate RET parameter"); 96668583Seric /* NOTREACHED */ 96768583Seric } 96868559Seric e->e_flags |= EF_RET_PARAM; 96968559Seric if (strcasecmp(vp, "hdrs") == 0) 97068559Seric e->e_flags |= EF_NO_BODY_RETN; 97168559Seric else if (strcasecmp(vp, "full") != 0) 97268559Seric { 97368559Seric usrerr("501 Bad argument \"%s\" to RET", vp); 97468559Seric /* NOTREACHED */ 97568559Seric } 97668559Seric } 97768559Seric else 97868559Seric { 97968559Seric usrerr("501 %s parameter unrecognized", kp); 98068559Seric /* NOTREACHED */ 98168559Seric } 98268559Seric } 98368559Seric /* 98467963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 98567963Seric ** 98667963Seric ** Parameters: 98767963Seric ** a -- the address corresponding to the To: parameter. 98867963Seric ** kp -- the parameter key. 98967963Seric ** vp -- the value of that parameter. 99067963Seric ** e -- the envelope. 99167963Seric ** 99267963Seric ** Returns: 99367963Seric ** none. 99467963Seric */ 99567963Seric 99667963Seric rcpt_esmtp_args(a, kp, vp, e) 99767963Seric ADDRESS *a; 99867963Seric char *kp; 99967963Seric char *vp; 100067963Seric ENVELOPE *e; 100167963Seric { 100267963Seric if (strcasecmp(kp, "notify") == 0) 100367963Seric { 100467963Seric char *p; 100567963Seric 100667963Seric if (vp == NULL) 100767963Seric { 100867963Seric usrerr("501 NOTIFY requires a value"); 100967963Seric /* NOTREACHED */ 101067963Seric } 101167963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 101268595Seric a->q_flags |= QHASNOTIFY; 101367963Seric if (strcasecmp(vp, "never") == 0) 101467963Seric return; 101567963Seric for (p = vp; p != NULL; vp = p) 101667963Seric { 101767963Seric p = strchr(p, ','); 101867963Seric if (p != NULL) 101967963Seric *p++ = '\0'; 102067963Seric if (strcasecmp(vp, "success") == 0) 102167963Seric a->q_flags |= QPINGONSUCCESS; 102267963Seric else if (strcasecmp(vp, "failure") == 0) 102367963Seric a->q_flags |= QPINGONFAILURE; 102467963Seric else if (strcasecmp(vp, "delay") == 0) 102567963Seric a->q_flags |= QPINGONDELAY; 102667963Seric else 102767963Seric { 102867963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 102967963Seric vp); 103067963Seric /* NOTREACHED */ 103167963Seric } 103267963Seric } 103367963Seric } 103467963Seric else if (strcasecmp(kp, "orcpt") == 0) 103567963Seric { 103667963Seric if (vp == NULL) 103767963Seric { 103867963Seric usrerr("501 ORCPT requires a value"); 103967963Seric /* NOTREACHED */ 104067963Seric } 104168583Seric if (!xtextok(vp)) 104268583Seric { 104368583Seric usrerr("501 Syntax error in ORCPT parameter value"); 104468583Seric /* NOTREACHED */ 104568583Seric } 104668583Seric if (a->q_orcpt != NULL) 104768583Seric { 104868583Seric usrerr("501 Duplicate ORCPT parameter"); 104968583Seric /* NOTREACHED */ 105068583Seric } 105167963Seric a->q_orcpt = newstr(vp); 105267963Seric } 105367963Seric else 105467963Seric { 105567963Seric usrerr("501 %s parameter unrecognized", kp); 105667963Seric /* NOTREACHED */ 105767963Seric } 105867963Seric } 105967963Seric /* 106058151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 106158151Seric ** 106258151Seric ** Parameters: 106358151Seric ** a -- the address to print 106458151Seric ** last -- set if this is the last one. 106558151Seric ** 106658151Seric ** Returns: 106758151Seric ** none. 106858151Seric ** 106958151Seric ** Side Effects: 107058151Seric ** Prints the appropriate 250 codes. 107158151Seric */ 107258151Seric 107358151Seric printvrfyaddr(a, last) 107458151Seric register ADDRESS *a; 107558151Seric bool last; 107658151Seric { 107758151Seric char fmtbuf[20]; 107858151Seric 107958151Seric strcpy(fmtbuf, "250"); 108058151Seric fmtbuf[3] = last ? ' ' : '-'; 108158151Seric 108259746Seric if (a->q_fullname == NULL) 108359746Seric { 108459746Seric if (strchr(a->q_user, '@') == NULL) 108559746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 108659746Seric else 108759746Seric strcpy(&fmtbuf[4], "<%s>"); 108859746Seric message(fmtbuf, a->q_user, MyHostName); 108959746Seric } 109058151Seric else 109158151Seric { 109259746Seric if (strchr(a->q_user, '@') == NULL) 109359746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 109459746Seric else 109559746Seric strcpy(&fmtbuf[4], "%s <%s>"); 109659746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 109758151Seric } 109858151Seric } 109958151Seric /* 11004577Seric ** HELP -- implement the HELP command. 11014577Seric ** 11024577Seric ** Parameters: 11034577Seric ** topic -- the topic we want help for. 11044577Seric ** 11054577Seric ** Returns: 11064577Seric ** none. 11074577Seric ** 11084577Seric ** Side Effects: 11094577Seric ** outputs the help file to message output. 11104577Seric */ 11114577Seric 11124577Seric help(topic) 11134577Seric char *topic; 11144577Seric { 11154577Seric register FILE *hf; 11164577Seric int len; 111768751Seric bool noinfo; 11184577Seric char buf[MAXLINE]; 111968751Seric extern char Version[]; 11204577Seric 112168751Seric 11228269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 11234577Seric { 11244577Seric /* no help */ 112511931Seric errno = 0; 112668751Seric message("502 Sendmail %s -- HELP not implemented", Version); 11274577Seric return; 11284577Seric } 11294577Seric 113049669Seric if (topic == NULL || *topic == '\0') 113168751Seric { 113249669Seric topic = "smtp"; 113368751Seric message("214-This is Sendmail version %s", Version); 113468751Seric noinfo = FALSE; 113568751Seric } 113649669Seric else 113768751Seric { 113849669Seric makelower(topic); 113968751Seric noinfo = TRUE; 114068751Seric } 114149669Seric 11424577Seric len = strlen(topic); 11434577Seric 11444577Seric while (fgets(buf, sizeof buf, hf) != NULL) 11454577Seric { 11464577Seric if (strncmp(buf, topic, len) == 0) 11474577Seric { 11484577Seric register char *p; 11494577Seric 115056795Seric p = strchr(buf, '\t'); 11514577Seric if (p == NULL) 11524577Seric p = buf; 11534577Seric else 11544577Seric p++; 11554577Seric fixcrlf(p, TRUE); 115658151Seric message("214-%s", p); 11574577Seric noinfo = FALSE; 11584577Seric } 11594577Seric } 11604577Seric 11614577Seric if (noinfo) 116258151Seric message("504 HELP topic unknown"); 11634577Seric else 116458151Seric message("214 End of HELP info"); 11654628Seric (void) fclose(hf); 11664577Seric } 11678544Seric /* 11689339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11699339Seric ** 11709339Seric ** Parameters: 11719339Seric ** label -- a string used in error messages 11729339Seric ** 11739339Seric ** Returns: 11749339Seric ** zero in the child 11759339Seric ** one in the parent 11769339Seric ** 11779339Seric ** Side Effects: 11789339Seric ** none. 11799339Seric */ 11808544Seric 118155012Seric runinchild(label, e) 11829339Seric char *label; 118355012Seric register ENVELOPE *e; 11849339Seric { 11859339Seric int childpid; 11869339Seric 118716158Seric if (!OneXact) 11889339Seric { 118916158Seric childpid = dofork(); 119016158Seric if (childpid < 0) 119116158Seric { 119216158Seric syserr("%s: cannot fork", label); 119316158Seric return (1); 119416158Seric } 119516158Seric if (childpid > 0) 119616158Seric { 119716158Seric auto int st; 11989339Seric 119916158Seric /* parent -- wait for child to complete */ 120061093Seric setproctitle("server %s child wait", CurHostName); 120116158Seric st = waitfor(childpid); 120216158Seric if (st == -1) 120316158Seric syserr("%s: lost child", label); 120464948Seric else if (!WIFEXITED(st)) 120564948Seric syserr("%s: died on signal %d", 120664948Seric label, st & 0177); 12079339Seric 120816158Seric /* if we exited on a QUIT command, complete the process */ 120966017Seric if (WEXITSTATUS(st) == EX_QUIT) 121066017Seric { 121166017Seric disconnect(1, e); 121216158Seric finis(); 121366017Seric } 12149339Seric 121516158Seric return (1); 121616158Seric } 121716158Seric else 121816158Seric { 121916158Seric /* child */ 122016158Seric InChild = TRUE; 122125050Seric QuickAbort = FALSE; 122255012Seric clearenvelope(e, FALSE); 122316158Seric } 12249339Seric } 122515256Seric 122616158Seric /* open alias database */ 122760537Seric initmaps(FALSE, e); 122816158Seric 122916158Seric return (0); 12309339Seric } 12319339Seric 123256795Seric # endif /* SMTP */ 1233