deliver.c revision 66494
138032Speter/* 264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1438032Speter#ifndef lint 1566494Sgshapirostatic char id[] = "@(#)$Id: deliver.c,v 8.600.2.1.2.44 2000/09/21 21:52:17 ca Exp $"; 1664562Sgshapiro#endif /* ! lint */ 1738032Speter 1864562Sgshapiro#include <sendmail.h> 1938032Speter 2064562Sgshapiro 2138032Speter#if HASSETUSERCONTEXT 2238032Speter# include <login_cap.h> 2364562Sgshapiro#endif /* HASSETUSERCONTEXT */ 2438032Speter 2564562Sgshapiro#if STARTTLS || (SASL && SFIO) 2664562Sgshapiro# include "sfsasl.h" 2764562Sgshapiro#endif /* STARTTLS || (SASL && SFIO) */ 2864562Sgshapiro 2964562Sgshapirostatic int deliver __P((ENVELOPE *, ADDRESS *)); 3064562Sgshapirostatic void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 3164562Sgshapirostatic void mailfiletimeout __P((void)); 3264562Sgshapirostatic void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool)); 3364562Sgshapirostatic int parse_hostsignature __P((char *, char **, MAILER *)); 3464562Sgshapirostatic void sendenvelope __P((ENVELOPE *, int)); 3564562Sgshapirostatic char *hostsignature __P((MAILER *, char *)); 3664562Sgshapiro 3738032Speter#if SMTP 3864562Sgshapiro# if STARTTLS 3964562Sgshapirostatic int starttls __P((MAILER *, MCI *, ENVELOPE *)); 4064562Sgshapiro# endif /* STARTTLS */ 4164562Sgshapiro#endif /* SMTP */ 4238032Speter 4338032Speter/* 4438032Speter** SENDALL -- actually send all the messages. 4538032Speter** 4638032Speter** Parameters: 4738032Speter** e -- the envelope to send. 4838032Speter** mode -- the delivery mode to use. If SM_DEFAULT, use 4938032Speter** the current e->e_sendmode. 5038032Speter** 5138032Speter** Returns: 5238032Speter** none. 5338032Speter** 5438032Speter** Side Effects: 5538032Speter** Scans the send lists and sends everything it finds. 5638032Speter** Delivers any appropriate error messages. 5738032Speter** If we are running in a non-interactive mode, takes the 5838032Speter** appropriate action. 5938032Speter*/ 6038032Speter 6138032Spetervoid 6238032Spetersendall(e, mode) 6338032Speter ENVELOPE *e; 6438032Speter int mode; 6538032Speter{ 6638032Speter register ADDRESS *q; 6738032Speter char *owner; 6838032Speter int otherowners; 6964562Sgshapiro int save_errno; 7038032Speter register ENVELOPE *ee; 7138032Speter ENVELOPE *splitenv = NULL; 7238032Speter int oldverbose = Verbose; 7338032Speter bool somedeliveries = FALSE, expensive = FALSE; 7438032Speter pid_t pid; 7538032Speter 7638032Speter /* 7738032Speter ** If this message is to be discarded, don't bother sending 7838032Speter ** the message at all. 7938032Speter */ 8038032Speter 8138032Speter if (bitset(EF_DISCARD, e->e_flags)) 8238032Speter { 8338032Speter if (tTd(13, 1)) 8464562Sgshapiro dprintf("sendall: discarding id %s\n", e->e_id); 8538032Speter e->e_flags |= EF_CLRQUEUE; 8638032Speter if (LogLevel > 4) 8738032Speter sm_syslog(LOG_INFO, e->e_id, "discarded"); 8838032Speter markstats(e, NULL, TRUE); 8938032Speter return; 9038032Speter } 9138032Speter 9238032Speter /* 9338032Speter ** If we have had global, fatal errors, don't bother sending 9438032Speter ** the message at all if we are in SMTP mode. Local errors 9538032Speter ** (e.g., a single address failing) will still cause the other 9638032Speter ** addresses to be sent. 9738032Speter */ 9838032Speter 9938032Speter if (bitset(EF_FATALERRS, e->e_flags) && 10038032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 10138032Speter { 10238032Speter e->e_flags |= EF_CLRQUEUE; 10338032Speter return; 10438032Speter } 10538032Speter 10638032Speter /* determine actual delivery mode */ 10738032Speter if (mode == SM_DEFAULT) 10838032Speter { 10938032Speter mode = e->e_sendmode; 11038032Speter if (mode != SM_VERIFY && mode != SM_DEFER && 11138032Speter shouldqueue(e->e_msgpriority, e->e_ctime)) 11238032Speter mode = SM_QUEUE; 11338032Speter } 11438032Speter 11538032Speter if (tTd(13, 1)) 11638032Speter { 11764562Sgshapiro dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 11838032Speter mode, e->e_id); 11938032Speter printaddr(&e->e_from, FALSE); 12064562Sgshapiro dprintf("\te_flags = "); 12138032Speter printenvflags(e); 12264562Sgshapiro dprintf("sendqueue:\n"); 12338032Speter printaddr(e->e_sendqueue, TRUE); 12438032Speter } 12538032Speter 12638032Speter /* 12738032Speter ** Do any preprocessing necessary for the mode we are running. 12838032Speter ** Check to make sure the hop count is reasonable. 12938032Speter ** Delete sends to the sender in mailing lists. 13038032Speter */ 13138032Speter 13238032Speter CurEnv = e; 13338032Speter if (tTd(62, 1)) 13438032Speter checkfds(NULL); 13538032Speter 13638032Speter if (e->e_hopcount > MaxHopCount) 13738032Speter { 13838032Speter errno = 0; 13938032Speter#if QUEUE 14038032Speter queueup(e, mode == SM_QUEUE || mode == SM_DEFER); 14164562Sgshapiro#endif /* QUEUE */ 14238032Speter e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; 14364562Sgshapiro ExitStat = EX_UNAVAILABLE; 14464562Sgshapiro syserr("554 5.0.0 Too many hops %d (%d max): from %s via %s, to %s", 14538032Speter e->e_hopcount, MaxHopCount, e->e_from.q_paddr, 14638032Speter RealHostName == NULL ? "localhost" : RealHostName, 14738032Speter e->e_sendqueue->q_paddr); 14864562Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 14964562Sgshapiro { 15064562Sgshapiro if (QS_IS_DEAD(q->q_state)) 15164562Sgshapiro continue; 15264562Sgshapiro q->q_state = QS_BADADDR; 15364562Sgshapiro q->q_status = "5.4.6"; 15464562Sgshapiro } 15538032Speter return; 15638032Speter } 15738032Speter 15838032Speter /* 15938032Speter ** Do sender deletion. 16038032Speter ** 16164562Sgshapiro ** If the sender should be queued up, skip this. 16238032Speter ** This can happen if the name server is hosed when you 16338032Speter ** are trying to send mail. The result is that the sender 16438032Speter ** is instantiated in the queue as a recipient. 16538032Speter */ 16638032Speter 16738032Speter if (!bitset(EF_METOO, e->e_flags) && 16864562Sgshapiro !QS_IS_QUEUEUP(e->e_from.q_state)) 16938032Speter { 17038032Speter if (tTd(13, 5)) 17138032Speter { 17264562Sgshapiro dprintf("sendall: QS_SENDER "); 17338032Speter printaddr(&e->e_from, FALSE); 17438032Speter } 17564562Sgshapiro e->e_from.q_state = QS_SENDER; 17638032Speter (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); 17738032Speter } 17838032Speter 17938032Speter /* 18038032Speter ** Handle alias owners. 18138032Speter ** 18238032Speter ** We scan up the q_alias chain looking for owners. 18338032Speter ** We discard owners that are the same as the return path. 18438032Speter */ 18538032Speter 18638032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 18738032Speter { 18838032Speter register struct address *a; 18938032Speter 19038032Speter for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) 19138032Speter continue; 19238032Speter if (a != NULL) 19338032Speter q->q_owner = a->q_owner; 19438032Speter 19538032Speter if (q->q_owner != NULL && 19664562Sgshapiro !QS_IS_DEAD(q->q_state) && 19738032Speter strcmp(q->q_owner, e->e_from.q_paddr) == 0) 19838032Speter q->q_owner = NULL; 19938032Speter } 20038032Speter 20138032Speter if (tTd(13, 25)) 20238032Speter { 20364562Sgshapiro dprintf("\nAfter first owner pass, sendq =\n"); 20438032Speter printaddr(e->e_sendqueue, TRUE); 20538032Speter } 20638032Speter 20738032Speter owner = ""; 20838032Speter otherowners = 1; 20938032Speter while (owner != NULL && otherowners > 0) 21038032Speter { 21138032Speter if (tTd(13, 28)) 21264562Sgshapiro dprintf("owner = \"%s\", otherowners = %d\n", 21338032Speter owner, otherowners); 21438032Speter owner = NULL; 21538032Speter otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; 21638032Speter 21738032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 21838032Speter { 21938032Speter if (tTd(13, 30)) 22038032Speter { 22164562Sgshapiro dprintf("Checking "); 22238032Speter printaddr(q, FALSE); 22338032Speter } 22464562Sgshapiro if (QS_IS_DEAD(q->q_state)) 22538032Speter { 22638032Speter if (tTd(13, 30)) 22764562Sgshapiro dprintf(" ... QS_IS_DEAD\n"); 22838032Speter continue; 22938032Speter } 23038032Speter if (tTd(13, 29) && !tTd(13, 30)) 23138032Speter { 23264562Sgshapiro dprintf("Checking "); 23338032Speter printaddr(q, FALSE); 23438032Speter } 23538032Speter 23638032Speter if (q->q_owner != NULL) 23738032Speter { 23838032Speter if (owner == NULL) 23938032Speter { 24038032Speter if (tTd(13, 40)) 24164562Sgshapiro dprintf(" ... First owner = \"%s\"\n", 24238032Speter q->q_owner); 24338032Speter owner = q->q_owner; 24438032Speter } 24538032Speter else if (owner != q->q_owner) 24638032Speter { 24738032Speter if (strcmp(owner, q->q_owner) == 0) 24838032Speter { 24938032Speter if (tTd(13, 40)) 25064562Sgshapiro dprintf(" ... Same owner = \"%s\"\n", 25138032Speter owner); 25238032Speter 25338032Speter /* make future comparisons cheap */ 25438032Speter q->q_owner = owner; 25538032Speter } 25638032Speter else 25738032Speter { 25838032Speter if (tTd(13, 40)) 25964562Sgshapiro dprintf(" ... Another owner \"%s\"\n", 26038032Speter q->q_owner); 26138032Speter otherowners++; 26238032Speter } 26338032Speter owner = q->q_owner; 26438032Speter } 26538032Speter else if (tTd(13, 40)) 26664562Sgshapiro dprintf(" ... Same owner = \"%s\"\n", 26738032Speter owner); 26838032Speter } 26938032Speter else 27038032Speter { 27138032Speter if (tTd(13, 40)) 27264562Sgshapiro dprintf(" ... Null owner\n"); 27338032Speter otherowners++; 27438032Speter } 27538032Speter 27664562Sgshapiro if (QS_IS_BADADDR(q->q_state)) 27764562Sgshapiro { 27864562Sgshapiro if (tTd(13, 30)) 27964562Sgshapiro dprintf(" ... QS_IS_BADADDR\n"); 28064562Sgshapiro continue; 28164562Sgshapiro } 28264562Sgshapiro 28364562Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 28464562Sgshapiro { 28564562Sgshapiro MAILER *m = q->q_mailer; 28664562Sgshapiro 28764562Sgshapiro /* 28864562Sgshapiro ** If we have temporary address failures 28964562Sgshapiro ** (e.g., dns failure) and a fallback MX is 29064562Sgshapiro ** set, send directly to the fallback MX host. 29164562Sgshapiro */ 29264562Sgshapiro 29364562Sgshapiro if (FallBackMX != NULL && 29464562Sgshapiro !wordinclass(FallBackMX, 'w') && 29564562Sgshapiro mode != SM_VERIFY && 29664562Sgshapiro (strcmp(m->m_mailer, "[IPC]") == 0 || 29764562Sgshapiro strcmp(m->m_mailer, "[TCP]") == 0) && 29864562Sgshapiro m->m_argv[0] != NULL && 29964562Sgshapiro (strcmp(m->m_argv[0], "TCP") == 0 || 30064562Sgshapiro strcmp(m->m_argv[0], "IPC") == 0)) 30164562Sgshapiro { 30264562Sgshapiro int len; 30364562Sgshapiro char *p; 30464562Sgshapiro 30564562Sgshapiro if (tTd(13, 30)) 30664562Sgshapiro dprintf(" ... FallBackMX\n"); 30764562Sgshapiro 30864562Sgshapiro len = strlen(FallBackMX) + 3; 30964562Sgshapiro p = xalloc(len); 31064562Sgshapiro snprintf(p, len, "[%s]", FallBackMX); 31164562Sgshapiro q->q_state = QS_OK; 31264562Sgshapiro q->q_host = p; 31364562Sgshapiro } 31464562Sgshapiro else 31564562Sgshapiro { 31664562Sgshapiro if (tTd(13, 30)) 31764562Sgshapiro dprintf(" ... QS_IS_QUEUEUP\n"); 31864562Sgshapiro continue; 31964562Sgshapiro } 32064562Sgshapiro } 32164562Sgshapiro 32238032Speter /* 32338032Speter ** If this mailer is expensive, and if we don't 32438032Speter ** want to make connections now, just mark these 32538032Speter ** addresses and return. This is useful if we 32638032Speter ** want to batch connections to reduce load. This 32738032Speter ** will cause the messages to be queued up, and a 32838032Speter ** daemon will come along to send the messages later. 32938032Speter */ 33038032Speter 33164562Sgshapiro if (NoConnect && !Verbose && 33264562Sgshapiro bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) 33338032Speter { 33438032Speter if (tTd(13, 30)) 33564562Sgshapiro dprintf(" ... expensive\n"); 33664562Sgshapiro q->q_state = QS_QUEUEUP; 33764562Sgshapiro expensive = TRUE; 33838032Speter } 33964562Sgshapiro else if (bitnset(M_HOLD, q->q_mailer->m_flags) && 34064562Sgshapiro QueueLimitId == NULL && 34164562Sgshapiro QueueLimitSender == NULL && 34264562Sgshapiro QueueLimitRecipient == NULL) 34338032Speter { 34438032Speter if (tTd(13, 30)) 34564562Sgshapiro dprintf(" ... hold\n"); 34664562Sgshapiro q->q_state = QS_QUEUEUP; 34738032Speter expensive = TRUE; 34838032Speter } 34938032Speter else 35038032Speter { 35138032Speter if (tTd(13, 30)) 35264562Sgshapiro dprintf(" ... deliverable\n"); 35338032Speter somedeliveries = TRUE; 35438032Speter } 35538032Speter } 35638032Speter 35738032Speter if (owner != NULL && otherowners > 0) 35838032Speter { 35938032Speter /* 36038032Speter ** Split this envelope into two. 36138032Speter */ 36238032Speter 36364562Sgshapiro ee = (ENVELOPE *) xalloc(sizeof *ee); 36438032Speter *ee = *e; 36564562Sgshapiro ee->e_message = NULL; 36638032Speter ee->e_id = NULL; 36764562Sgshapiro assign_queueid(ee); 36838032Speter 36938032Speter if (tTd(13, 1)) 37064562Sgshapiro dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 37138032Speter e->e_id, ee->e_id, owner, otherowners); 37238032Speter 37338032Speter ee->e_header = copyheader(e->e_header); 37438032Speter ee->e_sendqueue = copyqueue(e->e_sendqueue); 37538032Speter ee->e_errorqueue = copyqueue(e->e_errorqueue); 37638032Speter ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 37738032Speter ee->e_flags |= EF_NORECEIPT; 37838032Speter setsender(owner, ee, NULL, '\0', TRUE); 37938032Speter if (tTd(13, 5)) 38038032Speter { 38164562Sgshapiro dprintf("sendall(split): QS_SENDER "); 38238032Speter printaddr(&ee->e_from, FALSE); 38338032Speter } 38464562Sgshapiro ee->e_from.q_state = QS_SENDER; 38538032Speter ee->e_dfp = NULL; 38664562Sgshapiro ee->e_lockfp = NULL; 38738032Speter ee->e_xfp = NULL; 38864562Sgshapiro ee->e_queuedir = e->e_queuedir; 38938032Speter ee->e_errormode = EM_MAIL; 39038032Speter ee->e_sibling = splitenv; 39164562Sgshapiro ee->e_statmsg = NULL; 39238032Speter splitenv = ee; 39338032Speter 39438032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 39538032Speter { 39638032Speter if (q->q_owner == owner) 39738032Speter { 39864562Sgshapiro q->q_state = QS_CLONED; 39938032Speter if (tTd(13, 6)) 40064562Sgshapiro dprintf("\t... stripping %s from original envelope\n", 40138032Speter q->q_paddr); 40238032Speter } 40338032Speter } 40438032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 40538032Speter { 40638032Speter if (q->q_owner != owner) 40738032Speter { 40864562Sgshapiro q->q_state = QS_CLONED; 40938032Speter if (tTd(13, 6)) 41064562Sgshapiro dprintf("\t... dropping %s from cloned envelope\n", 41138032Speter q->q_paddr); 41238032Speter } 41338032Speter else 41438032Speter { 41538032Speter /* clear DSN parameters */ 41638032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 41738032Speter q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 41838032Speter if (tTd(13, 6)) 41964562Sgshapiro dprintf("\t... moving %s to cloned envelope\n", 42038032Speter q->q_paddr); 42138032Speter } 42238032Speter } 42338032Speter 42438032Speter if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 42538032Speter dup_queue_file(e, ee, 'd'); 42664562Sgshapiro 42764562Sgshapiro /* 42864562Sgshapiro ** Give the split envelope access to the parent 42964562Sgshapiro ** transcript file for errors obtained while 43064562Sgshapiro ** processing the recipients (done before the 43164562Sgshapiro ** envelope splitting). 43264562Sgshapiro */ 43364562Sgshapiro 43464562Sgshapiro if (e->e_xfp != NULL) 43564562Sgshapiro ee->e_xfp = bfdup(e->e_xfp); 43664562Sgshapiro 43764562Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 43864562Sgshapiro if (ee->e_xfp == NULL) 43964562Sgshapiro openxscript(ee); 44064562Sgshapiro 44142575Speter if (mode != SM_VERIFY && LogLevel > 4) 44238032Speter sm_syslog(LOG_INFO, ee->e_id, 44364562Sgshapiro "clone %s, owner=%s", 44464562Sgshapiro e->e_id, owner); 44538032Speter } 44638032Speter } 44738032Speter 44838032Speter if (owner != NULL) 44938032Speter { 45038032Speter setsender(owner, e, NULL, '\0', TRUE); 45138032Speter if (tTd(13, 5)) 45238032Speter { 45364562Sgshapiro dprintf("sendall(owner): QS_SENDER "); 45438032Speter printaddr(&e->e_from, FALSE); 45538032Speter } 45664562Sgshapiro e->e_from.q_state = QS_SENDER; 45738032Speter e->e_errormode = EM_MAIL; 45838032Speter e->e_flags |= EF_NORECEIPT; 45938032Speter e->e_flags &= ~EF_FATALERRS; 46038032Speter } 46138032Speter 46238032Speter /* if nothing to be delivered, just queue up everything */ 46338032Speter if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER && 46438032Speter mode != SM_VERIFY) 46538032Speter { 46638032Speter if (tTd(13, 29)) 46764562Sgshapiro dprintf("No deliveries: auto-queuing\n"); 46838032Speter mode = SM_QUEUE; 46938032Speter 47038032Speter /* treat this as a delivery in terms of counting tries */ 47138032Speter e->e_dtime = curtime(); 47238032Speter if (!expensive) 47338032Speter e->e_ntries++; 47438032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 47538032Speter { 47638032Speter ee->e_dtime = curtime(); 47738032Speter if (!expensive) 47838032Speter ee->e_ntries++; 47938032Speter } 48038032Speter } 48138032Speter 48264562Sgshapiro#if QUEUE 48338032Speter if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK || 48438032Speter (mode != SM_VERIFY && SuperSafe)) && 48538032Speter (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 48638032Speter { 48766494Sgshapiro /* 48866494Sgshapiro ** Be sure everything is instantiated in the queue. 48966494Sgshapiro ** Split envelopes first in case the machine crashes. 49066494Sgshapiro ** If the original were done first, we may lose 49166494Sgshapiro ** recipients. 49266494Sgshapiro */ 49366494Sgshapiro 49438032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 49538032Speter queueup(ee, mode == SM_QUEUE || mode == SM_DEFER); 49666494Sgshapiro queueup(e, mode == SM_QUEUE || mode == SM_DEFER); 49738032Speter } 49838032Speter#endif /* QUEUE */ 49938032Speter 50038032Speter if (tTd(62, 10)) 50138032Speter checkfds("after envelope splitting"); 50238032Speter 50338032Speter /* 50438032Speter ** If we belong in background, fork now. 50538032Speter */ 50638032Speter 50738032Speter if (tTd(13, 20)) 50838032Speter { 50964562Sgshapiro dprintf("sendall: final mode = %c\n", mode); 51038032Speter if (tTd(13, 21)) 51138032Speter { 51264562Sgshapiro dprintf("\n================ Final Send Queue(s) =====================\n"); 51364562Sgshapiro dprintf("\n *** Envelope %s, e_from=%s ***\n", 51438032Speter e->e_id, e->e_from.q_paddr); 51538032Speter printaddr(e->e_sendqueue, TRUE); 51638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 51738032Speter { 51864562Sgshapiro dprintf("\n *** Envelope %s, e_from=%s ***\n", 51938032Speter ee->e_id, ee->e_from.q_paddr); 52038032Speter printaddr(ee->e_sendqueue, TRUE); 52138032Speter } 52264562Sgshapiro dprintf("==========================================================\n\n"); 52338032Speter } 52438032Speter } 52538032Speter switch (mode) 52638032Speter { 52738032Speter case SM_VERIFY: 52838032Speter Verbose = 2; 52938032Speter break; 53038032Speter 53138032Speter case SM_QUEUE: 53238032Speter case SM_DEFER: 53364562Sgshapiro#if HASFLOCK 53438032Speter queueonly: 53564562Sgshapiro#endif /* HASFLOCK */ 53638032Speter if (e->e_nrcpts > 0) 53738032Speter e->e_flags |= EF_INQUEUE; 53838032Speter dropenvelope(e, splitenv != NULL); 53938032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 54038032Speter { 54138032Speter if (ee->e_nrcpts > 0) 54238032Speter ee->e_flags |= EF_INQUEUE; 54338032Speter dropenvelope(ee, FALSE); 54438032Speter } 54538032Speter return; 54638032Speter 54738032Speter case SM_FORK: 54838032Speter if (e->e_xfp != NULL) 54938032Speter (void) fflush(e->e_xfp); 55038032Speter 55164562Sgshapiro#if !HASFLOCK 55238032Speter /* 55338032Speter ** Since fcntl locking has the interesting semantic that 55438032Speter ** the lock is owned by a process, not by an open file 55538032Speter ** descriptor, we have to flush this to the queue, and 55638032Speter ** then restart from scratch in the child. 55738032Speter */ 55838032Speter 55938032Speter { 56038032Speter /* save id for future use */ 56138032Speter char *qid = e->e_id; 56238032Speter 56338032Speter /* now drop the envelope in the parent */ 56438032Speter e->e_flags |= EF_INQUEUE; 56538032Speter dropenvelope(e, splitenv != NULL); 56638032Speter 56738032Speter /* arrange to reacquire lock after fork */ 56838032Speter e->e_id = qid; 56938032Speter } 57038032Speter 57138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 57238032Speter { 57338032Speter /* save id for future use */ 57438032Speter char *qid = ee->e_id; 57538032Speter 57638032Speter /* drop envelope in parent */ 57738032Speter ee->e_flags |= EF_INQUEUE; 57838032Speter dropenvelope(ee, FALSE); 57938032Speter 58038032Speter /* and save qid for reacquisition */ 58138032Speter ee->e_id = qid; 58238032Speter } 58338032Speter 58464562Sgshapiro#endif /* !HASFLOCK */ 58538032Speter 58664562Sgshapiro /* 58764562Sgshapiro ** Since the delivery may happen in a child and the parent 58864562Sgshapiro ** does not wait, the parent may close the maps thereby 58964562Sgshapiro ** removing any shared memory used by the map. Therefore, 59064562Sgshapiro ** close the maps now so the child will dynamically open 59164562Sgshapiro ** them if necessary. 59264562Sgshapiro */ 59364562Sgshapiro 59464562Sgshapiro closemaps(); 59564562Sgshapiro 59638032Speter pid = fork(); 59738032Speter if (pid < 0) 59838032Speter { 59964562Sgshapiro syserr("deliver: fork 1"); 60064562Sgshapiro#if HASFLOCK 60138032Speter goto queueonly; 60264562Sgshapiro#else /* HASFLOCK */ 60338032Speter e->e_id = NULL; 60438032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 60538032Speter ee->e_id = NULL; 60638032Speter return; 60764562Sgshapiro#endif /* HASFLOCK */ 60838032Speter } 60938032Speter else if (pid > 0) 61038032Speter { 61164562Sgshapiro#if HASFLOCK 61238032Speter /* be sure we leave the temp files to our child */ 61338032Speter /* close any random open files in the envelope */ 61438032Speter closexscript(e); 61538032Speter if (e->e_dfp != NULL) 61664562Sgshapiro (void) bfclose(e->e_dfp); 61738032Speter e->e_dfp = NULL; 61838032Speter e->e_flags &= ~EF_HAS_DF; 61938032Speter 62038032Speter /* can't call unlockqueue to avoid unlink of xfp */ 62138032Speter if (e->e_lockfp != NULL) 62264562Sgshapiro (void) fclose(e->e_lockfp); 62364562Sgshapiro else 62464562Sgshapiro syserr("%s: sendall: null lockfp", e->e_id); 62538032Speter e->e_lockfp = NULL; 62664562Sgshapiro#endif /* HASFLOCK */ 62738032Speter 62838032Speter /* make sure the parent doesn't own the envelope */ 62938032Speter e->e_id = NULL; 63038032Speter 63138032Speter /* catch intermediate zombie */ 63238032Speter (void) waitfor(pid); 63338032Speter return; 63438032Speter } 63538032Speter 63666494Sgshapiro /* 63766494Sgshapiro ** Since we have accepted responsbility for the message, 63866494Sgshapiro ** change the SIGTERM handler. intsig() (the old handler) 63966494Sgshapiro ** would remove the envelope if this was a command line 64066494Sgshapiro ** message submission. 64166494Sgshapiro */ 64266494Sgshapiro 64366494Sgshapiro (void) setsignal(SIGTERM, SIG_DFL); 64466494Sgshapiro 64538032Speter /* double fork to avoid zombies */ 64638032Speter pid = fork(); 64738032Speter if (pid > 0) 64838032Speter exit(EX_OK); 64964562Sgshapiro save_errno = errno; 65038032Speter 65138032Speter /* be sure we are immune from the terminal */ 65238032Speter disconnect(2, e); 65364562Sgshapiro clearstats(); 65438032Speter 65538032Speter /* prevent parent from waiting if there was an error */ 65638032Speter if (pid < 0) 65738032Speter { 65864562Sgshapiro errno = save_errno; 65964562Sgshapiro syserr("deliver: fork 2"); 66064562Sgshapiro#if HASFLOCK 66138032Speter e->e_flags |= EF_INQUEUE; 66264562Sgshapiro#else /* HASFLOCK */ 66338032Speter e->e_id = NULL; 66464562Sgshapiro#endif /* HASFLOCK */ 66542575Speter finis(TRUE, ExitStat); 66638032Speter } 66738032Speter 66838032Speter /* be sure to give error messages in child */ 66938032Speter QuickAbort = FALSE; 67038032Speter 67138032Speter /* 67238032Speter ** Close any cached connections. 67338032Speter ** 67438032Speter ** We don't send the QUIT protocol because the parent 67538032Speter ** still knows about the connection. 67638032Speter ** 67738032Speter ** This should only happen when delivering an error 67838032Speter ** message. 67938032Speter */ 68038032Speter 68138032Speter mci_flush(FALSE, NULL); 68238032Speter 68364562Sgshapiro#if HASFLOCK 68438032Speter break; 68564562Sgshapiro#else /* HASFLOCK */ 68638032Speter 68738032Speter /* 68838032Speter ** Now reacquire and run the various queue files. 68938032Speter */ 69038032Speter 69138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 69238032Speter { 69338032Speter ENVELOPE *sibling = ee->e_sibling; 69438032Speter 69564562Sgshapiro (void) dowork(ee->e_queuedir, ee->e_id, 69664562Sgshapiro FALSE, FALSE, ee); 69738032Speter ee->e_sibling = sibling; 69838032Speter } 69964562Sgshapiro (void) dowork(e->e_queuedir, e->e_id, 70064562Sgshapiro FALSE, FALSE, e); 70142575Speter finis(TRUE, ExitStat); 70264562Sgshapiro#endif /* HASFLOCK */ 70338032Speter } 70438032Speter 70538032Speter sendenvelope(e, mode); 70638032Speter dropenvelope(e, TRUE); 70738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 70838032Speter { 70938032Speter CurEnv = ee; 71038032Speter if (mode != SM_VERIFY) 71138032Speter openxscript(ee); 71238032Speter sendenvelope(ee, mode); 71338032Speter dropenvelope(ee, TRUE); 71438032Speter } 71538032Speter CurEnv = e; 71638032Speter 71738032Speter Verbose = oldverbose; 71838032Speter if (mode == SM_FORK) 71942575Speter finis(TRUE, ExitStat); 72038032Speter} 72138032Speter 72264562Sgshapirostatic void 72338032Spetersendenvelope(e, mode) 72438032Speter register ENVELOPE *e; 72538032Speter int mode; 72638032Speter{ 72738032Speter register ADDRESS *q; 72838032Speter bool didany; 72938032Speter 73038032Speter if (tTd(13, 10)) 73164562Sgshapiro dprintf("sendenvelope(%s) e_flags=0x%lx\n", 73238032Speter e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 73338032Speter e->e_flags); 73438032Speter if (LogLevel > 80) 73538032Speter sm_syslog(LOG_DEBUG, e->e_id, 73664562Sgshapiro "sendenvelope, flags=0x%lx", 73764562Sgshapiro e->e_flags); 73838032Speter 73938032Speter /* 74038032Speter ** If we have had global, fatal errors, don't bother sending 74138032Speter ** the message at all if we are in SMTP mode. Local errors 74238032Speter ** (e.g., a single address failing) will still cause the other 74338032Speter ** addresses to be sent. 74438032Speter */ 74538032Speter 74638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 74738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 74838032Speter { 74938032Speter e->e_flags |= EF_CLRQUEUE; 75038032Speter return; 75138032Speter } 75238032Speter 75364562Sgshapiro /* Don't attempt deliveries if we want to bounce now */ 75464562Sgshapiro if (!bitset(EF_RESPONSE, e->e_flags) && 75564562Sgshapiro TimeOuts.to_q_return[e->e_timeoutclass] == NOW) 75664562Sgshapiro return; 75764562Sgshapiro 75838032Speter /* 75938032Speter ** Run through the list and send everything. 76038032Speter ** 76138032Speter ** Set EF_GLOBALERRS so that error messages during delivery 76238032Speter ** result in returned mail. 76338032Speter */ 76438032Speter 76538032Speter e->e_nsent = 0; 76638032Speter e->e_flags |= EF_GLOBALERRS; 76764562Sgshapiro 76838032Speter define(macid("{envid}", NULL), e->e_envid, e); 76938032Speter define(macid("{bodytype}", NULL), e->e_bodytype, e); 77038032Speter didany = FALSE; 77138032Speter 77238032Speter /* now run through the queue */ 77338032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 77438032Speter { 77538032Speter#if XDEBUG 77638032Speter char wbuf[MAXNAME + 20]; 77738032Speter 77838032Speter (void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", 77938032Speter MAXNAME, q->q_paddr); 78038032Speter checkfd012(wbuf); 78164562Sgshapiro#endif /* XDEBUG */ 78238032Speter if (mode == SM_VERIFY) 78338032Speter { 78438032Speter e->e_to = q->q_paddr; 78564562Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 78638032Speter { 78738032Speter if (q->q_host != NULL && q->q_host[0] != '\0') 78838032Speter message("deliverable: mailer %s, host %s, user %s", 78938032Speter q->q_mailer->m_name, 79038032Speter q->q_host, 79138032Speter q->q_user); 79238032Speter else 79338032Speter message("deliverable: mailer %s, user %s", 79438032Speter q->q_mailer->m_name, 79538032Speter q->q_user); 79638032Speter } 79738032Speter } 79864562Sgshapiro else if (QS_IS_OK(q->q_state)) 79938032Speter { 80064562Sgshapiro#if QUEUE 80138032Speter /* 80238032Speter ** Checkpoint the send list every few addresses 80338032Speter */ 80438032Speter 80566494Sgshapiro if (CheckpointInterval > 0 && 80666494Sgshapiro e->e_nsent >= CheckpointInterval) 80738032Speter { 80838032Speter queueup(e, FALSE); 80938032Speter e->e_nsent = 0; 81038032Speter } 81164562Sgshapiro#endif /* QUEUE */ 81238032Speter (void) deliver(e, q); 81338032Speter didany = TRUE; 81438032Speter } 81538032Speter } 81638032Speter if (didany) 81738032Speter { 81838032Speter e->e_dtime = curtime(); 81938032Speter e->e_ntries++; 82038032Speter } 82138032Speter 82238032Speter#if XDEBUG 82338032Speter checkfd012("end of sendenvelope"); 82464562Sgshapiro#endif /* XDEBUG */ 82538032Speter} 82638032Speter/* 82738032Speter** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 82838032Speter** 82938032Speter** Parameters: 83038032Speter** e -- the existing envelope 83138032Speter** ee -- the new envelope 83238032Speter** type -- the queue file type (e.g., 'd') 83338032Speter** 83438032Speter** Returns: 83538032Speter** none 83638032Speter*/ 83738032Speter 83864562Sgshapirostatic void 83938032Speterdup_queue_file(e, ee, type) 84038032Speter struct envelope *e, *ee; 84138032Speter int type; 84238032Speter{ 84364562Sgshapiro char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 84438032Speter 84538032Speter ee->e_dfp = NULL; 84638032Speter ee->e_xfp = NULL; 84764562Sgshapiro 84864562Sgshapiro /* 84964562Sgshapiro ** Make sure both are in the same directory. 85064562Sgshapiro */ 85164562Sgshapiro 85238032Speter snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type)); 85338032Speter snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type)); 85438032Speter if (link(f1buf, f2buf) < 0) 85538032Speter { 85664562Sgshapiro int save_errno = errno; 85738032Speter 85838032Speter syserr("sendall: link(%s, %s)", f1buf, f2buf); 85964562Sgshapiro if (save_errno == EEXIST) 86038032Speter { 86138032Speter if (unlink(f2buf) < 0) 86238032Speter { 86338032Speter syserr("!sendall: unlink(%s): permanent", 86438032Speter f2buf); 86564562Sgshapiro /* NOTREACHED */ 86638032Speter } 86738032Speter if (link(f1buf, f2buf) < 0) 86838032Speter { 86938032Speter syserr("!sendall: link(%s, %s): permanent", 87038032Speter f1buf, f2buf); 87164562Sgshapiro /* NOTREACHED */ 87238032Speter } 87338032Speter } 87438032Speter } 87538032Speter} 87638032Speter/* 87738032Speter** DOFORK -- do a fork, retrying a couple of times on failure. 87838032Speter** 87938032Speter** This MUST be a macro, since after a vfork we are running 88038032Speter** two processes on the same stack!!! 88138032Speter** 88238032Speter** Parameters: 88338032Speter** none. 88438032Speter** 88538032Speter** Returns: 88638032Speter** From a macro??? You've got to be kidding! 88738032Speter** 88838032Speter** Side Effects: 88938032Speter** Modifies the ==> LOCAL <== variable 'pid', leaving: 89038032Speter** pid of child in parent, zero in child. 89138032Speter** -1 on unrecoverable error. 89238032Speter** 89338032Speter** Notes: 89438032Speter** I'm awfully sorry this looks so awful. That's 89538032Speter** vfork for you..... 89638032Speter*/ 89738032Speter 89864562Sgshapiro#define NFORKTRIES 5 89938032Speter 90064562Sgshapiro#ifndef FORK 90138032Speter# define FORK fork 90264562Sgshapiro#endif /* ! FORK */ 90338032Speter 90464562Sgshapiro#define DOFORK(fORKfN) \ 90538032Speter{\ 90638032Speter register int i;\ 90738032Speter\ 90838032Speter for (i = NFORKTRIES; --i >= 0; )\ 90938032Speter {\ 91038032Speter pid = fORKfN();\ 91138032Speter if (pid >= 0)\ 91238032Speter break;\ 91338032Speter if (i > 0)\ 91464562Sgshapiro (void) sleep((unsigned) NFORKTRIES - i);\ 91538032Speter }\ 91638032Speter} 91738032Speter/* 91838032Speter** DOFORK -- simple fork interface to DOFORK. 91938032Speter** 92038032Speter** Parameters: 92138032Speter** none. 92238032Speter** 92338032Speter** Returns: 92438032Speter** pid of child in parent. 92538032Speter** zero in child. 92638032Speter** -1 on error. 92738032Speter** 92838032Speter** Side Effects: 92938032Speter** returns twice, once in parent and once in child. 93038032Speter*/ 93138032Speter 93238032Speterint 93338032Speterdofork() 93438032Speter{ 93538032Speter register pid_t pid = -1; 93638032Speter 93738032Speter DOFORK(fork); 93864562Sgshapiro return pid; 93938032Speter} 94038032Speter/* 94138032Speter** DELIVER -- Deliver a message to a list of addresses. 94238032Speter** 94338032Speter** This routine delivers to everyone on the same host as the 94438032Speter** user on the head of the list. It is clever about mailers 94538032Speter** that don't handle multiple users. It is NOT guaranteed 94638032Speter** that it will deliver to all these addresses however -- so 94738032Speter** deliver should be called once for each address on the 94838032Speter** list. 94938032Speter** 95038032Speter** Parameters: 95138032Speter** e -- the envelope to deliver. 95238032Speter** firstto -- head of the address list to deliver to. 95338032Speter** 95438032Speter** Returns: 95538032Speter** zero -- successfully delivered. 95638032Speter** else -- some failure, see ExitStat for more info. 95738032Speter** 95838032Speter** Side Effects: 95938032Speter** The standard input is passed off to someone. 96038032Speter*/ 96138032Speter 96238032Speter#ifndef NO_UID 96338032Speter# define NO_UID -1 96464562Sgshapiro#endif /* ! NO_UID */ 96538032Speter#ifndef NO_GID 96638032Speter# define NO_GID -1 96764562Sgshapiro#endif /* ! NO_GID */ 96838032Speter 96964562Sgshapirostatic int 97038032Speterdeliver(e, firstto) 97138032Speter register ENVELOPE *e; 97238032Speter ADDRESS *firstto; 97338032Speter{ 97438032Speter char *host; /* host being sent to */ 97538032Speter char *user; /* user being sent to */ 97638032Speter char **pvp; 97738032Speter register char **mvp; 97838032Speter register char *p; 97938032Speter register MAILER *m; /* mailer for this recipient */ 98038032Speter ADDRESS *volatile ctladdr; 98138032Speter ADDRESS *volatile contextaddr = NULL; 98238032Speter register MCI *volatile mci; 98338032Speter register ADDRESS *to = firstto; 98438032Speter volatile bool clever = FALSE; /* running user smtp to this mailer */ 98538032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 98638032Speter int rcode; /* response code */ 98738032Speter int lmtp_rcode = EX_OK; 98864562Sgshapiro int nummxhosts = 0; /* number of MX hosts available */ 98964562Sgshapiro int hostnum = 0; /* current MX host index */ 99038032Speter char *firstsig; /* signature of firstto */ 99138032Speter pid_t pid = -1; 99238032Speter char *volatile curhost; 99338032Speter register u_short port = 0; 99464562Sgshapiro#if NETUNIX 99564562Sgshapiro char *mux_path = NULL; /* path to UNIX domain socket */ 99664562Sgshapiro#endif /* NETUNIX */ 99738032Speter time_t xstart; 99838032Speter bool suidwarn; 99938032Speter bool anyok; /* at least one address was OK */ 100038032Speter bool goodmxfound = FALSE; /* at least one MX was OK */ 100164562Sgshapiro bool ovr; 100264562Sgshapiro#if _FFR_DYNAMIC_TOBUF 100364562Sgshapiro int strsize; 100464562Sgshapiro int rcptcount; 100564562Sgshapiro static int tobufsize = 0; 100664562Sgshapiro static char *tobuf = NULL; 100764562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 100864562Sgshapiro char tobuf[TOBUFSIZE]; /* text line of to people */ 100964562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 101038032Speter int mpvect[2]; 101138032Speter int rpvect[2]; 101264562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 101364562Sgshapiro char *pv[MAXPV + 1]; 101438032Speter char buf[MAXNAME + 1]; 101538032Speter char rpathbuf[MAXNAME + 1]; /* translated return path */ 101638032Speter 101738032Speter errno = 0; 101864562Sgshapiro if (!QS_IS_OK(to->q_state)) 101964562Sgshapiro return 0; 102038032Speter 102138032Speter suidwarn = geteuid() == 0; 102238032Speter 102338032Speter m = to->q_mailer; 102438032Speter host = to->q_host; 102538032Speter CurEnv = e; /* just in case */ 102638032Speter e->e_statmsg = NULL; 102738032Speter#if SMTP 102838032Speter SmtpError[0] = '\0'; 102964562Sgshapiro#endif /* SMTP */ 103038032Speter xstart = curtime(); 103138032Speter 103238032Speter if (tTd(10, 1)) 103364562Sgshapiro dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 103438032Speter e->e_id, m->m_name, host, to->q_user); 103538032Speter if (tTd(10, 100)) 103638032Speter printopenfds(FALSE); 103738032Speter 103838032Speter /* 103938032Speter ** Clear $&{client_*} macros if this is a bounce message to 104038032Speter ** prevent rejection by check_compat ruleset. 104138032Speter */ 104264562Sgshapiro 104338032Speter if (bitset(EF_RESPONSE, e->e_flags)) 104438032Speter { 104538032Speter define(macid("{client_name}", NULL), "", e); 104638032Speter define(macid("{client_addr}", NULL), "", e); 104738032Speter define(macid("{client_port}", NULL), "", e); 104838032Speter } 104964562Sgshapiro 105038032Speter /* 105138032Speter ** Do initial argv setup. 105238032Speter ** Insert the mailer name. Notice that $x expansion is 105338032Speter ** NOT done on the mailer name. Then, if the mailer has 105438032Speter ** a picky -f flag, we insert it as appropriate. This 105538032Speter ** code does not check for 'pv' overflow; this places a 105638032Speter ** manifest lower limit of 4 for MAXPV. 105738032Speter ** The from address rewrite is expected to make 105838032Speter ** the address relative to the other end. 105938032Speter */ 106038032Speter 106138032Speter /* rewrite from address, using rewriting rules */ 106238032Speter rcode = EX_OK; 106338032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 106438032Speter p = e->e_sender; 106538032Speter else 106638032Speter p = e->e_from.q_paddr; 106738032Speter p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 106838032Speter if (strlen(p) >= (SIZE_T) sizeof rpathbuf) 106938032Speter { 107038032Speter p = shortenstring(p, MAXSHORTSTR); 107138032Speter syserr("remotename: huge return %s", p); 107238032Speter } 107338032Speter snprintf(rpathbuf, sizeof rpathbuf, "%s", p); 107438032Speter define('g', rpathbuf, e); /* translated return path */ 107538032Speter define('h', host, e); /* to host */ 107638032Speter Errors = 0; 107738032Speter pvp = pv; 107838032Speter *pvp++ = m->m_argv[0]; 107938032Speter 108038032Speter /* insert -f or -r flag as appropriate */ 108164562Sgshapiro if (FromFlag && 108264562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 108364562Sgshapiro bitnset(M_ROPT, m->m_flags))) 108438032Speter { 108538032Speter if (bitnset(M_FOPT, m->m_flags)) 108638032Speter *pvp++ = "-f"; 108738032Speter else 108838032Speter *pvp++ = "-r"; 108938032Speter *pvp++ = newstr(rpathbuf); 109038032Speter } 109138032Speter 109238032Speter /* 109338032Speter ** Append the other fixed parts of the argv. These run 109438032Speter ** up to the first entry containing "$u". There can only 109538032Speter ** be one of these, and there are only a few more slots 109638032Speter ** in the pv after it. 109738032Speter */ 109838032Speter 109938032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 110038032Speter { 110138032Speter /* can't use strchr here because of sign extension problems */ 110238032Speter while (*p != '\0') 110338032Speter { 110438032Speter if ((*p++ & 0377) == MACROEXPAND) 110538032Speter { 110638032Speter if (*p == 'u') 110738032Speter break; 110838032Speter } 110938032Speter } 111038032Speter 111138032Speter if (*p != '\0') 111238032Speter break; 111338032Speter 111438032Speter /* this entry is safe -- go ahead and process it */ 111538032Speter expand(*mvp, buf, sizeof buf, e); 111638032Speter *pvp++ = newstr(buf); 111738032Speter if (pvp >= &pv[MAXPV - 3]) 111838032Speter { 111964562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 112064562Sgshapiro pv[0]); 112164562Sgshapiro return -1; 112238032Speter } 112338032Speter } 112438032Speter 112538032Speter /* 112638032Speter ** If we have no substitution for the user name in the argument 112738032Speter ** list, we know that we must supply the names otherwise -- and 112838032Speter ** SMTP is the answer!! 112938032Speter */ 113038032Speter 113138032Speter if (*mvp == NULL) 113238032Speter { 113338032Speter /* running SMTP */ 113464562Sgshapiro#if SMTP 113538032Speter clever = TRUE; 113638032Speter *pvp = NULL; 113764562Sgshapiro#else /* SMTP */ 113838032Speter /* oops! we don't implement SMTP */ 113964562Sgshapiro syserr("554 5.3.5 SMTP style mailer not implemented"); 114064562Sgshapiro return EX_SOFTWARE; 114164562Sgshapiro#endif /* SMTP */ 114238032Speter } 114338032Speter 114438032Speter /* 114538032Speter ** At this point *mvp points to the argument with $u. We 114638032Speter ** run through our address list and append all the addresses 114738032Speter ** we can. If we run out of space, do not fret! We can 114838032Speter ** always send another copy later. 114938032Speter */ 115038032Speter 115164562Sgshapiro#if _FFR_DYNAMIC_TOBUF 115264562Sgshapiro e->e_to = NULL; 115364562Sgshapiro strsize = 2; 115464562Sgshapiro rcptcount = 0; 115564562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 115638032Speter tobuf[0] = '\0'; 115738032Speter e->e_to = tobuf; 115864562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 115964562Sgshapiro 116038032Speter ctladdr = NULL; 116164562Sgshapiro firstsig = hostsignature(firstto->q_mailer, firstto->q_host); 116238032Speter for (; to != NULL; to = to->q_next) 116338032Speter { 116438032Speter /* avoid sending multiple recipients to dumb mailers */ 116564562Sgshapiro#if _FFR_DYNAMIC_TOBUF 116664562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 116764562Sgshapiro break; 116864562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 116938032Speter if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags)) 117038032Speter break; 117164562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 117238032Speter 117338032Speter /* if already sent or not for this host, don't send */ 117464562Sgshapiro if (!QS_IS_OK(to->q_state) || 117538032Speter to->q_mailer != firstto->q_mailer || 117664562Sgshapiro strcmp(hostsignature(to->q_mailer, to->q_host), 117764562Sgshapiro firstsig) != 0) 117838032Speter continue; 117938032Speter 118038032Speter /* avoid overflowing tobuf */ 118164562Sgshapiro#if _FFR_DYNAMIC_TOBUF 118264562Sgshapiro strsize += strlen(to->q_paddr) + 1; 118364562Sgshapiro if (!clever && strsize > TOBUFSIZE) 118464562Sgshapiro break; 118564562Sgshapiro 118664562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 118764562Sgshapiro break; 118864562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 118938032Speter if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2)) 119038032Speter break; 119164562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 119238032Speter 119338032Speter if (tTd(10, 1)) 119438032Speter { 119564562Sgshapiro dprintf("\nsend to "); 119638032Speter printaddr(to, FALSE); 119738032Speter } 119838032Speter 119938032Speter /* compute effective uid/gid when sending */ 120038032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 120138032Speter contextaddr = ctladdr = getctladdr(to); 120238032Speter 120338032Speter if (tTd(10, 2)) 120438032Speter { 120564562Sgshapiro dprintf("ctladdr="); 120638032Speter printaddr(ctladdr, FALSE); 120738032Speter } 120838032Speter 120938032Speter user = to->q_user; 121038032Speter e->e_to = to->q_paddr; 121138032Speter 121238032Speter /* 121338032Speter ** Check to see that these people are allowed to 121438032Speter ** talk to each other. 121566494Sgshapiro ** Check also for overflow of e_msgsize. 121638032Speter */ 121738032Speter 121866494Sgshapiro if (m->m_maxsize != 0 && 121966494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 122038032Speter { 122138032Speter e->e_flags |= EF_NO_BODY_RETN; 122238032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 122338032Speter to->q_status = "5.2.3"; 122438032Speter else 122538032Speter to->q_status = "5.3.4"; 122664562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 122764562Sgshapiro usrerrenh(to->q_status, 122864562Sgshapiro "552 Message is too large; %ld bytes max", 122964562Sgshapiro m->m_maxsize); 123064562Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, FALSE); 123164562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 123264562Sgshapiro NULL, ctladdr, xstart, e); 123338032Speter continue; 123438032Speter } 123538032Speter#if NAMED_BIND 123638032Speter h_errno = 0; 123764562Sgshapiro#endif /* NAMED_BIND */ 123838032Speter 123964562Sgshapiro ovr = TRUE; 124038032Speter /* do config file checking of compatibility */ 124164562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 124264562Sgshapiro e, TRUE, TRUE, 4); 124338032Speter if (rcode == EX_OK) 124438032Speter { 124542575Speter /* do in-code checking if not discarding */ 124642575Speter if (!bitset(EF_DISCARD, e->e_flags)) 124764562Sgshapiro { 124842575Speter rcode = checkcompat(to, e); 124964562Sgshapiro ovr = FALSE; 125064562Sgshapiro } 125138032Speter } 125238032Speter if (rcode != EX_OK) 125338032Speter { 125464562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 125564562Sgshapiro giveresponse(rcode, to->q_status, m, 125664562Sgshapiro NULL, ctladdr, xstart, e); 125738032Speter continue; 125838032Speter } 125942575Speter if (bitset(EF_DISCARD, e->e_flags)) 126042575Speter { 126142575Speter if (tTd(10, 5)) 126242575Speter { 126364562Sgshapiro dprintf("deliver: discarding recipient "); 126442575Speter printaddr(to, FALSE); 126542575Speter } 126638032Speter 126764562Sgshapiro /* pretend the message was sent */ 126864562Sgshapiro /* XXX should we log something here? */ 126964562Sgshapiro to->q_state = QS_DISCARDED; 127064562Sgshapiro 127142575Speter /* 127242575Speter ** Remove discard bit to prevent discard of 127364562Sgshapiro ** future recipients. This is safe because the 127464562Sgshapiro ** true "global discard" has been handled before 127564562Sgshapiro ** we get here. 127642575Speter */ 127764562Sgshapiro 127842575Speter e->e_flags &= ~EF_DISCARD; 127942575Speter continue; 128042575Speter } 128142575Speter 128238032Speter /* 128338032Speter ** Strip quote bits from names if the mailer is dumb 128438032Speter ** about them. 128538032Speter */ 128638032Speter 128738032Speter if (bitnset(M_STRIPQ, m->m_flags)) 128838032Speter { 128938032Speter stripquotes(user); 129038032Speter stripquotes(host); 129138032Speter } 129238032Speter 129338032Speter /* hack attack -- delivermail compatibility */ 129438032Speter if (m == ProgMailer && *user == '|') 129538032Speter user++; 129638032Speter 129738032Speter /* 129838032Speter ** If an error message has already been given, don't 129938032Speter ** bother to send to this address. 130038032Speter ** 130138032Speter ** >>>>>>>>>> This clause assumes that the local mailer 130238032Speter ** >> NOTE >> cannot do any further aliasing; that 130338032Speter ** >>>>>>>>>> function is subsumed by sendmail. 130438032Speter */ 130538032Speter 130664562Sgshapiro if (!QS_IS_OK(to->q_state)) 130738032Speter continue; 130838032Speter 130938032Speter /* 131038032Speter ** See if this user name is "special". 131138032Speter ** If the user name has a slash in it, assume that this 131238032Speter ** is a file -- send it off without further ado. Note 131338032Speter ** that this type of addresses is not processed along 131438032Speter ** with the others, so we fudge on the To person. 131538032Speter */ 131638032Speter 131738032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 131838032Speter { 131938032Speter define('u', user, e); /* to user */ 132038032Speter p = to->q_home; 132138032Speter if (p == NULL && ctladdr != NULL) 132238032Speter p = ctladdr->q_home; 132338032Speter define('z', p, e); /* user's home */ 132438032Speter expand(m->m_argv[1], buf, sizeof buf, e); 132538032Speter if (strlen(buf) > 0) 132638032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 132738032Speter else 132838032Speter { 132938032Speter syserr("empty filename specification for mailer %s", 133038032Speter m->m_name); 133138032Speter rcode = EX_CONFIG; 133238032Speter } 133364562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 133464562Sgshapiro ctladdr, xstart, e); 133564562Sgshapiro markfailure(e, to, NULL, rcode, TRUE); 133638032Speter e->e_nsent++; 133738032Speter if (rcode == EX_OK) 133838032Speter { 133964562Sgshapiro to->q_state = QS_SENT; 134038032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 134138032Speter bitset(QPINGONSUCCESS, to->q_flags)) 134238032Speter { 134338032Speter to->q_flags |= QDELIVERED; 134438032Speter to->q_status = "2.1.5"; 134538032Speter fprintf(e->e_xfp, "%s... Successfully delivered\n", 134638032Speter to->q_paddr); 134738032Speter } 134838032Speter } 134938032Speter to->q_statdate = curtime(); 135038032Speter markstats(e, to, FALSE); 135138032Speter continue; 135238032Speter } 135338032Speter 135438032Speter /* 135538032Speter ** Address is verified -- add this user to mailer 135638032Speter ** argv, and add it to the print list of recipients. 135738032Speter */ 135838032Speter 135938032Speter /* link together the chain of recipients */ 136038032Speter to->q_tchain = tochain; 136138032Speter tochain = to; 136238032Speter 136364562Sgshapiro#if _FFR_DYNAMIC_TOBUF 136464562Sgshapiro e->e_to = "[CHAIN]"; 136564562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 136638032Speter /* create list of users for error messages */ 136764562Sgshapiro (void) strlcat(tobuf, ",", sizeof tobuf); 136864562Sgshapiro (void) strlcat(tobuf, to->q_paddr, sizeof tobuf); 136964562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 137064562Sgshapiro 137138032Speter define('u', user, e); /* to user */ 137238032Speter p = to->q_home; 137338032Speter if (p == NULL && ctladdr != NULL) 137438032Speter p = ctladdr->q_home; 137538032Speter define('z', p, e); /* user's home */ 137638032Speter 137764562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 137864562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 137964562Sgshapiro { 138064562Sgshapiro char notify[MAXLINE]; 138164562Sgshapiro 138264562Sgshapiro notify[0] = '\0'; 138364562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 138464562Sgshapiro (void) strlcat(notify, "SUCCESS,", 138564562Sgshapiro sizeof notify); 138664562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 138764562Sgshapiro (void) strlcat(notify, "FAILURE,", 138864562Sgshapiro sizeof notify); 138964562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 139064562Sgshapiro (void) strlcat(notify, "DELAY,", sizeof notify); 139164562Sgshapiro 139264562Sgshapiro /* Set to NEVER or drop trailing comma */ 139364562Sgshapiro if (notify[0] == '\0') 139464562Sgshapiro (void) strlcat(notify, "NEVER", sizeof notify); 139564562Sgshapiro else 139664562Sgshapiro notify[strlen(notify) - 1] = '\0'; 139764562Sgshapiro 139864562Sgshapiro define(macid("{dsn_notify}", NULL), newstr(notify), e); 139964562Sgshapiro } 140064562Sgshapiro else 140164562Sgshapiro define(macid("{dsn_notify}", NULL), NULL, e); 140264562Sgshapiro 140338032Speter /* 140438032Speter ** Expand out this user into argument list. 140538032Speter */ 140638032Speter 140738032Speter if (!clever) 140838032Speter { 140938032Speter expand(*mvp, buf, sizeof buf, e); 141038032Speter *pvp++ = newstr(buf); 141138032Speter if (pvp >= &pv[MAXPV - 2]) 141238032Speter { 141338032Speter /* allow some space for trailing parms */ 141438032Speter break; 141538032Speter } 141638032Speter } 141738032Speter } 141838032Speter 141938032Speter /* see if any addresses still exist */ 142064562Sgshapiro#if _FFR_DYNAMIC_TOBUF 142164562Sgshapiro if (tochain == NULL) 142264562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 142338032Speter if (tobuf[0] == '\0') 142464562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 142538032Speter { 142638032Speter define('g', (char *) NULL, e); 142764562Sgshapiro e->e_to = NULL; 142864562Sgshapiro return 0; 142938032Speter } 143038032Speter 143138032Speter /* print out messages as full list */ 143264562Sgshapiro#if _FFR_DYNAMIC_TOBUF 143364562Sgshapiro { 143464562Sgshapiro int l = 1; 143564562Sgshapiro char *tobufptr; 143664562Sgshapiro 143764562Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 143864562Sgshapiro l += strlen(to->q_paddr) + 1; 143964562Sgshapiro if (l < TOBUFSIZE) 144064562Sgshapiro l = TOBUFSIZE; 144164562Sgshapiro if (l > tobufsize) 144264562Sgshapiro { 144364562Sgshapiro if (tobuf != NULL) 144464562Sgshapiro free(tobuf); 144564562Sgshapiro tobufsize = l; 144664562Sgshapiro tobuf = xalloc(tobufsize); 144764562Sgshapiro } 144864562Sgshapiro tobufptr = tobuf; 144964562Sgshapiro *tobufptr = '\0'; 145064562Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 145164562Sgshapiro { 145264562Sgshapiro snprintf(tobufptr, tobufsize - (tobufptr - tobuf), 145364562Sgshapiro ",%s", to->q_paddr); 145464562Sgshapiro tobufptr += strlen(tobufptr); 145564562Sgshapiro } 145664562Sgshapiro } 145764562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 145838032Speter e->e_to = tobuf + 1; 145938032Speter 146038032Speter /* 146138032Speter ** Fill out any parameters after the $u parameter. 146238032Speter */ 146338032Speter 146438032Speter while (!clever && *++mvp != NULL) 146538032Speter { 146638032Speter expand(*mvp, buf, sizeof buf, e); 146738032Speter *pvp++ = newstr(buf); 146838032Speter if (pvp >= &pv[MAXPV]) 146964562Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 147064562Sgshapiro pv[0]); 147138032Speter } 147238032Speter *pvp++ = NULL; 147338032Speter 147438032Speter /* 147538032Speter ** Call the mailer. 147638032Speter ** The argument vector gets built, pipes 147738032Speter ** are created as necessary, and we fork & exec as 147838032Speter ** appropriate. 147938032Speter ** If we are running SMTP, we just need to clean up. 148038032Speter */ 148138032Speter 148264562Sgshapiro /* XXX this seems a bit wierd */ 148338032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 148438032Speter bitset(QGOODUID, e->e_from.q_flags)) 148538032Speter ctladdr = &e->e_from; 148638032Speter 148738032Speter#if NAMED_BIND 148838032Speter if (ConfigLevel < 2) 148938032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 149064562Sgshapiro#endif /* NAMED_BIND */ 149138032Speter 149238032Speter if (tTd(11, 1)) 149338032Speter { 149464562Sgshapiro dprintf("openmailer:"); 149538032Speter printav(pv); 149638032Speter } 149738032Speter errno = 0; 149838032Speter#if NAMED_BIND 149938032Speter h_errno = 0; 150064562Sgshapiro#endif /* NAMED_BIND */ 150138032Speter 150238032Speter CurHostName = NULL; 150338032Speter 150438032Speter /* 150538032Speter ** Deal with the special case of mail handled through an IPC 150638032Speter ** connection. 150738032Speter ** In this case we don't actually fork. We must be 150838032Speter ** running SMTP for this to work. We will return a 150938032Speter ** zero pid to indicate that we are running IPC. 151038032Speter ** We also handle a debug version that just talks to stdin/out. 151138032Speter */ 151238032Speter 151338032Speter curhost = NULL; 151438032Speter SmtpPhase = NULL; 151538032Speter mci = NULL; 151638032Speter 151738032Speter#if XDEBUG 151838032Speter { 151938032Speter char wbuf[MAXLINE]; 152038032Speter 152138032Speter /* make absolutely certain 0, 1, and 2 are in use */ 152238032Speter snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", 152338032Speter shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 152438032Speter checkfd012(wbuf); 152538032Speter } 152664562Sgshapiro#endif /* XDEBUG */ 152738032Speter 152838032Speter /* check for 8-bit available */ 152938032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 153038032Speter bitnset(M_7BITS, m->m_flags) && 153138032Speter (bitset(EF_DONT_MIME, e->e_flags) || 153238032Speter !(bitset(MM_MIME8BIT, MimeMode) || 153338032Speter (bitset(EF_IS_MIME, e->e_flags) && 153464562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 153538032Speter { 153664562Sgshapiro e->e_status = "5.6.3"; 153764562Sgshapiro usrerrenh(e->e_status, 153864562Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 153938032Speter rcode = EX_DATAERR; 154038032Speter goto give_up; 154138032Speter } 154238032Speter 154338032Speter if (tTd(62, 8)) 154438032Speter checkfds("before delivery"); 154538032Speter 154638032Speter /* check for Local Person Communication -- not for mortals!!! */ 154738032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 154838032Speter { 154938032Speter mci = (MCI *) xalloc(sizeof *mci); 155064562Sgshapiro memset((char *) mci, '\0', sizeof *mci); 155138032Speter mci->mci_in = stdin; 155238032Speter mci->mci_out = stdout; 155338032Speter mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; 155438032Speter mci->mci_mailer = m; 155538032Speter } 155638032Speter else if (strcmp(m->m_mailer, "[IPC]") == 0 || 155738032Speter strcmp(m->m_mailer, "[TCP]") == 0) 155838032Speter { 155938032Speter#if DAEMON 156038032Speter register int i; 156138032Speter 156238032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 156338032Speter { 156464562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 156538032Speter rcode = EX_CONFIG; 156638032Speter goto give_up; 156738032Speter } 156838032Speter 156964562Sgshapiro# if NETUNIX 157064562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 157164562Sgshapiro { 157264562Sgshapiro curhost = CurHostName = "localhost"; 157364562Sgshapiro mux_path = pv[1]; 157464562Sgshapiro } 157564562Sgshapiro else 157664562Sgshapiro# endif /* NETUNIX */ 157764562Sgshapiro { 157864562Sgshapiro CurHostName = pv[1]; 157964562Sgshapiro curhost = hostsignature(m, pv[1]); 158064562Sgshapiro } 158138032Speter 158238032Speter if (curhost == NULL || curhost[0] == '\0') 158338032Speter { 158438032Speter syserr("null host signature for %s", pv[1]); 158538032Speter rcode = EX_CONFIG; 158638032Speter goto give_up; 158738032Speter } 158838032Speter 158938032Speter if (!clever) 159038032Speter { 159164562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 159238032Speter rcode = EX_CONFIG; 159338032Speter goto give_up; 159438032Speter } 159564562Sgshapiro if (pv[2] != NULL 159664562Sgshapiro# if NETUNIX 159764562Sgshapiro && mux_path == NULL 159864562Sgshapiro# endif /* NETUNIX */ 159964562Sgshapiro ) 160038032Speter { 160164562Sgshapiro port = htons((u_short)atoi(pv[2])); 160238032Speter if (port == 0) 160338032Speter { 160464562Sgshapiro# ifdef NO_GETSERVBYNAME 160564562Sgshapiro syserr("Invalid port number: %s", pv[2]); 160664562Sgshapiro# else /* NO_GETSERVBYNAME */ 160738032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 160838032Speter 160938032Speter if (sp == NULL) 161038032Speter syserr("Service %s unknown", pv[2]); 161138032Speter else 161238032Speter port = sp->s_port; 161364562Sgshapiro# endif /* NO_GETSERVBYNAME */ 161438032Speter } 161538032Speter } 161664562Sgshapiro 161764562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 161838032Spetertryhost: 161964562Sgshapiro while (hostnum < nummxhosts) 162038032Speter { 162164562Sgshapiro char sep = ':'; 162264562Sgshapiro char *endp; 162338032Speter static char hostbuf[MAXNAME + 1]; 162438032Speter 162564562Sgshapiro# if NETINET6 162664562Sgshapiro if (*mxhosts[hostnum] == '[') 162738032Speter { 162864562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 162964562Sgshapiro if (endp != NULL) 163064562Sgshapiro endp = strpbrk(endp + 1, ":,"); 163164562Sgshapiro } 163264562Sgshapiro else 163364562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 163464562Sgshapiro# else /* NETINET6 */ 163564562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 163664562Sgshapiro# endif /* NETINET6 */ 163764562Sgshapiro if (endp != NULL) 163864562Sgshapiro { 163964562Sgshapiro sep = *endp; 164064562Sgshapiro *endp = '\0'; 164164562Sgshapiro } 164264562Sgshapiro 164364562Sgshapiro if (*mxhosts[hostnum] == '\0') 164464562Sgshapiro { 164538032Speter syserr("deliver: null host name in signature"); 164664562Sgshapiro hostnum++; 164764562Sgshapiro if (endp != NULL) 164864562Sgshapiro *endp = sep; 164938032Speter continue; 165038032Speter } 165164562Sgshapiro (void) strlcpy(hostbuf, mxhosts[hostnum], 165264562Sgshapiro sizeof hostbuf); 165364562Sgshapiro hostnum++; 165464562Sgshapiro if (endp != NULL) 165564562Sgshapiro *endp = sep; 165638032Speter 165738032Speter /* see if we already know that this host is fried */ 165838032Speter CurHostName = hostbuf; 165938032Speter mci = mci_get(hostbuf, m); 166038032Speter if (mci->mci_state != MCIS_CLOSED) 166138032Speter { 166238032Speter if (tTd(11, 1)) 166338032Speter { 166464562Sgshapiro dprintf("openmailer: "); 166538032Speter mci_dump(mci, FALSE); 166638032Speter } 166738032Speter CurHostName = mci->mci_host; 166838032Speter message("Using cached %sSMTP connection to %s via %s...", 166938032Speter bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "", 167038032Speter hostbuf, m->m_name); 167164562Sgshapiro mci->mci_deliveries++; 167238032Speter break; 167338032Speter } 167438032Speter mci->mci_mailer = m; 167538032Speter if (mci->mci_exitstat != EX_OK) 167638032Speter { 167738032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 167838032Speter goodmxfound = TRUE; 167938032Speter continue; 168038032Speter } 168138032Speter 168238032Speter if (mci_lock_host(mci) != EX_OK) 168338032Speter { 168438032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 168538032Speter goodmxfound = TRUE; 168638032Speter continue; 168738032Speter } 168838032Speter 168938032Speter /* try the connection */ 169064562Sgshapiro sm_setproctitle(TRUE, e, "%s %s: %s", 169164562Sgshapiro qid_printname(e), 169264562Sgshapiro hostbuf, "user open"); 169364562Sgshapiro# if NETUNIX 169464562Sgshapiro if (mux_path != NULL) 169564562Sgshapiro { 169638032Speter message("Connecting to %s via %s...", 169764562Sgshapiro mux_path, m->m_name); 169864562Sgshapiro i = makeconnection_ds(mux_path, mci); 169964562Sgshapiro } 170038032Speter else 170164562Sgshapiro# endif /* NETUNIX */ 170264562Sgshapiro { 170364562Sgshapiro if (port == 0) 170464562Sgshapiro message("Connecting to %s via %s...", 170564562Sgshapiro hostbuf, m->m_name); 170664562Sgshapiro else 170764562Sgshapiro message("Connecting to %s port %d via %s...", 170864562Sgshapiro hostbuf, ntohs(port), 170964562Sgshapiro m->m_name); 171064562Sgshapiro i = makeconnection(hostbuf, port, mci, e); 171164562Sgshapiro } 171238032Speter mci->mci_lastuse = curtime(); 171364562Sgshapiro mci->mci_deliveries = 0; 171438032Speter mci->mci_exitstat = i; 171538032Speter mci->mci_errno = errno; 171664562Sgshapiro# if NAMED_BIND 171738032Speter mci->mci_herrno = h_errno; 171864562Sgshapiro# endif /* NAMED_BIND */ 171938032Speter if (i == EX_OK) 172038032Speter { 172138032Speter goodmxfound = TRUE; 172238032Speter mci->mci_state = MCIS_OPENING; 172338032Speter mci_cache(mci); 172438032Speter if (TrafficLogFile != NULL) 172538032Speter fprintf(TrafficLogFile, "%05d === CONNECT %s\n", 172638032Speter (int) getpid(), hostbuf); 172738032Speter break; 172838032Speter } 172938032Speter else 173038032Speter { 173164562Sgshapiro if (tTd(11, 1)) 173264562Sgshapiro dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 173338032Speter i, errno); 173438032Speter if (i == EX_TEMPFAIL) 173538032Speter goodmxfound = TRUE; 173638032Speter mci_unlock_host(mci); 173738032Speter } 173838032Speter 173938032Speter /* enter status of this host */ 174038032Speter setstat(i); 174138032Speter 174238032Speter /* should print some message here for -v mode */ 174338032Speter } 174438032Speter if (mci == NULL) 174538032Speter { 174638032Speter syserr("deliver: no host name"); 174738032Speter rcode = EX_SOFTWARE; 174838032Speter goto give_up; 174938032Speter } 175038032Speter mci->mci_pid = 0; 175164562Sgshapiro#else /* DAEMON */ 175264562Sgshapiro syserr("554 5.3.5 openmailer: no IPC"); 175338032Speter if (tTd(11, 1)) 175464562Sgshapiro dprintf("openmailer: NULL\n"); 175538032Speter rcode = EX_UNAVAILABLE; 175638032Speter goto give_up; 175738032Speter#endif /* DAEMON */ 175838032Speter } 175938032Speter else 176038032Speter { 176138032Speter /* flush any expired connections */ 176238032Speter (void) mci_scan(NULL); 176338032Speter mci = NULL; 176438032Speter 176538032Speter#if SMTP 176638032Speter if (bitnset(M_LMTP, m->m_flags)) 176738032Speter { 176838032Speter /* try to get a cached connection */ 176938032Speter mci = mci_get(m->m_name, m); 177038032Speter if (mci->mci_host == NULL) 177138032Speter mci->mci_host = m->m_name; 177238032Speter CurHostName = mci->mci_host; 177338032Speter if (mci->mci_state != MCIS_CLOSED) 177438032Speter { 177538032Speter message("Using cached LMTP connection for %s...", 177638032Speter m->m_name); 177764562Sgshapiro mci->mci_deliveries++; 177838032Speter goto do_transfer; 177938032Speter } 178038032Speter } 178164562Sgshapiro#endif /* SMTP */ 178238032Speter 178338032Speter /* announce the connection to verbose listeners */ 178438032Speter if (host == NULL || host[0] == '\0') 178538032Speter message("Connecting to %s...", m->m_name); 178638032Speter else 178738032Speter message("Connecting to %s via %s...", host, m->m_name); 178838032Speter if (TrafficLogFile != NULL) 178938032Speter { 179038032Speter char **av; 179138032Speter 179238032Speter fprintf(TrafficLogFile, "%05d === EXEC", (int) getpid()); 179338032Speter for (av = pv; *av != NULL; av++) 179438032Speter fprintf(TrafficLogFile, " %s", *av); 179538032Speter fprintf(TrafficLogFile, "\n"); 179638032Speter } 179738032Speter 179838032Speter#if XDEBUG 179938032Speter checkfd012("before creating mail pipe"); 180064562Sgshapiro#endif /* XDEBUG */ 180138032Speter 180238032Speter /* create a pipe to shove the mail through */ 180338032Speter if (pipe(mpvect) < 0) 180438032Speter { 180538032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 180638032Speter shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 180738032Speter if (tTd(11, 1)) 180864562Sgshapiro dprintf("openmailer: NULL\n"); 180938032Speter rcode = EX_OSERR; 181038032Speter goto give_up; 181138032Speter } 181238032Speter 181338032Speter#if XDEBUG 181438032Speter /* make sure we didn't get one of the standard I/O files */ 181538032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 181638032Speter { 181738032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 181838032Speter shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 181938032Speter mpvect[0], mpvect[1]); 182038032Speter printopenfds(TRUE); 182138032Speter if (tTd(11, 1)) 182264562Sgshapiro dprintf("openmailer: NULL\n"); 182338032Speter rcode = EX_OSERR; 182438032Speter goto give_up; 182538032Speter } 182638032Speter 182738032Speter /* make sure system call isn't dead meat */ 182838032Speter checkfdopen(mpvect[0], "mpvect[0]"); 182938032Speter checkfdopen(mpvect[1], "mpvect[1]"); 183038032Speter if (mpvect[0] == mpvect[1] || 183138032Speter (e->e_lockfp != NULL && 183238032Speter (mpvect[0] == fileno(e->e_lockfp) || 183338032Speter mpvect[1] == fileno(e->e_lockfp)))) 183438032Speter { 183538032Speter if (e->e_lockfp == NULL) 183638032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 183738032Speter shortenstring(e->e_to, MAXSHORTSTR), 183838032Speter m->m_name, mpvect[0], mpvect[1]); 183938032Speter else 184038032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 184138032Speter shortenstring(e->e_to, MAXSHORTSTR), 184238032Speter m->m_name, mpvect[0], mpvect[1], 184338032Speter fileno(e->e_lockfp)); 184438032Speter } 184564562Sgshapiro#endif /* XDEBUG */ 184638032Speter 184764562Sgshapiro /* create a return pipe */ 184864562Sgshapiro if (pipe(rpvect) < 0) 184938032Speter { 185064562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 185164562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 185264562Sgshapiro m->m_name); 185364562Sgshapiro (void) close(mpvect[0]); 185464562Sgshapiro (void) close(mpvect[1]); 185564562Sgshapiro if (tTd(11, 1)) 185664562Sgshapiro dprintf("openmailer: NULL\n"); 185764562Sgshapiro rcode = EX_OSERR; 185864562Sgshapiro goto give_up; 185938032Speter } 186064562Sgshapiro#if XDEBUG 186164562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 186264562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 186364562Sgshapiro#endif /* XDEBUG */ 186438032Speter 186538032Speter /* 186638032Speter ** Actually fork the mailer process. 186738032Speter ** DOFORK is clever about retrying. 186838032Speter ** 186938032Speter ** Dispose of SIGCHLD signal catchers that may be laying 187064562Sgshapiro ** around so that endmailer will get it. 187138032Speter */ 187238032Speter 187338032Speter if (e->e_xfp != NULL) 187464562Sgshapiro (void) fflush(e->e_xfp); /* for debugging */ 187538032Speter (void) fflush(stdout); 187638032Speter (void) setsignal(SIGCHLD, SIG_DFL); 187764562Sgshapiro 187864562Sgshapiro 187938032Speter DOFORK(FORK); 188038032Speter /* pid is set by DOFORK */ 188164562Sgshapiro 188238032Speter if (pid < 0) 188338032Speter { 188438032Speter /* failure */ 188538032Speter syserr("%s... openmailer(%s): cannot fork", 188638032Speter shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 188738032Speter (void) close(mpvect[0]); 188838032Speter (void) close(mpvect[1]); 188964562Sgshapiro (void) close(rpvect[0]); 189064562Sgshapiro (void) close(rpvect[1]); 189138032Speter if (tTd(11, 1)) 189264562Sgshapiro dprintf("openmailer: NULL\n"); 189338032Speter rcode = EX_OSERR; 189438032Speter goto give_up; 189538032Speter } 189638032Speter else if (pid == 0) 189738032Speter { 189838032Speter int i; 189964562Sgshapiro int save_errno; 190038032Speter int new_euid = NO_UID; 190138032Speter int new_ruid = NO_UID; 190238032Speter int new_gid = NO_GID; 190338032Speter struct stat stb; 190438032Speter extern int DtableSize; 190538032Speter 190638032Speter if (e->e_lockfp != NULL) 190738032Speter (void) close(fileno(e->e_lockfp)); 190838032Speter 190938032Speter /* child -- set up input & exec mailer */ 191038032Speter (void) setsignal(SIGINT, SIG_IGN); 191138032Speter (void) setsignal(SIGHUP, SIG_IGN); 191238032Speter (void) setsignal(SIGTERM, SIG_DFL); 191338032Speter 191438032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 191538032Speter stb.st_mode = 0; 191638032Speter 191764562Sgshapiro# if HASSETUSERCONTEXT 191838032Speter /* 191938032Speter ** Set user resources. 192038032Speter */ 192138032Speter 192238032Speter if (contextaddr != NULL) 192338032Speter { 192438032Speter struct passwd *pwd; 192538032Speter 192638032Speter if (contextaddr->q_ruser != NULL) 192738032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 192838032Speter else 192938032Speter pwd = sm_getpwnam(contextaddr->q_user); 193038032Speter if (pwd != NULL) 193138032Speter (void) setusercontext(NULL, 193238032Speter pwd, pwd->pw_uid, 193338032Speter LOGIN_SETRESOURCES|LOGIN_SETPRIORITY); 193438032Speter } 193564562Sgshapiro# endif /* HASSETUSERCONTEXT */ 193638032Speter 193738032Speter /* tweak niceness */ 193838032Speter if (m->m_nice != 0) 193964562Sgshapiro (void) nice(m->m_nice); 194038032Speter 194138032Speter /* reset group id */ 194238032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 194338032Speter new_gid = m->m_gid; 194438032Speter else if (bitset(S_ISGID, stb.st_mode)) 194538032Speter new_gid = stb.st_gid; 194638032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 194738032Speter { 194838032Speter if (!DontInitGroups) 194938032Speter { 195038032Speter char *u = ctladdr->q_ruser; 195138032Speter 195238032Speter if (u == NULL) 195338032Speter u = ctladdr->q_user; 195438032Speter 195538032Speter if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn) 195664562Sgshapiro { 195738032Speter syserr("openmailer: initgroups(%s, %d) failed", 195838032Speter u, ctladdr->q_gid); 195964562Sgshapiro exit(EX_TEMPFAIL); 196064562Sgshapiro } 196138032Speter } 196238032Speter else 196338032Speter { 196438032Speter GIDSET_T gidset[1]; 196538032Speter 196638032Speter gidset[0] = ctladdr->q_gid; 196738032Speter if (setgroups(1, gidset) == -1 && suidwarn) 196864562Sgshapiro { 196938032Speter syserr("openmailer: setgroups() failed"); 197064562Sgshapiro exit(EX_TEMPFAIL); 197164562Sgshapiro } 197238032Speter } 197338032Speter new_gid = ctladdr->q_gid; 197438032Speter } 197538032Speter else 197638032Speter { 197738032Speter if (!DontInitGroups) 197838032Speter { 197938032Speter if (initgroups(DefUser, DefGid) == -1 && suidwarn) 198064562Sgshapiro { 198138032Speter syserr("openmailer: initgroups(%s, %d) failed", 198238032Speter DefUser, DefGid); 198364562Sgshapiro exit(EX_TEMPFAIL); 198464562Sgshapiro } 198538032Speter } 198638032Speter else 198738032Speter { 198838032Speter GIDSET_T gidset[1]; 198938032Speter 199038032Speter gidset[0] = DefGid; 199138032Speter if (setgroups(1, gidset) == -1 && suidwarn) 199264562Sgshapiro { 199338032Speter syserr("openmailer: setgroups() failed"); 199464562Sgshapiro exit(EX_TEMPFAIL); 199564562Sgshapiro } 199638032Speter } 199738032Speter if (m->m_gid == 0) 199838032Speter new_gid = DefGid; 199938032Speter else 200038032Speter new_gid = m->m_gid; 200138032Speter } 200264562Sgshapiro if (new_gid != NO_GID) 200364562Sgshapiro { 200464562Sgshapiro if (RunAsUid != 0 && 200564562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 200664562Sgshapiro new_gid != getgid() && 200764562Sgshapiro new_gid != getegid()) 200864562Sgshapiro { 200964562Sgshapiro /* Only root can change the gid */ 201064562Sgshapiro syserr("openmailer: insufficient privileges to change gid"); 201164562Sgshapiro exit(EX_TEMPFAIL); 201264562Sgshapiro } 201338032Speter 201464562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 201564562Sgshapiro { 201664562Sgshapiro syserr("openmailer: setgid(%ld) failed", 201764562Sgshapiro (long) new_gid); 201864562Sgshapiro exit(EX_TEMPFAIL); 201964562Sgshapiro } 202064562Sgshapiro } 202164562Sgshapiro 202264562Sgshapiro /* change root to some "safe" directory */ 202364562Sgshapiro if (m->m_rootdir != NULL) 202464562Sgshapiro { 202564562Sgshapiro expand(m->m_rootdir, buf, sizeof buf, e); 202664562Sgshapiro if (tTd(11, 20)) 202764562Sgshapiro dprintf("openmailer: chroot %s\n", 202864562Sgshapiro buf); 202964562Sgshapiro if (chroot(buf) < 0) 203064562Sgshapiro { 203164562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 203264562Sgshapiro buf); 203364562Sgshapiro exit(EX_TEMPFAIL); 203464562Sgshapiro } 203564562Sgshapiro if (chdir("/") < 0) 203664562Sgshapiro { 203764562Sgshapiro syserr("openmailer: cannot chdir(/)"); 203864562Sgshapiro exit(EX_TEMPFAIL); 203964562Sgshapiro } 204064562Sgshapiro } 204164562Sgshapiro 204238032Speter /* reset user id */ 204338032Speter endpwent(); 204438032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 204538032Speter new_euid = m->m_uid; 204638032Speter else if (bitset(S_ISUID, stb.st_mode)) 204738032Speter new_ruid = stb.st_uid; 204838032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 204938032Speter new_ruid = ctladdr->q_uid; 205038032Speter else if (m->m_uid != 0) 205138032Speter new_ruid = m->m_uid; 205238032Speter else 205338032Speter new_ruid = DefUid; 205438032Speter if (new_euid != NO_UID) 205538032Speter { 205664562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 205764562Sgshapiro { 205864562Sgshapiro /* Only root can change the uid */ 205964562Sgshapiro syserr("openmailer: insufficient privileges to change uid"); 206064562Sgshapiro exit(EX_TEMPFAIL); 206164562Sgshapiro } 206264562Sgshapiro 206338032Speter vendor_set_uid(new_euid); 206464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 206538032Speter if (seteuid(new_euid) < 0 && suidwarn) 206664562Sgshapiro { 206738032Speter syserr("openmailer: seteuid(%ld) failed", 206838032Speter (long) new_euid); 206964562Sgshapiro exit(EX_TEMPFAIL); 207064562Sgshapiro } 207164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 207264562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 207338032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 207464562Sgshapiro { 207538032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 207638032Speter (long) new_ruid, (long) new_euid); 207764562Sgshapiro exit(EX_TEMPFAIL); 207864562Sgshapiro } 207964562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 208064562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID 208138032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 208264562Sgshapiro { 208338032Speter syserr("openmailer: setuid(%ld) failed", 208438032Speter (long) new_euid); 208564562Sgshapiro exit(EX_TEMPFAIL); 208664562Sgshapiro } 208764562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */ 208838032Speter } 208938032Speter else if (new_ruid != NO_UID) 209038032Speter { 209138032Speter vendor_set_uid(new_ruid); 209238032Speter if (setuid(new_ruid) < 0 && suidwarn) 209364562Sgshapiro { 209438032Speter syserr("openmailer: setuid(%ld) failed", 209538032Speter (long) new_ruid); 209664562Sgshapiro exit(EX_TEMPFAIL); 209764562Sgshapiro } 209838032Speter } 209938032Speter 210038032Speter if (tTd(11, 2)) 210164562Sgshapiro dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 210264562Sgshapiro (int) getuid(), (int) geteuid(), 210364562Sgshapiro (int) getgid(), (int) getegid()); 210438032Speter 210538032Speter /* move into some "safe" directory */ 210638032Speter if (m->m_execdir != NULL) 210738032Speter { 210838032Speter char *q; 210938032Speter 211038032Speter for (p = m->m_execdir; p != NULL; p = q) 211138032Speter { 211238032Speter q = strchr(p, ':'); 211338032Speter if (q != NULL) 211438032Speter *q = '\0'; 211538032Speter expand(p, buf, sizeof buf, e); 211638032Speter if (q != NULL) 211738032Speter *q++ = ':'; 211838032Speter if (tTd(11, 20)) 211964562Sgshapiro dprintf("openmailer: trydir %s\n", 212038032Speter buf); 212138032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 212238032Speter break; 212338032Speter } 212438032Speter } 212538032Speter 212638032Speter /* arrange to filter std & diag output of command */ 212764562Sgshapiro (void) close(rpvect[0]); 212864562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 212938032Speter { 213064562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 213164562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 213264562Sgshapiro m->m_name, rpvect[1]); 213364562Sgshapiro _exit(EX_OSERR); 213438032Speter } 213564562Sgshapiro (void) close(rpvect[1]); 213664562Sgshapiro 213738032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 213838032Speter { 213938032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 214038032Speter shortenstring(e->e_to, MAXSHORTSTR), 214138032Speter m->m_name); 214238032Speter _exit(EX_OSERR); 214338032Speter } 214438032Speter 214538032Speter /* arrange to get standard input */ 214638032Speter (void) close(mpvect[1]); 214738032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 214838032Speter { 214938032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 215038032Speter shortenstring(e->e_to, MAXSHORTSTR), 215138032Speter m->m_name, mpvect[0]); 215238032Speter _exit(EX_OSERR); 215338032Speter } 215438032Speter (void) close(mpvect[0]); 215538032Speter 215638032Speter /* arrange for all the files to be closed */ 215738032Speter for (i = 3; i < DtableSize; i++) 215838032Speter { 215938032Speter register int j; 216038032Speter 216138032Speter if ((j = fcntl(i, F_GETFD, 0)) != -1) 216264562Sgshapiro (void) fcntl(i, F_SETFD, 216364562Sgshapiro j | FD_CLOEXEC); 216438032Speter } 216538032Speter 216638032Speter /* run disconnected from terminal */ 216738032Speter (void) setsid(); 216838032Speter 216938032Speter /* try to execute the mailer */ 217064562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 217164562Sgshapiro (ARGV_T) UserEnviron); 217264562Sgshapiro save_errno = errno; 217338032Speter syserr("Cannot exec %s", m->m_mailer); 217438032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 217564562Sgshapiro transienterror(save_errno)) 217638032Speter _exit(EX_OSERR); 217738032Speter _exit(EX_UNAVAILABLE); 217838032Speter } 217938032Speter 218038032Speter /* 218138032Speter ** Set up return value. 218238032Speter */ 218338032Speter 218438032Speter if (mci == NULL) 218538032Speter { 218638032Speter mci = (MCI *) xalloc(sizeof *mci); 218764562Sgshapiro memset((char *) mci, '\0', sizeof *mci); 218838032Speter } 218938032Speter mci->mci_mailer = m; 219038032Speter if (clever) 219138032Speter { 219238032Speter mci->mci_state = MCIS_OPENING; 219338032Speter mci_cache(mci); 219438032Speter } 219538032Speter else 219638032Speter { 219738032Speter mci->mci_state = MCIS_OPEN; 219838032Speter } 219938032Speter mci->mci_pid = pid; 220038032Speter (void) close(mpvect[0]); 220138032Speter mci->mci_out = fdopen(mpvect[1], "w"); 220238032Speter if (mci->mci_out == NULL) 220338032Speter { 220438032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 220538032Speter mpvect[1]); 220638032Speter (void) close(mpvect[1]); 220764562Sgshapiro (void) close(rpvect[0]); 220864562Sgshapiro (void) close(rpvect[1]); 220938032Speter rcode = EX_OSERR; 221038032Speter goto give_up; 221138032Speter } 221264562Sgshapiro 221364562Sgshapiro (void) close(rpvect[1]); 221464562Sgshapiro mci->mci_in = fdopen(rpvect[0], "r"); 221564562Sgshapiro if (mci->mci_in == NULL) 221638032Speter { 221764562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 221864562Sgshapiro mpvect[1]); 221964562Sgshapiro (void) close(rpvect[0]); 222064562Sgshapiro (void) fclose(mci->mci_out); 222164562Sgshapiro mci->mci_out = NULL; 222264562Sgshapiro rcode = EX_OSERR; 222364562Sgshapiro goto give_up; 222438032Speter } 222564562Sgshapiro 222664562Sgshapiro /* Don't cache non-clever connections */ 222764562Sgshapiro if (!clever) 222838032Speter mci->mci_flags |= MCIF_TEMP; 222938032Speter } 223038032Speter 223138032Speter /* 223238032Speter ** If we are in SMTP opening state, send initial protocol. 223338032Speter */ 223438032Speter 223538032Speter if (bitnset(M_7BITS, m->m_flags) && 223638032Speter (!clever || mci->mci_state == MCIS_OPENING)) 223738032Speter mci->mci_flags |= MCIF_7BIT; 223838032Speter#if SMTP 223938032Speter if (clever && mci->mci_state != MCIS_CLOSED) 224038032Speter { 224164562Sgshapiro static u_short again; 224264562Sgshapiro# if SASL && SFIO 224364562Sgshapiro# define DONE_TLS_B 0x01 224464562Sgshapiro# define DONE_TLS bitset(DONE_TLS_B, again) 224564562Sgshapiro# endif /* SASL && SFIO */ 224664562Sgshapiro# if STARTTLS 224764562Sgshapiro# define DONE_STARTTLS_B 0x02 224864562Sgshapiro# define DONE_STARTTLS bitset(DONE_STARTTLS_B, again) 224964562Sgshapiro# endif /* STARTTLS */ 225064562Sgshapiro# define ONLY_HELO_B 0x04 225164562Sgshapiro# define ONLY_HELO bitset(ONLY_HELO_B, again) 225264562Sgshapiro# define SET_HELO again |= ONLY_HELO_B 225364562Sgshapiro# define CLR_HELO again &= ~ONLY_HELO_B 225438032Speter 225564562Sgshapiro again = 0; 225664562Sgshapiro# if STARTTLS || (SASL && SFIO) 225764562Sgshapiroreconnect: /* after switching to an authenticated connection */ 225864562Sgshapiro# endif /* STARTTLS || (SASL && SFIO) */ 225964562Sgshapiro 226064562Sgshapiro# if SASL 226164562Sgshapiro mci->mci_saslcap = NULL; 226264562Sgshapiro# endif /* SASL */ 226364562Sgshapiro smtpinit(m, mci, e, ONLY_HELO); 226464562Sgshapiro CLR_HELO; 226564562Sgshapiro 226664562Sgshapiro# if STARTTLS 226764562Sgshapiro /* first TLS then AUTH to provide a security layer */ 226864562Sgshapiro if (mci->mci_state != MCIS_CLOSED && !DONE_STARTTLS) 226964562Sgshapiro { 227064562Sgshapiro int olderrors; 227164562Sgshapiro bool hasdot; 227264562Sgshapiro bool usetls; 227364562Sgshapiro bool saveQuickAbort = QuickAbort; 227464562Sgshapiro bool saveSuprErrs = SuprErrs; 227566494Sgshapiro# if _FFR_TLS_CLT1 227666494Sgshapiro char *p; 227766494Sgshapiro# endif /* _FFR_TLS_CLT1 */ 227864562Sgshapiro extern SOCKADDR CurHostAddr; 227964562Sgshapiro 228064562Sgshapiro rcode = EX_OK; 228164562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 228266494Sgshapiro# if _FFR_TLS_CLT1 228366494Sgshapiro if (usetls && 228466494Sgshapiro (p = macvalue(macid("{client_flags}", NULL), e)) 228566494Sgshapiro != NULL) 228666494Sgshapiro { 228766494Sgshapiro for (; *p != '\0'; p++) 228866494Sgshapiro { 228966494Sgshapiro /* look for just this one flag */ 229066494Sgshapiro if (*p == D_CLTNOTLS) 229166494Sgshapiro { 229266494Sgshapiro usetls = FALSE; 229366494Sgshapiro break; 229466494Sgshapiro } 229566494Sgshapiro } 229666494Sgshapiro } 229766494Sgshapiro# endif /* _FFR_TLS_CLT1 */ 229866494Sgshapiro 229964562Sgshapiro hasdot = CurHostName[strlen(CurHostName) - 1] == '.'; 230064562Sgshapiro if (hasdot) 230164562Sgshapiro CurHostName[strlen(CurHostName) - 1] = '\0'; 230264562Sgshapiro define(macid("{server_name}", NULL), 230364562Sgshapiro newstr(CurHostName), e); 230464562Sgshapiro if (CurHostAddr.sa.sa_family != 0) 230564562Sgshapiro define(macid("{server_addr}", NULL), 230664562Sgshapiro newstr(anynet_ntoa(&CurHostAddr)), e); 230764562Sgshapiro else 230864562Sgshapiro define(macid("{server_addr}", NULL), NULL, e); 230964562Sgshapiro# if _FFR_TLS_O_T 231064562Sgshapiro if (usetls) 231164562Sgshapiro { 231264562Sgshapiro olderrors = Errors; 231364562Sgshapiro QuickAbort = FALSE; 231464562Sgshapiro SuprErrs = TRUE; 231564562Sgshapiro if (rscheck("try_tls", CurHostName, NULL, 231664562Sgshapiro e, TRUE, FALSE, 8) != EX_OK 231764562Sgshapiro || Errors > olderrors) 231864562Sgshapiro usetls = FALSE; 231964562Sgshapiro SuprErrs = saveSuprErrs; 232064562Sgshapiro QuickAbort = saveQuickAbort; 232164562Sgshapiro } 232264562Sgshapiro# endif /* _FFR_TLS_O_T */ 232364562Sgshapiro 232464562Sgshapiro /* undo change of CurHostName */ 232564562Sgshapiro if (hasdot) 232664562Sgshapiro CurHostName[strlen(CurHostName)] = '.'; 232764562Sgshapiro if (usetls) 232864562Sgshapiro { 232964562Sgshapiro if ((rcode = starttls(m, mci, e)) == EX_OK) 233064562Sgshapiro { 233164562Sgshapiro /* start again without STARTTLS */ 233264562Sgshapiro again |= DONE_STARTTLS_B; 233364562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 233464562Sgshapiro } 233564562Sgshapiro else 233664562Sgshapiro { 233764562Sgshapiro char *s; 233864562Sgshapiro 233964562Sgshapiro /* 234064562Sgshapiro ** TLS negotation failed, what to do? 234164562Sgshapiro ** fall back to unencrypted connection 234264562Sgshapiro ** or abort? How to decide? 234364562Sgshapiro ** set a macro and call a ruleset. 234464562Sgshapiro */ 234564562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 234664562Sgshapiro switch (rcode) 234764562Sgshapiro { 234864562Sgshapiro case EX_TEMPFAIL: 234964562Sgshapiro s = "TEMP"; 235064562Sgshapiro break; 235164562Sgshapiro case EX_USAGE: 235264562Sgshapiro s = "USAGE"; 235364562Sgshapiro break; 235464562Sgshapiro case EX_PROTOCOL: 235564562Sgshapiro s = "PROTOCOL"; 235664562Sgshapiro break; 235764562Sgshapiro case EX_SOFTWARE: 235864562Sgshapiro s = "SOFTWARE"; 235964562Sgshapiro break; 236064562Sgshapiro 236164562Sgshapiro /* everything else is a failure */ 236264562Sgshapiro default: 236364562Sgshapiro s = "FAILURE"; 236464562Sgshapiro rcode = EX_TEMPFAIL; 236564562Sgshapiro } 236664562Sgshapiro define(macid("{verify}", NULL), 236764562Sgshapiro newstr(s), e); 236864562Sgshapiro } 236964562Sgshapiro } 237064562Sgshapiro else 237164562Sgshapiro define(macid("{verify}", NULL), "NONE", e); 237264562Sgshapiro olderrors = Errors; 237364562Sgshapiro QuickAbort = FALSE; 237464562Sgshapiro SuprErrs = TRUE; 237564562Sgshapiro 237664562Sgshapiro /* 237764562Sgshapiro ** rcode == EX_SOFTWARE is special: 237864562Sgshapiro ** the TLS negotation failed 237964562Sgshapiro ** we have to drop the connection no matter what 238064562Sgshapiro ** However, we call tls_server to give it the chance 238164562Sgshapiro ** to log the problem and return an appropriate 238264562Sgshapiro ** error code. 238364562Sgshapiro */ 238464562Sgshapiro if (rscheck("tls_server", 238564562Sgshapiro macvalue(macid("{verify}", NULL), e), 238664562Sgshapiro NULL, e, TRUE, TRUE, 6) != EX_OK || 238764562Sgshapiro Errors > olderrors || 238864562Sgshapiro rcode == EX_SOFTWARE) 238964562Sgshapiro { 239064562Sgshapiro char enhsc[ENHSCLEN]; 239164562Sgshapiro extern char MsgBuf[]; 239264562Sgshapiro 239364562Sgshapiro if (ISSMTPCODE(MsgBuf) && 239464562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 239564562Sgshapiro { 239664562Sgshapiro p = newstr(MsgBuf); 239764562Sgshapiro } 239864562Sgshapiro else 239964562Sgshapiro { 240064562Sgshapiro p = "403 4.7.0 server not authenticated."; 240164562Sgshapiro (void) strlcpy(enhsc, "4.7.0", 240264562Sgshapiro sizeof enhsc); 240364562Sgshapiro } 240464562Sgshapiro SuprErrs = saveSuprErrs; 240564562Sgshapiro QuickAbort = saveQuickAbort; 240664562Sgshapiro 240764562Sgshapiro if (rcode == EX_SOFTWARE) 240864562Sgshapiro { 240964562Sgshapiro /* drop the connection */ 241064562Sgshapiro mci->mci_state = MCIS_QUITING; 241164562Sgshapiro if (mci->mci_in != NULL) 241264562Sgshapiro { 241364562Sgshapiro (void) fclose(mci->mci_in); 241464562Sgshapiro mci->mci_in = NULL; 241564562Sgshapiro } 241664562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 241764562Sgshapiro (void) endmailer(mci, e, pv); 241864562Sgshapiro } 241964562Sgshapiro else 242064562Sgshapiro { 242164562Sgshapiro /* abort transfer */ 242264562Sgshapiro smtpquit(m, mci, e); 242364562Sgshapiro } 242464562Sgshapiro 242564562Sgshapiro /* temp or permanent failure? */ 242664562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 242764562Sgshapiro : EX_UNAVAILABLE; 242864562Sgshapiro mci_setstat(mci, rcode, newstr(enhsc), p); 242964562Sgshapiro 243064562Sgshapiro /* 243164562Sgshapiro ** hack to get the error message into 243264562Sgshapiro ** the envelope (done in giveresponse()) 243364562Sgshapiro */ 243464562Sgshapiro (void) strlcpy(SmtpError, p, sizeof SmtpError); 243564562Sgshapiro } 243664562Sgshapiro QuickAbort = saveQuickAbort; 243764562Sgshapiro SuprErrs = saveSuprErrs; 243864562Sgshapiro if (DONE_STARTTLS && mci->mci_state != MCIS_CLOSED) 243964562Sgshapiro { 244064562Sgshapiro SET_HELO; 244164562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 244264562Sgshapiro goto reconnect; 244364562Sgshapiro } 244464562Sgshapiro } 244564562Sgshapiro# endif /* STARTTLS */ 244664562Sgshapiro# if SASL 244764562Sgshapiro /* if other server supports authentication let's authenticate */ 244864562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 244964562Sgshapiro mci->mci_saslcap != NULL && 245064562Sgshapiro# if SFIO 245164562Sgshapiro !DONE_TLS && 245264562Sgshapiro# endif /* SFIO */ 245364562Sgshapiro SASLInfo != NULL) 245464562Sgshapiro { 245564562Sgshapiro /* 245664562Sgshapiro ** should we require some minimum authentication? 245764562Sgshapiro ** XXX ignore result? 245864562Sgshapiro */ 245964562Sgshapiro if (smtpauth(m, mci, e) == EX_OK) 246064562Sgshapiro { 246164562Sgshapiro# if SFIO 246264562Sgshapiro int result; 246364562Sgshapiro sasl_ssf_t *ssf; 246464562Sgshapiro 246564562Sgshapiro /* get security strength (features) */ 246664562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 246764562Sgshapiro (void **) &ssf); 246864562Sgshapiro if (LogLevel > 9) 246964562Sgshapiro sm_syslog(LOG_INFO, NOQID, 247064562Sgshapiro "SASL: outgoing connection to %.64s: mech=%.16s, bits=%d", 247164562Sgshapiro mci->mci_host, 247264562Sgshapiro macvalue(macid("{auth_type}", 247364562Sgshapiro NULL), e), 247464562Sgshapiro *ssf); 247564562Sgshapiro /* 247664562Sgshapiro ** only switch to encrypted connection 247764562Sgshapiro ** if a security layer has been negotiated 247864562Sgshapiro */ 247964562Sgshapiro if (result == SASL_OK && *ssf > 0) 248064562Sgshapiro { 248164562Sgshapiro /* 248264562Sgshapiro ** convert sfio stuff to use SASL 248364562Sgshapiro ** check return values 248464562Sgshapiro ** if the call fails, 248564562Sgshapiro ** fall back to unencrypted version 248664562Sgshapiro ** unless some cf option requires 248764562Sgshapiro ** encryption then the connection must 248864562Sgshapiro ** be aborted 248964562Sgshapiro */ 249064562Sgshapiro if (sfdcsasl(mci->mci_in, mci->mci_out, 249164562Sgshapiro mci->mci_conn) == 0) 249264562Sgshapiro { 249364562Sgshapiro again |= DONE_TLS_B; 249464562Sgshapiro SET_HELO; 249564562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 249664562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 249764562Sgshapiro goto reconnect; 249864562Sgshapiro } 249964562Sgshapiro syserr("SASL TLS switch failed in client"); 250064562Sgshapiro } 250164562Sgshapiro /* else? XXX */ 250264562Sgshapiro# endif /* SFIO */ 250364562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 250464562Sgshapiro 250564562Sgshapiro } 250664562Sgshapiro } 250764562Sgshapiro# endif /* SASL */ 250838032Speter } 250938032Speter 251064562Sgshapiro#endif /* SMTP */ 251164562Sgshapiro 251238032Speterdo_transfer: 251338032Speter /* clear out per-message flags from connection structure */ 251438032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 251538032Speter 251638032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 251738032Speter !bitset(EF_DONT_MIME, e->e_flags) && 251838032Speter bitnset(M_7BITS, m->m_flags)) 251938032Speter mci->mci_flags |= MCIF_CVT8TO7; 252038032Speter 252138032Speter#if MIME7TO8 252238032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 252338032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 252438032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 252538032Speter (strcasecmp(p, "quoted-printable") == 0 || 252638032Speter strcasecmp(p, "base64") == 0) && 252738032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 252838032Speter { 252938032Speter /* may want to convert 7 -> 8 */ 253038032Speter /* XXX should really parse it here -- and use a class XXX */ 253138032Speter if (strncasecmp(p, "text/plain", 10) == 0 && 253238032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 253338032Speter mci->mci_flags |= MCIF_CVT7TO8; 253438032Speter } 253564562Sgshapiro#endif /* MIME7TO8 */ 253638032Speter 253738032Speter if (tTd(11, 1)) 253838032Speter { 253964562Sgshapiro dprintf("openmailer: "); 254038032Speter mci_dump(mci, FALSE); 254138032Speter } 254238032Speter 254338032Speter if (mci->mci_state != MCIS_OPEN) 254438032Speter { 254538032Speter /* couldn't open the mailer */ 254638032Speter rcode = mci->mci_exitstat; 254738032Speter errno = mci->mci_errno; 254838032Speter#if NAMED_BIND 254938032Speter h_errno = mci->mci_herrno; 255064562Sgshapiro#endif /* NAMED_BIND */ 255138032Speter if (rcode == EX_OK) 255238032Speter { 255338032Speter /* shouldn't happen */ 255464562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 255564562Sgshapiro (u_long) mci, rcode, errno, mci->mci_state, 255638032Speter firstsig); 255738032Speter mci_dump_all(TRUE); 255838032Speter rcode = EX_SOFTWARE; 255938032Speter } 256038032Speter#if DAEMON 256164562Sgshapiro else if (nummxhosts > hostnum) 256238032Speter { 256338032Speter /* try next MX site */ 256438032Speter goto tryhost; 256538032Speter } 256664562Sgshapiro#endif /* DAEMON */ 256738032Speter } 256838032Speter else if (!clever) 256938032Speter { 257038032Speter /* 257138032Speter ** Format and send message. 257238032Speter */ 257338032Speter 257438032Speter putfromline(mci, e); 257543730Speter (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 257638032Speter (*e->e_putbody)(mci, e, NULL); 257738032Speter 257838032Speter /* get the exit status */ 257938032Speter rcode = endmailer(mci, e, pv); 258038032Speter } 258138032Speter else 258238032Speter#if SMTP 258338032Speter { 258438032Speter /* 258538032Speter ** Send the MAIL FROM: protocol 258638032Speter */ 258738032Speter 258838032Speter rcode = smtpmailfrom(m, mci, e); 258938032Speter if (rcode == EX_OK) 259038032Speter { 259138032Speter register char *t = tobuf; 259238032Speter register int i; 259338032Speter 259438032Speter /* send the recipient list */ 259538032Speter tobuf[0] = '\0'; 259664562Sgshapiro 259738032Speter for (to = tochain; to != NULL; to = to->q_tchain) 259838032Speter { 259938032Speter e->e_to = to->q_paddr; 260064562Sgshapiro#if !_FFR_DYNAMIC_TOBUF 260164562Sgshapiro if (strlen(to->q_paddr) + 260264562Sgshapiro (t - tobuf) + 2 > sizeof tobuf) 260338032Speter { 260438032Speter /* not enough room */ 260538032Speter continue; 260638032Speter } 260764562Sgshapiro#endif /* !_FFR_DYNAMIC_TOBUF */ 260864562Sgshapiro 260964562Sgshapiro# if STARTTLS 261064562Sgshapiro# if _FFR_TLS_RCPT 261164562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 261264562Sgshapiro TRUE, TRUE, 4); 261364562Sgshapiro if (i != EX_OK) 261438032Speter { 261564562Sgshapiro markfailure(e, to, mci, i, FALSE); 261664562Sgshapiro giveresponse(i, to->q_status, m, 261764562Sgshapiro mci, ctladdr, xstart, e); 261864562Sgshapiro continue; 261938032Speter } 262064562Sgshapiro# endif /* _FFR_TLS_RCPT */ 262164562Sgshapiro# endif /* STARTTLS */ 262264562Sgshapiro 262364562Sgshapiro if ((i = smtprcpt(to, m, mci, e)) != EX_OK) 262464562Sgshapiro { 262564562Sgshapiro markfailure(e, to, mci, i, FALSE); 262664562Sgshapiro giveresponse(i, to->q_status, m, 262764562Sgshapiro mci, ctladdr, xstart, e); 262864562Sgshapiro } 262938032Speter else 263038032Speter { 263138032Speter *t++ = ','; 263238032Speter for (p = to->q_paddr; *p; *t++ = *p++) 263338032Speter continue; 263438032Speter *t = '\0'; 263538032Speter } 263638032Speter } 263738032Speter 263838032Speter /* now send the data */ 263938032Speter if (tobuf[0] == '\0') 264038032Speter { 264138032Speter rcode = EX_OK; 264238032Speter e->e_to = NULL; 264338032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 264438032Speter smtprset(m, mci, e); 264538032Speter } 264638032Speter else 264738032Speter { 264838032Speter e->e_to = tobuf + 1; 264938032Speter rcode = smtpdata(m, mci, e); 265038032Speter } 265138032Speter } 265238032Speter# if DAEMON 265364562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 265438032Speter { 265538032Speter /* try next MX site */ 265638032Speter goto tryhost; 265738032Speter } 265864562Sgshapiro# endif /* DAEMON */ 265938032Speter } 266064562Sgshapiro#else /* SMTP */ 266138032Speter { 266264562Sgshapiro syserr("554 5.3.5 deliver: need SMTP compiled to use clever mailer"); 266338032Speter rcode = EX_CONFIG; 266438032Speter goto give_up; 266538032Speter } 266638032Speter#endif /* SMTP */ 266738032Speter#if NAMED_BIND 266838032Speter if (ConfigLevel < 2) 266938032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 267064562Sgshapiro#endif /* NAMED_BIND */ 267138032Speter 267238032Speter if (tTd(62, 1)) 267338032Speter checkfds("after delivery"); 267438032Speter 267538032Speter /* 267638032Speter ** Do final status disposal. 267738032Speter ** We check for something in tobuf for the SMTP case. 267838032Speter ** If we got a temporary failure, arrange to queue the 267938032Speter ** addressees. 268038032Speter */ 268138032Speter 268238032Speter give_up: 268338032Speter#if SMTP 268438032Speter if (bitnset(M_LMTP, m->m_flags)) 268538032Speter { 268638032Speter lmtp_rcode = rcode; 268738032Speter tobuf[0] = '\0'; 268838032Speter anyok = FALSE; 268938032Speter } 269038032Speter else 269164562Sgshapiro#endif /* SMTP */ 269238032Speter anyok = rcode == EX_OK; 269338032Speter 269438032Speter for (to = tochain; to != NULL; to = to->q_tchain) 269538032Speter { 269638032Speter /* see if address already marked */ 269764562Sgshapiro if (!QS_IS_OK(to->q_state)) 269838032Speter continue; 269938032Speter 270038032Speter#if SMTP 270138032Speter /* if running LMTP, get the status for each address */ 270238032Speter if (bitnset(M_LMTP, m->m_flags)) 270338032Speter { 270438032Speter if (lmtp_rcode == EX_OK) 270538032Speter rcode = smtpgetstat(m, mci, e); 270638032Speter if (rcode == EX_OK) 270738032Speter { 270866494Sgshapiro#if _FFR_DYNAMIC_TOBUF 270966494Sgshapiro (void) strlcat(tobuf, ",", tobufsize); 271066494Sgshapiro (void) strlcat(tobuf, to->q_paddr, tobufsize); 271166494Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 271264562Sgshapiro if (strlen(to->q_paddr) + 271364562Sgshapiro strlen(tobuf) + 2 > sizeof tobuf) 271438032Speter { 271538032Speter syserr("LMTP tobuf overflow"); 271638032Speter } 271738032Speter else 271838032Speter { 271964562Sgshapiro (void) strlcat(tobuf, ",", 272064562Sgshapiro sizeof tobuf); 272164562Sgshapiro (void) strlcat(tobuf, to->q_paddr, 272264562Sgshapiro sizeof tobuf); 272338032Speter } 272466494Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 272538032Speter anyok = TRUE; 272638032Speter } 272738032Speter else 272838032Speter { 272938032Speter e->e_to = to->q_paddr; 273064562Sgshapiro markfailure(e, to, mci, rcode, TRUE); 273164562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 273264562Sgshapiro ctladdr, xstart, e); 273338032Speter e->e_to = tobuf + 1; 273438032Speter continue; 273538032Speter } 273638032Speter } 273738032Speter else 273864562Sgshapiro#endif /* SMTP */ 273938032Speter { 274038032Speter /* mark bad addresses */ 274138032Speter if (rcode != EX_OK) 274238032Speter { 274338032Speter if (goodmxfound && rcode == EX_NOHOST) 274438032Speter rcode = EX_TEMPFAIL; 274564562Sgshapiro markfailure(e, to, mci, rcode, TRUE); 274638032Speter continue; 274738032Speter } 274838032Speter } 274938032Speter 275038032Speter /* successful delivery */ 275164562Sgshapiro to->q_state = QS_SENT; 275238032Speter to->q_statdate = curtime(); 275338032Speter e->e_nsent++; 275464562Sgshapiro 275564562Sgshapiro#if QUEUE 275664562Sgshapiro /* 275764562Sgshapiro ** Checkpoint the send list every few addresses 275864562Sgshapiro */ 275964562Sgshapiro 276066494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 276164562Sgshapiro { 276264562Sgshapiro queueup(e, FALSE); 276364562Sgshapiro e->e_nsent = 0; 276464562Sgshapiro } 276564562Sgshapiro#endif /* QUEUE */ 276664562Sgshapiro 276738032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 276838032Speter bitset(QPINGONSUCCESS, to->q_flags)) 276938032Speter { 277038032Speter to->q_flags |= QDELIVERED; 277138032Speter to->q_status = "2.1.5"; 277238032Speter fprintf(e->e_xfp, "%s... Successfully delivered\n", 277338032Speter to->q_paddr); 277438032Speter } 277538032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 277638032Speter bitset(QPRIMARY, to->q_flags) && 277738032Speter !bitset(MCIF_DSN, mci->mci_flags)) 277838032Speter { 277938032Speter to->q_flags |= QRELAYED; 278038032Speter fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n", 278138032Speter to->q_paddr); 278238032Speter } 278338032Speter } 278438032Speter 278538032Speter#if SMTP 278638032Speter if (bitnset(M_LMTP, m->m_flags)) 278738032Speter { 278838032Speter /* 278938032Speter ** Global information applies to the last recipient only; 279038032Speter ** clear it out to avoid bogus errors. 279138032Speter */ 279238032Speter 279338032Speter rcode = EX_OK; 279438032Speter e->e_statmsg = NULL; 279538032Speter 279638032Speter /* reset the mci state for the next transaction */ 279738032Speter if (mci != NULL && mci->mci_state == MCIS_ACTIVE) 279838032Speter mci->mci_state = MCIS_OPEN; 279938032Speter } 280064562Sgshapiro#endif /* SMTP */ 280138032Speter 280238032Speter if (tobuf[0] != '\0') 280364562Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e); 280438032Speter if (anyok) 280538032Speter markstats(e, tochain, FALSE); 280638032Speter mci_store_persistent(mci); 280738032Speter 280838032Speter#if SMTP 280938032Speter /* now close the connection */ 281038032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 281138032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 281238032Speter smtpquit(m, mci, e); 281364562Sgshapiro#endif /* SMTP */ 281438032Speter 281538032Speter /* 281638032Speter ** Restore state and return. 281738032Speter */ 281838032Speter 281938032Speter#if XDEBUG 282038032Speter { 282138032Speter char wbuf[MAXLINE]; 282238032Speter 282338032Speter /* make absolutely certain 0, 1, and 2 are in use */ 282438032Speter snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)", 282538032Speter e->e_to == NULL ? "NO-TO-LIST" 282638032Speter : shortenstring(e->e_to, MAXSHORTSTR), 282738032Speter m->m_name); 282838032Speter checkfd012(wbuf); 282938032Speter } 283064562Sgshapiro#endif /* XDEBUG */ 283138032Speter 283238032Speter errno = 0; 283338032Speter define('g', (char *) NULL, e); 283464562Sgshapiro e->e_to = NULL; 283564562Sgshapiro return rcode; 283638032Speter} 283764562Sgshapiro 283838032Speter/* 283938032Speter** MARKFAILURE -- mark a failure on a specific address. 284038032Speter** 284138032Speter** Parameters: 284238032Speter** e -- the envelope we are sending. 284338032Speter** q -- the address to mark. 284438032Speter** mci -- mailer connection information. 284538032Speter** rcode -- the code signifying the particular failure. 284664562Sgshapiro** ovr -- override an existing code? 284738032Speter** 284838032Speter** Returns: 284938032Speter** none. 285038032Speter** 285138032Speter** Side Effects: 285238032Speter** marks the address (and possibly the envelope) with the 285338032Speter** failure so that an error will be returned or 285438032Speter** the message will be queued, as appropriate. 285538032Speter*/ 285638032Speter 285764562Sgshapirostatic void 285864562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 285938032Speter register ENVELOPE *e; 286038032Speter register ADDRESS *q; 286138032Speter register MCI *mci; 286238032Speter int rcode; 286364562Sgshapiro bool ovr; 286438032Speter{ 286564562Sgshapiro char *status = NULL; 286664562Sgshapiro char *rstatus = NULL; 286738032Speter 286838032Speter switch (rcode) 286938032Speter { 287038032Speter case EX_OK: 287138032Speter break; 287238032Speter 287338032Speter case EX_TEMPFAIL: 287438032Speter case EX_IOERR: 287538032Speter case EX_OSERR: 287664562Sgshapiro q->q_state = QS_QUEUEUP; 287738032Speter break; 287838032Speter 287938032Speter default: 288064562Sgshapiro q->q_state = QS_BADADDR; 288138032Speter break; 288238032Speter } 288338032Speter 288438032Speter /* find most specific error code possible */ 288538032Speter if (mci != NULL && mci->mci_status != NULL) 288638032Speter { 288764562Sgshapiro status = mci->mci_status; 288838032Speter if (mci->mci_rstatus != NULL) 288964562Sgshapiro rstatus = newstr(mci->mci_rstatus); 289038032Speter else 289164562Sgshapiro rstatus = NULL; 289238032Speter } 289338032Speter else if (e->e_status != NULL) 289438032Speter { 289564562Sgshapiro status = e->e_status; 289664562Sgshapiro rstatus = NULL; 289738032Speter } 289838032Speter else 289938032Speter { 290038032Speter switch (rcode) 290138032Speter { 290238032Speter case EX_USAGE: 290364562Sgshapiro status = "5.5.4"; 290438032Speter break; 290538032Speter 290638032Speter case EX_DATAERR: 290764562Sgshapiro status = "5.5.2"; 290838032Speter break; 290938032Speter 291038032Speter case EX_NOUSER: 291164562Sgshapiro status = "5.1.1"; 291238032Speter break; 291338032Speter 291438032Speter case EX_NOHOST: 291564562Sgshapiro status = "5.1.2"; 291638032Speter break; 291738032Speter 291838032Speter case EX_NOINPUT: 291938032Speter case EX_CANTCREAT: 292038032Speter case EX_NOPERM: 292164562Sgshapiro status = "5.3.0"; 292238032Speter break; 292338032Speter 292438032Speter case EX_UNAVAILABLE: 292538032Speter case EX_SOFTWARE: 292638032Speter case EX_OSFILE: 292738032Speter case EX_PROTOCOL: 292838032Speter case EX_CONFIG: 292964562Sgshapiro status = "5.5.0"; 293038032Speter break; 293138032Speter 293238032Speter case EX_OSERR: 293338032Speter case EX_IOERR: 293464562Sgshapiro status = "4.5.0"; 293538032Speter break; 293638032Speter 293738032Speter case EX_TEMPFAIL: 293864562Sgshapiro status = "4.2.0"; 293938032Speter break; 294038032Speter } 294138032Speter } 294238032Speter 294364562Sgshapiro /* new status? */ 294464562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 294564562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 294664562Sgshapiro { 294764562Sgshapiro q->q_status = status; 294864562Sgshapiro q->q_rstatus = rstatus; 294964562Sgshapiro } 295038032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 295138032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 295264562Sgshapiro strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 295338032Speter { 295464562Sgshapiro char buf[16]; 295538032Speter 295638032Speter (void) snprintf(buf, sizeof buf, "%d", rcode); 295738032Speter q->q_rstatus = newstr(buf); 295838032Speter } 295964562Sgshapiro 296064562Sgshapiro q->q_statdate = curtime(); 296164562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 296264562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 296364562Sgshapiro q->q_statmta = newstr(CurHostName); 296438032Speter} 296538032Speter/* 296638032Speter** ENDMAILER -- Wait for mailer to terminate. 296738032Speter** 296838032Speter** We should never get fatal errors (e.g., segmentation 296938032Speter** violation), so we report those specially. For other 297038032Speter** errors, we choose a status message (into statmsg), 297138032Speter** and if it represents an error, we print it. 297238032Speter** 297338032Speter** Parameters: 297438032Speter** pid -- pid of mailer. 297538032Speter** e -- the current envelope. 297638032Speter** pv -- the parameter vector that invoked the mailer 297738032Speter** (for error messages). 297838032Speter** 297938032Speter** Returns: 298038032Speter** exit code of mailer. 298138032Speter** 298238032Speter** Side Effects: 298338032Speter** none. 298438032Speter*/ 298538032Speter 298664562Sgshapirostatic jmp_buf EndWaitTimeout; 298764562Sgshapiro 298864562Sgshapirostatic void 298964562Sgshapiroendwaittimeout() 299064562Sgshapiro{ 299164562Sgshapiro errno = ETIMEDOUT; 299264562Sgshapiro longjmp(EndWaitTimeout, 1); 299364562Sgshapiro} 299464562Sgshapiro 299538032Speterint 299638032Speterendmailer(mci, e, pv) 299738032Speter register MCI *mci; 299838032Speter register ENVELOPE *e; 299938032Speter char **pv; 300038032Speter{ 300138032Speter int st; 300264562Sgshapiro int save_errno = errno; 300364562Sgshapiro char buf[MAXLINE]; 300464562Sgshapiro EVENT *ev = NULL; 300538032Speter 300664562Sgshapiro 300738032Speter mci_unlock_host(mci); 300838032Speter 300964562Sgshapiro#if SASL 301064562Sgshapiro /* shutdown SASL */ 301164562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 301264562Sgshapiro { 301364562Sgshapiro sasl_dispose(&mci->mci_conn); 301464562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 301564562Sgshapiro } 301664562Sgshapiro#endif /* SASL */ 301764562Sgshapiro 301864562Sgshapiro#if STARTTLS 301964562Sgshapiro /* shutdown TLS */ 302064562Sgshapiro (void) endtlsclt(mci); 302164562Sgshapiro#endif /* STARTTLS */ 302264562Sgshapiro 302364562Sgshapiro /* close output to mailer */ 302464562Sgshapiro if (mci->mci_out != NULL) 302564562Sgshapiro (void) fclose(mci->mci_out); 302664562Sgshapiro 302764562Sgshapiro /* copy any remaining input to transcript */ 302864562Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 302964562Sgshapiro e->e_xfp != NULL) 303064562Sgshapiro { 303164562Sgshapiro while (sfgets(buf, sizeof buf, mci->mci_in, 303264562Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 303364562Sgshapiro (void) fputs(buf, e->e_xfp); 303464562Sgshapiro } 303564562Sgshapiro 303664562Sgshapiro /* now close the input */ 303738032Speter if (mci->mci_in != NULL) 303864562Sgshapiro (void) fclose(mci->mci_in); 303938032Speter mci->mci_in = mci->mci_out = NULL; 304038032Speter mci->mci_state = MCIS_CLOSED; 304138032Speter 304264562Sgshapiro errno = save_errno; 304364562Sgshapiro 304438032Speter /* in the IPC case there is nothing to wait for */ 304538032Speter if (mci->mci_pid == 0) 304664562Sgshapiro return EX_OK; 304738032Speter 304864562Sgshapiro /* put a timeout around the wait */ 304964562Sgshapiro if (mci->mci_mailer->m_wait > 0) 305064562Sgshapiro { 305164562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 305264562Sgshapiro ev = setevent(mci->mci_mailer->m_wait, 305364562Sgshapiro endwaittimeout, 0); 305464562Sgshapiro else 305564562Sgshapiro { 305666494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 305764562Sgshapiro mci->mci_mailer->m_name, 305866494Sgshapiro (long) mci->mci_mailer->m_wait); 305964562Sgshapiro return EX_TEMPFAIL; 306064562Sgshapiro } 306164562Sgshapiro } 306238032Speter 306364562Sgshapiro /* wait for the mailer process, collect status */ 306438032Speter st = waitfor(mci->mci_pid); 306564562Sgshapiro save_errno = errno; 306664562Sgshapiro if (ev != NULL) 306764562Sgshapiro clrevent(ev); 306864562Sgshapiro errno = save_errno; 306964562Sgshapiro 307038032Speter if (st == -1) 307138032Speter { 307238032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 307364562Sgshapiro return EX_SOFTWARE; 307438032Speter } 307538032Speter 307638032Speter if (WIFEXITED(st)) 307738032Speter { 307838032Speter /* normal death -- return status */ 307938032Speter return (WEXITSTATUS(st)); 308038032Speter } 308138032Speter 308238032Speter /* it died a horrid death */ 308364562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 308464562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 308564562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 308664562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 308738032Speter 308838032Speter /* log the arguments */ 308938032Speter if (pv != NULL && e->e_xfp != NULL) 309038032Speter { 309138032Speter register char **av; 309238032Speter 309338032Speter fprintf(e->e_xfp, "Arguments:"); 309438032Speter for (av = pv; *av != NULL; av++) 309538032Speter fprintf(e->e_xfp, " %s", *av); 309638032Speter fprintf(e->e_xfp, "\n"); 309738032Speter } 309838032Speter 309938032Speter ExitStat = EX_TEMPFAIL; 310064562Sgshapiro return EX_TEMPFAIL; 310138032Speter} 310238032Speter/* 310338032Speter** GIVERESPONSE -- Interpret an error response from a mailer 310438032Speter** 310538032Speter** Parameters: 310664562Sgshapiro** status -- the status code from the mailer (high byte 310738032Speter** only; core dumps must have been taken care of 310838032Speter** already). 310964562Sgshapiro** dsn -- the DSN associated with the address, if any. 311038032Speter** m -- the mailer info for this mailer. 311138032Speter** mci -- the mailer connection info -- can be NULL if the 311238032Speter** response is given before the connection is made. 311338032Speter** ctladdr -- the controlling address for the recipient 311438032Speter** address(es). 311538032Speter** xstart -- the transaction start time, for computing 311638032Speter** transaction delays. 311738032Speter** e -- the current envelope. 311838032Speter** 311938032Speter** Returns: 312038032Speter** none. 312138032Speter** 312238032Speter** Side Effects: 312338032Speter** Errors may be incremented. 312438032Speter** ExitStat may be set. 312538032Speter*/ 312638032Speter 312738032Spetervoid 312864562Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e) 312964562Sgshapiro int status; 313064562Sgshapiro char *dsn; 313138032Speter register MAILER *m; 313238032Speter register MCI *mci; 313338032Speter ADDRESS *ctladdr; 313438032Speter time_t xstart; 313538032Speter ENVELOPE *e; 313638032Speter{ 313738032Speter register const char *statmsg; 313838032Speter extern char *SysExMsg[]; 313938032Speter register int i; 314064562Sgshapiro int errnum = errno; 314164562Sgshapiro int off = 4; 314238032Speter extern int N_SysEx; 314364562Sgshapiro char dsnbuf[ENHSCLEN]; 314438032Speter char buf[MAXLINE]; 314538032Speter 314638032Speter if (e == NULL) 314738032Speter syserr("giveresponse: null envelope"); 314838032Speter 314938032Speter /* 315038032Speter ** Compute status message from code. 315138032Speter */ 315238032Speter 315364562Sgshapiro i = status - EX__BASE; 315464562Sgshapiro if (status == 0) 315538032Speter { 315664562Sgshapiro statmsg = "250 2.0.0 Sent"; 315738032Speter if (e->e_statmsg != NULL) 315838032Speter { 315938032Speter (void) snprintf(buf, sizeof buf, "%s (%s)", 316064562Sgshapiro statmsg, 316164562Sgshapiro shortenstring(e->e_statmsg, 403)); 316238032Speter statmsg = buf; 316338032Speter } 316438032Speter } 316538032Speter else if (i < 0 || i >= N_SysEx) 316638032Speter { 316764562Sgshapiro (void) snprintf(buf, sizeof buf, 316864562Sgshapiro "554 5.3.0 unknown mailer error %d", 316964562Sgshapiro status); 317064562Sgshapiro status = EX_UNAVAILABLE; 317138032Speter statmsg = buf; 317238032Speter } 317364562Sgshapiro else if (status == EX_TEMPFAIL) 317438032Speter { 317538032Speter char *bp = buf; 317638032Speter 317738032Speter snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1); 317838032Speter bp += strlen(bp); 317938032Speter#if NAMED_BIND 318038032Speter if (h_errno == TRY_AGAIN) 318138032Speter statmsg = errstring(h_errno+E_DNSBASE); 318238032Speter else 318364562Sgshapiro#endif /* NAMED_BIND */ 318438032Speter { 318564562Sgshapiro if (errnum != 0) 318664562Sgshapiro statmsg = errstring(errnum); 318738032Speter else 318838032Speter { 318938032Speter#if SMTP 319038032Speter statmsg = SmtpError; 319138032Speter#else /* SMTP */ 319238032Speter statmsg = NULL; 319338032Speter#endif /* SMTP */ 319438032Speter } 319538032Speter } 319638032Speter if (statmsg != NULL && statmsg[0] != '\0') 319764562Sgshapiro { 319864562Sgshapiro switch (errnum) 319964562Sgshapiro { 320064562Sgshapiro#ifdef ENETDOWN 320164562Sgshapiro case ENETDOWN: /* Network is down */ 320264562Sgshapiro#endif /* ENETDOWN */ 320364562Sgshapiro#ifdef ENETUNREACH 320464562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 320564562Sgshapiro#endif /* ENETUNREACH */ 320664562Sgshapiro#ifdef ENETRESET 320764562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 320864562Sgshapiro#endif /* ENETRESET */ 320964562Sgshapiro#ifdef ECONNABORTED 321064562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 321164562Sgshapiro#endif /* ECONNABORTED */ 321264562Sgshapiro#ifdef EHOSTDOWN 321364562Sgshapiro case EHOSTDOWN: /* Host is down */ 321464562Sgshapiro#endif /* EHOSTDOWN */ 321564562Sgshapiro#ifdef EHOSTUNREACH 321664562Sgshapiro case EHOSTUNREACH: /* No route to host */ 321764562Sgshapiro#endif /* EHOSTUNREACH */ 321864562Sgshapiro if (mci->mci_host != NULL) 321964562Sgshapiro { 322064562Sgshapiro snprintf(bp, SPACELEFT(buf, bp), 322164562Sgshapiro ": %s", mci->mci_host); 322264562Sgshapiro bp += strlen(bp); 322364562Sgshapiro } 322464562Sgshapiro break; 322564562Sgshapiro } 322638032Speter snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg); 322764562Sgshapiro } 322838032Speter statmsg = buf; 322938032Speter } 323038032Speter#if NAMED_BIND 323164562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 323238032Speter { 323338032Speter statmsg = errstring(h_errno + E_DNSBASE); 323438032Speter (void) snprintf(buf, sizeof buf, "%s (%s)", 323538032Speter SysExMsg[i] + 1, statmsg); 323638032Speter statmsg = buf; 323738032Speter } 323864562Sgshapiro#endif /* NAMED_BIND */ 323938032Speter else 324038032Speter { 324138032Speter statmsg = SysExMsg[i]; 324264562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 324338032Speter { 324438032Speter (void) snprintf(buf, sizeof buf, "%s: %s", 324564562Sgshapiro statmsg, errstring(errnum)); 324638032Speter statmsg = buf; 324738032Speter } 324838032Speter } 324938032Speter 325038032Speter /* 325138032Speter ** Print the message as appropriate 325238032Speter */ 325338032Speter 325464562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 325538032Speter { 325638032Speter extern char MsgBuf[]; 325738032Speter 325864562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 325964562Sgshapiro { 326064562Sgshapiro if (dsn == NULL) 326164562Sgshapiro { 326264562Sgshapiro snprintf(dsnbuf, sizeof dsnbuf, 326364562Sgshapiro "%.*s", off, statmsg + 4); 326464562Sgshapiro dsn = dsnbuf; 326564562Sgshapiro } 326664562Sgshapiro off += 5; 326764562Sgshapiro } 326864562Sgshapiro else 326964562Sgshapiro { 327064562Sgshapiro off = 4; 327164562Sgshapiro } 327264562Sgshapiro message("%s", statmsg + off); 327364562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 327438032Speter fprintf(e->e_xfp, "%s\n", &MsgBuf[4]); 327538032Speter } 327638032Speter else 327738032Speter { 327864562Sgshapiro char mbuf[ENHSCLEN + 4]; 327938032Speter 328038032Speter Errors++; 328164562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 328264562Sgshapiro off < sizeof mbuf - 4) 328364562Sgshapiro { 328464562Sgshapiro if (dsn == NULL) 328564562Sgshapiro { 328664562Sgshapiro snprintf(dsnbuf, sizeof dsnbuf, 328764562Sgshapiro "%.*s", off, statmsg + 4); 328864562Sgshapiro dsn = dsnbuf; 328964562Sgshapiro } 329064562Sgshapiro off += 5; 329164562Sgshapiro (void) strlcpy(mbuf, statmsg, off); 329264562Sgshapiro (void) strlcat(mbuf, " %s", sizeof mbuf); 329364562Sgshapiro } 329464562Sgshapiro else 329564562Sgshapiro { 329664562Sgshapiro dsnbuf[0] = '\0'; 329764562Sgshapiro (void) snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg); 329864562Sgshapiro off = 4; 329964562Sgshapiro } 330064562Sgshapiro usrerr(mbuf, &statmsg[off]); 330138032Speter } 330238032Speter 330338032Speter /* 330438032Speter ** Final cleanup. 330538032Speter ** Log a record of the transaction. Compute the new 330638032Speter ** ExitStat -- if we already had an error, stick with 330738032Speter ** that. 330838032Speter */ 330938032Speter 331038032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 331164562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 331264562Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 331338032Speter 331438032Speter if (tTd(11, 2)) 331564562Sgshapiro dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s\n", 331664562Sgshapiro status, 331764562Sgshapiro dsn == NULL ? "<NULL>" : dsn, 331864562Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message); 331938032Speter 332064562Sgshapiro if (status != EX_TEMPFAIL) 332164562Sgshapiro setstat(status); 332264562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 332338032Speter { 332438032Speter if (e->e_message != NULL) 332538032Speter free(e->e_message); 332664562Sgshapiro e->e_message = newstr(statmsg + off); 332738032Speter } 332838032Speter errno = 0; 332938032Speter#if NAMED_BIND 333038032Speter h_errno = 0; 333164562Sgshapiro#endif /* NAMED_BIND */ 333238032Speter} 333338032Speter/* 333438032Speter** LOGDELIVERY -- log the delivery in the system log 333538032Speter** 333638032Speter** Care is taken to avoid logging lines that are too long, because 333738032Speter** some versions of syslog have an unfortunate proclivity for core 333838032Speter** dumping. This is a hack, to be sure, that is at best empirical. 333938032Speter** 334038032Speter** Parameters: 334138032Speter** m -- the mailer info. Can be NULL for initial queue. 334238032Speter** mci -- the mailer connection info -- can be NULL if the 334364562Sgshapiro** log is occurring when no connection is active. 334464562Sgshapiro** dsn -- the DSN attached to the status. 334564562Sgshapiro** status -- the message to print for the status. 334638032Speter** ctladdr -- the controlling address for the to list. 334738032Speter** xstart -- the transaction start time, used for 334838032Speter** computing transaction delay. 334938032Speter** e -- the current envelope. 335038032Speter** 335138032Speter** Returns: 335238032Speter** none 335338032Speter** 335438032Speter** Side Effects: 335538032Speter** none 335638032Speter*/ 335738032Speter 335838032Spetervoid 335964562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e) 336038032Speter MAILER *m; 336138032Speter register MCI *mci; 336264562Sgshapiro char *dsn; 336364562Sgshapiro const char *status; 336438032Speter ADDRESS *ctladdr; 336538032Speter time_t xstart; 336638032Speter register ENVELOPE *e; 336738032Speter{ 336838032Speter register char *bp; 336938032Speter register char *p; 337038032Speter int l; 337138032Speter char buf[1024]; 337238032Speter 337364562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 337438032Speter /* ctladdr: max 106 bytes */ 337538032Speter bp = buf; 337638032Speter if (ctladdr != NULL) 337738032Speter { 337838032Speter snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s", 337964562Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 338038032Speter bp += strlen(bp); 338138032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 338238032Speter { 338338032Speter (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 338464562Sgshapiro (int) ctladdr->q_uid, 338564562Sgshapiro (int) ctladdr->q_gid); 338638032Speter bp += strlen(bp); 338738032Speter } 338838032Speter } 338938032Speter 339038032Speter /* delay & xdelay: max 41 bytes */ 339138032Speter snprintf(bp, SPACELEFT(buf, bp), ", delay=%s", 339264562Sgshapiro pintvl(curtime() - e->e_ctime, TRUE)); 339338032Speter bp += strlen(bp); 339438032Speter 339538032Speter if (xstart != (time_t) 0) 339638032Speter { 339738032Speter snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", 339864562Sgshapiro pintvl(curtime() - xstart, TRUE)); 339938032Speter bp += strlen(bp); 340038032Speter } 340138032Speter 340238032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 340338032Speter if (m != NULL) 340438032Speter { 340538032Speter snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); 340638032Speter bp += strlen(bp); 340738032Speter } 340838032Speter 340964562Sgshapiro /* pri: changes with each delivery attempt */ 341064562Sgshapiro snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", e->e_msgpriority); 341164562Sgshapiro bp += strlen(bp); 341264562Sgshapiro 341338032Speter /* relay: max 66 bytes for IPv4 addresses */ 341438032Speter if (mci != NULL && mci->mci_host != NULL) 341538032Speter { 341638032Speter# if DAEMON 341738032Speter extern SOCKADDR CurHostAddr; 341864562Sgshapiro# endif /* DAEMON */ 341938032Speter 342038032Speter snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", 342164562Sgshapiro shortenstring(mci->mci_host, 40)); 342238032Speter bp += strlen(bp); 342338032Speter 342438032Speter# if DAEMON 342538032Speter if (CurHostAddr.sa.sa_family != 0) 342638032Speter { 342738032Speter snprintf(bp, SPACELEFT(buf, bp), " [%s]", 342864562Sgshapiro anynet_ntoa(&CurHostAddr)); 342938032Speter } 343064562Sgshapiro# endif /* DAEMON */ 343138032Speter } 343264562Sgshapiro else if (strcmp(status, "queued") != 0) 343338032Speter { 343438032Speter p = macvalue('h', e); 343538032Speter if (p != NULL && p[0] != '\0') 343638032Speter { 343738032Speter snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", 343864562Sgshapiro shortenstring(p, 40)); 343938032Speter } 344038032Speter } 344138032Speter bp += strlen(bp); 344238032Speter 344364562Sgshapiro /* dsn */ 344464562Sgshapiro if (dsn != NULL && *dsn != '\0') 344564562Sgshapiro { 344664562Sgshapiro snprintf(bp, SPACELEFT(buf, bp), ", dsn=%s", 344764562Sgshapiro shortenstring(dsn, ENHSCLEN)); 344864562Sgshapiro bp += strlen(bp); 344964562Sgshapiro } 345038032Speter 345164562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 345264562Sgshapiro# if (STATLEN) < 63 345364562Sgshapiro# undef STATLEN 345464562Sgshapiro# define STATLEN 63 345564562Sgshapiro# endif /* (STATLEN) < 63 */ 345664562Sgshapiro# if (STATLEN) > 203 345764562Sgshapiro# undef STATLEN 345864562Sgshapiro# define STATLEN 203 345964562Sgshapiro# endif /* (STATLEN) > 203 */ 346064562Sgshapiro 346138032Speter /* stat: max 210 bytes */ 346238032Speter if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) 346338032Speter { 346438032Speter /* desperation move -- truncate data */ 346538032Speter bp = buf + sizeof buf - ((STATLEN) + 17); 346664562Sgshapiro (void) strlcpy(bp, "...", SPACELEFT(buf, bp)); 346738032Speter bp += 3; 346838032Speter } 346938032Speter 347064562Sgshapiro (void) strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 347138032Speter bp += strlen(bp); 347238032Speter 347364562Sgshapiro (void) strlcpy(bp, shortenstring(status, STATLEN), SPACELEFT(buf, bp)); 347438032Speter 347538032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 347638032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 347764562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 347838032Speter while (strlen(p) >= (SIZE_T) l) 347938032Speter { 348064562Sgshapiro register char *q; 348138032Speter 348264562Sgshapiro#if _FFR_DYNAMIC_TOBUF 348364562Sgshapiro for (q = p + l; q > p; q--) 348464562Sgshapiro { 348564562Sgshapiro if (*q == ',') 348664562Sgshapiro break; 348764562Sgshapiro } 348864562Sgshapiro if (p == q) 348964562Sgshapiro break; 349064562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 349164562Sgshapiro q = strchr(p + l, ','); 349238032Speter if (q == NULL) 349338032Speter break; 349464562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 349564562Sgshapiro 349638032Speter sm_syslog(LOG_INFO, e->e_id, 349764562Sgshapiro "to=%.*s [more]%s", 349866494Sgshapiro (int) (++q - p), p, buf); 349938032Speter p = q; 350038032Speter } 350164562Sgshapiro#if _FFR_DYNAMIC_TOBUF 350264562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 350364562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 350438032Speter sm_syslog(LOG_INFO, e->e_id, "to=%s%s", p, buf); 350564562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 350638032Speter 350764562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 350838032Speter 350938032Speter l = SYSLOG_BUFSIZE - 85; 351064562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 351138032Speter while (strlen(p) >= (SIZE_T) l) 351238032Speter { 351364562Sgshapiro register char *q; 351438032Speter 351564562Sgshapiro#if _FFR_DYNAMIC_TOBUF 351664562Sgshapiro for (q = p + l; q > p; q--) 351764562Sgshapiro { 351864562Sgshapiro if (*q == ',') 351964562Sgshapiro break; 352064562Sgshapiro } 352164562Sgshapiro if (p == q) 352264562Sgshapiro break; 352364562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 352464562Sgshapiro q = strchr(p + l, ','); 352538032Speter if (q == NULL) 352638032Speter break; 352764562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 352864562Sgshapiro 352938032Speter sm_syslog(LOG_INFO, e->e_id, 353064562Sgshapiro "to=%.*s [more]", 353166494Sgshapiro (int) (++q - p), p); 353238032Speter p = q; 353338032Speter } 353464562Sgshapiro#if _FFR_DYNAMIC_TOBUF 353564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 353664562Sgshapiro#else /* _FFR_DYNAMIC_TOBUF */ 353738032Speter sm_syslog(LOG_INFO, e->e_id, "to=%s", p); 353864562Sgshapiro#endif /* _FFR_DYNAMIC_TOBUF */ 353938032Speter 354038032Speter if (ctladdr != NULL) 354138032Speter { 354238032Speter bp = buf; 354338032Speter snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s", 354464562Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 354538032Speter bp += strlen(bp); 354638032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 354738032Speter { 354838032Speter (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 354938032Speter ctladdr->q_uid, ctladdr->q_gid); 355038032Speter bp += strlen(bp); 355138032Speter } 355238032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 355338032Speter } 355438032Speter bp = buf; 355538032Speter snprintf(bp, SPACELEFT(buf, bp), "delay=%s", 355664562Sgshapiro pintvl(curtime() - e->e_ctime, TRUE)); 355738032Speter bp += strlen(bp); 355838032Speter if (xstart != (time_t) 0) 355938032Speter { 356038032Speter snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", 356164562Sgshapiro pintvl(curtime() - xstart, TRUE)); 356238032Speter bp += strlen(bp); 356338032Speter } 356438032Speter 356538032Speter if (m != NULL) 356638032Speter { 356738032Speter snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); 356838032Speter bp += strlen(bp); 356938032Speter } 357038032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 357138032Speter 357238032Speter buf[0] = '\0'; 357338032Speter bp = buf; 357438032Speter if (mci != NULL && mci->mci_host != NULL) 357538032Speter { 357638032Speter# if DAEMON 357738032Speter extern SOCKADDR CurHostAddr; 357864562Sgshapiro# endif /* DAEMON */ 357938032Speter 358038032Speter snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host); 358138032Speter bp += strlen(bp); 358238032Speter 358338032Speter# if DAEMON 358438032Speter if (CurHostAddr.sa.sa_family != 0) 358538032Speter snprintf(bp, SPACELEFT(buf, bp), " [%.100s]", 358638032Speter anynet_ntoa(&CurHostAddr)); 358764562Sgshapiro# endif /* DAEMON */ 358838032Speter } 358964562Sgshapiro else if (strcmp(status, "queued") != 0) 359038032Speter { 359138032Speter p = macvalue('h', e); 359238032Speter if (p != NULL && p[0] != '\0') 359338032Speter snprintf(buf, sizeof buf, "relay=%.100s", p); 359438032Speter } 359538032Speter if (buf[0] != '\0') 359638032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 359738032Speter 359864562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 359964562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 360038032Speter} 360138032Speter/* 360238032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 360338032Speter** 360438032Speter** This can be made an arbitrary message separator by changing $l 360538032Speter** 360638032Speter** One of the ugliest hacks seen by human eyes is contained herein: 360738032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 360838032Speter** does a well-meaning programmer such as myself have to deal with 360938032Speter** this kind of antique garbage???? 361038032Speter** 361138032Speter** Parameters: 361238032Speter** mci -- the connection information. 361338032Speter** e -- the envelope. 361438032Speter** 361538032Speter** Returns: 361638032Speter** none 361738032Speter** 361838032Speter** Side Effects: 361938032Speter** outputs some text to fp. 362038032Speter*/ 362138032Speter 362238032Spetervoid 362338032Speterputfromline(mci, e) 362438032Speter register MCI *mci; 362538032Speter ENVELOPE *e; 362638032Speter{ 362738032Speter char *template = UnixFromLine; 362838032Speter char buf[MAXLINE]; 362938032Speter char xbuf[MAXLINE]; 363038032Speter 363138032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 363238032Speter return; 363338032Speter 363438032Speter mci->mci_flags |= MCIF_INHEADER; 363538032Speter 363638032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 363738032Speter { 363838032Speter char *bang; 363938032Speter 364038032Speter expand("\201g", buf, sizeof buf, e); 364138032Speter bang = strchr(buf, '!'); 364238032Speter if (bang == NULL) 364338032Speter { 364438032Speter char *at; 364538032Speter char hname[MAXNAME]; 364638032Speter 364764562Sgshapiro /* 364842575Speter ** If we can construct a UUCP path, do so 364938032Speter */ 365038032Speter 365138032Speter at = strrchr(buf, '@'); 365238032Speter if (at == NULL) 365338032Speter { 365464562Sgshapiro expand("\201k", hname, sizeof hname, e); 365538032Speter at = hname; 365638032Speter } 365738032Speter else 365838032Speter *at++ = '\0'; 365938032Speter (void) snprintf(xbuf, sizeof xbuf, 366038032Speter "From %.800s \201d remote from %.100s\n", 366138032Speter buf, at); 366238032Speter } 366338032Speter else 366438032Speter { 366538032Speter *bang++ = '\0'; 366638032Speter (void) snprintf(xbuf, sizeof xbuf, 366738032Speter "From %.800s \201d remote from %.100s\n", 366838032Speter bang, buf); 366938032Speter template = xbuf; 367038032Speter } 367138032Speter } 367238032Speter expand(template, buf, sizeof buf, e); 367338032Speter putxline(buf, strlen(buf), mci, PXLF_HEADER); 367438032Speter} 367538032Speter/* 367638032Speter** PUTBODY -- put the body of a message. 367738032Speter** 367838032Speter** Parameters: 367938032Speter** mci -- the connection information. 368038032Speter** e -- the envelope to put out. 368138032Speter** separator -- if non-NULL, a message separator that must 368238032Speter** not be permitted in the resulting message. 368338032Speter** 368438032Speter** Returns: 368538032Speter** none. 368638032Speter** 368738032Speter** Side Effects: 368838032Speter** The message is written onto fp. 368938032Speter*/ 369038032Speter 369138032Speter/* values for output state variable */ 369238032Speter#define OS_HEAD 0 /* at beginning of line */ 369338032Speter#define OS_CR 1 /* read a carriage return */ 369438032Speter#define OS_INLINE 2 /* putting rest of line */ 369538032Speter 369638032Spetervoid 369738032Speterputbody(mci, e, separator) 369838032Speter register MCI *mci; 369938032Speter register ENVELOPE *e; 370038032Speter char *separator; 370138032Speter{ 370264562Sgshapiro bool dead = FALSE; 370338032Speter char buf[MAXLINE]; 370442575Speter char *boundaries[MAXMIMENESTING + 1]; 370538032Speter 370638032Speter /* 370738032Speter ** Output the body of the message 370838032Speter */ 370938032Speter 371038032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 371138032Speter { 371238032Speter char *df = queuename(e, 'd'); 371338032Speter 371438032Speter e->e_dfp = fopen(df, "r"); 371538032Speter if (e->e_dfp == NULL) 371664562Sgshapiro { 371764562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 371864562Sgshapiro 371964562Sgshapiro if (errno == ENOENT) 372064562Sgshapiro msg++; 372164562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 372264562Sgshapiro } 372338032Speter } 372438032Speter if (e->e_dfp == NULL) 372538032Speter { 372638032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 372738032Speter { 372838032Speter putline("", mci); 372938032Speter mci->mci_flags &= ~MCIF_INHEADER; 373038032Speter } 373138032Speter putline("<<< No Message Collected >>>", mci); 373238032Speter goto endofmessage; 373338032Speter } 373464562Sgshapiro 373538032Speter if (e->e_dfino == (ino_t) 0) 373638032Speter { 373738032Speter struct stat stbuf; 373838032Speter 373938032Speter if (fstat(fileno(e->e_dfp), &stbuf) < 0) 374038032Speter e->e_dfino = -1; 374138032Speter else 374238032Speter { 374338032Speter e->e_dfdev = stbuf.st_dev; 374438032Speter e->e_dfino = stbuf.st_ino; 374538032Speter } 374638032Speter } 374738032Speter 374864562Sgshapiro /* paranoia: the df file should always be in a rewound state */ 374964562Sgshapiro (void) bfrewind(e->e_dfp); 375064562Sgshapiro 375138032Speter#if MIME8TO7 375238032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 375338032Speter { 375438032Speter /* 375538032Speter ** Do 8 to 7 bit MIME conversion. 375638032Speter */ 375738032Speter 375838032Speter /* make sure it looks like a MIME message */ 375938032Speter if (hvalue("MIME-Version", e->e_header) == NULL) 376038032Speter putline("MIME-Version: 1.0", mci); 376138032Speter 376238032Speter if (hvalue("Content-Type", e->e_header) == NULL) 376338032Speter { 376438032Speter snprintf(buf, sizeof buf, 376538032Speter "Content-Type: text/plain; charset=%s", 376638032Speter defcharset(e)); 376738032Speter putline(buf, mci); 376838032Speter } 376938032Speter 377038032Speter /* now do the hard work */ 377138032Speter boundaries[0] = NULL; 377238032Speter mci->mci_flags |= MCIF_INHEADER; 377364562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); 377438032Speter } 377538032Speter# if MIME7TO8 377638032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 377738032Speter { 377864562Sgshapiro (void) mime7to8(mci, e->e_header, e); 377938032Speter } 378064562Sgshapiro# endif /* MIME7TO8 */ 378142575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 378242575Speter { 378364562Sgshapiro bool oldsuprerrs = SuprErrs; 378464562Sgshapiro 378542575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 378642575Speter boundaries[0] = NULL; 378742575Speter mci->mci_flags |= MCIF_INHEADER; 378864562Sgshapiro 378964562Sgshapiro /* 379064562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 379164562Sgshapiro ** and don't want to generate a new bounce message whose 379264562Sgshapiro ** body propagates the broken MIME. We can't just not call 379364562Sgshapiro ** mime8to7() as is done above since we need the security 379464562Sgshapiro ** checks. The best we can do is suppress the errors. 379564562Sgshapiro */ 379664562Sgshapiro 379764562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 379864562Sgshapiro SuprErrs = TRUE; 379964562Sgshapiro 380064562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, 380164562Sgshapiro M87F_OUTER|M87F_NO8TO7); 380264562Sgshapiro 380364562Sgshapiro /* restore SuprErrs */ 380464562Sgshapiro SuprErrs = oldsuprerrs; 380542575Speter } 380638032Speter else 380764562Sgshapiro#endif /* MIME8TO7 */ 380838032Speter { 380938032Speter int ostate; 381038032Speter register char *bp; 381138032Speter register char *pbp; 381238032Speter register int c; 381338032Speter register char *xp; 381438032Speter int padc; 381538032Speter char *buflim; 381638032Speter int pos = 0; 381764562Sgshapiro char peekbuf[12]; 381838032Speter 381938032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 382038032Speter { 382138032Speter putline("", mci); 382238032Speter mci->mci_flags &= ~MCIF_INHEADER; 382338032Speter } 382438032Speter 382538032Speter /* determine end of buffer; allow for short mailer lines */ 382638032Speter buflim = &buf[sizeof buf - 1]; 382738032Speter if (mci->mci_mailer->m_linelimit > 0 && 382838032Speter mci->mci_mailer->m_linelimit < sizeof buf - 1) 382938032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 383038032Speter 383138032Speter /* copy temp file to output with mapping */ 383238032Speter ostate = OS_HEAD; 383338032Speter bp = buf; 383438032Speter pbp = peekbuf; 383564562Sgshapiro while (!ferror(mci->mci_out) && !dead) 383638032Speter { 383738032Speter if (pbp > peekbuf) 383838032Speter c = *--pbp; 383938032Speter else if ((c = getc(e->e_dfp)) == EOF) 384038032Speter break; 384138032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 384238032Speter c &= 0x7f; 384338032Speter switch (ostate) 384438032Speter { 384538032Speter case OS_HEAD: 384638032Speter#if _FFR_NONULLS 384738032Speter if (c == '\0' && 384838032Speter bitnset(M_NONULLS, mci->mci_mailer->m_flags)) 384938032Speter break; 385064562Sgshapiro#endif /* _FFR_NONULLS */ 385138032Speter if (c != '\r' && c != '\n' && bp < buflim) 385238032Speter { 385338032Speter *bp++ = c; 385438032Speter break; 385538032Speter } 385638032Speter 385738032Speter /* check beginning of line for special cases */ 385838032Speter *bp = '\0'; 385938032Speter pos = 0; 386038032Speter padc = EOF; 386138032Speter if (buf[0] == 'F' && 386238032Speter bitnset(M_ESCFROM, mci->mci_mailer->m_flags) && 386338032Speter strncmp(buf, "From ", 5) == 0) 386438032Speter { 386538032Speter padc = '>'; 386638032Speter } 386738032Speter if (buf[0] == '-' && buf[1] == '-' && 386838032Speter separator != NULL) 386938032Speter { 387038032Speter /* possible separator */ 387138032Speter int sl = strlen(separator); 387238032Speter 387338032Speter if (strncmp(&buf[2], separator, sl) == 0) 387438032Speter padc = ' '; 387538032Speter } 387638032Speter if (buf[0] == '.' && 387738032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 387838032Speter { 387938032Speter padc = '.'; 388038032Speter } 388138032Speter 388238032Speter /* now copy out saved line */ 388338032Speter if (TrafficLogFile != NULL) 388438032Speter { 388538032Speter fprintf(TrafficLogFile, "%05d >>> ", 388638032Speter (int) getpid()); 388738032Speter if (padc != EOF) 388864562Sgshapiro (void) putc(padc, 388964562Sgshapiro TrafficLogFile); 389038032Speter for (xp = buf; xp < bp; xp++) 389164562Sgshapiro (void) putc((unsigned char) *xp, 389264562Sgshapiro TrafficLogFile); 389338032Speter if (c == '\n') 389464562Sgshapiro (void) fputs(mci->mci_mailer->m_eol, 389538032Speter TrafficLogFile); 389638032Speter } 389738032Speter if (padc != EOF) 389838032Speter { 389964562Sgshapiro if (putc(padc, mci->mci_out) == EOF) 390064562Sgshapiro { 390164562Sgshapiro dead = TRUE; 390264562Sgshapiro continue; 390364562Sgshapiro } 390438032Speter pos++; 390538032Speter } 390638032Speter for (xp = buf; xp < bp; xp++) 390738032Speter { 390864562Sgshapiro if (putc((unsigned char) *xp, 390964562Sgshapiro mci->mci_out) == EOF) 391064562Sgshapiro { 391164562Sgshapiro dead = TRUE; 391264562Sgshapiro break; 391364562Sgshapiro } 391464562Sgshapiro 391564562Sgshapiro /* record progress for DATA timeout */ 391664562Sgshapiro DataProgress = TRUE; 391738032Speter } 391864562Sgshapiro if (dead) 391964562Sgshapiro continue; 392038032Speter if (c == '\n') 392138032Speter { 392264562Sgshapiro if (fputs(mci->mci_mailer->m_eol, 392364562Sgshapiro mci->mci_out) == EOF) 392464562Sgshapiro break; 392538032Speter pos = 0; 392638032Speter } 392738032Speter else 392838032Speter { 392938032Speter pos += bp - buf; 393038032Speter if (c != '\r') 393138032Speter *pbp++ = c; 393238032Speter } 393364562Sgshapiro 393464562Sgshapiro /* record progress for DATA timeout */ 393564562Sgshapiro DataProgress = TRUE; 393638032Speter bp = buf; 393738032Speter 393838032Speter /* determine next state */ 393938032Speter if (c == '\n') 394038032Speter ostate = OS_HEAD; 394138032Speter else if (c == '\r') 394238032Speter ostate = OS_CR; 394338032Speter else 394438032Speter ostate = OS_INLINE; 394538032Speter continue; 394638032Speter 394738032Speter case OS_CR: 394838032Speter if (c == '\n') 394938032Speter { 395038032Speter /* got CRLF */ 395164562Sgshapiro if (fputs(mci->mci_mailer->m_eol, 395264562Sgshapiro mci->mci_out) == EOF) 395364562Sgshapiro continue; 395464562Sgshapiro 395564562Sgshapiro /* record progress for DATA timeout */ 395664562Sgshapiro DataProgress = TRUE; 395764562Sgshapiro 395838032Speter if (TrafficLogFile != NULL) 395938032Speter { 396064562Sgshapiro (void) fputs(mci->mci_mailer->m_eol, 396164562Sgshapiro TrafficLogFile); 396238032Speter } 396338032Speter ostate = OS_HEAD; 396438032Speter continue; 396538032Speter } 396638032Speter 396738032Speter /* had a naked carriage return */ 396838032Speter *pbp++ = c; 396938032Speter c = '\r'; 397038032Speter ostate = OS_INLINE; 397138032Speter goto putch; 397238032Speter 397338032Speter case OS_INLINE: 397438032Speter if (c == '\r') 397538032Speter { 397638032Speter ostate = OS_CR; 397738032Speter continue; 397838032Speter } 397938032Speter#if _FFR_NONULLS 398038032Speter if (c == '\0' && 398138032Speter bitnset(M_NONULLS, mci->mci_mailer->m_flags)) 398238032Speter break; 398364562Sgshapiro#endif /* _FFR_NONULLS */ 398438032Speterputch: 398538032Speter if (mci->mci_mailer->m_linelimit > 0 && 398664562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 398738032Speter c != '\n') 398838032Speter { 398964562Sgshapiro int d; 399064562Sgshapiro 399164562Sgshapiro /* check next character for EOL */ 399264562Sgshapiro if (pbp > peekbuf) 399364562Sgshapiro d = *(pbp - 1); 399464562Sgshapiro else if ((d = getc(e->e_dfp)) != EOF) 399564562Sgshapiro *pbp++ = d; 399664562Sgshapiro 399764562Sgshapiro if (d == '\n' || d == EOF) 399864562Sgshapiro { 399964562Sgshapiro if (TrafficLogFile != NULL) 400064562Sgshapiro (void) putc((unsigned char) c, 400164562Sgshapiro TrafficLogFile); 400264562Sgshapiro if (putc((unsigned char) c, 400364562Sgshapiro mci->mci_out) == EOF) 400464562Sgshapiro { 400564562Sgshapiro dead = TRUE; 400664562Sgshapiro continue; 400764562Sgshapiro } 400864562Sgshapiro pos++; 400964562Sgshapiro continue; 401064562Sgshapiro } 401164562Sgshapiro 401264562Sgshapiro if (putc('!', mci->mci_out) == EOF || 401364562Sgshapiro fputs(mci->mci_mailer->m_eol, 401464562Sgshapiro mci->mci_out) == EOF) 401564562Sgshapiro { 401664562Sgshapiro dead = TRUE; 401764562Sgshapiro continue; 401864562Sgshapiro } 401964562Sgshapiro 402064562Sgshapiro /* record progress for DATA timeout */ 402164562Sgshapiro DataProgress = TRUE; 402264562Sgshapiro 402338032Speter if (TrafficLogFile != NULL) 402438032Speter { 402538032Speter fprintf(TrafficLogFile, "!%s", 402638032Speter mci->mci_mailer->m_eol); 402738032Speter } 402838032Speter ostate = OS_HEAD; 402938032Speter *pbp++ = c; 403038032Speter continue; 403138032Speter } 403238032Speter if (c == '\n') 403338032Speter { 403438032Speter if (TrafficLogFile != NULL) 403564562Sgshapiro (void) fputs(mci->mci_mailer->m_eol, 403638032Speter TrafficLogFile); 403764562Sgshapiro if (fputs(mci->mci_mailer->m_eol, 403864562Sgshapiro mci->mci_out) == EOF) 403964562Sgshapiro continue; 404038032Speter pos = 0; 404138032Speter ostate = OS_HEAD; 404238032Speter } 404338032Speter else 404438032Speter { 404538032Speter if (TrafficLogFile != NULL) 404664562Sgshapiro (void) putc((unsigned char) c, 404764562Sgshapiro TrafficLogFile); 404864562Sgshapiro if (putc((unsigned char) c, 404964562Sgshapiro mci->mci_out) == EOF) 405064562Sgshapiro { 405164562Sgshapiro dead = TRUE; 405264562Sgshapiro continue; 405364562Sgshapiro } 405438032Speter pos++; 405538032Speter ostate = OS_INLINE; 405638032Speter } 405764562Sgshapiro 405864562Sgshapiro /* record progress for DATA timeout */ 405964562Sgshapiro DataProgress = TRUE; 406038032Speter break; 406138032Speter } 406238032Speter } 406338032Speter 406438032Speter /* make sure we are at the beginning of a line */ 406538032Speter if (bp > buf) 406638032Speter { 406738032Speter if (TrafficLogFile != NULL) 406838032Speter { 406938032Speter for (xp = buf; xp < bp; xp++) 407064562Sgshapiro (void) putc((unsigned char) *xp, 407164562Sgshapiro TrafficLogFile); 407238032Speter } 407338032Speter for (xp = buf; xp < bp; xp++) 407438032Speter { 407564562Sgshapiro if (putc((unsigned char) *xp, mci->mci_out) == 407664562Sgshapiro EOF) 407764562Sgshapiro { 407864562Sgshapiro dead = TRUE; 407964562Sgshapiro break; 408064562Sgshapiro } 408164562Sgshapiro 408264562Sgshapiro /* record progress for DATA timeout */ 408364562Sgshapiro DataProgress = TRUE; 408438032Speter } 408538032Speter pos += bp - buf; 408638032Speter } 408764562Sgshapiro if (!dead && pos > 0) 408838032Speter { 408938032Speter if (TrafficLogFile != NULL) 409064562Sgshapiro (void) fputs(mci->mci_mailer->m_eol, 409164562Sgshapiro TrafficLogFile); 409264562Sgshapiro (void) fputs(mci->mci_mailer->m_eol, mci->mci_out); 409364562Sgshapiro 409464562Sgshapiro /* record progress for DATA timeout */ 409564562Sgshapiro DataProgress = TRUE; 409638032Speter } 409738032Speter } 409838032Speter 409938032Speter if (ferror(e->e_dfp)) 410038032Speter { 410164562Sgshapiro syserr("putbody: %s/df%s: read error", 410264562Sgshapiro qid_printqueue(e->e_queuedir), e->e_id); 410338032Speter ExitStat = EX_IOERR; 410438032Speter } 410538032Speter 410638032Speterendofmessage: 410764562Sgshapiro /* 410864562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 410964562Sgshapiro ** the file offset in the stdio library for the 411064562Sgshapiro ** parent process will not agree with the in-kernel 411164562Sgshapiro ** file offset since the file descriptor is shared 411264562Sgshapiro ** between the processes. Therefore, it is vital 411364562Sgshapiro ** that the file always be rewound. This forces the 411464562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 411564562Sgshapiro ** offset to match. 411664562Sgshapiro */ 411764562Sgshapiro 411864562Sgshapiro if (e->e_dfp != NULL) 411964562Sgshapiro (void) bfrewind(e->e_dfp); 412064562Sgshapiro 412138032Speter /* some mailers want extra blank line at end of message */ 412264562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 412338032Speter buf[0] != '\0' && buf[0] != '\n') 412438032Speter putline("", mci); 412538032Speter 412638032Speter (void) fflush(mci->mci_out); 412738032Speter if (ferror(mci->mci_out) && errno != EPIPE) 412838032Speter { 412938032Speter syserr("putbody: write error"); 413038032Speter ExitStat = EX_IOERR; 413138032Speter } 413264562Sgshapiro 413338032Speter errno = 0; 413438032Speter} 413538032Speter/* 413638032Speter** MAILFILE -- Send a message to a file. 413738032Speter** 413838032Speter** If the file has the setuid/setgid bits set, but NO execute 413938032Speter** bits, sendmail will try to become the owner of that file 414038032Speter** rather than the real user. Obviously, this only works if 414138032Speter** sendmail runs as root. 414238032Speter** 414338032Speter** This could be done as a subordinate mailer, except that it 414438032Speter** is used implicitly to save messages in ~/dead.letter. We 414538032Speter** view this as being sufficiently important as to include it 414638032Speter** here. For example, if the system is dying, we shouldn't have 414738032Speter** to create another process plus some pipes to save the message. 414838032Speter** 414938032Speter** Parameters: 415038032Speter** filename -- the name of the file to send to. 415138032Speter** mailer -- mailer definition for recipient -- if NULL, 415238032Speter** use FileMailer. 415338032Speter** ctladdr -- the controlling address header -- includes 415438032Speter** the userid/groupid to be when sending. 415538032Speter** sfflags -- flags for opening. 415638032Speter** e -- the current envelope. 415738032Speter** 415838032Speter** Returns: 415938032Speter** The exit code associated with the operation. 416038032Speter** 416138032Speter** Side Effects: 416238032Speter** none. 416338032Speter*/ 416438032Speter 416538032Speterstatic jmp_buf CtxMailfileTimeout; 416638032Speter 416738032Speterint 416838032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 416938032Speter char *volatile filename; 417038032Speter MAILER *volatile mailer; 417138032Speter ADDRESS *ctladdr; 417264562Sgshapiro volatile long sfflags; 417338032Speter register ENVELOPE *e; 417438032Speter{ 417538032Speter register FILE *f; 417638032Speter register pid_t pid = -1; 417764562Sgshapiro volatile int mode; 417864562Sgshapiro int len; 417964562Sgshapiro off_t curoff; 418038032Speter bool suidwarn = geteuid() == 0; 418138032Speter char *p; 418264562Sgshapiro char *volatile realfile; 418338032Speter EVENT *ev; 418464562Sgshapiro char buf[MAXLINE + 1]; 418564562Sgshapiro char targetfile[MAXPATHLEN + 1]; 418638032Speter 418738032Speter if (tTd(11, 1)) 418838032Speter { 418964562Sgshapiro dprintf("mailfile %s\n ctladdr=", filename); 419038032Speter printaddr(ctladdr, FALSE); 419138032Speter } 419238032Speter 419338032Speter if (mailer == NULL) 419438032Speter mailer = FileMailer; 419538032Speter 419638032Speter if (e->e_xfp != NULL) 419764562Sgshapiro (void) fflush(e->e_xfp); 419838032Speter 419938032Speter /* 420038032Speter ** Special case /dev/null. This allows us to restrict file 420138032Speter ** delivery to regular files only. 420238032Speter */ 420338032Speter 420438032Speter if (strcmp(filename, "/dev/null") == 0) 420538032Speter return EX_OK; 420638032Speter 420738032Speter /* check for 8-bit available */ 420838032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 420938032Speter bitnset(M_7BITS, mailer->m_flags) && 421038032Speter (bitset(EF_DONT_MIME, e->e_flags) || 421138032Speter !(bitset(MM_MIME8BIT, MimeMode) || 421238032Speter (bitset(EF_IS_MIME, e->e_flags) && 421338032Speter bitset(MM_CVTMIME, MimeMode))))) 421438032Speter { 421538032Speter e->e_status = "5.6.3"; 421664562Sgshapiro usrerrenh(e->e_status, 421764562Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 421864562Sgshapiro return EX_DATAERR; 421938032Speter } 422038032Speter 422164562Sgshapiro /* Find the actual file */ 422264562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 422364562Sgshapiro { 422464562Sgshapiro len = strlen(SafeFileEnv); 422564562Sgshapiro 422664562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 422764562Sgshapiro filename += len; 422864562Sgshapiro 422964562Sgshapiro if (len + strlen(filename) + 1 > MAXPATHLEN) 423064562Sgshapiro { 423164562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 423264562Sgshapiro SafeFileEnv, filename); 423364562Sgshapiro return EX_CANTCREAT; 423464562Sgshapiro } 423564562Sgshapiro (void) strlcpy(targetfile, SafeFileEnv, sizeof targetfile); 423664562Sgshapiro realfile = targetfile + len; 423764562Sgshapiro if (targetfile[len - 1] != '/') 423864562Sgshapiro (void) strlcat(targetfile, "/", sizeof targetfile); 423964562Sgshapiro if (*filename == '/') 424064562Sgshapiro filename++; 424164562Sgshapiro (void) strlcat(targetfile, filename, sizeof targetfile); 424264562Sgshapiro } 424364562Sgshapiro else if (mailer->m_rootdir != NULL) 424464562Sgshapiro { 424564562Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof targetfile, e); 424664562Sgshapiro len = strlen(targetfile); 424764562Sgshapiro 424864562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 424964562Sgshapiro filename += len; 425064562Sgshapiro 425164562Sgshapiro if (len + strlen(filename) + 1 > MAXPATHLEN) 425264562Sgshapiro { 425364562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 425464562Sgshapiro targetfile, filename); 425564562Sgshapiro return EX_CANTCREAT; 425664562Sgshapiro } 425764562Sgshapiro realfile = targetfile + len; 425864562Sgshapiro if (targetfile[len - 1] != '/') 425964562Sgshapiro (void) strlcat(targetfile, "/", sizeof targetfile); 426064562Sgshapiro if (*filename == '/') 426164562Sgshapiro (void) strlcat(targetfile, filename + 1, 426264562Sgshapiro sizeof targetfile); 426364562Sgshapiro else 426464562Sgshapiro (void) strlcat(targetfile, filename, sizeof targetfile); 426564562Sgshapiro } 426664562Sgshapiro else 426764562Sgshapiro { 426864562Sgshapiro if (strlen(filename) > MAXPATHLEN) 426964562Sgshapiro { 427064562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 427164562Sgshapiro return EX_CANTCREAT; 427264562Sgshapiro } 427364562Sgshapiro (void) strlcpy(targetfile, filename, sizeof targetfile); 427464562Sgshapiro realfile = targetfile; 427564562Sgshapiro } 427664562Sgshapiro 427738032Speter /* 427838032Speter ** Fork so we can change permissions here. 427938032Speter ** Note that we MUST use fork, not vfork, because of 428038032Speter ** the complications of calling subroutines, etc. 428138032Speter */ 428238032Speter 428338032Speter DOFORK(fork); 428438032Speter 428538032Speter if (pid < 0) 428664562Sgshapiro return EX_OSERR; 428738032Speter else if (pid == 0) 428838032Speter { 428938032Speter /* child -- actually write to file */ 429038032Speter struct stat stb; 429138032Speter MCI mcibuf; 429242575Speter int err; 429338032Speter volatile int oflags = O_WRONLY|O_APPEND; 429438032Speter 429538032Speter if (e->e_lockfp != NULL) 429638032Speter (void) close(fileno(e->e_lockfp)); 429738032Speter 429838032Speter (void) setsignal(SIGINT, SIG_DFL); 429938032Speter (void) setsignal(SIGHUP, SIG_DFL); 430038032Speter (void) setsignal(SIGTERM, SIG_DFL); 430138032Speter (void) umask(OldUmask); 430238032Speter e->e_to = filename; 430338032Speter ExitStat = EX_OK; 430438032Speter 430538032Speter if (setjmp(CtxMailfileTimeout) != 0) 430638032Speter { 430738032Speter exit(EX_TEMPFAIL); 430838032Speter } 430938032Speter 431038032Speter if (TimeOuts.to_fileopen > 0) 431138032Speter ev = setevent(TimeOuts.to_fileopen, mailfiletimeout, 0); 431238032Speter else 431338032Speter ev = NULL; 431438032Speter 431564562Sgshapiro /* check file mode to see if setuid */ 431664562Sgshapiro if (stat(targetfile, &stb) < 0) 431764562Sgshapiro mode = FileMode; 431842575Speter else 431938032Speter mode = stb.st_mode; 432038032Speter 432138032Speter /* limit the errors to those actually caused in the child */ 432238032Speter errno = 0; 432338032Speter ExitStat = EX_OK; 432438032Speter 432564562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 432664562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 432764562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 432838032Speter { 432938032Speter /* ignore setuid and setgid bits */ 433038032Speter mode &= ~(S_ISGID|S_ISUID); 433164562Sgshapiro if (tTd(11, 20)) 433264562Sgshapiro dprintf("mailfile: ignoring setuid/setgid bits\n"); 433338032Speter } 433438032Speter 433538032Speter /* we have to open the dfile BEFORE setuid */ 433638032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 433738032Speter { 433838032Speter char *df = queuename(e, 'd'); 433938032Speter 434038032Speter e->e_dfp = fopen(df, "r"); 434138032Speter if (e->e_dfp == NULL) 434238032Speter { 434338032Speter syserr("mailfile: Cannot open %s for %s from %s", 434438032Speter df, e->e_to, e->e_from.q_paddr); 434538032Speter } 434638032Speter } 434738032Speter 434838032Speter /* select a new user to run as */ 434938032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 435038032Speter { 435138032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 435238032Speter { 435338032Speter RealUserName = NULL; 435438032Speter RealUid = mailer->m_uid; 435564562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 435664562Sgshapiro { 435764562Sgshapiro /* Only root can change the uid */ 435864562Sgshapiro syserr("mailfile: insufficient privileges to change uid"); 435964562Sgshapiro exit(EX_TEMPFAIL); 436064562Sgshapiro } 436138032Speter } 436238032Speter else if (bitset(S_ISUID, mode)) 436338032Speter { 436438032Speter RealUserName = NULL; 436538032Speter RealUid = stb.st_uid; 436638032Speter } 436738032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 436838032Speter { 436938032Speter if (ctladdr->q_ruser != NULL) 437038032Speter RealUserName = ctladdr->q_ruser; 437138032Speter else 437238032Speter RealUserName = ctladdr->q_user; 437338032Speter RealUid = ctladdr->q_uid; 437438032Speter } 437538032Speter else if (mailer != NULL && mailer->m_uid != 0) 437638032Speter { 437738032Speter RealUserName = DefUser; 437838032Speter RealUid = mailer->m_uid; 437938032Speter } 438038032Speter else 438138032Speter { 438238032Speter RealUserName = DefUser; 438338032Speter RealUid = DefUid; 438438032Speter } 438538032Speter 438638032Speter /* select a new group to run as */ 438738032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 438864562Sgshapiro { 438938032Speter RealGid = mailer->m_gid; 439064562Sgshapiro if (RunAsUid != 0 && 439164562Sgshapiro (RealGid != getgid() || 439264562Sgshapiro RealGid != getegid())) 439364562Sgshapiro { 439464562Sgshapiro /* Only root can change the gid */ 439564562Sgshapiro syserr("mailfile: insufficient privileges to change gid"); 439664562Sgshapiro exit(EX_TEMPFAIL); 439764562Sgshapiro } 439864562Sgshapiro } 439938032Speter else if (bitset(S_ISGID, mode)) 440038032Speter RealGid = stb.st_gid; 440138032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 440238032Speter RealGid = ctladdr->q_gid; 440364562Sgshapiro else if (ctladdr != NULL && 440464562Sgshapiro ctladdr->q_uid == DefUid && 440564562Sgshapiro ctladdr->q_gid == 0) 440664562Sgshapiro RealGid = DefGid; 440738032Speter else if (mailer != NULL && mailer->m_gid != 0) 440838032Speter RealGid = mailer->m_gid; 440938032Speter else 441038032Speter RealGid = DefGid; 441138032Speter } 441238032Speter 441338032Speter /* last ditch */ 441438032Speter if (!bitset(SFF_ROOTOK, sfflags)) 441538032Speter { 441638032Speter if (RealUid == 0) 441738032Speter RealUid = DefUid; 441838032Speter if (RealGid == 0) 441938032Speter RealGid = DefGid; 442038032Speter } 442138032Speter 442238032Speter /* set group id list (needs /etc/group access) */ 442338032Speter if (RealUserName != NULL && !DontInitGroups) 442438032Speter { 442538032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 442664562Sgshapiro { 442738032Speter syserr("mailfile: initgroups(%s, %d) failed", 442838032Speter RealUserName, RealGid); 442964562Sgshapiro exit(EX_TEMPFAIL); 443064562Sgshapiro } 443138032Speter } 443238032Speter else 443338032Speter { 443438032Speter GIDSET_T gidset[1]; 443538032Speter 443638032Speter gidset[0] = RealGid; 443738032Speter if (setgroups(1, gidset) == -1 && suidwarn) 443864562Sgshapiro { 443938032Speter syserr("mailfile: setgroups() failed"); 444064562Sgshapiro exit(EX_TEMPFAIL); 444164562Sgshapiro } 444238032Speter } 444338032Speter 444464562Sgshapiro /* 444564562Sgshapiro ** If you have a safe environment, go into it. 444664562Sgshapiro */ 444764562Sgshapiro 444864562Sgshapiro if (realfile != targetfile) 444938032Speter { 445064562Sgshapiro *realfile = '\0'; 445164562Sgshapiro if (tTd(11, 20)) 445264562Sgshapiro dprintf("mailfile: chroot %s\n", targetfile); 445364562Sgshapiro if (chroot(targetfile) < 0) 445438032Speter { 445538032Speter syserr("mailfile: Cannot chroot(%s)", 445664562Sgshapiro targetfile); 445738032Speter exit(EX_CANTCREAT); 445838032Speter } 445964562Sgshapiro *realfile = '/'; 446038032Speter } 446164562Sgshapiro 446264562Sgshapiro if (tTd(11, 40)) 446364562Sgshapiro dprintf("mailfile: deliver to %s\n", realfile); 446464562Sgshapiro 446538032Speter if (chdir("/") < 0) 446664562Sgshapiro { 446738032Speter syserr("mailfile: cannot chdir(/)"); 446864562Sgshapiro exit(EX_CANTCREAT); 446964562Sgshapiro } 447038032Speter 447138032Speter /* now reset the group and user ids */ 447238032Speter endpwent(); 447338032Speter if (setgid(RealGid) < 0 && suidwarn) 447464562Sgshapiro { 447538032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 447664562Sgshapiro exit(EX_TEMPFAIL); 447764562Sgshapiro } 447838032Speter vendor_set_uid(RealUid); 447938032Speter if (setuid(RealUid) < 0 && suidwarn) 448064562Sgshapiro { 448138032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 448264562Sgshapiro exit(EX_TEMPFAIL); 448364562Sgshapiro } 448438032Speter 448564562Sgshapiro if (tTd(11, 2)) 448664562Sgshapiro dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 448764562Sgshapiro (int) getuid(), (int) geteuid(), 448864562Sgshapiro (int) getgid(), (int) getegid()); 448964562Sgshapiro 449064562Sgshapiro 449138032Speter /* move into some "safe" directory */ 449238032Speter if (mailer->m_execdir != NULL) 449338032Speter { 449438032Speter char *q; 449538032Speter 449638032Speter for (p = mailer->m_execdir; p != NULL; p = q) 449738032Speter { 449838032Speter q = strchr(p, ':'); 449938032Speter if (q != NULL) 450038032Speter *q = '\0'; 450138032Speter expand(p, buf, sizeof buf, e); 450238032Speter if (q != NULL) 450338032Speter *q++ = ':'; 450438032Speter if (tTd(11, 20)) 450564562Sgshapiro dprintf("mailfile: trydir %s\n", buf); 450638032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 450738032Speter break; 450838032Speter } 450938032Speter } 451038032Speter 451164562Sgshapiro /* 451264562Sgshapiro ** Recheck the file after we have assumed the ID of the 451364562Sgshapiro ** delivery user to make sure we can deliver to it as 451464562Sgshapiro ** that user. This is necessary if sendmail is running 451564562Sgshapiro ** as root and the file is on an NFS mount which treats 451664562Sgshapiro ** root as nobody. 451764562Sgshapiro */ 451864562Sgshapiro 451964562Sgshapiro#if HASLSTAT 452064562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 452164562Sgshapiro err = stat(realfile, &stb); 452264562Sgshapiro else 452364562Sgshapiro err = lstat(realfile, &stb); 452464562Sgshapiro#else /* HASLSTAT */ 452564562Sgshapiro err = stat(realfile, &stb); 452664562Sgshapiro#endif /* HASLSTAT */ 452764562Sgshapiro 452864562Sgshapiro if (err < 0) 452964562Sgshapiro { 453064562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 453164562Sgshapiro mode = FileMode; 453264562Sgshapiro oflags |= O_CREAT|O_EXCL; 453364562Sgshapiro } 453464562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 453564562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 453664562Sgshapiro DontBlameSendmail) && 453764562Sgshapiro stb.st_nlink != 1) || 453864562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 453964562Sgshapiro exit(EX_CANTCREAT); 454064562Sgshapiro else 454164562Sgshapiro mode = stb.st_mode; 454264562Sgshapiro 454364562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 454438032Speter sfflags |= SFF_NOSLINK; 454564562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 454638032Speter sfflags |= SFF_NOHLINK; 454738032Speter sfflags &= ~SFF_OPENASROOT; 454864562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 454938032Speter if (f == NULL) 455038032Speter { 455164562Sgshapiro if (transienterror(errno)) 455264562Sgshapiro { 455364562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 455464562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 455564562Sgshapiro errstring(errno)); 455664562Sgshapiro exit(EX_TEMPFAIL); 455764562Sgshapiro } 455864562Sgshapiro else 455964562Sgshapiro { 456064562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 456164562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 456264562Sgshapiro errstring(errno)); 456364562Sgshapiro exit(EX_CANTCREAT); 456464562Sgshapiro } 456538032Speter } 456664562Sgshapiro if (filechanged(realfile, fileno(f), &stb)) 456738032Speter { 456864562Sgshapiro syserr("554 5.3.0 file changed after open"); 456938032Speter exit(EX_CANTCREAT); 457038032Speter } 457138032Speter if (fstat(fileno(f), &stb) < 0) 457238032Speter { 457364562Sgshapiro syserr("554 5.3.0 cannot fstat %s", errstring(errno)); 457438032Speter exit(EX_CANTCREAT); 457538032Speter } 457638032Speter 457764562Sgshapiro curoff = stb.st_size; 457864562Sgshapiro 457938032Speter if (ev != NULL) 458038032Speter clrevent(ev); 458138032Speter 458264562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 458338032Speter mcibuf.mci_mailer = mailer; 458438032Speter mcibuf.mci_out = f; 458538032Speter if (bitnset(M_7BITS, mailer->m_flags)) 458638032Speter mcibuf.mci_flags |= MCIF_7BIT; 458738032Speter 458838032Speter /* clear out per-message flags from connection structure */ 458938032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 459038032Speter 459138032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 459238032Speter !bitset(EF_DONT_MIME, e->e_flags) && 459338032Speter bitnset(M_7BITS, mailer->m_flags)) 459438032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 459538032Speter 459638032Speter#if MIME7TO8 459738032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 459838032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 459938032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 460038032Speter (strcasecmp(p, "quoted-printable") == 0 || 460138032Speter strcasecmp(p, "base64") == 0) && 460238032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 460338032Speter { 460438032Speter /* may want to convert 7 -> 8 */ 460538032Speter /* XXX should really parse it here -- and use a class XXX */ 460638032Speter if (strncasecmp(p, "text/plain", 10) == 0 && 460764562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 460838032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 460938032Speter } 461064562Sgshapiro#endif /* MIME7TO8 */ 461138032Speter 461238032Speter putfromline(&mcibuf, e); 461343730Speter (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); 461438032Speter (*e->e_putbody)(&mcibuf, e, NULL); 461538032Speter putline("\n", &mcibuf); 461664562Sgshapiro if (fflush(f) < 0 || 461764562Sgshapiro (SuperSafe && fsync(fileno(f)) < 0) || 461864562Sgshapiro ferror(f)) 461938032Speter { 462038032Speter setstat(EX_IOERR); 462164562Sgshapiro#if !NOFTRUNCATE 462264562Sgshapiro (void) ftruncate(fileno(f), curoff); 462364562Sgshapiro#endif /* !NOFTRUNCATE */ 462438032Speter } 462538032Speter 462638032Speter /* reset ISUID & ISGID bits for paranoid systems */ 462738032Speter#if HASFCHMOD 462864562Sgshapiro (void) fchmod(fileno(f), (MODE_T) mode); 462964562Sgshapiro#else /* HASFCHMOD */ 463064562Sgshapiro (void) chmod(filename, (MODE_T) mode); 463164562Sgshapiro#endif /* HASFCHMOD */ 463264562Sgshapiro if (fclose(f) < 0) 463364562Sgshapiro setstat(EX_IOERR); 463438032Speter (void) fflush(stdout); 463564562Sgshapiro (void) setuid(RealUid); 463638032Speter exit(ExitStat); 463764562Sgshapiro /* NOTREACHED */ 463838032Speter } 463938032Speter else 464038032Speter { 464138032Speter /* parent -- wait for exit status */ 464238032Speter int st; 464338032Speter 464438032Speter st = waitfor(pid); 464538032Speter if (st == -1) 464638032Speter { 464738032Speter syserr("mailfile: %s: wait", mailer->m_name); 464864562Sgshapiro return EX_SOFTWARE; 464938032Speter } 465038032Speter if (WIFEXITED(st)) 465138032Speter return (WEXITSTATUS(st)); 465238032Speter else 465338032Speter { 465438032Speter syserr("mailfile: %s: child died on signal %d", 465538032Speter mailer->m_name, st); 465664562Sgshapiro return EX_UNAVAILABLE; 465738032Speter } 465864562Sgshapiro /* NOTREACHED */ 465938032Speter } 466038032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 466138032Speter} 466238032Speter 466338032Speterstatic void 466438032Spetermailfiletimeout() 466538032Speter{ 466638032Speter longjmp(CtxMailfileTimeout, 1); 466738032Speter} 466838032Speter/* 466938032Speter** HOSTSIGNATURE -- return the "signature" for a host. 467038032Speter** 467138032Speter** The signature describes how we are going to send this -- it 467238032Speter** can be just the hostname (for non-Internet hosts) or can be 467338032Speter** an ordered list of MX hosts. 467438032Speter** 467538032Speter** Parameters: 467638032Speter** m -- the mailer describing this host. 467738032Speter** host -- the host name. 467838032Speter** 467938032Speter** Returns: 468038032Speter** The signature for this host. 468138032Speter** 468238032Speter** Side Effects: 468338032Speter** Can tweak the symbol table. 468438032Speter*/ 468564562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 468638032Speter 468764562Sgshapirostatic char * 468864562Sgshapirohostsignature(m, host) 468938032Speter register MAILER *m; 469038032Speter char *host; 469138032Speter{ 469238032Speter register char *p; 469338032Speter register STAB *s; 469464562Sgshapiro#if NAMED_BIND 469564562Sgshapiro char sep = ':'; 469664562Sgshapiro char prevsep = ':'; 469738032Speter int i; 469838032Speter int len; 469938032Speter int nmx; 470064562Sgshapiro int hl; 470138032Speter char *hp; 470238032Speter char *endp; 470338032Speter int oldoptions = _res.options; 470438032Speter char *mxhosts[MAXMXHOSTS + 1]; 470564562Sgshapiro u_short mxprefs[MAXMXHOSTS + 1]; 470664562Sgshapiro#endif /* NAMED_BIND */ 470738032Speter 470864562Sgshapiro if (tTd(17, 3)) 470964562Sgshapiro dprintf("hostsignature(%s)\n", host); 471064562Sgshapiro 471138032Speter /* 471264562Sgshapiro ** If local delivery, just return a constant. 471364562Sgshapiro */ 471464562Sgshapiro 471564562Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 471664562Sgshapiro return "localhost"; 471764562Sgshapiro 471864562Sgshapiro /* 471938032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 472038032Speter */ 472138032Speter 472238032Speter p = m->m_mailer; 472364562Sgshapiro if (strcmp(p, "[IPC]") != 0 && 472464562Sgshapiro strcmp(p, "[TCP]") != 0) 472538032Speter { 472638032Speter /* just an ordinary mailer */ 472738032Speter return host; 472838032Speter } 472964562Sgshapiro#if NETUNIX 473064562Sgshapiro else if (m->m_argv[0] != NULL && 473164562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 473264562Sgshapiro { 473364562Sgshapiro /* rendezvous in the file system, no MX records */ 473464562Sgshapiro return host; 473564562Sgshapiro } 473664562Sgshapiro#endif /* NETUNIX */ 473738032Speter 473838032Speter /* 473938032Speter ** Look it up in the symbol table. 474038032Speter */ 474138032Speter 474238032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 474338032Speter if (s->s_hostsig != NULL) 474464562Sgshapiro { 474564562Sgshapiro if (tTd(17, 3)) 474664562Sgshapiro dprintf("hostsignature(): stab(%s) found %s\n", host, 474764562Sgshapiro s->s_hostsig); 474838032Speter return s->s_hostsig; 474964562Sgshapiro } 475038032Speter 475138032Speter /* 475238032Speter ** Not already there -- create a signature. 475338032Speter */ 475438032Speter 475538032Speter#if NAMED_BIND 475638032Speter if (ConfigLevel < 2) 475738032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 475838032Speter 475938032Speter for (hp = host; hp != NULL; hp = endp) 476038032Speter { 476164562Sgshapiro#if NETINET6 476264562Sgshapiro if (*hp == '[') 476364562Sgshapiro { 476464562Sgshapiro endp = strchr(hp + 1, ']'); 476564562Sgshapiro if (endp != NULL) 476664562Sgshapiro endp = strpbrk(endp + 1, ":,"); 476764562Sgshapiro } 476864562Sgshapiro else 476964562Sgshapiro endp = strpbrk(hp, ":,"); 477064562Sgshapiro#else /* NETINET6 */ 477164562Sgshapiro endp = strpbrk(hp, ":,"); 477264562Sgshapiro#endif /* NETINET6 */ 477338032Speter if (endp != NULL) 477464562Sgshapiro { 477564562Sgshapiro sep = *endp; 477638032Speter *endp = '\0'; 477764562Sgshapiro } 477838032Speter 477938032Speter if (bitnset(M_NOMX, m->m_flags)) 478038032Speter { 478138032Speter /* skip MX lookups */ 478238032Speter nmx = 1; 478338032Speter mxhosts[0] = hp; 478438032Speter } 478538032Speter else 478638032Speter { 478738032Speter auto int rcode; 478838032Speter 478964562Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, TRUE, &rcode); 479038032Speter if (nmx <= 0) 479138032Speter { 479238032Speter register MCI *mci; 479338032Speter 479438032Speter /* update the connection info for this host */ 479538032Speter mci = mci_get(hp, m); 479638032Speter mci->mci_errno = errno; 479738032Speter mci->mci_herrno = h_errno; 479838032Speter mci->mci_lastuse = curtime(); 479964562Sgshapiro if (rcode == EX_NOHOST) 480064562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 480164562Sgshapiro "550 Host unknown"); 480264562Sgshapiro else 480364562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 480438032Speter 480538032Speter /* use the original host name as signature */ 480638032Speter nmx = 1; 480738032Speter mxhosts[0] = hp; 480838032Speter } 480964562Sgshapiro if (tTd(17, 3)) 481064562Sgshapiro dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 481164562Sgshapiro nmx, mxhosts[0]); 481238032Speter } 481338032Speter 481438032Speter len = 0; 481538032Speter for (i = 0; i < nmx; i++) 481638032Speter len += strlen(mxhosts[i]) + 1; 481738032Speter if (s->s_hostsig != NULL) 481838032Speter len += strlen(s->s_hostsig) + 1; 481964562Sgshapiro if (len >= MAXHOSTSIGNATURE) 482064562Sgshapiro { 482164562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 482264562Sgshapiro host, MAXHOSTSIGNATURE, len); 482364562Sgshapiro len = MAXHOSTSIGNATURE; 482464562Sgshapiro } 482538032Speter p = xalloc(len); 482638032Speter if (s->s_hostsig != NULL) 482738032Speter { 482864562Sgshapiro (void) strlcpy(p, s->s_hostsig, len); 482938032Speter free(s->s_hostsig); 483038032Speter s->s_hostsig = p; 483164562Sgshapiro hl = strlen(p); 483264562Sgshapiro p += hl; 483364562Sgshapiro *p++ = prevsep; 483464562Sgshapiro len -= hl + 1; 483538032Speter } 483638032Speter else 483738032Speter s->s_hostsig = p; 483838032Speter for (i = 0; i < nmx; i++) 483938032Speter { 484064562Sgshapiro hl = strlen(mxhosts[i]); 484164562Sgshapiro if (len - 1 < hl || len <= 1) 484264562Sgshapiro { 484364562Sgshapiro /* force to drop out of outer loop */ 484464562Sgshapiro len = -1; 484564562Sgshapiro break; 484664562Sgshapiro } 484738032Speter if (i != 0) 484864562Sgshapiro { 484964562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 485064562Sgshapiro *p++ = ','; 485164562Sgshapiro else 485264562Sgshapiro *p++ = ':'; 485364562Sgshapiro len--; 485464562Sgshapiro } 485564562Sgshapiro (void) strlcpy(p, mxhosts[i], len); 485664562Sgshapiro p += hl; 485764562Sgshapiro len -= hl; 485838032Speter } 485964562Sgshapiro 486064562Sgshapiro /* 486164562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 486264562Sgshapiro ** because we won't have more space for further hosts 486364562Sgshapiro ** anyway (separated by : in the .cf file). 486464562Sgshapiro */ 486564562Sgshapiro 486664562Sgshapiro if (len < 0) 486764562Sgshapiro break; 486838032Speter if (endp != NULL) 486964562Sgshapiro *endp++ = sep; 487064562Sgshapiro prevsep = sep; 487138032Speter } 487238032Speter makelower(s->s_hostsig); 487338032Speter if (ConfigLevel < 2) 487438032Speter _res.options = oldoptions; 487564562Sgshapiro#else /* NAMED_BIND */ 487638032Speter /* not using BIND -- the signature is just the host name */ 487738032Speter s->s_hostsig = host; 487864562Sgshapiro#endif /* NAMED_BIND */ 487938032Speter if (tTd(17, 1)) 488064562Sgshapiro dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig); 488138032Speter return s->s_hostsig; 488238032Speter} 488364562Sgshapiro/* 488464562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 488564562Sgshapiro** 488664562Sgshapiro** The signature describes how we are going to send this -- it 488764562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 488864562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 488964562Sgshapiro** MX preference values. 489064562Sgshapiro** 489164562Sgshapiro** Parameters: 489264562Sgshapiro** sig -- the host signature. 489364562Sgshapiro** mxhosts -- array to populate. 489464562Sgshapiro** 489564562Sgshapiro** Returns: 489664562Sgshapiro** The number of hosts inserted into mxhosts array. 489764562Sgshapiro** 489864562Sgshapiro** Side Effects: 489964562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 490064562Sgshapiro*/ 490164562Sgshapiro 490264562Sgshapirostatic int 490364562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 490464562Sgshapiro char *sig; 490564562Sgshapiro char **mxhosts; 490664562Sgshapiro MAILER *mailer; 490764562Sgshapiro{ 490864562Sgshapiro int nmx = 0; 490964562Sgshapiro int curpref = 0; 491064562Sgshapiro int i, j; 491164562Sgshapiro char *hp, *endp; 491264562Sgshapiro u_short prefer[MAXMXHOSTS]; 491364562Sgshapiro long rndm[MAXMXHOSTS]; 491464562Sgshapiro 491564562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 491664562Sgshapiro { 491764562Sgshapiro char sep = ':'; 491864562Sgshapiro 491964562Sgshapiro#if NETINET6 492064562Sgshapiro if (*hp == '[') 492164562Sgshapiro { 492264562Sgshapiro endp = strchr(hp + 1, ']'); 492364562Sgshapiro if (endp != NULL) 492464562Sgshapiro endp = strpbrk(endp + 1, ":,"); 492564562Sgshapiro } 492664562Sgshapiro else 492764562Sgshapiro endp = strpbrk(hp, ":,"); 492864562Sgshapiro#else /* NETINET6 */ 492964562Sgshapiro endp = strpbrk(hp, ":,"); 493064562Sgshapiro#endif /* NETINET6 */ 493164562Sgshapiro if (endp != NULL) 493264562Sgshapiro { 493364562Sgshapiro sep = *endp; 493464562Sgshapiro *endp = '\0'; 493564562Sgshapiro } 493664562Sgshapiro 493764562Sgshapiro mxhosts[nmx] = hp; 493864562Sgshapiro prefer[nmx] = curpref; 493964562Sgshapiro if (mci_match(hp, mailer)) 494064562Sgshapiro rndm[nmx] = 0; 494164562Sgshapiro else 494264562Sgshapiro rndm[nmx] = get_random(); 494364562Sgshapiro 494464562Sgshapiro if (endp != NULL) 494564562Sgshapiro { 494664562Sgshapiro /* 494764562Sgshapiro ** Since we don't have the original MX prefs, 494864562Sgshapiro ** make our own. If the separator is a ':', that 494964562Sgshapiro ** means the preference for the next host will be 495064562Sgshapiro ** higher than this one, so simply increment curpref. 495164562Sgshapiro */ 495264562Sgshapiro 495364562Sgshapiro if (sep == ':') 495464562Sgshapiro curpref++; 495564562Sgshapiro 495664562Sgshapiro *endp++ = sep; 495764562Sgshapiro } 495864562Sgshapiro if (++nmx >= MAXMXHOSTS) 495964562Sgshapiro break; 496064562Sgshapiro } 496164562Sgshapiro 496264562Sgshapiro /* sort the records using the random factor for equal preferences */ 496364562Sgshapiro for (i = 0; i < nmx; i++) 496464562Sgshapiro { 496564562Sgshapiro for (j = i + 1; j < nmx; j++) 496664562Sgshapiro { 496764562Sgshapiro /* 496864562Sgshapiro ** List is already sorted by MX preference, only 496964562Sgshapiro ** need to look for equal preference MX records 497064562Sgshapiro */ 497164562Sgshapiro 497264562Sgshapiro if (prefer[i] < prefer[j]) 497364562Sgshapiro break; 497464562Sgshapiro 497564562Sgshapiro if (prefer[i] > prefer[j] || 497664562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 497764562Sgshapiro { 497864562Sgshapiro register u_short tempp; 497964562Sgshapiro register long tempr; 498064562Sgshapiro register char *temp1; 498164562Sgshapiro 498264562Sgshapiro tempp = prefer[i]; 498364562Sgshapiro prefer[i] = prefer[j]; 498464562Sgshapiro prefer[j] = tempp; 498564562Sgshapiro temp1 = mxhosts[i]; 498664562Sgshapiro mxhosts[i] = mxhosts[j]; 498764562Sgshapiro mxhosts[j] = temp1; 498864562Sgshapiro tempr = rndm[i]; 498964562Sgshapiro rndm[i] = rndm[j]; 499064562Sgshapiro rndm[j] = tempr; 499164562Sgshapiro } 499264562Sgshapiro } 499364562Sgshapiro } 499464562Sgshapiro return nmx; 499564562Sgshapiro} 499664562Sgshapiro 499764562Sgshapiro#if SMTP 499864562Sgshapiro# if STARTTLS 499964562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 500064562Sgshapiro 500164562Sgshapiro/* 500264562Sgshapiro** INITCLTTLS -- initialize client side TLS 500364562Sgshapiro** 500464562Sgshapiro** Parameters: 500564562Sgshapiro** none. 500664562Sgshapiro** 500764562Sgshapiro** Returns: 500864562Sgshapiro** succeeded? 500964562Sgshapiro*/ 501064562Sgshapiro 501164562Sgshapirobool 501264562Sgshapiroinitclttls() 501364562Sgshapiro{ 501464562Sgshapiro if (clt_ctx != NULL) 501564562Sgshapiro return TRUE; /* already done */ 501664562Sgshapiro return inittls(&clt_ctx, TLS_I_CLT, FALSE, CltCERTfile, Cltkeyfile, 501764562Sgshapiro CACERTpath, CACERTfile, DHParams); 501864562Sgshapiro} 501964562Sgshapiro 502064562Sgshapiro/* 502164562Sgshapiro** STARTTLS -- try to start secure connection (client side) 502264562Sgshapiro** 502364562Sgshapiro** Parameters: 502464562Sgshapiro** m -- the mailer. 502564562Sgshapiro** mci -- the mailer connection info. 502664562Sgshapiro** e -- the envelope. 502764562Sgshapiro** 502864562Sgshapiro** Returns: 502964562Sgshapiro** success? 503064562Sgshapiro** (maybe this should be some other code than EX_ 503164562Sgshapiro** that denotes which stage failed.) 503264562Sgshapiro*/ 503364562Sgshapiro 503464562Sgshapirostatic int 503564562Sgshapirostarttls(m, mci, e) 503664562Sgshapiro MAILER *m; 503764562Sgshapiro MCI *mci; 503864562Sgshapiro ENVELOPE *e; 503964562Sgshapiro{ 504064562Sgshapiro int smtpresult; 504166494Sgshapiro int result = 0; 504266494Sgshapiro int rfd, wfd; 504364562Sgshapiro SSL *clt_ssl = NULL; 504464562Sgshapiro 504566494Sgshapiro if (clt_ctx == NULL && !initclttls()) 504666494Sgshapiro return EX_TEMPFAIL; 504764562Sgshapiro smtpmessage("STARTTLS", m, mci); 504864562Sgshapiro 504964562Sgshapiro /* get the reply */ 505064562Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, NULL, NULL); 505164562Sgshapiro /* which timeout? XXX */ 505264562Sgshapiro 505364562Sgshapiro /* check return code from server */ 505464562Sgshapiro if (smtpresult == 454) 505564562Sgshapiro return EX_TEMPFAIL; 505664562Sgshapiro if (smtpresult == 501) 505764562Sgshapiro return EX_USAGE; 505864562Sgshapiro if (smtpresult == -1) 505964562Sgshapiro return smtpresult; 506064562Sgshapiro if (smtpresult != 220) 506164562Sgshapiro return EX_PROTOCOL; 506264562Sgshapiro 506364562Sgshapiro if (LogLevel > 13) 506464562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "TLS: start client"); 506564562Sgshapiro 506664562Sgshapiro /* start connection */ 506764562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 506864562Sgshapiro { 506964562Sgshapiro if (LogLevel > 5) 507064562Sgshapiro { 507164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 507264562Sgshapiro "TLS: error: client: SSL_new failed"); 507364562Sgshapiro if (LogLevel > 9) 507464562Sgshapiro tlslogerr(); 507564562Sgshapiro } 507664562Sgshapiro return EX_SOFTWARE; 507764562Sgshapiro } 507864562Sgshapiro 507966494Sgshapiro rfd = fileno(mci->mci_in); 508066494Sgshapiro wfd = fileno(mci->mci_out); 508166494Sgshapiro 508264562Sgshapiro /* SSL_clear(clt_ssl); ? */ 508366494Sgshapiro if (rfd < 0 || wfd < 0 || 508466494Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) <= 0 || 508566494Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) <= 0) 508664562Sgshapiro { 508764562Sgshapiro if (LogLevel > 5) 508864562Sgshapiro { 508964562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 509064562Sgshapiro "TLS: error: SSL_set_xfd failed=%d", result); 509164562Sgshapiro if (LogLevel > 9) 509264562Sgshapiro tlslogerr(); 509364562Sgshapiro } 509464562Sgshapiro return EX_SOFTWARE; 509564562Sgshapiro } 509664562Sgshapiro SSL_set_connect_state(clt_ssl); 509764562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 509864562Sgshapiro { 509964562Sgshapiro int i; 510064562Sgshapiro 510164562Sgshapiro /* what to do in this case? */ 510264562Sgshapiro i = SSL_get_error(clt_ssl, result); 510364562Sgshapiro if (LogLevel > 5) 510464562Sgshapiro { 510564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 510664562Sgshapiro "TLS: error: SSL_connect failed=%d (%d)", 510764562Sgshapiro result, i); 510864562Sgshapiro if (LogLevel > 9) 510964562Sgshapiro tlslogerr(); 511064562Sgshapiro } 511164562Sgshapiro SSL_free(clt_ssl); 511264562Sgshapiro clt_ssl = NULL; 511364562Sgshapiro return EX_SOFTWARE; 511464562Sgshapiro } 511564562Sgshapiro mci->mci_ssl = clt_ssl; 511664562Sgshapiro result = tls_get_info(clt_ssl, e, FALSE, mci->mci_host); 511764562Sgshapiro 511864562Sgshapiro /* switch to use SSL... */ 511964562Sgshapiro#if SFIO 512064562Sgshapiro if (sfdctls(mci->mci_in, mci->mci_out, mci->mci_ssl) == 0) 512164562Sgshapiro return EX_OK; 512264562Sgshapiro#else /* SFIO */ 512364562Sgshapiro# if _FFR_TLS_TOREK 512464562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 512564562Sgshapiro return EX_OK; 512664562Sgshapiro# endif /* _FFR_TLS_TOREK */ 512764562Sgshapiro#endif /* SFIO */ 512864562Sgshapiro 512964562Sgshapiro /* failure */ 513064562Sgshapiro SSL_free(clt_ssl); 513164562Sgshapiro clt_ssl = NULL; 513264562Sgshapiro return EX_SOFTWARE; 513364562Sgshapiro} 513464562Sgshapiro 513564562Sgshapiro/* 513664562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 513764562Sgshapiro** 513864562Sgshapiro** Parameters: 513964562Sgshapiro** mci -- the mailer connection info. 514064562Sgshapiro** 514164562Sgshapiro** Returns: 514264562Sgshapiro** success? 514364562Sgshapiro*/ 514464562Sgshapiroint 514564562Sgshapiroendtlsclt(mci) 514664562Sgshapiro MCI *mci; 514764562Sgshapiro{ 514864562Sgshapiro int r; 514964562Sgshapiro 515064562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 515164562Sgshapiro return EX_OK; 515264562Sgshapiro r = endtls(mci->mci_ssl, "client"); 515364562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 515464562Sgshapiro return r; 515564562Sgshapiro} 515664562Sgshapiro/* 515764562Sgshapiro** ENDTLS -- shutdown secure connection 515864562Sgshapiro** 515964562Sgshapiro** Parameters: 516064562Sgshapiro** ssl -- SSL connection information. 516164562Sgshapiro** side -- srv/clt (for logging). 516264562Sgshapiro** 516364562Sgshapiro** Returns: 516464562Sgshapiro** success? 516564562Sgshapiro*/ 516664562Sgshapiro 516764562Sgshapiroint 516864562Sgshapiroendtls(ssl, side) 516964562Sgshapiro SSL *ssl; 517064562Sgshapiro char *side; 517164562Sgshapiro{ 517264562Sgshapiro if (ssl != NULL) 517364562Sgshapiro { 517464562Sgshapiro int r; 517564562Sgshapiro 517664562Sgshapiro if ((r = SSL_shutdown(ssl)) < 0) 517764562Sgshapiro { 517864562Sgshapiro if (LogLevel > 11) 517964562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 518064562Sgshapiro "SSL_shutdown %s failed: %d", 518164562Sgshapiro side, r); 518264562Sgshapiro return EX_SOFTWARE; 518364562Sgshapiro } 518464562Sgshapiro else if (r == 0) 518564562Sgshapiro { 518664562Sgshapiro if (LogLevel > 13) 518764562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 518864562Sgshapiro "SSL_shutdown %s not done", 518964562Sgshapiro side); 519064562Sgshapiro return EX_SOFTWARE; 519164562Sgshapiro } 519264562Sgshapiro SSL_free(ssl); 519364562Sgshapiro ssl = NULL; 519464562Sgshapiro } 519564562Sgshapiro return EX_OK; 519664562Sgshapiro} 519764562Sgshapiro# endif /* STARTTLS */ 519864562Sgshapiro#endif /* SMTP */ 5199