122712Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362532Sbostic * Copyright (c) 1988, 1993 462532Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822712Sdist 933731Sbostic # include "sendmail.h" 1022712Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*67880Seric static char sccsid[] = "@(#)srvrsmtp.c 8.45 (Berkeley) 11/04/94 (with SMTP)"; 1433731Sbostic #else 15*67880Seric static char sccsid[] = "@(#)srvrsmtp.c 8.45 (Berkeley) 11/04/94 (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 */ 11266772Seric unsigned long msize; /* approximate maximum message size */ 11366005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11458333Seric auto char *delimptr; 11558714Seric char *id; 11659747Seric int nrcpts; /* number of RCPT commands */ 11763787Seric bool doublequeue; 11866283Seric int badcommands = 0; /* count of bad commands */ 1198544Seric char inp[MAXLINE]; 12057232Seric char cmdbuf[MAXLINE]; 1217124Seric extern char Version[]; 12224943Seric extern ENVELOPE BlankEnvelope; 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); 13958050Seric expand("\201e", inp, &inp[sizeof inp], e); 14064496Seric if (BrokenSmtpPeers) 14164496Seric { 14266762Seric p = strchr(inp, '\n'); 14366762Seric if (p != NULL) 14466762Seric *p = '\0'; 14564496Seric message("220 %s", inp); 14664496Seric } 14764496Seric else 14864496Seric { 14966745Seric char *q = inp; 15066745Seric 15166745Seric while (q != NULL) 15266745Seric { 15366762Seric p = strchr(q, '\n'); 15466762Seric if (p != NULL) 15566762Seric *p++ = '\0'; 15666745Seric message("220-%s", q); 15766745Seric q = p; 15866745Seric } 15964496Seric message("220 ESMTP spoken here"); 16064496Seric } 16158330Seric protocol = NULL; 16259016Seric sendinghost = macvalue('s', e); 16358082Seric gothello = FALSE; 16458330Seric gotmail = FALSE; 1654549Seric for (;;) 1664549Seric { 16712612Seric /* arrange for backout */ 16865751Seric if (setjmp(TopFrame) > 0) 16959058Seric { 17065751Seric /* if() nesting is necessary for Cray UNICOS */ 17165751Seric if (InChild) 17265751Seric { 17365751Seric QuickAbort = FALSE; 17465751Seric SuprErrs = TRUE; 17565751Seric finis(); 17665751Seric } 17759058Seric } 17812612Seric QuickAbort = FALSE; 17912612Seric HoldErrs = FALSE; 18051951Seric LogUsrErrs = FALSE; 18163843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 18212612Seric 1837356Seric /* setup for the read */ 18455012Seric e->e_to = NULL; 1854577Seric Errors = 0; 1867275Seric (void) fflush(stdout); 1877356Seric 1887356Seric /* read the input line */ 18961093Seric SmtpPhase = "server cmd read"; 19061093Seric setproctitle("server %s cmd read", CurHostName); 19161093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 19261093Seric SmtpPhase); 1937356Seric 1947685Seric /* handle errors */ 1957356Seric if (p == NULL) 1967356Seric { 1974549Seric /* end of file, just die */ 19866017Seric disconnect(1, e); 19958151Seric message("421 %s Lost input channel from %s", 20065017Seric MyHostName, CurSmtpClient); 20155464Seric #ifdef LOG 20263843Seric if (LogLevel > (gotmail ? 1 : 19)) 20355464Seric syslog(LOG_NOTICE, "lost input channel from %s", 20465017Seric CurSmtpClient); 20555464Seric #endif 20658069Seric if (InChild) 20758069Seric ExitStat = EX_QUIT; 2084549Seric finis(); 2094549Seric } 2104549Seric 2114549Seric /* clean up end of line */ 2124558Seric fixcrlf(inp, TRUE); 2134549Seric 2144713Seric /* echo command to transcript */ 21555012Seric if (e->e_xfp != NULL) 21655012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2174713Seric 21859060Seric if (e->e_id == NULL) 21965058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 22059060Seric else 22165058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 22259060Seric 2234549Seric /* break off command */ 22458050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2254549Seric continue; 22657232Seric cmd = cmdbuf; 22758050Seric while (*p != '\0' && 22858050Seric !(isascii(*p) && isspace(*p)) && 22958050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 23024981Seric *cmd++ = *p++; 23124981Seric *cmd = '\0'; 2324549Seric 23325691Seric /* throw away leading whitespace */ 23458050Seric while (isascii(*p) && isspace(*p)) 23525691Seric p++; 23625691Seric 2374549Seric /* decode command */ 2384549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2394549Seric { 24033725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2414549Seric break; 2424549Seric } 2434549Seric 24451954Seric /* reset errors */ 24551954Seric errno = 0; 24651954Seric 2474549Seric /* process command */ 2484549Seric switch (c->cmdcode) 2494549Seric { 2504976Seric case CMDHELO: /* hello -- introduce yourself */ 25158323Seric case CMDEHLO: /* extended hello */ 25258323Seric if (c->cmdcode == CMDEHLO) 25358323Seric { 25458323Seric protocol = "ESMTP"; 25561093Seric SmtpPhase = "server EHLO"; 25658323Seric } 25758323Seric else 25858323Seric { 25958323Seric protocol = "SMTP"; 26061093Seric SmtpPhase = "server HELO"; 26158323Seric } 26267445Seric 26367445Seric /* check for valid domain name (re 1123 5.2.5) */ 26467445Seric if (*p == '\0') 26567445Seric { 26667445Seric message("501 %s requires domain address", 26767445Seric cmdbuf); 26867445Seric break; 26967445Seric } 27067445Seric else 27167445Seric { 27267445Seric register char *q; 27367445Seric 27467445Seric for (q = p; *q != '\0'; q++) 27567445Seric { 27667445Seric if (!isascii(*q)) 27767445Seric break; 27867445Seric if (isalnum(*q)) 27967445Seric continue; 28067445Seric if (strchr("[].-_#", *q) == NULL) 28167445Seric break; 28267445Seric } 28367445Seric if (*q != '\0') 28467445Seric { 28567445Seric message("501 Invalid domain name"); 28667445Seric break; 28767445Seric } 28867445Seric } 28967445Seric 29059016Seric sendinghost = newstr(p); 29160210Seric gothello = TRUE; 29260210Seric if (c->cmdcode != CMDEHLO) 29360239Seric { 29460239Seric /* print old message and be done with it */ 29560239Seric message("250 %s Hello %s, pleased to meet you", 29665017Seric MyHostName, CurSmtpClient); 29760210Seric break; 29860239Seric } 29960239Seric 30060239Seric /* print extended message and brag */ 30160239Seric message("250-%s Hello %s, pleased to meet you", 30266760Seric MyHostName, CurSmtpClient); 30358323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 30458323Seric message("250-EXPN"); 30567417Seric message("250-8BITMIME"); 30664359Seric if (MaxMessageSize > 0) 30764359Seric message("250-SIZE %ld", MaxMessageSize); 30859271Seric else 30959271Seric message("250-SIZE"); 310*67880Seric message("250-DSN"); 31158323Seric message("250 HELP"); 3124976Seric break; 3134976Seric 3144549Seric case CMDMAIL: /* mail -- designate sender */ 31561093Seric SmtpPhase = "server MAIL"; 31624943Seric 3179314Seric /* check for validity of this command */ 31858789Seric if (!gothello) 31958082Seric { 32058957Seric /* set sending host to our known value */ 32159016Seric if (sendinghost == NULL) 32266005Seric sendinghost = peerhostname; 32358957Seric 32458789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 32558821Seric { 32658789Seric message("503 Polite people say HELO first"); 32758821Seric break; 32858821Seric } 32958082Seric } 33058109Seric if (gotmail) 3314558Seric { 33258151Seric message("503 Sender already specified"); 33363843Seric if (InChild) 33463843Seric finis(); 3354558Seric break; 3364558Seric } 3379339Seric if (InChild) 3389339Seric { 33936230Skarels errno = 0; 34058151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34158069Seric finis(); 3429339Seric } 3439339Seric 3449339Seric /* fork a subprocess to process this command */ 34555012Seric if (runinchild("SMTP-MAIL", e) > 0) 3469339Seric break; 34763753Seric if (!gothello) 34863753Seric { 34963753Seric auth_warning(e, 35066005Seric "Host %s didn't use HELO protocol", 35166005Seric peerhostname); 35263753Seric } 35365947Seric #ifdef PICKY_HELO_CHECK 35466005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 35566005Seric (strcasecmp(peerhostname, "localhost") != 0 || 35665823Seric strcasecmp(sendinghost, MyHostName) != 0)) 35765823Seric { 35865823Seric auth_warning(e, "Host %s claimed to be %s", 35966005Seric peerhostname, sendinghost); 36065823Seric } 36165947Seric #endif 36265823Seric 36358323Seric if (protocol == NULL) 36458323Seric protocol = "SMTP"; 36558323Seric define('r', protocol, e); 36659016Seric define('s', sendinghost, e); 36755012Seric initsys(e); 36859747Seric nrcpts = 0; 36965089Seric e->e_flags |= EF_LOGSENDER; 37065058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3719339Seric 3729339Seric /* child -- go do the processing */ 3734549Seric p = skipword(p, "from"); 3744549Seric if (p == NULL) 3754549Seric break; 37657977Seric if (setjmp(TopFrame) > 0) 37758147Seric { 37858147Seric /* this failed -- undo work */ 37958147Seric if (InChild) 38059058Seric { 38159058Seric QuickAbort = FALSE; 38259058Seric SuprErrs = TRUE; 38363787Seric e->e_flags &= ~EF_FATALERRS; 38458147Seric finis(); 38559058Seric } 38657977Seric break; 38758147Seric } 38857977Seric QuickAbort = TRUE; 38958333Seric 39058333Seric /* must parse sender first */ 39158333Seric delimptr = NULL; 39258704Seric setsender(p, e, &delimptr, FALSE); 39358333Seric p = delimptr; 39458333Seric if (p != NULL && *p != '\0') 39558333Seric *p++ = '\0'; 39658333Seric 39766325Seric /* check for possible spoofing */ 39866325Seric if (RealUid != 0 && OpMode == MD_SMTP && 39967473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40067473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40166325Seric { 40266325Seric auth_warning(e, "%s owned process doing -bs", 40366325Seric RealUserName); 40466325Seric } 40566325Seric 40658333Seric /* now parse ESMTP arguments */ 40758333Seric msize = 0; 40866764Seric while (p != NULL && *p != '\0') 40958333Seric { 41058333Seric char *kp; 41166304Seric char *vp = NULL; 41258333Seric 41358333Seric /* locate the beginning of the keyword */ 41458333Seric while (isascii(*p) && isspace(*p)) 41558333Seric p++; 41658333Seric if (*p == '\0') 41758333Seric break; 41858333Seric kp = p; 41958333Seric 42058333Seric /* skip to the value portion */ 42158333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42258333Seric p++; 42358333Seric if (*p == '=') 42458333Seric { 42558333Seric *p++ = '\0'; 42658333Seric vp = p; 42758333Seric 42858333Seric /* skip to the end of the value */ 42958333Seric while (*p != '\0' && *p != ' ' && 43058333Seric !(isascii(*p) && iscntrl(*p)) && 43158333Seric *p != '=') 43258333Seric p++; 43358333Seric } 43458333Seric 43558333Seric if (*p != '\0') 43658333Seric *p++ = '\0'; 43758333Seric 43858333Seric if (tTd(19, 1)) 43966764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 44058333Seric vp == NULL ? "<null>" : vp); 44158333Seric 44258333Seric if (strcasecmp(kp, "size") == 0) 44358333Seric { 44459093Seric if (vp == NULL) 44558333Seric { 44658333Seric usrerr("501 SIZE requires a value"); 44758333Seric /* NOTREACHED */ 44858333Seric } 44966772Seric # ifdef __STDC__ 45066772Seric msize = strtoul(vp, (char **) NULL, 10); 45166772Seric # else 45266772Seric msize = strtol(vp, (char **) NULL, 10); 45366772Seric # endif 45458333Seric } 45559093Seric else if (strcasecmp(kp, "body") == 0) 45659093Seric { 45759093Seric if (vp == NULL) 45859093Seric { 45959093Seric usrerr("501 BODY requires a value"); 46059093Seric /* NOTREACHED */ 46159093Seric } 46267417Seric e->e_bodytype = newstr(vp); 46359093Seric if (strcasecmp(vp, "8bitmime") == 0) 46459093Seric { 46567546Seric SevenBitInput = FALSE; 46659093Seric } 46759093Seric else if (strcasecmp(vp, "7bit") == 0) 46859093Seric { 46967546Seric SevenBitInput = TRUE; 47059093Seric } 47159093Seric else 47259093Seric { 47359093Seric usrerr("501 Unknown BODY type %s", 47459093Seric vp); 47567417Seric /* NOTREACHED */ 47659093Seric } 47759093Seric } 478*67880Seric else if (strcasecmp(kp, "envid") == 0) 479*67880Seric { 480*67880Seric if (vp == NULL) 481*67880Seric { 482*67880Seric usrerr("501 ENVID requires a value"); 483*67880Seric /* NOTREACHED */ 484*67880Seric } 485*67880Seric e->e_envid = newstr(vp); 486*67880Seric } 48758333Seric else 48858333Seric { 48958333Seric usrerr("501 %s parameter unrecognized", kp); 49058333Seric /* NOTREACHED */ 49158333Seric } 49258333Seric } 49359284Seric 49459284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 49559284Seric { 49659284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 49759284Seric MaxMessageSize); 49859284Seric /* NOTREACHED */ 49959284Seric } 50058333Seric 50158333Seric if (!enoughspace(msize)) 50258333Seric { 50358333Seric message("452 Insufficient disk space; try again later"); 50458333Seric break; 50558333Seric } 50658151Seric message("250 Sender ok"); 50758147Seric gotmail = TRUE; 5084549Seric break; 5094549Seric 5104976Seric case CMDRCPT: /* rcpt -- designate recipient */ 51158850Seric if (!gotmail) 51258850Seric { 51358850Seric usrerr("503 Need MAIL before RCPT"); 51458850Seric break; 51558850Seric } 51661093Seric SmtpPhase = "server RCPT"; 51712612Seric if (setjmp(TopFrame) > 0) 51814785Seric { 51955012Seric e->e_flags &= ~EF_FATALERRS; 52012612Seric break; 52114785Seric } 52212612Seric QuickAbort = TRUE; 52351951Seric LogUsrErrs = TRUE; 52458093Seric 52559699Seric if (e->e_sendmode != SM_DELIVER) 52659699Seric e->e_flags |= EF_VRFYONLY; 52758919Seric 5284549Seric p = skipword(p, "to"); 5294549Seric if (p == NULL) 5304549Seric break; 531*67880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 53212612Seric if (a == NULL) 53312612Seric break; 534*67880Seric p = delimptr; 53516886Seric a->q_flags |= QPRIMARY; 53655012Seric a = recipient(a, &e->e_sendqueue, e); 537*67880Seric 538*67880Seric /* now parse ESMTP arguments */ 539*67880Seric while (p != NULL && *p != '\0') 540*67880Seric { 541*67880Seric char *kp; 542*67880Seric char *vp = NULL; 543*67880Seric 544*67880Seric /* locate the beginning of the keyword */ 545*67880Seric while (isascii(*p) && isspace(*p)) 546*67880Seric p++; 547*67880Seric if (*p == '\0') 548*67880Seric break; 549*67880Seric kp = p; 550*67880Seric 551*67880Seric /* skip to the value portion */ 552*67880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 553*67880Seric p++; 554*67880Seric if (*p == '=') 555*67880Seric { 556*67880Seric *p++ = '\0'; 557*67880Seric vp = p; 558*67880Seric 559*67880Seric /* skip to the end of the value */ 560*67880Seric while (*p != '\0' && *p != ' ' && 561*67880Seric !(isascii(*p) && iscntrl(*p)) && 562*67880Seric *p != '=') 563*67880Seric p++; 564*67880Seric } 565*67880Seric 566*67880Seric if (*p != '\0') 567*67880Seric *p++ = '\0'; 568*67880Seric 569*67880Seric if (tTd(19, 1)) 570*67880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 571*67880Seric vp == NULL ? "<null>" : vp); 572*67880Seric 573*67880Seric if (strcasecmp(kp, "notify") == 0) 574*67880Seric { 575*67880Seric if (vp == NULL) 576*67880Seric { 577*67880Seric usrerr("501 NOTIFY requires a value"); 578*67880Seric /* NOTREACHED */ 579*67880Seric } 580*67880Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE); 581*67880Seric if (strcasecmp(vp, "success") == 0) 582*67880Seric a->q_flags |= QPINGONSUCCESS; 583*67880Seric else if (strcasecmp(vp, "failure") == 0) 584*67880Seric a->q_flags |= QPINGONFAILURE; 585*67880Seric else if (strcasecmp(vp, "always") == 0) 586*67880Seric a->q_flags |= QPINGONSUCCESS | 587*67880Seric QPINGONFAILURE; 588*67880Seric else if (strcasecmp(vp, "never") != 0) 589*67880Seric { 590*67880Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 591*67880Seric vp); 592*67880Seric /* NOTREACHED */ 593*67880Seric } 594*67880Seric } 595*67880Seric else if (strcasecmp(kp, "ret") == 0) 596*67880Seric { 597*67880Seric if (vp == NULL) 598*67880Seric { 599*67880Seric usrerr("501 RET requires a value"); 600*67880Seric /* NOTREACHED */ 601*67880Seric } 602*67880Seric a->q_flags |= QHASRETPARAM; 603*67880Seric if (strcasecmp(vp, "no") == 0) 604*67880Seric a->q_flags |= QNOBODYRETURN; 605*67880Seric else if (strcasecmp(vp, "yes") != 0) 606*67880Seric { 607*67880Seric usrerr("501 Bad argument \"%s\" to RET", 608*67880Seric vp); 609*67880Seric /* NOTREACHED */ 610*67880Seric } 611*67880Seric } 612*67880Seric else if (strcasecmp(kp, "orcpt") == 0) 613*67880Seric { 614*67880Seric if (vp == NULL) 615*67880Seric { 616*67880Seric usrerr("501 ORCPT requires a value"); 617*67880Seric /* NOTREACHED */ 618*67880Seric } 619*67880Seric a->q_orcpt = newstr(vp); 620*67880Seric } 621*67880Seric else 622*67880Seric { 623*67880Seric usrerr("501 %s parameter unrecognized", kp); 624*67880Seric /* NOTREACHED */ 625*67880Seric } 626*67880Seric } 62712612Seric if (Errors != 0) 62812612Seric break; 62912612Seric 63012612Seric /* no errors during parsing, but might be a duplicate */ 63155012Seric e->e_to = p; 63212612Seric if (!bitset(QBADADDR, a->q_flags)) 63359747Seric { 63464718Seric message("250 Recipient ok%s", 63564718Seric bitset(QQUEUEUP, a->q_flags) ? 63664718Seric " (will queue)" : ""); 63759747Seric nrcpts++; 63859747Seric } 63912612Seric else 6404549Seric { 64112612Seric /* punt -- should keep message in ADDRESS.... */ 64258151Seric message("550 Addressee unknown"); 6434549Seric } 64455012Seric e->e_to = NULL; 6454549Seric break; 6464549Seric 6474549Seric case CMDDATA: /* data -- text of mail */ 64861093Seric SmtpPhase = "server DATA"; 64958109Seric if (!gotmail) 6504549Seric { 65158151Seric message("503 Need MAIL command"); 6524976Seric break; 6534549Seric } 65464718Seric else if (nrcpts <= 0) 6554549Seric { 65658151Seric message("503 Need RCPT (recipient)"); 6574976Seric break; 6584549Seric } 6594976Seric 66058929Seric /* check to see if we need to re-expand aliases */ 66163787Seric /* also reset QBADADDR on already-diagnosted addrs */ 66263787Seric doublequeue = FALSE; 66358929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 66458929Seric { 66558929Seric if (bitset(QVERIFIED, a->q_flags)) 66663787Seric { 66763787Seric /* need to re-expand aliases */ 66863787Seric doublequeue = TRUE; 66963787Seric } 67063787Seric if (bitset(QBADADDR, a->q_flags)) 67163787Seric { 67263787Seric /* make this "go away" */ 67363787Seric a->q_flags |= QDONTSEND; 67463787Seric a->q_flags &= ~QBADADDR; 67563787Seric } 67658929Seric } 67758929Seric 6784976Seric /* collect the text of the message */ 67924943Seric SmtpPhase = "collect"; 68067546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 68164766Seric if (Errors != 0) 68264766Seric goto abortmessage; 68367131Seric 68467131Seric /* from now on, we have to operate silently */ 68563965Seric HoldErrs = TRUE; 68667131Seric e->e_errormode = EM_MAIL; 6874976Seric 6888238Seric /* 6898238Seric ** Arrange to send to everyone. 6908238Seric ** If sending to multiple people, mail back 6918238Seric ** errors rather than reporting directly. 6928238Seric ** In any case, don't mail back errors for 6938238Seric ** anything that has happened up to 6948238Seric ** now (the other end will do this). 69510197Seric ** Truncate our transcript -- the mail has gotten 69610197Seric ** to us successfully, and if we have 69710197Seric ** to mail this back, it will be easier 69810197Seric ** on the reader. 6998238Seric ** Then send to everyone. 7008238Seric ** Finally give a reply code. If an error has 7018238Seric ** already been given, don't mail a 7028238Seric ** message back. 7039339Seric ** We goose error returns by clearing error bit. 7048238Seric */ 7058238Seric 70624943Seric SmtpPhase = "delivery"; 70755012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 70858714Seric id = e->e_id; 7094976Seric 71067131Seric if (doublequeue) 71159730Seric { 71267131Seric /* make sure it is in the queue */ 71367131Seric queueup(e, TRUE, FALSE); 71459730Seric } 7158238Seric else 71658919Seric { 71767131Seric /* send to all recipients */ 71867131Seric sendall(e, SM_DEFAULT); 71967131Seric } 72067131Seric e->e_to = NULL; 72159747Seric 72267131Seric /* issue success message */ 72367131Seric message("250 %s Message accepted for delivery", id); 72464296Seric 72567131Seric /* if we just queued, poke it */ 72667131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 72767131Seric { 72867131Seric extern pid_t dowork(); 72967131Seric 73067131Seric unlockqueue(e); 73167131Seric (void) dowork(id, TRUE, TRUE, e); 73258919Seric } 73358883Seric 73459747Seric abortmessage: 7359339Seric /* if in a child, pop back to our parent */ 7369339Seric if (InChild) 7379339Seric finis(); 73824943Seric 73924943Seric /* clean up a bit */ 74058109Seric gotmail = FALSE; 74155012Seric dropenvelope(e); 74258179Seric CurEnv = e = newenvelope(e, CurEnv); 74355012Seric e->e_flags = BlankEnvelope.e_flags; 7444549Seric break; 7454549Seric 7464549Seric case CMDRSET: /* rset -- reset state */ 74758151Seric message("250 Reset state"); 74864359Seric e->e_flags |= EF_CLRQUEUE; 7499339Seric if (InChild) 7509339Seric finis(); 75158109Seric 75258109Seric /* clean up a bit */ 75358109Seric gotmail = FALSE; 75458109Seric dropenvelope(e); 75558179Seric CurEnv = e = newenvelope(e, CurEnv); 7569339Seric break; 7574549Seric 7584549Seric case CMDVRFY: /* vrfy -- verify address */ 75958092Seric case CMDEXPN: /* expn -- expand address */ 76058092Seric vrfy = c->cmdcode == CMDVRFY; 76158092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 76258092Seric PrivacyFlags)) 76358082Seric { 76458412Seric if (vrfy) 76567160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 76658412Seric else 76765192Seric message("502 Sorry, we do not allow this operation"); 76865017Seric #ifdef LOG 76965017Seric if (LogLevel > 5) 77065017Seric syslog(LOG_INFO, "%s: %s [rejected]", 77165017Seric CurSmtpClient, inp); 77265017Seric #endif 77358082Seric break; 77458082Seric } 77558082Seric else if (!gothello && 77658092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 77758092Seric PrivacyFlags)) 77858082Seric { 77958151Seric message("503 I demand that you introduce yourself first"); 78058082Seric break; 78158082Seric } 78258092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7839339Seric break; 78455173Seric #ifdef LOG 78558020Seric if (LogLevel > 5) 78665017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 78755173Seric #endif 7885003Seric vrfyqueue = NULL; 7897762Seric QuickAbort = TRUE; 79058092Seric if (vrfy) 79158092Seric e->e_flags |= EF_VRFYONLY; 79262373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 79367615Seric p++; 79462373Seric if (*p == '\0') 79562373Seric { 79662373Seric message("501 Argument required"); 79762373Seric Errors++; 79862373Seric } 79962373Seric else 80062373Seric { 80164284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 80262373Seric } 8037762Seric if (Errors != 0) 8049339Seric { 8059339Seric if (InChild) 8069339Seric finis(); 8077762Seric break; 8089339Seric } 80962373Seric if (vrfyqueue == NULL) 81062373Seric { 81162373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 81262373Seric } 8135003Seric while (vrfyqueue != NULL) 8145003Seric { 81563971Seric a = vrfyqueue; 81663971Seric while ((a = a->q_next) != NULL && 81763971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 81863971Seric continue; 8197685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 82058151Seric printvrfyaddr(vrfyqueue, a == NULL); 82163847Seric vrfyqueue = vrfyqueue->q_next; 8225003Seric } 8239339Seric if (InChild) 8249339Seric finis(); 8254549Seric break; 8264549Seric 8274549Seric case CMDHELP: /* help -- give user info */ 8284577Seric help(p); 8294549Seric break; 8304549Seric 8314549Seric case CMDNOOP: /* noop -- do nothing */ 83264122Seric message("250 OK"); 8334549Seric break; 8344549Seric 8354549Seric case CMDQUIT: /* quit -- leave mail */ 83658151Seric message("221 %s closing connection", MyHostName); 83761051Seric 83866283Seric doquit: 83961051Seric /* avoid future 050 messages */ 84066017Seric disconnect(1, e); 84161051Seric 8429339Seric if (InChild) 8439339Seric ExitStat = EX_QUIT; 8444549Seric finis(); 8454549Seric 8468544Seric case CMDVERB: /* set verbose mode */ 84759957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 84859957Seric { 84959957Seric /* this would give out the same info */ 85059957Seric message("502 Verbose unavailable"); 85159957Seric break; 85259957Seric } 8538544Seric Verbose = TRUE; 85458734Seric e->e_sendmode = SM_DELIVER; 85559957Seric message("250 Verbose mode"); 8568544Seric break; 8578544Seric 8589314Seric case CMDONEX: /* doing one transaction only */ 8599378Seric OneXact = TRUE; 86059957Seric message("250 Only one transaction"); 8619314Seric break; 8629314Seric 86336230Skarels # ifdef SMTPDEBUG 8649339Seric case CMDDBGQSHOW: /* show queues */ 8656907Seric printf("Send Queue="); 86655012Seric printaddr(e->e_sendqueue, TRUE); 8675003Seric break; 8687275Seric 8697275Seric case CMDDBGDEBUG: /* set debug mode */ 8707676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 8717676Seric tTflag(p); 87258151Seric message("200 Debug set"); 8737275Seric break; 8747275Seric 87536230Skarels # else /* not SMTPDEBUG */ 87636230Skarels case CMDDBGQSHOW: /* show queues */ 87736230Skarels case CMDDBGDEBUG: /* set debug mode */ 87864685Seric # endif /* SMTPDEBUG */ 87964685Seric case CMDLOGBOGUS: /* bogus command */ 88036233Skarels # ifdef LOG 88158308Seric if (LogLevel > 0) 88264685Seric syslog(LOG_CRIT, 88358020Seric "\"%s\" command from %s (%s)", 88466005Seric c->cmdname, peerhostname, 88558755Seric anynet_ntoa(&RealHostAddr)); 88636233Skarels # endif 88736230Skarels /* FALL THROUGH */ 88836230Skarels 8894549Seric case CMDERROR: /* unknown command */ 89066283Seric if (++badcommands > MAXBADCOMMANDS) 89166283Seric { 89266283Seric message("421 %s Too many bad commands; closing connection", 89366283Seric MyHostName); 89466283Seric goto doquit; 89566283Seric } 89666283Seric 89758151Seric message("500 Command unrecognized"); 8984549Seric break; 8994549Seric 9004549Seric default: 90136230Skarels errno = 0; 90258151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 9034549Seric break; 9044549Seric } 9054549Seric } 9064549Seric } 9074549Seric /* 9084549Seric ** SKIPWORD -- skip a fixed word. 9094549Seric ** 9104549Seric ** Parameters: 9114549Seric ** p -- place to start looking. 9124549Seric ** w -- word to skip. 9134549Seric ** 9144549Seric ** Returns: 9154549Seric ** p following w. 9164549Seric ** NULL on error. 9174549Seric ** 9184549Seric ** Side Effects: 9194549Seric ** clobbers the p data area. 9204549Seric */ 9214549Seric 9224549Seric static char * 9234549Seric skipword(p, w) 9244549Seric register char *p; 9254549Seric char *w; 9264549Seric { 9274549Seric register char *q; 92866005Seric char *firstp = p; 9294549Seric 9304549Seric /* find beginning of word */ 93158050Seric while (isascii(*p) && isspace(*p)) 9324549Seric p++; 9334549Seric q = p; 9344549Seric 9354549Seric /* find end of word */ 93658050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 9374549Seric p++; 93858050Seric while (isascii(*p) && isspace(*p)) 9394549Seric *p++ = '\0'; 9404549Seric if (*p != ':') 9414549Seric { 9424549Seric syntax: 94366005Seric message("501 Syntax error in parameters scanning \"%s\"", 94466005Seric firstp); 9454549Seric Errors++; 9464549Seric return (NULL); 9474549Seric } 9484549Seric *p++ = '\0'; 94958050Seric while (isascii(*p) && isspace(*p)) 9504549Seric p++; 9514549Seric 95262373Seric if (*p == '\0') 95362373Seric goto syntax; 95462373Seric 9554549Seric /* see if the input word matches desired word */ 95633725Sbostic if (strcasecmp(q, w)) 9574549Seric goto syntax; 9584549Seric 9594549Seric return (p); 9604549Seric } 9614577Seric /* 96258151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 96358151Seric ** 96458151Seric ** Parameters: 96558151Seric ** a -- the address to print 96658151Seric ** last -- set if this is the last one. 96758151Seric ** 96858151Seric ** Returns: 96958151Seric ** none. 97058151Seric ** 97158151Seric ** Side Effects: 97258151Seric ** Prints the appropriate 250 codes. 97358151Seric */ 97458151Seric 97558151Seric printvrfyaddr(a, last) 97658151Seric register ADDRESS *a; 97758151Seric bool last; 97858151Seric { 97958151Seric char fmtbuf[20]; 98058151Seric 98158151Seric strcpy(fmtbuf, "250"); 98258151Seric fmtbuf[3] = last ? ' ' : '-'; 98358151Seric 98459746Seric if (a->q_fullname == NULL) 98559746Seric { 98659746Seric if (strchr(a->q_user, '@') == NULL) 98759746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 98859746Seric else 98959746Seric strcpy(&fmtbuf[4], "<%s>"); 99059746Seric message(fmtbuf, a->q_user, MyHostName); 99159746Seric } 99258151Seric else 99358151Seric { 99459746Seric if (strchr(a->q_user, '@') == NULL) 99559746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 99659746Seric else 99759746Seric strcpy(&fmtbuf[4], "%s <%s>"); 99859746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 99958151Seric } 100058151Seric } 100158151Seric /* 10024577Seric ** HELP -- implement the HELP command. 10034577Seric ** 10044577Seric ** Parameters: 10054577Seric ** topic -- the topic we want help for. 10064577Seric ** 10074577Seric ** Returns: 10084577Seric ** none. 10094577Seric ** 10104577Seric ** Side Effects: 10114577Seric ** outputs the help file to message output. 10124577Seric */ 10134577Seric 10144577Seric help(topic) 10154577Seric char *topic; 10164577Seric { 10174577Seric register FILE *hf; 10184577Seric int len; 10194577Seric char buf[MAXLINE]; 10204577Seric bool noinfo; 10214577Seric 10228269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 10234577Seric { 10244577Seric /* no help */ 102511931Seric errno = 0; 102658151Seric message("502 HELP not implemented"); 10274577Seric return; 10284577Seric } 10294577Seric 103049669Seric if (topic == NULL || *topic == '\0') 103149669Seric topic = "smtp"; 103249669Seric else 103349669Seric makelower(topic); 103449669Seric 10354577Seric len = strlen(topic); 10364577Seric noinfo = TRUE; 10374577Seric 10384577Seric while (fgets(buf, sizeof buf, hf) != NULL) 10394577Seric { 10404577Seric if (strncmp(buf, topic, len) == 0) 10414577Seric { 10424577Seric register char *p; 10434577Seric 104456795Seric p = strchr(buf, '\t'); 10454577Seric if (p == NULL) 10464577Seric p = buf; 10474577Seric else 10484577Seric p++; 10494577Seric fixcrlf(p, TRUE); 105058151Seric message("214-%s", p); 10514577Seric noinfo = FALSE; 10524577Seric } 10534577Seric } 10544577Seric 10554577Seric if (noinfo) 105658151Seric message("504 HELP topic unknown"); 10574577Seric else 105858151Seric message("214 End of HELP info"); 10594628Seric (void) fclose(hf); 10604577Seric } 10618544Seric /* 10629339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 10639339Seric ** 10649339Seric ** Parameters: 10659339Seric ** label -- a string used in error messages 10669339Seric ** 10679339Seric ** Returns: 10689339Seric ** zero in the child 10699339Seric ** one in the parent 10709339Seric ** 10719339Seric ** Side Effects: 10729339Seric ** none. 10739339Seric */ 10748544Seric 107555012Seric runinchild(label, e) 10769339Seric char *label; 107755012Seric register ENVELOPE *e; 10789339Seric { 10799339Seric int childpid; 10809339Seric 108116158Seric if (!OneXact) 10829339Seric { 108316158Seric childpid = dofork(); 108416158Seric if (childpid < 0) 108516158Seric { 108616158Seric syserr("%s: cannot fork", label); 108716158Seric return (1); 108816158Seric } 108916158Seric if (childpid > 0) 109016158Seric { 109116158Seric auto int st; 10929339Seric 109316158Seric /* parent -- wait for child to complete */ 109461093Seric setproctitle("server %s child wait", CurHostName); 109516158Seric st = waitfor(childpid); 109616158Seric if (st == -1) 109716158Seric syserr("%s: lost child", label); 109864948Seric else if (!WIFEXITED(st)) 109964948Seric syserr("%s: died on signal %d", 110064948Seric label, st & 0177); 11019339Seric 110216158Seric /* if we exited on a QUIT command, complete the process */ 110366017Seric if (WEXITSTATUS(st) == EX_QUIT) 110466017Seric { 110566017Seric disconnect(1, e); 110616158Seric finis(); 110766017Seric } 11089339Seric 110916158Seric return (1); 111016158Seric } 111116158Seric else 111216158Seric { 111316158Seric /* child */ 111416158Seric InChild = TRUE; 111525050Seric QuickAbort = FALSE; 111655012Seric clearenvelope(e, FALSE); 111716158Seric } 11189339Seric } 111915256Seric 112016158Seric /* open alias database */ 112160537Seric initmaps(FALSE, e); 112216158Seric 112316158Seric return (0); 11249339Seric } 11259339Seric 112656795Seric # endif /* SMTP */ 1127