deliver.c revision 125820
138032Speter/* 2111823Sgshapiro * Copyright (c) 1998-2003 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 1464562Sgshapiro#include <sendmail.h> 1590792Sgshapiro#include <sys/time.h> 1638032Speter 17125820SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.940.2.20 2003/09/26 18:26:19 ca Exp $") 1864562Sgshapiro 1938032Speter#if HASSETUSERCONTEXT 2038032Speter# include <login_cap.h> 2164562Sgshapiro#endif /* HASSETUSERCONTEXT */ 2238032Speter 2394334Sgshapiro#if NETINET || NETINET6 2494334Sgshapiro# include <arpa/inet.h> 2594334Sgshapiro#endif /* NETINET || NETINET6 */ 2694334Sgshapiro 2790792Sgshapiro#if STARTTLS || SASL 2864562Sgshapiro# include "sfsasl.h" 2990792Sgshapiro#endif /* STARTTLS || SASL */ 3064562Sgshapiro 3190792Sgshapirovoid markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool)); 3264562Sgshapirostatic int deliver __P((ENVELOPE *, ADDRESS *)); 3364562Sgshapirostatic void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 3464562Sgshapirostatic void mailfiletimeout __P((void)); 3564562Sgshapirostatic int parse_hostsignature __P((char *, char **, MAILER *)); 3664562Sgshapirostatic void sendenvelope __P((ENVELOPE *, int)); 3790792Sgshapiroextern MCI *mci_new __P((SM_RPOOL_T *)); 3890792Sgshapirostatic int coloncmp __P((const char *, const char *)); 3964562Sgshapiro 4090792Sgshapiro#if STARTTLS 4164562Sgshapirostatic int starttls __P((MAILER *, MCI *, ENVELOPE *)); 4290792Sgshapirostatic int endtlsclt __P((MCI *)); 4390792Sgshapiro#endif /* STARTTLS */ 4490792Sgshapiro# if STARTTLS || SASL 4590792Sgshapirostatic bool iscltflgset __P((ENVELOPE *, int)); 4690792Sgshapiro# endif /* STARTTLS || SASL */ 4738032Speter 4838032Speter/* 4938032Speter** SENDALL -- actually send all the messages. 5038032Speter** 5138032Speter** Parameters: 5238032Speter** e -- the envelope to send. 5338032Speter** mode -- the delivery mode to use. If SM_DEFAULT, use 5438032Speter** the current e->e_sendmode. 5538032Speter** 5638032Speter** Returns: 5738032Speter** none. 5838032Speter** 5938032Speter** Side Effects: 6038032Speter** Scans the send lists and sends everything it finds. 6138032Speter** Delivers any appropriate error messages. 6238032Speter** If we are running in a non-interactive mode, takes the 6338032Speter** appropriate action. 6438032Speter*/ 6538032Speter 6638032Spetervoid 6738032Spetersendall(e, mode) 6838032Speter ENVELOPE *e; 6938032Speter int mode; 7038032Speter{ 7138032Speter register ADDRESS *q; 7238032Speter char *owner; 7338032Speter int otherowners; 7464562Sgshapiro int save_errno; 7538032Speter register ENVELOPE *ee; 7638032Speter ENVELOPE *splitenv = NULL; 7738032Speter int oldverbose = Verbose; 7890792Sgshapiro bool somedeliveries = false, expensive = false; 7938032Speter pid_t pid; 8038032Speter 8138032Speter /* 8238032Speter ** If this message is to be discarded, don't bother sending 8338032Speter ** the message at all. 8438032Speter */ 8538032Speter 8638032Speter if (bitset(EF_DISCARD, e->e_flags)) 8738032Speter { 8838032Speter if (tTd(13, 1)) 8990792Sgshapiro sm_dprintf("sendall: discarding id %s\n", e->e_id); 9038032Speter e->e_flags |= EF_CLRQUEUE; 9190792Sgshapiro if (LogLevel > 9) 9290792Sgshapiro logundelrcpts(e, "discarded", 9, true); 9390792Sgshapiro else if (LogLevel > 4) 9438032Speter sm_syslog(LOG_INFO, e->e_id, "discarded"); 9590792Sgshapiro markstats(e, NULL, STATS_REJECT); 9638032Speter return; 9738032Speter } 9838032Speter 9938032Speter /* 10038032Speter ** If we have had global, fatal errors, don't bother sending 10138032Speter ** the message at all if we are in SMTP mode. Local errors 10238032Speter ** (e.g., a single address failing) will still cause the other 10338032Speter ** addresses to be sent. 10438032Speter */ 10538032Speter 10638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 10738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 10838032Speter { 10938032Speter e->e_flags |= EF_CLRQUEUE; 11038032Speter return; 11138032Speter } 11238032Speter 11338032Speter /* determine actual delivery mode */ 11438032Speter if (mode == SM_DEFAULT) 11538032Speter { 11638032Speter mode = e->e_sendmode; 11738032Speter if (mode != SM_VERIFY && mode != SM_DEFER && 11838032Speter shouldqueue(e->e_msgpriority, e->e_ctime)) 11938032Speter mode = SM_QUEUE; 12038032Speter } 12138032Speter 12238032Speter if (tTd(13, 1)) 12338032Speter { 12490792Sgshapiro sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 12538032Speter mode, e->e_id); 12690792Sgshapiro printaddr(&e->e_from, false); 12790792Sgshapiro sm_dprintf("\te_flags = "); 12838032Speter printenvflags(e); 12990792Sgshapiro sm_dprintf("sendqueue:\n"); 13090792Sgshapiro printaddr(e->e_sendqueue, true); 13138032Speter } 13238032Speter 13338032Speter /* 13438032Speter ** Do any preprocessing necessary for the mode we are running. 13538032Speter ** Check to make sure the hop count is reasonable. 13638032Speter ** Delete sends to the sender in mailing lists. 13738032Speter */ 13838032Speter 13938032Speter CurEnv = e; 14038032Speter if (tTd(62, 1)) 14138032Speter checkfds(NULL); 14238032Speter 14338032Speter if (e->e_hopcount > MaxHopCount) 14438032Speter { 14577349Sgshapiro char *recip; 14677349Sgshapiro 14777349Sgshapiro if (e->e_sendqueue != NULL && 14877349Sgshapiro e->e_sendqueue->q_paddr != NULL) 14977349Sgshapiro recip = e->e_sendqueue->q_paddr; 15077349Sgshapiro else 15177349Sgshapiro recip = "(nobody)"; 15277349Sgshapiro 15338032Speter errno = 0; 15490792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), false); 15538032Speter e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; 15664562Sgshapiro ExitStat = EX_UNAVAILABLE; 15777349Sgshapiro syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s", 15877349Sgshapiro e->e_hopcount, MaxHopCount, e->e_from.q_paddr, 15977349Sgshapiro RealHostName == NULL ? "localhost" : RealHostName, 16077349Sgshapiro recip); 16164562Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 16264562Sgshapiro { 16364562Sgshapiro if (QS_IS_DEAD(q->q_state)) 16464562Sgshapiro continue; 16564562Sgshapiro q->q_state = QS_BADADDR; 16664562Sgshapiro q->q_status = "5.4.6"; 16777349Sgshapiro q->q_rstatus = "554 5.4.6 Too many hops"; 16864562Sgshapiro } 16938032Speter return; 17038032Speter } 17138032Speter 17238032Speter /* 17338032Speter ** Do sender deletion. 17438032Speter ** 17564562Sgshapiro ** If the sender should be queued up, skip this. 17638032Speter ** This can happen if the name server is hosed when you 17738032Speter ** are trying to send mail. The result is that the sender 17838032Speter ** is instantiated in the queue as a recipient. 17938032Speter */ 18038032Speter 18138032Speter if (!bitset(EF_METOO, e->e_flags) && 18264562Sgshapiro !QS_IS_QUEUEUP(e->e_from.q_state)) 18338032Speter { 18438032Speter if (tTd(13, 5)) 18538032Speter { 18690792Sgshapiro sm_dprintf("sendall: QS_SENDER "); 18790792Sgshapiro printaddr(&e->e_from, false); 18838032Speter } 18964562Sgshapiro e->e_from.q_state = QS_SENDER; 19038032Speter (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); 19138032Speter } 19238032Speter 19338032Speter /* 19438032Speter ** Handle alias owners. 19538032Speter ** 19638032Speter ** We scan up the q_alias chain looking for owners. 19738032Speter ** We discard owners that are the same as the return path. 19838032Speter */ 19938032Speter 20038032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 20138032Speter { 20238032Speter register struct address *a; 20338032Speter 20438032Speter for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) 20538032Speter continue; 20638032Speter if (a != NULL) 20738032Speter q->q_owner = a->q_owner; 20838032Speter 20938032Speter if (q->q_owner != NULL && 21064562Sgshapiro !QS_IS_DEAD(q->q_state) && 21138032Speter strcmp(q->q_owner, e->e_from.q_paddr) == 0) 21238032Speter q->q_owner = NULL; 21338032Speter } 21438032Speter 21538032Speter if (tTd(13, 25)) 21638032Speter { 21790792Sgshapiro sm_dprintf("\nAfter first owner pass, sendq =\n"); 21890792Sgshapiro printaddr(e->e_sendqueue, true); 21938032Speter } 22038032Speter 22138032Speter owner = ""; 22238032Speter otherowners = 1; 22338032Speter while (owner != NULL && otherowners > 0) 22438032Speter { 22538032Speter if (tTd(13, 28)) 22690792Sgshapiro sm_dprintf("owner = \"%s\", otherowners = %d\n", 22790792Sgshapiro owner, otherowners); 22838032Speter owner = NULL; 22938032Speter otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; 23038032Speter 23138032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 23238032Speter { 23338032Speter if (tTd(13, 30)) 23438032Speter { 23590792Sgshapiro sm_dprintf("Checking "); 23690792Sgshapiro printaddr(q, false); 23738032Speter } 23864562Sgshapiro if (QS_IS_DEAD(q->q_state)) 23938032Speter { 24038032Speter if (tTd(13, 30)) 24190792Sgshapiro sm_dprintf(" ... QS_IS_DEAD\n"); 24238032Speter continue; 24338032Speter } 24438032Speter if (tTd(13, 29) && !tTd(13, 30)) 24538032Speter { 24690792Sgshapiro sm_dprintf("Checking "); 24790792Sgshapiro printaddr(q, false); 24838032Speter } 24938032Speter 25038032Speter if (q->q_owner != NULL) 25138032Speter { 25238032Speter if (owner == NULL) 25338032Speter { 25438032Speter if (tTd(13, 40)) 25590792Sgshapiro sm_dprintf(" ... First owner = \"%s\"\n", 25690792Sgshapiro q->q_owner); 25738032Speter owner = q->q_owner; 25838032Speter } 25938032Speter else if (owner != q->q_owner) 26038032Speter { 26138032Speter if (strcmp(owner, q->q_owner) == 0) 26238032Speter { 26338032Speter if (tTd(13, 40)) 26490792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 26590792Sgshapiro owner); 26638032Speter 26738032Speter /* make future comparisons cheap */ 26838032Speter q->q_owner = owner; 26938032Speter } 27038032Speter else 27138032Speter { 27238032Speter if (tTd(13, 40)) 27390792Sgshapiro sm_dprintf(" ... Another owner \"%s\"\n", 27490792Sgshapiro q->q_owner); 27538032Speter otherowners++; 27638032Speter } 27738032Speter owner = q->q_owner; 27838032Speter } 27938032Speter else if (tTd(13, 40)) 28090792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 28190792Sgshapiro owner); 28238032Speter } 28338032Speter else 28438032Speter { 28538032Speter if (tTd(13, 40)) 28690792Sgshapiro sm_dprintf(" ... Null owner\n"); 28738032Speter otherowners++; 28838032Speter } 28938032Speter 29064562Sgshapiro if (QS_IS_BADADDR(q->q_state)) 29164562Sgshapiro { 29264562Sgshapiro if (tTd(13, 30)) 29390792Sgshapiro sm_dprintf(" ... QS_IS_BADADDR\n"); 29464562Sgshapiro continue; 29564562Sgshapiro } 29664562Sgshapiro 29764562Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 29864562Sgshapiro { 29964562Sgshapiro MAILER *m = q->q_mailer; 30064562Sgshapiro 30164562Sgshapiro /* 30264562Sgshapiro ** If we have temporary address failures 30364562Sgshapiro ** (e.g., dns failure) and a fallback MX is 30464562Sgshapiro ** set, send directly to the fallback MX host. 30564562Sgshapiro */ 30664562Sgshapiro 30764562Sgshapiro if (FallBackMX != NULL && 30864562Sgshapiro !wordinclass(FallBackMX, 'w') && 30964562Sgshapiro mode != SM_VERIFY && 31090792Sgshapiro !bitnset(M_NOMX, m->m_flags) && 31190792Sgshapiro strcmp(m->m_mailer, "[IPC]") == 0 && 31264562Sgshapiro m->m_argv[0] != NULL && 31390792Sgshapiro strcmp(m->m_argv[0], "TCP") == 0) 31464562Sgshapiro { 31564562Sgshapiro int len; 31664562Sgshapiro char *p; 31764562Sgshapiro 31864562Sgshapiro if (tTd(13, 30)) 31990792Sgshapiro sm_dprintf(" ... FallBackMX\n"); 32064562Sgshapiro 32190792Sgshapiro len = strlen(FallBackMX) + 1; 32290792Sgshapiro p = sm_rpool_malloc_x(e->e_rpool, len); 32390792Sgshapiro (void) sm_strlcpy(p, FallBackMX, len); 32464562Sgshapiro q->q_state = QS_OK; 32564562Sgshapiro q->q_host = p; 32664562Sgshapiro } 32764562Sgshapiro else 32864562Sgshapiro { 32964562Sgshapiro if (tTd(13, 30)) 33090792Sgshapiro sm_dprintf(" ... QS_IS_QUEUEUP\n"); 33164562Sgshapiro continue; 33264562Sgshapiro } 33364562Sgshapiro } 33464562Sgshapiro 33538032Speter /* 33638032Speter ** If this mailer is expensive, and if we don't 33738032Speter ** want to make connections now, just mark these 33838032Speter ** addresses and return. This is useful if we 33938032Speter ** want to batch connections to reduce load. This 34038032Speter ** will cause the messages to be queued up, and a 34138032Speter ** daemon will come along to send the messages later. 34238032Speter */ 34338032Speter 34464562Sgshapiro if (NoConnect && !Verbose && 34564562Sgshapiro bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) 34638032Speter { 34738032Speter if (tTd(13, 30)) 34890792Sgshapiro sm_dprintf(" ... expensive\n"); 34964562Sgshapiro q->q_state = QS_QUEUEUP; 35090792Sgshapiro expensive = true; 35138032Speter } 35264562Sgshapiro else if (bitnset(M_HOLD, q->q_mailer->m_flags) && 35364562Sgshapiro QueueLimitId == NULL && 35464562Sgshapiro QueueLimitSender == NULL && 35564562Sgshapiro QueueLimitRecipient == NULL) 35638032Speter { 35738032Speter if (tTd(13, 30)) 35890792Sgshapiro sm_dprintf(" ... hold\n"); 35964562Sgshapiro q->q_state = QS_QUEUEUP; 36090792Sgshapiro expensive = true; 36138032Speter } 36290792Sgshapiro#if _FFR_QUARANTINE 36390792Sgshapiro else if (QueueMode != QM_QUARANTINE && 36490792Sgshapiro e->e_quarmsg != NULL) 36590792Sgshapiro { 36690792Sgshapiro if (tTd(13, 30)) 36790792Sgshapiro sm_dprintf(" ... quarantine: %s\n", 36890792Sgshapiro e->e_quarmsg); 36990792Sgshapiro q->q_state = QS_QUEUEUP; 37090792Sgshapiro expensive = true; 37190792Sgshapiro } 37290792Sgshapiro#endif /* _FFR_QUARANTINE */ 37338032Speter else 37438032Speter { 37538032Speter if (tTd(13, 30)) 37690792Sgshapiro sm_dprintf(" ... deliverable\n"); 37790792Sgshapiro somedeliveries = true; 37838032Speter } 37938032Speter } 38038032Speter 38138032Speter if (owner != NULL && otherowners > 0) 38238032Speter { 38338032Speter /* 38438032Speter ** Split this envelope into two. 38538032Speter */ 38638032Speter 38790792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 38890792Sgshapiro sizeof *ee); 38990792Sgshapiro STRUCTCOPY(*e, *ee); 39064562Sgshapiro ee->e_message = NULL; 39138032Speter ee->e_id = NULL; 39264562Sgshapiro assign_queueid(ee); 39338032Speter 39438032Speter if (tTd(13, 1)) 39590792Sgshapiro sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 39690792Sgshapiro e->e_id, ee->e_id, owner, 39790792Sgshapiro otherowners); 39838032Speter 39990792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 40090792Sgshapiro ee->e_sendqueue = copyqueue(e->e_sendqueue, 40190792Sgshapiro ee->e_rpool); 40290792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, 40390792Sgshapiro ee->e_rpool); 40438032Speter ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 40538032Speter ee->e_flags |= EF_NORECEIPT; 40690792Sgshapiro setsender(owner, ee, NULL, '\0', true); 40738032Speter if (tTd(13, 5)) 40838032Speter { 40990792Sgshapiro sm_dprintf("sendall(split): QS_SENDER "); 41090792Sgshapiro printaddr(&ee->e_from, false); 41138032Speter } 41264562Sgshapiro ee->e_from.q_state = QS_SENDER; 41338032Speter ee->e_dfp = NULL; 41464562Sgshapiro ee->e_lockfp = NULL; 41538032Speter ee->e_xfp = NULL; 41690792Sgshapiro ee->e_qgrp = e->e_qgrp; 41790792Sgshapiro ee->e_qdir = e->e_qdir; 41838032Speter ee->e_errormode = EM_MAIL; 41938032Speter ee->e_sibling = splitenv; 42064562Sgshapiro ee->e_statmsg = NULL; 42190792Sgshapiro#if _FFR_QUARANTINE 42290792Sgshapiro if (e->e_quarmsg != NULL) 42390792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 42490792Sgshapiro e->e_quarmsg); 42590792Sgshapiro#endif /* _FFR_QUARANTINE */ 42638032Speter splitenv = ee; 42738032Speter 42838032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 42938032Speter { 43038032Speter if (q->q_owner == owner) 43138032Speter { 43264562Sgshapiro q->q_state = QS_CLONED; 43338032Speter if (tTd(13, 6)) 43490792Sgshapiro sm_dprintf("\t... stripping %s from original envelope\n", 43590792Sgshapiro q->q_paddr); 43638032Speter } 43738032Speter } 43838032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 43938032Speter { 44038032Speter if (q->q_owner != owner) 44138032Speter { 44264562Sgshapiro q->q_state = QS_CLONED; 44338032Speter if (tTd(13, 6)) 44490792Sgshapiro sm_dprintf("\t... dropping %s from cloned envelope\n", 44590792Sgshapiro q->q_paddr); 44638032Speter } 44738032Speter else 44838032Speter { 44938032Speter /* clear DSN parameters */ 45038032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 45138032Speter q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 45238032Speter if (tTd(13, 6)) 45390792Sgshapiro sm_dprintf("\t... moving %s to cloned envelope\n", 45490792Sgshapiro q->q_paddr); 45538032Speter } 45638032Speter } 45738032Speter 45838032Speter if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 45990792Sgshapiro dup_queue_file(e, ee, DATAFL_LETTER); 46064562Sgshapiro 46164562Sgshapiro /* 46264562Sgshapiro ** Give the split envelope access to the parent 46364562Sgshapiro ** transcript file for errors obtained while 46464562Sgshapiro ** processing the recipients (done before the 46564562Sgshapiro ** envelope splitting). 46664562Sgshapiro */ 46764562Sgshapiro 46864562Sgshapiro if (e->e_xfp != NULL) 46990792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 47064562Sgshapiro 47164562Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 47264562Sgshapiro if (ee->e_xfp == NULL) 47364562Sgshapiro openxscript(ee); 47464562Sgshapiro 47542575Speter if (mode != SM_VERIFY && LogLevel > 4) 47690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 47790792Sgshapiro "%s: clone: owner=%s", 47890792Sgshapiro ee->e_id, owner); 47938032Speter } 48038032Speter } 48138032Speter 48238032Speter if (owner != NULL) 48338032Speter { 48490792Sgshapiro setsender(owner, e, NULL, '\0', true); 48538032Speter if (tTd(13, 5)) 48638032Speter { 48790792Sgshapiro sm_dprintf("sendall(owner): QS_SENDER "); 48890792Sgshapiro printaddr(&e->e_from, false); 48938032Speter } 49064562Sgshapiro e->e_from.q_state = QS_SENDER; 49138032Speter e->e_errormode = EM_MAIL; 49238032Speter e->e_flags |= EF_NORECEIPT; 49338032Speter e->e_flags &= ~EF_FATALERRS; 49438032Speter } 49538032Speter 49638032Speter /* if nothing to be delivered, just queue up everything */ 49790792Sgshapiro if (!somedeliveries && !WILL_BE_QUEUED(mode) && 49838032Speter mode != SM_VERIFY) 49938032Speter { 50090792Sgshapiro time_t now; 50171345Sgshapiro 50238032Speter if (tTd(13, 29)) 50390792Sgshapiro sm_dprintf("No deliveries: auto-queuing\n"); 50438032Speter mode = SM_QUEUE; 50590792Sgshapiro now = curtime(); 50638032Speter 50738032Speter /* treat this as a delivery in terms of counting tries */ 50871345Sgshapiro e->e_dtime = now; 50938032Speter if (!expensive) 51038032Speter e->e_ntries++; 51138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 51238032Speter { 51371345Sgshapiro ee->e_dtime = now; 51438032Speter if (!expensive) 51538032Speter ee->e_ntries++; 51638032Speter } 51738032Speter } 51838032Speter 51990792Sgshapiro if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 52090792Sgshapiro (mode != SM_VERIFY && SuperSafe == SAFE_REALLY)) && 52138032Speter (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 52238032Speter { 52390792Sgshapiro bool msync; 52490792Sgshapiro 52566494Sgshapiro /* 52666494Sgshapiro ** Be sure everything is instantiated in the queue. 52766494Sgshapiro ** Split envelopes first in case the machine crashes. 52866494Sgshapiro ** If the original were done first, we may lose 52966494Sgshapiro ** recipients. 53066494Sgshapiro */ 53166494Sgshapiro 53290792Sgshapiro#if !HASFLOCK 53390792Sgshapiro msync = false; 53490792Sgshapiro#else /* !HASFLOCK */ 53590792Sgshapiro msync = mode == SM_FORK; 53690792Sgshapiro#endif /* !HASFLOCK */ 53790792Sgshapiro 53838032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 53990792Sgshapiro queueup(ee, WILL_BE_QUEUED(mode), msync); 54090792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), msync); 54138032Speter } 54238032Speter 54338032Speter if (tTd(62, 10)) 54438032Speter checkfds("after envelope splitting"); 54538032Speter 54638032Speter /* 54738032Speter ** If we belong in background, fork now. 54838032Speter */ 54938032Speter 55038032Speter if (tTd(13, 20)) 55138032Speter { 55290792Sgshapiro sm_dprintf("sendall: final mode = %c\n", mode); 55338032Speter if (tTd(13, 21)) 55438032Speter { 55590792Sgshapiro sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 55690792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 55790792Sgshapiro e->e_id, e->e_from.q_paddr); 55890792Sgshapiro printaddr(e->e_sendqueue, true); 55938032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 56038032Speter { 56190792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 56290792Sgshapiro ee->e_id, ee->e_from.q_paddr); 56390792Sgshapiro printaddr(ee->e_sendqueue, true); 56438032Speter } 56590792Sgshapiro sm_dprintf("==========================================================\n\n"); 56638032Speter } 56738032Speter } 56838032Speter switch (mode) 56938032Speter { 57038032Speter case SM_VERIFY: 57138032Speter Verbose = 2; 57238032Speter break; 57338032Speter 57438032Speter case SM_QUEUE: 57538032Speter case SM_DEFER: 57664562Sgshapiro#if HASFLOCK 57738032Speter queueonly: 57864562Sgshapiro#endif /* HASFLOCK */ 57938032Speter if (e->e_nrcpts > 0) 58038032Speter e->e_flags |= EF_INQUEUE; 58190792Sgshapiro dropenvelope(e, splitenv != NULL, true); 58238032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 58338032Speter { 58438032Speter if (ee->e_nrcpts > 0) 58538032Speter ee->e_flags |= EF_INQUEUE; 58690792Sgshapiro dropenvelope(ee, false, true); 58738032Speter } 58838032Speter return; 58938032Speter 59038032Speter case SM_FORK: 59138032Speter if (e->e_xfp != NULL) 59290792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 59338032Speter 59464562Sgshapiro#if !HASFLOCK 59538032Speter /* 59638032Speter ** Since fcntl locking has the interesting semantic that 59738032Speter ** the lock is owned by a process, not by an open file 59838032Speter ** descriptor, we have to flush this to the queue, and 59938032Speter ** then restart from scratch in the child. 60038032Speter */ 60138032Speter 60238032Speter { 60338032Speter /* save id for future use */ 60438032Speter char *qid = e->e_id; 60538032Speter 60638032Speter /* now drop the envelope in the parent */ 60738032Speter e->e_flags |= EF_INQUEUE; 60890792Sgshapiro dropenvelope(e, splitenv != NULL, false); 60938032Speter 61038032Speter /* arrange to reacquire lock after fork */ 61138032Speter e->e_id = qid; 61238032Speter } 61338032Speter 61438032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 61538032Speter { 61638032Speter /* save id for future use */ 61738032Speter char *qid = ee->e_id; 61838032Speter 61938032Speter /* drop envelope in parent */ 62038032Speter ee->e_flags |= EF_INQUEUE; 62190792Sgshapiro dropenvelope(ee, false, false); 62238032Speter 62338032Speter /* and save qid for reacquisition */ 62438032Speter ee->e_id = qid; 62538032Speter } 62664562Sgshapiro#endif /* !HASFLOCK */ 62738032Speter 62864562Sgshapiro /* 62964562Sgshapiro ** Since the delivery may happen in a child and the parent 63064562Sgshapiro ** does not wait, the parent may close the maps thereby 63164562Sgshapiro ** removing any shared memory used by the map. Therefore, 63264562Sgshapiro ** close the maps now so the child will dynamically open 63364562Sgshapiro ** them if necessary. 63464562Sgshapiro */ 63564562Sgshapiro 63690792Sgshapiro closemaps(false); 63764562Sgshapiro 63838032Speter pid = fork(); 63938032Speter if (pid < 0) 64038032Speter { 64164562Sgshapiro syserr("deliver: fork 1"); 64264562Sgshapiro#if HASFLOCK 64338032Speter goto queueonly; 64464562Sgshapiro#else /* HASFLOCK */ 64538032Speter e->e_id = NULL; 64638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 64738032Speter ee->e_id = NULL; 64838032Speter return; 64964562Sgshapiro#endif /* HASFLOCK */ 65038032Speter } 65138032Speter else if (pid > 0) 65238032Speter { 65364562Sgshapiro#if HASFLOCK 65438032Speter /* be sure we leave the temp files to our child */ 65538032Speter /* close any random open files in the envelope */ 65638032Speter closexscript(e); 65738032Speter if (e->e_dfp != NULL) 65890792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 65938032Speter e->e_dfp = NULL; 66038032Speter e->e_flags &= ~EF_HAS_DF; 66138032Speter 66238032Speter /* can't call unlockqueue to avoid unlink of xfp */ 66338032Speter if (e->e_lockfp != NULL) 66490792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 66564562Sgshapiro else 66664562Sgshapiro syserr("%s: sendall: null lockfp", e->e_id); 66738032Speter e->e_lockfp = NULL; 66864562Sgshapiro#endif /* HASFLOCK */ 66938032Speter 67038032Speter /* make sure the parent doesn't own the envelope */ 67138032Speter e->e_id = NULL; 67238032Speter 67390792Sgshapiro#if USE_DOUBLE_FORK 67438032Speter /* catch intermediate zombie */ 67538032Speter (void) waitfor(pid); 67690792Sgshapiro#endif /* USE_DOUBLE_FORK */ 67738032Speter return; 67838032Speter } 67938032Speter 68077349Sgshapiro /* Reset global flags */ 68177349Sgshapiro RestartRequest = NULL; 68290792Sgshapiro RestartWorkGroup = false; 68377349Sgshapiro ShutdownRequest = NULL; 68477349Sgshapiro PendingSignal = 0; 68577349Sgshapiro 68666494Sgshapiro /* 68790792Sgshapiro ** Initialize exception stack and default exception 68890792Sgshapiro ** handler for child process. 68990792Sgshapiro */ 69090792Sgshapiro 69190792Sgshapiro sm_exc_newthread(fatal_error); 69290792Sgshapiro 69390792Sgshapiro /* 69466494Sgshapiro ** Since we have accepted responsbility for the message, 69566494Sgshapiro ** change the SIGTERM handler. intsig() (the old handler) 69666494Sgshapiro ** would remove the envelope if this was a command line 69766494Sgshapiro ** message submission. 69866494Sgshapiro */ 69966494Sgshapiro 70090792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 70166494Sgshapiro 70290792Sgshapiro#if USE_DOUBLE_FORK 70338032Speter /* double fork to avoid zombies */ 70438032Speter pid = fork(); 70538032Speter if (pid > 0) 70638032Speter exit(EX_OK); 70764562Sgshapiro save_errno = errno; 70890792Sgshapiro#endif /* USE_DOUBLE_FORK */ 70938032Speter 71090792Sgshapiro CurrentPid = getpid(); 71190792Sgshapiro 71238032Speter /* be sure we are immune from the terminal */ 71338032Speter disconnect(2, e); 71464562Sgshapiro clearstats(); 71538032Speter 71638032Speter /* prevent parent from waiting if there was an error */ 71738032Speter if (pid < 0) 71838032Speter { 71964562Sgshapiro errno = save_errno; 72064562Sgshapiro syserr("deliver: fork 2"); 72164562Sgshapiro#if HASFLOCK 72238032Speter e->e_flags |= EF_INQUEUE; 72364562Sgshapiro#else /* HASFLOCK */ 72438032Speter e->e_id = NULL; 72564562Sgshapiro#endif /* HASFLOCK */ 72690792Sgshapiro finis(true, true, ExitStat); 72738032Speter } 72838032Speter 72938032Speter /* be sure to give error messages in child */ 73090792Sgshapiro QuickAbort = false; 73138032Speter 73238032Speter /* 73338032Speter ** Close any cached connections. 73438032Speter ** 73538032Speter ** We don't send the QUIT protocol because the parent 73638032Speter ** still knows about the connection. 73738032Speter ** 73838032Speter ** This should only happen when delivering an error 73938032Speter ** message. 74038032Speter */ 74138032Speter 74290792Sgshapiro mci_flush(false, NULL); 74338032Speter 74464562Sgshapiro#if HASFLOCK 74538032Speter break; 74664562Sgshapiro#else /* HASFLOCK */ 74738032Speter 74838032Speter /* 74938032Speter ** Now reacquire and run the various queue files. 75038032Speter */ 75138032Speter 75238032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 75338032Speter { 75438032Speter ENVELOPE *sibling = ee->e_sibling; 75538032Speter 75690792Sgshapiro (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 75790792Sgshapiro false, false, ee); 75838032Speter ee->e_sibling = sibling; 75938032Speter } 76090792Sgshapiro (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 76190792Sgshapiro false, false, e); 76290792Sgshapiro finis(true, true, ExitStat); 76364562Sgshapiro#endif /* HASFLOCK */ 76438032Speter } 76538032Speter 76638032Speter sendenvelope(e, mode); 76790792Sgshapiro dropenvelope(e, true, true); 76838032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 76938032Speter { 77038032Speter CurEnv = ee; 77138032Speter if (mode != SM_VERIFY) 77238032Speter openxscript(ee); 77338032Speter sendenvelope(ee, mode); 77490792Sgshapiro dropenvelope(ee, true, true); 77538032Speter } 77638032Speter CurEnv = e; 77738032Speter 77838032Speter Verbose = oldverbose; 77938032Speter if (mode == SM_FORK) 78090792Sgshapiro finis(true, true, ExitStat); 78138032Speter} 78238032Speter 78364562Sgshapirostatic void 78438032Spetersendenvelope(e, mode) 78538032Speter register ENVELOPE *e; 78638032Speter int mode; 78738032Speter{ 78838032Speter register ADDRESS *q; 78938032Speter bool didany; 79038032Speter 79138032Speter if (tTd(13, 10)) 79290792Sgshapiro sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 79390792Sgshapiro e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 79490792Sgshapiro e->e_flags); 79538032Speter if (LogLevel > 80) 79638032Speter sm_syslog(LOG_DEBUG, e->e_id, 79764562Sgshapiro "sendenvelope, flags=0x%lx", 79864562Sgshapiro e->e_flags); 79938032Speter 80038032Speter /* 80138032Speter ** If we have had global, fatal errors, don't bother sending 80238032Speter ** the message at all if we are in SMTP mode. Local errors 80338032Speter ** (e.g., a single address failing) will still cause the other 80438032Speter ** addresses to be sent. 80538032Speter */ 80638032Speter 80738032Speter if (bitset(EF_FATALERRS, e->e_flags) && 80838032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 80938032Speter { 81038032Speter e->e_flags |= EF_CLRQUEUE; 81138032Speter return; 81238032Speter } 81338032Speter 81490792Sgshapiro /* 81590792Sgshapiro ** Don't attempt deliveries if we want to bounce now 81690792Sgshapiro ** or if deliver-by time is exceeded. 81790792Sgshapiro */ 81890792Sgshapiro 81964562Sgshapiro if (!bitset(EF_RESPONSE, e->e_flags) && 82090792Sgshapiro (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 82190792Sgshapiro (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 82290792Sgshapiro curtime() > e->e_ctime + e->e_deliver_by))) 82364562Sgshapiro return; 82464562Sgshapiro 82538032Speter /* 82638032Speter ** Run through the list and send everything. 82738032Speter ** 82838032Speter ** Set EF_GLOBALERRS so that error messages during delivery 82938032Speter ** result in returned mail. 83038032Speter */ 83138032Speter 83238032Speter e->e_nsent = 0; 83338032Speter e->e_flags |= EF_GLOBALERRS; 83464562Sgshapiro 83590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 83690792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 83790792Sgshapiro didany = false; 83838032Speter 83990792Sgshapiro if (!bitset(EF_SPLIT, e->e_flags)) 84090792Sgshapiro { 84190792Sgshapiro ENVELOPE *oldsib; 84290792Sgshapiro ENVELOPE *ee; 84390792Sgshapiro 84490792Sgshapiro /* 84590792Sgshapiro ** Save old sibling and set it to NULL to avoid 84690792Sgshapiro ** queueing up the same envelopes again. 84790792Sgshapiro ** This requires that envelopes in that list have 84890792Sgshapiro ** been take care of before (or at some other place). 84990792Sgshapiro */ 85090792Sgshapiro 85190792Sgshapiro oldsib = e->e_sibling; 85290792Sgshapiro e->e_sibling = NULL; 85390792Sgshapiro if (!split_by_recipient(e) && 85490792Sgshapiro bitset(EF_FATALERRS, e->e_flags)) 85590792Sgshapiro { 85690792Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 85790792Sgshapiro e->e_flags |= EF_CLRQUEUE; 85890792Sgshapiro return; 85990792Sgshapiro } 86090792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86190792Sgshapiro queueup(ee, false, true); 86290792Sgshapiro 86390792Sgshapiro /* clean up */ 86490792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86590792Sgshapiro { 86690792Sgshapiro /* now unlock the job */ 86790792Sgshapiro closexscript(ee); 86890792Sgshapiro unlockqueue(ee); 86990792Sgshapiro 87090792Sgshapiro /* this envelope is marked unused */ 87190792Sgshapiro if (ee->e_dfp != NULL) 87290792Sgshapiro { 87390792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 87490792Sgshapiro ee->e_dfp = NULL; 87590792Sgshapiro } 87690792Sgshapiro ee->e_id = NULL; 87790792Sgshapiro ee->e_flags &= ~EF_HAS_DF; 87890792Sgshapiro } 87990792Sgshapiro e->e_sibling = oldsib; 88090792Sgshapiro } 88190792Sgshapiro 88238032Speter /* now run through the queue */ 88338032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 88438032Speter { 88538032Speter#if XDEBUG 88638032Speter char wbuf[MAXNAME + 20]; 88738032Speter 88890792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", 88990792Sgshapiro MAXNAME, q->q_paddr); 89038032Speter checkfd012(wbuf); 89164562Sgshapiro#endif /* XDEBUG */ 89238032Speter if (mode == SM_VERIFY) 89338032Speter { 89438032Speter e->e_to = q->q_paddr; 89564562Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 89638032Speter { 89738032Speter if (q->q_host != NULL && q->q_host[0] != '\0') 89838032Speter message("deliverable: mailer %s, host %s, user %s", 89938032Speter q->q_mailer->m_name, 90038032Speter q->q_host, 90138032Speter q->q_user); 90238032Speter else 90338032Speter message("deliverable: mailer %s, user %s", 90438032Speter q->q_mailer->m_name, 90538032Speter q->q_user); 90638032Speter } 90738032Speter } 90864562Sgshapiro else if (QS_IS_OK(q->q_state)) 90938032Speter { 91038032Speter /* 91138032Speter ** Checkpoint the send list every few addresses 91238032Speter */ 91338032Speter 91466494Sgshapiro if (CheckpointInterval > 0 && 91566494Sgshapiro e->e_nsent >= CheckpointInterval) 91638032Speter { 91790792Sgshapiro queueup(e, false, false); 91838032Speter e->e_nsent = 0; 91938032Speter } 92038032Speter (void) deliver(e, q); 92190792Sgshapiro didany = true; 92238032Speter } 92338032Speter } 92438032Speter if (didany) 92538032Speter { 92638032Speter e->e_dtime = curtime(); 92738032Speter e->e_ntries++; 92838032Speter } 92938032Speter 93038032Speter#if XDEBUG 93138032Speter checkfd012("end of sendenvelope"); 93264562Sgshapiro#endif /* XDEBUG */ 93338032Speter} 93490792Sgshapiro 93590792Sgshapiro#if REQUIRES_DIR_FSYNC 93690792Sgshapiro/* 93790792Sgshapiro** SYNC_DIR -- fsync a directory based on a filename 93890792Sgshapiro** 93990792Sgshapiro** Parameters: 94090792Sgshapiro** filename -- path of file 94190792Sgshapiro** panic -- panic? 94290792Sgshapiro** 94390792Sgshapiro** Returns: 94490792Sgshapiro** none 94590792Sgshapiro*/ 94690792Sgshapiro 94790792Sgshapirovoid 94890792Sgshapirosync_dir(filename, panic) 94990792Sgshapiro char *filename; 95090792Sgshapiro bool panic; 95190792Sgshapiro{ 95290792Sgshapiro int dirfd; 95390792Sgshapiro char *dirp; 95490792Sgshapiro char dir[MAXPATHLEN]; 95590792Sgshapiro 956110560Sgshapiro#if _FFR_REQ_DIR_FSYNC_OPT 957110560Sgshapiro if (!RequiresDirfsync) 958110560Sgshapiro return; 959110560Sgshapiro#endif /* _FFR_REQ_DIR_FSYNC_OPT */ 960110560Sgshapiro 96190792Sgshapiro /* filesystems which require the directory be synced */ 96290792Sgshapiro dirp = strrchr(filename, '/'); 96390792Sgshapiro if (dirp != NULL) 96490792Sgshapiro { 96590792Sgshapiro if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir) 96690792Sgshapiro return; 96790792Sgshapiro dir[dirp - filename] = '\0'; 96890792Sgshapiro dirp = dir; 96990792Sgshapiro } 97090792Sgshapiro else 97190792Sgshapiro dirp = "."; 97290792Sgshapiro dirfd = open(dirp, O_RDONLY, 0700); 97390792Sgshapiro if (tTd(40,32)) 97490792Sgshapiro sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 97590792Sgshapiro dirp, dirfd); 97690792Sgshapiro if (dirfd >= 0) 97790792Sgshapiro { 97890792Sgshapiro if (fsync(dirfd) < 0) 97990792Sgshapiro { 98090792Sgshapiro if (panic) 98190792Sgshapiro syserr("!sync_dir: cannot fsync directory %s", 98290792Sgshapiro dirp); 98390792Sgshapiro else if (LogLevel > 1) 98490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 98590792Sgshapiro "sync_dir: cannot fsync directory %s: %s", 98690792Sgshapiro dirp, sm_errstring(errno)); 98790792Sgshapiro } 98890792Sgshapiro (void) close(dirfd); 98990792Sgshapiro } 99090792Sgshapiro} 99190792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */ 99290792Sgshapiro/* 99338032Speter** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 99438032Speter** 99538032Speter** Parameters: 99638032Speter** e -- the existing envelope 99738032Speter** ee -- the new envelope 99890792Sgshapiro** type -- the queue file type (e.g., DATAFL_LETTER) 99938032Speter** 100038032Speter** Returns: 100138032Speter** none 100238032Speter*/ 100338032Speter 100464562Sgshapirostatic void 100538032Speterdup_queue_file(e, ee, type) 100690792Sgshapiro ENVELOPE *e, *ee; 100738032Speter int type; 100838032Speter{ 100964562Sgshapiro char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 101038032Speter 101138032Speter ee->e_dfp = NULL; 101238032Speter ee->e_xfp = NULL; 101364562Sgshapiro 101464562Sgshapiro /* 101564562Sgshapiro ** Make sure both are in the same directory. 101664562Sgshapiro */ 101764562Sgshapiro 101890792Sgshapiro (void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf); 101990792Sgshapiro (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf); 1020102528Sgshapiro 1021102528Sgshapiro /* Force the df to disk if it's not there yet */ 1022102528Sgshapiro if (type == DATAFL_LETTER && e->e_dfp != NULL && 1023102528Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 1024102528Sgshapiro errno != EINVAL) 1025102528Sgshapiro { 1026102528Sgshapiro syserr("!dup_queue_file: can't commit %s", f1buf); 1027102528Sgshapiro /* NOTREACHED */ 1028102528Sgshapiro } 1029102528Sgshapiro 103038032Speter if (link(f1buf, f2buf) < 0) 103138032Speter { 103264562Sgshapiro int save_errno = errno; 103338032Speter 103438032Speter syserr("sendall: link(%s, %s)", f1buf, f2buf); 103564562Sgshapiro if (save_errno == EEXIST) 103638032Speter { 103738032Speter if (unlink(f2buf) < 0) 103838032Speter { 103938032Speter syserr("!sendall: unlink(%s): permanent", 104090792Sgshapiro f2buf); 104164562Sgshapiro /* NOTREACHED */ 104238032Speter } 104338032Speter if (link(f1buf, f2buf) < 0) 104438032Speter { 104538032Speter syserr("!sendall: link(%s, %s): permanent", 104690792Sgshapiro f1buf, f2buf); 104764562Sgshapiro /* NOTREACHED */ 104838032Speter } 104938032Speter } 105038032Speter } 105190792Sgshapiro SYNC_DIR(f2buf, true); 105238032Speter} 105390792Sgshapiro/* 105438032Speter** DOFORK -- do a fork, retrying a couple of times on failure. 105538032Speter** 105638032Speter** This MUST be a macro, since after a vfork we are running 105738032Speter** two processes on the same stack!!! 105838032Speter** 105938032Speter** Parameters: 106038032Speter** none. 106138032Speter** 106238032Speter** Returns: 106338032Speter** From a macro??? You've got to be kidding! 106438032Speter** 106538032Speter** Side Effects: 106638032Speter** Modifies the ==> LOCAL <== variable 'pid', leaving: 106738032Speter** pid of child in parent, zero in child. 106838032Speter** -1 on unrecoverable error. 106938032Speter** 107038032Speter** Notes: 107138032Speter** I'm awfully sorry this looks so awful. That's 107238032Speter** vfork for you..... 107338032Speter*/ 107438032Speter 107564562Sgshapiro#define NFORKTRIES 5 107638032Speter 107764562Sgshapiro#ifndef FORK 107838032Speter# define FORK fork 107964562Sgshapiro#endif /* ! FORK */ 108038032Speter 108164562Sgshapiro#define DOFORK(fORKfN) \ 108238032Speter{\ 108338032Speter register int i;\ 108438032Speter\ 108538032Speter for (i = NFORKTRIES; --i >= 0; )\ 108638032Speter {\ 108738032Speter pid = fORKfN();\ 108838032Speter if (pid >= 0)\ 108938032Speter break;\ 109038032Speter if (i > 0)\ 109164562Sgshapiro (void) sleep((unsigned) NFORKTRIES - i);\ 109238032Speter }\ 109338032Speter} 109490792Sgshapiro/* 109538032Speter** DOFORK -- simple fork interface to DOFORK. 109638032Speter** 109738032Speter** Parameters: 109838032Speter** none. 109938032Speter** 110038032Speter** Returns: 110138032Speter** pid of child in parent. 110238032Speter** zero in child. 110338032Speter** -1 on error. 110438032Speter** 110538032Speter** Side Effects: 110638032Speter** returns twice, once in parent and once in child. 110738032Speter*/ 110838032Speter 110977349Sgshapiropid_t 111038032Speterdofork() 111138032Speter{ 111238032Speter register pid_t pid = -1; 111338032Speter 111438032Speter DOFORK(fork); 111564562Sgshapiro return pid; 111638032Speter} 111790792Sgshapiro 111890792Sgshapiro/* 111990792Sgshapiro** COLONCMP -- compare host-signatures up to first ':' or EOS 112090792Sgshapiro** 112190792Sgshapiro** This takes two strings which happen to be host-signatures and 112290792Sgshapiro** compares them. If the lowest preference portions of the MX-RR's 112390792Sgshapiro** match (up to ':' or EOS, whichever is first), then we have 112490792Sgshapiro** match. This is used for coattail-piggybacking messages during 112590792Sgshapiro** message delivery. 112690792Sgshapiro** If the signatures are the same up to the first ':' the remainder of 112790792Sgshapiro** the signatures are then compared with a normal strcmp(). This saves 112890792Sgshapiro** re-examining the first part of the signatures. 112990792Sgshapiro** 113090792Sgshapiro** Parameters: 113190792Sgshapiro** a - first host-signature 113290792Sgshapiro** b - second host-signature 113390792Sgshapiro** 113490792Sgshapiro** Returns: 113590792Sgshapiro** HS_MATCH_NO -- no "match". 113690792Sgshapiro** HS_MATCH_FIRST -- "match" for the first MX preference 113790792Sgshapiro** (up to the first colon (':')). 113890792Sgshapiro** HS_MATCH_FULL -- match for the entire MX record. 113990792Sgshapiro** 114090792Sgshapiro** Side Effects: 114190792Sgshapiro** none. 114290792Sgshapiro*/ 114390792Sgshapiro 114490792Sgshapiro#define HS_MATCH_NO 0 114590792Sgshapiro#define HS_MATCH_FIRST 1 114690792Sgshapiro#define HS_MATCH_FULL 2 114790792Sgshapiro 114890792Sgshapirostatic int 114990792Sgshapirocoloncmp(a, b) 115090792Sgshapiro register const char *a; 115190792Sgshapiro register const char *b; 115290792Sgshapiro{ 115390792Sgshapiro int ret = HS_MATCH_NO; 115490792Sgshapiro int braclev = 0; 115590792Sgshapiro 115690792Sgshapiro while (*a == *b++) 115790792Sgshapiro { 115890792Sgshapiro /* Need to account for IPv6 bracketed addresses */ 115990792Sgshapiro if (*a == '[') 116090792Sgshapiro braclev++; 1161112810Sgshapiro else if (*a == ']' && braclev > 0) 116290792Sgshapiro braclev--; 116390792Sgshapiro else if (*a == ':' && braclev <= 0) 116490792Sgshapiro { 116590792Sgshapiro ret = HS_MATCH_FIRST; 116690792Sgshapiro a++; 116790792Sgshapiro break; 116890792Sgshapiro } 116990792Sgshapiro else if (*a == '\0') 117090792Sgshapiro return HS_MATCH_FULL; /* a full match */ 117190792Sgshapiro a++; 117290792Sgshapiro } 117390792Sgshapiro if (ret == HS_MATCH_NO && 117490792Sgshapiro braclev <= 0 && 117590792Sgshapiro ((*a == '\0' && *(b - 1) == ':') || 117690792Sgshapiro (*a == ':' && *(b - 1) == '\0'))) 117790792Sgshapiro return HS_MATCH_FIRST; 117890792Sgshapiro if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 117990792Sgshapiro return HS_MATCH_FULL; 118090792Sgshapiro 118190792Sgshapiro return ret; 118290792Sgshapiro} 118390792Sgshapiro/* 118438032Speter** DELIVER -- Deliver a message to a list of addresses. 118538032Speter** 118638032Speter** This routine delivers to everyone on the same host as the 118738032Speter** user on the head of the list. It is clever about mailers 118838032Speter** that don't handle multiple users. It is NOT guaranteed 118938032Speter** that it will deliver to all these addresses however -- so 119038032Speter** deliver should be called once for each address on the 119138032Speter** list. 119290792Sgshapiro** Deliver tries to be as opportunistic as possible about piggybacking 119390792Sgshapiro** messages. Some definitions to make understanding easier follow below. 119490792Sgshapiro** Piggybacking occurs when an existing connection to a mail host can 119590792Sgshapiro** be used to send the same message to more than one recipient at the 119690792Sgshapiro** same time. So "no piggybacking" means one message for one recipient 119790792Sgshapiro** per connection. "Intentional piggybacking" happens when the 119890792Sgshapiro** recipients' host address (not the mail host address) is used to 119990792Sgshapiro** attempt piggybacking. Recipients with the same host address 120090792Sgshapiro** have the same mail host. "Coincidental piggybacking" relies on 120190792Sgshapiro** piggybacking based on all the mail host addresses in the MX-RR. This 120290792Sgshapiro** is "coincidental" in the fact it could not be predicted until the 120390792Sgshapiro** MX Resource Records for the hosts were obtained and examined. For 120490792Sgshapiro** example (preference order and equivalence is important, not values): 120590792Sgshapiro** domain1 IN MX 10 mxhost-A 120690792Sgshapiro** IN MX 20 mxhost-B 120790792Sgshapiro** domain2 IN MX 4 mxhost-A 120890792Sgshapiro** IN MX 8 mxhost-B 120990792Sgshapiro** Domain1 and domain2 can piggyback the same message to mxhost-A or 121090792Sgshapiro** mxhost-B (if mxhost-A cannot be reached). 121190792Sgshapiro** "Coattail piggybacking" relaxes the strictness of "coincidental 121290792Sgshapiro** piggybacking" in the hope that most significant (lowest value) 121390792Sgshapiro** MX preference host(s) can create more piggybacking. For example 121490792Sgshapiro** (again, preference order and equivalence is important, not values): 121590792Sgshapiro** domain3 IN MX 100 mxhost-C 121690792Sgshapiro** IN MX 100 mxhost-D 121790792Sgshapiro** IN MX 200 mxhost-E 121890792Sgshapiro** domain4 IN MX 50 mxhost-C 121990792Sgshapiro** IN MX 50 mxhost-D 122090792Sgshapiro** IN MX 80 mxhost-F 122190792Sgshapiro** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 122290792Sgshapiro** is available. Same with mxhost-D because in both RR's the preference 122390792Sgshapiro** value is the same as mxhost-C, respectively. 122490792Sgshapiro** So deliver attempts coattail piggybacking when possible. If the 122590792Sgshapiro** first MX preference level hosts cannot be used then the piggybacking 122690792Sgshapiro** reverts to coincidental piggybacking. Using the above example you 122790792Sgshapiro** cannot deliver to mxhost-F for domain3 regardless of preference value. 122890792Sgshapiro** ("Coattail" from "riding on the coattails of your predecessor" meaning 122990792Sgshapiro** gaining benefit from a predecessor effort with no or little addition 123090792Sgshapiro** effort. The predecessor here being the preceding MX RR). 123138032Speter** 123238032Speter** Parameters: 123338032Speter** e -- the envelope to deliver. 123438032Speter** firstto -- head of the address list to deliver to. 123538032Speter** 123638032Speter** Returns: 123738032Speter** zero -- successfully delivered. 123838032Speter** else -- some failure, see ExitStat for more info. 123938032Speter** 124038032Speter** Side Effects: 124138032Speter** The standard input is passed off to someone. 124238032Speter*/ 124338032Speter 124438032Speter#ifndef NO_UID 124538032Speter# define NO_UID -1 124664562Sgshapiro#endif /* ! NO_UID */ 124738032Speter#ifndef NO_GID 124838032Speter# define NO_GID -1 124964562Sgshapiro#endif /* ! NO_GID */ 125038032Speter 125164562Sgshapirostatic int 125238032Speterdeliver(e, firstto) 125338032Speter register ENVELOPE *e; 125438032Speter ADDRESS *firstto; 125538032Speter{ 125638032Speter char *host; /* host being sent to */ 125738032Speter char *user; /* user being sent to */ 125838032Speter char **pvp; 125938032Speter register char **mvp; 126038032Speter register char *p; 126138032Speter register MAILER *m; /* mailer for this recipient */ 126238032Speter ADDRESS *volatile ctladdr; 126390792Sgshapiro#if HASSETUSERCONTEXT 126438032Speter ADDRESS *volatile contextaddr = NULL; 126590792Sgshapiro#endif /* HASSETUSERCONTEXT */ 126638032Speter register MCI *volatile mci; 126790792Sgshapiro register ADDRESS *SM_NONVOLATILE to = firstto; 126890792Sgshapiro volatile bool clever = false; /* running user smtp to this mailer */ 126938032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 127038032Speter int rcode; /* response code */ 127190792Sgshapiro SM_NONVOLATILE int lmtp_rcode = EX_OK; 127290792Sgshapiro SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 127390792Sgshapiro SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 127438032Speter char *firstsig; /* signature of firstto */ 127590792Sgshapiro volatile pid_t pid = -1; 127638032Speter char *volatile curhost; 127790792Sgshapiro SM_NONVOLATILE unsigned short port = 0; 127890792Sgshapiro SM_NONVOLATILE time_t enough = 0; 127964562Sgshapiro#if NETUNIX 128090792Sgshapiro char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 128164562Sgshapiro#endif /* NETUNIX */ 128238032Speter time_t xstart; 128338032Speter bool suidwarn; 128438032Speter bool anyok; /* at least one address was OK */ 128590792Sgshapiro SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 128664562Sgshapiro bool ovr; 128790792Sgshapiro#if _FFR_QUARANTINE 128890792Sgshapiro bool quarantine; 128990792Sgshapiro#endif /* _FFR_QUARANTINE */ 129064562Sgshapiro int strsize; 129164562Sgshapiro int rcptcount; 129290792Sgshapiro int ret; 129364562Sgshapiro static int tobufsize = 0; 129464562Sgshapiro static char *tobuf = NULL; 129590792Sgshapiro char *rpath; /* translated return path */ 129638032Speter int mpvect[2]; 129738032Speter int rpvect[2]; 129864562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 129964562Sgshapiro char *pv[MAXPV + 1]; 130038032Speter char buf[MAXNAME + 1]; 130198121Sgshapiro char cbuf[MAXPATHLEN]; 130238032Speter 130338032Speter errno = 0; 130464562Sgshapiro if (!QS_IS_OK(to->q_state)) 130564562Sgshapiro return 0; 130638032Speter 130738032Speter suidwarn = geteuid() == 0; 130838032Speter 130938032Speter m = to->q_mailer; 131038032Speter host = to->q_host; 131138032Speter CurEnv = e; /* just in case */ 131238032Speter e->e_statmsg = NULL; 131338032Speter SmtpError[0] = '\0'; 131438032Speter xstart = curtime(); 131538032Speter 131638032Speter if (tTd(10, 1)) 131790792Sgshapiro sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 131838032Speter e->e_id, m->m_name, host, to->q_user); 131938032Speter if (tTd(10, 100)) 132090792Sgshapiro printopenfds(false); 132138032Speter 132238032Speter /* 132390792Sgshapiro ** Clear {client_*} macros if this is a bounce message to 132438032Speter ** prevent rejection by check_compat ruleset. 132538032Speter */ 132664562Sgshapiro 132738032Speter if (bitset(EF_RESPONSE, e->e_flags)) 132838032Speter { 132990792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 133090792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 133190792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 133290792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 133338032Speter } 133464562Sgshapiro 133590792Sgshapiro SM_TRY 133690792Sgshapiro { 133790792Sgshapiro ADDRESS *skip_back = NULL; 133890792Sgshapiro 133938032Speter /* 134038032Speter ** Do initial argv setup. 134138032Speter ** Insert the mailer name. Notice that $x expansion is 134238032Speter ** NOT done on the mailer name. Then, if the mailer has 134338032Speter ** a picky -f flag, we insert it as appropriate. This 134438032Speter ** code does not check for 'pv' overflow; this places a 134538032Speter ** manifest lower limit of 4 for MAXPV. 134638032Speter ** The from address rewrite is expected to make 134738032Speter ** the address relative to the other end. 134838032Speter */ 134938032Speter 135038032Speter /* rewrite from address, using rewriting rules */ 135138032Speter rcode = EX_OK; 135238032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 135338032Speter p = e->e_sender; 135438032Speter else 135538032Speter p = e->e_from.q_paddr; 135690792Sgshapiro rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 135790792Sgshapiro if (strlen(rpath) > MAXSHORTSTR) 135838032Speter { 135990792Sgshapiro rpath = shortenstring(rpath, MAXSHORTSTR); 136090792Sgshapiro 136190792Sgshapiro /* avoid bogus errno */ 136290792Sgshapiro errno = 0; 136390792Sgshapiro syserr("remotename: huge return path %s", rpath); 136438032Speter } 136590792Sgshapiro rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 136690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', rpath); 136790792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', host); 136838032Speter Errors = 0; 136938032Speter pvp = pv; 137038032Speter *pvp++ = m->m_argv[0]; 137138032Speter 137238032Speter /* insert -f or -r flag as appropriate */ 137364562Sgshapiro if (FromFlag && 137464562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 137564562Sgshapiro bitnset(M_ROPT, m->m_flags))) 137638032Speter { 137738032Speter if (bitnset(M_FOPT, m->m_flags)) 137838032Speter *pvp++ = "-f"; 137938032Speter else 138038032Speter *pvp++ = "-r"; 138190792Sgshapiro *pvp++ = rpath; 138238032Speter } 138338032Speter 138438032Speter /* 138538032Speter ** Append the other fixed parts of the argv. These run 138638032Speter ** up to the first entry containing "$u". There can only 138738032Speter ** be one of these, and there are only a few more slots 138838032Speter ** in the pv after it. 138938032Speter */ 139038032Speter 139138032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 139238032Speter { 139338032Speter /* can't use strchr here because of sign extension problems */ 139438032Speter while (*p != '\0') 139538032Speter { 139638032Speter if ((*p++ & 0377) == MACROEXPAND) 139738032Speter { 139838032Speter if (*p == 'u') 139938032Speter break; 140038032Speter } 140138032Speter } 140238032Speter 140338032Speter if (*p != '\0') 140438032Speter break; 140538032Speter 140638032Speter /* this entry is safe -- go ahead and process it */ 140738032Speter expand(*mvp, buf, sizeof buf, e); 140890792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 140938032Speter if (pvp >= &pv[MAXPV - 3]) 141038032Speter { 141164562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 141264562Sgshapiro pv[0]); 141390792Sgshapiro rcode = -1; 141490792Sgshapiro goto cleanup; 141538032Speter } 141638032Speter } 141738032Speter 141838032Speter /* 141938032Speter ** If we have no substitution for the user name in the argument 142038032Speter ** list, we know that we must supply the names otherwise -- and 142138032Speter ** SMTP is the answer!! 142238032Speter */ 142338032Speter 142438032Speter if (*mvp == NULL) 142538032Speter { 142673188Sgshapiro /* running LMTP or SMTP */ 142790792Sgshapiro clever = true; 142838032Speter *pvp = NULL; 142938032Speter } 143073188Sgshapiro else if (bitnset(M_LMTP, m->m_flags)) 143173188Sgshapiro { 143273188Sgshapiro /* not running LMTP */ 143373188Sgshapiro sm_syslog(LOG_ERR, NULL, 143473188Sgshapiro "Warning: mailer %s: LMTP flag (F=z) turned off", 143573188Sgshapiro m->m_name); 143673188Sgshapiro clrbitn(M_LMTP, m->m_flags); 143773188Sgshapiro } 143838032Speter 143938032Speter /* 144038032Speter ** At this point *mvp points to the argument with $u. We 144138032Speter ** run through our address list and append all the addresses 144238032Speter ** we can. If we run out of space, do not fret! We can 144338032Speter ** always send another copy later. 144438032Speter */ 144538032Speter 144664562Sgshapiro e->e_to = NULL; 144764562Sgshapiro strsize = 2; 144864562Sgshapiro rcptcount = 0; 144990792Sgshapiro ctladdr = NULL; 145090792Sgshapiro if (firstto->q_signature == NULL) 145190792Sgshapiro firstto->q_signature = hostsignature(firstto->q_mailer, 145290792Sgshapiro firstto->q_host); 145390792Sgshapiro firstsig = firstto->q_signature; 145464562Sgshapiro 145538032Speter for (; to != NULL; to = to->q_next) 145638032Speter { 145738032Speter /* avoid sending multiple recipients to dumb mailers */ 145864562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 145964562Sgshapiro break; 146038032Speter 146138032Speter /* if already sent or not for this host, don't send */ 146290792Sgshapiro if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 146338032Speter continue; 146438032Speter 146590792Sgshapiro /* 146690792Sgshapiro ** Must be same mailer to keep grouping rcpts. 146790792Sgshapiro ** If mailers don't match: continue; sendqueue is not 146890792Sgshapiro ** sorted by mailers, so don't break; 146990792Sgshapiro */ 147090792Sgshapiro 147190792Sgshapiro if (to->q_mailer != firstto->q_mailer) 147290792Sgshapiro continue; 147390792Sgshapiro 147490792Sgshapiro if (to->q_signature == NULL) /* for safety */ 147590792Sgshapiro to->q_signature = hostsignature(to->q_mailer, 147690792Sgshapiro to->q_host); 147790792Sgshapiro 147890792Sgshapiro /* 147990792Sgshapiro ** This is for coincidental and tailcoat piggybacking messages 148090792Sgshapiro ** to the same mail host. While the signatures are identical 148190792Sgshapiro ** (that's the MX-RR's are identical) we can do coincidental 148290792Sgshapiro ** piggybacking. We try hard for coattail piggybacking 148390792Sgshapiro ** with the same mail host when the next recipient has the 148490792Sgshapiro ** same host at lowest preference. It may be that this 148590792Sgshapiro ** won't work out, so 'skip_back' is maintained if a backup 148690792Sgshapiro ** to coincidental piggybacking or full signature must happen. 148790792Sgshapiro */ 148890792Sgshapiro 148990792Sgshapiro ret = firstto == to ? HS_MATCH_FULL : 149090792Sgshapiro coloncmp(to->q_signature, firstsig); 149190792Sgshapiro if (ret == HS_MATCH_FULL) 149290792Sgshapiro skip_back = to; 149390792Sgshapiro else if (ret == HS_MATCH_NO) 149464562Sgshapiro break; 149564562Sgshapiro 149690792Sgshapiro if (!clever) 149790792Sgshapiro { 149890792Sgshapiro /* avoid overflowing tobuf */ 149990792Sgshapiro strsize += strlen(to->q_paddr) + 1; 150090792Sgshapiro if (strsize > TOBUFSIZE) 150190792Sgshapiro break; 150290792Sgshapiro } 150390792Sgshapiro 150464562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 150564562Sgshapiro break; 150638032Speter 150738032Speter if (tTd(10, 1)) 150838032Speter { 150990792Sgshapiro sm_dprintf("\nsend to "); 151090792Sgshapiro printaddr(to, false); 151138032Speter } 151238032Speter 151338032Speter /* compute effective uid/gid when sending */ 151438032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 151590792Sgshapiro# if HASSETUSERCONTEXT 151638032Speter contextaddr = ctladdr = getctladdr(to); 151790792Sgshapiro# else /* HASSETUSERCONTEXT */ 151890792Sgshapiro ctladdr = getctladdr(to); 151990792Sgshapiro# endif /* HASSETUSERCONTEXT */ 152038032Speter 152138032Speter if (tTd(10, 2)) 152238032Speter { 152390792Sgshapiro sm_dprintf("ctladdr="); 152490792Sgshapiro printaddr(ctladdr, false); 152538032Speter } 152638032Speter 152738032Speter user = to->q_user; 152838032Speter e->e_to = to->q_paddr; 152938032Speter 153038032Speter /* 153138032Speter ** Check to see that these people are allowed to 153238032Speter ** talk to each other. 153366494Sgshapiro ** Check also for overflow of e_msgsize. 153438032Speter */ 153538032Speter 153666494Sgshapiro if (m->m_maxsize != 0 && 153766494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 153838032Speter { 153938032Speter e->e_flags |= EF_NO_BODY_RETN; 154038032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 154138032Speter to->q_status = "5.2.3"; 154238032Speter else 154338032Speter to->q_status = "5.3.4"; 154490792Sgshapiro 154564562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 154664562Sgshapiro usrerrenh(to->q_status, 154764562Sgshapiro "552 Message is too large; %ld bytes max", 154864562Sgshapiro m->m_maxsize); 154990792Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, false); 155064562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 155190792Sgshapiro NULL, ctladdr, xstart, e, to); 155238032Speter continue; 155338032Speter } 155473188Sgshapiro SM_SET_H_ERRNO(0); 155590792Sgshapiro ovr = true; 155638032Speter 155738032Speter /* do config file checking of compatibility */ 155890792Sgshapiro#if _FFR_QUARANTINE 155990792Sgshapiro quarantine = (e->e_quarmsg != NULL); 156090792Sgshapiro#endif /* _FFR_QUARANTINE */ 156164562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1562102528Sgshapiro e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1563102528Sgshapiro e->e_id); 156438032Speter if (rcode == EX_OK) 156538032Speter { 156642575Speter /* do in-code checking if not discarding */ 156742575Speter if (!bitset(EF_DISCARD, e->e_flags)) 156864562Sgshapiro { 156942575Speter rcode = checkcompat(to, e); 157090792Sgshapiro ovr = false; 157164562Sgshapiro } 157238032Speter } 157338032Speter if (rcode != EX_OK) 157438032Speter { 157564562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 157664562Sgshapiro giveresponse(rcode, to->q_status, m, 157790792Sgshapiro NULL, ctladdr, xstart, e, to); 157838032Speter continue; 157938032Speter } 158090792Sgshapiro#if _FFR_QUARANTINE 158190792Sgshapiro if (!quarantine && e->e_quarmsg != NULL) 158290792Sgshapiro { 158390792Sgshapiro /* 158490792Sgshapiro ** check_compat or checkcompat() has tried 158590792Sgshapiro ** to quarantine but that isn't supported. 158690792Sgshapiro ** Revert the attempt. 158790792Sgshapiro */ 158890792Sgshapiro 158990792Sgshapiro e->e_quarmsg = NULL; 159090792Sgshapiro macdefine(&e->e_macro, A_PERM, 159190792Sgshapiro macid("{quarantine}"), ""); 159290792Sgshapiro } 159390792Sgshapiro#endif /* _FFR_QUARANTINE */ 159442575Speter if (bitset(EF_DISCARD, e->e_flags)) 159542575Speter { 159642575Speter if (tTd(10, 5)) 159742575Speter { 159890792Sgshapiro sm_dprintf("deliver: discarding recipient "); 159990792Sgshapiro printaddr(to, false); 160042575Speter } 160138032Speter 160264562Sgshapiro /* pretend the message was sent */ 160364562Sgshapiro /* XXX should we log something here? */ 160464562Sgshapiro to->q_state = QS_DISCARDED; 160564562Sgshapiro 160642575Speter /* 160742575Speter ** Remove discard bit to prevent discard of 160864562Sgshapiro ** future recipients. This is safe because the 160964562Sgshapiro ** true "global discard" has been handled before 161064562Sgshapiro ** we get here. 161142575Speter */ 161264562Sgshapiro 161342575Speter e->e_flags &= ~EF_DISCARD; 161442575Speter continue; 161542575Speter } 161642575Speter 161738032Speter /* 161838032Speter ** Strip quote bits from names if the mailer is dumb 161938032Speter ** about them. 162038032Speter */ 162138032Speter 162238032Speter if (bitnset(M_STRIPQ, m->m_flags)) 162338032Speter { 162438032Speter stripquotes(user); 162538032Speter stripquotes(host); 162638032Speter } 1627110560Sgshapiro#if _FFR_STRIPBACKSL 1628110560Sgshapiro /* 1629112810Sgshapiro ** Strip one leading backslash if requested and the 1630110560Sgshapiro ** next character is alphanumerical (the latter can 1631110560Sgshapiro ** probably relaxed a bit, see RFC2821). 1632110560Sgshapiro */ 163338032Speter 1634110560Sgshapiro if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1635110560Sgshapiro stripbackslash(user); 1636110560Sgshapiro#endif /* _FFR_STRIPBACKSL */ 1637110560Sgshapiro 163838032Speter /* hack attack -- delivermail compatibility */ 163938032Speter if (m == ProgMailer && *user == '|') 164038032Speter user++; 164138032Speter 164238032Speter /* 164338032Speter ** If an error message has already been given, don't 164438032Speter ** bother to send to this address. 164538032Speter ** 164638032Speter ** >>>>>>>>>> This clause assumes that the local mailer 164738032Speter ** >> NOTE >> cannot do any further aliasing; that 164838032Speter ** >>>>>>>>>> function is subsumed by sendmail. 164938032Speter */ 165038032Speter 165164562Sgshapiro if (!QS_IS_OK(to->q_state)) 165238032Speter continue; 165338032Speter 165438032Speter /* 165538032Speter ** See if this user name is "special". 165638032Speter ** If the user name has a slash in it, assume that this 165738032Speter ** is a file -- send it off without further ado. Note 165838032Speter ** that this type of addresses is not processed along 165938032Speter ** with the others, so we fudge on the To person. 166038032Speter */ 166138032Speter 166238032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 166338032Speter { 166490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); 166538032Speter p = to->q_home; 166638032Speter if (p == NULL && ctladdr != NULL) 166738032Speter p = ctladdr->q_home; 166890792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); 166938032Speter expand(m->m_argv[1], buf, sizeof buf, e); 167038032Speter if (strlen(buf) > 0) 167138032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 167238032Speter else 167338032Speter { 167438032Speter syserr("empty filename specification for mailer %s", 167538032Speter m->m_name); 167638032Speter rcode = EX_CONFIG; 167738032Speter } 167864562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 167990792Sgshapiro ctladdr, xstart, e, to); 168090792Sgshapiro markfailure(e, to, NULL, rcode, true); 168138032Speter e->e_nsent++; 168238032Speter if (rcode == EX_OK) 168338032Speter { 168464562Sgshapiro to->q_state = QS_SENT; 168538032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 168638032Speter bitset(QPINGONSUCCESS, to->q_flags)) 168738032Speter { 168838032Speter to->q_flags |= QDELIVERED; 168938032Speter to->q_status = "2.1.5"; 169090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, 169190792Sgshapiro SM_TIME_DEFAULT, 169290792Sgshapiro "%s... Successfully delivered\n", 169390792Sgshapiro to->q_paddr); 169438032Speter } 169538032Speter } 169638032Speter to->q_statdate = curtime(); 169790792Sgshapiro markstats(e, to, STATS_NORMAL); 169838032Speter continue; 169938032Speter } 170038032Speter 170138032Speter /* 170238032Speter ** Address is verified -- add this user to mailer 170338032Speter ** argv, and add it to the print list of recipients. 170438032Speter */ 170538032Speter 170638032Speter /* link together the chain of recipients */ 170738032Speter to->q_tchain = tochain; 170838032Speter tochain = to; 170964562Sgshapiro e->e_to = "[CHAIN]"; 171064562Sgshapiro 171190792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 171238032Speter p = to->q_home; 171338032Speter if (p == NULL && ctladdr != NULL) 171438032Speter p = ctladdr->q_home; 171590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 171638032Speter 171764562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 171864562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 171964562Sgshapiro { 172064562Sgshapiro char notify[MAXLINE]; 172164562Sgshapiro 172264562Sgshapiro notify[0] = '\0'; 172364562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 172490792Sgshapiro (void) sm_strlcat(notify, "SUCCESS,", 172590792Sgshapiro sizeof notify); 172664562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 172790792Sgshapiro (void) sm_strlcat(notify, "FAILURE,", 172890792Sgshapiro sizeof notify); 172964562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 173090792Sgshapiro (void) sm_strlcat(notify, "DELAY,", 173190792Sgshapiro sizeof notify); 173264562Sgshapiro 173364562Sgshapiro /* Set to NEVER or drop trailing comma */ 173464562Sgshapiro if (notify[0] == '\0') 173590792Sgshapiro (void) sm_strlcat(notify, "NEVER", 173690792Sgshapiro sizeof notify); 173764562Sgshapiro else 173864562Sgshapiro notify[strlen(notify) - 1] = '\0'; 173964562Sgshapiro 174090792Sgshapiro macdefine(&e->e_macro, A_TEMP, 174190792Sgshapiro macid("{dsn_notify}"), notify); 174264562Sgshapiro } 174364562Sgshapiro else 174490792Sgshapiro macdefine(&e->e_macro, A_PERM, 174590792Sgshapiro macid("{dsn_notify}"), NULL); 174664562Sgshapiro 174738032Speter /* 174838032Speter ** Expand out this user into argument list. 174938032Speter */ 175038032Speter 175138032Speter if (!clever) 175238032Speter { 175338032Speter expand(*mvp, buf, sizeof buf, e); 175490792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 175538032Speter if (pvp >= &pv[MAXPV - 2]) 175638032Speter { 175738032Speter /* allow some space for trailing parms */ 175838032Speter break; 175938032Speter } 176038032Speter } 176138032Speter } 176238032Speter 176338032Speter /* see if any addresses still exist */ 176464562Sgshapiro if (tochain == NULL) 176538032Speter { 176690792Sgshapiro rcode = 0; 176790792Sgshapiro goto cleanup; 176838032Speter } 176938032Speter 177038032Speter /* print out messages as full list */ 177190792Sgshapiro strsize = 1; 177290792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 177390792Sgshapiro strsize += strlen(to->q_paddr) + 1; 177490792Sgshapiro if (strsize < TOBUFSIZE) 177590792Sgshapiro strsize = TOBUFSIZE; 177690792Sgshapiro if (strsize > tobufsize) 177764562Sgshapiro { 177890792Sgshapiro SM_FREE_CLR(tobuf); 177990792Sgshapiro tobuf = sm_pmalloc_x(strsize); 178090792Sgshapiro tobufsize = strsize; 178164562Sgshapiro } 178290792Sgshapiro p = tobuf; 178390792Sgshapiro *p = '\0'; 178490792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 178590792Sgshapiro { 178690792Sgshapiro (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 178790792Sgshapiro ",", to->q_paddr); 178890792Sgshapiro p += strlen(p); 178990792Sgshapiro } 179038032Speter e->e_to = tobuf + 1; 179138032Speter 179238032Speter /* 179338032Speter ** Fill out any parameters after the $u parameter. 179438032Speter */ 179538032Speter 179690792Sgshapiro if (!clever) 179738032Speter { 179890792Sgshapiro while (*++mvp != NULL) 179990792Sgshapiro { 180090792Sgshapiro expand(*mvp, buf, sizeof buf, e); 180190792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 180290792Sgshapiro if (pvp >= &pv[MAXPV]) 180390792Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 180490792Sgshapiro pv[0]); 180590792Sgshapiro } 180638032Speter } 180738032Speter *pvp++ = NULL; 180838032Speter 180938032Speter /* 181038032Speter ** Call the mailer. 181138032Speter ** The argument vector gets built, pipes 181238032Speter ** are created as necessary, and we fork & exec as 181338032Speter ** appropriate. 181438032Speter ** If we are running SMTP, we just need to clean up. 181538032Speter */ 181638032Speter 181764562Sgshapiro /* XXX this seems a bit wierd */ 181838032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 181938032Speter bitset(QGOODUID, e->e_from.q_flags)) 182038032Speter ctladdr = &e->e_from; 182138032Speter 182238032Speter#if NAMED_BIND 182338032Speter if (ConfigLevel < 2) 182438032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 182564562Sgshapiro#endif /* NAMED_BIND */ 182638032Speter 182738032Speter if (tTd(11, 1)) 182838032Speter { 182990792Sgshapiro sm_dprintf("openmailer:"); 183038032Speter printav(pv); 183138032Speter } 183238032Speter errno = 0; 183373188Sgshapiro SM_SET_H_ERRNO(0); 183438032Speter CurHostName = NULL; 183538032Speter 183638032Speter /* 183738032Speter ** Deal with the special case of mail handled through an IPC 183838032Speter ** connection. 183938032Speter ** In this case we don't actually fork. We must be 184038032Speter ** running SMTP for this to work. We will return a 184138032Speter ** zero pid to indicate that we are running IPC. 184238032Speter ** We also handle a debug version that just talks to stdin/out. 184338032Speter */ 184438032Speter 184538032Speter curhost = NULL; 184638032Speter SmtpPhase = NULL; 184738032Speter mci = NULL; 184838032Speter 184938032Speter#if XDEBUG 185038032Speter { 185138032Speter char wbuf[MAXLINE]; 185238032Speter 185338032Speter /* make absolutely certain 0, 1, and 2 are in use */ 185490792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", 185590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 185690792Sgshapiro m->m_name); 185738032Speter checkfd012(wbuf); 185838032Speter } 185964562Sgshapiro#endif /* XDEBUG */ 186038032Speter 186138032Speter /* check for 8-bit available */ 186238032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 186338032Speter bitnset(M_7BITS, m->m_flags) && 186438032Speter (bitset(EF_DONT_MIME, e->e_flags) || 186538032Speter !(bitset(MM_MIME8BIT, MimeMode) || 186638032Speter (bitset(EF_IS_MIME, e->e_flags) && 186764562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 186838032Speter { 186964562Sgshapiro e->e_status = "5.6.3"; 187064562Sgshapiro usrerrenh(e->e_status, 187190792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 187238032Speter rcode = EX_DATAERR; 187338032Speter goto give_up; 187438032Speter } 187538032Speter 187638032Speter if (tTd(62, 8)) 187738032Speter checkfds("before delivery"); 187838032Speter 187938032Speter /* check for Local Person Communication -- not for mortals!!! */ 188038032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 188138032Speter { 188290792Sgshapiro#if _FFR_CACHE_LPC 188390792Sgshapiro if (clever) 188490792Sgshapiro { 188590792Sgshapiro /* flush any expired connections */ 188690792Sgshapiro (void) mci_scan(NULL); 188790792Sgshapiro 188890792Sgshapiro /* try to get a cached connection or just a slot */ 188990792Sgshapiro mci = mci_get(m->m_name, m); 189090792Sgshapiro if (mci->mci_host == NULL) 189190792Sgshapiro mci->mci_host = m->m_name; 189290792Sgshapiro CurHostName = mci->mci_host; 189390792Sgshapiro if (mci->mci_state != MCIS_CLOSED) 189490792Sgshapiro { 189590792Sgshapiro message("Using cached SMTP/LPC connection for %s...", 189690792Sgshapiro m->m_name); 189790792Sgshapiro mci->mci_deliveries++; 189890792Sgshapiro goto do_transfer; 189990792Sgshapiro } 190090792Sgshapiro } 190190792Sgshapiro else 190290792Sgshapiro { 190390792Sgshapiro mci = mci_new(e->e_rpool); 190490792Sgshapiro } 190590792Sgshapiro mci->mci_in = smioin; 190690792Sgshapiro mci->mci_out = smioout; 190790792Sgshapiro mci->mci_mailer = m; 190890792Sgshapiro mci->mci_host = m->m_name; 190990792Sgshapiro if (clever) 191090792Sgshapiro { 191190792Sgshapiro mci->mci_state = MCIS_OPENING; 191290792Sgshapiro mci_cache(mci); 191390792Sgshapiro } 191490792Sgshapiro else 191590792Sgshapiro mci->mci_state = MCIS_OPEN; 191690792Sgshapiro#else /* _FFR_CACHE_LPC */ 191790792Sgshapiro mci = mci_new(e->e_rpool); 191890792Sgshapiro mci->mci_in = smioin; 191990792Sgshapiro mci->mci_out = smioout; 192038032Speter mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; 192138032Speter mci->mci_mailer = m; 192290792Sgshapiro#endif /* _FFR_CACHE_LPC */ 192338032Speter } 192490792Sgshapiro else if (strcmp(m->m_mailer, "[IPC]") == 0) 192538032Speter { 192638032Speter register int i; 192738032Speter 192838032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 192938032Speter { 193064562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 193138032Speter rcode = EX_CONFIG; 193238032Speter goto give_up; 193338032Speter } 193438032Speter 193564562Sgshapiro# if NETUNIX 193664562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 193764562Sgshapiro { 193864562Sgshapiro curhost = CurHostName = "localhost"; 193964562Sgshapiro mux_path = pv[1]; 194064562Sgshapiro } 194164562Sgshapiro else 194264562Sgshapiro# endif /* NETUNIX */ 194364562Sgshapiro { 194464562Sgshapiro CurHostName = pv[1]; 194564562Sgshapiro curhost = hostsignature(m, pv[1]); 194664562Sgshapiro } 194738032Speter 194838032Speter if (curhost == NULL || curhost[0] == '\0') 194938032Speter { 195038032Speter syserr("null host signature for %s", pv[1]); 195138032Speter rcode = EX_CONFIG; 195238032Speter goto give_up; 195338032Speter } 195438032Speter 195538032Speter if (!clever) 195638032Speter { 195764562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 195838032Speter rcode = EX_CONFIG; 195938032Speter goto give_up; 196038032Speter } 196164562Sgshapiro if (pv[2] != NULL 196264562Sgshapiro# if NETUNIX 196364562Sgshapiro && mux_path == NULL 196464562Sgshapiro# endif /* NETUNIX */ 196564562Sgshapiro ) 196638032Speter { 196790792Sgshapiro port = htons((unsigned short) atoi(pv[2])); 196838032Speter if (port == 0) 196938032Speter { 197064562Sgshapiro# ifdef NO_GETSERVBYNAME 197164562Sgshapiro syserr("Invalid port number: %s", pv[2]); 197264562Sgshapiro# else /* NO_GETSERVBYNAME */ 197338032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 197438032Speter 197538032Speter if (sp == NULL) 197638032Speter syserr("Service %s unknown", pv[2]); 197738032Speter else 197838032Speter port = sp->s_port; 197964562Sgshapiro# endif /* NO_GETSERVBYNAME */ 198038032Speter } 198138032Speter } 198264562Sgshapiro 198364562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 198490792Sgshapiro if (TimeOuts.to_aconnect > 0) 198590792Sgshapiro enough = curtime() + TimeOuts.to_aconnect; 198638032Spetertryhost: 198764562Sgshapiro while (hostnum < nummxhosts) 198838032Speter { 198964562Sgshapiro char sep = ':'; 199064562Sgshapiro char *endp; 199138032Speter static char hostbuf[MAXNAME + 1]; 199238032Speter 199364562Sgshapiro# if NETINET6 199464562Sgshapiro if (*mxhosts[hostnum] == '[') 199538032Speter { 199664562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 199764562Sgshapiro if (endp != NULL) 199864562Sgshapiro endp = strpbrk(endp + 1, ":,"); 199964562Sgshapiro } 200064562Sgshapiro else 200164562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 200264562Sgshapiro# else /* NETINET6 */ 200364562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 200464562Sgshapiro# endif /* NETINET6 */ 200564562Sgshapiro if (endp != NULL) 200664562Sgshapiro { 200764562Sgshapiro sep = *endp; 200864562Sgshapiro *endp = '\0'; 200964562Sgshapiro } 201064562Sgshapiro 201190792Sgshapiro if (hostnum == 1 && skip_back != NULL) 201290792Sgshapiro { 201390792Sgshapiro /* 201490792Sgshapiro ** Coattail piggybacking is no longer an 201590792Sgshapiro ** option with the mail host next to be tried 201690792Sgshapiro ** no longer the lowest MX preference 201790792Sgshapiro ** (hostnum == 1 meaning we're on the second 201890792Sgshapiro ** preference). We do not try to coattail 201990792Sgshapiro ** piggyback more than the first MX preference. 202090792Sgshapiro ** Revert 'tochain' to last location for 202190792Sgshapiro ** coincidental piggybacking. This works this 202290792Sgshapiro ** easily because the q_tchain kept getting 202390792Sgshapiro ** added to the top of the linked list. 202490792Sgshapiro */ 202590792Sgshapiro 202690792Sgshapiro tochain = skip_back; 202790792Sgshapiro } 202890792Sgshapiro 202964562Sgshapiro if (*mxhosts[hostnum] == '\0') 203064562Sgshapiro { 203138032Speter syserr("deliver: null host name in signature"); 203264562Sgshapiro hostnum++; 203364562Sgshapiro if (endp != NULL) 203464562Sgshapiro *endp = sep; 203538032Speter continue; 203638032Speter } 203790792Sgshapiro (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 203890792Sgshapiro sizeof hostbuf); 203964562Sgshapiro hostnum++; 204064562Sgshapiro if (endp != NULL) 204164562Sgshapiro *endp = sep; 204238032Speter 204338032Speter /* see if we already know that this host is fried */ 204438032Speter CurHostName = hostbuf; 204538032Speter mci = mci_get(hostbuf, m); 204638032Speter if (mci->mci_state != MCIS_CLOSED) 204738032Speter { 204890792Sgshapiro char *type; 204990792Sgshapiro 205038032Speter if (tTd(11, 1)) 205138032Speter { 205290792Sgshapiro sm_dprintf("openmailer: "); 205390792Sgshapiro mci_dump(mci, false); 205438032Speter } 205538032Speter CurHostName = mci->mci_host; 205690792Sgshapiro if (bitnset(M_LMTP, m->m_flags)) 205790792Sgshapiro type = "L"; 205890792Sgshapiro else if (bitset(MCIF_ESMTP, mci->mci_flags)) 205990792Sgshapiro type = "ES"; 206090792Sgshapiro else 206190792Sgshapiro type = "S"; 206290792Sgshapiro message("Using cached %sMTP connection to %s via %s...", 206390792Sgshapiro type, hostbuf, m->m_name); 206464562Sgshapiro mci->mci_deliveries++; 206538032Speter break; 206638032Speter } 206738032Speter mci->mci_mailer = m; 206838032Speter if (mci->mci_exitstat != EX_OK) 206938032Speter { 207038032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 207190792Sgshapiro goodmxfound = true; 207238032Speter continue; 207338032Speter } 207438032Speter 207538032Speter if (mci_lock_host(mci) != EX_OK) 207638032Speter { 207738032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 207890792Sgshapiro goodmxfound = true; 207938032Speter continue; 208038032Speter } 208138032Speter 208238032Speter /* try the connection */ 208390792Sgshapiro sm_setproctitle(true, e, "%s %s: %s", 208464562Sgshapiro qid_printname(e), 208564562Sgshapiro hostbuf, "user open"); 208664562Sgshapiro# if NETUNIX 208764562Sgshapiro if (mux_path != NULL) 208864562Sgshapiro { 208938032Speter message("Connecting to %s via %s...", 209064562Sgshapiro mux_path, m->m_name); 209190792Sgshapiro i = makeconnection_ds((char *) mux_path, mci); 209264562Sgshapiro } 209338032Speter else 209464562Sgshapiro# endif /* NETUNIX */ 209564562Sgshapiro { 209664562Sgshapiro if (port == 0) 209764562Sgshapiro message("Connecting to %s via %s...", 209864562Sgshapiro hostbuf, m->m_name); 209964562Sgshapiro else 210064562Sgshapiro message("Connecting to %s port %d via %s...", 210164562Sgshapiro hostbuf, ntohs(port), 210264562Sgshapiro m->m_name); 210390792Sgshapiro i = makeconnection(hostbuf, port, mci, e, 210490792Sgshapiro enough); 210564562Sgshapiro } 210677349Sgshapiro mci->mci_errno = errno; 210738032Speter mci->mci_lastuse = curtime(); 210864562Sgshapiro mci->mci_deliveries = 0; 210938032Speter mci->mci_exitstat = i; 211064562Sgshapiro# if NAMED_BIND 211138032Speter mci->mci_herrno = h_errno; 211264562Sgshapiro# endif /* NAMED_BIND */ 211390792Sgshapiro 211490792Sgshapiro /* 211590792Sgshapiro ** Have we tried long enough to get a connection? 211690792Sgshapiro ** If yes, skip to the fallback MX hosts 211790792Sgshapiro ** (if existent). 211890792Sgshapiro */ 211990792Sgshapiro 212090792Sgshapiro if (enough > 0 && mci->mci_lastuse >= enough) 212190792Sgshapiro { 212290792Sgshapiro int h; 212390792Sgshapiro# if NAMED_BIND 212490792Sgshapiro extern int NumFallBackMXHosts; 212590792Sgshapiro# else /* NAMED_BIND */ 212690792Sgshapiro const int NumFallBackMXHosts = 0; 212790792Sgshapiro# endif /* NAMED_BIND */ 212890792Sgshapiro 212990792Sgshapiro if (hostnum < nummxhosts && LogLevel > 9) 213090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 213190792Sgshapiro "Timeout.to_aconnect occurred before exhausting all addresses"); 213290792Sgshapiro 213390792Sgshapiro /* turn off timeout if fallback available */ 213490792Sgshapiro if (NumFallBackMXHosts > 0) 213590792Sgshapiro enough = 0; 213690792Sgshapiro 213790792Sgshapiro /* skip to a fallback MX host */ 213890792Sgshapiro h = nummxhosts - NumFallBackMXHosts; 213990792Sgshapiro if (hostnum < h) 214090792Sgshapiro hostnum = h; 214190792Sgshapiro } 214238032Speter if (i == EX_OK) 214338032Speter { 214490792Sgshapiro goodmxfound = true; 214594334Sgshapiro markstats(e, firstto, STATS_CONNECT); 214638032Speter mci->mci_state = MCIS_OPENING; 214738032Speter mci_cache(mci); 214838032Speter if (TrafficLogFile != NULL) 214990792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 215090792Sgshapiro SM_TIME_DEFAULT, 215190792Sgshapiro "%05d === CONNECT %s\n", 215290792Sgshapiro (int) CurrentPid, 215390792Sgshapiro hostbuf); 215438032Speter break; 215538032Speter } 215638032Speter else 215738032Speter { 215864562Sgshapiro if (tTd(11, 1)) 215990792Sgshapiro sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 216090792Sgshapiro i, errno); 216138032Speter if (i == EX_TEMPFAIL) 216290792Sgshapiro goodmxfound = true; 216338032Speter mci_unlock_host(mci); 216438032Speter } 216538032Speter 216638032Speter /* enter status of this host */ 216738032Speter setstat(i); 216838032Speter 216938032Speter /* should print some message here for -v mode */ 217038032Speter } 217138032Speter if (mci == NULL) 217238032Speter { 217338032Speter syserr("deliver: no host name"); 217438032Speter rcode = EX_SOFTWARE; 217538032Speter goto give_up; 217638032Speter } 217738032Speter mci->mci_pid = 0; 217838032Speter } 217938032Speter else 218038032Speter { 218138032Speter /* flush any expired connections */ 218238032Speter (void) mci_scan(NULL); 218338032Speter mci = NULL; 218438032Speter 218538032Speter if (bitnset(M_LMTP, m->m_flags)) 218638032Speter { 218738032Speter /* try to get a cached connection */ 218838032Speter mci = mci_get(m->m_name, m); 218938032Speter if (mci->mci_host == NULL) 219038032Speter mci->mci_host = m->m_name; 219138032Speter CurHostName = mci->mci_host; 219238032Speter if (mci->mci_state != MCIS_CLOSED) 219338032Speter { 219438032Speter message("Using cached LMTP connection for %s...", 219538032Speter m->m_name); 219664562Sgshapiro mci->mci_deliveries++; 219738032Speter goto do_transfer; 219838032Speter } 219938032Speter } 220038032Speter 220138032Speter /* announce the connection to verbose listeners */ 220238032Speter if (host == NULL || host[0] == '\0') 220338032Speter message("Connecting to %s...", m->m_name); 220438032Speter else 220538032Speter message("Connecting to %s via %s...", host, m->m_name); 220638032Speter if (TrafficLogFile != NULL) 220738032Speter { 220838032Speter char **av; 220938032Speter 221090792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 221190792Sgshapiro "%05d === EXEC", (int) CurrentPid); 221238032Speter for (av = pv; *av != NULL; av++) 221390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 221490792Sgshapiro SM_TIME_DEFAULT, " %s", 221590792Sgshapiro *av); 221690792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 221790792Sgshapiro "\n"); 221838032Speter } 221938032Speter 222038032Speter#if XDEBUG 222138032Speter checkfd012("before creating mail pipe"); 222264562Sgshapiro#endif /* XDEBUG */ 222338032Speter 222438032Speter /* create a pipe to shove the mail through */ 222538032Speter if (pipe(mpvect) < 0) 222638032Speter { 222738032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 222890792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 222938032Speter if (tTd(11, 1)) 223090792Sgshapiro sm_dprintf("openmailer: NULL\n"); 223138032Speter rcode = EX_OSERR; 223238032Speter goto give_up; 223338032Speter } 223438032Speter 223538032Speter#if XDEBUG 223638032Speter /* make sure we didn't get one of the standard I/O files */ 223738032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 223838032Speter { 223938032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 224090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 224190792Sgshapiro mpvect[0], mpvect[1]); 224290792Sgshapiro printopenfds(true); 224338032Speter if (tTd(11, 1)) 224490792Sgshapiro sm_dprintf("openmailer: NULL\n"); 224538032Speter rcode = EX_OSERR; 224638032Speter goto give_up; 224738032Speter } 224838032Speter 224938032Speter /* make sure system call isn't dead meat */ 225038032Speter checkfdopen(mpvect[0], "mpvect[0]"); 225138032Speter checkfdopen(mpvect[1], "mpvect[1]"); 225238032Speter if (mpvect[0] == mpvect[1] || 225338032Speter (e->e_lockfp != NULL && 225490792Sgshapiro (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 225590792Sgshapiro NULL) || 225690792Sgshapiro mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 225790792Sgshapiro NULL)))) 225838032Speter { 225938032Speter if (e->e_lockfp == NULL) 226038032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 226190792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 226290792Sgshapiro m->m_name, mpvect[0], mpvect[1]); 226338032Speter else 226438032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 226590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 226690792Sgshapiro m->m_name, mpvect[0], mpvect[1], 226790792Sgshapiro sm_io_getinfo(e->e_lockfp, 226890792Sgshapiro SM_IO_WHAT_FD, NULL)); 226938032Speter } 227064562Sgshapiro#endif /* XDEBUG */ 227138032Speter 227264562Sgshapiro /* create a return pipe */ 227364562Sgshapiro if (pipe(rpvect) < 0) 227438032Speter { 227564562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 227690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 227790792Sgshapiro m->m_name); 227864562Sgshapiro (void) close(mpvect[0]); 227964562Sgshapiro (void) close(mpvect[1]); 228064562Sgshapiro if (tTd(11, 1)) 228190792Sgshapiro sm_dprintf("openmailer: NULL\n"); 228264562Sgshapiro rcode = EX_OSERR; 228364562Sgshapiro goto give_up; 228438032Speter } 228564562Sgshapiro#if XDEBUG 228664562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 228764562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 228864562Sgshapiro#endif /* XDEBUG */ 228938032Speter 229038032Speter /* 229138032Speter ** Actually fork the mailer process. 229238032Speter ** DOFORK is clever about retrying. 229338032Speter ** 229438032Speter ** Dispose of SIGCHLD signal catchers that may be laying 229564562Sgshapiro ** around so that endmailer will get it. 229638032Speter */ 229738032Speter 229890792Sgshapiro if (e->e_xfp != NULL) /* for debugging */ 229990792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 230090792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 230190792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 230264562Sgshapiro 230364562Sgshapiro 230438032Speter DOFORK(FORK); 230538032Speter /* pid is set by DOFORK */ 230664562Sgshapiro 230738032Speter if (pid < 0) 230838032Speter { 230938032Speter /* failure */ 231038032Speter syserr("%s... openmailer(%s): cannot fork", 231190792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 231238032Speter (void) close(mpvect[0]); 231338032Speter (void) close(mpvect[1]); 231464562Sgshapiro (void) close(rpvect[0]); 231564562Sgshapiro (void) close(rpvect[1]); 231638032Speter if (tTd(11, 1)) 231790792Sgshapiro sm_dprintf("openmailer: NULL\n"); 231838032Speter rcode = EX_OSERR; 231938032Speter goto give_up; 232038032Speter } 232138032Speter else if (pid == 0) 232238032Speter { 232338032Speter int i; 232464562Sgshapiro int save_errno; 232590792Sgshapiro int sff; 232638032Speter int new_euid = NO_UID; 232738032Speter int new_ruid = NO_UID; 232838032Speter int new_gid = NO_GID; 232990792Sgshapiro char *user = NULL; 233038032Speter struct stat stb; 233138032Speter extern int DtableSize; 233238032Speter 233390792Sgshapiro CurrentPid = getpid(); 233490792Sgshapiro 233580785Sgshapiro /* clear the events to turn off SIGALRMs */ 233690792Sgshapiro sm_clear_events(); 233780785Sgshapiro 233877349Sgshapiro /* Reset global flags */ 233977349Sgshapiro RestartRequest = NULL; 234090792Sgshapiro RestartWorkGroup = false; 234177349Sgshapiro ShutdownRequest = NULL; 234277349Sgshapiro PendingSignal = 0; 234377349Sgshapiro 234438032Speter if (e->e_lockfp != NULL) 234590792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, 234690792Sgshapiro SM_IO_WHAT_FD, 234790792Sgshapiro NULL)); 234838032Speter 234938032Speter /* child -- set up input & exec mailer */ 235090792Sgshapiro (void) sm_signal(SIGALRM, sm_signal_noop); 235190792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 235290792Sgshapiro (void) sm_signal(SIGHUP, SIG_IGN); 235390792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 235490792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 235580785Sgshapiro# ifdef SIGUSR1 235690792Sgshapiro (void) sm_signal(SIGUSR1, sm_signal_noop); 235780785Sgshapiro# endif /* SIGUSR1 */ 235838032Speter 235938032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 236038032Speter stb.st_mode = 0; 236138032Speter 236264562Sgshapiro# if HASSETUSERCONTEXT 236338032Speter /* 236438032Speter ** Set user resources. 236538032Speter */ 236638032Speter 236738032Speter if (contextaddr != NULL) 236838032Speter { 2369110560Sgshapiro int sucflags; 237038032Speter struct passwd *pwd; 237138032Speter 237238032Speter if (contextaddr->q_ruser != NULL) 237338032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 237438032Speter else 237538032Speter pwd = sm_getpwnam(contextaddr->q_user); 2376110560Sgshapiro sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2377110560Sgshapiro#ifdef LOGIN_SETMAC 2378110560Sgshapiro sucflags |= LOGIN_SETMAC; 2379110560Sgshapiro#endif /* LOGIN_SETMAC */ 2380102528Sgshapiro if (pwd != NULL && 2381102528Sgshapiro setusercontext(NULL, pwd, pwd->pw_uid, 2382110560Sgshapiro sucflags) == -1 && 2383102528Sgshapiro suidwarn) 2384102528Sgshapiro { 2385102528Sgshapiro syserr("openmailer: setusercontext() failed"); 2386102528Sgshapiro exit(EX_TEMPFAIL); 2387102528Sgshapiro } 238838032Speter } 238964562Sgshapiro# endif /* HASSETUSERCONTEXT */ 239038032Speter 239190792Sgshapiro#if HASNICE 239238032Speter /* tweak niceness */ 239338032Speter if (m->m_nice != 0) 239464562Sgshapiro (void) nice(m->m_nice); 239590792Sgshapiro#endif /* HASNICE */ 239638032Speter 239738032Speter /* reset group id */ 239838032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 239938032Speter new_gid = m->m_gid; 240038032Speter else if (bitset(S_ISGID, stb.st_mode)) 240138032Speter new_gid = stb.st_gid; 240238032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 240338032Speter { 240438032Speter if (!DontInitGroups) 240538032Speter { 240690792Sgshapiro user = ctladdr->q_ruser; 240790792Sgshapiro if (user == NULL) 240890792Sgshapiro user = ctladdr->q_user; 240938032Speter 241090792Sgshapiro if (initgroups(user, 241190792Sgshapiro ctladdr->q_gid) == -1 241290792Sgshapiro && suidwarn) 241364562Sgshapiro { 241438032Speter syserr("openmailer: initgroups(%s, %d) failed", 241590792Sgshapiro user, ctladdr->q_gid); 241664562Sgshapiro exit(EX_TEMPFAIL); 241764562Sgshapiro } 241838032Speter } 241938032Speter else 242038032Speter { 242138032Speter GIDSET_T gidset[1]; 242238032Speter 242338032Speter gidset[0] = ctladdr->q_gid; 242490792Sgshapiro if (setgroups(1, gidset) == -1 242590792Sgshapiro && suidwarn) 242664562Sgshapiro { 242738032Speter syserr("openmailer: setgroups() failed"); 242864562Sgshapiro exit(EX_TEMPFAIL); 242964562Sgshapiro } 243038032Speter } 243138032Speter new_gid = ctladdr->q_gid; 243238032Speter } 243338032Speter else 243438032Speter { 243538032Speter if (!DontInitGroups) 243638032Speter { 243790792Sgshapiro user = DefUser; 243890792Sgshapiro if (initgroups(DefUser, DefGid) == -1 && 243990792Sgshapiro suidwarn) 244064562Sgshapiro { 244138032Speter syserr("openmailer: initgroups(%s, %d) failed", 244290792Sgshapiro DefUser, DefGid); 244364562Sgshapiro exit(EX_TEMPFAIL); 244464562Sgshapiro } 244538032Speter } 244638032Speter else 244738032Speter { 244838032Speter GIDSET_T gidset[1]; 244938032Speter 245038032Speter gidset[0] = DefGid; 245190792Sgshapiro if (setgroups(1, gidset) == -1 245290792Sgshapiro && suidwarn) 245364562Sgshapiro { 245438032Speter syserr("openmailer: setgroups() failed"); 245564562Sgshapiro exit(EX_TEMPFAIL); 245664562Sgshapiro } 245738032Speter } 245838032Speter if (m->m_gid == 0) 245938032Speter new_gid = DefGid; 246038032Speter else 246138032Speter new_gid = m->m_gid; 246238032Speter } 246364562Sgshapiro if (new_gid != NO_GID) 246464562Sgshapiro { 246564562Sgshapiro if (RunAsUid != 0 && 246664562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 246764562Sgshapiro new_gid != getgid() && 246864562Sgshapiro new_gid != getegid()) 246964562Sgshapiro { 247064562Sgshapiro /* Only root can change the gid */ 247190792Sgshapiro syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d", 247290792Sgshapiro (int) RunAsUid, (int) new_gid, 247390792Sgshapiro (int) getgid(), (int) getegid()); 247464562Sgshapiro exit(EX_TEMPFAIL); 247564562Sgshapiro } 247638032Speter 247764562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 247864562Sgshapiro { 247964562Sgshapiro syserr("openmailer: setgid(%ld) failed", 248064562Sgshapiro (long) new_gid); 248164562Sgshapiro exit(EX_TEMPFAIL); 248264562Sgshapiro } 248364562Sgshapiro } 248464562Sgshapiro 248564562Sgshapiro /* change root to some "safe" directory */ 248664562Sgshapiro if (m->m_rootdir != NULL) 248764562Sgshapiro { 248898121Sgshapiro expand(m->m_rootdir, cbuf, sizeof cbuf, e); 248964562Sgshapiro if (tTd(11, 20)) 249090792Sgshapiro sm_dprintf("openmailer: chroot %s\n", 249198121Sgshapiro cbuf); 249298121Sgshapiro if (chroot(cbuf) < 0) 249364562Sgshapiro { 249464562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 249598121Sgshapiro cbuf); 249664562Sgshapiro exit(EX_TEMPFAIL); 249764562Sgshapiro } 249864562Sgshapiro if (chdir("/") < 0) 249964562Sgshapiro { 250064562Sgshapiro syserr("openmailer: cannot chdir(/)"); 250164562Sgshapiro exit(EX_TEMPFAIL); 250264562Sgshapiro } 250364562Sgshapiro } 250464562Sgshapiro 250538032Speter /* reset user id */ 250638032Speter endpwent(); 250790792Sgshapiro sm_mbdb_terminate(); 250838032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 250980785Sgshapiro { 251038032Speter new_euid = m->m_uid; 251180785Sgshapiro 251280785Sgshapiro /* 251380785Sgshapiro ** Undo the effects of the uid change in main 251480785Sgshapiro ** for signal handling. The real uid may 251580785Sgshapiro ** be used by mailer in adding a "From " 251680785Sgshapiro ** line. 251780785Sgshapiro */ 251880785Sgshapiro 251980785Sgshapiro if (RealUid != 0 && RealUid != getuid()) 252090792Sgshapiro { 252190792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 252290792Sgshapiro# if HASSETREUID 252390792Sgshapiro if (setreuid(RealUid, geteuid()) < 0) 252490792Sgshapiro { 252590792Sgshapiro syserr("openmailer: setreuid(%d, %d) failed", 252690792Sgshapiro (int) RealUid, (int) geteuid()); 252790792Sgshapiro exit(EX_OSERR); 252890792Sgshapiro } 252990792Sgshapiro# endif /* HASSETREUID */ 253090792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 253190792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 253280785Sgshapiro new_ruid = RealUid; 253390792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 253490792Sgshapiro } 253580785Sgshapiro } 253638032Speter else if (bitset(S_ISUID, stb.st_mode)) 253738032Speter new_ruid = stb.st_uid; 253838032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 253938032Speter new_ruid = ctladdr->q_uid; 254038032Speter else if (m->m_uid != 0) 254138032Speter new_ruid = m->m_uid; 254238032Speter else 254338032Speter new_ruid = DefUid; 254494334Sgshapiro 254594334Sgshapiro# if _FFR_USE_SETLOGIN 254694334Sgshapiro /* run disconnected from terminal and set login name */ 254794334Sgshapiro if (setsid() >= 0 && 254894334Sgshapiro ctladdr != NULL && ctladdr->q_uid != 0 && 254994334Sgshapiro new_euid == ctladdr->q_uid) 255094334Sgshapiro { 255194334Sgshapiro struct passwd *pwd; 255294334Sgshapiro 255394334Sgshapiro pwd = sm_getpwuid(ctladdr->q_uid); 255494334Sgshapiro if (pwd != NULL && suidwarn) 255594334Sgshapiro (void) setlogin(pwd->pw_name); 255694334Sgshapiro endpwent(); 255794334Sgshapiro } 255894334Sgshapiro# endif /* _FFR_USE_SETLOGIN */ 255994334Sgshapiro 256038032Speter if (new_euid != NO_UID) 256138032Speter { 256264562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 256364562Sgshapiro { 256464562Sgshapiro /* Only root can change the uid */ 256590792Sgshapiro syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d", 256690792Sgshapiro (int) new_euid, (int) RunAsUid); 256764562Sgshapiro exit(EX_TEMPFAIL); 256864562Sgshapiro } 256964562Sgshapiro 257038032Speter vendor_set_uid(new_euid); 257164562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 257238032Speter if (seteuid(new_euid) < 0 && suidwarn) 257364562Sgshapiro { 257438032Speter syserr("openmailer: seteuid(%ld) failed", 257590792Sgshapiro (long) new_euid); 257664562Sgshapiro exit(EX_TEMPFAIL); 257764562Sgshapiro } 257864562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 257964562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 258038032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 258164562Sgshapiro { 258238032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 258390792Sgshapiro (long) new_ruid, (long) new_euid); 258464562Sgshapiro exit(EX_TEMPFAIL); 258564562Sgshapiro } 258664562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 258764562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID 258838032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 258964562Sgshapiro { 259038032Speter syserr("openmailer: setuid(%ld) failed", 259190792Sgshapiro (long) new_euid); 259264562Sgshapiro exit(EX_TEMPFAIL); 259364562Sgshapiro } 259464562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */ 259538032Speter } 259638032Speter else if (new_ruid != NO_UID) 259738032Speter { 259838032Speter vendor_set_uid(new_ruid); 259938032Speter if (setuid(new_ruid) < 0 && suidwarn) 260064562Sgshapiro { 260138032Speter syserr("openmailer: setuid(%ld) failed", 260290792Sgshapiro (long) new_ruid); 260364562Sgshapiro exit(EX_TEMPFAIL); 260464562Sgshapiro } 260538032Speter } 260638032Speter 260738032Speter if (tTd(11, 2)) 260890792Sgshapiro sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 260990792Sgshapiro (int) getuid(), (int) geteuid(), 261090792Sgshapiro (int) getgid(), (int) getegid()); 261138032Speter 261238032Speter /* move into some "safe" directory */ 261338032Speter if (m->m_execdir != NULL) 261438032Speter { 261538032Speter char *q; 261638032Speter 261738032Speter for (p = m->m_execdir; p != NULL; p = q) 261838032Speter { 261938032Speter q = strchr(p, ':'); 262038032Speter if (q != NULL) 262138032Speter *q = '\0'; 262298121Sgshapiro expand(p, cbuf, sizeof cbuf, e); 262338032Speter if (q != NULL) 262438032Speter *q++ = ':'; 262538032Speter if (tTd(11, 20)) 262690792Sgshapiro sm_dprintf("openmailer: trydir %s\n", 262798121Sgshapiro cbuf); 262898121Sgshapiro if (cbuf[0] != '\0' && 262998121Sgshapiro chdir(cbuf) >= 0) 263038032Speter break; 263138032Speter } 263238032Speter } 263338032Speter 263490792Sgshapiro /* Check safety of program to be run */ 263590792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 263690792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, 263790792Sgshapiro DontBlameSendmail)) 263890792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 263990792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 264090792Sgshapiro DontBlameSendmail)) 264190792Sgshapiro sff |= SFF_NOPATHCHECK; 264290792Sgshapiro else 264390792Sgshapiro sff |= SFF_SAFEDIRPATH; 264490792Sgshapiro ret = safefile(m->m_mailer, getuid(), getgid(), 264590792Sgshapiro user, sff, 0, NULL); 264690792Sgshapiro if (ret != 0) 264790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 264890792Sgshapiro "Warning: program %s unsafe: %s", 264990792Sgshapiro m->m_mailer, sm_errstring(ret)); 265090792Sgshapiro 265138032Speter /* arrange to filter std & diag output of command */ 265264562Sgshapiro (void) close(rpvect[0]); 265364562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 265438032Speter { 265564562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 265664562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 265764562Sgshapiro m->m_name, rpvect[1]); 265864562Sgshapiro _exit(EX_OSERR); 265938032Speter } 266064562Sgshapiro (void) close(rpvect[1]); 266164562Sgshapiro 266238032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 266338032Speter { 266438032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 266590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 266690792Sgshapiro m->m_name); 266738032Speter _exit(EX_OSERR); 266838032Speter } 266938032Speter 267038032Speter /* arrange to get standard input */ 267138032Speter (void) close(mpvect[1]); 267238032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 267338032Speter { 267438032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 267590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 267690792Sgshapiro m->m_name, mpvect[0]); 267738032Speter _exit(EX_OSERR); 267838032Speter } 267938032Speter (void) close(mpvect[0]); 268038032Speter 268138032Speter /* arrange for all the files to be closed */ 268238032Speter for (i = 3; i < DtableSize; i++) 268338032Speter { 268438032Speter register int j; 268538032Speter 268638032Speter if ((j = fcntl(i, F_GETFD, 0)) != -1) 268764562Sgshapiro (void) fcntl(i, F_SETFD, 268864562Sgshapiro j | FD_CLOEXEC); 268938032Speter } 269038032Speter 269194334Sgshapiro# if !_FFR_USE_SETLOGIN 269238032Speter /* run disconnected from terminal */ 269338032Speter (void) setsid(); 269494334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */ 269538032Speter 269638032Speter /* try to execute the mailer */ 269764562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 269864562Sgshapiro (ARGV_T) UserEnviron); 269964562Sgshapiro save_errno = errno; 270038032Speter syserr("Cannot exec %s", m->m_mailer); 270138032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 270264562Sgshapiro transienterror(save_errno)) 270338032Speter _exit(EX_OSERR); 270438032Speter _exit(EX_UNAVAILABLE); 270538032Speter } 270638032Speter 270738032Speter /* 270838032Speter ** Set up return value. 270938032Speter */ 271038032Speter 271138032Speter if (mci == NULL) 271238032Speter { 271390792Sgshapiro if (clever) 271490792Sgshapiro { 271590792Sgshapiro /* 271690792Sgshapiro ** Allocate from general heap, not 271790792Sgshapiro ** envelope rpool, because this mci 271890792Sgshapiro ** is going to be cached. 271990792Sgshapiro */ 272090792Sgshapiro 272190792Sgshapiro mci = mci_new(NULL); 272290792Sgshapiro } 272390792Sgshapiro else 272490792Sgshapiro { 272590792Sgshapiro /* 272690792Sgshapiro ** Prevent a storage leak by allocating 272790792Sgshapiro ** this from the envelope rpool. 272890792Sgshapiro */ 272990792Sgshapiro 273090792Sgshapiro mci = mci_new(e->e_rpool); 273190792Sgshapiro } 273238032Speter } 273338032Speter mci->mci_mailer = m; 273438032Speter if (clever) 273538032Speter { 273638032Speter mci->mci_state = MCIS_OPENING; 273738032Speter mci_cache(mci); 273838032Speter } 273938032Speter else 274038032Speter { 274138032Speter mci->mci_state = MCIS_OPEN; 274238032Speter } 274338032Speter mci->mci_pid = pid; 274438032Speter (void) close(mpvect[0]); 274590792Sgshapiro mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 274690792Sgshapiro (void *) &(mpvect[1]), SM_IO_WRONLY, 274790792Sgshapiro NULL); 274838032Speter if (mci->mci_out == NULL) 274938032Speter { 275038032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 275190792Sgshapiro mpvect[1]); 275238032Speter (void) close(mpvect[1]); 275364562Sgshapiro (void) close(rpvect[0]); 275464562Sgshapiro (void) close(rpvect[1]); 275538032Speter rcode = EX_OSERR; 275638032Speter goto give_up; 275738032Speter } 275864562Sgshapiro 275964562Sgshapiro (void) close(rpvect[1]); 276090792Sgshapiro mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 276190792Sgshapiro (void *) &(rpvect[0]), SM_IO_RDONLY, 276290792Sgshapiro NULL); 276364562Sgshapiro if (mci->mci_in == NULL) 276438032Speter { 276564562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 276664562Sgshapiro mpvect[1]); 276764562Sgshapiro (void) close(rpvect[0]); 276890792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 276964562Sgshapiro mci->mci_out = NULL; 277064562Sgshapiro rcode = EX_OSERR; 277164562Sgshapiro goto give_up; 277238032Speter } 277338032Speter } 277438032Speter 277538032Speter /* 277638032Speter ** If we are in SMTP opening state, send initial protocol. 277738032Speter */ 277838032Speter 277938032Speter if (bitnset(M_7BITS, m->m_flags) && 278038032Speter (!clever || mci->mci_state == MCIS_OPENING)) 278138032Speter mci->mci_flags |= MCIF_7BIT; 278238032Speter if (clever && mci->mci_state != MCIS_CLOSED) 278338032Speter { 278490792Sgshapiro# if STARTTLS || SASL 278590792Sgshapiro int dotpos; 278690792Sgshapiro char *srvname; 278790792Sgshapiro extern SOCKADDR CurHostAddr; 278890792Sgshapiro# endif /* STARTTLS || SASL */ 278990792Sgshapiro 279090792Sgshapiro# if SASL 279171345Sgshapiro# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 279290792Sgshapiro# endif /* SASL */ 279364562Sgshapiro# if STARTTLS 279471345Sgshapiro# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 279564562Sgshapiro# endif /* STARTTLS */ 279671345Sgshapiro# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 279771345Sgshapiro# define SET_HELO(f) f |= MCIF_ONLY_EHLO 279871345Sgshapiro# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 279938032Speter 280090792Sgshapiro# if STARTTLS || SASL 280190792Sgshapiro /* don't use CurHostName, it is changed in many places */ 280290792Sgshapiro if (mci->mci_host != NULL) 280390792Sgshapiro { 280490792Sgshapiro srvname = mci->mci_host; 280590792Sgshapiro dotpos = strlen(srvname) - 1; 280690792Sgshapiro if (dotpos >= 0) 280790792Sgshapiro { 280890792Sgshapiro if (srvname[dotpos] == '.') 280990792Sgshapiro srvname[dotpos] = '\0'; 281090792Sgshapiro else 281190792Sgshapiro dotpos = -1; 281290792Sgshapiro } 281390792Sgshapiro } 281490792Sgshapiro else if (mci->mci_mailer != NULL) 281590792Sgshapiro { 281690792Sgshapiro srvname = mci->mci_mailer->m_name; 281790792Sgshapiro dotpos = -1; 281890792Sgshapiro } 281990792Sgshapiro else 282090792Sgshapiro { 282190792Sgshapiro srvname = "local"; 282290792Sgshapiro dotpos = -1; 282390792Sgshapiro } 282471345Sgshapiro 282590792Sgshapiro /* don't set {server_name} to NULL or "": see getauth() */ 282690792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 282790792Sgshapiro srvname); 282864562Sgshapiro 282990792Sgshapiro /* CurHostAddr is set by makeconnection() and mci_get() */ 283090792Sgshapiro if (CurHostAddr.sa.sa_family != 0) 283190792Sgshapiro { 283290792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, 283390792Sgshapiro macid("{server_addr}"), 283490792Sgshapiro anynet_ntoa(&CurHostAddr)); 283590792Sgshapiro } 283690792Sgshapiro else if (mci->mci_mailer != NULL) 283790792Sgshapiro { 283890792Sgshapiro /* mailer name is unique, use it as address */ 283990792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 284090792Sgshapiro macid("{server_addr}"), 284190792Sgshapiro mci->mci_mailer->m_name); 284290792Sgshapiro } 284390792Sgshapiro else 284490792Sgshapiro { 284590792Sgshapiro /* don't set it to NULL or "": see getauth() */ 284690792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 284790792Sgshapiro macid("{server_addr}"), "0"); 284890792Sgshapiro } 284990792Sgshapiro 285090792Sgshapiro /* undo change of srvname (mci->mci_host) */ 285190792Sgshapiro if (dotpos >= 0) 285290792Sgshapiro srvname[dotpos] = '.'; 285390792Sgshapiro 285490792Sgshapiroreconnect: /* after switching to an encrypted connection */ 285590792Sgshapiro# endif /* STARTTLS || SASL */ 285690792Sgshapiro 285790792Sgshapiro /* set the current connection information */ 285890792Sgshapiro e->e_mci = mci; 285964562Sgshapiro# if SASL 286064562Sgshapiro mci->mci_saslcap = NULL; 286164562Sgshapiro# endif /* SASL */ 286271345Sgshapiro smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 286371345Sgshapiro CLR_HELO(mci->mci_flags); 286464562Sgshapiro 286590792Sgshapiro if (IS_DLVR_RETURN(e)) 286690792Sgshapiro { 286790792Sgshapiro /* 286890792Sgshapiro ** Check whether other side can deliver e-mail 286990792Sgshapiro ** fast enough 287090792Sgshapiro */ 287190792Sgshapiro 287290792Sgshapiro if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 287390792Sgshapiro { 287490792Sgshapiro e->e_status = "5.4.7"; 287590792Sgshapiro usrerrenh(e->e_status, 287690792Sgshapiro "554 Server does not support Deliver By"); 287790792Sgshapiro rcode = EX_UNAVAILABLE; 287890792Sgshapiro goto give_up; 287990792Sgshapiro } 288090792Sgshapiro if (e->e_deliver_by > 0 && 288190792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime) < 288290792Sgshapiro mci->mci_min_by) 288390792Sgshapiro { 288490792Sgshapiro e->e_status = "5.4.7"; 288590792Sgshapiro usrerrenh(e->e_status, 288690792Sgshapiro "554 Message can't be delivered in time; %ld < %ld", 288790792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime), 288890792Sgshapiro mci->mci_min_by); 288990792Sgshapiro rcode = EX_UNAVAILABLE; 289090792Sgshapiro goto give_up; 289190792Sgshapiro } 289290792Sgshapiro } 289390792Sgshapiro 289464562Sgshapiro# if STARTTLS 289564562Sgshapiro /* first TLS then AUTH to provide a security layer */ 289671345Sgshapiro if (mci->mci_state != MCIS_CLOSED && 289771345Sgshapiro !DONE_STARTTLS(mci->mci_flags)) 289864562Sgshapiro { 289964562Sgshapiro int olderrors; 290064562Sgshapiro bool usetls; 290164562Sgshapiro bool saveQuickAbort = QuickAbort; 290264562Sgshapiro bool saveSuprErrs = SuprErrs; 290371345Sgshapiro char *host = NULL; 290464562Sgshapiro 290564562Sgshapiro rcode = EX_OK; 290664562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 290790792Sgshapiro if (usetls) 290890792Sgshapiro usetls = !iscltflgset(e, D_NOTLS); 290966494Sgshapiro 291064562Sgshapiro if (usetls) 291164562Sgshapiro { 291290792Sgshapiro host = macvalue(macid("{server_name}"), e); 291364562Sgshapiro olderrors = Errors; 291490792Sgshapiro QuickAbort = false; 291590792Sgshapiro SuprErrs = true; 2916102528Sgshapiro if (rscheck("try_tls", host, NULL, e, 2917102528Sgshapiro RSF_RMCOMM, 7, host, NOQID) != EX_OK 291864562Sgshapiro || Errors > olderrors) 291990792Sgshapiro usetls = false; 292064562Sgshapiro SuprErrs = saveSuprErrs; 292164562Sgshapiro QuickAbort = saveQuickAbort; 292264562Sgshapiro } 292364562Sgshapiro 292464562Sgshapiro if (usetls) 292564562Sgshapiro { 292664562Sgshapiro if ((rcode = starttls(m, mci, e)) == EX_OK) 292764562Sgshapiro { 292864562Sgshapiro /* start again without STARTTLS */ 292964562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 293064562Sgshapiro } 293164562Sgshapiro else 293264562Sgshapiro { 293364562Sgshapiro char *s; 293464562Sgshapiro 293564562Sgshapiro /* 293664562Sgshapiro ** TLS negotation failed, what to do? 293764562Sgshapiro ** fall back to unencrypted connection 293864562Sgshapiro ** or abort? How to decide? 293964562Sgshapiro ** set a macro and call a ruleset. 294064562Sgshapiro */ 294190792Sgshapiro 294264562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 294364562Sgshapiro switch (rcode) 294464562Sgshapiro { 294564562Sgshapiro case EX_TEMPFAIL: 294664562Sgshapiro s = "TEMP"; 294764562Sgshapiro break; 294864562Sgshapiro case EX_USAGE: 294964562Sgshapiro s = "USAGE"; 295064562Sgshapiro break; 295164562Sgshapiro case EX_PROTOCOL: 295264562Sgshapiro s = "PROTOCOL"; 295364562Sgshapiro break; 295464562Sgshapiro case EX_SOFTWARE: 295564562Sgshapiro s = "SOFTWARE"; 295664562Sgshapiro break; 295764562Sgshapiro 295864562Sgshapiro /* everything else is a failure */ 295964562Sgshapiro default: 296064562Sgshapiro s = "FAILURE"; 296164562Sgshapiro rcode = EX_TEMPFAIL; 296264562Sgshapiro } 296390792Sgshapiro macdefine(&e->e_macro, A_PERM, 296490792Sgshapiro macid("{verify}"), s); 296564562Sgshapiro } 296664562Sgshapiro } 296764562Sgshapiro else 296890792Sgshapiro macdefine(&e->e_macro, A_PERM, 296990792Sgshapiro macid("{verify}"), "NONE"); 297064562Sgshapiro olderrors = Errors; 297190792Sgshapiro QuickAbort = false; 297290792Sgshapiro SuprErrs = true; 297364562Sgshapiro 297464562Sgshapiro /* 297564562Sgshapiro ** rcode == EX_SOFTWARE is special: 297664562Sgshapiro ** the TLS negotation failed 297764562Sgshapiro ** we have to drop the connection no matter what 297864562Sgshapiro ** However, we call tls_server to give it the chance 297964562Sgshapiro ** to log the problem and return an appropriate 298064562Sgshapiro ** error code. 298164562Sgshapiro */ 298290792Sgshapiro 298364562Sgshapiro if (rscheck("tls_server", 298490792Sgshapiro macvalue(macid("{verify}"), e), 2985102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 2986102528Sgshapiro host, NOQID) != EX_OK || 298764562Sgshapiro Errors > olderrors || 298864562Sgshapiro rcode == EX_SOFTWARE) 298964562Sgshapiro { 299064562Sgshapiro char enhsc[ENHSCLEN]; 299164562Sgshapiro extern char MsgBuf[]; 299264562Sgshapiro 299364562Sgshapiro if (ISSMTPCODE(MsgBuf) && 299464562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 299564562Sgshapiro { 299690792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 299790792Sgshapiro MsgBuf); 299864562Sgshapiro } 299964562Sgshapiro else 300064562Sgshapiro { 300164562Sgshapiro p = "403 4.7.0 server not authenticated."; 300290792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 300390792Sgshapiro sizeof enhsc); 300464562Sgshapiro } 300564562Sgshapiro SuprErrs = saveSuprErrs; 300664562Sgshapiro QuickAbort = saveQuickAbort; 300764562Sgshapiro 300864562Sgshapiro if (rcode == EX_SOFTWARE) 300964562Sgshapiro { 301064562Sgshapiro /* drop the connection */ 301164562Sgshapiro mci->mci_state = MCIS_QUITING; 301264562Sgshapiro if (mci->mci_in != NULL) 301364562Sgshapiro { 301490792Sgshapiro (void) sm_io_close(mci->mci_in, 301590792Sgshapiro SM_TIME_DEFAULT); 301664562Sgshapiro mci->mci_in = NULL; 301764562Sgshapiro } 301864562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 301964562Sgshapiro (void) endmailer(mci, e, pv); 302064562Sgshapiro } 302164562Sgshapiro else 302264562Sgshapiro { 302364562Sgshapiro /* abort transfer */ 302464562Sgshapiro smtpquit(m, mci, e); 302564562Sgshapiro } 302664562Sgshapiro 302771345Sgshapiro /* avoid bogus error msg */ 302871345Sgshapiro mci->mci_errno = 0; 302971345Sgshapiro 303064562Sgshapiro /* temp or permanent failure? */ 303164562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 303264562Sgshapiro : EX_UNAVAILABLE; 303390792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 303464562Sgshapiro 303564562Sgshapiro /* 303664562Sgshapiro ** hack to get the error message into 303764562Sgshapiro ** the envelope (done in giveresponse()) 303864562Sgshapiro */ 303990792Sgshapiro 304090792Sgshapiro (void) sm_strlcpy(SmtpError, p, 304190792Sgshapiro sizeof SmtpError); 304264562Sgshapiro } 304364562Sgshapiro QuickAbort = saveQuickAbort; 304464562Sgshapiro SuprErrs = saveSuprErrs; 304571345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 304671345Sgshapiro mci->mci_state != MCIS_CLOSED) 304764562Sgshapiro { 304871345Sgshapiro SET_HELO(mci->mci_flags); 304964562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 305064562Sgshapiro goto reconnect; 305164562Sgshapiro } 305264562Sgshapiro } 305364562Sgshapiro# endif /* STARTTLS */ 305464562Sgshapiro# if SASL 305564562Sgshapiro /* if other server supports authentication let's authenticate */ 305664562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 305764562Sgshapiro mci->mci_saslcap != NULL && 305890792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 305964562Sgshapiro { 306090792Sgshapiro /* Should we require some minimum authentication? */ 306190792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 306264562Sgshapiro { 306364562Sgshapiro int result; 306490792Sgshapiro sasl_ssf_t *ssf = NULL; 306564562Sgshapiro 306690792Sgshapiro /* Get security strength (features) */ 306764562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 306898121Sgshapiro# if SASL >= 20000 306998121Sgshapiro (const void **) &ssf); 307098121Sgshapiro# else /* SASL >= 20000 */ 307164562Sgshapiro (void **) &ssf); 307298121Sgshapiro# endif /* SASL >= 20000 */ 307390792Sgshapiro 307490792Sgshapiro /* XXX authid? */ 307564562Sgshapiro if (LogLevel > 9) 307664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 307790792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 307864562Sgshapiro mci->mci_host, 307990792Sgshapiro macvalue(macid("{auth_type}"), e), 308090792Sgshapiro result == SASL_OK ? *ssf : 0); 308177349Sgshapiro 308264562Sgshapiro /* 308390792Sgshapiro ** Only switch to encrypted connection 308464562Sgshapiro ** if a security layer has been negotiated 308564562Sgshapiro */ 308690792Sgshapiro 308764562Sgshapiro if (result == SASL_OK && *ssf > 0) 308864562Sgshapiro { 308964562Sgshapiro /* 309090792Sgshapiro ** Convert I/O layer to use SASL. 309190792Sgshapiro ** If the call fails, the connection 309290792Sgshapiro ** is aborted. 309364562Sgshapiro */ 309490792Sgshapiro 309590792Sgshapiro if (sfdcsasl(&mci->mci_in, 309690792Sgshapiro &mci->mci_out, 309764562Sgshapiro mci->mci_conn) == 0) 309864562Sgshapiro { 309964562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 310090792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 310190792Sgshapiro MCIF_ONLY_EHLO; 310264562Sgshapiro goto reconnect; 310364562Sgshapiro } 310490792Sgshapiro syserr("AUTH TLS switch failed in client"); 310564562Sgshapiro } 310664562Sgshapiro /* else? XXX */ 310764562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 310864562Sgshapiro 310964562Sgshapiro } 311090792Sgshapiro else if (ret == EX_TEMPFAIL) 311190792Sgshapiro { 311290792Sgshapiro if (LogLevel > 8) 311390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 311490792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 311590792Sgshapiro mci->mci_host); 311690792Sgshapiro smtpquit(m, mci, e); 311790792Sgshapiro 311890792Sgshapiro /* avoid bogus error msg */ 311990792Sgshapiro mci->mci_errno = 0; 312090792Sgshapiro rcode = EX_TEMPFAIL; 312190792Sgshapiro mci_setstat(mci, rcode, "4.7.1", p); 312290792Sgshapiro 312390792Sgshapiro /* 312490792Sgshapiro ** hack to get the error message into 312590792Sgshapiro ** the envelope (done in giveresponse()) 312690792Sgshapiro */ 312790792Sgshapiro 312890792Sgshapiro (void) sm_strlcpy(SmtpError, 312990792Sgshapiro "Temporary AUTH failure", 313090792Sgshapiro sizeof SmtpError); 313190792Sgshapiro } 313264562Sgshapiro } 313364562Sgshapiro# endif /* SASL */ 313438032Speter } 313538032Speter 313664562Sgshapiro 313738032Speterdo_transfer: 313838032Speter /* clear out per-message flags from connection structure */ 313938032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 314038032Speter 314138032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 314238032Speter !bitset(EF_DONT_MIME, e->e_flags) && 314338032Speter bitnset(M_7BITS, m->m_flags)) 314438032Speter mci->mci_flags |= MCIF_CVT8TO7; 314538032Speter 314638032Speter#if MIME7TO8 314738032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 314838032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 314938032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 315090792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 315190792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 315238032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 315338032Speter { 315438032Speter /* may want to convert 7 -> 8 */ 315538032Speter /* XXX should really parse it here -- and use a class XXX */ 315690792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 315738032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 315838032Speter mci->mci_flags |= MCIF_CVT7TO8; 315938032Speter } 316064562Sgshapiro#endif /* MIME7TO8 */ 316138032Speter 316238032Speter if (tTd(11, 1)) 316338032Speter { 316490792Sgshapiro sm_dprintf("openmailer: "); 316590792Sgshapiro mci_dump(mci, false); 316638032Speter } 316738032Speter 316890792Sgshapiro#if _FFR_CLIENT_SIZE 316990792Sgshapiro /* 317090792Sgshapiro ** See if we know the maximum size and 317190792Sgshapiro ** abort if the message is too big. 317290792Sgshapiro ** 317390792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 317490792Sgshapiro */ 317590792Sgshapiro 317690792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 317790792Sgshapiro mci->mci_maxsize > 0 && 317890792Sgshapiro e->e_msgsize > mci->mci_maxsize) 317990792Sgshapiro { 318090792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 318190792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 318290792Sgshapiro e->e_status = "5.2.3"; 318390792Sgshapiro else 318490792Sgshapiro e->e_status = "5.3.4"; 318590792Sgshapiro 318690792Sgshapiro usrerrenh(e->e_status, 318790792Sgshapiro "552 Message is too large; %ld bytes max", 318890792Sgshapiro mci->mci_maxsize); 318990792Sgshapiro rcode = EX_DATAERR; 319090792Sgshapiro 319190792Sgshapiro /* Need an e_message for error */ 319290792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 319390792Sgshapiro "Message is too large; %ld bytes max", 319490792Sgshapiro mci->mci_maxsize); 319590792Sgshapiro goto give_up; 319690792Sgshapiro } 319790792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 319890792Sgshapiro 319938032Speter if (mci->mci_state != MCIS_OPEN) 320038032Speter { 320138032Speter /* couldn't open the mailer */ 320238032Speter rcode = mci->mci_exitstat; 320338032Speter errno = mci->mci_errno; 320473188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 320538032Speter if (rcode == EX_OK) 320638032Speter { 320738032Speter /* shouldn't happen */ 320864562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 320990792Sgshapiro (unsigned long) mci, rcode, errno, 321090792Sgshapiro mci->mci_state, firstsig); 321190792Sgshapiro mci_dump_all(true); 321238032Speter rcode = EX_SOFTWARE; 321338032Speter } 321464562Sgshapiro else if (nummxhosts > hostnum) 321538032Speter { 321638032Speter /* try next MX site */ 321738032Speter goto tryhost; 321838032Speter } 321938032Speter } 322038032Speter else if (!clever) 322138032Speter { 322238032Speter /* 322338032Speter ** Format and send message. 322438032Speter */ 322538032Speter 322638032Speter putfromline(mci, e); 322743730Speter (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 322838032Speter (*e->e_putbody)(mci, e, NULL); 322938032Speter 323038032Speter /* get the exit status */ 323138032Speter rcode = endmailer(mci, e, pv); 323290792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 323373188Sgshapiro { 323473188Sgshapiro /* 323573188Sgshapiro ** Need an e_message for mailq display. 323673188Sgshapiro ** We set SmtpError as 323773188Sgshapiro */ 323873188Sgshapiro 323990792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 324090792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 324190792Sgshapiro m->m_name, m->m_mailer); 324273188Sgshapiro } 324338032Speter } 324438032Speter else 324538032Speter { 324638032Speter /* 324738032Speter ** Send the MAIL FROM: protocol 324838032Speter */ 324938032Speter 325090792Sgshapiro /* XXX this isn't pipelined... */ 325138032Speter rcode = smtpmailfrom(m, mci, e); 325238032Speter if (rcode == EX_OK) 325338032Speter { 325438032Speter register int i; 325590792Sgshapiro# if PIPELINING 325690792Sgshapiro ADDRESS *volatile pchain; 325790792Sgshapiro# endif /* PIPELINING */ 325838032Speter 325938032Speter /* send the recipient list */ 326038032Speter tobuf[0] = '\0'; 326190792Sgshapiro mci->mci_retryrcpt = false; 326290792Sgshapiro mci->mci_tolist = tobuf; 326390792Sgshapiro# if PIPELINING 326490792Sgshapiro pchain = NULL; 326590792Sgshapiro mci->mci_nextaddr = NULL; 326690792Sgshapiro# endif /* PIPELINING */ 326764562Sgshapiro 326838032Speter for (to = tochain; to != NULL; to = to->q_tchain) 326938032Speter { 327090792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 327138032Speter continue; 327264562Sgshapiro 327390792Sgshapiro /* mark recipient state as "ok so far" */ 327490792Sgshapiro to->q_state = QS_OK; 327590792Sgshapiro e->e_to = to->q_paddr; 327664562Sgshapiro# if STARTTLS 327764562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3278102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3279102528Sgshapiro mci->mci_host, e->e_id); 328064562Sgshapiro if (i != EX_OK) 328138032Speter { 328290792Sgshapiro markfailure(e, to, mci, i, false); 328390792Sgshapiro giveresponse(i, to->q_status, m, mci, 328490792Sgshapiro ctladdr, xstart, e, to); 328590792Sgshapiro if (i == EX_TEMPFAIL) 328690792Sgshapiro { 328790792Sgshapiro mci->mci_retryrcpt = true; 328890792Sgshapiro to->q_state = QS_RETRY; 328990792Sgshapiro } 329064562Sgshapiro continue; 329138032Speter } 329264562Sgshapiro# endif /* STARTTLS */ 329364562Sgshapiro 329490792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 329590792Sgshapiro# if PIPELINING 329690792Sgshapiro if (i == EX_OK && 329790792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 329864562Sgshapiro { 329990792Sgshapiro /* 330090792Sgshapiro ** Add new element to list of 330190792Sgshapiro ** recipients for pipelining. 330290792Sgshapiro */ 330390792Sgshapiro 330490792Sgshapiro to->q_pchain = NULL; 330590792Sgshapiro if (mci->mci_nextaddr == NULL) 330690792Sgshapiro mci->mci_nextaddr = to; 330790792Sgshapiro if (pchain == NULL) 330890792Sgshapiro pchain = to; 330990792Sgshapiro else 331090792Sgshapiro { 331190792Sgshapiro pchain->q_pchain = to; 331290792Sgshapiro pchain = pchain->q_pchain; 331390792Sgshapiro } 331464562Sgshapiro } 331590792Sgshapiro# endif /* PIPELINING */ 331690792Sgshapiro if (i != EX_OK) 331738032Speter { 331890792Sgshapiro markfailure(e, to, mci, i, false); 331998841Sgshapiro giveresponse(i, to->q_status, m, mci, 332090792Sgshapiro ctladdr, xstart, e, to); 332190792Sgshapiro if (i == EX_TEMPFAIL) 332290792Sgshapiro to->q_state = QS_RETRY; 332338032Speter } 332438032Speter } 332538032Speter 332690792Sgshapiro /* No recipients in list and no missing responses? */ 332790792Sgshapiro if (tobuf[0] == '\0' 332890792Sgshapiro# if PIPELINING 332990792Sgshapiro && mci->mci_nextaddr == NULL 333090792Sgshapiro# endif /* PIPELINING */ 333190792Sgshapiro ) 333238032Speter { 333338032Speter rcode = EX_OK; 333438032Speter e->e_to = NULL; 333538032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 333638032Speter smtprset(m, mci, e); 333738032Speter } 333838032Speter else 333938032Speter { 334038032Speter e->e_to = tobuf + 1; 334190792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 334238032Speter } 334338032Speter } 334464562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 334538032Speter { 334638032Speter /* try next MX site */ 334738032Speter goto tryhost; 334838032Speter } 334938032Speter } 335038032Speter#if NAMED_BIND 335138032Speter if (ConfigLevel < 2) 335238032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 335364562Sgshapiro#endif /* NAMED_BIND */ 335438032Speter 335538032Speter if (tTd(62, 1)) 335638032Speter checkfds("after delivery"); 335738032Speter 335838032Speter /* 335938032Speter ** Do final status disposal. 336038032Speter ** We check for something in tobuf for the SMTP case. 336138032Speter ** If we got a temporary failure, arrange to queue the 336238032Speter ** addressees. 336338032Speter */ 336438032Speter 336538032Speter give_up: 336638032Speter if (bitnset(M_LMTP, m->m_flags)) 336738032Speter { 336838032Speter lmtp_rcode = rcode; 336938032Speter tobuf[0] = '\0'; 337090792Sgshapiro anyok = false; 337190792Sgshapiro strsize = 0; 337238032Speter } 337338032Speter else 337438032Speter anyok = rcode == EX_OK; 337538032Speter 337638032Speter for (to = tochain; to != NULL; to = to->q_tchain) 337738032Speter { 337838032Speter /* see if address already marked */ 337964562Sgshapiro if (!QS_IS_OK(to->q_state)) 338038032Speter continue; 338138032Speter 338238032Speter /* if running LMTP, get the status for each address */ 338338032Speter if (bitnset(M_LMTP, m->m_flags)) 338438032Speter { 338538032Speter if (lmtp_rcode == EX_OK) 338638032Speter rcode = smtpgetstat(m, mci, e); 338738032Speter if (rcode == EX_OK) 338838032Speter { 338990792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 339090792Sgshapiro to->q_paddr, 339190792Sgshapiro tobufsize - strsize); 339290792Sgshapiro SM_ASSERT(strsize < tobufsize); 339390792Sgshapiro anyok = true; 339438032Speter } 339538032Speter else 339638032Speter { 339738032Speter e->e_to = to->q_paddr; 339890792Sgshapiro markfailure(e, to, mci, rcode, true); 339964562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 340090792Sgshapiro ctladdr, xstart, e, to); 340138032Speter e->e_to = tobuf + 1; 340238032Speter continue; 340338032Speter } 340438032Speter } 340538032Speter else 340638032Speter { 340738032Speter /* mark bad addresses */ 340838032Speter if (rcode != EX_OK) 340938032Speter { 341038032Speter if (goodmxfound && rcode == EX_NOHOST) 341138032Speter rcode = EX_TEMPFAIL; 341290792Sgshapiro markfailure(e, to, mci, rcode, true); 341338032Speter continue; 341438032Speter } 341538032Speter } 341638032Speter 341738032Speter /* successful delivery */ 341864562Sgshapiro to->q_state = QS_SENT; 341938032Speter to->q_statdate = curtime(); 342038032Speter e->e_nsent++; 342164562Sgshapiro 342264562Sgshapiro /* 342364562Sgshapiro ** Checkpoint the send list every few addresses 342464562Sgshapiro */ 342564562Sgshapiro 342666494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 342764562Sgshapiro { 342890792Sgshapiro queueup(e, false, false); 342964562Sgshapiro e->e_nsent = 0; 343064562Sgshapiro } 343164562Sgshapiro 343238032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 343338032Speter bitset(QPINGONSUCCESS, to->q_flags)) 343438032Speter { 343538032Speter to->q_flags |= QDELIVERED; 343638032Speter to->q_status = "2.1.5"; 343790792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 343890792Sgshapiro "%s... Successfully delivered\n", 343990792Sgshapiro to->q_paddr); 344038032Speter } 344138032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 344238032Speter bitset(QPRIMARY, to->q_flags) && 344338032Speter !bitset(MCIF_DSN, mci->mci_flags)) 344438032Speter { 344538032Speter to->q_flags |= QRELAYED; 344690792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 344790792Sgshapiro "%s... relayed; expect no further notifications\n", 344890792Sgshapiro to->q_paddr); 344938032Speter } 345090792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 345190792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 345290792Sgshapiro bitset(QPRIMARY, to->q_flags) && 345390792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 345490792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 345590792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 345690792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 345790792Sgshapiro { 345890792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 345990792Sgshapiro to->q_flags |= QBYNRELAY; 346090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 346190792Sgshapiro "%s... Deliver-by notify: relayed\n", 346290792Sgshapiro to->q_paddr); 346390792Sgshapiro } 346490792Sgshapiro else if (IS_DLVR_TRACE(e) && 346590792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 346690792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 346790792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 346890792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 346990792Sgshapiro bitset(QPRIMARY, to->q_flags)) 347090792Sgshapiro { 347190792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 347290792Sgshapiro to->q_flags |= QBYTRACE; 347390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 347490792Sgshapiro "%s... Deliver-By trace: relayed\n", 347590792Sgshapiro to->q_paddr); 347690792Sgshapiro } 347738032Speter } 347838032Speter 347938032Speter if (bitnset(M_LMTP, m->m_flags)) 348038032Speter { 348138032Speter /* 348238032Speter ** Global information applies to the last recipient only; 348338032Speter ** clear it out to avoid bogus errors. 348438032Speter */ 348538032Speter 348638032Speter rcode = EX_OK; 348738032Speter e->e_statmsg = NULL; 348838032Speter 348938032Speter /* reset the mci state for the next transaction */ 349090792Sgshapiro if (mci != NULL && 349190792Sgshapiro (mci->mci_state == MCIS_MAIL || 349290792Sgshapiro mci->mci_state == MCIS_RCPT || 349390792Sgshapiro mci->mci_state == MCIS_DATA)) 3494125820Sgshapiro { 349538032Speter mci->mci_state = MCIS_OPEN; 3496125820Sgshapiro SmtpPhase = mci->mci_phase = "idle"; 3497125820Sgshapiro sm_setproctitle(true, e, "%s: %s", CurHostName, 3498125820Sgshapiro mci->mci_phase); 3499125820Sgshapiro } 350038032Speter } 350138032Speter 350238032Speter if (tobuf[0] != '\0') 350390792Sgshapiro { 350490792Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 350590792Sgshapiro#if 0 350690792Sgshapiro /* 350790792Sgshapiro ** This code is disabled for now because I am not 350890792Sgshapiro ** sure that copying status from the first recipient 350990792Sgshapiro ** to all non-status'ed recipients is a good idea. 351090792Sgshapiro */ 351190792Sgshapiro 351290792Sgshapiro if (tochain->q_message != NULL && 351390792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 351490792Sgshapiro { 351590792Sgshapiro for (to = tochain->q_tchain; to != NULL; 351690792Sgshapiro to = to->q_tchain) 351790792Sgshapiro { 351890792Sgshapiro /* see if address already marked */ 351990792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 352090792Sgshapiro to->q_message == NULL) 352190792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 352290792Sgshapiro tochain->q_message); 352390792Sgshapiro } 352490792Sgshapiro } 352590792Sgshapiro#endif /* 0 */ 352690792Sgshapiro } 352738032Speter if (anyok) 352890792Sgshapiro markstats(e, tochain, STATS_NORMAL); 352938032Speter mci_store_persistent(mci); 353038032Speter 353190792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 353290792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 353390792Sgshapiro { 353490792Sgshapiro /* try next MX site */ 353590792Sgshapiro goto tryhost; 353690792Sgshapiro } 353790792Sgshapiro 353838032Speter /* now close the connection */ 353938032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 354038032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 354138032Speter smtpquit(m, mci, e); 354238032Speter 354390792Sgshapirocleanup: ; 354490792Sgshapiro } 354590792Sgshapiro SM_FINALLY 354690792Sgshapiro { 354790792Sgshapiro /* 354890792Sgshapiro ** Restore state and return. 354990792Sgshapiro */ 355038032Speter#if XDEBUG 355138032Speter char wbuf[MAXLINE]; 355238032Speter 355338032Speter /* make absolutely certain 0, 1, and 2 are in use */ 355490792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, 355590792Sgshapiro "%s... end of deliver(%s)", 355690792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 355790792Sgshapiro : shortenstring(e->e_to, 355890792Sgshapiro MAXSHORTSTR), 355990792Sgshapiro m->m_name); 356038032Speter checkfd012(wbuf); 356164562Sgshapiro#endif /* XDEBUG */ 356238032Speter 356390792Sgshapiro errno = 0; 356490792Sgshapiro 356590792Sgshapiro /* 356690792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 356790792Sgshapiro ** because it previously pointed to an auto buffer. 356890792Sgshapiro ** We don't do this any more, so this may be unnecessary. 356990792Sgshapiro */ 357090792Sgshapiro 357190792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 357290792Sgshapiro e->e_to = NULL; 357390792Sgshapiro } 357490792Sgshapiro SM_END_TRY 357564562Sgshapiro return rcode; 357638032Speter} 357764562Sgshapiro 357890792Sgshapiro/* 357938032Speter** MARKFAILURE -- mark a failure on a specific address. 358038032Speter** 358138032Speter** Parameters: 358238032Speter** e -- the envelope we are sending. 358338032Speter** q -- the address to mark. 358438032Speter** mci -- mailer connection information. 358538032Speter** rcode -- the code signifying the particular failure. 358664562Sgshapiro** ovr -- override an existing code? 358738032Speter** 358838032Speter** Returns: 358938032Speter** none. 359038032Speter** 359138032Speter** Side Effects: 359238032Speter** marks the address (and possibly the envelope) with the 359338032Speter** failure so that an error will be returned or 359438032Speter** the message will be queued, as appropriate. 359538032Speter*/ 359638032Speter 359790792Sgshapirovoid 359864562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 359938032Speter register ENVELOPE *e; 360038032Speter register ADDRESS *q; 360138032Speter register MCI *mci; 360238032Speter int rcode; 360364562Sgshapiro bool ovr; 360438032Speter{ 360590792Sgshapiro int save_errno = errno; 360664562Sgshapiro char *status = NULL; 360764562Sgshapiro char *rstatus = NULL; 360838032Speter 360938032Speter switch (rcode) 361038032Speter { 361138032Speter case EX_OK: 361238032Speter break; 361338032Speter 361438032Speter case EX_TEMPFAIL: 361538032Speter case EX_IOERR: 361638032Speter case EX_OSERR: 361764562Sgshapiro q->q_state = QS_QUEUEUP; 361838032Speter break; 361938032Speter 362038032Speter default: 362164562Sgshapiro q->q_state = QS_BADADDR; 362238032Speter break; 362338032Speter } 362438032Speter 362538032Speter /* find most specific error code possible */ 362638032Speter if (mci != NULL && mci->mci_status != NULL) 362738032Speter { 362890792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 362938032Speter if (mci->mci_rstatus != NULL) 363090792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 363190792Sgshapiro mci->mci_rstatus); 363238032Speter else 363364562Sgshapiro rstatus = NULL; 363438032Speter } 363538032Speter else if (e->e_status != NULL) 363638032Speter { 363764562Sgshapiro status = e->e_status; 363864562Sgshapiro rstatus = NULL; 363938032Speter } 364038032Speter else 364138032Speter { 364238032Speter switch (rcode) 364338032Speter { 364438032Speter case EX_USAGE: 364564562Sgshapiro status = "5.5.4"; 364638032Speter break; 364738032Speter 364838032Speter case EX_DATAERR: 364964562Sgshapiro status = "5.5.2"; 365038032Speter break; 365138032Speter 365238032Speter case EX_NOUSER: 365364562Sgshapiro status = "5.1.1"; 365438032Speter break; 365538032Speter 365638032Speter case EX_NOHOST: 365764562Sgshapiro status = "5.1.2"; 365838032Speter break; 365938032Speter 366038032Speter case EX_NOINPUT: 366138032Speter case EX_CANTCREAT: 366238032Speter case EX_NOPERM: 366364562Sgshapiro status = "5.3.0"; 366438032Speter break; 366538032Speter 366638032Speter case EX_UNAVAILABLE: 366738032Speter case EX_SOFTWARE: 366838032Speter case EX_OSFILE: 366938032Speter case EX_PROTOCOL: 367038032Speter case EX_CONFIG: 367164562Sgshapiro status = "5.5.0"; 367238032Speter break; 367338032Speter 367438032Speter case EX_OSERR: 367538032Speter case EX_IOERR: 367664562Sgshapiro status = "4.5.0"; 367738032Speter break; 367838032Speter 367938032Speter case EX_TEMPFAIL: 368064562Sgshapiro status = "4.2.0"; 368138032Speter break; 368238032Speter } 368338032Speter } 368438032Speter 368564562Sgshapiro /* new status? */ 368664562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 368764562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 368864562Sgshapiro { 368964562Sgshapiro q->q_status = status; 369064562Sgshapiro q->q_rstatus = rstatus; 369164562Sgshapiro } 369238032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 369338032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 369490792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 369538032Speter { 369664562Sgshapiro char buf[16]; 369738032Speter 369890792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", rcode); 369990792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 370038032Speter } 370164562Sgshapiro 370264562Sgshapiro q->q_statdate = curtime(); 370364562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 370464562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 370590792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 370690792Sgshapiro 370790792Sgshapiro /* restore errno */ 370890792Sgshapiro errno = save_errno; 370938032Speter} 371090792Sgshapiro/* 371138032Speter** ENDMAILER -- Wait for mailer to terminate. 371238032Speter** 371338032Speter** We should never get fatal errors (e.g., segmentation 371438032Speter** violation), so we report those specially. For other 371538032Speter** errors, we choose a status message (into statmsg), 371638032Speter** and if it represents an error, we print it. 371738032Speter** 371838032Speter** Parameters: 371980785Sgshapiro** mci -- the mailer connection info. 372038032Speter** e -- the current envelope. 372138032Speter** pv -- the parameter vector that invoked the mailer 372238032Speter** (for error messages). 372338032Speter** 372438032Speter** Returns: 372538032Speter** exit code of mailer. 372638032Speter** 372738032Speter** Side Effects: 372838032Speter** none. 372938032Speter*/ 373038032Speter 373164562Sgshapirostatic jmp_buf EndWaitTimeout; 373264562Sgshapiro 373364562Sgshapirostatic void 373464562Sgshapiroendwaittimeout() 373564562Sgshapiro{ 373677349Sgshapiro /* 373777349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 373877349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 373977349Sgshapiro ** DOING. 374077349Sgshapiro */ 374177349Sgshapiro 374264562Sgshapiro errno = ETIMEDOUT; 374364562Sgshapiro longjmp(EndWaitTimeout, 1); 374464562Sgshapiro} 374564562Sgshapiro 374638032Speterint 374738032Speterendmailer(mci, e, pv) 374838032Speter register MCI *mci; 374938032Speter register ENVELOPE *e; 375038032Speter char **pv; 375138032Speter{ 375238032Speter int st; 375364562Sgshapiro int save_errno = errno; 375464562Sgshapiro char buf[MAXLINE]; 375590792Sgshapiro SM_EVENT *ev = NULL; 375638032Speter 375764562Sgshapiro 375838032Speter mci_unlock_host(mci); 375938032Speter 376077349Sgshapiro /* close output to mailer */ 376177349Sgshapiro if (mci->mci_out != NULL) 376290792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 376377349Sgshapiro 376477349Sgshapiro /* copy any remaining input to transcript */ 376577349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 376677349Sgshapiro e->e_xfp != NULL) 376777349Sgshapiro { 376877349Sgshapiro while (sfgets(buf, sizeof buf, mci->mci_in, 376990792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 377090792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 377177349Sgshapiro } 377277349Sgshapiro 377364562Sgshapiro#if SASL 377490792Sgshapiro /* close SASL connection */ 377564562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 377664562Sgshapiro { 377764562Sgshapiro sasl_dispose(&mci->mci_conn); 377864562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 377964562Sgshapiro } 378064562Sgshapiro#endif /* SASL */ 378164562Sgshapiro 378264562Sgshapiro#if STARTTLS 378364562Sgshapiro /* shutdown TLS */ 378464562Sgshapiro (void) endtlsclt(mci); 378564562Sgshapiro#endif /* STARTTLS */ 378664562Sgshapiro 378764562Sgshapiro /* now close the input */ 378838032Speter if (mci->mci_in != NULL) 378990792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 379038032Speter mci->mci_in = mci->mci_out = NULL; 379138032Speter mci->mci_state = MCIS_CLOSED; 379238032Speter 379364562Sgshapiro errno = save_errno; 379464562Sgshapiro 379538032Speter /* in the IPC case there is nothing to wait for */ 379638032Speter if (mci->mci_pid == 0) 379764562Sgshapiro return EX_OK; 379838032Speter 379964562Sgshapiro /* put a timeout around the wait */ 380064562Sgshapiro if (mci->mci_mailer->m_wait > 0) 380164562Sgshapiro { 380264562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 380390792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 380490792Sgshapiro endwaittimeout, 0); 380564562Sgshapiro else 380664562Sgshapiro { 380766494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 380864562Sgshapiro mci->mci_mailer->m_name, 380966494Sgshapiro (long) mci->mci_mailer->m_wait); 381064562Sgshapiro return EX_TEMPFAIL; 381164562Sgshapiro } 381264562Sgshapiro } 381338032Speter 381464562Sgshapiro /* wait for the mailer process, collect status */ 381538032Speter st = waitfor(mci->mci_pid); 381664562Sgshapiro save_errno = errno; 381764562Sgshapiro if (ev != NULL) 381890792Sgshapiro sm_clrevent(ev); 381964562Sgshapiro errno = save_errno; 382064562Sgshapiro 382138032Speter if (st == -1) 382238032Speter { 382338032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 382464562Sgshapiro return EX_SOFTWARE; 382538032Speter } 382638032Speter 382738032Speter if (WIFEXITED(st)) 382838032Speter { 382938032Speter /* normal death -- return status */ 383038032Speter return (WEXITSTATUS(st)); 383138032Speter } 383238032Speter 383338032Speter /* it died a horrid death */ 383464562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 383564562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 383664562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 383764562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 383838032Speter 383938032Speter /* log the arguments */ 384038032Speter if (pv != NULL && e->e_xfp != NULL) 384138032Speter { 384238032Speter register char **av; 384338032Speter 384490792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 384538032Speter for (av = pv; *av != NULL; av++) 384690792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 384790792Sgshapiro *av); 384890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 384938032Speter } 385038032Speter 385138032Speter ExitStat = EX_TEMPFAIL; 385264562Sgshapiro return EX_TEMPFAIL; 385338032Speter} 385490792Sgshapiro/* 385538032Speter** GIVERESPONSE -- Interpret an error response from a mailer 385638032Speter** 385738032Speter** Parameters: 385864562Sgshapiro** status -- the status code from the mailer (high byte 385938032Speter** only; core dumps must have been taken care of 386038032Speter** already). 386164562Sgshapiro** dsn -- the DSN associated with the address, if any. 386238032Speter** m -- the mailer info for this mailer. 386338032Speter** mci -- the mailer connection info -- can be NULL if the 386438032Speter** response is given before the connection is made. 386538032Speter** ctladdr -- the controlling address for the recipient 386638032Speter** address(es). 386738032Speter** xstart -- the transaction start time, for computing 386838032Speter** transaction delays. 386938032Speter** e -- the current envelope. 387090792Sgshapiro** to -- the current recipient (NULL if none). 387138032Speter** 387238032Speter** Returns: 387338032Speter** none. 387438032Speter** 387538032Speter** Side Effects: 387638032Speter** Errors may be incremented. 387738032Speter** ExitStat may be set. 387838032Speter*/ 387938032Speter 388038032Spetervoid 388190792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 388264562Sgshapiro int status; 388364562Sgshapiro char *dsn; 388438032Speter register MAILER *m; 388538032Speter register MCI *mci; 388638032Speter ADDRESS *ctladdr; 388738032Speter time_t xstart; 388838032Speter ENVELOPE *e; 388990792Sgshapiro ADDRESS *to; 389038032Speter{ 389138032Speter register const char *statmsg; 389264562Sgshapiro int errnum = errno; 389364562Sgshapiro int off = 4; 389490792Sgshapiro bool usestat = false; 389564562Sgshapiro char dsnbuf[ENHSCLEN]; 389638032Speter char buf[MAXLINE]; 389790792Sgshapiro char *exmsg; 389838032Speter 389938032Speter if (e == NULL) 390038032Speter syserr("giveresponse: null envelope"); 390138032Speter 390238032Speter /* 390338032Speter ** Compute status message from code. 390438032Speter */ 390538032Speter 390690792Sgshapiro exmsg = sm_sysexmsg(status); 390764562Sgshapiro if (status == 0) 390838032Speter { 390964562Sgshapiro statmsg = "250 2.0.0 Sent"; 391038032Speter if (e->e_statmsg != NULL) 391138032Speter { 391290792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", 391390792Sgshapiro statmsg, 391490792Sgshapiro shortenstring(e->e_statmsg, 403)); 391538032Speter statmsg = buf; 391638032Speter } 391738032Speter } 391890792Sgshapiro else if (exmsg == NULL) 391938032Speter { 392090792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 392190792Sgshapiro "554 5.3.0 unknown mailer error %d", 392290792Sgshapiro status); 392364562Sgshapiro status = EX_UNAVAILABLE; 392438032Speter statmsg = buf; 392590792Sgshapiro usestat = true; 392638032Speter } 392764562Sgshapiro else if (status == EX_TEMPFAIL) 392838032Speter { 392938032Speter char *bp = buf; 393038032Speter 393190792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 393238032Speter bp += strlen(bp); 393338032Speter#if NAMED_BIND 393438032Speter if (h_errno == TRY_AGAIN) 393590792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 393638032Speter else 393764562Sgshapiro#endif /* NAMED_BIND */ 393838032Speter { 393964562Sgshapiro if (errnum != 0) 394090792Sgshapiro statmsg = sm_errstring(errnum); 394138032Speter else 394238032Speter statmsg = SmtpError; 394338032Speter } 394438032Speter if (statmsg != NULL && statmsg[0] != '\0') 394564562Sgshapiro { 394664562Sgshapiro switch (errnum) 394764562Sgshapiro { 394864562Sgshapiro#ifdef ENETDOWN 394964562Sgshapiro case ENETDOWN: /* Network is down */ 395064562Sgshapiro#endif /* ENETDOWN */ 395164562Sgshapiro#ifdef ENETUNREACH 395264562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 395364562Sgshapiro#endif /* ENETUNREACH */ 395464562Sgshapiro#ifdef ENETRESET 395564562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 395664562Sgshapiro#endif /* ENETRESET */ 395764562Sgshapiro#ifdef ECONNABORTED 395864562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 395964562Sgshapiro#endif /* ECONNABORTED */ 396064562Sgshapiro#ifdef EHOSTDOWN 396164562Sgshapiro case EHOSTDOWN: /* Host is down */ 396264562Sgshapiro#endif /* EHOSTDOWN */ 396364562Sgshapiro#ifdef EHOSTUNREACH 396464562Sgshapiro case EHOSTUNREACH: /* No route to host */ 396564562Sgshapiro#endif /* EHOSTUNREACH */ 396664562Sgshapiro if (mci->mci_host != NULL) 396764562Sgshapiro { 396890792Sgshapiro (void) sm_strlcpyn(bp, 396990792Sgshapiro SPACELEFT(buf, bp), 397090792Sgshapiro 2, ": ", 397190792Sgshapiro mci->mci_host); 397264562Sgshapiro bp += strlen(bp); 397364562Sgshapiro } 397464562Sgshapiro break; 397564562Sgshapiro } 397690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 397790792Sgshapiro statmsg); 397890792Sgshapiro usestat = true; 397964562Sgshapiro } 398038032Speter statmsg = buf; 398138032Speter } 398238032Speter#if NAMED_BIND 398364562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 398438032Speter { 398590792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 398690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1, 398790792Sgshapiro statmsg); 398838032Speter statmsg = buf; 398990792Sgshapiro usestat = true; 399038032Speter } 399164562Sgshapiro#endif /* NAMED_BIND */ 399238032Speter else 399338032Speter { 399490792Sgshapiro statmsg = exmsg; 399564562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 399638032Speter { 399790792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg, 399890792Sgshapiro sm_errstring(errnum)); 399938032Speter statmsg = buf; 400090792Sgshapiro usestat = true; 400138032Speter } 400294334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 400394334Sgshapiro { 400494334Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg, 400594334Sgshapiro shortenstring(e->e_statmsg, 403)); 400694334Sgshapiro statmsg = buf; 400794334Sgshapiro usestat = true; 400894334Sgshapiro } 400938032Speter } 401038032Speter 401138032Speter /* 401238032Speter ** Print the message as appropriate 401338032Speter */ 401438032Speter 401564562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 401638032Speter { 401738032Speter extern char MsgBuf[]; 401838032Speter 401964562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 402064562Sgshapiro { 402164562Sgshapiro if (dsn == NULL) 402264562Sgshapiro { 402390792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 402490792Sgshapiro "%.*s", off, statmsg + 4); 402564562Sgshapiro dsn = dsnbuf; 402664562Sgshapiro } 402764562Sgshapiro off += 5; 402864562Sgshapiro } 402964562Sgshapiro else 403064562Sgshapiro { 403164562Sgshapiro off = 4; 403264562Sgshapiro } 403364562Sgshapiro message("%s", statmsg + off); 403464562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 403590792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 403690792Sgshapiro &MsgBuf[4]); 403738032Speter } 403838032Speter else 403938032Speter { 404064562Sgshapiro char mbuf[ENHSCLEN + 4]; 404138032Speter 404238032Speter Errors++; 404364562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 404464562Sgshapiro off < sizeof mbuf - 4) 404564562Sgshapiro { 404664562Sgshapiro if (dsn == NULL) 404764562Sgshapiro { 404890792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 404990792Sgshapiro "%.*s", off, statmsg + 4); 405064562Sgshapiro dsn = dsnbuf; 405164562Sgshapiro } 405264562Sgshapiro off += 5; 405390792Sgshapiro 405490792Sgshapiro /* copy only part of statmsg to mbuf */ 405590792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 405690792Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof mbuf); 405764562Sgshapiro } 405864562Sgshapiro else 405964562Sgshapiro { 406064562Sgshapiro dsnbuf[0] = '\0'; 406190792Sgshapiro (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s", 406290792Sgshapiro statmsg); 406364562Sgshapiro off = 4; 406464562Sgshapiro } 406564562Sgshapiro usrerr(mbuf, &statmsg[off]); 406638032Speter } 406738032Speter 406838032Speter /* 406938032Speter ** Final cleanup. 407038032Speter ** Log a record of the transaction. Compute the new 407138032Speter ** ExitStat -- if we already had an error, stick with 407238032Speter ** that. 407338032Speter */ 407438032Speter 407538032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 407664562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 407764562Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 407838032Speter 407938032Speter if (tTd(11, 2)) 408090792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 408190792Sgshapiro status, 408290792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 408390792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 408490792Sgshapiro errnum); 408538032Speter 408664562Sgshapiro if (status != EX_TEMPFAIL) 408764562Sgshapiro setstat(status); 408864562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 408990792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 409090792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 409138032Speter { 409290792Sgshapiro if (!usestat && e->e_message != NULL) 409390792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 409490792Sgshapiro e->e_message); 409590792Sgshapiro else 409690792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 409790792Sgshapiro statmsg + off); 409838032Speter } 409938032Speter errno = 0; 410073188Sgshapiro SM_SET_H_ERRNO(0); 410138032Speter} 410290792Sgshapiro/* 410338032Speter** LOGDELIVERY -- log the delivery in the system log 410438032Speter** 410538032Speter** Care is taken to avoid logging lines that are too long, because 410638032Speter** some versions of syslog have an unfortunate proclivity for core 410738032Speter** dumping. This is a hack, to be sure, that is at best empirical. 410838032Speter** 410938032Speter** Parameters: 411038032Speter** m -- the mailer info. Can be NULL for initial queue. 411138032Speter** mci -- the mailer connection info -- can be NULL if the 411264562Sgshapiro** log is occurring when no connection is active. 411364562Sgshapiro** dsn -- the DSN attached to the status. 411464562Sgshapiro** status -- the message to print for the status. 411538032Speter** ctladdr -- the controlling address for the to list. 411638032Speter** xstart -- the transaction start time, used for 411738032Speter** computing transaction delay. 411838032Speter** e -- the current envelope. 411938032Speter** 412038032Speter** Returns: 412138032Speter** none 412238032Speter** 412338032Speter** Side Effects: 412438032Speter** none 412538032Speter*/ 412638032Speter 412738032Spetervoid 412864562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e) 412938032Speter MAILER *m; 413038032Speter register MCI *mci; 413164562Sgshapiro char *dsn; 413264562Sgshapiro const char *status; 413338032Speter ADDRESS *ctladdr; 413438032Speter time_t xstart; 413538032Speter register ENVELOPE *e; 413638032Speter{ 413738032Speter register char *bp; 413838032Speter register char *p; 413938032Speter int l; 414090792Sgshapiro time_t now = curtime(); 414138032Speter char buf[1024]; 414238032Speter 414364562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 414438032Speter /* ctladdr: max 106 bytes */ 414538032Speter bp = buf; 414638032Speter if (ctladdr != NULL) 414738032Speter { 414890792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 414990792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 415038032Speter bp += strlen(bp); 415138032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 415238032Speter { 415390792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 415490792Sgshapiro (int) ctladdr->q_uid, 415590792Sgshapiro (int) ctladdr->q_gid); 415638032Speter bp += strlen(bp); 415738032Speter } 415838032Speter } 415938032Speter 416038032Speter /* delay & xdelay: max 41 bytes */ 416190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 416290792Sgshapiro pintvl(now - e->e_ctime, true)); 416338032Speter bp += strlen(bp); 416438032Speter 416538032Speter if (xstart != (time_t) 0) 416638032Speter { 416790792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 416890792Sgshapiro pintvl(now - xstart, true)); 416938032Speter bp += strlen(bp); 417038032Speter } 417138032Speter 417238032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 417338032Speter if (m != NULL) 417438032Speter { 417590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 417690792Sgshapiro m->m_name); 417738032Speter bp += strlen(bp); 417838032Speter } 417938032Speter 418064562Sgshapiro /* pri: changes with each delivery attempt */ 418190792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 418290792Sgshapiro e->e_msgpriority); 418364562Sgshapiro bp += strlen(bp); 418464562Sgshapiro 418538032Speter /* relay: max 66 bytes for IPv4 addresses */ 418638032Speter if (mci != NULL && mci->mci_host != NULL) 418738032Speter { 418838032Speter extern SOCKADDR CurHostAddr; 418938032Speter 419090792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 419190792Sgshapiro shortenstring(mci->mci_host, 40)); 419238032Speter bp += strlen(bp); 419338032Speter 419438032Speter if (CurHostAddr.sa.sa_family != 0) 419538032Speter { 419690792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 419790792Sgshapiro anynet_ntoa(&CurHostAddr)); 419838032Speter } 419938032Speter } 420090792Sgshapiro#if _FFR_QUARANTINE 420190792Sgshapiro else if (strcmp(status, "quarantined") == 0) 420290792Sgshapiro { 420390792Sgshapiro if (e->e_quarmsg != NULL) 420490792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 420590792Sgshapiro ", quarantine=%s", 420690792Sgshapiro shortenstring(e->e_quarmsg, 40)); 420790792Sgshapiro } 420890792Sgshapiro#endif /* _FFR_QUARANTINE */ 420964562Sgshapiro else if (strcmp(status, "queued") != 0) 421038032Speter { 421138032Speter p = macvalue('h', e); 421238032Speter if (p != NULL && p[0] != '\0') 421338032Speter { 421490792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 421590792Sgshapiro ", relay=%s", shortenstring(p, 40)); 421638032Speter } 421738032Speter } 421838032Speter bp += strlen(bp); 421938032Speter 422064562Sgshapiro /* dsn */ 422164562Sgshapiro if (dsn != NULL && *dsn != '\0') 422264562Sgshapiro { 422390792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 422490792Sgshapiro shortenstring(dsn, ENHSCLEN)); 422564562Sgshapiro bp += strlen(bp); 422664562Sgshapiro } 422738032Speter 422864562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 422964562Sgshapiro# if (STATLEN) < 63 423064562Sgshapiro# undef STATLEN 423164562Sgshapiro# define STATLEN 63 423264562Sgshapiro# endif /* (STATLEN) < 63 */ 423364562Sgshapiro# if (STATLEN) > 203 423464562Sgshapiro# undef STATLEN 423564562Sgshapiro# define STATLEN 203 423664562Sgshapiro# endif /* (STATLEN) > 203 */ 423764562Sgshapiro 423838032Speter /* stat: max 210 bytes */ 423938032Speter if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) 424038032Speter { 424138032Speter /* desperation move -- truncate data */ 424238032Speter bp = buf + sizeof buf - ((STATLEN) + 17); 424390792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 424438032Speter bp += 3; 424538032Speter } 424638032Speter 424790792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 424838032Speter bp += strlen(bp); 424938032Speter 425090792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 425190792Sgshapiro SPACELEFT(buf, bp)); 425238032Speter 425338032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 425438032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 425590792Sgshapiro if (l < 0) 425690792Sgshapiro l = 0; 425764562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 425890792Sgshapiro while (strlen(p) >= l) 425938032Speter { 426064562Sgshapiro register char *q; 426138032Speter 426264562Sgshapiro for (q = p + l; q > p; q--) 426364562Sgshapiro { 426464562Sgshapiro if (*q == ',') 426564562Sgshapiro break; 426664562Sgshapiro } 426764562Sgshapiro if (p == q) 426864562Sgshapiro break; 426990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 427066494Sgshapiro (int) (++q - p), p, buf); 427138032Speter p = q; 427238032Speter } 427364562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 427438032Speter 427564562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 427638032Speter 427738032Speter l = SYSLOG_BUFSIZE - 85; 427890792Sgshapiro if (l < 0) 427990792Sgshapiro l = 0; 428064562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 428190792Sgshapiro while (strlen(p) >= l) 428238032Speter { 428364562Sgshapiro register char *q; 428438032Speter 428564562Sgshapiro for (q = p + l; q > p; q--) 428664562Sgshapiro { 428764562Sgshapiro if (*q == ',') 428864562Sgshapiro break; 428964562Sgshapiro } 429064562Sgshapiro if (p == q) 429164562Sgshapiro break; 429264562Sgshapiro 429390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 429466494Sgshapiro (int) (++q - p), p); 429538032Speter p = q; 429638032Speter } 429764562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 429838032Speter 429938032Speter if (ctladdr != NULL) 430038032Speter { 430138032Speter bp = buf; 430290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 430390792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 430438032Speter bp += strlen(bp); 430538032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 430638032Speter { 430790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 430890792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 430938032Speter bp += strlen(bp); 431038032Speter } 431138032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 431238032Speter } 431338032Speter bp = buf; 431490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 431590792Sgshapiro pintvl(now - e->e_ctime, true)); 431638032Speter bp += strlen(bp); 431738032Speter if (xstart != (time_t) 0) 431838032Speter { 431990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 432090792Sgshapiro pintvl(now - xstart, true)); 432138032Speter bp += strlen(bp); 432238032Speter } 432338032Speter 432438032Speter if (m != NULL) 432538032Speter { 432690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 432790792Sgshapiro m->m_name); 432838032Speter bp += strlen(bp); 432938032Speter } 433038032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 433138032Speter 433238032Speter buf[0] = '\0'; 433338032Speter bp = buf; 433438032Speter if (mci != NULL && mci->mci_host != NULL) 433538032Speter { 433638032Speter extern SOCKADDR CurHostAddr; 433738032Speter 433890792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 433990792Sgshapiro mci->mci_host); 434038032Speter bp += strlen(bp); 434138032Speter 434238032Speter if (CurHostAddr.sa.sa_family != 0) 434390792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 434490792Sgshapiro " [%.100s]", 434590792Sgshapiro anynet_ntoa(&CurHostAddr)); 434638032Speter } 434790792Sgshapiro#if _FFR_QUARANTINE 434890792Sgshapiro else if (strcmp(status, "quarantined") == 0) 434990792Sgshapiro { 435090792Sgshapiro if (e->e_quarmsg != NULL) 435190792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 435290792Sgshapiro ", quarantine=%.100s", 435390792Sgshapiro e->e_quarmsg); 435490792Sgshapiro } 435590792Sgshapiro#endif /* _FFR_QUARANTINE */ 435664562Sgshapiro else if (strcmp(status, "queued") != 0) 435738032Speter { 435838032Speter p = macvalue('h', e); 435938032Speter if (p != NULL && p[0] != '\0') 436090792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p); 436138032Speter } 436238032Speter if (buf[0] != '\0') 436338032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 436438032Speter 436564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 436664562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 436738032Speter} 436890792Sgshapiro/* 436938032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 437038032Speter** 437138032Speter** This can be made an arbitrary message separator by changing $l 437238032Speter** 437338032Speter** One of the ugliest hacks seen by human eyes is contained herein: 437438032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 437538032Speter** does a well-meaning programmer such as myself have to deal with 437638032Speter** this kind of antique garbage???? 437738032Speter** 437838032Speter** Parameters: 437938032Speter** mci -- the connection information. 438038032Speter** e -- the envelope. 438138032Speter** 438238032Speter** Returns: 438338032Speter** none 438438032Speter** 438538032Speter** Side Effects: 438638032Speter** outputs some text to fp. 438738032Speter*/ 438838032Speter 438938032Spetervoid 439038032Speterputfromline(mci, e) 439138032Speter register MCI *mci; 439238032Speter ENVELOPE *e; 439338032Speter{ 439438032Speter char *template = UnixFromLine; 439538032Speter char buf[MAXLINE]; 439638032Speter char xbuf[MAXLINE]; 439738032Speter 439838032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 439938032Speter return; 440038032Speter 440138032Speter mci->mci_flags |= MCIF_INHEADER; 440238032Speter 440338032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 440438032Speter { 440538032Speter char *bang; 440638032Speter 440738032Speter expand("\201g", buf, sizeof buf, e); 440838032Speter bang = strchr(buf, '!'); 440938032Speter if (bang == NULL) 441038032Speter { 441138032Speter char *at; 441238032Speter char hname[MAXNAME]; 441338032Speter 441464562Sgshapiro /* 441542575Speter ** If we can construct a UUCP path, do so 441638032Speter */ 441738032Speter 441838032Speter at = strrchr(buf, '@'); 441938032Speter if (at == NULL) 442038032Speter { 442164562Sgshapiro expand("\201k", hname, sizeof hname, e); 442238032Speter at = hname; 442338032Speter } 442438032Speter else 442538032Speter *at++ = '\0'; 442690792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 442790792Sgshapiro "From %.800s \201d remote from %.100s\n", 442890792Sgshapiro buf, at); 442938032Speter } 443038032Speter else 443138032Speter { 443238032Speter *bang++ = '\0'; 443390792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 443490792Sgshapiro "From %.800s \201d remote from %.100s\n", 443590792Sgshapiro bang, buf); 443638032Speter template = xbuf; 443738032Speter } 443838032Speter } 443938032Speter expand(template, buf, sizeof buf, e); 444038032Speter putxline(buf, strlen(buf), mci, PXLF_HEADER); 444138032Speter} 444290792Sgshapiro/* 444338032Speter** PUTBODY -- put the body of a message. 444438032Speter** 444538032Speter** Parameters: 444638032Speter** mci -- the connection information. 444738032Speter** e -- the envelope to put out. 444838032Speter** separator -- if non-NULL, a message separator that must 444938032Speter** not be permitted in the resulting message. 445038032Speter** 445138032Speter** Returns: 445238032Speter** none. 445338032Speter** 445438032Speter** Side Effects: 445538032Speter** The message is written onto fp. 445638032Speter*/ 445738032Speter 445838032Speter/* values for output state variable */ 445938032Speter#define OS_HEAD 0 /* at beginning of line */ 446038032Speter#define OS_CR 1 /* read a carriage return */ 446138032Speter#define OS_INLINE 2 /* putting rest of line */ 446238032Speter 446338032Spetervoid 446438032Speterputbody(mci, e, separator) 446538032Speter register MCI *mci; 446638032Speter register ENVELOPE *e; 446738032Speter char *separator; 446838032Speter{ 446990792Sgshapiro bool dead = false; 447038032Speter char buf[MAXLINE]; 447190792Sgshapiro#if MIME8TO7 447242575Speter char *boundaries[MAXMIMENESTING + 1]; 447390792Sgshapiro#endif /* MIME8TO7 */ 447438032Speter 447538032Speter /* 447638032Speter ** Output the body of the message 447738032Speter */ 447838032Speter 447938032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 448038032Speter { 448190792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 448238032Speter 448390792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4484120256Sgshapiro SM_IO_RDONLY_B, NULL); 448538032Speter if (e->e_dfp == NULL) 448664562Sgshapiro { 448764562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 448864562Sgshapiro 448964562Sgshapiro if (errno == ENOENT) 449064562Sgshapiro msg++; 449164562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 449264562Sgshapiro } 449390792Sgshapiro 449438032Speter } 449538032Speter if (e->e_dfp == NULL) 449638032Speter { 449738032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 449838032Speter { 449938032Speter putline("", mci); 450038032Speter mci->mci_flags &= ~MCIF_INHEADER; 450138032Speter } 450238032Speter putline("<<< No Message Collected >>>", mci); 450338032Speter goto endofmessage; 450438032Speter } 450564562Sgshapiro 450638032Speter if (e->e_dfino == (ino_t) 0) 450738032Speter { 450838032Speter struct stat stbuf; 450938032Speter 451090792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 451190792Sgshapiro < 0) 451238032Speter e->e_dfino = -1; 451338032Speter else 451438032Speter { 451538032Speter e->e_dfdev = stbuf.st_dev; 451638032Speter e->e_dfino = stbuf.st_ino; 451738032Speter } 451838032Speter } 451938032Speter 452090792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 452164562Sgshapiro (void) bfrewind(e->e_dfp); 452264562Sgshapiro 452338032Speter#if MIME8TO7 452438032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 452538032Speter { 452638032Speter /* 452738032Speter ** Do 8 to 7 bit MIME conversion. 452838032Speter */ 452938032Speter 453038032Speter /* make sure it looks like a MIME message */ 453138032Speter if (hvalue("MIME-Version", e->e_header) == NULL) 453238032Speter putline("MIME-Version: 1.0", mci); 453338032Speter 453438032Speter if (hvalue("Content-Type", e->e_header) == NULL) 453538032Speter { 453690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 453790792Sgshapiro "Content-Type: text/plain; charset=%s", 453890792Sgshapiro defcharset(e)); 453938032Speter putline(buf, mci); 454038032Speter } 454138032Speter 454238032Speter /* now do the hard work */ 454338032Speter boundaries[0] = NULL; 454438032Speter mci->mci_flags |= MCIF_INHEADER; 454564562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); 454638032Speter } 454738032Speter# if MIME7TO8 454838032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 454938032Speter { 455064562Sgshapiro (void) mime7to8(mci, e->e_header, e); 455138032Speter } 455264562Sgshapiro# endif /* MIME7TO8 */ 455342575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 455442575Speter { 455564562Sgshapiro bool oldsuprerrs = SuprErrs; 455664562Sgshapiro 455742575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 455842575Speter boundaries[0] = NULL; 455942575Speter mci->mci_flags |= MCIF_INHEADER; 456064562Sgshapiro 456164562Sgshapiro /* 456264562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 456364562Sgshapiro ** and don't want to generate a new bounce message whose 456464562Sgshapiro ** body propagates the broken MIME. We can't just not call 456564562Sgshapiro ** mime8to7() as is done above since we need the security 456664562Sgshapiro ** checks. The best we can do is suppress the errors. 456764562Sgshapiro */ 456864562Sgshapiro 456964562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 457090792Sgshapiro SuprErrs = true; 457164562Sgshapiro 457264562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, 457364562Sgshapiro M87F_OUTER|M87F_NO8TO7); 457464562Sgshapiro 457564562Sgshapiro /* restore SuprErrs */ 457664562Sgshapiro SuprErrs = oldsuprerrs; 457742575Speter } 457838032Speter else 457964562Sgshapiro#endif /* MIME8TO7 */ 458038032Speter { 458138032Speter int ostate; 458238032Speter register char *bp; 458338032Speter register char *pbp; 458438032Speter register int c; 458538032Speter register char *xp; 458638032Speter int padc; 458738032Speter char *buflim; 458838032Speter int pos = 0; 458964562Sgshapiro char peekbuf[12]; 459038032Speter 459138032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 459238032Speter { 459338032Speter putline("", mci); 459438032Speter mci->mci_flags &= ~MCIF_INHEADER; 459538032Speter } 459638032Speter 459738032Speter /* determine end of buffer; allow for short mailer lines */ 459838032Speter buflim = &buf[sizeof buf - 1]; 459938032Speter if (mci->mci_mailer->m_linelimit > 0 && 460038032Speter mci->mci_mailer->m_linelimit < sizeof buf - 1) 460138032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 460238032Speter 460338032Speter /* copy temp file to output with mapping */ 460438032Speter ostate = OS_HEAD; 460538032Speter bp = buf; 460638032Speter pbp = peekbuf; 460790792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 460838032Speter { 460938032Speter if (pbp > peekbuf) 461038032Speter c = *--pbp; 461190792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 461290792Sgshapiro == SM_IO_EOF) 461338032Speter break; 461438032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 461538032Speter c &= 0x7f; 461638032Speter switch (ostate) 461738032Speter { 461838032Speter case OS_HEAD: 461938032Speter if (c == '\0' && 462090792Sgshapiro bitnset(M_NONULLS, 462190792Sgshapiro mci->mci_mailer->m_flags)) 462238032Speter break; 462338032Speter if (c != '\r' && c != '\n' && bp < buflim) 462438032Speter { 462538032Speter *bp++ = c; 462638032Speter break; 462738032Speter } 462838032Speter 462938032Speter /* check beginning of line for special cases */ 463038032Speter *bp = '\0'; 463138032Speter pos = 0; 463290792Sgshapiro padc = SM_IO_EOF; 463338032Speter if (buf[0] == 'F' && 463490792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 463590792Sgshapiro && strncmp(buf, "From ", 5) == 0) 463638032Speter { 463738032Speter padc = '>'; 463838032Speter } 463938032Speter if (buf[0] == '-' && buf[1] == '-' && 464038032Speter separator != NULL) 464138032Speter { 464238032Speter /* possible separator */ 464338032Speter int sl = strlen(separator); 464438032Speter 464590792Sgshapiro if (strncmp(&buf[2], separator, sl) 464690792Sgshapiro == 0) 464738032Speter padc = ' '; 464838032Speter } 464938032Speter if (buf[0] == '.' && 465038032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 465138032Speter { 465238032Speter padc = '.'; 465338032Speter } 465438032Speter 465538032Speter /* now copy out saved line */ 465638032Speter if (TrafficLogFile != NULL) 465738032Speter { 465890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 465990792Sgshapiro SM_TIME_DEFAULT, 466090792Sgshapiro "%05d >>> ", 466190792Sgshapiro (int) CurrentPid); 466290792Sgshapiro if (padc != SM_IO_EOF) 466390792Sgshapiro (void) sm_io_putc(TrafficLogFile, 466490792Sgshapiro SM_TIME_DEFAULT, 466590792Sgshapiro padc); 466638032Speter for (xp = buf; xp < bp; xp++) 466790792Sgshapiro (void) sm_io_putc(TrafficLogFile, 466890792Sgshapiro SM_TIME_DEFAULT, 466990792Sgshapiro (unsigned char) *xp); 467038032Speter if (c == '\n') 467190792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 467290792Sgshapiro SM_TIME_DEFAULT, 467390792Sgshapiro mci->mci_mailer->m_eol); 467438032Speter } 467590792Sgshapiro if (padc != SM_IO_EOF) 467638032Speter { 467790792Sgshapiro if (sm_io_putc(mci->mci_out, 467890792Sgshapiro SM_TIME_DEFAULT, padc) 467990792Sgshapiro == SM_IO_EOF) 468064562Sgshapiro { 468190792Sgshapiro dead = true; 468264562Sgshapiro continue; 468364562Sgshapiro } 468471345Sgshapiro else 468571345Sgshapiro { 468671345Sgshapiro /* record progress for DATA timeout */ 468790792Sgshapiro DataProgress = true; 468871345Sgshapiro } 468938032Speter pos++; 469038032Speter } 469138032Speter for (xp = buf; xp < bp; xp++) 469238032Speter { 469390792Sgshapiro if (sm_io_putc(mci->mci_out, 469490792Sgshapiro SM_TIME_DEFAULT, 469590792Sgshapiro (unsigned char) *xp) 469690792Sgshapiro == SM_IO_EOF) 469764562Sgshapiro { 469890792Sgshapiro dead = true; 469964562Sgshapiro break; 470064562Sgshapiro } 470171345Sgshapiro else 470271345Sgshapiro { 470371345Sgshapiro /* record progress for DATA timeout */ 470490792Sgshapiro DataProgress = true; 470571345Sgshapiro } 470638032Speter } 470764562Sgshapiro if (dead) 470864562Sgshapiro continue; 470938032Speter if (c == '\n') 471038032Speter { 471190792Sgshapiro if (sm_io_fputs(mci->mci_out, 471290792Sgshapiro SM_TIME_DEFAULT, 471390792Sgshapiro mci->mci_mailer->m_eol) 471490792Sgshapiro == SM_IO_EOF) 471564562Sgshapiro break; 471671345Sgshapiro else 471771345Sgshapiro { 471871345Sgshapiro /* record progress for DATA timeout */ 471990792Sgshapiro DataProgress = true; 472071345Sgshapiro } 472138032Speter pos = 0; 472238032Speter } 472338032Speter else 472438032Speter { 472538032Speter pos += bp - buf; 472638032Speter if (c != '\r') 4727112810Sgshapiro { 4728112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4729112810Sgshapiro sizeof(peekbuf)); 473038032Speter *pbp++ = c; 4731112810Sgshapiro } 473238032Speter } 473364562Sgshapiro 473438032Speter bp = buf; 473538032Speter 473638032Speter /* determine next state */ 473738032Speter if (c == '\n') 473838032Speter ostate = OS_HEAD; 473938032Speter else if (c == '\r') 474038032Speter ostate = OS_CR; 474138032Speter else 474238032Speter ostate = OS_INLINE; 474338032Speter continue; 474438032Speter 474538032Speter case OS_CR: 474638032Speter if (c == '\n') 474738032Speter { 474838032Speter /* got CRLF */ 474990792Sgshapiro if (sm_io_fputs(mci->mci_out, 475090792Sgshapiro SM_TIME_DEFAULT, 475190792Sgshapiro mci->mci_mailer->m_eol) 475290792Sgshapiro == SM_IO_EOF) 475364562Sgshapiro continue; 475471345Sgshapiro else 475571345Sgshapiro { 475671345Sgshapiro /* record progress for DATA timeout */ 475790792Sgshapiro DataProgress = true; 475871345Sgshapiro } 475964562Sgshapiro 476038032Speter if (TrafficLogFile != NULL) 476138032Speter { 476290792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 476390792Sgshapiro SM_TIME_DEFAULT, 476490792Sgshapiro mci->mci_mailer->m_eol); 476538032Speter } 476638032Speter ostate = OS_HEAD; 476738032Speter continue; 476838032Speter } 476938032Speter 477038032Speter /* had a naked carriage return */ 4771112810Sgshapiro SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 477238032Speter *pbp++ = c; 477338032Speter c = '\r'; 477438032Speter ostate = OS_INLINE; 477538032Speter goto putch; 477638032Speter 477738032Speter case OS_INLINE: 477838032Speter if (c == '\r') 477938032Speter { 478038032Speter ostate = OS_CR; 478138032Speter continue; 478238032Speter } 478338032Speter if (c == '\0' && 478490792Sgshapiro bitnset(M_NONULLS, 478590792Sgshapiro mci->mci_mailer->m_flags)) 478638032Speter break; 478738032Speterputch: 478838032Speter if (mci->mci_mailer->m_linelimit > 0 && 478964562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 479038032Speter c != '\n') 479138032Speter { 479264562Sgshapiro int d; 479364562Sgshapiro 479464562Sgshapiro /* check next character for EOL */ 479564562Sgshapiro if (pbp > peekbuf) 479664562Sgshapiro d = *(pbp - 1); 479790792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 479890792Sgshapiro SM_TIME_DEFAULT)) 479990792Sgshapiro != SM_IO_EOF) 4800112810Sgshapiro { 4801112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4802112810Sgshapiro sizeof(peekbuf)); 480364562Sgshapiro *pbp++ = d; 4804112810Sgshapiro } 480564562Sgshapiro 480690792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 480764562Sgshapiro { 480864562Sgshapiro if (TrafficLogFile != NULL) 480990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 481090792Sgshapiro SM_TIME_DEFAULT, 481190792Sgshapiro (unsigned char) c); 481290792Sgshapiro if (sm_io_putc(mci->mci_out, 481390792Sgshapiro SM_TIME_DEFAULT, 481490792Sgshapiro (unsigned char) c) 481590792Sgshapiro == SM_IO_EOF) 481664562Sgshapiro { 481790792Sgshapiro dead = true; 481864562Sgshapiro continue; 481964562Sgshapiro } 482071345Sgshapiro else 482171345Sgshapiro { 482271345Sgshapiro /* record progress for DATA timeout */ 482390792Sgshapiro DataProgress = true; 482471345Sgshapiro } 482564562Sgshapiro pos++; 482664562Sgshapiro continue; 482764562Sgshapiro } 482864562Sgshapiro 482990792Sgshapiro if (sm_io_putc(mci->mci_out, 483090792Sgshapiro SM_TIME_DEFAULT, '!') 483190792Sgshapiro == SM_IO_EOF || 483290792Sgshapiro sm_io_fputs(mci->mci_out, 483390792Sgshapiro SM_TIME_DEFAULT, 483490792Sgshapiro mci->mci_mailer->m_eol) 483590792Sgshapiro == SM_IO_EOF) 483664562Sgshapiro { 483790792Sgshapiro dead = true; 483864562Sgshapiro continue; 483964562Sgshapiro } 484071345Sgshapiro else 484171345Sgshapiro { 484271345Sgshapiro /* record progress for DATA timeout */ 484390792Sgshapiro DataProgress = true; 484471345Sgshapiro } 484564562Sgshapiro 484638032Speter if (TrafficLogFile != NULL) 484738032Speter { 484890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 484990792Sgshapiro SM_TIME_DEFAULT, 485090792Sgshapiro "!%s", 485190792Sgshapiro mci->mci_mailer->m_eol); 485238032Speter } 485338032Speter ostate = OS_HEAD; 4854112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4855112810Sgshapiro sizeof(peekbuf)); 485638032Speter *pbp++ = c; 485738032Speter continue; 485838032Speter } 485938032Speter if (c == '\n') 486038032Speter { 486138032Speter if (TrafficLogFile != NULL) 486290792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 486390792Sgshapiro SM_TIME_DEFAULT, 486490792Sgshapiro mci->mci_mailer->m_eol); 486590792Sgshapiro if (sm_io_fputs(mci->mci_out, 486690792Sgshapiro SM_TIME_DEFAULT, 486790792Sgshapiro mci->mci_mailer->m_eol) 486890792Sgshapiro == SM_IO_EOF) 486964562Sgshapiro continue; 487071345Sgshapiro else 487171345Sgshapiro { 487271345Sgshapiro /* record progress for DATA timeout */ 487390792Sgshapiro DataProgress = true; 487471345Sgshapiro } 487538032Speter pos = 0; 487638032Speter ostate = OS_HEAD; 487738032Speter } 487838032Speter else 487938032Speter { 488038032Speter if (TrafficLogFile != NULL) 488190792Sgshapiro (void) sm_io_putc(TrafficLogFile, 488290792Sgshapiro SM_TIME_DEFAULT, 488390792Sgshapiro (unsigned char) c); 488490792Sgshapiro if (sm_io_putc(mci->mci_out, 488590792Sgshapiro SM_TIME_DEFAULT, 488690792Sgshapiro (unsigned char) c) 488790792Sgshapiro == SM_IO_EOF) 488864562Sgshapiro { 488990792Sgshapiro dead = true; 489064562Sgshapiro continue; 489164562Sgshapiro } 489271345Sgshapiro else 489371345Sgshapiro { 489471345Sgshapiro /* record progress for DATA timeout */ 489590792Sgshapiro DataProgress = true; 489671345Sgshapiro } 489738032Speter pos++; 489838032Speter ostate = OS_INLINE; 489938032Speter } 490038032Speter break; 490138032Speter } 490238032Speter } 490338032Speter 490438032Speter /* make sure we are at the beginning of a line */ 490538032Speter if (bp > buf) 490638032Speter { 490738032Speter if (TrafficLogFile != NULL) 490838032Speter { 490938032Speter for (xp = buf; xp < bp; xp++) 491090792Sgshapiro (void) sm_io_putc(TrafficLogFile, 491190792Sgshapiro SM_TIME_DEFAULT, 491290792Sgshapiro (unsigned char) *xp); 491338032Speter } 491438032Speter for (xp = buf; xp < bp; xp++) 491538032Speter { 491690792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 491790792Sgshapiro (unsigned char) *xp) 491890792Sgshapiro == SM_IO_EOF) 491964562Sgshapiro { 492090792Sgshapiro dead = true; 492164562Sgshapiro break; 492264562Sgshapiro } 492371345Sgshapiro else 492471345Sgshapiro { 492571345Sgshapiro /* record progress for DATA timeout */ 492690792Sgshapiro DataProgress = true; 492771345Sgshapiro } 492838032Speter } 492938032Speter pos += bp - buf; 493038032Speter } 493164562Sgshapiro if (!dead && pos > 0) 493238032Speter { 493338032Speter if (TrafficLogFile != NULL) 493490792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 493590792Sgshapiro SM_TIME_DEFAULT, 493690792Sgshapiro mci->mci_mailer->m_eol); 493790792Sgshapiro (void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 493890792Sgshapiro mci->mci_mailer->m_eol); 493964562Sgshapiro 494064562Sgshapiro /* record progress for DATA timeout */ 494190792Sgshapiro DataProgress = true; 494238032Speter } 494338032Speter } 494438032Speter 494590792Sgshapiro if (sm_io_error(e->e_dfp)) 494638032Speter { 494790792Sgshapiro syserr("putbody: %s/%cf%s: read error", 494890792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 494990792Sgshapiro DATAFL_LETTER, e->e_id); 495038032Speter ExitStat = EX_IOERR; 495138032Speter } 495238032Speter 495338032Speterendofmessage: 495464562Sgshapiro /* 495564562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 495664562Sgshapiro ** the file offset in the stdio library for the 495764562Sgshapiro ** parent process will not agree with the in-kernel 495864562Sgshapiro ** file offset since the file descriptor is shared 495964562Sgshapiro ** between the processes. Therefore, it is vital 496064562Sgshapiro ** that the file always be rewound. This forces the 496164562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 496264562Sgshapiro ** offset to match. 496364562Sgshapiro */ 496464562Sgshapiro 496564562Sgshapiro if (e->e_dfp != NULL) 496664562Sgshapiro (void) bfrewind(e->e_dfp); 496764562Sgshapiro 496838032Speter /* some mailers want extra blank line at end of message */ 496964562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 497038032Speter buf[0] != '\0' && buf[0] != '\n') 497138032Speter putline("", mci); 497238032Speter 497390792Sgshapiro (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); 497490792Sgshapiro if (sm_io_error(mci->mci_out) && errno != EPIPE) 497538032Speter { 497638032Speter syserr("putbody: write error"); 497738032Speter ExitStat = EX_IOERR; 497838032Speter } 497964562Sgshapiro 498038032Speter errno = 0; 498138032Speter} 498290792Sgshapiro/* 498338032Speter** MAILFILE -- Send a message to a file. 498438032Speter** 498590792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 498690792Sgshapiro** execute bits, sendmail will try to become the owner of that file 498738032Speter** rather than the real user. Obviously, this only works if 498838032Speter** sendmail runs as root. 498938032Speter** 499038032Speter** This could be done as a subordinate mailer, except that it 499138032Speter** is used implicitly to save messages in ~/dead.letter. We 499238032Speter** view this as being sufficiently important as to include it 499338032Speter** here. For example, if the system is dying, we shouldn't have 499438032Speter** to create another process plus some pipes to save the message. 499538032Speter** 499638032Speter** Parameters: 499738032Speter** filename -- the name of the file to send to. 499838032Speter** mailer -- mailer definition for recipient -- if NULL, 499938032Speter** use FileMailer. 500038032Speter** ctladdr -- the controlling address header -- includes 500138032Speter** the userid/groupid to be when sending. 500238032Speter** sfflags -- flags for opening. 500338032Speter** e -- the current envelope. 500438032Speter** 500538032Speter** Returns: 500638032Speter** The exit code associated with the operation. 500738032Speter** 500838032Speter** Side Effects: 500938032Speter** none. 501038032Speter*/ 501138032Speter 501290792Sgshapiro# define RETURN(st) exit(st); 501390792Sgshapiro 501438032Speterstatic jmp_buf CtxMailfileTimeout; 501538032Speter 501638032Speterint 501738032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 501838032Speter char *volatile filename; 501938032Speter MAILER *volatile mailer; 502038032Speter ADDRESS *ctladdr; 502164562Sgshapiro volatile long sfflags; 502238032Speter register ENVELOPE *e; 502338032Speter{ 502490792Sgshapiro register SM_FILE_T *f; 502538032Speter register pid_t pid = -1; 502664562Sgshapiro volatile int mode; 502764562Sgshapiro int len; 502864562Sgshapiro off_t curoff; 502938032Speter bool suidwarn = geteuid() == 0; 503038032Speter char *p; 503164562Sgshapiro char *volatile realfile; 503290792Sgshapiro SM_EVENT *ev; 503398121Sgshapiro char buf[MAXPATHLEN]; 503498121Sgshapiro char targetfile[MAXPATHLEN]; 503538032Speter 503638032Speter if (tTd(11, 1)) 503738032Speter { 503890792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 503990792Sgshapiro printaddr(ctladdr, false); 504038032Speter } 504138032Speter 504238032Speter if (mailer == NULL) 504338032Speter mailer = FileMailer; 504438032Speter 504538032Speter if (e->e_xfp != NULL) 504690792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 504738032Speter 504838032Speter /* 504938032Speter ** Special case /dev/null. This allows us to restrict file 505038032Speter ** delivery to regular files only. 505138032Speter */ 505238032Speter 505390792Sgshapiro if (sm_path_isdevnull(filename)) 505438032Speter return EX_OK; 505538032Speter 505638032Speter /* check for 8-bit available */ 505738032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 505838032Speter bitnset(M_7BITS, mailer->m_flags) && 505938032Speter (bitset(EF_DONT_MIME, e->e_flags) || 506038032Speter !(bitset(MM_MIME8BIT, MimeMode) || 506138032Speter (bitset(EF_IS_MIME, e->e_flags) && 506238032Speter bitset(MM_CVTMIME, MimeMode))))) 506338032Speter { 506438032Speter e->e_status = "5.6.3"; 506564562Sgshapiro usrerrenh(e->e_status, 506690792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 506790792Sgshapiro errno = 0; 506864562Sgshapiro return EX_DATAERR; 506938032Speter } 507038032Speter 507164562Sgshapiro /* Find the actual file */ 507264562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 507364562Sgshapiro { 507464562Sgshapiro len = strlen(SafeFileEnv); 507564562Sgshapiro 507664562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 507764562Sgshapiro filename += len; 507864562Sgshapiro 507990792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 508064562Sgshapiro { 508164562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 508264562Sgshapiro SafeFileEnv, filename); 508364562Sgshapiro return EX_CANTCREAT; 508464562Sgshapiro } 508590792Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile); 508664562Sgshapiro realfile = targetfile + len; 508764562Sgshapiro if (*filename == '/') 508864562Sgshapiro filename++; 508994334Sgshapiro if (*filename != '\0') 509094334Sgshapiro { 509194334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 509294334Sgshapiro if (targetfile[len - 1] != '/') 509394334Sgshapiro (void) sm_strlcat(targetfile, 509494334Sgshapiro "/", sizeof targetfile); 509594334Sgshapiro (void) sm_strlcat(targetfile, filename, 509694334Sgshapiro sizeof targetfile); 509794334Sgshapiro } 509864562Sgshapiro } 509964562Sgshapiro else if (mailer->m_rootdir != NULL) 510064562Sgshapiro { 510164562Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof targetfile, e); 510264562Sgshapiro len = strlen(targetfile); 510364562Sgshapiro 510464562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 510564562Sgshapiro filename += len; 510664562Sgshapiro 510790792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 510864562Sgshapiro { 510964562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 511064562Sgshapiro targetfile, filename); 511164562Sgshapiro return EX_CANTCREAT; 511264562Sgshapiro } 511364562Sgshapiro realfile = targetfile + len; 511464562Sgshapiro if (targetfile[len - 1] != '/') 511590792Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof targetfile); 511664562Sgshapiro if (*filename == '/') 511790792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 511890792Sgshapiro sizeof targetfile); 511964562Sgshapiro else 512090792Sgshapiro (void) sm_strlcat(targetfile, filename, 512190792Sgshapiro sizeof targetfile); 512264562Sgshapiro } 512364562Sgshapiro else 512464562Sgshapiro { 512590792Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof targetfile) >= 512690792Sgshapiro sizeof targetfile) 512764562Sgshapiro { 512864562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 512964562Sgshapiro return EX_CANTCREAT; 513064562Sgshapiro } 513164562Sgshapiro realfile = targetfile; 513264562Sgshapiro } 513364562Sgshapiro 513438032Speter /* 513538032Speter ** Fork so we can change permissions here. 513638032Speter ** Note that we MUST use fork, not vfork, because of 513738032Speter ** the complications of calling subroutines, etc. 513838032Speter */ 513938032Speter 514094334Sgshapiro 514194334Sgshapiro /* 514294334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 514394334Sgshapiro ** around so that the waitfor() below will get it. 514494334Sgshapiro */ 514594334Sgshapiro 514694334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 514794334Sgshapiro 514838032Speter DOFORK(fork); 514938032Speter 515038032Speter if (pid < 0) 515164562Sgshapiro return EX_OSERR; 515238032Speter else if (pid == 0) 515338032Speter { 515438032Speter /* child -- actually write to file */ 515538032Speter struct stat stb; 515638032Speter MCI mcibuf; 515742575Speter int err; 515838032Speter volatile int oflags = O_WRONLY|O_APPEND; 515938032Speter 516077349Sgshapiro /* Reset global flags */ 516177349Sgshapiro RestartRequest = NULL; 516290792Sgshapiro RestartWorkGroup = false; 516377349Sgshapiro ShutdownRequest = NULL; 516477349Sgshapiro PendingSignal = 0; 516590792Sgshapiro CurrentPid = getpid(); 516677349Sgshapiro 516738032Speter if (e->e_lockfp != NULL) 516890792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 516990792Sgshapiro NULL)); 517038032Speter 517190792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 517290792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 517390792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 517438032Speter (void) umask(OldUmask); 517538032Speter e->e_to = filename; 517638032Speter ExitStat = EX_OK; 517738032Speter 517838032Speter if (setjmp(CtxMailfileTimeout) != 0) 517938032Speter { 518090792Sgshapiro RETURN(EX_TEMPFAIL); 518138032Speter } 518238032Speter 518338032Speter if (TimeOuts.to_fileopen > 0) 518490792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 518590792Sgshapiro 0); 518638032Speter else 518738032Speter ev = NULL; 518838032Speter 518990792Sgshapiro /* check file mode to see if set-user-ID */ 519064562Sgshapiro if (stat(targetfile, &stb) < 0) 519164562Sgshapiro mode = FileMode; 519242575Speter else 519338032Speter mode = stb.st_mode; 519438032Speter 519538032Speter /* limit the errors to those actually caused in the child */ 519638032Speter errno = 0; 519738032Speter ExitStat = EX_OK; 519838032Speter 519964562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 520064562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 520164562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 520238032Speter { 520390792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 520438032Speter mode &= ~(S_ISGID|S_ISUID); 520564562Sgshapiro if (tTd(11, 20)) 520690792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 520738032Speter } 520838032Speter 520990792Sgshapiro /* we have to open the data file BEFORE setuid() */ 521038032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 521138032Speter { 521290792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 521338032Speter 521490792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5215120256Sgshapiro SM_IO_RDONLY_B, NULL); 521638032Speter if (e->e_dfp == NULL) 521738032Speter { 521838032Speter syserr("mailfile: Cannot open %s for %s from %s", 521938032Speter df, e->e_to, e->e_from.q_paddr); 522038032Speter } 522138032Speter } 522238032Speter 522338032Speter /* select a new user to run as */ 522438032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 522538032Speter { 522638032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 522738032Speter { 522838032Speter RealUserName = NULL; 522938032Speter RealUid = mailer->m_uid; 523064562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 523164562Sgshapiro { 523264562Sgshapiro /* Only root can change the uid */ 523390792Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 523490792Sgshapiro (int) RunAsUid, (int) RealUid); 523590792Sgshapiro RETURN(EX_TEMPFAIL); 523664562Sgshapiro } 523738032Speter } 523838032Speter else if (bitset(S_ISUID, mode)) 523938032Speter { 524038032Speter RealUserName = NULL; 524138032Speter RealUid = stb.st_uid; 524238032Speter } 524338032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 524438032Speter { 524538032Speter if (ctladdr->q_ruser != NULL) 524638032Speter RealUserName = ctladdr->q_ruser; 524738032Speter else 524838032Speter RealUserName = ctladdr->q_user; 524938032Speter RealUid = ctladdr->q_uid; 525038032Speter } 525138032Speter else if (mailer != NULL && mailer->m_uid != 0) 525238032Speter { 525338032Speter RealUserName = DefUser; 525438032Speter RealUid = mailer->m_uid; 525538032Speter } 525638032Speter else 525738032Speter { 525838032Speter RealUserName = DefUser; 525938032Speter RealUid = DefUid; 526038032Speter } 526138032Speter 526238032Speter /* select a new group to run as */ 526338032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 526464562Sgshapiro { 526538032Speter RealGid = mailer->m_gid; 526664562Sgshapiro if (RunAsUid != 0 && 526764562Sgshapiro (RealGid != getgid() || 526864562Sgshapiro RealGid != getegid())) 526964562Sgshapiro { 527064562Sgshapiro /* Only root can change the gid */ 527190792Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 527290792Sgshapiro (int) RealGid, (int) RunAsUid, 527390792Sgshapiro (int) getgid(), (int) getegid()); 527490792Sgshapiro RETURN(EX_TEMPFAIL); 527564562Sgshapiro } 527664562Sgshapiro } 527738032Speter else if (bitset(S_ISGID, mode)) 527838032Speter RealGid = stb.st_gid; 527964562Sgshapiro else if (ctladdr != NULL && 528064562Sgshapiro ctladdr->q_uid == DefUid && 528164562Sgshapiro ctladdr->q_gid == 0) 528271345Sgshapiro { 528371345Sgshapiro /* 528471345Sgshapiro ** Special case: This means it is an 528571345Sgshapiro ** alias and we should act as DefaultUser. 528671345Sgshapiro ** See alias()'s comments. 528771345Sgshapiro */ 528871345Sgshapiro 528964562Sgshapiro RealGid = DefGid; 529071345Sgshapiro RealUserName = DefUser; 529171345Sgshapiro } 529271345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 529371345Sgshapiro RealGid = ctladdr->q_gid; 529438032Speter else if (mailer != NULL && mailer->m_gid != 0) 529538032Speter RealGid = mailer->m_gid; 529638032Speter else 529738032Speter RealGid = DefGid; 529838032Speter } 529938032Speter 530038032Speter /* last ditch */ 530138032Speter if (!bitset(SFF_ROOTOK, sfflags)) 530238032Speter { 530338032Speter if (RealUid == 0) 530438032Speter RealUid = DefUid; 530538032Speter if (RealGid == 0) 530638032Speter RealGid = DefGid; 530738032Speter } 530838032Speter 530938032Speter /* set group id list (needs /etc/group access) */ 531038032Speter if (RealUserName != NULL && !DontInitGroups) 531138032Speter { 531238032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 531364562Sgshapiro { 531438032Speter syserr("mailfile: initgroups(%s, %d) failed", 531538032Speter RealUserName, RealGid); 531690792Sgshapiro RETURN(EX_TEMPFAIL); 531764562Sgshapiro } 531838032Speter } 531938032Speter else 532038032Speter { 532138032Speter GIDSET_T gidset[1]; 532238032Speter 532338032Speter gidset[0] = RealGid; 532438032Speter if (setgroups(1, gidset) == -1 && suidwarn) 532564562Sgshapiro { 532638032Speter syserr("mailfile: setgroups() failed"); 532790792Sgshapiro RETURN(EX_TEMPFAIL); 532864562Sgshapiro } 532938032Speter } 533038032Speter 533164562Sgshapiro /* 533264562Sgshapiro ** If you have a safe environment, go into it. 533364562Sgshapiro */ 533464562Sgshapiro 533564562Sgshapiro if (realfile != targetfile) 533638032Speter { 533794334Sgshapiro char save; 533894334Sgshapiro 533994334Sgshapiro save = *realfile; 534064562Sgshapiro *realfile = '\0'; 534164562Sgshapiro if (tTd(11, 20)) 534290792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 534364562Sgshapiro if (chroot(targetfile) < 0) 534438032Speter { 534538032Speter syserr("mailfile: Cannot chroot(%s)", 534664562Sgshapiro targetfile); 534790792Sgshapiro RETURN(EX_CANTCREAT); 534838032Speter } 534994334Sgshapiro *realfile = save; 535038032Speter } 535164562Sgshapiro 535264562Sgshapiro if (tTd(11, 40)) 535390792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 535464562Sgshapiro 535538032Speter if (chdir("/") < 0) 535664562Sgshapiro { 535738032Speter syserr("mailfile: cannot chdir(/)"); 535890792Sgshapiro RETURN(EX_CANTCREAT); 535964562Sgshapiro } 536038032Speter 536138032Speter /* now reset the group and user ids */ 536238032Speter endpwent(); 536390792Sgshapiro sm_mbdb_terminate(); 536438032Speter if (setgid(RealGid) < 0 && suidwarn) 536564562Sgshapiro { 536638032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 536790792Sgshapiro RETURN(EX_TEMPFAIL); 536864562Sgshapiro } 536938032Speter vendor_set_uid(RealUid); 537038032Speter if (setuid(RealUid) < 0 && suidwarn) 537164562Sgshapiro { 537238032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 537390792Sgshapiro RETURN(EX_TEMPFAIL); 537464562Sgshapiro } 537538032Speter 537664562Sgshapiro if (tTd(11, 2)) 537790792Sgshapiro sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 537864562Sgshapiro (int) getuid(), (int) geteuid(), 537964562Sgshapiro (int) getgid(), (int) getegid()); 538064562Sgshapiro 538164562Sgshapiro 538238032Speter /* move into some "safe" directory */ 538338032Speter if (mailer->m_execdir != NULL) 538438032Speter { 538538032Speter char *q; 538638032Speter 538738032Speter for (p = mailer->m_execdir; p != NULL; p = q) 538838032Speter { 538938032Speter q = strchr(p, ':'); 539038032Speter if (q != NULL) 539138032Speter *q = '\0'; 539238032Speter expand(p, buf, sizeof buf, e); 539338032Speter if (q != NULL) 539438032Speter *q++ = ':'; 539538032Speter if (tTd(11, 20)) 539690792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 539790792Sgshapiro buf); 539838032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 539938032Speter break; 540038032Speter } 540138032Speter } 540238032Speter 540364562Sgshapiro /* 540464562Sgshapiro ** Recheck the file after we have assumed the ID of the 540564562Sgshapiro ** delivery user to make sure we can deliver to it as 540664562Sgshapiro ** that user. This is necessary if sendmail is running 540764562Sgshapiro ** as root and the file is on an NFS mount which treats 540864562Sgshapiro ** root as nobody. 540964562Sgshapiro */ 541064562Sgshapiro 541164562Sgshapiro#if HASLSTAT 541264562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 541364562Sgshapiro err = stat(realfile, &stb); 541464562Sgshapiro else 541564562Sgshapiro err = lstat(realfile, &stb); 541664562Sgshapiro#else /* HASLSTAT */ 541764562Sgshapiro err = stat(realfile, &stb); 541864562Sgshapiro#endif /* HASLSTAT */ 541964562Sgshapiro 542064562Sgshapiro if (err < 0) 542164562Sgshapiro { 542264562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 542364562Sgshapiro mode = FileMode; 542464562Sgshapiro oflags |= O_CREAT|O_EXCL; 542564562Sgshapiro } 542664562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 542764562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 542864562Sgshapiro DontBlameSendmail) && 542964562Sgshapiro stb.st_nlink != 1) || 543064562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 543164562Sgshapiro exit(EX_CANTCREAT); 543264562Sgshapiro else 543364562Sgshapiro mode = stb.st_mode; 543464562Sgshapiro 543564562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 543638032Speter sfflags |= SFF_NOSLINK; 543764562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 543838032Speter sfflags |= SFF_NOHLINK; 543938032Speter sfflags &= ~SFF_OPENASROOT; 544064562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 544138032Speter if (f == NULL) 544238032Speter { 544364562Sgshapiro if (transienterror(errno)) 544464562Sgshapiro { 544564562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 544664562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 544790792Sgshapiro sm_errstring(errno)); 544890792Sgshapiro RETURN(EX_TEMPFAIL); 544964562Sgshapiro } 545064562Sgshapiro else 545164562Sgshapiro { 545264562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 545364562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 545490792Sgshapiro sm_errstring(errno)); 545590792Sgshapiro RETURN(EX_CANTCREAT); 545664562Sgshapiro } 545738032Speter } 545890792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 545990792Sgshapiro &stb)) 546038032Speter { 546164562Sgshapiro syserr("554 5.3.0 file changed after open"); 546290792Sgshapiro RETURN(EX_CANTCREAT); 546338032Speter } 546490792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 546538032Speter { 546690792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 546790792Sgshapiro sm_errstring(errno)); 546890792Sgshapiro RETURN(EX_CANTCREAT); 546938032Speter } 547038032Speter 547164562Sgshapiro curoff = stb.st_size; 547264562Sgshapiro 547338032Speter if (ev != NULL) 547490792Sgshapiro sm_clrevent(ev); 547538032Speter 547664562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 547738032Speter mcibuf.mci_mailer = mailer; 547838032Speter mcibuf.mci_out = f; 547938032Speter if (bitnset(M_7BITS, mailer->m_flags)) 548038032Speter mcibuf.mci_flags |= MCIF_7BIT; 548138032Speter 548238032Speter /* clear out per-message flags from connection structure */ 548338032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 548438032Speter 548538032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 548638032Speter !bitset(EF_DONT_MIME, e->e_flags) && 548738032Speter bitnset(M_7BITS, mailer->m_flags)) 548838032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 548938032Speter 549038032Speter#if MIME7TO8 549138032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 549238032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 549338032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 549490792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 549590792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 549638032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 549738032Speter { 549838032Speter /* may want to convert 7 -> 8 */ 549938032Speter /* XXX should really parse it here -- and use a class XXX */ 550090792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 550164562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 550238032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 550338032Speter } 550464562Sgshapiro#endif /* MIME7TO8 */ 550538032Speter 550638032Speter putfromline(&mcibuf, e); 550743730Speter (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); 550838032Speter (*e->e_putbody)(&mcibuf, e, NULL); 550938032Speter putline("\n", &mcibuf); 551090792Sgshapiro if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 551190792Sgshapiro (SuperSafe != SAFE_NO && 551290792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 551390792Sgshapiro sm_io_error(f)) 551438032Speter { 551538032Speter setstat(EX_IOERR); 551664562Sgshapiro#if !NOFTRUNCATE 551790792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 551890792Sgshapiro curoff); 551964562Sgshapiro#endif /* !NOFTRUNCATE */ 552038032Speter } 552138032Speter 552238032Speter /* reset ISUID & ISGID bits for paranoid systems */ 552338032Speter#if HASFCHMOD 552490792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 552590792Sgshapiro (MODE_T) mode); 552664562Sgshapiro#else /* HASFCHMOD */ 552764562Sgshapiro (void) chmod(filename, (MODE_T) mode); 552864562Sgshapiro#endif /* HASFCHMOD */ 552990792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 553064562Sgshapiro setstat(EX_IOERR); 553190792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 553264562Sgshapiro (void) setuid(RealUid); 553338032Speter exit(ExitStat); 553464562Sgshapiro /* NOTREACHED */ 553538032Speter } 553638032Speter else 553738032Speter { 553838032Speter /* parent -- wait for exit status */ 553938032Speter int st; 554038032Speter 554138032Speter st = waitfor(pid); 554238032Speter if (st == -1) 554338032Speter { 554438032Speter syserr("mailfile: %s: wait", mailer->m_name); 554564562Sgshapiro return EX_SOFTWARE; 554638032Speter } 554738032Speter if (WIFEXITED(st)) 554890792Sgshapiro { 554990792Sgshapiro errno = 0; 555038032Speter return (WEXITSTATUS(st)); 555190792Sgshapiro } 555238032Speter else 555338032Speter { 555438032Speter syserr("mailfile: %s: child died on signal %d", 555538032Speter mailer->m_name, st); 555664562Sgshapiro return EX_UNAVAILABLE; 555738032Speter } 555864562Sgshapiro /* NOTREACHED */ 555938032Speter } 556038032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 556138032Speter} 556238032Speter 556338032Speterstatic void 556438032Spetermailfiletimeout() 556538032Speter{ 556677349Sgshapiro /* 556777349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 556877349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 556977349Sgshapiro ** DOING. 557077349Sgshapiro */ 557177349Sgshapiro 557277349Sgshapiro errno = ETIMEDOUT; 557338032Speter longjmp(CtxMailfileTimeout, 1); 557438032Speter} 557590792Sgshapiro/* 557638032Speter** HOSTSIGNATURE -- return the "signature" for a host. 557738032Speter** 557838032Speter** The signature describes how we are going to send this -- it 557938032Speter** can be just the hostname (for non-Internet hosts) or can be 558038032Speter** an ordered list of MX hosts. 558138032Speter** 558238032Speter** Parameters: 558338032Speter** m -- the mailer describing this host. 558438032Speter** host -- the host name. 558538032Speter** 558638032Speter** Returns: 558738032Speter** The signature for this host. 558838032Speter** 558938032Speter** Side Effects: 559038032Speter** Can tweak the symbol table. 559138032Speter*/ 559290792Sgshapiro 559364562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 559438032Speter 559590792Sgshapirochar * 559664562Sgshapirohostsignature(m, host) 559738032Speter register MAILER *m; 559838032Speter char *host; 559938032Speter{ 560038032Speter register char *p; 560138032Speter register STAB *s; 560290792Sgshapiro time_t now; 560364562Sgshapiro#if NAMED_BIND 560464562Sgshapiro char sep = ':'; 560564562Sgshapiro char prevsep = ':'; 560638032Speter int i; 560738032Speter int len; 560838032Speter int nmx; 560964562Sgshapiro int hl; 561038032Speter char *hp; 561138032Speter char *endp; 561238032Speter int oldoptions = _res.options; 561338032Speter char *mxhosts[MAXMXHOSTS + 1]; 561490792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 561564562Sgshapiro#endif /* NAMED_BIND */ 561638032Speter 561764562Sgshapiro if (tTd(17, 3)) 561890792Sgshapiro sm_dprintf("hostsignature(%s)\n", host); 561964562Sgshapiro 562038032Speter /* 562177349Sgshapiro ** If local delivery (and not remote), just return a constant. 562264562Sgshapiro */ 562364562Sgshapiro 562477349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 562590792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 562690792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 562764562Sgshapiro return "localhost"; 562864562Sgshapiro 562964562Sgshapiro /* 563038032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 563138032Speter */ 563238032Speter 563390792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 563490792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 563538032Speter { 563690792Sgshapiro /* just an ordinary mailer or deferred mode */ 563738032Speter return host; 563838032Speter } 563964562Sgshapiro#if NETUNIX 564064562Sgshapiro else if (m->m_argv[0] != NULL && 564164562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 564264562Sgshapiro { 564364562Sgshapiro /* rendezvous in the file system, no MX records */ 564464562Sgshapiro return host; 564564562Sgshapiro } 564664562Sgshapiro#endif /* NETUNIX */ 564738032Speter 564838032Speter /* 564938032Speter ** Look it up in the symbol table. 565038032Speter */ 565138032Speter 565290792Sgshapiro now = curtime(); 565338032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 565490792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 565564562Sgshapiro { 565690792Sgshapiro if (s->s_hostsig.hs_exp >= now) 565790792Sgshapiro { 565890792Sgshapiro if (tTd(17, 3)) 565990792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 566090792Sgshapiro s->s_hostsig.hs_sig); 566190792Sgshapiro return s->s_hostsig.hs_sig; 566290792Sgshapiro } 566390792Sgshapiro 566490792Sgshapiro /* signature is expired: clear it */ 566590792Sgshapiro sm_free(s->s_hostsig.hs_sig); 566690792Sgshapiro s->s_hostsig.hs_sig = NULL; 566764562Sgshapiro } 566838032Speter 566990792Sgshapiro /* set default TTL */ 567090792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 567190792Sgshapiro 567238032Speter /* 567390792Sgshapiro ** Not already there or expired -- create a signature. 567438032Speter */ 567538032Speter 567638032Speter#if NAMED_BIND 567738032Speter if (ConfigLevel < 2) 567838032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 567938032Speter 568038032Speter for (hp = host; hp != NULL; hp = endp) 568138032Speter { 568264562Sgshapiro#if NETINET6 568364562Sgshapiro if (*hp == '[') 568464562Sgshapiro { 568564562Sgshapiro endp = strchr(hp + 1, ']'); 568664562Sgshapiro if (endp != NULL) 568764562Sgshapiro endp = strpbrk(endp + 1, ":,"); 568864562Sgshapiro } 568964562Sgshapiro else 569064562Sgshapiro endp = strpbrk(hp, ":,"); 569164562Sgshapiro#else /* NETINET6 */ 569264562Sgshapiro endp = strpbrk(hp, ":,"); 569364562Sgshapiro#endif /* NETINET6 */ 569438032Speter if (endp != NULL) 569564562Sgshapiro { 569664562Sgshapiro sep = *endp; 569738032Speter *endp = '\0'; 569864562Sgshapiro } 569938032Speter 570038032Speter if (bitnset(M_NOMX, m->m_flags)) 570138032Speter { 570238032Speter /* skip MX lookups */ 570338032Speter nmx = 1; 570438032Speter mxhosts[0] = hp; 570538032Speter } 570638032Speter else 570738032Speter { 570838032Speter auto int rcode; 570990792Sgshapiro int ttl; 571038032Speter 571190792Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 571290792Sgshapiro &ttl); 571338032Speter if (nmx <= 0) 571438032Speter { 571580785Sgshapiro int save_errno; 571638032Speter register MCI *mci; 571738032Speter 571838032Speter /* update the connection info for this host */ 571980785Sgshapiro save_errno = errno; 572038032Speter mci = mci_get(hp, m); 572180785Sgshapiro mci->mci_errno = save_errno; 572238032Speter mci->mci_herrno = h_errno; 572371345Sgshapiro mci->mci_lastuse = now; 572464562Sgshapiro if (rcode == EX_NOHOST) 572564562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 572690792Sgshapiro "550 Host unknown"); 572764562Sgshapiro else 572864562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 572938032Speter 573038032Speter /* use the original host name as signature */ 573138032Speter nmx = 1; 573238032Speter mxhosts[0] = hp; 573338032Speter } 573464562Sgshapiro if (tTd(17, 3)) 573590792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 573690792Sgshapiro nmx, mxhosts[0]); 573790792Sgshapiro 573890792Sgshapiro /* 573990792Sgshapiro ** Set new TTL: we use only one! 574090792Sgshapiro ** We could try to use the minimum instead. 574190792Sgshapiro */ 574290792Sgshapiro 574390792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 574438032Speter } 574538032Speter 574638032Speter len = 0; 574738032Speter for (i = 0; i < nmx; i++) 574838032Speter len += strlen(mxhosts[i]) + 1; 574990792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 575090792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 575190792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 575264562Sgshapiro { 575364562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 575464562Sgshapiro host, MAXHOSTSIGNATURE, len); 575564562Sgshapiro len = MAXHOSTSIGNATURE; 575664562Sgshapiro } 575790792Sgshapiro p = sm_pmalloc_x(len); 575890792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 575938032Speter { 576090792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 576190792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 576290792Sgshapiro s->s_hostsig.hs_sig = p; 576364562Sgshapiro hl = strlen(p); 576464562Sgshapiro p += hl; 576564562Sgshapiro *p++ = prevsep; 576664562Sgshapiro len -= hl + 1; 576738032Speter } 576838032Speter else 576990792Sgshapiro s->s_hostsig.hs_sig = p; 577038032Speter for (i = 0; i < nmx; i++) 577138032Speter { 577264562Sgshapiro hl = strlen(mxhosts[i]); 577364562Sgshapiro if (len - 1 < hl || len <= 1) 577464562Sgshapiro { 577564562Sgshapiro /* force to drop out of outer loop */ 577664562Sgshapiro len = -1; 577764562Sgshapiro break; 577864562Sgshapiro } 577938032Speter if (i != 0) 578064562Sgshapiro { 578164562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 578264562Sgshapiro *p++ = ','; 578364562Sgshapiro else 578464562Sgshapiro *p++ = ':'; 578564562Sgshapiro len--; 578664562Sgshapiro } 578790792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 578864562Sgshapiro p += hl; 578964562Sgshapiro len -= hl; 579038032Speter } 579164562Sgshapiro 579264562Sgshapiro /* 579364562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 579464562Sgshapiro ** because we won't have more space for further hosts 579564562Sgshapiro ** anyway (separated by : in the .cf file). 579664562Sgshapiro */ 579764562Sgshapiro 579864562Sgshapiro if (len < 0) 579964562Sgshapiro break; 580038032Speter if (endp != NULL) 580164562Sgshapiro *endp++ = sep; 580264562Sgshapiro prevsep = sep; 580338032Speter } 580490792Sgshapiro makelower(s->s_hostsig.hs_sig); 580538032Speter if (ConfigLevel < 2) 580638032Speter _res.options = oldoptions; 580764562Sgshapiro#else /* NAMED_BIND */ 580838032Speter /* not using BIND -- the signature is just the host name */ 580990792Sgshapiro /* 581090792Sgshapiro ** 'host' points to storage that will be freed after we are 581190792Sgshapiro ** done processing the current envelope, so we copy it. 581290792Sgshapiro */ 581390792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 581464562Sgshapiro#endif /* NAMED_BIND */ 581538032Speter if (tTd(17, 1)) 581690792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 581790792Sgshapiro return s->s_hostsig.hs_sig; 581838032Speter} 581990792Sgshapiro/* 582064562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 582164562Sgshapiro** 582264562Sgshapiro** The signature describes how we are going to send this -- it 582364562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 582464562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 582564562Sgshapiro** MX preference values. 582664562Sgshapiro** 582764562Sgshapiro** Parameters: 582864562Sgshapiro** sig -- the host signature. 582964562Sgshapiro** mxhosts -- array to populate. 583090792Sgshapiro** mailer -- mailer. 583164562Sgshapiro** 583264562Sgshapiro** Returns: 583364562Sgshapiro** The number of hosts inserted into mxhosts array. 583464562Sgshapiro** 583564562Sgshapiro** Side Effects: 583664562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 583764562Sgshapiro*/ 583864562Sgshapiro 583964562Sgshapirostatic int 584064562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 584164562Sgshapiro char *sig; 584264562Sgshapiro char **mxhosts; 584364562Sgshapiro MAILER *mailer; 584464562Sgshapiro{ 584590792Sgshapiro unsigned short curpref = 0; 584690792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 584764562Sgshapiro char *hp, *endp; 584890792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 584964562Sgshapiro long rndm[MAXMXHOSTS]; 585064562Sgshapiro 585164562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 585264562Sgshapiro { 585364562Sgshapiro char sep = ':'; 585464562Sgshapiro 585564562Sgshapiro#if NETINET6 585664562Sgshapiro if (*hp == '[') 585764562Sgshapiro { 585864562Sgshapiro endp = strchr(hp + 1, ']'); 585964562Sgshapiro if (endp != NULL) 586064562Sgshapiro endp = strpbrk(endp + 1, ":,"); 586164562Sgshapiro } 586264562Sgshapiro else 586364562Sgshapiro endp = strpbrk(hp, ":,"); 586464562Sgshapiro#else /* NETINET6 */ 586564562Sgshapiro endp = strpbrk(hp, ":,"); 586664562Sgshapiro#endif /* NETINET6 */ 586764562Sgshapiro if (endp != NULL) 586864562Sgshapiro { 586964562Sgshapiro sep = *endp; 587064562Sgshapiro *endp = '\0'; 587164562Sgshapiro } 587264562Sgshapiro 587364562Sgshapiro mxhosts[nmx] = hp; 587464562Sgshapiro prefer[nmx] = curpref; 587564562Sgshapiro if (mci_match(hp, mailer)) 587664562Sgshapiro rndm[nmx] = 0; 587764562Sgshapiro else 587864562Sgshapiro rndm[nmx] = get_random(); 587964562Sgshapiro 588064562Sgshapiro if (endp != NULL) 588164562Sgshapiro { 588264562Sgshapiro /* 588364562Sgshapiro ** Since we don't have the original MX prefs, 588464562Sgshapiro ** make our own. If the separator is a ':', that 588564562Sgshapiro ** means the preference for the next host will be 588664562Sgshapiro ** higher than this one, so simply increment curpref. 588764562Sgshapiro */ 588864562Sgshapiro 588964562Sgshapiro if (sep == ':') 589064562Sgshapiro curpref++; 589164562Sgshapiro 589264562Sgshapiro *endp++ = sep; 589364562Sgshapiro } 589464562Sgshapiro if (++nmx >= MAXMXHOSTS) 589564562Sgshapiro break; 589664562Sgshapiro } 589764562Sgshapiro 589864562Sgshapiro /* sort the records using the random factor for equal preferences */ 589964562Sgshapiro for (i = 0; i < nmx; i++) 590064562Sgshapiro { 590164562Sgshapiro for (j = i + 1; j < nmx; j++) 590264562Sgshapiro { 590364562Sgshapiro /* 590464562Sgshapiro ** List is already sorted by MX preference, only 590564562Sgshapiro ** need to look for equal preference MX records 590664562Sgshapiro */ 590764562Sgshapiro 590864562Sgshapiro if (prefer[i] < prefer[j]) 590964562Sgshapiro break; 591064562Sgshapiro 591164562Sgshapiro if (prefer[i] > prefer[j] || 591264562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 591364562Sgshapiro { 591490792Sgshapiro register unsigned short tempp; 591564562Sgshapiro register long tempr; 591664562Sgshapiro register char *temp1; 591764562Sgshapiro 591864562Sgshapiro tempp = prefer[i]; 591964562Sgshapiro prefer[i] = prefer[j]; 592064562Sgshapiro prefer[j] = tempp; 592164562Sgshapiro temp1 = mxhosts[i]; 592264562Sgshapiro mxhosts[i] = mxhosts[j]; 592364562Sgshapiro mxhosts[j] = temp1; 592464562Sgshapiro tempr = rndm[i]; 592564562Sgshapiro rndm[i] = rndm[j]; 592664562Sgshapiro rndm[j] = tempr; 592764562Sgshapiro } 592864562Sgshapiro } 592964562Sgshapiro } 593064562Sgshapiro return nmx; 593164562Sgshapiro} 593264562Sgshapiro 593364562Sgshapiro# if STARTTLS 593464562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 593590792Sgshapirostatic bool tls_ok_clt = true; 593664562Sgshapiro 593790792Sgshapiro/* 593890792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 593964562Sgshapiro** 594064562Sgshapiro** Parameters: 594190792Sgshapiro** tls_ok -- should tls be done? 594290792Sgshapiro** 594390792Sgshapiro** Returns: 594464562Sgshapiro** none. 594564562Sgshapiro** 594690792Sgshapiro** Side Effects: 594790792Sgshapiro** sets tls_ok_clt (static variable in this module) 594890792Sgshapiro*/ 594990792Sgshapiro 595090792Sgshapirovoid 595190792Sgshapirosetclttls(tls_ok) 595290792Sgshapiro bool tls_ok; 595390792Sgshapiro{ 595490792Sgshapiro tls_ok_clt = tls_ok; 595590792Sgshapiro return; 595690792Sgshapiro} 595790792Sgshapiro/* 595890792Sgshapiro** INITCLTTLS -- initialize client side TLS 595990792Sgshapiro** 596090792Sgshapiro** Parameters: 596190792Sgshapiro** tls_ok -- should tls initialization be done? 596290792Sgshapiro** 596364562Sgshapiro** Returns: 596464562Sgshapiro** succeeded? 596590792Sgshapiro** 596690792Sgshapiro** Side Effects: 596790792Sgshapiro** sets tls_ok_clt (static variable in this module) 596864562Sgshapiro*/ 596964562Sgshapiro 597064562Sgshapirobool 597190792Sgshapiroinitclttls(tls_ok) 597290792Sgshapiro bool tls_ok; 597364562Sgshapiro{ 597490792Sgshapiro if (!tls_ok_clt) 597590792Sgshapiro return false; 597690792Sgshapiro tls_ok_clt = tls_ok; 597790792Sgshapiro if (!tls_ok_clt) 597890792Sgshapiro return false; 597964562Sgshapiro if (clt_ctx != NULL) 598090792Sgshapiro return true; /* already done */ 5981110560Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile, 5982110560Sgshapiro CltKeyFile, CACertPath, CACertFile, DHParams); 598390792Sgshapiro return tls_ok_clt; 598464562Sgshapiro} 598564562Sgshapiro 598690792Sgshapiro/* 598764562Sgshapiro** STARTTLS -- try to start secure connection (client side) 598864562Sgshapiro** 598964562Sgshapiro** Parameters: 599064562Sgshapiro** m -- the mailer. 599164562Sgshapiro** mci -- the mailer connection info. 599264562Sgshapiro** e -- the envelope. 599364562Sgshapiro** 599464562Sgshapiro** Returns: 599564562Sgshapiro** success? 599664562Sgshapiro** (maybe this should be some other code than EX_ 599764562Sgshapiro** that denotes which stage failed.) 599864562Sgshapiro*/ 599964562Sgshapiro 600064562Sgshapirostatic int 600164562Sgshapirostarttls(m, mci, e) 600264562Sgshapiro MAILER *m; 600364562Sgshapiro MCI *mci; 600464562Sgshapiro ENVELOPE *e; 600564562Sgshapiro{ 600664562Sgshapiro int smtpresult; 600766494Sgshapiro int result = 0; 600866494Sgshapiro int rfd, wfd; 600964562Sgshapiro SSL *clt_ssl = NULL; 601090792Sgshapiro time_t tlsstart; 601164562Sgshapiro 601290792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 601366494Sgshapiro return EX_TEMPFAIL; 601464562Sgshapiro smtpmessage("STARTTLS", m, mci); 601564562Sgshapiro 601664562Sgshapiro /* get the reply */ 601790792Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL); 601864562Sgshapiro 601964562Sgshapiro /* check return code from server */ 602064562Sgshapiro if (smtpresult == 454) 602164562Sgshapiro return EX_TEMPFAIL; 602264562Sgshapiro if (smtpresult == 501) 602364562Sgshapiro return EX_USAGE; 602464562Sgshapiro if (smtpresult == -1) 602564562Sgshapiro return smtpresult; 602664562Sgshapiro if (smtpresult != 220) 602764562Sgshapiro return EX_PROTOCOL; 602864562Sgshapiro 602964562Sgshapiro if (LogLevel > 13) 603090792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 603164562Sgshapiro 603264562Sgshapiro /* start connection */ 603364562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 603464562Sgshapiro { 603564562Sgshapiro if (LogLevel > 5) 603664562Sgshapiro { 603790792Sgshapiro sm_syslog(LOG_ERR, NOQID, 603890792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 603964562Sgshapiro if (LogLevel > 9) 604090792Sgshapiro tlslogerr("client"); 604164562Sgshapiro } 604264562Sgshapiro return EX_SOFTWARE; 604364562Sgshapiro } 604464562Sgshapiro 604590792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 604690792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 604766494Sgshapiro 604864562Sgshapiro /* SSL_clear(clt_ssl); ? */ 604966494Sgshapiro if (rfd < 0 || wfd < 0 || 605090792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 605190792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 605264562Sgshapiro { 605364562Sgshapiro if (LogLevel > 5) 605464562Sgshapiro { 605590792Sgshapiro sm_syslog(LOG_ERR, NOQID, 605690792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 605790792Sgshapiro result); 605864562Sgshapiro if (LogLevel > 9) 605990792Sgshapiro tlslogerr("client"); 606064562Sgshapiro } 606164562Sgshapiro return EX_SOFTWARE; 606264562Sgshapiro } 606364562Sgshapiro SSL_set_connect_state(clt_ssl); 606490792Sgshapiro tlsstart = curtime(); 606590792Sgshapiro 606690792Sgshapirossl_retry: 606764562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 606864562Sgshapiro { 606964562Sgshapiro int i; 607090792Sgshapiro bool timedout; 607190792Sgshapiro time_t left; 607290792Sgshapiro time_t now = curtime(); 607390792Sgshapiro struct timeval tv; 607464562Sgshapiro 607564562Sgshapiro /* what to do in this case? */ 607664562Sgshapiro i = SSL_get_error(clt_ssl, result); 607790792Sgshapiro 607890792Sgshapiro /* 607990792Sgshapiro ** For SSL_ERROR_WANT_{READ,WRITE}: 608090792Sgshapiro ** There is not a complete SSL record available yet 608190792Sgshapiro ** or there is only a partial SSL record removed from 608290792Sgshapiro ** the network (socket) buffer into the SSL buffer. 608390792Sgshapiro ** The SSL_connect will only succeed when a full 608490792Sgshapiro ** SSL record is available (assuming a "real" error 608590792Sgshapiro ** doesn't happen). To handle when a "real" error 608690792Sgshapiro ** does happen the select is set for exceptions too. 608790792Sgshapiro ** The connection may be re-negotiated during this time 608890792Sgshapiro ** so both read and write "want errors" need to be handled. 608990792Sgshapiro ** A select() exception loops back so that a proper SSL 609090792Sgshapiro ** error message can be gotten. 609190792Sgshapiro */ 609290792Sgshapiro 609390792Sgshapiro left = TimeOuts.to_starttls - (now - tlsstart); 609490792Sgshapiro timedout = left <= 0; 609590792Sgshapiro if (!timedout) 609690792Sgshapiro { 609790792Sgshapiro tv.tv_sec = left; 609890792Sgshapiro tv.tv_usec = 0; 609990792Sgshapiro } 610090792Sgshapiro 6101110560Sgshapiro if (!timedout && FD_SETSIZE > 0 && 6102110560Sgshapiro (rfd >= FD_SETSIZE || 6103110560Sgshapiro (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE))) 6104110560Sgshapiro { 6105110560Sgshapiro if (LogLevel > 5) 6106110560Sgshapiro { 6107110560Sgshapiro sm_syslog(LOG_ERR, e->e_id, 6108110560Sgshapiro "STARTTLS=client, error: fd %d/%d too large", 6109110560Sgshapiro rfd, wfd); 6110110560Sgshapiro if (LogLevel > 8) 6111110560Sgshapiro tlslogerr("client"); 6112110560Sgshapiro } 6113110560Sgshapiro errno = EINVAL; 6114110560Sgshapiro goto tlsfail; 6115110560Sgshapiro } 611690792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_READ) 611790792Sgshapiro { 611890792Sgshapiro fd_set ssl_maskr, ssl_maskx; 611990792Sgshapiro 612090792Sgshapiro FD_ZERO(&ssl_maskr); 612190792Sgshapiro FD_SET(rfd, &ssl_maskr); 612290792Sgshapiro FD_ZERO(&ssl_maskx); 612390792Sgshapiro FD_SET(rfd, &ssl_maskx); 612490792Sgshapiro if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv) 612590792Sgshapiro > 0) 612690792Sgshapiro goto ssl_retry; 612790792Sgshapiro } 612890792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_WRITE) 612990792Sgshapiro { 613090792Sgshapiro fd_set ssl_maskw, ssl_maskx; 613190792Sgshapiro 613290792Sgshapiro FD_ZERO(&ssl_maskw); 613390792Sgshapiro FD_SET(wfd, &ssl_maskw); 613490792Sgshapiro FD_ZERO(&ssl_maskx); 613590792Sgshapiro FD_SET(rfd, &ssl_maskx); 613690792Sgshapiro if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv) 613790792Sgshapiro > 0) 613890792Sgshapiro goto ssl_retry; 613990792Sgshapiro } 614064562Sgshapiro if (LogLevel > 5) 614164562Sgshapiro { 614264562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 6143111823Sgshapiro "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d, errno=%d", 6144111823Sgshapiro result, i, (int) timedout, errno); 614590792Sgshapiro if (LogLevel > 8) 614690792Sgshapiro tlslogerr("client"); 614764562Sgshapiro } 6148110560Sgshapirotlsfail: 614964562Sgshapiro SSL_free(clt_ssl); 615064562Sgshapiro clt_ssl = NULL; 615164562Sgshapiro return EX_SOFTWARE; 615264562Sgshapiro } 615364562Sgshapiro mci->mci_ssl = clt_ssl; 615490792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 615590792Sgshapiro &mci->mci_macro, true); 615664562Sgshapiro 615790792Sgshapiro /* switch to use TLS... */ 615864562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 615964562Sgshapiro return EX_OK; 616064562Sgshapiro 616164562Sgshapiro /* failure */ 616264562Sgshapiro SSL_free(clt_ssl); 616364562Sgshapiro clt_ssl = NULL; 616464562Sgshapiro return EX_SOFTWARE; 616564562Sgshapiro} 616690792Sgshapiro/* 616764562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 616864562Sgshapiro** 616964562Sgshapiro** Parameters: 617064562Sgshapiro** mci -- the mailer connection info. 617164562Sgshapiro** 617264562Sgshapiro** Returns: 617364562Sgshapiro** success? 617464562Sgshapiro*/ 617590792Sgshapiro 617690792Sgshapirostatic int 617764562Sgshapiroendtlsclt(mci) 617864562Sgshapiro MCI *mci; 617964562Sgshapiro{ 618064562Sgshapiro int r; 618164562Sgshapiro 618264562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 618364562Sgshapiro return EX_OK; 618464562Sgshapiro r = endtls(mci->mci_ssl, "client"); 618564562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 618664562Sgshapiro return r; 618764562Sgshapiro} 618890792Sgshapiro# endif /* STARTTLS */ 618990792Sgshapiro# if STARTTLS || SASL 619090792Sgshapiro/* 619190792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 619264562Sgshapiro** 619364562Sgshapiro** Parameters: 619490792Sgshapiro** e -- envelope. 619590792Sgshapiro** flag -- flag to check in {client_flags} 619664562Sgshapiro** 619764562Sgshapiro** Returns: 619890792Sgshapiro** true iff flag is set. 619964562Sgshapiro*/ 620064562Sgshapiro 620190792Sgshapirostatic bool 620290792Sgshapiroiscltflgset(e, flag) 620390792Sgshapiro ENVELOPE *e; 620490792Sgshapiro int flag; 620564562Sgshapiro{ 620690792Sgshapiro char *p; 620773188Sgshapiro 620890792Sgshapiro p = macvalue(macid("{client_flags}"), e); 620990792Sgshapiro if (p == NULL) 621090792Sgshapiro return false; 621190792Sgshapiro for (; *p != '\0'; p++) 621264562Sgshapiro { 621390792Sgshapiro /* look for just this one flag */ 621490792Sgshapiro if (*p == (char) flag) 621590792Sgshapiro return true; 621664562Sgshapiro } 621790792Sgshapiro return false; 621864562Sgshapiro} 621990792Sgshapiro# endif /* STARTTLS || SASL */ 6220