deliver.c revision 141858
138032Speter/* 2141858Sgshapiro * Copyright (c) 1998-2005 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 17141858SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.983 2005/01/07 17:43:22 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 3164562Sgshapirostatic int deliver __P((ENVELOPE *, ADDRESS *)); 3264562Sgshapirostatic void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 33141858Sgshapirostatic void mailfiletimeout __P((int)); 34141858Sgshapirostatic void endwaittimeout __P((int)); 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); 126132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 12790792Sgshapiro sm_dprintf("\te_flags = "); 12838032Speter printenvflags(e); 12990792Sgshapiro sm_dprintf("sendqueue:\n"); 130132943Sgshapiro printaddr(sm_debug_file(), 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 "); 187132943Sgshapiro printaddr(sm_debug_file(), &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"); 218132943Sgshapiro printaddr(sm_debug_file(), 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 "); 236132943Sgshapiro printaddr(sm_debug_file(), 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 "); 247132943Sgshapiro printaddr(sm_debug_file(), 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 307132943Sgshapiro if (FallbackMX != NULL && 308132943Sgshapiro !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)) 319132943Sgshapiro sm_dprintf(" ... FallbackMX\n"); 32064562Sgshapiro 321132943Sgshapiro len = strlen(FallbackMX) + 1; 32290792Sgshapiro p = sm_rpool_malloc_x(e->e_rpool, len); 323132943Sgshapiro (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 else if (QueueMode != QM_QUARANTINE && 36390792Sgshapiro e->e_quarmsg != NULL) 36490792Sgshapiro { 36590792Sgshapiro if (tTd(13, 30)) 36690792Sgshapiro sm_dprintf(" ... quarantine: %s\n", 36790792Sgshapiro e->e_quarmsg); 36890792Sgshapiro q->q_state = QS_QUEUEUP; 36990792Sgshapiro expensive = true; 37090792Sgshapiro } 37138032Speter else 37238032Speter { 37338032Speter if (tTd(13, 30)) 37490792Sgshapiro sm_dprintf(" ... deliverable\n"); 37590792Sgshapiro somedeliveries = true; 37638032Speter } 37738032Speter } 37838032Speter 37938032Speter if (owner != NULL && otherowners > 0) 38038032Speter { 38138032Speter /* 38238032Speter ** Split this envelope into two. 38338032Speter */ 38438032Speter 38590792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 38690792Sgshapiro sizeof *ee); 38790792Sgshapiro STRUCTCOPY(*e, *ee); 38864562Sgshapiro ee->e_message = NULL; 38938032Speter ee->e_id = NULL; 39064562Sgshapiro assign_queueid(ee); 39138032Speter 39238032Speter if (tTd(13, 1)) 39390792Sgshapiro sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 39490792Sgshapiro e->e_id, ee->e_id, owner, 39590792Sgshapiro otherowners); 39638032Speter 39790792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 39890792Sgshapiro ee->e_sendqueue = copyqueue(e->e_sendqueue, 39990792Sgshapiro ee->e_rpool); 40090792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, 40190792Sgshapiro ee->e_rpool); 40238032Speter ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 40338032Speter ee->e_flags |= EF_NORECEIPT; 40490792Sgshapiro setsender(owner, ee, NULL, '\0', true); 40538032Speter if (tTd(13, 5)) 40638032Speter { 40790792Sgshapiro sm_dprintf("sendall(split): QS_SENDER "); 408132943Sgshapiro printaddr(sm_debug_file(), &ee->e_from, false); 40938032Speter } 41064562Sgshapiro ee->e_from.q_state = QS_SENDER; 41138032Speter ee->e_dfp = NULL; 41264562Sgshapiro ee->e_lockfp = NULL; 41338032Speter ee->e_xfp = NULL; 41490792Sgshapiro ee->e_qgrp = e->e_qgrp; 41590792Sgshapiro ee->e_qdir = e->e_qdir; 41638032Speter ee->e_errormode = EM_MAIL; 41738032Speter ee->e_sibling = splitenv; 41864562Sgshapiro ee->e_statmsg = NULL; 41990792Sgshapiro if (e->e_quarmsg != NULL) 42090792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 42190792Sgshapiro e->e_quarmsg); 42238032Speter splitenv = ee; 42338032Speter 42438032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 42538032Speter { 42638032Speter if (q->q_owner == owner) 42738032Speter { 42864562Sgshapiro q->q_state = QS_CLONED; 42938032Speter if (tTd(13, 6)) 43090792Sgshapiro sm_dprintf("\t... stripping %s from original envelope\n", 43190792Sgshapiro q->q_paddr); 43238032Speter } 43338032Speter } 43438032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 43538032Speter { 43638032Speter if (q->q_owner != owner) 43738032Speter { 43864562Sgshapiro q->q_state = QS_CLONED; 43938032Speter if (tTd(13, 6)) 44090792Sgshapiro sm_dprintf("\t... dropping %s from cloned envelope\n", 44190792Sgshapiro q->q_paddr); 44238032Speter } 44338032Speter else 44438032Speter { 44538032Speter /* clear DSN parameters */ 44638032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 44738032Speter q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 44838032Speter if (tTd(13, 6)) 44990792Sgshapiro sm_dprintf("\t... moving %s to cloned envelope\n", 45090792Sgshapiro q->q_paddr); 45138032Speter } 45238032Speter } 45338032Speter 45438032Speter if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 45590792Sgshapiro dup_queue_file(e, ee, DATAFL_LETTER); 45664562Sgshapiro 45764562Sgshapiro /* 45864562Sgshapiro ** Give the split envelope access to the parent 45964562Sgshapiro ** transcript file for errors obtained while 46064562Sgshapiro ** processing the recipients (done before the 46164562Sgshapiro ** envelope splitting). 46264562Sgshapiro */ 46364562Sgshapiro 46464562Sgshapiro if (e->e_xfp != NULL) 46590792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 46664562Sgshapiro 46764562Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 46864562Sgshapiro if (ee->e_xfp == NULL) 46964562Sgshapiro openxscript(ee); 47064562Sgshapiro 47142575Speter if (mode != SM_VERIFY && LogLevel > 4) 47290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 47390792Sgshapiro "%s: clone: owner=%s", 47490792Sgshapiro ee->e_id, owner); 47538032Speter } 47638032Speter } 47738032Speter 47838032Speter if (owner != NULL) 47938032Speter { 48090792Sgshapiro setsender(owner, e, NULL, '\0', true); 48138032Speter if (tTd(13, 5)) 48238032Speter { 48390792Sgshapiro sm_dprintf("sendall(owner): QS_SENDER "); 484132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 48538032Speter } 48664562Sgshapiro e->e_from.q_state = QS_SENDER; 48738032Speter e->e_errormode = EM_MAIL; 48838032Speter e->e_flags |= EF_NORECEIPT; 48938032Speter e->e_flags &= ~EF_FATALERRS; 49038032Speter } 49138032Speter 49238032Speter /* if nothing to be delivered, just queue up everything */ 49390792Sgshapiro if (!somedeliveries && !WILL_BE_QUEUED(mode) && 49438032Speter mode != SM_VERIFY) 49538032Speter { 49690792Sgshapiro time_t now; 49771345Sgshapiro 49838032Speter if (tTd(13, 29)) 49990792Sgshapiro sm_dprintf("No deliveries: auto-queuing\n"); 50038032Speter mode = SM_QUEUE; 50190792Sgshapiro now = curtime(); 50238032Speter 50338032Speter /* treat this as a delivery in terms of counting tries */ 50471345Sgshapiro e->e_dtime = now; 50538032Speter if (!expensive) 50638032Speter e->e_ntries++; 50738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 50838032Speter { 50971345Sgshapiro ee->e_dtime = now; 51038032Speter if (!expensive) 51138032Speter ee->e_ntries++; 51238032Speter } 51338032Speter } 51438032Speter 51590792Sgshapiro if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 516132943Sgshapiro (mode != SM_VERIFY && 517132943Sgshapiro (SuperSafe == SAFE_REALLY || 518132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER))) && 51938032Speter (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 52038032Speter { 52190792Sgshapiro bool msync; 52290792Sgshapiro 52366494Sgshapiro /* 52466494Sgshapiro ** Be sure everything is instantiated in the queue. 52566494Sgshapiro ** Split envelopes first in case the machine crashes. 52666494Sgshapiro ** If the original were done first, we may lose 52766494Sgshapiro ** recipients. 52866494Sgshapiro */ 52966494Sgshapiro 53090792Sgshapiro#if !HASFLOCK 53190792Sgshapiro msync = false; 53290792Sgshapiro#else /* !HASFLOCK */ 53390792Sgshapiro msync = mode == SM_FORK; 53490792Sgshapiro#endif /* !HASFLOCK */ 53590792Sgshapiro 53638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 53790792Sgshapiro queueup(ee, WILL_BE_QUEUED(mode), msync); 53890792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), msync); 53938032Speter } 54038032Speter 54138032Speter if (tTd(62, 10)) 54238032Speter checkfds("after envelope splitting"); 54338032Speter 54438032Speter /* 54538032Speter ** If we belong in background, fork now. 54638032Speter */ 54738032Speter 54838032Speter if (tTd(13, 20)) 54938032Speter { 55090792Sgshapiro sm_dprintf("sendall: final mode = %c\n", mode); 55138032Speter if (tTd(13, 21)) 55238032Speter { 55390792Sgshapiro sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 55490792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 55590792Sgshapiro e->e_id, e->e_from.q_paddr); 556132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 55738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 55838032Speter { 55990792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 56090792Sgshapiro ee->e_id, ee->e_from.q_paddr); 561132943Sgshapiro printaddr(sm_debug_file(), ee->e_sendqueue, true); 56238032Speter } 56390792Sgshapiro sm_dprintf("==========================================================\n\n"); 56438032Speter } 56538032Speter } 56638032Speter switch (mode) 56738032Speter { 56838032Speter case SM_VERIFY: 56938032Speter Verbose = 2; 57038032Speter break; 57138032Speter 57238032Speter case SM_QUEUE: 57338032Speter case SM_DEFER: 57464562Sgshapiro#if HASFLOCK 57538032Speter queueonly: 57664562Sgshapiro#endif /* HASFLOCK */ 57738032Speter if (e->e_nrcpts > 0) 57838032Speter e->e_flags |= EF_INQUEUE; 57990792Sgshapiro dropenvelope(e, splitenv != NULL, true); 58038032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 58138032Speter { 58238032Speter if (ee->e_nrcpts > 0) 58338032Speter ee->e_flags |= EF_INQUEUE; 58490792Sgshapiro dropenvelope(ee, false, true); 58538032Speter } 58638032Speter return; 58738032Speter 58838032Speter case SM_FORK: 58938032Speter if (e->e_xfp != NULL) 59090792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 59138032Speter 59264562Sgshapiro#if !HASFLOCK 59338032Speter /* 59438032Speter ** Since fcntl locking has the interesting semantic that 59538032Speter ** the lock is owned by a process, not by an open file 59638032Speter ** descriptor, we have to flush this to the queue, and 59738032Speter ** then restart from scratch in the child. 59838032Speter */ 59938032Speter 60038032Speter { 60138032Speter /* save id for future use */ 60238032Speter char *qid = e->e_id; 60338032Speter 60438032Speter /* now drop the envelope in the parent */ 60538032Speter e->e_flags |= EF_INQUEUE; 60690792Sgshapiro dropenvelope(e, splitenv != NULL, false); 60738032Speter 60838032Speter /* arrange to reacquire lock after fork */ 60938032Speter e->e_id = qid; 61038032Speter } 61138032Speter 61238032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 61338032Speter { 61438032Speter /* save id for future use */ 61538032Speter char *qid = ee->e_id; 61638032Speter 61738032Speter /* drop envelope in parent */ 61838032Speter ee->e_flags |= EF_INQUEUE; 61990792Sgshapiro dropenvelope(ee, false, false); 62038032Speter 62138032Speter /* and save qid for reacquisition */ 62238032Speter ee->e_id = qid; 62338032Speter } 624132943Sgshapiro 62564562Sgshapiro#endif /* !HASFLOCK */ 62638032Speter 62764562Sgshapiro /* 62864562Sgshapiro ** Since the delivery may happen in a child and the parent 62964562Sgshapiro ** does not wait, the parent may close the maps thereby 63064562Sgshapiro ** removing any shared memory used by the map. Therefore, 63164562Sgshapiro ** close the maps now so the child will dynamically open 63264562Sgshapiro ** them if necessary. 63364562Sgshapiro */ 63464562Sgshapiro 63590792Sgshapiro closemaps(false); 63664562Sgshapiro 63738032Speter pid = fork(); 63838032Speter if (pid < 0) 63938032Speter { 64064562Sgshapiro syserr("deliver: fork 1"); 64164562Sgshapiro#if HASFLOCK 64238032Speter goto queueonly; 64364562Sgshapiro#else /* HASFLOCK */ 64438032Speter e->e_id = NULL; 64538032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 64638032Speter ee->e_id = NULL; 64738032Speter return; 64864562Sgshapiro#endif /* HASFLOCK */ 64938032Speter } 65038032Speter else if (pid > 0) 65138032Speter { 65264562Sgshapiro#if HASFLOCK 65338032Speter /* be sure we leave the temp files to our child */ 65438032Speter /* close any random open files in the envelope */ 65538032Speter closexscript(e); 65638032Speter if (e->e_dfp != NULL) 65790792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 65838032Speter e->e_dfp = NULL; 65938032Speter e->e_flags &= ~EF_HAS_DF; 66038032Speter 66138032Speter /* can't call unlockqueue to avoid unlink of xfp */ 66238032Speter if (e->e_lockfp != NULL) 66390792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 66464562Sgshapiro else 66564562Sgshapiro syserr("%s: sendall: null lockfp", e->e_id); 66638032Speter e->e_lockfp = NULL; 66764562Sgshapiro#endif /* HASFLOCK */ 66838032Speter 66938032Speter /* make sure the parent doesn't own the envelope */ 67038032Speter e->e_id = NULL; 67138032Speter 67290792Sgshapiro#if USE_DOUBLE_FORK 67338032Speter /* catch intermediate zombie */ 67438032Speter (void) waitfor(pid); 67590792Sgshapiro#endif /* USE_DOUBLE_FORK */ 67638032Speter return; 67738032Speter } 67838032Speter 67977349Sgshapiro /* Reset global flags */ 68077349Sgshapiro RestartRequest = NULL; 68190792Sgshapiro RestartWorkGroup = false; 68277349Sgshapiro ShutdownRequest = NULL; 68377349Sgshapiro PendingSignal = 0; 68477349Sgshapiro 68566494Sgshapiro /* 68690792Sgshapiro ** Initialize exception stack and default exception 68790792Sgshapiro ** handler for child process. 68890792Sgshapiro */ 68990792Sgshapiro 69090792Sgshapiro sm_exc_newthread(fatal_error); 69190792Sgshapiro 69290792Sgshapiro /* 69366494Sgshapiro ** Since we have accepted responsbility for the message, 69466494Sgshapiro ** change the SIGTERM handler. intsig() (the old handler) 69566494Sgshapiro ** would remove the envelope if this was a command line 69666494Sgshapiro ** message submission. 69766494Sgshapiro */ 69866494Sgshapiro 69990792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 70066494Sgshapiro 70190792Sgshapiro#if USE_DOUBLE_FORK 70238032Speter /* double fork to avoid zombies */ 70338032Speter pid = fork(); 70438032Speter if (pid > 0) 70538032Speter exit(EX_OK); 70664562Sgshapiro save_errno = errno; 70790792Sgshapiro#endif /* USE_DOUBLE_FORK */ 70838032Speter 70990792Sgshapiro CurrentPid = getpid(); 71090792Sgshapiro 71138032Speter /* be sure we are immune from the terminal */ 71238032Speter disconnect(2, e); 71364562Sgshapiro clearstats(); 71438032Speter 71538032Speter /* prevent parent from waiting if there was an error */ 71638032Speter if (pid < 0) 71738032Speter { 71864562Sgshapiro errno = save_errno; 71964562Sgshapiro syserr("deliver: fork 2"); 72064562Sgshapiro#if HASFLOCK 72138032Speter e->e_flags |= EF_INQUEUE; 72264562Sgshapiro#else /* HASFLOCK */ 72338032Speter e->e_id = NULL; 72464562Sgshapiro#endif /* HASFLOCK */ 72590792Sgshapiro finis(true, true, ExitStat); 72638032Speter } 72738032Speter 72838032Speter /* be sure to give error messages in child */ 72990792Sgshapiro QuickAbort = false; 73038032Speter 73138032Speter /* 73238032Speter ** Close any cached connections. 73338032Speter ** 73438032Speter ** We don't send the QUIT protocol because the parent 73538032Speter ** still knows about the connection. 73638032Speter ** 73738032Speter ** This should only happen when delivering an error 73838032Speter ** message. 73938032Speter */ 74038032Speter 74190792Sgshapiro mci_flush(false, NULL); 74238032Speter 74364562Sgshapiro#if HASFLOCK 74438032Speter break; 74564562Sgshapiro#else /* HASFLOCK */ 74638032Speter 74738032Speter /* 74838032Speter ** Now reacquire and run the various queue files. 74938032Speter */ 75038032Speter 75138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 75238032Speter { 75338032Speter ENVELOPE *sibling = ee->e_sibling; 75438032Speter 75590792Sgshapiro (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 75690792Sgshapiro false, false, ee); 75738032Speter ee->e_sibling = sibling; 75838032Speter } 75990792Sgshapiro (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 76090792Sgshapiro false, false, e); 76190792Sgshapiro finis(true, true, ExitStat); 76264562Sgshapiro#endif /* HASFLOCK */ 76338032Speter } 76438032Speter 76538032Speter sendenvelope(e, mode); 76690792Sgshapiro dropenvelope(e, true, true); 76738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 76838032Speter { 76938032Speter CurEnv = ee; 77038032Speter if (mode != SM_VERIFY) 77138032Speter openxscript(ee); 77238032Speter sendenvelope(ee, mode); 77390792Sgshapiro dropenvelope(ee, true, true); 77438032Speter } 77538032Speter CurEnv = e; 77638032Speter 77738032Speter Verbose = oldverbose; 77838032Speter if (mode == SM_FORK) 77990792Sgshapiro finis(true, true, ExitStat); 78038032Speter} 78138032Speter 78264562Sgshapirostatic void 78338032Spetersendenvelope(e, mode) 78438032Speter register ENVELOPE *e; 78538032Speter int mode; 78638032Speter{ 78738032Speter register ADDRESS *q; 78838032Speter bool didany; 78938032Speter 79038032Speter if (tTd(13, 10)) 79190792Sgshapiro sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 79290792Sgshapiro e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 79390792Sgshapiro e->e_flags); 79438032Speter if (LogLevel > 80) 79538032Speter sm_syslog(LOG_DEBUG, e->e_id, 79664562Sgshapiro "sendenvelope, flags=0x%lx", 79764562Sgshapiro e->e_flags); 79838032Speter 79938032Speter /* 80038032Speter ** If we have had global, fatal errors, don't bother sending 80138032Speter ** the message at all if we are in SMTP mode. Local errors 80238032Speter ** (e.g., a single address failing) will still cause the other 80338032Speter ** addresses to be sent. 80438032Speter */ 80538032Speter 80638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 80738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 80838032Speter { 80938032Speter e->e_flags |= EF_CLRQUEUE; 81038032Speter return; 81138032Speter } 81238032Speter 81390792Sgshapiro /* 81490792Sgshapiro ** Don't attempt deliveries if we want to bounce now 81590792Sgshapiro ** or if deliver-by time is exceeded. 81690792Sgshapiro */ 81790792Sgshapiro 81864562Sgshapiro if (!bitset(EF_RESPONSE, e->e_flags) && 81990792Sgshapiro (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 82090792Sgshapiro (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 82190792Sgshapiro curtime() > e->e_ctime + e->e_deliver_by))) 82264562Sgshapiro return; 82364562Sgshapiro 82438032Speter /* 82538032Speter ** Run through the list and send everything. 82638032Speter ** 82738032Speter ** Set EF_GLOBALERRS so that error messages during delivery 82838032Speter ** result in returned mail. 82938032Speter */ 83038032Speter 83138032Speter e->e_nsent = 0; 83238032Speter e->e_flags |= EF_GLOBALERRS; 83364562Sgshapiro 83490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 83590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 83690792Sgshapiro didany = false; 83738032Speter 83890792Sgshapiro if (!bitset(EF_SPLIT, e->e_flags)) 83990792Sgshapiro { 84090792Sgshapiro ENVELOPE *oldsib; 84190792Sgshapiro ENVELOPE *ee; 84290792Sgshapiro 84390792Sgshapiro /* 84490792Sgshapiro ** Save old sibling and set it to NULL to avoid 84590792Sgshapiro ** queueing up the same envelopes again. 84690792Sgshapiro ** This requires that envelopes in that list have 84790792Sgshapiro ** been take care of before (or at some other place). 84890792Sgshapiro */ 84990792Sgshapiro 85090792Sgshapiro oldsib = e->e_sibling; 85190792Sgshapiro e->e_sibling = NULL; 85290792Sgshapiro if (!split_by_recipient(e) && 85390792Sgshapiro bitset(EF_FATALERRS, e->e_flags)) 85490792Sgshapiro { 85590792Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 85690792Sgshapiro e->e_flags |= EF_CLRQUEUE; 85790792Sgshapiro return; 85890792Sgshapiro } 85990792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86090792Sgshapiro queueup(ee, false, true); 86190792Sgshapiro 86290792Sgshapiro /* clean up */ 86390792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86490792Sgshapiro { 86590792Sgshapiro /* now unlock the job */ 86690792Sgshapiro closexscript(ee); 86790792Sgshapiro unlockqueue(ee); 86890792Sgshapiro 86990792Sgshapiro /* this envelope is marked unused */ 87090792Sgshapiro if (ee->e_dfp != NULL) 87190792Sgshapiro { 87290792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 87390792Sgshapiro ee->e_dfp = NULL; 87490792Sgshapiro } 87590792Sgshapiro ee->e_id = NULL; 87690792Sgshapiro ee->e_flags &= ~EF_HAS_DF; 87790792Sgshapiro } 87890792Sgshapiro e->e_sibling = oldsib; 87990792Sgshapiro } 88090792Sgshapiro 88138032Speter /* now run through the queue */ 88238032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 88338032Speter { 88438032Speter#if XDEBUG 88538032Speter char wbuf[MAXNAME + 20]; 88638032Speter 88790792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", 88890792Sgshapiro MAXNAME, q->q_paddr); 88938032Speter checkfd012(wbuf); 89064562Sgshapiro#endif /* XDEBUG */ 89138032Speter if (mode == SM_VERIFY) 89238032Speter { 89338032Speter e->e_to = q->q_paddr; 89464562Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 89538032Speter { 89638032Speter if (q->q_host != NULL && q->q_host[0] != '\0') 89738032Speter message("deliverable: mailer %s, host %s, user %s", 89838032Speter q->q_mailer->m_name, 89938032Speter q->q_host, 90038032Speter q->q_user); 90138032Speter else 90238032Speter message("deliverable: mailer %s, user %s", 90338032Speter q->q_mailer->m_name, 90438032Speter q->q_user); 90538032Speter } 90638032Speter } 90764562Sgshapiro else if (QS_IS_OK(q->q_state)) 90838032Speter { 90938032Speter /* 91038032Speter ** Checkpoint the send list every few addresses 91138032Speter */ 91238032Speter 91366494Sgshapiro if (CheckpointInterval > 0 && 91466494Sgshapiro e->e_nsent >= CheckpointInterval) 91538032Speter { 91690792Sgshapiro queueup(e, false, false); 91738032Speter e->e_nsent = 0; 91838032Speter } 91938032Speter (void) deliver(e, q); 92090792Sgshapiro didany = true; 92138032Speter } 92238032Speter } 92338032Speter if (didany) 92438032Speter { 92538032Speter e->e_dtime = curtime(); 92638032Speter e->e_ntries++; 92738032Speter } 92838032Speter 92938032Speter#if XDEBUG 93038032Speter checkfd012("end of sendenvelope"); 93164562Sgshapiro#endif /* XDEBUG */ 93238032Speter} 93390792Sgshapiro 93490792Sgshapiro#if REQUIRES_DIR_FSYNC 93590792Sgshapiro/* 93690792Sgshapiro** SYNC_DIR -- fsync a directory based on a filename 93790792Sgshapiro** 93890792Sgshapiro** Parameters: 93990792Sgshapiro** filename -- path of file 94090792Sgshapiro** panic -- panic? 94190792Sgshapiro** 94290792Sgshapiro** Returns: 94390792Sgshapiro** none 94490792Sgshapiro*/ 94590792Sgshapiro 94690792Sgshapirovoid 94790792Sgshapirosync_dir(filename, panic) 94890792Sgshapiro char *filename; 94990792Sgshapiro bool panic; 95090792Sgshapiro{ 95190792Sgshapiro int dirfd; 95290792Sgshapiro char *dirp; 95390792Sgshapiro char dir[MAXPATHLEN]; 95490792Sgshapiro 955110560Sgshapiro if (!RequiresDirfsync) 956110560Sgshapiro return; 957110560Sgshapiro 95890792Sgshapiro /* filesystems which require the directory be synced */ 95990792Sgshapiro dirp = strrchr(filename, '/'); 96090792Sgshapiro if (dirp != NULL) 96190792Sgshapiro { 96290792Sgshapiro if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir) 96390792Sgshapiro return; 96490792Sgshapiro dir[dirp - filename] = '\0'; 96590792Sgshapiro dirp = dir; 96690792Sgshapiro } 96790792Sgshapiro else 96890792Sgshapiro dirp = "."; 96990792Sgshapiro dirfd = open(dirp, O_RDONLY, 0700); 97090792Sgshapiro if (tTd(40,32)) 97190792Sgshapiro sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 97290792Sgshapiro dirp, dirfd); 97390792Sgshapiro if (dirfd >= 0) 97490792Sgshapiro { 97590792Sgshapiro if (fsync(dirfd) < 0) 97690792Sgshapiro { 97790792Sgshapiro if (panic) 97890792Sgshapiro syserr("!sync_dir: cannot fsync directory %s", 97990792Sgshapiro dirp); 98090792Sgshapiro else if (LogLevel > 1) 98190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 98290792Sgshapiro "sync_dir: cannot fsync directory %s: %s", 98390792Sgshapiro dirp, sm_errstring(errno)); 98490792Sgshapiro } 98590792Sgshapiro (void) close(dirfd); 98690792Sgshapiro } 98790792Sgshapiro} 98890792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */ 98990792Sgshapiro/* 99038032Speter** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 99138032Speter** 99238032Speter** Parameters: 99338032Speter** e -- the existing envelope 99438032Speter** ee -- the new envelope 99590792Sgshapiro** type -- the queue file type (e.g., DATAFL_LETTER) 99638032Speter** 99738032Speter** Returns: 99838032Speter** none 99938032Speter*/ 100038032Speter 100164562Sgshapirostatic void 100238032Speterdup_queue_file(e, ee, type) 100390792Sgshapiro ENVELOPE *e, *ee; 100438032Speter int type; 100538032Speter{ 100664562Sgshapiro char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 100738032Speter 100838032Speter ee->e_dfp = NULL; 100938032Speter ee->e_xfp = NULL; 101064562Sgshapiro 101164562Sgshapiro /* 101264562Sgshapiro ** Make sure both are in the same directory. 101364562Sgshapiro */ 101464562Sgshapiro 101590792Sgshapiro (void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf); 101690792Sgshapiro (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf); 1017102528Sgshapiro 1018102528Sgshapiro /* Force the df to disk if it's not there yet */ 1019102528Sgshapiro if (type == DATAFL_LETTER && e->e_dfp != NULL && 1020102528Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 1021102528Sgshapiro errno != EINVAL) 1022102528Sgshapiro { 1023102528Sgshapiro syserr("!dup_queue_file: can't commit %s", f1buf); 1024102528Sgshapiro /* NOTREACHED */ 1025102528Sgshapiro } 1026102528Sgshapiro 102738032Speter if (link(f1buf, f2buf) < 0) 102838032Speter { 102964562Sgshapiro int save_errno = errno; 103038032Speter 103138032Speter syserr("sendall: link(%s, %s)", f1buf, f2buf); 103264562Sgshapiro if (save_errno == EEXIST) 103338032Speter { 103438032Speter if (unlink(f2buf) < 0) 103538032Speter { 103638032Speter syserr("!sendall: unlink(%s): permanent", 103790792Sgshapiro f2buf); 103864562Sgshapiro /* NOTREACHED */ 103938032Speter } 104038032Speter if (link(f1buf, f2buf) < 0) 104138032Speter { 104238032Speter syserr("!sendall: link(%s, %s): permanent", 104390792Sgshapiro f1buf, f2buf); 104464562Sgshapiro /* NOTREACHED */ 104538032Speter } 104638032Speter } 104738032Speter } 104890792Sgshapiro SYNC_DIR(f2buf, true); 104938032Speter} 105090792Sgshapiro/* 105138032Speter** DOFORK -- do a fork, retrying a couple of times on failure. 105238032Speter** 105338032Speter** This MUST be a macro, since after a vfork we are running 105438032Speter** two processes on the same stack!!! 105538032Speter** 105638032Speter** Parameters: 105738032Speter** none. 105838032Speter** 105938032Speter** Returns: 106038032Speter** From a macro??? You've got to be kidding! 106138032Speter** 106238032Speter** Side Effects: 106338032Speter** Modifies the ==> LOCAL <== variable 'pid', leaving: 106438032Speter** pid of child in parent, zero in child. 106538032Speter** -1 on unrecoverable error. 106638032Speter** 106738032Speter** Notes: 106838032Speter** I'm awfully sorry this looks so awful. That's 106938032Speter** vfork for you..... 107038032Speter*/ 107138032Speter 107264562Sgshapiro#define NFORKTRIES 5 107338032Speter 107464562Sgshapiro#ifndef FORK 107538032Speter# define FORK fork 107664562Sgshapiro#endif /* ! FORK */ 107738032Speter 107864562Sgshapiro#define DOFORK(fORKfN) \ 107938032Speter{\ 108038032Speter register int i;\ 108138032Speter\ 108238032Speter for (i = NFORKTRIES; --i >= 0; )\ 108338032Speter {\ 108438032Speter pid = fORKfN();\ 108538032Speter if (pid >= 0)\ 108638032Speter break;\ 108738032Speter if (i > 0)\ 108864562Sgshapiro (void) sleep((unsigned) NFORKTRIES - i);\ 108938032Speter }\ 109038032Speter} 109190792Sgshapiro/* 109238032Speter** DOFORK -- simple fork interface to DOFORK. 109338032Speter** 109438032Speter** Parameters: 109538032Speter** none. 109638032Speter** 109738032Speter** Returns: 109838032Speter** pid of child in parent. 109938032Speter** zero in child. 110038032Speter** -1 on error. 110138032Speter** 110238032Speter** Side Effects: 110338032Speter** returns twice, once in parent and once in child. 110438032Speter*/ 110538032Speter 110677349Sgshapiropid_t 110738032Speterdofork() 110838032Speter{ 110938032Speter register pid_t pid = -1; 111038032Speter 111138032Speter DOFORK(fork); 111264562Sgshapiro return pid; 111338032Speter} 111490792Sgshapiro 111590792Sgshapiro/* 111690792Sgshapiro** COLONCMP -- compare host-signatures up to first ':' or EOS 111790792Sgshapiro** 111890792Sgshapiro** This takes two strings which happen to be host-signatures and 111990792Sgshapiro** compares them. If the lowest preference portions of the MX-RR's 112090792Sgshapiro** match (up to ':' or EOS, whichever is first), then we have 112190792Sgshapiro** match. This is used for coattail-piggybacking messages during 112290792Sgshapiro** message delivery. 112390792Sgshapiro** If the signatures are the same up to the first ':' the remainder of 112490792Sgshapiro** the signatures are then compared with a normal strcmp(). This saves 112590792Sgshapiro** re-examining the first part of the signatures. 112690792Sgshapiro** 112790792Sgshapiro** Parameters: 112890792Sgshapiro** a - first host-signature 112990792Sgshapiro** b - second host-signature 113090792Sgshapiro** 113190792Sgshapiro** Returns: 113290792Sgshapiro** HS_MATCH_NO -- no "match". 113390792Sgshapiro** HS_MATCH_FIRST -- "match" for the first MX preference 113490792Sgshapiro** (up to the first colon (':')). 113590792Sgshapiro** HS_MATCH_FULL -- match for the entire MX record. 113690792Sgshapiro** 113790792Sgshapiro** Side Effects: 113890792Sgshapiro** none. 113990792Sgshapiro*/ 114090792Sgshapiro 114190792Sgshapiro#define HS_MATCH_NO 0 114290792Sgshapiro#define HS_MATCH_FIRST 1 114390792Sgshapiro#define HS_MATCH_FULL 2 114490792Sgshapiro 114590792Sgshapirostatic int 114690792Sgshapirocoloncmp(a, b) 114790792Sgshapiro register const char *a; 114890792Sgshapiro register const char *b; 114990792Sgshapiro{ 115090792Sgshapiro int ret = HS_MATCH_NO; 115190792Sgshapiro int braclev = 0; 115290792Sgshapiro 115390792Sgshapiro while (*a == *b++) 115490792Sgshapiro { 115590792Sgshapiro /* Need to account for IPv6 bracketed addresses */ 115690792Sgshapiro if (*a == '[') 115790792Sgshapiro braclev++; 1158112810Sgshapiro else if (*a == ']' && braclev > 0) 115990792Sgshapiro braclev--; 116090792Sgshapiro else if (*a == ':' && braclev <= 0) 116190792Sgshapiro { 116290792Sgshapiro ret = HS_MATCH_FIRST; 116390792Sgshapiro a++; 116490792Sgshapiro break; 116590792Sgshapiro } 116690792Sgshapiro else if (*a == '\0') 116790792Sgshapiro return HS_MATCH_FULL; /* a full match */ 116890792Sgshapiro a++; 116990792Sgshapiro } 117090792Sgshapiro if (ret == HS_MATCH_NO && 117190792Sgshapiro braclev <= 0 && 117290792Sgshapiro ((*a == '\0' && *(b - 1) == ':') || 117390792Sgshapiro (*a == ':' && *(b - 1) == '\0'))) 117490792Sgshapiro return HS_MATCH_FIRST; 117590792Sgshapiro if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 117690792Sgshapiro return HS_MATCH_FULL; 117790792Sgshapiro 117890792Sgshapiro return ret; 117990792Sgshapiro} 1180132943Sgshapiro 118190792Sgshapiro/* 1182132943Sgshapiro** SHOULD_TRY_FBSH -- Should try FallbackSmartHost? 1183132943Sgshapiro** 1184132943Sgshapiro** Parameters: 1185132943Sgshapiro** e -- envelope 1186132943Sgshapiro** tried_fallbacksmarthost -- has been tried already? (in/out) 1187132943Sgshapiro** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out) 1188132943Sgshapiro** hbsz -- size of hostbuf 1189132943Sgshapiro** status -- current delivery status 1190132943Sgshapiro** 1191132943Sgshapiro** Returns: 1192132943Sgshapiro** true iff FallbackSmartHost should be tried. 1193132943Sgshapiro*/ 1194132943Sgshapiro 1195132943Sgshapirostatic bool 1196132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status) 1197132943Sgshapiro ENVELOPE *e; 1198132943Sgshapiro bool *tried_fallbacksmarthost; 1199132943Sgshapiro char *hostbuf; 1200132943Sgshapiro size_t hbsz; 1201132943Sgshapiro int status; 1202132943Sgshapiro{ 1203132943Sgshapiro /* 1204132943Sgshapiro ** If the host was not found and a FallbackSmartHost is defined 1205132943Sgshapiro ** (and we have not yet tried it), then make one last try with 1206132943Sgshapiro ** it as the host. 1207132943Sgshapiro */ 1208132943Sgshapiro 1209132943Sgshapiro if (status == EX_NOHOST && FallbackSmartHost != NULL && 1210132943Sgshapiro !*tried_fallbacksmarthost) 1211132943Sgshapiro { 1212132943Sgshapiro *tried_fallbacksmarthost = true; 1213132943Sgshapiro expand(FallbackSmartHost, hostbuf, hbsz, e); 1214132943Sgshapiro if (!wordinclass(hostbuf, 'w')) 1215132943Sgshapiro { 1216132943Sgshapiro if (tTd(11, 1)) 1217132943Sgshapiro sm_dprintf("one last try with FallbackSmartHost %s\n", 1218132943Sgshapiro hostbuf); 1219132943Sgshapiro return true; 1220132943Sgshapiro } 1221132943Sgshapiro } 1222132943Sgshapiro return false; 1223132943Sgshapiro} 1224132943Sgshapiro/* 122538032Speter** DELIVER -- Deliver a message to a list of addresses. 122638032Speter** 122738032Speter** This routine delivers to everyone on the same host as the 122838032Speter** user on the head of the list. It is clever about mailers 122938032Speter** that don't handle multiple users. It is NOT guaranteed 123038032Speter** that it will deliver to all these addresses however -- so 123138032Speter** deliver should be called once for each address on the 123238032Speter** list. 123390792Sgshapiro** Deliver tries to be as opportunistic as possible about piggybacking 123490792Sgshapiro** messages. Some definitions to make understanding easier follow below. 123590792Sgshapiro** Piggybacking occurs when an existing connection to a mail host can 123690792Sgshapiro** be used to send the same message to more than one recipient at the 123790792Sgshapiro** same time. So "no piggybacking" means one message for one recipient 123890792Sgshapiro** per connection. "Intentional piggybacking" happens when the 123990792Sgshapiro** recipients' host address (not the mail host address) is used to 124090792Sgshapiro** attempt piggybacking. Recipients with the same host address 124190792Sgshapiro** have the same mail host. "Coincidental piggybacking" relies on 124290792Sgshapiro** piggybacking based on all the mail host addresses in the MX-RR. This 124390792Sgshapiro** is "coincidental" in the fact it could not be predicted until the 124490792Sgshapiro** MX Resource Records for the hosts were obtained and examined. For 124590792Sgshapiro** example (preference order and equivalence is important, not values): 124690792Sgshapiro** domain1 IN MX 10 mxhost-A 124790792Sgshapiro** IN MX 20 mxhost-B 124890792Sgshapiro** domain2 IN MX 4 mxhost-A 124990792Sgshapiro** IN MX 8 mxhost-B 125090792Sgshapiro** Domain1 and domain2 can piggyback the same message to mxhost-A or 125190792Sgshapiro** mxhost-B (if mxhost-A cannot be reached). 125290792Sgshapiro** "Coattail piggybacking" relaxes the strictness of "coincidental 125390792Sgshapiro** piggybacking" in the hope that most significant (lowest value) 125490792Sgshapiro** MX preference host(s) can create more piggybacking. For example 125590792Sgshapiro** (again, preference order and equivalence is important, not values): 125690792Sgshapiro** domain3 IN MX 100 mxhost-C 125790792Sgshapiro** IN MX 100 mxhost-D 125890792Sgshapiro** IN MX 200 mxhost-E 125990792Sgshapiro** domain4 IN MX 50 mxhost-C 126090792Sgshapiro** IN MX 50 mxhost-D 126190792Sgshapiro** IN MX 80 mxhost-F 126290792Sgshapiro** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 126390792Sgshapiro** is available. Same with mxhost-D because in both RR's the preference 126490792Sgshapiro** value is the same as mxhost-C, respectively. 126590792Sgshapiro** So deliver attempts coattail piggybacking when possible. If the 126690792Sgshapiro** first MX preference level hosts cannot be used then the piggybacking 126790792Sgshapiro** reverts to coincidental piggybacking. Using the above example you 126890792Sgshapiro** cannot deliver to mxhost-F for domain3 regardless of preference value. 126990792Sgshapiro** ("Coattail" from "riding on the coattails of your predecessor" meaning 127090792Sgshapiro** gaining benefit from a predecessor effort with no or little addition 127190792Sgshapiro** effort. The predecessor here being the preceding MX RR). 127238032Speter** 127338032Speter** Parameters: 127438032Speter** e -- the envelope to deliver. 127538032Speter** firstto -- head of the address list to deliver to. 127638032Speter** 127738032Speter** Returns: 127838032Speter** zero -- successfully delivered. 127938032Speter** else -- some failure, see ExitStat for more info. 128038032Speter** 128138032Speter** Side Effects: 128238032Speter** The standard input is passed off to someone. 128338032Speter*/ 128438032Speter 128564562Sgshapirostatic int 128638032Speterdeliver(e, firstto) 128738032Speter register ENVELOPE *e; 128838032Speter ADDRESS *firstto; 128938032Speter{ 129038032Speter char *host; /* host being sent to */ 129138032Speter char *user; /* user being sent to */ 129238032Speter char **pvp; 129338032Speter register char **mvp; 129438032Speter register char *p; 129538032Speter register MAILER *m; /* mailer for this recipient */ 129638032Speter ADDRESS *volatile ctladdr; 129790792Sgshapiro#if HASSETUSERCONTEXT 129838032Speter ADDRESS *volatile contextaddr = NULL; 129990792Sgshapiro#endif /* HASSETUSERCONTEXT */ 130038032Speter register MCI *volatile mci; 130190792Sgshapiro register ADDRESS *SM_NONVOLATILE to = firstto; 130290792Sgshapiro volatile bool clever = false; /* running user smtp to this mailer */ 130338032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 130438032Speter int rcode; /* response code */ 130590792Sgshapiro SM_NONVOLATILE int lmtp_rcode = EX_OK; 130690792Sgshapiro SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 130790792Sgshapiro SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 130838032Speter char *firstsig; /* signature of firstto */ 130990792Sgshapiro volatile pid_t pid = -1; 131038032Speter char *volatile curhost; 131190792Sgshapiro SM_NONVOLATILE unsigned short port = 0; 131290792Sgshapiro SM_NONVOLATILE time_t enough = 0; 131364562Sgshapiro#if NETUNIX 131490792Sgshapiro char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 131564562Sgshapiro#endif /* NETUNIX */ 131638032Speter time_t xstart; 131738032Speter bool suidwarn; 131838032Speter bool anyok; /* at least one address was OK */ 131990792Sgshapiro SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 132064562Sgshapiro bool ovr; 132190792Sgshapiro bool quarantine; 132264562Sgshapiro int strsize; 132364562Sgshapiro int rcptcount; 132490792Sgshapiro int ret; 132564562Sgshapiro static int tobufsize = 0; 132664562Sgshapiro static char *tobuf = NULL; 132790792Sgshapiro char *rpath; /* translated return path */ 132838032Speter int mpvect[2]; 132938032Speter int rpvect[2]; 133064562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 133164562Sgshapiro char *pv[MAXPV + 1]; 133238032Speter char buf[MAXNAME + 1]; 133398121Sgshapiro char cbuf[MAXPATHLEN]; 133438032Speter 133538032Speter errno = 0; 133664562Sgshapiro if (!QS_IS_OK(to->q_state)) 133764562Sgshapiro return 0; 133838032Speter 133938032Speter suidwarn = geteuid() == 0; 134038032Speter 134138032Speter m = to->q_mailer; 134238032Speter host = to->q_host; 134338032Speter CurEnv = e; /* just in case */ 134438032Speter e->e_statmsg = NULL; 134538032Speter SmtpError[0] = '\0'; 134638032Speter xstart = curtime(); 134738032Speter 134838032Speter if (tTd(10, 1)) 134990792Sgshapiro sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 135038032Speter e->e_id, m->m_name, host, to->q_user); 135138032Speter if (tTd(10, 100)) 135290792Sgshapiro printopenfds(false); 135338032Speter 135438032Speter /* 135590792Sgshapiro ** Clear {client_*} macros if this is a bounce message to 135638032Speter ** prevent rejection by check_compat ruleset. 135738032Speter */ 135864562Sgshapiro 135938032Speter if (bitset(EF_RESPONSE, e->e_flags)) 136038032Speter { 136190792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 1362132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), ""); 136390792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 136490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 136590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 136638032Speter } 136764562Sgshapiro 136890792Sgshapiro SM_TRY 136990792Sgshapiro { 137090792Sgshapiro ADDRESS *skip_back = NULL; 137190792Sgshapiro 137238032Speter /* 137338032Speter ** Do initial argv setup. 137438032Speter ** Insert the mailer name. Notice that $x expansion is 137538032Speter ** NOT done on the mailer name. Then, if the mailer has 137638032Speter ** a picky -f flag, we insert it as appropriate. This 137738032Speter ** code does not check for 'pv' overflow; this places a 137838032Speter ** manifest lower limit of 4 for MAXPV. 137938032Speter ** The from address rewrite is expected to make 138038032Speter ** the address relative to the other end. 138138032Speter */ 138238032Speter 138338032Speter /* rewrite from address, using rewriting rules */ 138438032Speter rcode = EX_OK; 138538032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 138638032Speter p = e->e_sender; 138738032Speter else 138838032Speter p = e->e_from.q_paddr; 138990792Sgshapiro rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 139090792Sgshapiro if (strlen(rpath) > MAXSHORTSTR) 139138032Speter { 139290792Sgshapiro rpath = shortenstring(rpath, MAXSHORTSTR); 139390792Sgshapiro 139490792Sgshapiro /* avoid bogus errno */ 139590792Sgshapiro errno = 0; 139690792Sgshapiro syserr("remotename: huge return path %s", rpath); 139738032Speter } 139890792Sgshapiro rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 139990792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', rpath); 140090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', host); 140138032Speter Errors = 0; 140238032Speter pvp = pv; 140338032Speter *pvp++ = m->m_argv[0]; 140438032Speter 1405132943Sgshapiro /* ignore long term host status information if mailer flag W is set */ 1406132943Sgshapiro if (bitnset(M_NOHOSTSTAT, m->m_flags)) 1407132943Sgshapiro IgnoreHostStatus = true; 1408132943Sgshapiro 140938032Speter /* insert -f or -r flag as appropriate */ 141064562Sgshapiro if (FromFlag && 141164562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 141264562Sgshapiro bitnset(M_ROPT, m->m_flags))) 141338032Speter { 141438032Speter if (bitnset(M_FOPT, m->m_flags)) 141538032Speter *pvp++ = "-f"; 141638032Speter else 141738032Speter *pvp++ = "-r"; 141890792Sgshapiro *pvp++ = rpath; 141938032Speter } 142038032Speter 142138032Speter /* 142238032Speter ** Append the other fixed parts of the argv. These run 142338032Speter ** up to the first entry containing "$u". There can only 142438032Speter ** be one of these, and there are only a few more slots 142538032Speter ** in the pv after it. 142638032Speter */ 142738032Speter 142838032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 142938032Speter { 143038032Speter /* can't use strchr here because of sign extension problems */ 143138032Speter while (*p != '\0') 143238032Speter { 143338032Speter if ((*p++ & 0377) == MACROEXPAND) 143438032Speter { 143538032Speter if (*p == 'u') 143638032Speter break; 143738032Speter } 143838032Speter } 143938032Speter 144038032Speter if (*p != '\0') 144138032Speter break; 144238032Speter 144338032Speter /* this entry is safe -- go ahead and process it */ 144438032Speter expand(*mvp, buf, sizeof buf, e); 144590792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 144638032Speter if (pvp >= &pv[MAXPV - 3]) 144738032Speter { 144864562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 144964562Sgshapiro pv[0]); 145090792Sgshapiro rcode = -1; 145190792Sgshapiro goto cleanup; 145238032Speter } 145338032Speter } 145438032Speter 145538032Speter /* 145638032Speter ** If we have no substitution for the user name in the argument 145738032Speter ** list, we know that we must supply the names otherwise -- and 145838032Speter ** SMTP is the answer!! 145938032Speter */ 146038032Speter 146138032Speter if (*mvp == NULL) 146238032Speter { 146373188Sgshapiro /* running LMTP or SMTP */ 146490792Sgshapiro clever = true; 146538032Speter *pvp = NULL; 146638032Speter } 146773188Sgshapiro else if (bitnset(M_LMTP, m->m_flags)) 146873188Sgshapiro { 146973188Sgshapiro /* not running LMTP */ 147073188Sgshapiro sm_syslog(LOG_ERR, NULL, 147173188Sgshapiro "Warning: mailer %s: LMTP flag (F=z) turned off", 147273188Sgshapiro m->m_name); 147373188Sgshapiro clrbitn(M_LMTP, m->m_flags); 147473188Sgshapiro } 147538032Speter 147638032Speter /* 147738032Speter ** At this point *mvp points to the argument with $u. We 147838032Speter ** run through our address list and append all the addresses 147938032Speter ** we can. If we run out of space, do not fret! We can 148038032Speter ** always send another copy later. 148138032Speter */ 148238032Speter 148364562Sgshapiro e->e_to = NULL; 148464562Sgshapiro strsize = 2; 148564562Sgshapiro rcptcount = 0; 148690792Sgshapiro ctladdr = NULL; 148790792Sgshapiro if (firstto->q_signature == NULL) 148890792Sgshapiro firstto->q_signature = hostsignature(firstto->q_mailer, 148990792Sgshapiro firstto->q_host); 149090792Sgshapiro firstsig = firstto->q_signature; 149164562Sgshapiro 149238032Speter for (; to != NULL; to = to->q_next) 149338032Speter { 149438032Speter /* avoid sending multiple recipients to dumb mailers */ 149564562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 149664562Sgshapiro break; 149738032Speter 149838032Speter /* if already sent or not for this host, don't send */ 149990792Sgshapiro if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 150038032Speter continue; 150138032Speter 150290792Sgshapiro /* 150390792Sgshapiro ** Must be same mailer to keep grouping rcpts. 150490792Sgshapiro ** If mailers don't match: continue; sendqueue is not 150590792Sgshapiro ** sorted by mailers, so don't break; 150690792Sgshapiro */ 150790792Sgshapiro 150890792Sgshapiro if (to->q_mailer != firstto->q_mailer) 150990792Sgshapiro continue; 151090792Sgshapiro 151190792Sgshapiro if (to->q_signature == NULL) /* for safety */ 151290792Sgshapiro to->q_signature = hostsignature(to->q_mailer, 151390792Sgshapiro to->q_host); 151490792Sgshapiro 151590792Sgshapiro /* 151690792Sgshapiro ** This is for coincidental and tailcoat piggybacking messages 151790792Sgshapiro ** to the same mail host. While the signatures are identical 151890792Sgshapiro ** (that's the MX-RR's are identical) we can do coincidental 151990792Sgshapiro ** piggybacking. We try hard for coattail piggybacking 152090792Sgshapiro ** with the same mail host when the next recipient has the 152190792Sgshapiro ** same host at lowest preference. It may be that this 152290792Sgshapiro ** won't work out, so 'skip_back' is maintained if a backup 152390792Sgshapiro ** to coincidental piggybacking or full signature must happen. 152490792Sgshapiro */ 152590792Sgshapiro 152690792Sgshapiro ret = firstto == to ? HS_MATCH_FULL : 152790792Sgshapiro coloncmp(to->q_signature, firstsig); 152890792Sgshapiro if (ret == HS_MATCH_FULL) 152990792Sgshapiro skip_back = to; 153090792Sgshapiro else if (ret == HS_MATCH_NO) 153164562Sgshapiro break; 153264562Sgshapiro 153390792Sgshapiro if (!clever) 153490792Sgshapiro { 153590792Sgshapiro /* avoid overflowing tobuf */ 153690792Sgshapiro strsize += strlen(to->q_paddr) + 1; 153790792Sgshapiro if (strsize > TOBUFSIZE) 153890792Sgshapiro break; 153990792Sgshapiro } 154090792Sgshapiro 154164562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 154264562Sgshapiro break; 154338032Speter 154438032Speter if (tTd(10, 1)) 154538032Speter { 154690792Sgshapiro sm_dprintf("\nsend to "); 1547132943Sgshapiro printaddr(sm_debug_file(), to, false); 154838032Speter } 154938032Speter 155038032Speter /* compute effective uid/gid when sending */ 155138032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 155290792Sgshapiro# if HASSETUSERCONTEXT 155338032Speter contextaddr = ctladdr = getctladdr(to); 155490792Sgshapiro# else /* HASSETUSERCONTEXT */ 155590792Sgshapiro ctladdr = getctladdr(to); 155690792Sgshapiro# endif /* HASSETUSERCONTEXT */ 155738032Speter 155838032Speter if (tTd(10, 2)) 155938032Speter { 156090792Sgshapiro sm_dprintf("ctladdr="); 1561132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 156238032Speter } 156338032Speter 156438032Speter user = to->q_user; 156538032Speter e->e_to = to->q_paddr; 156638032Speter 156738032Speter /* 156838032Speter ** Check to see that these people are allowed to 156938032Speter ** talk to each other. 157066494Sgshapiro ** Check also for overflow of e_msgsize. 157138032Speter */ 157238032Speter 157366494Sgshapiro if (m->m_maxsize != 0 && 157466494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 157538032Speter { 157638032Speter e->e_flags |= EF_NO_BODY_RETN; 157738032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 157838032Speter to->q_status = "5.2.3"; 157938032Speter else 158038032Speter to->q_status = "5.3.4"; 158190792Sgshapiro 158264562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 158364562Sgshapiro usrerrenh(to->q_status, 158464562Sgshapiro "552 Message is too large; %ld bytes max", 158564562Sgshapiro m->m_maxsize); 158690792Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, false); 158764562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 158890792Sgshapiro NULL, ctladdr, xstart, e, to); 158938032Speter continue; 159038032Speter } 159173188Sgshapiro SM_SET_H_ERRNO(0); 159290792Sgshapiro ovr = true; 159338032Speter 159438032Speter /* do config file checking of compatibility */ 159590792Sgshapiro quarantine = (e->e_quarmsg != NULL); 159664562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1597102528Sgshapiro e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1598102528Sgshapiro e->e_id); 159938032Speter if (rcode == EX_OK) 160038032Speter { 160142575Speter /* do in-code checking if not discarding */ 160242575Speter if (!bitset(EF_DISCARD, e->e_flags)) 160364562Sgshapiro { 160442575Speter rcode = checkcompat(to, e); 160590792Sgshapiro ovr = false; 160664562Sgshapiro } 160738032Speter } 160838032Speter if (rcode != EX_OK) 160938032Speter { 161064562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 161164562Sgshapiro giveresponse(rcode, to->q_status, m, 161290792Sgshapiro NULL, ctladdr, xstart, e, to); 161338032Speter continue; 161438032Speter } 161590792Sgshapiro if (!quarantine && e->e_quarmsg != NULL) 161690792Sgshapiro { 161790792Sgshapiro /* 161890792Sgshapiro ** check_compat or checkcompat() has tried 161990792Sgshapiro ** to quarantine but that isn't supported. 162090792Sgshapiro ** Revert the attempt. 162190792Sgshapiro */ 162290792Sgshapiro 162390792Sgshapiro e->e_quarmsg = NULL; 162490792Sgshapiro macdefine(&e->e_macro, A_PERM, 162590792Sgshapiro macid("{quarantine}"), ""); 162690792Sgshapiro } 162742575Speter if (bitset(EF_DISCARD, e->e_flags)) 162842575Speter { 162942575Speter if (tTd(10, 5)) 163042575Speter { 163190792Sgshapiro sm_dprintf("deliver: discarding recipient "); 1632132943Sgshapiro printaddr(sm_debug_file(), to, false); 163342575Speter } 163438032Speter 163564562Sgshapiro /* pretend the message was sent */ 163664562Sgshapiro /* XXX should we log something here? */ 163764562Sgshapiro to->q_state = QS_DISCARDED; 163864562Sgshapiro 163942575Speter /* 164042575Speter ** Remove discard bit to prevent discard of 164164562Sgshapiro ** future recipients. This is safe because the 164264562Sgshapiro ** true "global discard" has been handled before 164364562Sgshapiro ** we get here. 164442575Speter */ 164564562Sgshapiro 164642575Speter e->e_flags &= ~EF_DISCARD; 164742575Speter continue; 164842575Speter } 164942575Speter 165038032Speter /* 165138032Speter ** Strip quote bits from names if the mailer is dumb 165238032Speter ** about them. 165338032Speter */ 165438032Speter 165538032Speter if (bitnset(M_STRIPQ, m->m_flags)) 165638032Speter { 165738032Speter stripquotes(user); 165838032Speter stripquotes(host); 165938032Speter } 1660132943Sgshapiro 1661110560Sgshapiro /* 1662141858Sgshapiro ** Strip all leading backslashes if requested and the 1663110560Sgshapiro ** next character is alphanumerical (the latter can 1664110560Sgshapiro ** probably relaxed a bit, see RFC2821). 1665110560Sgshapiro */ 166638032Speter 1667110560Sgshapiro if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1668110560Sgshapiro stripbackslash(user); 1669110560Sgshapiro 167038032Speter /* hack attack -- delivermail compatibility */ 167138032Speter if (m == ProgMailer && *user == '|') 167238032Speter user++; 167338032Speter 167438032Speter /* 167538032Speter ** If an error message has already been given, don't 167638032Speter ** bother to send to this address. 167738032Speter ** 167838032Speter ** >>>>>>>>>> This clause assumes that the local mailer 167938032Speter ** >> NOTE >> cannot do any further aliasing; that 168038032Speter ** >>>>>>>>>> function is subsumed by sendmail. 168138032Speter */ 168238032Speter 168364562Sgshapiro if (!QS_IS_OK(to->q_state)) 168438032Speter continue; 168538032Speter 168638032Speter /* 168738032Speter ** See if this user name is "special". 168838032Speter ** If the user name has a slash in it, assume that this 168938032Speter ** is a file -- send it off without further ado. Note 169038032Speter ** that this type of addresses is not processed along 169138032Speter ** with the others, so we fudge on the To person. 169238032Speter */ 169338032Speter 169438032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 169538032Speter { 169690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); 169738032Speter p = to->q_home; 169838032Speter if (p == NULL && ctladdr != NULL) 169938032Speter p = ctladdr->q_home; 170090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); 170138032Speter expand(m->m_argv[1], buf, sizeof buf, e); 170238032Speter if (strlen(buf) > 0) 170338032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 170438032Speter else 170538032Speter { 170638032Speter syserr("empty filename specification for mailer %s", 170738032Speter m->m_name); 170838032Speter rcode = EX_CONFIG; 170938032Speter } 171064562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 171190792Sgshapiro ctladdr, xstart, e, to); 171290792Sgshapiro markfailure(e, to, NULL, rcode, true); 171338032Speter e->e_nsent++; 171438032Speter if (rcode == EX_OK) 171538032Speter { 171664562Sgshapiro to->q_state = QS_SENT; 171738032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 171838032Speter bitset(QPINGONSUCCESS, to->q_flags)) 171938032Speter { 172038032Speter to->q_flags |= QDELIVERED; 172138032Speter to->q_status = "2.1.5"; 172290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, 172390792Sgshapiro SM_TIME_DEFAULT, 172490792Sgshapiro "%s... Successfully delivered\n", 172590792Sgshapiro to->q_paddr); 172638032Speter } 172738032Speter } 172838032Speter to->q_statdate = curtime(); 172990792Sgshapiro markstats(e, to, STATS_NORMAL); 173038032Speter continue; 173138032Speter } 173238032Speter 173338032Speter /* 173438032Speter ** Address is verified -- add this user to mailer 173538032Speter ** argv, and add it to the print list of recipients. 173638032Speter */ 173738032Speter 173838032Speter /* link together the chain of recipients */ 173938032Speter to->q_tchain = tochain; 174038032Speter tochain = to; 174164562Sgshapiro e->e_to = "[CHAIN]"; 174264562Sgshapiro 174390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 174438032Speter p = to->q_home; 174538032Speter if (p == NULL && ctladdr != NULL) 174638032Speter p = ctladdr->q_home; 174790792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 174838032Speter 174964562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 175064562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 175164562Sgshapiro { 175264562Sgshapiro char notify[MAXLINE]; 175364562Sgshapiro 175464562Sgshapiro notify[0] = '\0'; 175564562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 175690792Sgshapiro (void) sm_strlcat(notify, "SUCCESS,", 175790792Sgshapiro sizeof notify); 175864562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 175990792Sgshapiro (void) sm_strlcat(notify, "FAILURE,", 176090792Sgshapiro sizeof notify); 176164562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 176290792Sgshapiro (void) sm_strlcat(notify, "DELAY,", 176390792Sgshapiro sizeof notify); 176464562Sgshapiro 176564562Sgshapiro /* Set to NEVER or drop trailing comma */ 176664562Sgshapiro if (notify[0] == '\0') 176790792Sgshapiro (void) sm_strlcat(notify, "NEVER", 176890792Sgshapiro sizeof notify); 176964562Sgshapiro else 177064562Sgshapiro notify[strlen(notify) - 1] = '\0'; 177164562Sgshapiro 177290792Sgshapiro macdefine(&e->e_macro, A_TEMP, 177390792Sgshapiro macid("{dsn_notify}"), notify); 177464562Sgshapiro } 177564562Sgshapiro else 177690792Sgshapiro macdefine(&e->e_macro, A_PERM, 177790792Sgshapiro macid("{dsn_notify}"), NULL); 177864562Sgshapiro 177938032Speter /* 178038032Speter ** Expand out this user into argument list. 178138032Speter */ 178238032Speter 178338032Speter if (!clever) 178438032Speter { 178538032Speter expand(*mvp, buf, sizeof buf, e); 178690792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 178738032Speter if (pvp >= &pv[MAXPV - 2]) 178838032Speter { 178938032Speter /* allow some space for trailing parms */ 179038032Speter break; 179138032Speter } 179238032Speter } 179338032Speter } 179438032Speter 179538032Speter /* see if any addresses still exist */ 179664562Sgshapiro if (tochain == NULL) 179738032Speter { 179890792Sgshapiro rcode = 0; 179990792Sgshapiro goto cleanup; 180038032Speter } 180138032Speter 180238032Speter /* print out messages as full list */ 180390792Sgshapiro strsize = 1; 180490792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 180590792Sgshapiro strsize += strlen(to->q_paddr) + 1; 180690792Sgshapiro if (strsize < TOBUFSIZE) 180790792Sgshapiro strsize = TOBUFSIZE; 180890792Sgshapiro if (strsize > tobufsize) 180964562Sgshapiro { 181090792Sgshapiro SM_FREE_CLR(tobuf); 181190792Sgshapiro tobuf = sm_pmalloc_x(strsize); 181290792Sgshapiro tobufsize = strsize; 181364562Sgshapiro } 181490792Sgshapiro p = tobuf; 181590792Sgshapiro *p = '\0'; 181690792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 181790792Sgshapiro { 181890792Sgshapiro (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 181990792Sgshapiro ",", to->q_paddr); 182090792Sgshapiro p += strlen(p); 182190792Sgshapiro } 182238032Speter e->e_to = tobuf + 1; 182338032Speter 182438032Speter /* 182538032Speter ** Fill out any parameters after the $u parameter. 182638032Speter */ 182738032Speter 182890792Sgshapiro if (!clever) 182938032Speter { 183090792Sgshapiro while (*++mvp != NULL) 183190792Sgshapiro { 183290792Sgshapiro expand(*mvp, buf, sizeof buf, e); 183390792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 183490792Sgshapiro if (pvp >= &pv[MAXPV]) 183590792Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 183690792Sgshapiro pv[0]); 183790792Sgshapiro } 183838032Speter } 183938032Speter *pvp++ = NULL; 184038032Speter 184138032Speter /* 184238032Speter ** Call the mailer. 184338032Speter ** The argument vector gets built, pipes 184438032Speter ** are created as necessary, and we fork & exec as 184538032Speter ** appropriate. 184638032Speter ** If we are running SMTP, we just need to clean up. 184738032Speter */ 184838032Speter 184964562Sgshapiro /* XXX this seems a bit wierd */ 185038032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 185138032Speter bitset(QGOODUID, e->e_from.q_flags)) 185238032Speter ctladdr = &e->e_from; 185338032Speter 185438032Speter#if NAMED_BIND 185538032Speter if (ConfigLevel < 2) 185638032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 185764562Sgshapiro#endif /* NAMED_BIND */ 185838032Speter 185938032Speter if (tTd(11, 1)) 186038032Speter { 186190792Sgshapiro sm_dprintf("openmailer:"); 1862132943Sgshapiro printav(sm_debug_file(), pv); 186338032Speter } 186438032Speter errno = 0; 186573188Sgshapiro SM_SET_H_ERRNO(0); 186638032Speter CurHostName = NULL; 186738032Speter 186838032Speter /* 186938032Speter ** Deal with the special case of mail handled through an IPC 187038032Speter ** connection. 187138032Speter ** In this case we don't actually fork. We must be 187238032Speter ** running SMTP for this to work. We will return a 187338032Speter ** zero pid to indicate that we are running IPC. 187438032Speter ** We also handle a debug version that just talks to stdin/out. 187538032Speter */ 187638032Speter 187738032Speter curhost = NULL; 187838032Speter SmtpPhase = NULL; 187938032Speter mci = NULL; 188038032Speter 188138032Speter#if XDEBUG 188238032Speter { 188338032Speter char wbuf[MAXLINE]; 188438032Speter 188538032Speter /* make absolutely certain 0, 1, and 2 are in use */ 188690792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", 188790792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 188890792Sgshapiro m->m_name); 188938032Speter checkfd012(wbuf); 189038032Speter } 189164562Sgshapiro#endif /* XDEBUG */ 189238032Speter 189338032Speter /* check for 8-bit available */ 189438032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 189538032Speter bitnset(M_7BITS, m->m_flags) && 189638032Speter (bitset(EF_DONT_MIME, e->e_flags) || 189738032Speter !(bitset(MM_MIME8BIT, MimeMode) || 189838032Speter (bitset(EF_IS_MIME, e->e_flags) && 189964562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 190038032Speter { 190164562Sgshapiro e->e_status = "5.6.3"; 190264562Sgshapiro usrerrenh(e->e_status, 190390792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 190438032Speter rcode = EX_DATAERR; 190538032Speter goto give_up; 190638032Speter } 190738032Speter 190838032Speter if (tTd(62, 8)) 190938032Speter checkfds("before delivery"); 191038032Speter 191138032Speter /* check for Local Person Communication -- not for mortals!!! */ 191238032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 191338032Speter { 191490792Sgshapiro if (clever) 191590792Sgshapiro { 191690792Sgshapiro /* flush any expired connections */ 191790792Sgshapiro (void) mci_scan(NULL); 191890792Sgshapiro 191990792Sgshapiro /* try to get a cached connection or just a slot */ 192090792Sgshapiro mci = mci_get(m->m_name, m); 192190792Sgshapiro if (mci->mci_host == NULL) 192290792Sgshapiro mci->mci_host = m->m_name; 192390792Sgshapiro CurHostName = mci->mci_host; 192490792Sgshapiro if (mci->mci_state != MCIS_CLOSED) 192590792Sgshapiro { 192690792Sgshapiro message("Using cached SMTP/LPC connection for %s...", 192790792Sgshapiro m->m_name); 192890792Sgshapiro mci->mci_deliveries++; 192990792Sgshapiro goto do_transfer; 193090792Sgshapiro } 193190792Sgshapiro } 193290792Sgshapiro else 193390792Sgshapiro { 193490792Sgshapiro mci = mci_new(e->e_rpool); 193590792Sgshapiro } 193690792Sgshapiro mci->mci_in = smioin; 193790792Sgshapiro mci->mci_out = smioout; 193890792Sgshapiro mci->mci_mailer = m; 193990792Sgshapiro mci->mci_host = m->m_name; 194090792Sgshapiro if (clever) 194190792Sgshapiro { 194290792Sgshapiro mci->mci_state = MCIS_OPENING; 194390792Sgshapiro mci_cache(mci); 194490792Sgshapiro } 194590792Sgshapiro else 194690792Sgshapiro mci->mci_state = MCIS_OPEN; 194738032Speter } 194890792Sgshapiro else if (strcmp(m->m_mailer, "[IPC]") == 0) 194938032Speter { 195038032Speter register int i; 195138032Speter 195238032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 195338032Speter { 195464562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 195538032Speter rcode = EX_CONFIG; 195638032Speter goto give_up; 195738032Speter } 195838032Speter 195964562Sgshapiro# if NETUNIX 196064562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 196164562Sgshapiro { 196264562Sgshapiro curhost = CurHostName = "localhost"; 196364562Sgshapiro mux_path = pv[1]; 196464562Sgshapiro } 196564562Sgshapiro else 196664562Sgshapiro# endif /* NETUNIX */ 196764562Sgshapiro { 196864562Sgshapiro CurHostName = pv[1]; 196964562Sgshapiro curhost = hostsignature(m, pv[1]); 197064562Sgshapiro } 197138032Speter 197238032Speter if (curhost == NULL || curhost[0] == '\0') 197338032Speter { 197438032Speter syserr("null host signature for %s", pv[1]); 197538032Speter rcode = EX_CONFIG; 197638032Speter goto give_up; 197738032Speter } 197838032Speter 197938032Speter if (!clever) 198038032Speter { 198164562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 198238032Speter rcode = EX_CONFIG; 198338032Speter goto give_up; 198438032Speter } 198564562Sgshapiro if (pv[2] != NULL 198664562Sgshapiro# if NETUNIX 198764562Sgshapiro && mux_path == NULL 198864562Sgshapiro# endif /* NETUNIX */ 198964562Sgshapiro ) 199038032Speter { 199190792Sgshapiro port = htons((unsigned short) atoi(pv[2])); 199238032Speter if (port == 0) 199338032Speter { 199464562Sgshapiro# ifdef NO_GETSERVBYNAME 199564562Sgshapiro syserr("Invalid port number: %s", pv[2]); 199664562Sgshapiro# else /* NO_GETSERVBYNAME */ 199738032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 199838032Speter 199938032Speter if (sp == NULL) 200038032Speter syserr("Service %s unknown", pv[2]); 200138032Speter else 200238032Speter port = sp->s_port; 200364562Sgshapiro# endif /* NO_GETSERVBYNAME */ 200438032Speter } 200538032Speter } 200664562Sgshapiro 200764562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 200890792Sgshapiro if (TimeOuts.to_aconnect > 0) 200990792Sgshapiro enough = curtime() + TimeOuts.to_aconnect; 201038032Spetertryhost: 201164562Sgshapiro while (hostnum < nummxhosts) 201238032Speter { 201364562Sgshapiro char sep = ':'; 201464562Sgshapiro char *endp; 201538032Speter static char hostbuf[MAXNAME + 1]; 2016132943Sgshapiro bool tried_fallbacksmarthost = false; 201738032Speter 201864562Sgshapiro# if NETINET6 201964562Sgshapiro if (*mxhosts[hostnum] == '[') 202038032Speter { 202164562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 202264562Sgshapiro if (endp != NULL) 202364562Sgshapiro endp = strpbrk(endp + 1, ":,"); 202464562Sgshapiro } 202564562Sgshapiro else 202664562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 202764562Sgshapiro# else /* NETINET6 */ 202864562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 202964562Sgshapiro# endif /* NETINET6 */ 203064562Sgshapiro if (endp != NULL) 203164562Sgshapiro { 203264562Sgshapiro sep = *endp; 203364562Sgshapiro *endp = '\0'; 203464562Sgshapiro } 203564562Sgshapiro 203690792Sgshapiro if (hostnum == 1 && skip_back != NULL) 203790792Sgshapiro { 203890792Sgshapiro /* 203990792Sgshapiro ** Coattail piggybacking is no longer an 204090792Sgshapiro ** option with the mail host next to be tried 204190792Sgshapiro ** no longer the lowest MX preference 204290792Sgshapiro ** (hostnum == 1 meaning we're on the second 204390792Sgshapiro ** preference). We do not try to coattail 204490792Sgshapiro ** piggyback more than the first MX preference. 204590792Sgshapiro ** Revert 'tochain' to last location for 204690792Sgshapiro ** coincidental piggybacking. This works this 204790792Sgshapiro ** easily because the q_tchain kept getting 204890792Sgshapiro ** added to the top of the linked list. 204990792Sgshapiro */ 205090792Sgshapiro 205190792Sgshapiro tochain = skip_back; 205290792Sgshapiro } 205390792Sgshapiro 205464562Sgshapiro if (*mxhosts[hostnum] == '\0') 205564562Sgshapiro { 205638032Speter syserr("deliver: null host name in signature"); 205764562Sgshapiro hostnum++; 205864562Sgshapiro if (endp != NULL) 205964562Sgshapiro *endp = sep; 206038032Speter continue; 206138032Speter } 206290792Sgshapiro (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 206390792Sgshapiro sizeof hostbuf); 206464562Sgshapiro hostnum++; 206564562Sgshapiro if (endp != NULL) 206664562Sgshapiro *endp = sep; 206738032Speter 2068132943Sgshapiro one_last_try: 206938032Speter /* see if we already know that this host is fried */ 207038032Speter CurHostName = hostbuf; 207138032Speter mci = mci_get(hostbuf, m); 207238032Speter if (mci->mci_state != MCIS_CLOSED) 207338032Speter { 207490792Sgshapiro char *type; 207590792Sgshapiro 207638032Speter if (tTd(11, 1)) 207738032Speter { 207890792Sgshapiro sm_dprintf("openmailer: "); 2079132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 208038032Speter } 208138032Speter CurHostName = mci->mci_host; 208290792Sgshapiro if (bitnset(M_LMTP, m->m_flags)) 208390792Sgshapiro type = "L"; 208490792Sgshapiro else if (bitset(MCIF_ESMTP, mci->mci_flags)) 208590792Sgshapiro type = "ES"; 208690792Sgshapiro else 208790792Sgshapiro type = "S"; 208890792Sgshapiro message("Using cached %sMTP connection to %s via %s...", 208990792Sgshapiro type, hostbuf, m->m_name); 209064562Sgshapiro mci->mci_deliveries++; 209138032Speter break; 209238032Speter } 209338032Speter mci->mci_mailer = m; 209438032Speter if (mci->mci_exitstat != EX_OK) 209538032Speter { 209638032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 209790792Sgshapiro goodmxfound = true; 2098132943Sgshapiro 2099132943Sgshapiro /* Try FallbackSmartHost? */ 2100132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2101132943Sgshapiro hostbuf, sizeof hostbuf, 2102132943Sgshapiro mci->mci_exitstat)) 2103132943Sgshapiro goto one_last_try; 2104132943Sgshapiro 210538032Speter continue; 210638032Speter } 210738032Speter 210838032Speter if (mci_lock_host(mci) != EX_OK) 210938032Speter { 211038032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 211190792Sgshapiro goodmxfound = true; 211238032Speter continue; 211338032Speter } 211438032Speter 211538032Speter /* try the connection */ 211690792Sgshapiro sm_setproctitle(true, e, "%s %s: %s", 211764562Sgshapiro qid_printname(e), 211864562Sgshapiro hostbuf, "user open"); 211964562Sgshapiro# if NETUNIX 212064562Sgshapiro if (mux_path != NULL) 212164562Sgshapiro { 212238032Speter message("Connecting to %s via %s...", 212364562Sgshapiro mux_path, m->m_name); 212490792Sgshapiro i = makeconnection_ds((char *) mux_path, mci); 212564562Sgshapiro } 212638032Speter else 212764562Sgshapiro# endif /* NETUNIX */ 212864562Sgshapiro { 212964562Sgshapiro if (port == 0) 213064562Sgshapiro message("Connecting to %s via %s...", 213164562Sgshapiro hostbuf, m->m_name); 213264562Sgshapiro else 213364562Sgshapiro message("Connecting to %s port %d via %s...", 213464562Sgshapiro hostbuf, ntohs(port), 213564562Sgshapiro m->m_name); 213690792Sgshapiro i = makeconnection(hostbuf, port, mci, e, 213790792Sgshapiro enough); 213864562Sgshapiro } 213977349Sgshapiro mci->mci_errno = errno; 214038032Speter mci->mci_lastuse = curtime(); 214164562Sgshapiro mci->mci_deliveries = 0; 214238032Speter mci->mci_exitstat = i; 214364562Sgshapiro# if NAMED_BIND 214438032Speter mci->mci_herrno = h_errno; 214564562Sgshapiro# endif /* NAMED_BIND */ 214690792Sgshapiro 214790792Sgshapiro /* 214890792Sgshapiro ** Have we tried long enough to get a connection? 214990792Sgshapiro ** If yes, skip to the fallback MX hosts 215090792Sgshapiro ** (if existent). 215190792Sgshapiro */ 215290792Sgshapiro 215390792Sgshapiro if (enough > 0 && mci->mci_lastuse >= enough) 215490792Sgshapiro { 215590792Sgshapiro int h; 215690792Sgshapiro# if NAMED_BIND 2157132943Sgshapiro extern int NumFallbackMXHosts; 215890792Sgshapiro# else /* NAMED_BIND */ 2159132943Sgshapiro const int NumFallbackMXHosts = 0; 216090792Sgshapiro# endif /* NAMED_BIND */ 216190792Sgshapiro 216290792Sgshapiro if (hostnum < nummxhosts && LogLevel > 9) 216390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 216490792Sgshapiro "Timeout.to_aconnect occurred before exhausting all addresses"); 216590792Sgshapiro 216690792Sgshapiro /* turn off timeout if fallback available */ 2167132943Sgshapiro if (NumFallbackMXHosts > 0) 216890792Sgshapiro enough = 0; 216990792Sgshapiro 217090792Sgshapiro /* skip to a fallback MX host */ 2171132943Sgshapiro h = nummxhosts - NumFallbackMXHosts; 217290792Sgshapiro if (hostnum < h) 217390792Sgshapiro hostnum = h; 217490792Sgshapiro } 217538032Speter if (i == EX_OK) 217638032Speter { 217790792Sgshapiro goodmxfound = true; 217894334Sgshapiro markstats(e, firstto, STATS_CONNECT); 217938032Speter mci->mci_state = MCIS_OPENING; 218038032Speter mci_cache(mci); 218138032Speter if (TrafficLogFile != NULL) 218290792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 218390792Sgshapiro SM_TIME_DEFAULT, 218490792Sgshapiro "%05d === CONNECT %s\n", 218590792Sgshapiro (int) CurrentPid, 218690792Sgshapiro hostbuf); 218738032Speter break; 218838032Speter } 218938032Speter else 219038032Speter { 2191132943Sgshapiro /* Try FallbackSmartHost? */ 2192132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2193132943Sgshapiro hostbuf, sizeof hostbuf, i)) 2194132943Sgshapiro goto one_last_try; 2195132943Sgshapiro 219664562Sgshapiro if (tTd(11, 1)) 219790792Sgshapiro sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 219890792Sgshapiro i, errno); 219938032Speter if (i == EX_TEMPFAIL) 220090792Sgshapiro goodmxfound = true; 220138032Speter mci_unlock_host(mci); 220238032Speter } 220338032Speter 220438032Speter /* enter status of this host */ 220538032Speter setstat(i); 220638032Speter 220738032Speter /* should print some message here for -v mode */ 220838032Speter } 220938032Speter if (mci == NULL) 221038032Speter { 221138032Speter syserr("deliver: no host name"); 221238032Speter rcode = EX_SOFTWARE; 221338032Speter goto give_up; 221438032Speter } 221538032Speter mci->mci_pid = 0; 221638032Speter } 221738032Speter else 221838032Speter { 221938032Speter /* flush any expired connections */ 222038032Speter (void) mci_scan(NULL); 222138032Speter mci = NULL; 222238032Speter 222338032Speter if (bitnset(M_LMTP, m->m_flags)) 222438032Speter { 222538032Speter /* try to get a cached connection */ 222638032Speter mci = mci_get(m->m_name, m); 222738032Speter if (mci->mci_host == NULL) 222838032Speter mci->mci_host = m->m_name; 222938032Speter CurHostName = mci->mci_host; 223038032Speter if (mci->mci_state != MCIS_CLOSED) 223138032Speter { 223238032Speter message("Using cached LMTP connection for %s...", 223338032Speter m->m_name); 223464562Sgshapiro mci->mci_deliveries++; 223538032Speter goto do_transfer; 223638032Speter } 223738032Speter } 223838032Speter 223938032Speter /* announce the connection to verbose listeners */ 224038032Speter if (host == NULL || host[0] == '\0') 224138032Speter message("Connecting to %s...", m->m_name); 224238032Speter else 224338032Speter message("Connecting to %s via %s...", host, m->m_name); 224438032Speter if (TrafficLogFile != NULL) 224538032Speter { 224638032Speter char **av; 224738032Speter 224890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 224990792Sgshapiro "%05d === EXEC", (int) CurrentPid); 225038032Speter for (av = pv; *av != NULL; av++) 225190792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 225290792Sgshapiro SM_TIME_DEFAULT, " %s", 225390792Sgshapiro *av); 225490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 225590792Sgshapiro "\n"); 225638032Speter } 225738032Speter 225838032Speter#if XDEBUG 225938032Speter checkfd012("before creating mail pipe"); 226064562Sgshapiro#endif /* XDEBUG */ 226138032Speter 226238032Speter /* create a pipe to shove the mail through */ 226338032Speter if (pipe(mpvect) < 0) 226438032Speter { 226538032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 226690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 226738032Speter if (tTd(11, 1)) 226890792Sgshapiro sm_dprintf("openmailer: NULL\n"); 226938032Speter rcode = EX_OSERR; 227038032Speter goto give_up; 227138032Speter } 227238032Speter 227338032Speter#if XDEBUG 227438032Speter /* make sure we didn't get one of the standard I/O files */ 227538032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 227638032Speter { 227738032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 227890792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 227990792Sgshapiro mpvect[0], mpvect[1]); 228090792Sgshapiro printopenfds(true); 228138032Speter if (tTd(11, 1)) 228290792Sgshapiro sm_dprintf("openmailer: NULL\n"); 228338032Speter rcode = EX_OSERR; 228438032Speter goto give_up; 228538032Speter } 228638032Speter 228738032Speter /* make sure system call isn't dead meat */ 228838032Speter checkfdopen(mpvect[0], "mpvect[0]"); 228938032Speter checkfdopen(mpvect[1], "mpvect[1]"); 229038032Speter if (mpvect[0] == mpvect[1] || 229138032Speter (e->e_lockfp != NULL && 229290792Sgshapiro (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 229390792Sgshapiro NULL) || 229490792Sgshapiro mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 229590792Sgshapiro NULL)))) 229638032Speter { 229738032Speter if (e->e_lockfp == NULL) 229838032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 229990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 230090792Sgshapiro m->m_name, mpvect[0], mpvect[1]); 230138032Speter else 230238032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 230390792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 230490792Sgshapiro m->m_name, mpvect[0], mpvect[1], 230590792Sgshapiro sm_io_getinfo(e->e_lockfp, 230690792Sgshapiro SM_IO_WHAT_FD, NULL)); 230738032Speter } 230864562Sgshapiro#endif /* XDEBUG */ 230938032Speter 231064562Sgshapiro /* create a return pipe */ 231164562Sgshapiro if (pipe(rpvect) < 0) 231238032Speter { 231364562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 231490792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 231590792Sgshapiro m->m_name); 231664562Sgshapiro (void) close(mpvect[0]); 231764562Sgshapiro (void) close(mpvect[1]); 231864562Sgshapiro if (tTd(11, 1)) 231990792Sgshapiro sm_dprintf("openmailer: NULL\n"); 232064562Sgshapiro rcode = EX_OSERR; 232164562Sgshapiro goto give_up; 232238032Speter } 232364562Sgshapiro#if XDEBUG 232464562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 232564562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 232664562Sgshapiro#endif /* XDEBUG */ 232738032Speter 232838032Speter /* 232938032Speter ** Actually fork the mailer process. 233038032Speter ** DOFORK is clever about retrying. 233138032Speter ** 233238032Speter ** Dispose of SIGCHLD signal catchers that may be laying 233364562Sgshapiro ** around so that endmailer will get it. 233438032Speter */ 233538032Speter 233690792Sgshapiro if (e->e_xfp != NULL) /* for debugging */ 233790792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 233890792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 233990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 234064562Sgshapiro 234164562Sgshapiro 234238032Speter DOFORK(FORK); 234338032Speter /* pid is set by DOFORK */ 234464562Sgshapiro 234538032Speter if (pid < 0) 234638032Speter { 234738032Speter /* failure */ 234838032Speter syserr("%s... openmailer(%s): cannot fork", 234990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 235038032Speter (void) close(mpvect[0]); 235138032Speter (void) close(mpvect[1]); 235264562Sgshapiro (void) close(rpvect[0]); 235364562Sgshapiro (void) close(rpvect[1]); 235438032Speter if (tTd(11, 1)) 235590792Sgshapiro sm_dprintf("openmailer: NULL\n"); 235638032Speter rcode = EX_OSERR; 235738032Speter goto give_up; 235838032Speter } 235938032Speter else if (pid == 0) 236038032Speter { 236164562Sgshapiro int save_errno; 236290792Sgshapiro int sff; 236338032Speter int new_euid = NO_UID; 236438032Speter int new_ruid = NO_UID; 236538032Speter int new_gid = NO_GID; 236690792Sgshapiro char *user = NULL; 236738032Speter struct stat stb; 236838032Speter extern int DtableSize; 236938032Speter 237090792Sgshapiro CurrentPid = getpid(); 237190792Sgshapiro 237280785Sgshapiro /* clear the events to turn off SIGALRMs */ 237390792Sgshapiro sm_clear_events(); 237480785Sgshapiro 237577349Sgshapiro /* Reset global flags */ 237677349Sgshapiro RestartRequest = NULL; 237790792Sgshapiro RestartWorkGroup = false; 237877349Sgshapiro ShutdownRequest = NULL; 237977349Sgshapiro PendingSignal = 0; 238077349Sgshapiro 238138032Speter if (e->e_lockfp != NULL) 238290792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, 238390792Sgshapiro SM_IO_WHAT_FD, 238490792Sgshapiro NULL)); 238538032Speter 238638032Speter /* child -- set up input & exec mailer */ 238790792Sgshapiro (void) sm_signal(SIGALRM, sm_signal_noop); 238890792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 238990792Sgshapiro (void) sm_signal(SIGHUP, SIG_IGN); 239090792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 239190792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 239280785Sgshapiro# ifdef SIGUSR1 239390792Sgshapiro (void) sm_signal(SIGUSR1, sm_signal_noop); 239480785Sgshapiro# endif /* SIGUSR1 */ 239538032Speter 239638032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 239738032Speter stb.st_mode = 0; 239838032Speter 239964562Sgshapiro# if HASSETUSERCONTEXT 240038032Speter /* 240138032Speter ** Set user resources. 240238032Speter */ 240338032Speter 240438032Speter if (contextaddr != NULL) 240538032Speter { 2406110560Sgshapiro int sucflags; 240738032Speter struct passwd *pwd; 240838032Speter 240938032Speter if (contextaddr->q_ruser != NULL) 241038032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 241138032Speter else 241238032Speter pwd = sm_getpwnam(contextaddr->q_user); 2413110560Sgshapiro sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2414110560Sgshapiro#ifdef LOGIN_SETMAC 2415110560Sgshapiro sucflags |= LOGIN_SETMAC; 2416110560Sgshapiro#endif /* LOGIN_SETMAC */ 2417102528Sgshapiro if (pwd != NULL && 2418102528Sgshapiro setusercontext(NULL, pwd, pwd->pw_uid, 2419110560Sgshapiro sucflags) == -1 && 2420102528Sgshapiro suidwarn) 2421102528Sgshapiro { 2422102528Sgshapiro syserr("openmailer: setusercontext() failed"); 2423102528Sgshapiro exit(EX_TEMPFAIL); 2424102528Sgshapiro } 242538032Speter } 242664562Sgshapiro# endif /* HASSETUSERCONTEXT */ 242738032Speter 242890792Sgshapiro#if HASNICE 242938032Speter /* tweak niceness */ 243038032Speter if (m->m_nice != 0) 243164562Sgshapiro (void) nice(m->m_nice); 243290792Sgshapiro#endif /* HASNICE */ 243338032Speter 243438032Speter /* reset group id */ 243538032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 2436132943Sgshapiro { 2437132943Sgshapiro if (m->m_gid == NO_GID) 2438132943Sgshapiro new_gid = RunAsGid; 2439132943Sgshapiro else 2440132943Sgshapiro new_gid = m->m_gid; 2441132943Sgshapiro } 244238032Speter else if (bitset(S_ISGID, stb.st_mode)) 244338032Speter new_gid = stb.st_gid; 244438032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 244538032Speter { 244638032Speter if (!DontInitGroups) 244738032Speter { 244890792Sgshapiro user = ctladdr->q_ruser; 244990792Sgshapiro if (user == NULL) 245090792Sgshapiro user = ctladdr->q_user; 245138032Speter 245290792Sgshapiro if (initgroups(user, 245390792Sgshapiro ctladdr->q_gid) == -1 245490792Sgshapiro && suidwarn) 245564562Sgshapiro { 245638032Speter syserr("openmailer: initgroups(%s, %d) failed", 245790792Sgshapiro user, ctladdr->q_gid); 245864562Sgshapiro exit(EX_TEMPFAIL); 245964562Sgshapiro } 246038032Speter } 246138032Speter else 246238032Speter { 246338032Speter GIDSET_T gidset[1]; 246438032Speter 246538032Speter gidset[0] = ctladdr->q_gid; 246690792Sgshapiro if (setgroups(1, gidset) == -1 246790792Sgshapiro && suidwarn) 246864562Sgshapiro { 246938032Speter syserr("openmailer: setgroups() failed"); 247064562Sgshapiro exit(EX_TEMPFAIL); 247164562Sgshapiro } 247238032Speter } 247338032Speter new_gid = ctladdr->q_gid; 247438032Speter } 247538032Speter else 247638032Speter { 247738032Speter if (!DontInitGroups) 247838032Speter { 247990792Sgshapiro user = DefUser; 248090792Sgshapiro if (initgroups(DefUser, DefGid) == -1 && 248190792Sgshapiro suidwarn) 248264562Sgshapiro { 248338032Speter syserr("openmailer: initgroups(%s, %d) failed", 248490792Sgshapiro DefUser, DefGid); 248564562Sgshapiro exit(EX_TEMPFAIL); 248664562Sgshapiro } 248738032Speter } 248838032Speter else 248938032Speter { 249038032Speter GIDSET_T gidset[1]; 249138032Speter 249238032Speter gidset[0] = DefGid; 249390792Sgshapiro if (setgroups(1, gidset) == -1 249490792Sgshapiro && suidwarn) 249564562Sgshapiro { 249638032Speter syserr("openmailer: setgroups() failed"); 249764562Sgshapiro exit(EX_TEMPFAIL); 249864562Sgshapiro } 249938032Speter } 2500132943Sgshapiro if (m->m_gid == NO_GID) 250138032Speter new_gid = DefGid; 250238032Speter else 250338032Speter new_gid = m->m_gid; 250438032Speter } 250564562Sgshapiro if (new_gid != NO_GID) 250664562Sgshapiro { 250764562Sgshapiro if (RunAsUid != 0 && 250864562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 250964562Sgshapiro new_gid != getgid() && 251064562Sgshapiro new_gid != getegid()) 251164562Sgshapiro { 251264562Sgshapiro /* Only root can change the gid */ 251390792Sgshapiro syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d", 251490792Sgshapiro (int) RunAsUid, (int) new_gid, 251590792Sgshapiro (int) getgid(), (int) getegid()); 251664562Sgshapiro exit(EX_TEMPFAIL); 251764562Sgshapiro } 251838032Speter 251964562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 252064562Sgshapiro { 252164562Sgshapiro syserr("openmailer: setgid(%ld) failed", 252264562Sgshapiro (long) new_gid); 252364562Sgshapiro exit(EX_TEMPFAIL); 252464562Sgshapiro } 252564562Sgshapiro } 252664562Sgshapiro 252764562Sgshapiro /* change root to some "safe" directory */ 252864562Sgshapiro if (m->m_rootdir != NULL) 252964562Sgshapiro { 253098121Sgshapiro expand(m->m_rootdir, cbuf, sizeof cbuf, e); 253164562Sgshapiro if (tTd(11, 20)) 253290792Sgshapiro sm_dprintf("openmailer: chroot %s\n", 253398121Sgshapiro cbuf); 253498121Sgshapiro if (chroot(cbuf) < 0) 253564562Sgshapiro { 253664562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 253798121Sgshapiro cbuf); 253864562Sgshapiro exit(EX_TEMPFAIL); 253964562Sgshapiro } 254064562Sgshapiro if (chdir("/") < 0) 254164562Sgshapiro { 254264562Sgshapiro syserr("openmailer: cannot chdir(/)"); 254364562Sgshapiro exit(EX_TEMPFAIL); 254464562Sgshapiro } 254564562Sgshapiro } 254664562Sgshapiro 254738032Speter /* reset user id */ 254838032Speter endpwent(); 254990792Sgshapiro sm_mbdb_terminate(); 255038032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 255180785Sgshapiro { 2552132943Sgshapiro if (m->m_uid == NO_UID) 2553132943Sgshapiro new_euid = RunAsUid; 2554132943Sgshapiro else 2555132943Sgshapiro new_euid = m->m_uid; 255680785Sgshapiro 255780785Sgshapiro /* 255880785Sgshapiro ** Undo the effects of the uid change in main 255980785Sgshapiro ** for signal handling. The real uid may 256080785Sgshapiro ** be used by mailer in adding a "From " 256180785Sgshapiro ** line. 256280785Sgshapiro */ 256380785Sgshapiro 256480785Sgshapiro if (RealUid != 0 && RealUid != getuid()) 256590792Sgshapiro { 256690792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 256790792Sgshapiro# if HASSETREUID 256890792Sgshapiro if (setreuid(RealUid, geteuid()) < 0) 256990792Sgshapiro { 257090792Sgshapiro syserr("openmailer: setreuid(%d, %d) failed", 257190792Sgshapiro (int) RealUid, (int) geteuid()); 257290792Sgshapiro exit(EX_OSERR); 257390792Sgshapiro } 257490792Sgshapiro# endif /* HASSETREUID */ 257590792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 257690792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 257780785Sgshapiro new_ruid = RealUid; 257890792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 257990792Sgshapiro } 258080785Sgshapiro } 258138032Speter else if (bitset(S_ISUID, stb.st_mode)) 258238032Speter new_ruid = stb.st_uid; 258338032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 258438032Speter new_ruid = ctladdr->q_uid; 2585132943Sgshapiro else if (m->m_uid != NO_UID) 258638032Speter new_ruid = m->m_uid; 258738032Speter else 258838032Speter new_ruid = DefUid; 258994334Sgshapiro 259094334Sgshapiro# if _FFR_USE_SETLOGIN 259194334Sgshapiro /* run disconnected from terminal and set login name */ 259294334Sgshapiro if (setsid() >= 0 && 259394334Sgshapiro ctladdr != NULL && ctladdr->q_uid != 0 && 259494334Sgshapiro new_euid == ctladdr->q_uid) 259594334Sgshapiro { 259694334Sgshapiro struct passwd *pwd; 259794334Sgshapiro 259894334Sgshapiro pwd = sm_getpwuid(ctladdr->q_uid); 259994334Sgshapiro if (pwd != NULL && suidwarn) 260094334Sgshapiro (void) setlogin(pwd->pw_name); 260194334Sgshapiro endpwent(); 260294334Sgshapiro } 260394334Sgshapiro# endif /* _FFR_USE_SETLOGIN */ 260494334Sgshapiro 260538032Speter if (new_euid != NO_UID) 260638032Speter { 260764562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 260864562Sgshapiro { 260964562Sgshapiro /* Only root can change the uid */ 261090792Sgshapiro syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d", 261190792Sgshapiro (int) new_euid, (int) RunAsUid); 261264562Sgshapiro exit(EX_TEMPFAIL); 261364562Sgshapiro } 261464562Sgshapiro 261538032Speter vendor_set_uid(new_euid); 261664562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 261738032Speter if (seteuid(new_euid) < 0 && suidwarn) 261864562Sgshapiro { 261938032Speter syserr("openmailer: seteuid(%ld) failed", 262090792Sgshapiro (long) new_euid); 262164562Sgshapiro exit(EX_TEMPFAIL); 262264562Sgshapiro } 262364562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 262464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 262538032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 262664562Sgshapiro { 262738032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 262890792Sgshapiro (long) new_ruid, (long) new_euid); 262964562Sgshapiro exit(EX_TEMPFAIL); 263064562Sgshapiro } 263164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 263264562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID 263338032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 263464562Sgshapiro { 263538032Speter syserr("openmailer: setuid(%ld) failed", 263690792Sgshapiro (long) new_euid); 263764562Sgshapiro exit(EX_TEMPFAIL); 263864562Sgshapiro } 263964562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */ 264038032Speter } 264138032Speter else if (new_ruid != NO_UID) 264238032Speter { 264338032Speter vendor_set_uid(new_ruid); 264438032Speter if (setuid(new_ruid) < 0 && suidwarn) 264564562Sgshapiro { 264638032Speter syserr("openmailer: setuid(%ld) failed", 264790792Sgshapiro (long) new_ruid); 264864562Sgshapiro exit(EX_TEMPFAIL); 264964562Sgshapiro } 265038032Speter } 265138032Speter 265238032Speter if (tTd(11, 2)) 265390792Sgshapiro sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 265490792Sgshapiro (int) getuid(), (int) geteuid(), 265590792Sgshapiro (int) getgid(), (int) getegid()); 265638032Speter 265738032Speter /* move into some "safe" directory */ 265838032Speter if (m->m_execdir != NULL) 265938032Speter { 266038032Speter char *q; 266138032Speter 266238032Speter for (p = m->m_execdir; p != NULL; p = q) 266338032Speter { 266438032Speter q = strchr(p, ':'); 266538032Speter if (q != NULL) 266638032Speter *q = '\0'; 266798121Sgshapiro expand(p, cbuf, sizeof cbuf, e); 266838032Speter if (q != NULL) 266938032Speter *q++ = ':'; 267038032Speter if (tTd(11, 20)) 267190792Sgshapiro sm_dprintf("openmailer: trydir %s\n", 267298121Sgshapiro cbuf); 267398121Sgshapiro if (cbuf[0] != '\0' && 267498121Sgshapiro chdir(cbuf) >= 0) 267538032Speter break; 267638032Speter } 267738032Speter } 267838032Speter 267990792Sgshapiro /* Check safety of program to be run */ 268090792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 268190792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, 268290792Sgshapiro DontBlameSendmail)) 268390792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 268490792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 268590792Sgshapiro DontBlameSendmail)) 268690792Sgshapiro sff |= SFF_NOPATHCHECK; 268790792Sgshapiro else 268890792Sgshapiro sff |= SFF_SAFEDIRPATH; 268990792Sgshapiro ret = safefile(m->m_mailer, getuid(), getgid(), 269090792Sgshapiro user, sff, 0, NULL); 269190792Sgshapiro if (ret != 0) 269290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 269390792Sgshapiro "Warning: program %s unsafe: %s", 269490792Sgshapiro m->m_mailer, sm_errstring(ret)); 269590792Sgshapiro 269638032Speter /* arrange to filter std & diag output of command */ 269764562Sgshapiro (void) close(rpvect[0]); 269864562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 269938032Speter { 270064562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 270164562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 270264562Sgshapiro m->m_name, rpvect[1]); 270364562Sgshapiro _exit(EX_OSERR); 270438032Speter } 270564562Sgshapiro (void) close(rpvect[1]); 270664562Sgshapiro 270738032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 270838032Speter { 270938032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 271090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 271190792Sgshapiro m->m_name); 271238032Speter _exit(EX_OSERR); 271338032Speter } 271438032Speter 271538032Speter /* arrange to get standard input */ 271638032Speter (void) close(mpvect[1]); 271738032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 271838032Speter { 271938032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 272090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 272190792Sgshapiro m->m_name, mpvect[0]); 272238032Speter _exit(EX_OSERR); 272338032Speter } 272438032Speter (void) close(mpvect[0]); 272538032Speter 272638032Speter /* arrange for all the files to be closed */ 2727132943Sgshapiro sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 272838032Speter 272994334Sgshapiro# if !_FFR_USE_SETLOGIN 273038032Speter /* run disconnected from terminal */ 273138032Speter (void) setsid(); 273294334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */ 273338032Speter 273438032Speter /* try to execute the mailer */ 273564562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 273664562Sgshapiro (ARGV_T) UserEnviron); 273764562Sgshapiro save_errno = errno; 273838032Speter syserr("Cannot exec %s", m->m_mailer); 273938032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 274064562Sgshapiro transienterror(save_errno)) 274138032Speter _exit(EX_OSERR); 274238032Speter _exit(EX_UNAVAILABLE); 274338032Speter } 274438032Speter 274538032Speter /* 274638032Speter ** Set up return value. 274738032Speter */ 274838032Speter 274938032Speter if (mci == NULL) 275038032Speter { 275190792Sgshapiro if (clever) 275290792Sgshapiro { 275390792Sgshapiro /* 275490792Sgshapiro ** Allocate from general heap, not 275590792Sgshapiro ** envelope rpool, because this mci 275690792Sgshapiro ** is going to be cached. 275790792Sgshapiro */ 275890792Sgshapiro 275990792Sgshapiro mci = mci_new(NULL); 276090792Sgshapiro } 276190792Sgshapiro else 276290792Sgshapiro { 276390792Sgshapiro /* 276490792Sgshapiro ** Prevent a storage leak by allocating 276590792Sgshapiro ** this from the envelope rpool. 276690792Sgshapiro */ 276790792Sgshapiro 276890792Sgshapiro mci = mci_new(e->e_rpool); 276990792Sgshapiro } 277038032Speter } 277138032Speter mci->mci_mailer = m; 277238032Speter if (clever) 277338032Speter { 277438032Speter mci->mci_state = MCIS_OPENING; 277538032Speter mci_cache(mci); 277638032Speter } 277738032Speter else 277838032Speter { 277938032Speter mci->mci_state = MCIS_OPEN; 278038032Speter } 278138032Speter mci->mci_pid = pid; 278238032Speter (void) close(mpvect[0]); 278390792Sgshapiro mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2784132943Sgshapiro (void *) &(mpvect[1]), SM_IO_WRONLY_B, 278590792Sgshapiro NULL); 278638032Speter if (mci->mci_out == NULL) 278738032Speter { 278838032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 278990792Sgshapiro mpvect[1]); 279038032Speter (void) close(mpvect[1]); 279164562Sgshapiro (void) close(rpvect[0]); 279264562Sgshapiro (void) close(rpvect[1]); 279338032Speter rcode = EX_OSERR; 279438032Speter goto give_up; 279538032Speter } 279664562Sgshapiro 279764562Sgshapiro (void) close(rpvect[1]); 279890792Sgshapiro mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2799132943Sgshapiro (void *) &(rpvect[0]), SM_IO_RDONLY_B, 280090792Sgshapiro NULL); 280164562Sgshapiro if (mci->mci_in == NULL) 280238032Speter { 280364562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 280464562Sgshapiro mpvect[1]); 280564562Sgshapiro (void) close(rpvect[0]); 280690792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 280764562Sgshapiro mci->mci_out = NULL; 280864562Sgshapiro rcode = EX_OSERR; 280964562Sgshapiro goto give_up; 281038032Speter } 281138032Speter } 281238032Speter 281338032Speter /* 281438032Speter ** If we are in SMTP opening state, send initial protocol. 281538032Speter */ 281638032Speter 281738032Speter if (bitnset(M_7BITS, m->m_flags) && 281838032Speter (!clever || mci->mci_state == MCIS_OPENING)) 281938032Speter mci->mci_flags |= MCIF_7BIT; 282038032Speter if (clever && mci->mci_state != MCIS_CLOSED) 282138032Speter { 282290792Sgshapiro# if STARTTLS || SASL 282390792Sgshapiro int dotpos; 282490792Sgshapiro char *srvname; 282590792Sgshapiro extern SOCKADDR CurHostAddr; 282690792Sgshapiro# endif /* STARTTLS || SASL */ 282790792Sgshapiro 282890792Sgshapiro# if SASL 282971345Sgshapiro# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 283090792Sgshapiro# endif /* SASL */ 283164562Sgshapiro# if STARTTLS 283271345Sgshapiro# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 283364562Sgshapiro# endif /* STARTTLS */ 283471345Sgshapiro# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 283571345Sgshapiro# define SET_HELO(f) f |= MCIF_ONLY_EHLO 283671345Sgshapiro# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 283738032Speter 283890792Sgshapiro# if STARTTLS || SASL 283990792Sgshapiro /* don't use CurHostName, it is changed in many places */ 284090792Sgshapiro if (mci->mci_host != NULL) 284190792Sgshapiro { 284290792Sgshapiro srvname = mci->mci_host; 284390792Sgshapiro dotpos = strlen(srvname) - 1; 284490792Sgshapiro if (dotpos >= 0) 284590792Sgshapiro { 284690792Sgshapiro if (srvname[dotpos] == '.') 284790792Sgshapiro srvname[dotpos] = '\0'; 284890792Sgshapiro else 284990792Sgshapiro dotpos = -1; 285090792Sgshapiro } 285190792Sgshapiro } 285290792Sgshapiro else if (mci->mci_mailer != NULL) 285390792Sgshapiro { 285490792Sgshapiro srvname = mci->mci_mailer->m_name; 285590792Sgshapiro dotpos = -1; 285690792Sgshapiro } 285790792Sgshapiro else 285890792Sgshapiro { 285990792Sgshapiro srvname = "local"; 286090792Sgshapiro dotpos = -1; 286190792Sgshapiro } 286271345Sgshapiro 286390792Sgshapiro /* don't set {server_name} to NULL or "": see getauth() */ 286490792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 286590792Sgshapiro srvname); 286664562Sgshapiro 286790792Sgshapiro /* CurHostAddr is set by makeconnection() and mci_get() */ 286890792Sgshapiro if (CurHostAddr.sa.sa_family != 0) 286990792Sgshapiro { 287090792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, 287190792Sgshapiro macid("{server_addr}"), 287290792Sgshapiro anynet_ntoa(&CurHostAddr)); 287390792Sgshapiro } 287490792Sgshapiro else if (mci->mci_mailer != NULL) 287590792Sgshapiro { 287690792Sgshapiro /* mailer name is unique, use it as address */ 287790792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 287890792Sgshapiro macid("{server_addr}"), 287990792Sgshapiro mci->mci_mailer->m_name); 288090792Sgshapiro } 288190792Sgshapiro else 288290792Sgshapiro { 288390792Sgshapiro /* don't set it to NULL or "": see getauth() */ 288490792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 288590792Sgshapiro macid("{server_addr}"), "0"); 288690792Sgshapiro } 288790792Sgshapiro 288890792Sgshapiro /* undo change of srvname (mci->mci_host) */ 288990792Sgshapiro if (dotpos >= 0) 289090792Sgshapiro srvname[dotpos] = '.'; 289190792Sgshapiro 289290792Sgshapiroreconnect: /* after switching to an encrypted connection */ 289390792Sgshapiro# endif /* STARTTLS || SASL */ 289490792Sgshapiro 289590792Sgshapiro /* set the current connection information */ 289690792Sgshapiro e->e_mci = mci; 289764562Sgshapiro# if SASL 289864562Sgshapiro mci->mci_saslcap = NULL; 289964562Sgshapiro# endif /* SASL */ 290071345Sgshapiro smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 290171345Sgshapiro CLR_HELO(mci->mci_flags); 290264562Sgshapiro 290390792Sgshapiro if (IS_DLVR_RETURN(e)) 290490792Sgshapiro { 290590792Sgshapiro /* 290690792Sgshapiro ** Check whether other side can deliver e-mail 290790792Sgshapiro ** fast enough 290890792Sgshapiro */ 290990792Sgshapiro 291090792Sgshapiro if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 291190792Sgshapiro { 291290792Sgshapiro e->e_status = "5.4.7"; 291390792Sgshapiro usrerrenh(e->e_status, 291490792Sgshapiro "554 Server does not support Deliver By"); 291590792Sgshapiro rcode = EX_UNAVAILABLE; 291690792Sgshapiro goto give_up; 291790792Sgshapiro } 291890792Sgshapiro if (e->e_deliver_by > 0 && 291990792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime) < 292090792Sgshapiro mci->mci_min_by) 292190792Sgshapiro { 292290792Sgshapiro e->e_status = "5.4.7"; 292390792Sgshapiro usrerrenh(e->e_status, 292490792Sgshapiro "554 Message can't be delivered in time; %ld < %ld", 292590792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime), 292690792Sgshapiro mci->mci_min_by); 292790792Sgshapiro rcode = EX_UNAVAILABLE; 292890792Sgshapiro goto give_up; 292990792Sgshapiro } 293090792Sgshapiro } 293190792Sgshapiro 293264562Sgshapiro# if STARTTLS 293364562Sgshapiro /* first TLS then AUTH to provide a security layer */ 293471345Sgshapiro if (mci->mci_state != MCIS_CLOSED && 293571345Sgshapiro !DONE_STARTTLS(mci->mci_flags)) 293664562Sgshapiro { 293764562Sgshapiro int olderrors; 293864562Sgshapiro bool usetls; 293964562Sgshapiro bool saveQuickAbort = QuickAbort; 294064562Sgshapiro bool saveSuprErrs = SuprErrs; 294171345Sgshapiro char *host = NULL; 294264562Sgshapiro 294364562Sgshapiro rcode = EX_OK; 294464562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 294590792Sgshapiro if (usetls) 294690792Sgshapiro usetls = !iscltflgset(e, D_NOTLS); 294766494Sgshapiro 294864562Sgshapiro if (usetls) 294964562Sgshapiro { 295090792Sgshapiro host = macvalue(macid("{server_name}"), e); 295164562Sgshapiro olderrors = Errors; 295290792Sgshapiro QuickAbort = false; 295390792Sgshapiro SuprErrs = true; 2954102528Sgshapiro if (rscheck("try_tls", host, NULL, e, 2955102528Sgshapiro RSF_RMCOMM, 7, host, NOQID) != EX_OK 295664562Sgshapiro || Errors > olderrors) 295790792Sgshapiro usetls = false; 295864562Sgshapiro SuprErrs = saveSuprErrs; 295964562Sgshapiro QuickAbort = saveQuickAbort; 296064562Sgshapiro } 296164562Sgshapiro 296264562Sgshapiro if (usetls) 296364562Sgshapiro { 296464562Sgshapiro if ((rcode = starttls(m, mci, e)) == EX_OK) 296564562Sgshapiro { 296664562Sgshapiro /* start again without STARTTLS */ 296764562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 296864562Sgshapiro } 296964562Sgshapiro else 297064562Sgshapiro { 297164562Sgshapiro char *s; 297264562Sgshapiro 297364562Sgshapiro /* 297464562Sgshapiro ** TLS negotation failed, what to do? 297564562Sgshapiro ** fall back to unencrypted connection 297664562Sgshapiro ** or abort? How to decide? 297764562Sgshapiro ** set a macro and call a ruleset. 297864562Sgshapiro */ 297990792Sgshapiro 298064562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 298164562Sgshapiro switch (rcode) 298264562Sgshapiro { 298364562Sgshapiro case EX_TEMPFAIL: 298464562Sgshapiro s = "TEMP"; 298564562Sgshapiro break; 298664562Sgshapiro case EX_USAGE: 298764562Sgshapiro s = "USAGE"; 298864562Sgshapiro break; 298964562Sgshapiro case EX_PROTOCOL: 299064562Sgshapiro s = "PROTOCOL"; 299164562Sgshapiro break; 299264562Sgshapiro case EX_SOFTWARE: 299364562Sgshapiro s = "SOFTWARE"; 299464562Sgshapiro break; 299564562Sgshapiro 299664562Sgshapiro /* everything else is a failure */ 299764562Sgshapiro default: 299864562Sgshapiro s = "FAILURE"; 299964562Sgshapiro rcode = EX_TEMPFAIL; 300064562Sgshapiro } 300190792Sgshapiro macdefine(&e->e_macro, A_PERM, 300290792Sgshapiro macid("{verify}"), s); 300364562Sgshapiro } 300464562Sgshapiro } 300564562Sgshapiro else 300690792Sgshapiro macdefine(&e->e_macro, A_PERM, 300790792Sgshapiro macid("{verify}"), "NONE"); 300864562Sgshapiro olderrors = Errors; 300990792Sgshapiro QuickAbort = false; 301090792Sgshapiro SuprErrs = true; 301164562Sgshapiro 301264562Sgshapiro /* 301364562Sgshapiro ** rcode == EX_SOFTWARE is special: 301464562Sgshapiro ** the TLS negotation failed 301564562Sgshapiro ** we have to drop the connection no matter what 301664562Sgshapiro ** However, we call tls_server to give it the chance 301764562Sgshapiro ** to log the problem and return an appropriate 301864562Sgshapiro ** error code. 301964562Sgshapiro */ 302090792Sgshapiro 302164562Sgshapiro if (rscheck("tls_server", 302290792Sgshapiro macvalue(macid("{verify}"), e), 3023102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3024102528Sgshapiro host, NOQID) != EX_OK || 302564562Sgshapiro Errors > olderrors || 302664562Sgshapiro rcode == EX_SOFTWARE) 302764562Sgshapiro { 302864562Sgshapiro char enhsc[ENHSCLEN]; 302964562Sgshapiro extern char MsgBuf[]; 303064562Sgshapiro 303164562Sgshapiro if (ISSMTPCODE(MsgBuf) && 303264562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 303364562Sgshapiro { 303490792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 303590792Sgshapiro MsgBuf); 303664562Sgshapiro } 303764562Sgshapiro else 303864562Sgshapiro { 303964562Sgshapiro p = "403 4.7.0 server not authenticated."; 304090792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 304190792Sgshapiro sizeof enhsc); 304264562Sgshapiro } 304364562Sgshapiro SuprErrs = saveSuprErrs; 304464562Sgshapiro QuickAbort = saveQuickAbort; 304564562Sgshapiro 304664562Sgshapiro if (rcode == EX_SOFTWARE) 304764562Sgshapiro { 304864562Sgshapiro /* drop the connection */ 304964562Sgshapiro mci->mci_state = MCIS_QUITING; 305064562Sgshapiro if (mci->mci_in != NULL) 305164562Sgshapiro { 305290792Sgshapiro (void) sm_io_close(mci->mci_in, 305390792Sgshapiro SM_TIME_DEFAULT); 305464562Sgshapiro mci->mci_in = NULL; 305564562Sgshapiro } 305664562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 305764562Sgshapiro (void) endmailer(mci, e, pv); 305864562Sgshapiro } 305964562Sgshapiro else 306064562Sgshapiro { 306164562Sgshapiro /* abort transfer */ 306264562Sgshapiro smtpquit(m, mci, e); 306364562Sgshapiro } 306464562Sgshapiro 306571345Sgshapiro /* avoid bogus error msg */ 306671345Sgshapiro mci->mci_errno = 0; 306771345Sgshapiro 306864562Sgshapiro /* temp or permanent failure? */ 306964562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 307064562Sgshapiro : EX_UNAVAILABLE; 307190792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 307264562Sgshapiro 307364562Sgshapiro /* 307464562Sgshapiro ** hack to get the error message into 307564562Sgshapiro ** the envelope (done in giveresponse()) 307664562Sgshapiro */ 307790792Sgshapiro 307890792Sgshapiro (void) sm_strlcpy(SmtpError, p, 307990792Sgshapiro sizeof SmtpError); 308064562Sgshapiro } 308164562Sgshapiro QuickAbort = saveQuickAbort; 308264562Sgshapiro SuprErrs = saveSuprErrs; 308371345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 308471345Sgshapiro mci->mci_state != MCIS_CLOSED) 308564562Sgshapiro { 308671345Sgshapiro SET_HELO(mci->mci_flags); 308764562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 308864562Sgshapiro goto reconnect; 308964562Sgshapiro } 309064562Sgshapiro } 309164562Sgshapiro# endif /* STARTTLS */ 309264562Sgshapiro# if SASL 309364562Sgshapiro /* if other server supports authentication let's authenticate */ 309464562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 309564562Sgshapiro mci->mci_saslcap != NULL && 309690792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 309764562Sgshapiro { 309890792Sgshapiro /* Should we require some minimum authentication? */ 309990792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 310064562Sgshapiro { 310164562Sgshapiro int result; 310290792Sgshapiro sasl_ssf_t *ssf = NULL; 310364562Sgshapiro 310490792Sgshapiro /* Get security strength (features) */ 310564562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 310698121Sgshapiro# if SASL >= 20000 310798121Sgshapiro (const void **) &ssf); 310898121Sgshapiro# else /* SASL >= 20000 */ 310964562Sgshapiro (void **) &ssf); 311098121Sgshapiro# endif /* SASL >= 20000 */ 311190792Sgshapiro 311290792Sgshapiro /* XXX authid? */ 311364562Sgshapiro if (LogLevel > 9) 311464562Sgshapiro sm_syslog(LOG_INFO, NOQID, 311590792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 311664562Sgshapiro mci->mci_host, 311790792Sgshapiro macvalue(macid("{auth_type}"), e), 311890792Sgshapiro result == SASL_OK ? *ssf : 0); 311977349Sgshapiro 312064562Sgshapiro /* 312190792Sgshapiro ** Only switch to encrypted connection 312264562Sgshapiro ** if a security layer has been negotiated 312364562Sgshapiro */ 312490792Sgshapiro 312564562Sgshapiro if (result == SASL_OK && *ssf > 0) 312664562Sgshapiro { 312764562Sgshapiro /* 312890792Sgshapiro ** Convert I/O layer to use SASL. 312990792Sgshapiro ** If the call fails, the connection 313090792Sgshapiro ** is aborted. 313164562Sgshapiro */ 313290792Sgshapiro 313390792Sgshapiro if (sfdcsasl(&mci->mci_in, 313490792Sgshapiro &mci->mci_out, 313564562Sgshapiro mci->mci_conn) == 0) 313664562Sgshapiro { 313764562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 313890792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 313990792Sgshapiro MCIF_ONLY_EHLO; 314064562Sgshapiro goto reconnect; 314164562Sgshapiro } 314290792Sgshapiro syserr("AUTH TLS switch failed in client"); 314364562Sgshapiro } 314464562Sgshapiro /* else? XXX */ 314564562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 314664562Sgshapiro 314764562Sgshapiro } 314890792Sgshapiro else if (ret == EX_TEMPFAIL) 314990792Sgshapiro { 315090792Sgshapiro if (LogLevel > 8) 315190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 315290792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 315390792Sgshapiro mci->mci_host); 315490792Sgshapiro smtpquit(m, mci, e); 315590792Sgshapiro 315690792Sgshapiro /* avoid bogus error msg */ 315790792Sgshapiro mci->mci_errno = 0; 315890792Sgshapiro rcode = EX_TEMPFAIL; 3159132943Sgshapiro mci_setstat(mci, rcode, "4.3.0", p); 316090792Sgshapiro 316190792Sgshapiro /* 316290792Sgshapiro ** hack to get the error message into 316390792Sgshapiro ** the envelope (done in giveresponse()) 316490792Sgshapiro */ 316590792Sgshapiro 316690792Sgshapiro (void) sm_strlcpy(SmtpError, 316790792Sgshapiro "Temporary AUTH failure", 316890792Sgshapiro sizeof SmtpError); 316990792Sgshapiro } 317064562Sgshapiro } 317164562Sgshapiro# endif /* SASL */ 317238032Speter } 317338032Speter 317464562Sgshapiro 317538032Speterdo_transfer: 317638032Speter /* clear out per-message flags from connection structure */ 317738032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 317838032Speter 317938032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 318038032Speter !bitset(EF_DONT_MIME, e->e_flags) && 318138032Speter bitnset(M_7BITS, m->m_flags)) 318238032Speter mci->mci_flags |= MCIF_CVT8TO7; 318338032Speter 318438032Speter#if MIME7TO8 318538032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 318638032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 318738032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 318890792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 318990792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 319038032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 319138032Speter { 319238032Speter /* may want to convert 7 -> 8 */ 319338032Speter /* XXX should really parse it here -- and use a class XXX */ 319490792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 319538032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 319638032Speter mci->mci_flags |= MCIF_CVT7TO8; 319738032Speter } 319864562Sgshapiro#endif /* MIME7TO8 */ 319938032Speter 320038032Speter if (tTd(11, 1)) 320138032Speter { 320290792Sgshapiro sm_dprintf("openmailer: "); 3203132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 320438032Speter } 320538032Speter 320690792Sgshapiro#if _FFR_CLIENT_SIZE 320790792Sgshapiro /* 320890792Sgshapiro ** See if we know the maximum size and 320990792Sgshapiro ** abort if the message is too big. 321090792Sgshapiro ** 321190792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 321290792Sgshapiro */ 321390792Sgshapiro 321490792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 321590792Sgshapiro mci->mci_maxsize > 0 && 321690792Sgshapiro e->e_msgsize > mci->mci_maxsize) 321790792Sgshapiro { 321890792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 321990792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 322090792Sgshapiro e->e_status = "5.2.3"; 322190792Sgshapiro else 322290792Sgshapiro e->e_status = "5.3.4"; 322390792Sgshapiro 322490792Sgshapiro usrerrenh(e->e_status, 322590792Sgshapiro "552 Message is too large; %ld bytes max", 322690792Sgshapiro mci->mci_maxsize); 322790792Sgshapiro rcode = EX_DATAERR; 322890792Sgshapiro 322990792Sgshapiro /* Need an e_message for error */ 323090792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 323190792Sgshapiro "Message is too large; %ld bytes max", 323290792Sgshapiro mci->mci_maxsize); 323390792Sgshapiro goto give_up; 323490792Sgshapiro } 323590792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 323690792Sgshapiro 323738032Speter if (mci->mci_state != MCIS_OPEN) 323838032Speter { 323938032Speter /* couldn't open the mailer */ 324038032Speter rcode = mci->mci_exitstat; 324138032Speter errno = mci->mci_errno; 324273188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 324338032Speter if (rcode == EX_OK) 324438032Speter { 324538032Speter /* shouldn't happen */ 324664562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 324790792Sgshapiro (unsigned long) mci, rcode, errno, 324890792Sgshapiro mci->mci_state, firstsig); 3249132943Sgshapiro mci_dump_all(smioout, true); 325038032Speter rcode = EX_SOFTWARE; 325138032Speter } 325264562Sgshapiro else if (nummxhosts > hostnum) 325338032Speter { 325438032Speter /* try next MX site */ 325538032Speter goto tryhost; 325638032Speter } 325738032Speter } 325838032Speter else if (!clever) 325938032Speter { 326038032Speter /* 326138032Speter ** Format and send message. 326238032Speter */ 326338032Speter 326438032Speter putfromline(mci, e); 326543730Speter (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 326638032Speter (*e->e_putbody)(mci, e, NULL); 326738032Speter 326838032Speter /* get the exit status */ 326938032Speter rcode = endmailer(mci, e, pv); 327090792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 327173188Sgshapiro { 327273188Sgshapiro /* 327373188Sgshapiro ** Need an e_message for mailq display. 327473188Sgshapiro ** We set SmtpError as 327573188Sgshapiro */ 327673188Sgshapiro 327790792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 327890792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 327990792Sgshapiro m->m_name, m->m_mailer); 328073188Sgshapiro } 328138032Speter } 328238032Speter else 328338032Speter { 328438032Speter /* 328538032Speter ** Send the MAIL FROM: protocol 328638032Speter */ 328738032Speter 328890792Sgshapiro /* XXX this isn't pipelined... */ 328938032Speter rcode = smtpmailfrom(m, mci, e); 329038032Speter if (rcode == EX_OK) 329138032Speter { 329238032Speter register int i; 329390792Sgshapiro# if PIPELINING 329490792Sgshapiro ADDRESS *volatile pchain; 329590792Sgshapiro# endif /* PIPELINING */ 329638032Speter 329738032Speter /* send the recipient list */ 329838032Speter tobuf[0] = '\0'; 329990792Sgshapiro mci->mci_retryrcpt = false; 330090792Sgshapiro mci->mci_tolist = tobuf; 330190792Sgshapiro# if PIPELINING 330290792Sgshapiro pchain = NULL; 330390792Sgshapiro mci->mci_nextaddr = NULL; 330490792Sgshapiro# endif /* PIPELINING */ 330564562Sgshapiro 330638032Speter for (to = tochain; to != NULL; to = to->q_tchain) 330738032Speter { 330890792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 330938032Speter continue; 331064562Sgshapiro 331190792Sgshapiro /* mark recipient state as "ok so far" */ 331290792Sgshapiro to->q_state = QS_OK; 331390792Sgshapiro e->e_to = to->q_paddr; 331464562Sgshapiro# if STARTTLS 331564562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3316102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3317102528Sgshapiro mci->mci_host, e->e_id); 331864562Sgshapiro if (i != EX_OK) 331938032Speter { 332090792Sgshapiro markfailure(e, to, mci, i, false); 332190792Sgshapiro giveresponse(i, to->q_status, m, mci, 332290792Sgshapiro ctladdr, xstart, e, to); 332390792Sgshapiro if (i == EX_TEMPFAIL) 332490792Sgshapiro { 332590792Sgshapiro mci->mci_retryrcpt = true; 332690792Sgshapiro to->q_state = QS_RETRY; 332790792Sgshapiro } 332864562Sgshapiro continue; 332938032Speter } 333064562Sgshapiro# endif /* STARTTLS */ 333164562Sgshapiro 333290792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 333390792Sgshapiro# if PIPELINING 333490792Sgshapiro if (i == EX_OK && 333590792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 333664562Sgshapiro { 333790792Sgshapiro /* 333890792Sgshapiro ** Add new element to list of 333990792Sgshapiro ** recipients for pipelining. 334090792Sgshapiro */ 334190792Sgshapiro 334290792Sgshapiro to->q_pchain = NULL; 334390792Sgshapiro if (mci->mci_nextaddr == NULL) 334490792Sgshapiro mci->mci_nextaddr = to; 334590792Sgshapiro if (pchain == NULL) 334690792Sgshapiro pchain = to; 334790792Sgshapiro else 334890792Sgshapiro { 334990792Sgshapiro pchain->q_pchain = to; 335090792Sgshapiro pchain = pchain->q_pchain; 335190792Sgshapiro } 335264562Sgshapiro } 335390792Sgshapiro# endif /* PIPELINING */ 335490792Sgshapiro if (i != EX_OK) 335538032Speter { 335690792Sgshapiro markfailure(e, to, mci, i, false); 335798841Sgshapiro giveresponse(i, to->q_status, m, mci, 335890792Sgshapiro ctladdr, xstart, e, to); 335990792Sgshapiro if (i == EX_TEMPFAIL) 336090792Sgshapiro to->q_state = QS_RETRY; 336138032Speter } 336238032Speter } 336338032Speter 336490792Sgshapiro /* No recipients in list and no missing responses? */ 336590792Sgshapiro if (tobuf[0] == '\0' 336690792Sgshapiro# if PIPELINING 336790792Sgshapiro && mci->mci_nextaddr == NULL 336890792Sgshapiro# endif /* PIPELINING */ 336990792Sgshapiro ) 337038032Speter { 337138032Speter rcode = EX_OK; 337238032Speter e->e_to = NULL; 337338032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 337438032Speter smtprset(m, mci, e); 337538032Speter } 337638032Speter else 337738032Speter { 337838032Speter e->e_to = tobuf + 1; 337990792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 338038032Speter } 338138032Speter } 338264562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 338338032Speter { 338438032Speter /* try next MX site */ 338538032Speter goto tryhost; 338638032Speter } 338738032Speter } 338838032Speter#if NAMED_BIND 338938032Speter if (ConfigLevel < 2) 339038032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 339164562Sgshapiro#endif /* NAMED_BIND */ 339238032Speter 339338032Speter if (tTd(62, 1)) 339438032Speter checkfds("after delivery"); 339538032Speter 339638032Speter /* 339738032Speter ** Do final status disposal. 339838032Speter ** We check for something in tobuf for the SMTP case. 339938032Speter ** If we got a temporary failure, arrange to queue the 340038032Speter ** addressees. 340138032Speter */ 340238032Speter 340338032Speter give_up: 340438032Speter if (bitnset(M_LMTP, m->m_flags)) 340538032Speter { 340638032Speter lmtp_rcode = rcode; 340738032Speter tobuf[0] = '\0'; 340890792Sgshapiro anyok = false; 340990792Sgshapiro strsize = 0; 341038032Speter } 341138032Speter else 341238032Speter anyok = rcode == EX_OK; 341338032Speter 341438032Speter for (to = tochain; to != NULL; to = to->q_tchain) 341538032Speter { 341638032Speter /* see if address already marked */ 341764562Sgshapiro if (!QS_IS_OK(to->q_state)) 341838032Speter continue; 341938032Speter 342038032Speter /* if running LMTP, get the status for each address */ 342138032Speter if (bitnset(M_LMTP, m->m_flags)) 342238032Speter { 342338032Speter if (lmtp_rcode == EX_OK) 342438032Speter rcode = smtpgetstat(m, mci, e); 342538032Speter if (rcode == EX_OK) 342638032Speter { 342790792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 342890792Sgshapiro to->q_paddr, 342990792Sgshapiro tobufsize - strsize); 343090792Sgshapiro SM_ASSERT(strsize < tobufsize); 343190792Sgshapiro anyok = true; 343238032Speter } 343338032Speter else 343438032Speter { 343538032Speter e->e_to = to->q_paddr; 343690792Sgshapiro markfailure(e, to, mci, rcode, true); 343764562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 343890792Sgshapiro ctladdr, xstart, e, to); 343938032Speter e->e_to = tobuf + 1; 344038032Speter continue; 344138032Speter } 344238032Speter } 344338032Speter else 344438032Speter { 344538032Speter /* mark bad addresses */ 344638032Speter if (rcode != EX_OK) 344738032Speter { 344838032Speter if (goodmxfound && rcode == EX_NOHOST) 344938032Speter rcode = EX_TEMPFAIL; 345090792Sgshapiro markfailure(e, to, mci, rcode, true); 345138032Speter continue; 345238032Speter } 345338032Speter } 345438032Speter 345538032Speter /* successful delivery */ 345664562Sgshapiro to->q_state = QS_SENT; 345738032Speter to->q_statdate = curtime(); 345838032Speter e->e_nsent++; 345964562Sgshapiro 346064562Sgshapiro /* 346164562Sgshapiro ** Checkpoint the send list every few addresses 346264562Sgshapiro */ 346364562Sgshapiro 346466494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 346564562Sgshapiro { 346690792Sgshapiro queueup(e, false, false); 346764562Sgshapiro e->e_nsent = 0; 346864562Sgshapiro } 346964562Sgshapiro 347038032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 347138032Speter bitset(QPINGONSUCCESS, to->q_flags)) 347238032Speter { 347338032Speter to->q_flags |= QDELIVERED; 347438032Speter to->q_status = "2.1.5"; 347590792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 347690792Sgshapiro "%s... Successfully delivered\n", 347790792Sgshapiro to->q_paddr); 347838032Speter } 347938032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 348038032Speter bitset(QPRIMARY, to->q_flags) && 348138032Speter !bitset(MCIF_DSN, mci->mci_flags)) 348238032Speter { 348338032Speter to->q_flags |= QRELAYED; 348490792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 348590792Sgshapiro "%s... relayed; expect no further notifications\n", 348690792Sgshapiro to->q_paddr); 348738032Speter } 348890792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 348990792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 349090792Sgshapiro bitset(QPRIMARY, to->q_flags) && 349190792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 349290792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 349390792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 349490792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 349590792Sgshapiro { 349690792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 349790792Sgshapiro to->q_flags |= QBYNRELAY; 349890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 349990792Sgshapiro "%s... Deliver-by notify: relayed\n", 350090792Sgshapiro to->q_paddr); 350190792Sgshapiro } 350290792Sgshapiro else if (IS_DLVR_TRACE(e) && 350390792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 350490792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 350590792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 350690792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 350790792Sgshapiro bitset(QPRIMARY, to->q_flags)) 350890792Sgshapiro { 350990792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 351090792Sgshapiro to->q_flags |= QBYTRACE; 351190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 351290792Sgshapiro "%s... Deliver-By trace: relayed\n", 351390792Sgshapiro to->q_paddr); 351490792Sgshapiro } 351538032Speter } 351638032Speter 351738032Speter if (bitnset(M_LMTP, m->m_flags)) 351838032Speter { 351938032Speter /* 352038032Speter ** Global information applies to the last recipient only; 352138032Speter ** clear it out to avoid bogus errors. 352238032Speter */ 352338032Speter 352438032Speter rcode = EX_OK; 352538032Speter e->e_statmsg = NULL; 352638032Speter 352738032Speter /* reset the mci state for the next transaction */ 352890792Sgshapiro if (mci != NULL && 352990792Sgshapiro (mci->mci_state == MCIS_MAIL || 353090792Sgshapiro mci->mci_state == MCIS_RCPT || 353190792Sgshapiro mci->mci_state == MCIS_DATA)) 3532125820Sgshapiro { 353338032Speter mci->mci_state = MCIS_OPEN; 3534125820Sgshapiro SmtpPhase = mci->mci_phase = "idle"; 3535125820Sgshapiro sm_setproctitle(true, e, "%s: %s", CurHostName, 3536125820Sgshapiro mci->mci_phase); 3537125820Sgshapiro } 353838032Speter } 353938032Speter 354038032Speter if (tobuf[0] != '\0') 354190792Sgshapiro { 354290792Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 354390792Sgshapiro#if 0 354490792Sgshapiro /* 354590792Sgshapiro ** This code is disabled for now because I am not 354690792Sgshapiro ** sure that copying status from the first recipient 354790792Sgshapiro ** to all non-status'ed recipients is a good idea. 354890792Sgshapiro */ 354990792Sgshapiro 355090792Sgshapiro if (tochain->q_message != NULL && 355190792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 355290792Sgshapiro { 355390792Sgshapiro for (to = tochain->q_tchain; to != NULL; 355490792Sgshapiro to = to->q_tchain) 355590792Sgshapiro { 355690792Sgshapiro /* see if address already marked */ 355790792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 355890792Sgshapiro to->q_message == NULL) 355990792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 356090792Sgshapiro tochain->q_message); 356190792Sgshapiro } 356290792Sgshapiro } 356390792Sgshapiro#endif /* 0 */ 356490792Sgshapiro } 356538032Speter if (anyok) 356690792Sgshapiro markstats(e, tochain, STATS_NORMAL); 356738032Speter mci_store_persistent(mci); 356838032Speter 356990792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 357090792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 357190792Sgshapiro { 357290792Sgshapiro /* try next MX site */ 357390792Sgshapiro goto tryhost; 357490792Sgshapiro } 357590792Sgshapiro 357638032Speter /* now close the connection */ 357738032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 357838032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 357938032Speter smtpquit(m, mci, e); 358038032Speter 358190792Sgshapirocleanup: ; 358290792Sgshapiro } 358390792Sgshapiro SM_FINALLY 358490792Sgshapiro { 358590792Sgshapiro /* 358690792Sgshapiro ** Restore state and return. 358790792Sgshapiro */ 358838032Speter#if XDEBUG 358938032Speter char wbuf[MAXLINE]; 359038032Speter 359138032Speter /* make absolutely certain 0, 1, and 2 are in use */ 359290792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, 359390792Sgshapiro "%s... end of deliver(%s)", 359490792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 359590792Sgshapiro : shortenstring(e->e_to, 359690792Sgshapiro MAXSHORTSTR), 359790792Sgshapiro m->m_name); 359838032Speter checkfd012(wbuf); 359964562Sgshapiro#endif /* XDEBUG */ 360038032Speter 360190792Sgshapiro errno = 0; 360290792Sgshapiro 360390792Sgshapiro /* 360490792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 360590792Sgshapiro ** because it previously pointed to an auto buffer. 360690792Sgshapiro ** We don't do this any more, so this may be unnecessary. 360790792Sgshapiro */ 360890792Sgshapiro 360990792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 361090792Sgshapiro e->e_to = NULL; 361190792Sgshapiro } 361290792Sgshapiro SM_END_TRY 361364562Sgshapiro return rcode; 361438032Speter} 361564562Sgshapiro 361690792Sgshapiro/* 361738032Speter** MARKFAILURE -- mark a failure on a specific address. 361838032Speter** 361938032Speter** Parameters: 362038032Speter** e -- the envelope we are sending. 362138032Speter** q -- the address to mark. 362238032Speter** mci -- mailer connection information. 362338032Speter** rcode -- the code signifying the particular failure. 362464562Sgshapiro** ovr -- override an existing code? 362538032Speter** 362638032Speter** Returns: 362738032Speter** none. 362838032Speter** 362938032Speter** Side Effects: 363038032Speter** marks the address (and possibly the envelope) with the 363138032Speter** failure so that an error will be returned or 363238032Speter** the message will be queued, as appropriate. 363338032Speter*/ 363438032Speter 363590792Sgshapirovoid 363664562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 363738032Speter register ENVELOPE *e; 363838032Speter register ADDRESS *q; 363938032Speter register MCI *mci; 364038032Speter int rcode; 364164562Sgshapiro bool ovr; 364238032Speter{ 364390792Sgshapiro int save_errno = errno; 364464562Sgshapiro char *status = NULL; 364564562Sgshapiro char *rstatus = NULL; 364638032Speter 364738032Speter switch (rcode) 364838032Speter { 364938032Speter case EX_OK: 365038032Speter break; 365138032Speter 365238032Speter case EX_TEMPFAIL: 365338032Speter case EX_IOERR: 365438032Speter case EX_OSERR: 365564562Sgshapiro q->q_state = QS_QUEUEUP; 365638032Speter break; 365738032Speter 365838032Speter default: 365964562Sgshapiro q->q_state = QS_BADADDR; 366038032Speter break; 366138032Speter } 366238032Speter 366338032Speter /* find most specific error code possible */ 366438032Speter if (mci != NULL && mci->mci_status != NULL) 366538032Speter { 366690792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 366738032Speter if (mci->mci_rstatus != NULL) 366890792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 366990792Sgshapiro mci->mci_rstatus); 367038032Speter else 367164562Sgshapiro rstatus = NULL; 367238032Speter } 367338032Speter else if (e->e_status != NULL) 367438032Speter { 367564562Sgshapiro status = e->e_status; 367664562Sgshapiro rstatus = NULL; 367738032Speter } 367838032Speter else 367938032Speter { 368038032Speter switch (rcode) 368138032Speter { 368238032Speter case EX_USAGE: 368364562Sgshapiro status = "5.5.4"; 368438032Speter break; 368538032Speter 368638032Speter case EX_DATAERR: 368764562Sgshapiro status = "5.5.2"; 368838032Speter break; 368938032Speter 369038032Speter case EX_NOUSER: 369164562Sgshapiro status = "5.1.1"; 369238032Speter break; 369338032Speter 369438032Speter case EX_NOHOST: 369564562Sgshapiro status = "5.1.2"; 369638032Speter break; 369738032Speter 369838032Speter case EX_NOINPUT: 369938032Speter case EX_CANTCREAT: 370038032Speter case EX_NOPERM: 370164562Sgshapiro status = "5.3.0"; 370238032Speter break; 370338032Speter 370438032Speter case EX_UNAVAILABLE: 370538032Speter case EX_SOFTWARE: 370638032Speter case EX_OSFILE: 370738032Speter case EX_PROTOCOL: 370838032Speter case EX_CONFIG: 370964562Sgshapiro status = "5.5.0"; 371038032Speter break; 371138032Speter 371238032Speter case EX_OSERR: 371338032Speter case EX_IOERR: 371464562Sgshapiro status = "4.5.0"; 371538032Speter break; 371638032Speter 371738032Speter case EX_TEMPFAIL: 371864562Sgshapiro status = "4.2.0"; 371938032Speter break; 372038032Speter } 372138032Speter } 372238032Speter 372364562Sgshapiro /* new status? */ 372464562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 372564562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 372664562Sgshapiro { 372764562Sgshapiro q->q_status = status; 372864562Sgshapiro q->q_rstatus = rstatus; 372964562Sgshapiro } 373038032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 373138032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 373290792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 373338032Speter { 373464562Sgshapiro char buf[16]; 373538032Speter 373690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", rcode); 373790792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 373838032Speter } 373964562Sgshapiro 374064562Sgshapiro q->q_statdate = curtime(); 374164562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 374264562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 374390792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 374490792Sgshapiro 374590792Sgshapiro /* restore errno */ 374690792Sgshapiro errno = save_errno; 374738032Speter} 374890792Sgshapiro/* 374938032Speter** ENDMAILER -- Wait for mailer to terminate. 375038032Speter** 375138032Speter** We should never get fatal errors (e.g., segmentation 375238032Speter** violation), so we report those specially. For other 375338032Speter** errors, we choose a status message (into statmsg), 375438032Speter** and if it represents an error, we print it. 375538032Speter** 375638032Speter** Parameters: 375780785Sgshapiro** mci -- the mailer connection info. 375838032Speter** e -- the current envelope. 375938032Speter** pv -- the parameter vector that invoked the mailer 376038032Speter** (for error messages). 376138032Speter** 376238032Speter** Returns: 376338032Speter** exit code of mailer. 376438032Speter** 376538032Speter** Side Effects: 376638032Speter** none. 376738032Speter*/ 376838032Speter 376964562Sgshapirostatic jmp_buf EndWaitTimeout; 377064562Sgshapiro 377164562Sgshapirostatic void 3772141858Sgshapiroendwaittimeout(ignore) 3773141858Sgshapiro int ignore; 377464562Sgshapiro{ 377577349Sgshapiro /* 377677349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 377777349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 377877349Sgshapiro ** DOING. 377977349Sgshapiro */ 378077349Sgshapiro 378164562Sgshapiro errno = ETIMEDOUT; 378264562Sgshapiro longjmp(EndWaitTimeout, 1); 378364562Sgshapiro} 378464562Sgshapiro 378538032Speterint 378638032Speterendmailer(mci, e, pv) 378738032Speter register MCI *mci; 378838032Speter register ENVELOPE *e; 378938032Speter char **pv; 379038032Speter{ 379138032Speter int st; 379264562Sgshapiro int save_errno = errno; 379364562Sgshapiro char buf[MAXLINE]; 379490792Sgshapiro SM_EVENT *ev = NULL; 379538032Speter 379664562Sgshapiro 379738032Speter mci_unlock_host(mci); 379838032Speter 379977349Sgshapiro /* close output to mailer */ 380077349Sgshapiro if (mci->mci_out != NULL) 3801141858Sgshapiro { 380290792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 3803141858Sgshapiro mci->mci_out = NULL; 3804141858Sgshapiro } 380577349Sgshapiro 380677349Sgshapiro /* copy any remaining input to transcript */ 380777349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 380877349Sgshapiro e->e_xfp != NULL) 380977349Sgshapiro { 381077349Sgshapiro while (sfgets(buf, sizeof buf, mci->mci_in, 381190792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 381290792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 381377349Sgshapiro } 381477349Sgshapiro 381564562Sgshapiro#if SASL 381690792Sgshapiro /* close SASL connection */ 381764562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 381864562Sgshapiro { 381964562Sgshapiro sasl_dispose(&mci->mci_conn); 382064562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 382164562Sgshapiro } 382264562Sgshapiro#endif /* SASL */ 382364562Sgshapiro 382464562Sgshapiro#if STARTTLS 382564562Sgshapiro /* shutdown TLS */ 382664562Sgshapiro (void) endtlsclt(mci); 382764562Sgshapiro#endif /* STARTTLS */ 382864562Sgshapiro 382964562Sgshapiro /* now close the input */ 383038032Speter if (mci->mci_in != NULL) 3831141858Sgshapiro { 383290792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 3833141858Sgshapiro mci->mci_in = NULL; 3834141858Sgshapiro } 383538032Speter mci->mci_state = MCIS_CLOSED; 383638032Speter 383764562Sgshapiro errno = save_errno; 383864562Sgshapiro 383938032Speter /* in the IPC case there is nothing to wait for */ 384038032Speter if (mci->mci_pid == 0) 384164562Sgshapiro return EX_OK; 384238032Speter 384364562Sgshapiro /* put a timeout around the wait */ 384464562Sgshapiro if (mci->mci_mailer->m_wait > 0) 384564562Sgshapiro { 384664562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 384790792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 384890792Sgshapiro endwaittimeout, 0); 384964562Sgshapiro else 385064562Sgshapiro { 385166494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 385264562Sgshapiro mci->mci_mailer->m_name, 385366494Sgshapiro (long) mci->mci_mailer->m_wait); 385464562Sgshapiro return EX_TEMPFAIL; 385564562Sgshapiro } 385664562Sgshapiro } 385738032Speter 385864562Sgshapiro /* wait for the mailer process, collect status */ 385938032Speter st = waitfor(mci->mci_pid); 386064562Sgshapiro save_errno = errno; 386164562Sgshapiro if (ev != NULL) 386290792Sgshapiro sm_clrevent(ev); 386364562Sgshapiro errno = save_errno; 386464562Sgshapiro 386538032Speter if (st == -1) 386638032Speter { 386738032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 386864562Sgshapiro return EX_SOFTWARE; 386938032Speter } 387038032Speter 387138032Speter if (WIFEXITED(st)) 387238032Speter { 387338032Speter /* normal death -- return status */ 387438032Speter return (WEXITSTATUS(st)); 387538032Speter } 387638032Speter 387738032Speter /* it died a horrid death */ 387864562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 387964562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 388064562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 388164562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 388238032Speter 388338032Speter /* log the arguments */ 388438032Speter if (pv != NULL && e->e_xfp != NULL) 388538032Speter { 388638032Speter register char **av; 388738032Speter 388890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 388938032Speter for (av = pv; *av != NULL; av++) 389090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 389190792Sgshapiro *av); 389290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 389338032Speter } 389438032Speter 389538032Speter ExitStat = EX_TEMPFAIL; 389664562Sgshapiro return EX_TEMPFAIL; 389738032Speter} 389890792Sgshapiro/* 389938032Speter** GIVERESPONSE -- Interpret an error response from a mailer 390038032Speter** 390138032Speter** Parameters: 390264562Sgshapiro** status -- the status code from the mailer (high byte 390338032Speter** only; core dumps must have been taken care of 390438032Speter** already). 390564562Sgshapiro** dsn -- the DSN associated with the address, if any. 390638032Speter** m -- the mailer info for this mailer. 390738032Speter** mci -- the mailer connection info -- can be NULL if the 390838032Speter** response is given before the connection is made. 390938032Speter** ctladdr -- the controlling address for the recipient 391038032Speter** address(es). 391138032Speter** xstart -- the transaction start time, for computing 391238032Speter** transaction delays. 391338032Speter** e -- the current envelope. 391490792Sgshapiro** to -- the current recipient (NULL if none). 391538032Speter** 391638032Speter** Returns: 391738032Speter** none. 391838032Speter** 391938032Speter** Side Effects: 392038032Speter** Errors may be incremented. 392138032Speter** ExitStat may be set. 392238032Speter*/ 392338032Speter 392438032Spetervoid 392590792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 392664562Sgshapiro int status; 392764562Sgshapiro char *dsn; 392838032Speter register MAILER *m; 392938032Speter register MCI *mci; 393038032Speter ADDRESS *ctladdr; 393138032Speter time_t xstart; 393238032Speter ENVELOPE *e; 393390792Sgshapiro ADDRESS *to; 393438032Speter{ 393538032Speter register const char *statmsg; 393664562Sgshapiro int errnum = errno; 393764562Sgshapiro int off = 4; 393890792Sgshapiro bool usestat = false; 393964562Sgshapiro char dsnbuf[ENHSCLEN]; 394038032Speter char buf[MAXLINE]; 394190792Sgshapiro char *exmsg; 394238032Speter 394338032Speter if (e == NULL) 394438032Speter syserr("giveresponse: null envelope"); 394538032Speter 394638032Speter /* 394738032Speter ** Compute status message from code. 394838032Speter */ 394938032Speter 395090792Sgshapiro exmsg = sm_sysexmsg(status); 395164562Sgshapiro if (status == 0) 395238032Speter { 395364562Sgshapiro statmsg = "250 2.0.0 Sent"; 395438032Speter if (e->e_statmsg != NULL) 395538032Speter { 395690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", 395790792Sgshapiro statmsg, 395890792Sgshapiro shortenstring(e->e_statmsg, 403)); 395938032Speter statmsg = buf; 396038032Speter } 396138032Speter } 396290792Sgshapiro else if (exmsg == NULL) 396338032Speter { 396490792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 396590792Sgshapiro "554 5.3.0 unknown mailer error %d", 396690792Sgshapiro status); 396764562Sgshapiro status = EX_UNAVAILABLE; 396838032Speter statmsg = buf; 396990792Sgshapiro usestat = true; 397038032Speter } 397164562Sgshapiro else if (status == EX_TEMPFAIL) 397238032Speter { 397338032Speter char *bp = buf; 397438032Speter 397590792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 397638032Speter bp += strlen(bp); 397738032Speter#if NAMED_BIND 397838032Speter if (h_errno == TRY_AGAIN) 397990792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 398038032Speter else 398164562Sgshapiro#endif /* NAMED_BIND */ 398238032Speter { 398364562Sgshapiro if (errnum != 0) 398490792Sgshapiro statmsg = sm_errstring(errnum); 398538032Speter else 398638032Speter statmsg = SmtpError; 398738032Speter } 398838032Speter if (statmsg != NULL && statmsg[0] != '\0') 398964562Sgshapiro { 399064562Sgshapiro switch (errnum) 399164562Sgshapiro { 399264562Sgshapiro#ifdef ENETDOWN 399364562Sgshapiro case ENETDOWN: /* Network is down */ 399464562Sgshapiro#endif /* ENETDOWN */ 399564562Sgshapiro#ifdef ENETUNREACH 399664562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 399764562Sgshapiro#endif /* ENETUNREACH */ 399864562Sgshapiro#ifdef ENETRESET 399964562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 400064562Sgshapiro#endif /* ENETRESET */ 400164562Sgshapiro#ifdef ECONNABORTED 400264562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 400364562Sgshapiro#endif /* ECONNABORTED */ 400464562Sgshapiro#ifdef EHOSTDOWN 400564562Sgshapiro case EHOSTDOWN: /* Host is down */ 400664562Sgshapiro#endif /* EHOSTDOWN */ 400764562Sgshapiro#ifdef EHOSTUNREACH 400864562Sgshapiro case EHOSTUNREACH: /* No route to host */ 400964562Sgshapiro#endif /* EHOSTUNREACH */ 4010141858Sgshapiro if (mci != NULL && mci->mci_host != NULL) 401164562Sgshapiro { 401290792Sgshapiro (void) sm_strlcpyn(bp, 401390792Sgshapiro SPACELEFT(buf, bp), 401490792Sgshapiro 2, ": ", 401590792Sgshapiro mci->mci_host); 401664562Sgshapiro bp += strlen(bp); 401764562Sgshapiro } 401864562Sgshapiro break; 401964562Sgshapiro } 402090792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 402190792Sgshapiro statmsg); 402290792Sgshapiro usestat = true; 402364562Sgshapiro } 402438032Speter statmsg = buf; 402538032Speter } 402638032Speter#if NAMED_BIND 402764562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 402838032Speter { 402990792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 403090792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1, 403190792Sgshapiro statmsg); 403238032Speter statmsg = buf; 403390792Sgshapiro usestat = true; 403438032Speter } 403564562Sgshapiro#endif /* NAMED_BIND */ 403638032Speter else 403738032Speter { 403890792Sgshapiro statmsg = exmsg; 403964562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 404038032Speter { 404190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg, 404290792Sgshapiro sm_errstring(errnum)); 404338032Speter statmsg = buf; 404490792Sgshapiro usestat = true; 404538032Speter } 404694334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 404794334Sgshapiro { 404894334Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg, 404994334Sgshapiro shortenstring(e->e_statmsg, 403)); 405094334Sgshapiro statmsg = buf; 405194334Sgshapiro usestat = true; 405294334Sgshapiro } 405338032Speter } 405438032Speter 405538032Speter /* 405638032Speter ** Print the message as appropriate 405738032Speter */ 405838032Speter 405964562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 406038032Speter { 406138032Speter extern char MsgBuf[]; 406238032Speter 406364562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 406464562Sgshapiro { 406564562Sgshapiro if (dsn == NULL) 406664562Sgshapiro { 406790792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 406890792Sgshapiro "%.*s", off, statmsg + 4); 406964562Sgshapiro dsn = dsnbuf; 407064562Sgshapiro } 407164562Sgshapiro off += 5; 407264562Sgshapiro } 407364562Sgshapiro else 407464562Sgshapiro { 407564562Sgshapiro off = 4; 407664562Sgshapiro } 407764562Sgshapiro message("%s", statmsg + off); 407864562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 407990792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 408090792Sgshapiro &MsgBuf[4]); 408138032Speter } 408238032Speter else 408338032Speter { 408464562Sgshapiro char mbuf[ENHSCLEN + 4]; 408538032Speter 408638032Speter Errors++; 408764562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 408864562Sgshapiro off < sizeof mbuf - 4) 408964562Sgshapiro { 409064562Sgshapiro if (dsn == NULL) 409164562Sgshapiro { 409290792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 409390792Sgshapiro "%.*s", off, statmsg + 4); 409464562Sgshapiro dsn = dsnbuf; 409564562Sgshapiro } 409664562Sgshapiro off += 5; 409790792Sgshapiro 409890792Sgshapiro /* copy only part of statmsg to mbuf */ 409990792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 410090792Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof mbuf); 410164562Sgshapiro } 410264562Sgshapiro else 410364562Sgshapiro { 410464562Sgshapiro dsnbuf[0] = '\0'; 410590792Sgshapiro (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s", 410690792Sgshapiro statmsg); 410764562Sgshapiro off = 4; 410864562Sgshapiro } 410964562Sgshapiro usrerr(mbuf, &statmsg[off]); 411038032Speter } 411138032Speter 411238032Speter /* 411338032Speter ** Final cleanup. 411438032Speter ** Log a record of the transaction. Compute the new 411538032Speter ** ExitStat -- if we already had an error, stick with 411638032Speter ** that. 411738032Speter */ 411838032Speter 411938032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 412064562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 412164562Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 412238032Speter 412338032Speter if (tTd(11, 2)) 412490792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 412590792Sgshapiro status, 412690792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 412790792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 412890792Sgshapiro errnum); 412938032Speter 413064562Sgshapiro if (status != EX_TEMPFAIL) 413164562Sgshapiro setstat(status); 413264562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 413390792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 413490792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 413538032Speter { 413690792Sgshapiro if (!usestat && e->e_message != NULL) 413790792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 413890792Sgshapiro e->e_message); 413990792Sgshapiro else 414090792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 414190792Sgshapiro statmsg + off); 414238032Speter } 414338032Speter errno = 0; 414473188Sgshapiro SM_SET_H_ERRNO(0); 414538032Speter} 414690792Sgshapiro/* 414738032Speter** LOGDELIVERY -- log the delivery in the system log 414838032Speter** 414938032Speter** Care is taken to avoid logging lines that are too long, because 415038032Speter** some versions of syslog have an unfortunate proclivity for core 415138032Speter** dumping. This is a hack, to be sure, that is at best empirical. 415238032Speter** 415338032Speter** Parameters: 415438032Speter** m -- the mailer info. Can be NULL for initial queue. 415538032Speter** mci -- the mailer connection info -- can be NULL if the 415664562Sgshapiro** log is occurring when no connection is active. 415764562Sgshapiro** dsn -- the DSN attached to the status. 415864562Sgshapiro** status -- the message to print for the status. 415938032Speter** ctladdr -- the controlling address for the to list. 416038032Speter** xstart -- the transaction start time, used for 416138032Speter** computing transaction delay. 416238032Speter** e -- the current envelope. 416338032Speter** 416438032Speter** Returns: 416538032Speter** none 416638032Speter** 416738032Speter** Side Effects: 416838032Speter** none 416938032Speter*/ 417038032Speter 417138032Spetervoid 417264562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e) 417338032Speter MAILER *m; 417438032Speter register MCI *mci; 417564562Sgshapiro char *dsn; 417664562Sgshapiro const char *status; 417738032Speter ADDRESS *ctladdr; 417838032Speter time_t xstart; 417938032Speter register ENVELOPE *e; 418038032Speter{ 418138032Speter register char *bp; 418238032Speter register char *p; 418338032Speter int l; 418490792Sgshapiro time_t now = curtime(); 418538032Speter char buf[1024]; 418638032Speter 418764562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 418838032Speter /* ctladdr: max 106 bytes */ 418938032Speter bp = buf; 419038032Speter if (ctladdr != NULL) 419138032Speter { 419290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 419390792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 419438032Speter bp += strlen(bp); 419538032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 419638032Speter { 419790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 419890792Sgshapiro (int) ctladdr->q_uid, 419990792Sgshapiro (int) ctladdr->q_gid); 420038032Speter bp += strlen(bp); 420138032Speter } 420238032Speter } 420338032Speter 420438032Speter /* delay & xdelay: max 41 bytes */ 420590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 420690792Sgshapiro pintvl(now - e->e_ctime, true)); 420738032Speter bp += strlen(bp); 420838032Speter 420938032Speter if (xstart != (time_t) 0) 421038032Speter { 421190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 421290792Sgshapiro pintvl(now - xstart, true)); 421338032Speter bp += strlen(bp); 421438032Speter } 421538032Speter 421638032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 421738032Speter if (m != NULL) 421838032Speter { 421990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 422090792Sgshapiro m->m_name); 422138032Speter bp += strlen(bp); 422238032Speter } 422338032Speter 422464562Sgshapiro /* pri: changes with each delivery attempt */ 422590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 422690792Sgshapiro e->e_msgpriority); 422764562Sgshapiro bp += strlen(bp); 422864562Sgshapiro 422938032Speter /* relay: max 66 bytes for IPv4 addresses */ 423038032Speter if (mci != NULL && mci->mci_host != NULL) 423138032Speter { 423238032Speter extern SOCKADDR CurHostAddr; 423338032Speter 423490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 423590792Sgshapiro shortenstring(mci->mci_host, 40)); 423638032Speter bp += strlen(bp); 423738032Speter 423838032Speter if (CurHostAddr.sa.sa_family != 0) 423938032Speter { 424090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 424190792Sgshapiro anynet_ntoa(&CurHostAddr)); 424238032Speter } 424338032Speter } 424490792Sgshapiro else if (strcmp(status, "quarantined") == 0) 424590792Sgshapiro { 424690792Sgshapiro if (e->e_quarmsg != NULL) 424790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 424890792Sgshapiro ", quarantine=%s", 424990792Sgshapiro shortenstring(e->e_quarmsg, 40)); 425090792Sgshapiro } 425164562Sgshapiro else if (strcmp(status, "queued") != 0) 425238032Speter { 425338032Speter p = macvalue('h', e); 425438032Speter if (p != NULL && p[0] != '\0') 425538032Speter { 425690792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 425790792Sgshapiro ", relay=%s", shortenstring(p, 40)); 425838032Speter } 425938032Speter } 426038032Speter bp += strlen(bp); 426138032Speter 426264562Sgshapiro /* dsn */ 426364562Sgshapiro if (dsn != NULL && *dsn != '\0') 426464562Sgshapiro { 426590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 426690792Sgshapiro shortenstring(dsn, ENHSCLEN)); 426764562Sgshapiro bp += strlen(bp); 426864562Sgshapiro } 426938032Speter 427064562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 427164562Sgshapiro# if (STATLEN) < 63 427264562Sgshapiro# undef STATLEN 427364562Sgshapiro# define STATLEN 63 427464562Sgshapiro# endif /* (STATLEN) < 63 */ 427564562Sgshapiro# if (STATLEN) > 203 427664562Sgshapiro# undef STATLEN 427764562Sgshapiro# define STATLEN 203 427864562Sgshapiro# endif /* (STATLEN) > 203 */ 427964562Sgshapiro 428038032Speter /* stat: max 210 bytes */ 428138032Speter if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) 428238032Speter { 428338032Speter /* desperation move -- truncate data */ 428438032Speter bp = buf + sizeof buf - ((STATLEN) + 17); 428590792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 428638032Speter bp += 3; 428738032Speter } 428838032Speter 428990792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 429038032Speter bp += strlen(bp); 429138032Speter 429290792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 429390792Sgshapiro SPACELEFT(buf, bp)); 429438032Speter 429538032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 429638032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 429790792Sgshapiro if (l < 0) 429890792Sgshapiro l = 0; 429964562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 430090792Sgshapiro while (strlen(p) >= l) 430138032Speter { 430264562Sgshapiro register char *q; 430338032Speter 430464562Sgshapiro for (q = p + l; q > p; q--) 430564562Sgshapiro { 430664562Sgshapiro if (*q == ',') 430764562Sgshapiro break; 430864562Sgshapiro } 430964562Sgshapiro if (p == q) 431064562Sgshapiro break; 431190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 431266494Sgshapiro (int) (++q - p), p, buf); 431338032Speter p = q; 431438032Speter } 431564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 431638032Speter 431764562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 431838032Speter 431938032Speter l = SYSLOG_BUFSIZE - 85; 432090792Sgshapiro if (l < 0) 432190792Sgshapiro l = 0; 432264562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 432390792Sgshapiro while (strlen(p) >= l) 432438032Speter { 432564562Sgshapiro register char *q; 432638032Speter 432764562Sgshapiro for (q = p + l; q > p; q--) 432864562Sgshapiro { 432964562Sgshapiro if (*q == ',') 433064562Sgshapiro break; 433164562Sgshapiro } 433264562Sgshapiro if (p == q) 433364562Sgshapiro break; 433464562Sgshapiro 433590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 433666494Sgshapiro (int) (++q - p), p); 433738032Speter p = q; 433838032Speter } 433964562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 434038032Speter 434138032Speter if (ctladdr != NULL) 434238032Speter { 434338032Speter bp = buf; 434490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 434590792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 434638032Speter bp += strlen(bp); 434738032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 434838032Speter { 434990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 435090792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 435138032Speter bp += strlen(bp); 435238032Speter } 435338032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 435438032Speter } 435538032Speter bp = buf; 435690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 435790792Sgshapiro pintvl(now - e->e_ctime, true)); 435838032Speter bp += strlen(bp); 435938032Speter if (xstart != (time_t) 0) 436038032Speter { 436190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 436290792Sgshapiro pintvl(now - xstart, true)); 436338032Speter bp += strlen(bp); 436438032Speter } 436538032Speter 436638032Speter if (m != NULL) 436738032Speter { 436890792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 436990792Sgshapiro m->m_name); 437038032Speter bp += strlen(bp); 437138032Speter } 437238032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 437338032Speter 437438032Speter buf[0] = '\0'; 437538032Speter bp = buf; 437638032Speter if (mci != NULL && mci->mci_host != NULL) 437738032Speter { 437838032Speter extern SOCKADDR CurHostAddr; 437938032Speter 438090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 438190792Sgshapiro mci->mci_host); 438238032Speter bp += strlen(bp); 438338032Speter 438438032Speter if (CurHostAddr.sa.sa_family != 0) 438590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 438690792Sgshapiro " [%.100s]", 438790792Sgshapiro anynet_ntoa(&CurHostAddr)); 438838032Speter } 438990792Sgshapiro else if (strcmp(status, "quarantined") == 0) 439090792Sgshapiro { 439190792Sgshapiro if (e->e_quarmsg != NULL) 439290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 439390792Sgshapiro ", quarantine=%.100s", 439490792Sgshapiro e->e_quarmsg); 439590792Sgshapiro } 439664562Sgshapiro else if (strcmp(status, "queued") != 0) 439738032Speter { 439838032Speter p = macvalue('h', e); 439938032Speter if (p != NULL && p[0] != '\0') 440090792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p); 440138032Speter } 440238032Speter if (buf[0] != '\0') 440338032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 440438032Speter 440564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 440664562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 440738032Speter} 440890792Sgshapiro/* 440938032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 441038032Speter** 441138032Speter** This can be made an arbitrary message separator by changing $l 441238032Speter** 441338032Speter** One of the ugliest hacks seen by human eyes is contained herein: 441438032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 441538032Speter** does a well-meaning programmer such as myself have to deal with 441638032Speter** this kind of antique garbage???? 441738032Speter** 441838032Speter** Parameters: 441938032Speter** mci -- the connection information. 442038032Speter** e -- the envelope. 442138032Speter** 442238032Speter** Returns: 442338032Speter** none 442438032Speter** 442538032Speter** Side Effects: 442638032Speter** outputs some text to fp. 442738032Speter*/ 442838032Speter 442938032Spetervoid 443038032Speterputfromline(mci, e) 443138032Speter register MCI *mci; 443238032Speter ENVELOPE *e; 443338032Speter{ 443438032Speter char *template = UnixFromLine; 443538032Speter char buf[MAXLINE]; 443638032Speter char xbuf[MAXLINE]; 443738032Speter 443838032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 443938032Speter return; 444038032Speter 444138032Speter mci->mci_flags |= MCIF_INHEADER; 444238032Speter 444338032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 444438032Speter { 444538032Speter char *bang; 444638032Speter 444738032Speter expand("\201g", buf, sizeof buf, e); 444838032Speter bang = strchr(buf, '!'); 444938032Speter if (bang == NULL) 445038032Speter { 445138032Speter char *at; 445238032Speter char hname[MAXNAME]; 445338032Speter 445464562Sgshapiro /* 445542575Speter ** If we can construct a UUCP path, do so 445638032Speter */ 445738032Speter 445838032Speter at = strrchr(buf, '@'); 445938032Speter if (at == NULL) 446038032Speter { 446164562Sgshapiro expand("\201k", hname, sizeof hname, e); 446238032Speter at = hname; 446338032Speter } 446438032Speter else 446538032Speter *at++ = '\0'; 446690792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 446790792Sgshapiro "From %.800s \201d remote from %.100s\n", 446890792Sgshapiro buf, at); 446938032Speter } 447038032Speter else 447138032Speter { 447238032Speter *bang++ = '\0'; 447390792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 447490792Sgshapiro "From %.800s \201d remote from %.100s\n", 447590792Sgshapiro bang, buf); 447638032Speter template = xbuf; 447738032Speter } 447838032Speter } 447938032Speter expand(template, buf, sizeof buf, e); 448038032Speter putxline(buf, strlen(buf), mci, PXLF_HEADER); 448138032Speter} 448290792Sgshapiro/* 448338032Speter** PUTBODY -- put the body of a message. 448438032Speter** 448538032Speter** Parameters: 448638032Speter** mci -- the connection information. 448738032Speter** e -- the envelope to put out. 448838032Speter** separator -- if non-NULL, a message separator that must 448938032Speter** not be permitted in the resulting message. 449038032Speter** 449138032Speter** Returns: 449238032Speter** none. 449338032Speter** 449438032Speter** Side Effects: 449538032Speter** The message is written onto fp. 449638032Speter*/ 449738032Speter 449838032Speter/* values for output state variable */ 449938032Speter#define OS_HEAD 0 /* at beginning of line */ 450038032Speter#define OS_CR 1 /* read a carriage return */ 450138032Speter#define OS_INLINE 2 /* putting rest of line */ 450238032Speter 450338032Spetervoid 450438032Speterputbody(mci, e, separator) 450538032Speter register MCI *mci; 450638032Speter register ENVELOPE *e; 450738032Speter char *separator; 450838032Speter{ 450990792Sgshapiro bool dead = false; 451038032Speter char buf[MAXLINE]; 451190792Sgshapiro#if MIME8TO7 451242575Speter char *boundaries[MAXMIMENESTING + 1]; 451390792Sgshapiro#endif /* MIME8TO7 */ 451438032Speter 451538032Speter /* 451638032Speter ** Output the body of the message 451738032Speter */ 451838032Speter 451938032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 452038032Speter { 452190792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 452238032Speter 452390792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4524120256Sgshapiro SM_IO_RDONLY_B, NULL); 452538032Speter if (e->e_dfp == NULL) 452664562Sgshapiro { 452764562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 452864562Sgshapiro 452964562Sgshapiro if (errno == ENOENT) 453064562Sgshapiro msg++; 453164562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 453264562Sgshapiro } 453390792Sgshapiro 453438032Speter } 453538032Speter if (e->e_dfp == NULL) 453638032Speter { 453738032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 453838032Speter { 453938032Speter putline("", mci); 454038032Speter mci->mci_flags &= ~MCIF_INHEADER; 454138032Speter } 454238032Speter putline("<<< No Message Collected >>>", mci); 454338032Speter goto endofmessage; 454438032Speter } 454564562Sgshapiro 454638032Speter if (e->e_dfino == (ino_t) 0) 454738032Speter { 454838032Speter struct stat stbuf; 454938032Speter 455090792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 455190792Sgshapiro < 0) 455238032Speter e->e_dfino = -1; 455338032Speter else 455438032Speter { 455538032Speter e->e_dfdev = stbuf.st_dev; 455638032Speter e->e_dfino = stbuf.st_ino; 455738032Speter } 455838032Speter } 455938032Speter 456090792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 456164562Sgshapiro (void) bfrewind(e->e_dfp); 456264562Sgshapiro 456338032Speter#if MIME8TO7 456438032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 456538032Speter { 456638032Speter /* 456738032Speter ** Do 8 to 7 bit MIME conversion. 456838032Speter */ 456938032Speter 457038032Speter /* make sure it looks like a MIME message */ 457138032Speter if (hvalue("MIME-Version", e->e_header) == NULL) 457238032Speter putline("MIME-Version: 1.0", mci); 457338032Speter 457438032Speter if (hvalue("Content-Type", e->e_header) == NULL) 457538032Speter { 457690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 457790792Sgshapiro "Content-Type: text/plain; charset=%s", 457890792Sgshapiro defcharset(e)); 457938032Speter putline(buf, mci); 458038032Speter } 458138032Speter 458238032Speter /* now do the hard work */ 458338032Speter boundaries[0] = NULL; 458438032Speter mci->mci_flags |= MCIF_INHEADER; 458564562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); 458638032Speter } 458738032Speter# if MIME7TO8 458838032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 458938032Speter { 459064562Sgshapiro (void) mime7to8(mci, e->e_header, e); 459138032Speter } 459264562Sgshapiro# endif /* MIME7TO8 */ 459342575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 459442575Speter { 459564562Sgshapiro bool oldsuprerrs = SuprErrs; 459664562Sgshapiro 459742575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 459842575Speter boundaries[0] = NULL; 459942575Speter mci->mci_flags |= MCIF_INHEADER; 460064562Sgshapiro 460164562Sgshapiro /* 460264562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 460364562Sgshapiro ** and don't want to generate a new bounce message whose 460464562Sgshapiro ** body propagates the broken MIME. We can't just not call 460564562Sgshapiro ** mime8to7() as is done above since we need the security 460664562Sgshapiro ** checks. The best we can do is suppress the errors. 460764562Sgshapiro */ 460864562Sgshapiro 460964562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 461090792Sgshapiro SuprErrs = true; 461164562Sgshapiro 461264562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, 461364562Sgshapiro M87F_OUTER|M87F_NO8TO7); 461464562Sgshapiro 461564562Sgshapiro /* restore SuprErrs */ 461664562Sgshapiro SuprErrs = oldsuprerrs; 461742575Speter } 461838032Speter else 461964562Sgshapiro#endif /* MIME8TO7 */ 462038032Speter { 462138032Speter int ostate; 462238032Speter register char *bp; 462338032Speter register char *pbp; 462438032Speter register int c; 462538032Speter register char *xp; 462638032Speter int padc; 462738032Speter char *buflim; 462838032Speter int pos = 0; 462964562Sgshapiro char peekbuf[12]; 463038032Speter 463138032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 463238032Speter { 463338032Speter putline("", mci); 463438032Speter mci->mci_flags &= ~MCIF_INHEADER; 463538032Speter } 463638032Speter 463738032Speter /* determine end of buffer; allow for short mailer lines */ 463838032Speter buflim = &buf[sizeof buf - 1]; 463938032Speter if (mci->mci_mailer->m_linelimit > 0 && 464038032Speter mci->mci_mailer->m_linelimit < sizeof buf - 1) 464138032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 464238032Speter 464338032Speter /* copy temp file to output with mapping */ 464438032Speter ostate = OS_HEAD; 464538032Speter bp = buf; 464638032Speter pbp = peekbuf; 464790792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 464838032Speter { 464938032Speter if (pbp > peekbuf) 465038032Speter c = *--pbp; 465190792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 465290792Sgshapiro == SM_IO_EOF) 465338032Speter break; 465438032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 465538032Speter c &= 0x7f; 465638032Speter switch (ostate) 465738032Speter { 465838032Speter case OS_HEAD: 465938032Speter if (c == '\0' && 466090792Sgshapiro bitnset(M_NONULLS, 466190792Sgshapiro mci->mci_mailer->m_flags)) 466238032Speter break; 466338032Speter if (c != '\r' && c != '\n' && bp < buflim) 466438032Speter { 466538032Speter *bp++ = c; 466638032Speter break; 466738032Speter } 466838032Speter 466938032Speter /* check beginning of line for special cases */ 467038032Speter *bp = '\0'; 467138032Speter pos = 0; 467290792Sgshapiro padc = SM_IO_EOF; 467338032Speter if (buf[0] == 'F' && 467490792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 467590792Sgshapiro && strncmp(buf, "From ", 5) == 0) 467638032Speter { 467738032Speter padc = '>'; 467838032Speter } 467938032Speter if (buf[0] == '-' && buf[1] == '-' && 468038032Speter separator != NULL) 468138032Speter { 468238032Speter /* possible separator */ 468338032Speter int sl = strlen(separator); 468438032Speter 468590792Sgshapiro if (strncmp(&buf[2], separator, sl) 468690792Sgshapiro == 0) 468738032Speter padc = ' '; 468838032Speter } 468938032Speter if (buf[0] == '.' && 469038032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 469138032Speter { 469238032Speter padc = '.'; 469338032Speter } 469438032Speter 469538032Speter /* now copy out saved line */ 469638032Speter if (TrafficLogFile != NULL) 469738032Speter { 469890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 469990792Sgshapiro SM_TIME_DEFAULT, 470090792Sgshapiro "%05d >>> ", 470190792Sgshapiro (int) CurrentPid); 470290792Sgshapiro if (padc != SM_IO_EOF) 470390792Sgshapiro (void) sm_io_putc(TrafficLogFile, 470490792Sgshapiro SM_TIME_DEFAULT, 470590792Sgshapiro padc); 470638032Speter for (xp = buf; xp < bp; xp++) 470790792Sgshapiro (void) sm_io_putc(TrafficLogFile, 470890792Sgshapiro SM_TIME_DEFAULT, 470990792Sgshapiro (unsigned char) *xp); 471038032Speter if (c == '\n') 471190792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 471290792Sgshapiro SM_TIME_DEFAULT, 471390792Sgshapiro mci->mci_mailer->m_eol); 471438032Speter } 471590792Sgshapiro if (padc != SM_IO_EOF) 471638032Speter { 471790792Sgshapiro if (sm_io_putc(mci->mci_out, 471890792Sgshapiro SM_TIME_DEFAULT, padc) 471990792Sgshapiro == SM_IO_EOF) 472064562Sgshapiro { 472190792Sgshapiro dead = true; 472264562Sgshapiro continue; 472364562Sgshapiro } 472471345Sgshapiro else 472571345Sgshapiro { 472671345Sgshapiro /* record progress for DATA timeout */ 472790792Sgshapiro DataProgress = true; 472871345Sgshapiro } 472938032Speter pos++; 473038032Speter } 473138032Speter for (xp = buf; xp < bp; xp++) 473238032Speter { 473390792Sgshapiro if (sm_io_putc(mci->mci_out, 473490792Sgshapiro SM_TIME_DEFAULT, 473590792Sgshapiro (unsigned char) *xp) 473690792Sgshapiro == SM_IO_EOF) 473764562Sgshapiro { 473890792Sgshapiro dead = true; 473964562Sgshapiro break; 474064562Sgshapiro } 474171345Sgshapiro else 474271345Sgshapiro { 474371345Sgshapiro /* record progress for DATA timeout */ 474490792Sgshapiro DataProgress = true; 474571345Sgshapiro } 474638032Speter } 474764562Sgshapiro if (dead) 474864562Sgshapiro continue; 474938032Speter if (c == '\n') 475038032Speter { 475190792Sgshapiro if (sm_io_fputs(mci->mci_out, 475290792Sgshapiro SM_TIME_DEFAULT, 475390792Sgshapiro mci->mci_mailer->m_eol) 475490792Sgshapiro == SM_IO_EOF) 475564562Sgshapiro break; 475671345Sgshapiro else 475771345Sgshapiro { 475871345Sgshapiro /* record progress for DATA timeout */ 475990792Sgshapiro DataProgress = true; 476071345Sgshapiro } 476138032Speter pos = 0; 476238032Speter } 476338032Speter else 476438032Speter { 476538032Speter pos += bp - buf; 476638032Speter if (c != '\r') 4767112810Sgshapiro { 4768112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4769112810Sgshapiro sizeof(peekbuf)); 477038032Speter *pbp++ = c; 4771112810Sgshapiro } 477238032Speter } 477364562Sgshapiro 477438032Speter bp = buf; 477538032Speter 477638032Speter /* determine next state */ 477738032Speter if (c == '\n') 477838032Speter ostate = OS_HEAD; 477938032Speter else if (c == '\r') 478038032Speter ostate = OS_CR; 478138032Speter else 478238032Speter ostate = OS_INLINE; 478338032Speter continue; 478438032Speter 478538032Speter case OS_CR: 478638032Speter if (c == '\n') 478738032Speter { 478838032Speter /* got CRLF */ 478990792Sgshapiro if (sm_io_fputs(mci->mci_out, 479090792Sgshapiro SM_TIME_DEFAULT, 479190792Sgshapiro mci->mci_mailer->m_eol) 479290792Sgshapiro == SM_IO_EOF) 479364562Sgshapiro continue; 479471345Sgshapiro else 479571345Sgshapiro { 479671345Sgshapiro /* record progress for DATA timeout */ 479790792Sgshapiro DataProgress = true; 479871345Sgshapiro } 479964562Sgshapiro 480038032Speter if (TrafficLogFile != NULL) 480138032Speter { 480290792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 480390792Sgshapiro SM_TIME_DEFAULT, 480490792Sgshapiro mci->mci_mailer->m_eol); 480538032Speter } 480638032Speter ostate = OS_HEAD; 480738032Speter continue; 480838032Speter } 480938032Speter 481038032Speter /* had a naked carriage return */ 4811112810Sgshapiro SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 481238032Speter *pbp++ = c; 481338032Speter c = '\r'; 481438032Speter ostate = OS_INLINE; 481538032Speter goto putch; 481638032Speter 481738032Speter case OS_INLINE: 481838032Speter if (c == '\r') 481938032Speter { 482038032Speter ostate = OS_CR; 482138032Speter continue; 482238032Speter } 482338032Speter if (c == '\0' && 482490792Sgshapiro bitnset(M_NONULLS, 482590792Sgshapiro mci->mci_mailer->m_flags)) 482638032Speter break; 482738032Speterputch: 482838032Speter if (mci->mci_mailer->m_linelimit > 0 && 482964562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 483038032Speter c != '\n') 483138032Speter { 483264562Sgshapiro int d; 483364562Sgshapiro 483464562Sgshapiro /* check next character for EOL */ 483564562Sgshapiro if (pbp > peekbuf) 483664562Sgshapiro d = *(pbp - 1); 483790792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 483890792Sgshapiro SM_TIME_DEFAULT)) 483990792Sgshapiro != SM_IO_EOF) 4840112810Sgshapiro { 4841112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4842112810Sgshapiro sizeof(peekbuf)); 484364562Sgshapiro *pbp++ = d; 4844112810Sgshapiro } 484564562Sgshapiro 484690792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 484764562Sgshapiro { 484864562Sgshapiro if (TrafficLogFile != NULL) 484990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 485090792Sgshapiro SM_TIME_DEFAULT, 485190792Sgshapiro (unsigned char) c); 485290792Sgshapiro if (sm_io_putc(mci->mci_out, 485390792Sgshapiro SM_TIME_DEFAULT, 485490792Sgshapiro (unsigned char) c) 485590792Sgshapiro == SM_IO_EOF) 485664562Sgshapiro { 485790792Sgshapiro dead = true; 485864562Sgshapiro continue; 485964562Sgshapiro } 486071345Sgshapiro else 486171345Sgshapiro { 486271345Sgshapiro /* record progress for DATA timeout */ 486390792Sgshapiro DataProgress = true; 486471345Sgshapiro } 486564562Sgshapiro pos++; 486664562Sgshapiro continue; 486764562Sgshapiro } 486864562Sgshapiro 486990792Sgshapiro if (sm_io_putc(mci->mci_out, 487090792Sgshapiro SM_TIME_DEFAULT, '!') 487190792Sgshapiro == SM_IO_EOF || 487290792Sgshapiro sm_io_fputs(mci->mci_out, 487390792Sgshapiro SM_TIME_DEFAULT, 487490792Sgshapiro mci->mci_mailer->m_eol) 487590792Sgshapiro == SM_IO_EOF) 487664562Sgshapiro { 487790792Sgshapiro dead = true; 487864562Sgshapiro continue; 487964562Sgshapiro } 488071345Sgshapiro else 488171345Sgshapiro { 488271345Sgshapiro /* record progress for DATA timeout */ 488390792Sgshapiro DataProgress = true; 488471345Sgshapiro } 488564562Sgshapiro 488638032Speter if (TrafficLogFile != NULL) 488738032Speter { 488890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 488990792Sgshapiro SM_TIME_DEFAULT, 489090792Sgshapiro "!%s", 489190792Sgshapiro mci->mci_mailer->m_eol); 489238032Speter } 489338032Speter ostate = OS_HEAD; 4894112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4895112810Sgshapiro sizeof(peekbuf)); 489638032Speter *pbp++ = c; 489738032Speter continue; 489838032Speter } 489938032Speter if (c == '\n') 490038032Speter { 490138032Speter if (TrafficLogFile != NULL) 490290792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 490390792Sgshapiro SM_TIME_DEFAULT, 490490792Sgshapiro mci->mci_mailer->m_eol); 490590792Sgshapiro if (sm_io_fputs(mci->mci_out, 490690792Sgshapiro SM_TIME_DEFAULT, 490790792Sgshapiro mci->mci_mailer->m_eol) 490890792Sgshapiro == SM_IO_EOF) 490964562Sgshapiro continue; 491071345Sgshapiro else 491171345Sgshapiro { 491271345Sgshapiro /* record progress for DATA timeout */ 491390792Sgshapiro DataProgress = true; 491471345Sgshapiro } 491538032Speter pos = 0; 491638032Speter ostate = OS_HEAD; 491738032Speter } 491838032Speter else 491938032Speter { 492038032Speter if (TrafficLogFile != NULL) 492190792Sgshapiro (void) sm_io_putc(TrafficLogFile, 492290792Sgshapiro SM_TIME_DEFAULT, 492390792Sgshapiro (unsigned char) c); 492490792Sgshapiro if (sm_io_putc(mci->mci_out, 492590792Sgshapiro SM_TIME_DEFAULT, 492690792Sgshapiro (unsigned char) c) 492790792Sgshapiro == SM_IO_EOF) 492864562Sgshapiro { 492990792Sgshapiro dead = true; 493064562Sgshapiro continue; 493164562Sgshapiro } 493271345Sgshapiro else 493371345Sgshapiro { 493471345Sgshapiro /* record progress for DATA timeout */ 493590792Sgshapiro DataProgress = true; 493671345Sgshapiro } 493738032Speter pos++; 493838032Speter ostate = OS_INLINE; 493938032Speter } 494038032Speter break; 494138032Speter } 494238032Speter } 494338032Speter 494438032Speter /* make sure we are at the beginning of a line */ 494538032Speter if (bp > buf) 494638032Speter { 494738032Speter if (TrafficLogFile != NULL) 494838032Speter { 494938032Speter for (xp = buf; xp < bp; xp++) 495090792Sgshapiro (void) sm_io_putc(TrafficLogFile, 495190792Sgshapiro SM_TIME_DEFAULT, 495290792Sgshapiro (unsigned char) *xp); 495338032Speter } 495438032Speter for (xp = buf; xp < bp; xp++) 495538032Speter { 495690792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 495790792Sgshapiro (unsigned char) *xp) 495890792Sgshapiro == SM_IO_EOF) 495964562Sgshapiro { 496090792Sgshapiro dead = true; 496164562Sgshapiro break; 496264562Sgshapiro } 496371345Sgshapiro else 496471345Sgshapiro { 496571345Sgshapiro /* record progress for DATA timeout */ 496690792Sgshapiro DataProgress = true; 496771345Sgshapiro } 496838032Speter } 496938032Speter pos += bp - buf; 497038032Speter } 497164562Sgshapiro if (!dead && pos > 0) 497238032Speter { 497338032Speter if (TrafficLogFile != NULL) 497490792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 497590792Sgshapiro SM_TIME_DEFAULT, 497690792Sgshapiro mci->mci_mailer->m_eol); 497790792Sgshapiro (void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 497890792Sgshapiro mci->mci_mailer->m_eol); 497964562Sgshapiro 498064562Sgshapiro /* record progress for DATA timeout */ 498190792Sgshapiro DataProgress = true; 498238032Speter } 498338032Speter } 498438032Speter 498590792Sgshapiro if (sm_io_error(e->e_dfp)) 498638032Speter { 498790792Sgshapiro syserr("putbody: %s/%cf%s: read error", 498890792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 498990792Sgshapiro DATAFL_LETTER, e->e_id); 499038032Speter ExitStat = EX_IOERR; 499138032Speter } 499238032Speter 499338032Speterendofmessage: 499464562Sgshapiro /* 499564562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 499664562Sgshapiro ** the file offset in the stdio library for the 499764562Sgshapiro ** parent process will not agree with the in-kernel 499864562Sgshapiro ** file offset since the file descriptor is shared 499964562Sgshapiro ** between the processes. Therefore, it is vital 500064562Sgshapiro ** that the file always be rewound. This forces the 500164562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 500264562Sgshapiro ** offset to match. 500364562Sgshapiro */ 500464562Sgshapiro 500564562Sgshapiro if (e->e_dfp != NULL) 500664562Sgshapiro (void) bfrewind(e->e_dfp); 500764562Sgshapiro 500838032Speter /* some mailers want extra blank line at end of message */ 500964562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 501038032Speter buf[0] != '\0' && buf[0] != '\n') 501138032Speter putline("", mci); 501238032Speter 501390792Sgshapiro (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); 501490792Sgshapiro if (sm_io_error(mci->mci_out) && errno != EPIPE) 501538032Speter { 501638032Speter syserr("putbody: write error"); 501738032Speter ExitStat = EX_IOERR; 501838032Speter } 501964562Sgshapiro 502038032Speter errno = 0; 502138032Speter} 502290792Sgshapiro/* 502338032Speter** MAILFILE -- Send a message to a file. 502438032Speter** 502590792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 502690792Sgshapiro** execute bits, sendmail will try to become the owner of that file 502738032Speter** rather than the real user. Obviously, this only works if 502838032Speter** sendmail runs as root. 502938032Speter** 503038032Speter** This could be done as a subordinate mailer, except that it 503138032Speter** is used implicitly to save messages in ~/dead.letter. We 503238032Speter** view this as being sufficiently important as to include it 503338032Speter** here. For example, if the system is dying, we shouldn't have 503438032Speter** to create another process plus some pipes to save the message. 503538032Speter** 503638032Speter** Parameters: 503738032Speter** filename -- the name of the file to send to. 503838032Speter** mailer -- mailer definition for recipient -- if NULL, 503938032Speter** use FileMailer. 504038032Speter** ctladdr -- the controlling address header -- includes 504138032Speter** the userid/groupid to be when sending. 504238032Speter** sfflags -- flags for opening. 504338032Speter** e -- the current envelope. 504438032Speter** 504538032Speter** Returns: 504638032Speter** The exit code associated with the operation. 504738032Speter** 504838032Speter** Side Effects: 504938032Speter** none. 505038032Speter*/ 505138032Speter 505290792Sgshapiro# define RETURN(st) exit(st); 505390792Sgshapiro 505438032Speterstatic jmp_buf CtxMailfileTimeout; 505538032Speter 505638032Speterint 505738032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 505838032Speter char *volatile filename; 505938032Speter MAILER *volatile mailer; 506038032Speter ADDRESS *ctladdr; 506164562Sgshapiro volatile long sfflags; 506238032Speter register ENVELOPE *e; 506338032Speter{ 506490792Sgshapiro register SM_FILE_T *f; 506538032Speter register pid_t pid = -1; 506664562Sgshapiro volatile int mode; 506764562Sgshapiro int len; 506864562Sgshapiro off_t curoff; 506938032Speter bool suidwarn = geteuid() == 0; 507038032Speter char *p; 507164562Sgshapiro char *volatile realfile; 507290792Sgshapiro SM_EVENT *ev; 507398121Sgshapiro char buf[MAXPATHLEN]; 507498121Sgshapiro char targetfile[MAXPATHLEN]; 507538032Speter 507638032Speter if (tTd(11, 1)) 507738032Speter { 507890792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 5079132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 508038032Speter } 508138032Speter 508238032Speter if (mailer == NULL) 508338032Speter mailer = FileMailer; 508438032Speter 508538032Speter if (e->e_xfp != NULL) 508690792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 508738032Speter 508838032Speter /* 508938032Speter ** Special case /dev/null. This allows us to restrict file 509038032Speter ** delivery to regular files only. 509138032Speter */ 509238032Speter 509390792Sgshapiro if (sm_path_isdevnull(filename)) 509438032Speter return EX_OK; 509538032Speter 509638032Speter /* check for 8-bit available */ 509738032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 509838032Speter bitnset(M_7BITS, mailer->m_flags) && 509938032Speter (bitset(EF_DONT_MIME, e->e_flags) || 510038032Speter !(bitset(MM_MIME8BIT, MimeMode) || 510138032Speter (bitset(EF_IS_MIME, e->e_flags) && 510238032Speter bitset(MM_CVTMIME, MimeMode))))) 510338032Speter { 510438032Speter e->e_status = "5.6.3"; 510564562Sgshapiro usrerrenh(e->e_status, 510690792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 510790792Sgshapiro errno = 0; 510864562Sgshapiro return EX_DATAERR; 510938032Speter } 511038032Speter 511164562Sgshapiro /* Find the actual file */ 511264562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 511364562Sgshapiro { 511464562Sgshapiro len = strlen(SafeFileEnv); 511564562Sgshapiro 511664562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 511764562Sgshapiro filename += len; 511864562Sgshapiro 511990792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 512064562Sgshapiro { 512164562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 512264562Sgshapiro SafeFileEnv, filename); 512364562Sgshapiro return EX_CANTCREAT; 512464562Sgshapiro } 512590792Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile); 512664562Sgshapiro realfile = targetfile + len; 512764562Sgshapiro if (*filename == '/') 512864562Sgshapiro filename++; 512994334Sgshapiro if (*filename != '\0') 513094334Sgshapiro { 513194334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 513294334Sgshapiro if (targetfile[len - 1] != '/') 513394334Sgshapiro (void) sm_strlcat(targetfile, 513494334Sgshapiro "/", sizeof targetfile); 513594334Sgshapiro (void) sm_strlcat(targetfile, filename, 513694334Sgshapiro sizeof targetfile); 513794334Sgshapiro } 513864562Sgshapiro } 513964562Sgshapiro else if (mailer->m_rootdir != NULL) 514064562Sgshapiro { 514164562Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof targetfile, e); 514264562Sgshapiro len = strlen(targetfile); 514364562Sgshapiro 514464562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 514564562Sgshapiro filename += len; 514664562Sgshapiro 514790792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 514864562Sgshapiro { 514964562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 515064562Sgshapiro targetfile, filename); 515164562Sgshapiro return EX_CANTCREAT; 515264562Sgshapiro } 515364562Sgshapiro realfile = targetfile + len; 515464562Sgshapiro if (targetfile[len - 1] != '/') 515590792Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof targetfile); 515664562Sgshapiro if (*filename == '/') 515790792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 515890792Sgshapiro sizeof targetfile); 515964562Sgshapiro else 516090792Sgshapiro (void) sm_strlcat(targetfile, filename, 516190792Sgshapiro sizeof targetfile); 516264562Sgshapiro } 516364562Sgshapiro else 516464562Sgshapiro { 516590792Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof targetfile) >= 516690792Sgshapiro sizeof targetfile) 516764562Sgshapiro { 516864562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 516964562Sgshapiro return EX_CANTCREAT; 517064562Sgshapiro } 517164562Sgshapiro realfile = targetfile; 517264562Sgshapiro } 517364562Sgshapiro 517438032Speter /* 517538032Speter ** Fork so we can change permissions here. 517638032Speter ** Note that we MUST use fork, not vfork, because of 517738032Speter ** the complications of calling subroutines, etc. 517838032Speter */ 517938032Speter 518094334Sgshapiro 518194334Sgshapiro /* 518294334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 518394334Sgshapiro ** around so that the waitfor() below will get it. 518494334Sgshapiro */ 518594334Sgshapiro 518694334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 518794334Sgshapiro 518838032Speter DOFORK(fork); 518938032Speter 519038032Speter if (pid < 0) 519164562Sgshapiro return EX_OSERR; 519238032Speter else if (pid == 0) 519338032Speter { 519438032Speter /* child -- actually write to file */ 519538032Speter struct stat stb; 519638032Speter MCI mcibuf; 519742575Speter int err; 519838032Speter volatile int oflags = O_WRONLY|O_APPEND; 519938032Speter 520077349Sgshapiro /* Reset global flags */ 520177349Sgshapiro RestartRequest = NULL; 520290792Sgshapiro RestartWorkGroup = false; 520377349Sgshapiro ShutdownRequest = NULL; 520477349Sgshapiro PendingSignal = 0; 520590792Sgshapiro CurrentPid = getpid(); 520677349Sgshapiro 520738032Speter if (e->e_lockfp != NULL) 520890792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 520990792Sgshapiro NULL)); 521038032Speter 521190792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 521290792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 521390792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 521438032Speter (void) umask(OldUmask); 521538032Speter e->e_to = filename; 521638032Speter ExitStat = EX_OK; 521738032Speter 521838032Speter if (setjmp(CtxMailfileTimeout) != 0) 521938032Speter { 522090792Sgshapiro RETURN(EX_TEMPFAIL); 522138032Speter } 522238032Speter 522338032Speter if (TimeOuts.to_fileopen > 0) 522490792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 522590792Sgshapiro 0); 522638032Speter else 522738032Speter ev = NULL; 522838032Speter 522990792Sgshapiro /* check file mode to see if set-user-ID */ 523064562Sgshapiro if (stat(targetfile, &stb) < 0) 523164562Sgshapiro mode = FileMode; 523242575Speter else 523338032Speter mode = stb.st_mode; 523438032Speter 523538032Speter /* limit the errors to those actually caused in the child */ 523638032Speter errno = 0; 523738032Speter ExitStat = EX_OK; 523838032Speter 523964562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 524064562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 524164562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 524238032Speter { 524390792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 524438032Speter mode &= ~(S_ISGID|S_ISUID); 524564562Sgshapiro if (tTd(11, 20)) 524690792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 524738032Speter } 524838032Speter 524990792Sgshapiro /* we have to open the data file BEFORE setuid() */ 525038032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 525138032Speter { 525290792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 525338032Speter 525490792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5255120256Sgshapiro SM_IO_RDONLY_B, NULL); 525638032Speter if (e->e_dfp == NULL) 525738032Speter { 525838032Speter syserr("mailfile: Cannot open %s for %s from %s", 525938032Speter df, e->e_to, e->e_from.q_paddr); 526038032Speter } 526138032Speter } 526238032Speter 526338032Speter /* select a new user to run as */ 526438032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 526538032Speter { 526638032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 526738032Speter { 526838032Speter RealUserName = NULL; 5269132943Sgshapiro if (mailer->m_uid == NO_UID) 5270132943Sgshapiro RealUid = RunAsUid; 5271132943Sgshapiro else 5272132943Sgshapiro RealUid = mailer->m_uid; 527364562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 527464562Sgshapiro { 527564562Sgshapiro /* Only root can change the uid */ 527690792Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 527790792Sgshapiro (int) RunAsUid, (int) RealUid); 527890792Sgshapiro RETURN(EX_TEMPFAIL); 527964562Sgshapiro } 528038032Speter } 528138032Speter else if (bitset(S_ISUID, mode)) 528238032Speter { 528338032Speter RealUserName = NULL; 528438032Speter RealUid = stb.st_uid; 528538032Speter } 528638032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 528738032Speter { 528838032Speter if (ctladdr->q_ruser != NULL) 528938032Speter RealUserName = ctladdr->q_ruser; 529038032Speter else 529138032Speter RealUserName = ctladdr->q_user; 529238032Speter RealUid = ctladdr->q_uid; 529338032Speter } 5294132943Sgshapiro else if (mailer != NULL && mailer->m_uid != NO_UID) 529538032Speter { 529638032Speter RealUserName = DefUser; 529738032Speter RealUid = mailer->m_uid; 529838032Speter } 529938032Speter else 530038032Speter { 530138032Speter RealUserName = DefUser; 530238032Speter RealUid = DefUid; 530338032Speter } 530438032Speter 530538032Speter /* select a new group to run as */ 530638032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 530764562Sgshapiro { 5308132943Sgshapiro if (mailer->m_gid == NO_GID) 5309132943Sgshapiro RealGid = RunAsGid; 5310132943Sgshapiro else 5311132943Sgshapiro RealGid = mailer->m_gid; 531264562Sgshapiro if (RunAsUid != 0 && 531364562Sgshapiro (RealGid != getgid() || 531464562Sgshapiro RealGid != getegid())) 531564562Sgshapiro { 531664562Sgshapiro /* Only root can change the gid */ 531790792Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 531890792Sgshapiro (int) RealGid, (int) RunAsUid, 531990792Sgshapiro (int) getgid(), (int) getegid()); 532090792Sgshapiro RETURN(EX_TEMPFAIL); 532164562Sgshapiro } 532264562Sgshapiro } 532338032Speter else if (bitset(S_ISGID, mode)) 532438032Speter RealGid = stb.st_gid; 532564562Sgshapiro else if (ctladdr != NULL && 532664562Sgshapiro ctladdr->q_uid == DefUid && 532764562Sgshapiro ctladdr->q_gid == 0) 532871345Sgshapiro { 532971345Sgshapiro /* 533071345Sgshapiro ** Special case: This means it is an 533171345Sgshapiro ** alias and we should act as DefaultUser. 533271345Sgshapiro ** See alias()'s comments. 533371345Sgshapiro */ 533471345Sgshapiro 533564562Sgshapiro RealGid = DefGid; 533671345Sgshapiro RealUserName = DefUser; 533771345Sgshapiro } 533871345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 533971345Sgshapiro RealGid = ctladdr->q_gid; 5340132943Sgshapiro else if (mailer != NULL && mailer->m_gid != NO_GID) 534138032Speter RealGid = mailer->m_gid; 534238032Speter else 534338032Speter RealGid = DefGid; 534438032Speter } 534538032Speter 534638032Speter /* last ditch */ 534738032Speter if (!bitset(SFF_ROOTOK, sfflags)) 534838032Speter { 534938032Speter if (RealUid == 0) 535038032Speter RealUid = DefUid; 535138032Speter if (RealGid == 0) 535238032Speter RealGid = DefGid; 535338032Speter } 535438032Speter 535538032Speter /* set group id list (needs /etc/group access) */ 535638032Speter if (RealUserName != NULL && !DontInitGroups) 535738032Speter { 535838032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 535964562Sgshapiro { 536038032Speter syserr("mailfile: initgroups(%s, %d) failed", 536138032Speter RealUserName, RealGid); 536290792Sgshapiro RETURN(EX_TEMPFAIL); 536364562Sgshapiro } 536438032Speter } 536538032Speter else 536638032Speter { 536738032Speter GIDSET_T gidset[1]; 536838032Speter 536938032Speter gidset[0] = RealGid; 537038032Speter if (setgroups(1, gidset) == -1 && suidwarn) 537164562Sgshapiro { 537238032Speter syserr("mailfile: setgroups() failed"); 537390792Sgshapiro RETURN(EX_TEMPFAIL); 537464562Sgshapiro } 537538032Speter } 537638032Speter 537764562Sgshapiro /* 537864562Sgshapiro ** If you have a safe environment, go into it. 537964562Sgshapiro */ 538064562Sgshapiro 538164562Sgshapiro if (realfile != targetfile) 538238032Speter { 538394334Sgshapiro char save; 538494334Sgshapiro 538594334Sgshapiro save = *realfile; 538664562Sgshapiro *realfile = '\0'; 538764562Sgshapiro if (tTd(11, 20)) 538890792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 538964562Sgshapiro if (chroot(targetfile) < 0) 539038032Speter { 539138032Speter syserr("mailfile: Cannot chroot(%s)", 539264562Sgshapiro targetfile); 539390792Sgshapiro RETURN(EX_CANTCREAT); 539438032Speter } 539594334Sgshapiro *realfile = save; 539638032Speter } 539764562Sgshapiro 539864562Sgshapiro if (tTd(11, 40)) 539990792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 540064562Sgshapiro 540138032Speter if (chdir("/") < 0) 540264562Sgshapiro { 540338032Speter syserr("mailfile: cannot chdir(/)"); 540490792Sgshapiro RETURN(EX_CANTCREAT); 540564562Sgshapiro } 540638032Speter 540738032Speter /* now reset the group and user ids */ 540838032Speter endpwent(); 540990792Sgshapiro sm_mbdb_terminate(); 541038032Speter if (setgid(RealGid) < 0 && suidwarn) 541164562Sgshapiro { 541238032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 541390792Sgshapiro RETURN(EX_TEMPFAIL); 541464562Sgshapiro } 541538032Speter vendor_set_uid(RealUid); 541638032Speter if (setuid(RealUid) < 0 && suidwarn) 541764562Sgshapiro { 541838032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 541990792Sgshapiro RETURN(EX_TEMPFAIL); 542064562Sgshapiro } 542138032Speter 542264562Sgshapiro if (tTd(11, 2)) 542390792Sgshapiro sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 542464562Sgshapiro (int) getuid(), (int) geteuid(), 542564562Sgshapiro (int) getgid(), (int) getegid()); 542664562Sgshapiro 542764562Sgshapiro 542838032Speter /* move into some "safe" directory */ 542938032Speter if (mailer->m_execdir != NULL) 543038032Speter { 543138032Speter char *q; 543238032Speter 543338032Speter for (p = mailer->m_execdir; p != NULL; p = q) 543438032Speter { 543538032Speter q = strchr(p, ':'); 543638032Speter if (q != NULL) 543738032Speter *q = '\0'; 543838032Speter expand(p, buf, sizeof buf, e); 543938032Speter if (q != NULL) 544038032Speter *q++ = ':'; 544138032Speter if (tTd(11, 20)) 544290792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 544390792Sgshapiro buf); 544438032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 544538032Speter break; 544638032Speter } 544738032Speter } 544838032Speter 544964562Sgshapiro /* 545064562Sgshapiro ** Recheck the file after we have assumed the ID of the 545164562Sgshapiro ** delivery user to make sure we can deliver to it as 545264562Sgshapiro ** that user. This is necessary if sendmail is running 545364562Sgshapiro ** as root and the file is on an NFS mount which treats 545464562Sgshapiro ** root as nobody. 545564562Sgshapiro */ 545664562Sgshapiro 545764562Sgshapiro#if HASLSTAT 545864562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 545964562Sgshapiro err = stat(realfile, &stb); 546064562Sgshapiro else 546164562Sgshapiro err = lstat(realfile, &stb); 546264562Sgshapiro#else /* HASLSTAT */ 546364562Sgshapiro err = stat(realfile, &stb); 546464562Sgshapiro#endif /* HASLSTAT */ 546564562Sgshapiro 546664562Sgshapiro if (err < 0) 546764562Sgshapiro { 546864562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 546964562Sgshapiro mode = FileMode; 547064562Sgshapiro oflags |= O_CREAT|O_EXCL; 547164562Sgshapiro } 547264562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 547364562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 547464562Sgshapiro DontBlameSendmail) && 547564562Sgshapiro stb.st_nlink != 1) || 547664562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 547764562Sgshapiro exit(EX_CANTCREAT); 547864562Sgshapiro else 547964562Sgshapiro mode = stb.st_mode; 548064562Sgshapiro 548164562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 548238032Speter sfflags |= SFF_NOSLINK; 548364562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 548438032Speter sfflags |= SFF_NOHLINK; 548538032Speter sfflags &= ~SFF_OPENASROOT; 548664562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 548738032Speter if (f == NULL) 548838032Speter { 548964562Sgshapiro if (transienterror(errno)) 549064562Sgshapiro { 549164562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 549264562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 549390792Sgshapiro sm_errstring(errno)); 549490792Sgshapiro RETURN(EX_TEMPFAIL); 549564562Sgshapiro } 549664562Sgshapiro else 549764562Sgshapiro { 549864562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 549964562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 550090792Sgshapiro sm_errstring(errno)); 550190792Sgshapiro RETURN(EX_CANTCREAT); 550264562Sgshapiro } 550338032Speter } 550490792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 550590792Sgshapiro &stb)) 550638032Speter { 550764562Sgshapiro syserr("554 5.3.0 file changed after open"); 550890792Sgshapiro RETURN(EX_CANTCREAT); 550938032Speter } 551090792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 551138032Speter { 551290792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 551390792Sgshapiro sm_errstring(errno)); 551490792Sgshapiro RETURN(EX_CANTCREAT); 551538032Speter } 551638032Speter 551764562Sgshapiro curoff = stb.st_size; 551864562Sgshapiro 551938032Speter if (ev != NULL) 552090792Sgshapiro sm_clrevent(ev); 552138032Speter 552264562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 552338032Speter mcibuf.mci_mailer = mailer; 552438032Speter mcibuf.mci_out = f; 552538032Speter if (bitnset(M_7BITS, mailer->m_flags)) 552638032Speter mcibuf.mci_flags |= MCIF_7BIT; 552738032Speter 552838032Speter /* clear out per-message flags from connection structure */ 552938032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 553038032Speter 553138032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 553238032Speter !bitset(EF_DONT_MIME, e->e_flags) && 553338032Speter bitnset(M_7BITS, mailer->m_flags)) 553438032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 553538032Speter 553638032Speter#if MIME7TO8 553738032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 553838032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 553938032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 554090792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 554190792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 554238032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 554338032Speter { 554438032Speter /* may want to convert 7 -> 8 */ 554538032Speter /* XXX should really parse it here -- and use a class XXX */ 554690792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 554764562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 554838032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 554938032Speter } 555064562Sgshapiro#endif /* MIME7TO8 */ 555138032Speter 555238032Speter putfromline(&mcibuf, e); 555343730Speter (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); 555438032Speter (*e->e_putbody)(&mcibuf, e, NULL); 555538032Speter putline("\n", &mcibuf); 555690792Sgshapiro if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 555790792Sgshapiro (SuperSafe != SAFE_NO && 555890792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 555990792Sgshapiro sm_io_error(f)) 556038032Speter { 556138032Speter setstat(EX_IOERR); 556264562Sgshapiro#if !NOFTRUNCATE 556390792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 556490792Sgshapiro curoff); 556564562Sgshapiro#endif /* !NOFTRUNCATE */ 556638032Speter } 556738032Speter 556838032Speter /* reset ISUID & ISGID bits for paranoid systems */ 556938032Speter#if HASFCHMOD 557090792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 557190792Sgshapiro (MODE_T) mode); 557264562Sgshapiro#else /* HASFCHMOD */ 557364562Sgshapiro (void) chmod(filename, (MODE_T) mode); 557464562Sgshapiro#endif /* HASFCHMOD */ 557590792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 557664562Sgshapiro setstat(EX_IOERR); 557790792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 557864562Sgshapiro (void) setuid(RealUid); 557938032Speter exit(ExitStat); 558064562Sgshapiro /* NOTREACHED */ 558138032Speter } 558238032Speter else 558338032Speter { 558438032Speter /* parent -- wait for exit status */ 558538032Speter int st; 558638032Speter 558738032Speter st = waitfor(pid); 558838032Speter if (st == -1) 558938032Speter { 559038032Speter syserr("mailfile: %s: wait", mailer->m_name); 559164562Sgshapiro return EX_SOFTWARE; 559238032Speter } 559338032Speter if (WIFEXITED(st)) 559490792Sgshapiro { 559590792Sgshapiro errno = 0; 559638032Speter return (WEXITSTATUS(st)); 559790792Sgshapiro } 559838032Speter else 559938032Speter { 560038032Speter syserr("mailfile: %s: child died on signal %d", 560138032Speter mailer->m_name, st); 560264562Sgshapiro return EX_UNAVAILABLE; 560338032Speter } 560464562Sgshapiro /* NOTREACHED */ 560538032Speter } 560638032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 560738032Speter} 560838032Speter 560938032Speterstatic void 5610141858Sgshapiromailfiletimeout(ignore) 5611141858Sgshapiro int ignore; 561238032Speter{ 561377349Sgshapiro /* 561477349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 561577349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 561677349Sgshapiro ** DOING. 561777349Sgshapiro */ 561877349Sgshapiro 561977349Sgshapiro errno = ETIMEDOUT; 562038032Speter longjmp(CtxMailfileTimeout, 1); 562138032Speter} 562290792Sgshapiro/* 562338032Speter** HOSTSIGNATURE -- return the "signature" for a host. 562438032Speter** 562538032Speter** The signature describes how we are going to send this -- it 562638032Speter** can be just the hostname (for non-Internet hosts) or can be 562738032Speter** an ordered list of MX hosts. 562838032Speter** 562938032Speter** Parameters: 563038032Speter** m -- the mailer describing this host. 563138032Speter** host -- the host name. 563238032Speter** 563338032Speter** Returns: 563438032Speter** The signature for this host. 563538032Speter** 563638032Speter** Side Effects: 563738032Speter** Can tweak the symbol table. 563838032Speter*/ 563990792Sgshapiro 564064562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 564138032Speter 564290792Sgshapirochar * 564364562Sgshapirohostsignature(m, host) 564438032Speter register MAILER *m; 564538032Speter char *host; 564638032Speter{ 564738032Speter register char *p; 564838032Speter register STAB *s; 564990792Sgshapiro time_t now; 565064562Sgshapiro#if NAMED_BIND 565164562Sgshapiro char sep = ':'; 565264562Sgshapiro char prevsep = ':'; 565338032Speter int i; 565438032Speter int len; 565538032Speter int nmx; 565664562Sgshapiro int hl; 565738032Speter char *hp; 565838032Speter char *endp; 565938032Speter int oldoptions = _res.options; 566038032Speter char *mxhosts[MAXMXHOSTS + 1]; 566190792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 566264562Sgshapiro#endif /* NAMED_BIND */ 566338032Speter 566464562Sgshapiro if (tTd(17, 3)) 566590792Sgshapiro sm_dprintf("hostsignature(%s)\n", host); 566664562Sgshapiro 566738032Speter /* 566877349Sgshapiro ** If local delivery (and not remote), just return a constant. 566964562Sgshapiro */ 567064562Sgshapiro 567177349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 567290792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 567390792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 567464562Sgshapiro return "localhost"; 567564562Sgshapiro 567664562Sgshapiro /* 567738032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 567838032Speter */ 567938032Speter 568090792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 568190792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 568238032Speter { 568390792Sgshapiro /* just an ordinary mailer or deferred mode */ 568438032Speter return host; 568538032Speter } 568664562Sgshapiro#if NETUNIX 568764562Sgshapiro else if (m->m_argv[0] != NULL && 568864562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 568964562Sgshapiro { 569064562Sgshapiro /* rendezvous in the file system, no MX records */ 569164562Sgshapiro return host; 569264562Sgshapiro } 569364562Sgshapiro#endif /* NETUNIX */ 569438032Speter 569538032Speter /* 569638032Speter ** Look it up in the symbol table. 569738032Speter */ 569838032Speter 569990792Sgshapiro now = curtime(); 570038032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 570190792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 570264562Sgshapiro { 570390792Sgshapiro if (s->s_hostsig.hs_exp >= now) 570490792Sgshapiro { 570590792Sgshapiro if (tTd(17, 3)) 570690792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 570790792Sgshapiro s->s_hostsig.hs_sig); 570890792Sgshapiro return s->s_hostsig.hs_sig; 570990792Sgshapiro } 571090792Sgshapiro 571190792Sgshapiro /* signature is expired: clear it */ 571290792Sgshapiro sm_free(s->s_hostsig.hs_sig); 571390792Sgshapiro s->s_hostsig.hs_sig = NULL; 571464562Sgshapiro } 571538032Speter 571690792Sgshapiro /* set default TTL */ 571790792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 571890792Sgshapiro 571938032Speter /* 572090792Sgshapiro ** Not already there or expired -- create a signature. 572138032Speter */ 572238032Speter 572338032Speter#if NAMED_BIND 572438032Speter if (ConfigLevel < 2) 572538032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 572638032Speter 572738032Speter for (hp = host; hp != NULL; hp = endp) 572838032Speter { 572964562Sgshapiro#if NETINET6 573064562Sgshapiro if (*hp == '[') 573164562Sgshapiro { 573264562Sgshapiro endp = strchr(hp + 1, ']'); 573364562Sgshapiro if (endp != NULL) 573464562Sgshapiro endp = strpbrk(endp + 1, ":,"); 573564562Sgshapiro } 573664562Sgshapiro else 573764562Sgshapiro endp = strpbrk(hp, ":,"); 573864562Sgshapiro#else /* NETINET6 */ 573964562Sgshapiro endp = strpbrk(hp, ":,"); 574064562Sgshapiro#endif /* NETINET6 */ 574138032Speter if (endp != NULL) 574264562Sgshapiro { 574364562Sgshapiro sep = *endp; 574438032Speter *endp = '\0'; 574564562Sgshapiro } 574638032Speter 574738032Speter if (bitnset(M_NOMX, m->m_flags)) 574838032Speter { 574938032Speter /* skip MX lookups */ 575038032Speter nmx = 1; 575138032Speter mxhosts[0] = hp; 575238032Speter } 575338032Speter else 575438032Speter { 575538032Speter auto int rcode; 575690792Sgshapiro int ttl; 575738032Speter 575890792Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 575990792Sgshapiro &ttl); 576038032Speter if (nmx <= 0) 576138032Speter { 576280785Sgshapiro int save_errno; 576338032Speter register MCI *mci; 576438032Speter 576538032Speter /* update the connection info for this host */ 576680785Sgshapiro save_errno = errno; 576738032Speter mci = mci_get(hp, m); 576880785Sgshapiro mci->mci_errno = save_errno; 576938032Speter mci->mci_herrno = h_errno; 577071345Sgshapiro mci->mci_lastuse = now; 577164562Sgshapiro if (rcode == EX_NOHOST) 577264562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 577390792Sgshapiro "550 Host unknown"); 577464562Sgshapiro else 577564562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 577638032Speter 577738032Speter /* use the original host name as signature */ 577838032Speter nmx = 1; 577938032Speter mxhosts[0] = hp; 578038032Speter } 578164562Sgshapiro if (tTd(17, 3)) 578290792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 578390792Sgshapiro nmx, mxhosts[0]); 578490792Sgshapiro 578590792Sgshapiro /* 578690792Sgshapiro ** Set new TTL: we use only one! 578790792Sgshapiro ** We could try to use the minimum instead. 578890792Sgshapiro */ 578990792Sgshapiro 579090792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 579138032Speter } 579238032Speter 579338032Speter len = 0; 579438032Speter for (i = 0; i < nmx; i++) 579538032Speter len += strlen(mxhosts[i]) + 1; 579690792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 579790792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 579890792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 579964562Sgshapiro { 580064562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 580164562Sgshapiro host, MAXHOSTSIGNATURE, len); 580264562Sgshapiro len = MAXHOSTSIGNATURE; 580364562Sgshapiro } 580490792Sgshapiro p = sm_pmalloc_x(len); 580590792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 580638032Speter { 580790792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 580890792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 580990792Sgshapiro s->s_hostsig.hs_sig = p; 581064562Sgshapiro hl = strlen(p); 581164562Sgshapiro p += hl; 581264562Sgshapiro *p++ = prevsep; 581364562Sgshapiro len -= hl + 1; 581438032Speter } 581538032Speter else 581690792Sgshapiro s->s_hostsig.hs_sig = p; 581738032Speter for (i = 0; i < nmx; i++) 581838032Speter { 581964562Sgshapiro hl = strlen(mxhosts[i]); 582064562Sgshapiro if (len - 1 < hl || len <= 1) 582164562Sgshapiro { 582264562Sgshapiro /* force to drop out of outer loop */ 582364562Sgshapiro len = -1; 582464562Sgshapiro break; 582564562Sgshapiro } 582638032Speter if (i != 0) 582764562Sgshapiro { 582864562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 582964562Sgshapiro *p++ = ','; 583064562Sgshapiro else 583164562Sgshapiro *p++ = ':'; 583264562Sgshapiro len--; 583364562Sgshapiro } 583490792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 583564562Sgshapiro p += hl; 583664562Sgshapiro len -= hl; 583738032Speter } 583864562Sgshapiro 583964562Sgshapiro /* 584064562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 584164562Sgshapiro ** because we won't have more space for further hosts 584264562Sgshapiro ** anyway (separated by : in the .cf file). 584364562Sgshapiro */ 584464562Sgshapiro 584564562Sgshapiro if (len < 0) 584664562Sgshapiro break; 584738032Speter if (endp != NULL) 584864562Sgshapiro *endp++ = sep; 584964562Sgshapiro prevsep = sep; 585038032Speter } 585190792Sgshapiro makelower(s->s_hostsig.hs_sig); 585238032Speter if (ConfigLevel < 2) 585338032Speter _res.options = oldoptions; 585464562Sgshapiro#else /* NAMED_BIND */ 585538032Speter /* not using BIND -- the signature is just the host name */ 585690792Sgshapiro /* 585790792Sgshapiro ** 'host' points to storage that will be freed after we are 585890792Sgshapiro ** done processing the current envelope, so we copy it. 585990792Sgshapiro */ 586090792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 586164562Sgshapiro#endif /* NAMED_BIND */ 586238032Speter if (tTd(17, 1)) 586390792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 586490792Sgshapiro return s->s_hostsig.hs_sig; 586538032Speter} 586690792Sgshapiro/* 586764562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 586864562Sgshapiro** 586964562Sgshapiro** The signature describes how we are going to send this -- it 587064562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 587164562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 587264562Sgshapiro** MX preference values. 587364562Sgshapiro** 587464562Sgshapiro** Parameters: 587564562Sgshapiro** sig -- the host signature. 587664562Sgshapiro** mxhosts -- array to populate. 587790792Sgshapiro** mailer -- mailer. 587864562Sgshapiro** 587964562Sgshapiro** Returns: 588064562Sgshapiro** The number of hosts inserted into mxhosts array. 588164562Sgshapiro** 588264562Sgshapiro** Side Effects: 588364562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 588464562Sgshapiro*/ 588564562Sgshapiro 588664562Sgshapirostatic int 588764562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 588864562Sgshapiro char *sig; 588964562Sgshapiro char **mxhosts; 589064562Sgshapiro MAILER *mailer; 589164562Sgshapiro{ 589290792Sgshapiro unsigned short curpref = 0; 589390792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 589464562Sgshapiro char *hp, *endp; 589590792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 589664562Sgshapiro long rndm[MAXMXHOSTS]; 589764562Sgshapiro 589864562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 589964562Sgshapiro { 590064562Sgshapiro char sep = ':'; 590164562Sgshapiro 590264562Sgshapiro#if NETINET6 590364562Sgshapiro if (*hp == '[') 590464562Sgshapiro { 590564562Sgshapiro endp = strchr(hp + 1, ']'); 590664562Sgshapiro if (endp != NULL) 590764562Sgshapiro endp = strpbrk(endp + 1, ":,"); 590864562Sgshapiro } 590964562Sgshapiro else 591064562Sgshapiro endp = strpbrk(hp, ":,"); 591164562Sgshapiro#else /* NETINET6 */ 591264562Sgshapiro endp = strpbrk(hp, ":,"); 591364562Sgshapiro#endif /* NETINET6 */ 591464562Sgshapiro if (endp != NULL) 591564562Sgshapiro { 591664562Sgshapiro sep = *endp; 591764562Sgshapiro *endp = '\0'; 591864562Sgshapiro } 591964562Sgshapiro 592064562Sgshapiro mxhosts[nmx] = hp; 592164562Sgshapiro prefer[nmx] = curpref; 592264562Sgshapiro if (mci_match(hp, mailer)) 592364562Sgshapiro rndm[nmx] = 0; 592464562Sgshapiro else 592564562Sgshapiro rndm[nmx] = get_random(); 592664562Sgshapiro 592764562Sgshapiro if (endp != NULL) 592864562Sgshapiro { 592964562Sgshapiro /* 593064562Sgshapiro ** Since we don't have the original MX prefs, 593164562Sgshapiro ** make our own. If the separator is a ':', that 593264562Sgshapiro ** means the preference for the next host will be 593364562Sgshapiro ** higher than this one, so simply increment curpref. 593464562Sgshapiro */ 593564562Sgshapiro 593664562Sgshapiro if (sep == ':') 593764562Sgshapiro curpref++; 593864562Sgshapiro 593964562Sgshapiro *endp++ = sep; 594064562Sgshapiro } 594164562Sgshapiro if (++nmx >= MAXMXHOSTS) 594264562Sgshapiro break; 594364562Sgshapiro } 594464562Sgshapiro 594564562Sgshapiro /* sort the records using the random factor for equal preferences */ 594664562Sgshapiro for (i = 0; i < nmx; i++) 594764562Sgshapiro { 594864562Sgshapiro for (j = i + 1; j < nmx; j++) 594964562Sgshapiro { 595064562Sgshapiro /* 595164562Sgshapiro ** List is already sorted by MX preference, only 595264562Sgshapiro ** need to look for equal preference MX records 595364562Sgshapiro */ 595464562Sgshapiro 595564562Sgshapiro if (prefer[i] < prefer[j]) 595664562Sgshapiro break; 595764562Sgshapiro 595864562Sgshapiro if (prefer[i] > prefer[j] || 595964562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 596064562Sgshapiro { 596190792Sgshapiro register unsigned short tempp; 596264562Sgshapiro register long tempr; 596364562Sgshapiro register char *temp1; 596464562Sgshapiro 596564562Sgshapiro tempp = prefer[i]; 596664562Sgshapiro prefer[i] = prefer[j]; 596764562Sgshapiro prefer[j] = tempp; 596864562Sgshapiro temp1 = mxhosts[i]; 596964562Sgshapiro mxhosts[i] = mxhosts[j]; 597064562Sgshapiro mxhosts[j] = temp1; 597164562Sgshapiro tempr = rndm[i]; 597264562Sgshapiro rndm[i] = rndm[j]; 597364562Sgshapiro rndm[j] = tempr; 597464562Sgshapiro } 597564562Sgshapiro } 597664562Sgshapiro } 597764562Sgshapiro return nmx; 597864562Sgshapiro} 597964562Sgshapiro 598064562Sgshapiro# if STARTTLS 598164562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 598290792Sgshapirostatic bool tls_ok_clt = true; 598364562Sgshapiro 598490792Sgshapiro/* 598590792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 598664562Sgshapiro** 598764562Sgshapiro** Parameters: 598890792Sgshapiro** tls_ok -- should tls be done? 598990792Sgshapiro** 599090792Sgshapiro** Returns: 599164562Sgshapiro** none. 599264562Sgshapiro** 599390792Sgshapiro** Side Effects: 599490792Sgshapiro** sets tls_ok_clt (static variable in this module) 599590792Sgshapiro*/ 599690792Sgshapiro 599790792Sgshapirovoid 599890792Sgshapirosetclttls(tls_ok) 599990792Sgshapiro bool tls_ok; 600090792Sgshapiro{ 600190792Sgshapiro tls_ok_clt = tls_ok; 600290792Sgshapiro return; 600390792Sgshapiro} 600490792Sgshapiro/* 600590792Sgshapiro** INITCLTTLS -- initialize client side TLS 600690792Sgshapiro** 600790792Sgshapiro** Parameters: 600890792Sgshapiro** tls_ok -- should tls initialization be done? 600990792Sgshapiro** 601064562Sgshapiro** Returns: 601164562Sgshapiro** succeeded? 601290792Sgshapiro** 601390792Sgshapiro** Side Effects: 601490792Sgshapiro** sets tls_ok_clt (static variable in this module) 601564562Sgshapiro*/ 601664562Sgshapiro 601764562Sgshapirobool 601890792Sgshapiroinitclttls(tls_ok) 601990792Sgshapiro bool tls_ok; 602064562Sgshapiro{ 602190792Sgshapiro if (!tls_ok_clt) 602290792Sgshapiro return false; 602390792Sgshapiro tls_ok_clt = tls_ok; 602490792Sgshapiro if (!tls_ok_clt) 602590792Sgshapiro return false; 602664562Sgshapiro if (clt_ctx != NULL) 602790792Sgshapiro return true; /* already done */ 6028110560Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile, 6029110560Sgshapiro CltKeyFile, CACertPath, CACertFile, DHParams); 603090792Sgshapiro return tls_ok_clt; 603164562Sgshapiro} 603264562Sgshapiro 603390792Sgshapiro/* 603464562Sgshapiro** STARTTLS -- try to start secure connection (client side) 603564562Sgshapiro** 603664562Sgshapiro** Parameters: 603764562Sgshapiro** m -- the mailer. 603864562Sgshapiro** mci -- the mailer connection info. 603964562Sgshapiro** e -- the envelope. 604064562Sgshapiro** 604164562Sgshapiro** Returns: 604264562Sgshapiro** success? 604364562Sgshapiro** (maybe this should be some other code than EX_ 604464562Sgshapiro** that denotes which stage failed.) 604564562Sgshapiro*/ 604664562Sgshapiro 604764562Sgshapirostatic int 604864562Sgshapirostarttls(m, mci, e) 604964562Sgshapiro MAILER *m; 605064562Sgshapiro MCI *mci; 605164562Sgshapiro ENVELOPE *e; 605264562Sgshapiro{ 605364562Sgshapiro int smtpresult; 605466494Sgshapiro int result = 0; 605566494Sgshapiro int rfd, wfd; 605664562Sgshapiro SSL *clt_ssl = NULL; 605790792Sgshapiro time_t tlsstart; 605864562Sgshapiro 605990792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 606066494Sgshapiro return EX_TEMPFAIL; 606164562Sgshapiro smtpmessage("STARTTLS", m, mci); 606264562Sgshapiro 606364562Sgshapiro /* get the reply */ 6064132943Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 6065132943Sgshapiro XS_STARTTLS); 606664562Sgshapiro 606764562Sgshapiro /* check return code from server */ 606864562Sgshapiro if (smtpresult == 454) 606964562Sgshapiro return EX_TEMPFAIL; 607064562Sgshapiro if (smtpresult == 501) 607164562Sgshapiro return EX_USAGE; 607264562Sgshapiro if (smtpresult == -1) 607364562Sgshapiro return smtpresult; 607464562Sgshapiro if (smtpresult != 220) 607564562Sgshapiro return EX_PROTOCOL; 607664562Sgshapiro 607764562Sgshapiro if (LogLevel > 13) 607890792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 607964562Sgshapiro 608064562Sgshapiro /* start connection */ 608164562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 608264562Sgshapiro { 608364562Sgshapiro if (LogLevel > 5) 608464562Sgshapiro { 608590792Sgshapiro sm_syslog(LOG_ERR, NOQID, 608690792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 608764562Sgshapiro if (LogLevel > 9) 608890792Sgshapiro tlslogerr("client"); 608964562Sgshapiro } 609064562Sgshapiro return EX_SOFTWARE; 609164562Sgshapiro } 609264562Sgshapiro 609390792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 609490792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 609566494Sgshapiro 609664562Sgshapiro /* SSL_clear(clt_ssl); ? */ 609766494Sgshapiro if (rfd < 0 || wfd < 0 || 609890792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 609990792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 610064562Sgshapiro { 610164562Sgshapiro if (LogLevel > 5) 610264562Sgshapiro { 610390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 610490792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 610590792Sgshapiro result); 610664562Sgshapiro if (LogLevel > 9) 610790792Sgshapiro tlslogerr("client"); 610864562Sgshapiro } 610964562Sgshapiro return EX_SOFTWARE; 611064562Sgshapiro } 611164562Sgshapiro SSL_set_connect_state(clt_ssl); 611290792Sgshapiro tlsstart = curtime(); 611390792Sgshapiro 611490792Sgshapirossl_retry: 611564562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 611664562Sgshapiro { 611764562Sgshapiro int i; 611890792Sgshapiro bool timedout; 611990792Sgshapiro time_t left; 612090792Sgshapiro time_t now = curtime(); 612190792Sgshapiro struct timeval tv; 612264562Sgshapiro 612364562Sgshapiro /* what to do in this case? */ 612464562Sgshapiro i = SSL_get_error(clt_ssl, result); 612590792Sgshapiro 612690792Sgshapiro /* 612790792Sgshapiro ** For SSL_ERROR_WANT_{READ,WRITE}: 612890792Sgshapiro ** There is not a complete SSL record available yet 612990792Sgshapiro ** or there is only a partial SSL record removed from 613090792Sgshapiro ** the network (socket) buffer into the SSL buffer. 613190792Sgshapiro ** The SSL_connect will only succeed when a full 613290792Sgshapiro ** SSL record is available (assuming a "real" error 613390792Sgshapiro ** doesn't happen). To handle when a "real" error 613490792Sgshapiro ** does happen the select is set for exceptions too. 613590792Sgshapiro ** The connection may be re-negotiated during this time 613690792Sgshapiro ** so both read and write "want errors" need to be handled. 613790792Sgshapiro ** A select() exception loops back so that a proper SSL 613890792Sgshapiro ** error message can be gotten. 613990792Sgshapiro */ 614090792Sgshapiro 614190792Sgshapiro left = TimeOuts.to_starttls - (now - tlsstart); 614290792Sgshapiro timedout = left <= 0; 614390792Sgshapiro if (!timedout) 614490792Sgshapiro { 614590792Sgshapiro tv.tv_sec = left; 614690792Sgshapiro tv.tv_usec = 0; 614790792Sgshapiro } 614890792Sgshapiro 6149110560Sgshapiro if (!timedout && FD_SETSIZE > 0 && 6150110560Sgshapiro (rfd >= FD_SETSIZE || 6151110560Sgshapiro (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE))) 6152110560Sgshapiro { 6153110560Sgshapiro if (LogLevel > 5) 6154110560Sgshapiro { 6155110560Sgshapiro sm_syslog(LOG_ERR, e->e_id, 6156110560Sgshapiro "STARTTLS=client, error: fd %d/%d too large", 6157110560Sgshapiro rfd, wfd); 6158110560Sgshapiro if (LogLevel > 8) 6159110560Sgshapiro tlslogerr("client"); 6160110560Sgshapiro } 6161110560Sgshapiro errno = EINVAL; 6162110560Sgshapiro goto tlsfail; 6163110560Sgshapiro } 616490792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_READ) 616590792Sgshapiro { 616690792Sgshapiro fd_set ssl_maskr, ssl_maskx; 616790792Sgshapiro 616890792Sgshapiro FD_ZERO(&ssl_maskr); 616990792Sgshapiro FD_SET(rfd, &ssl_maskr); 617090792Sgshapiro FD_ZERO(&ssl_maskx); 617190792Sgshapiro FD_SET(rfd, &ssl_maskx); 617290792Sgshapiro if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv) 617390792Sgshapiro > 0) 617490792Sgshapiro goto ssl_retry; 617590792Sgshapiro } 617690792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_WRITE) 617790792Sgshapiro { 617890792Sgshapiro fd_set ssl_maskw, ssl_maskx; 617990792Sgshapiro 618090792Sgshapiro FD_ZERO(&ssl_maskw); 618190792Sgshapiro FD_SET(wfd, &ssl_maskw); 618290792Sgshapiro FD_ZERO(&ssl_maskx); 618390792Sgshapiro FD_SET(rfd, &ssl_maskx); 618490792Sgshapiro if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv) 618590792Sgshapiro > 0) 618690792Sgshapiro goto ssl_retry; 618790792Sgshapiro } 618864562Sgshapiro if (LogLevel > 5) 618964562Sgshapiro { 619064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 6191111823Sgshapiro "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d, errno=%d", 6192111823Sgshapiro result, i, (int) timedout, errno); 619390792Sgshapiro if (LogLevel > 8) 619490792Sgshapiro tlslogerr("client"); 619564562Sgshapiro } 6196110560Sgshapirotlsfail: 619764562Sgshapiro SSL_free(clt_ssl); 619864562Sgshapiro clt_ssl = NULL; 619964562Sgshapiro return EX_SOFTWARE; 620064562Sgshapiro } 620164562Sgshapiro mci->mci_ssl = clt_ssl; 620290792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 620390792Sgshapiro &mci->mci_macro, true); 620464562Sgshapiro 620590792Sgshapiro /* switch to use TLS... */ 620664562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 620764562Sgshapiro return EX_OK; 620864562Sgshapiro 620964562Sgshapiro /* failure */ 621064562Sgshapiro SSL_free(clt_ssl); 621164562Sgshapiro clt_ssl = NULL; 621264562Sgshapiro return EX_SOFTWARE; 621364562Sgshapiro} 621490792Sgshapiro/* 621564562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 621664562Sgshapiro** 621764562Sgshapiro** Parameters: 621864562Sgshapiro** mci -- the mailer connection info. 621964562Sgshapiro** 622064562Sgshapiro** Returns: 622164562Sgshapiro** success? 622264562Sgshapiro*/ 622390792Sgshapiro 622490792Sgshapirostatic int 622564562Sgshapiroendtlsclt(mci) 622664562Sgshapiro MCI *mci; 622764562Sgshapiro{ 622864562Sgshapiro int r; 622964562Sgshapiro 623064562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 623164562Sgshapiro return EX_OK; 623264562Sgshapiro r = endtls(mci->mci_ssl, "client"); 623364562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 623464562Sgshapiro return r; 623564562Sgshapiro} 623690792Sgshapiro# endif /* STARTTLS */ 623790792Sgshapiro# if STARTTLS || SASL 623890792Sgshapiro/* 623990792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 624064562Sgshapiro** 624164562Sgshapiro** Parameters: 624290792Sgshapiro** e -- envelope. 624390792Sgshapiro** flag -- flag to check in {client_flags} 624464562Sgshapiro** 624564562Sgshapiro** Returns: 624690792Sgshapiro** true iff flag is set. 624764562Sgshapiro*/ 624864562Sgshapiro 624990792Sgshapirostatic bool 625090792Sgshapiroiscltflgset(e, flag) 625190792Sgshapiro ENVELOPE *e; 625290792Sgshapiro int flag; 625364562Sgshapiro{ 625490792Sgshapiro char *p; 625573188Sgshapiro 625690792Sgshapiro p = macvalue(macid("{client_flags}"), e); 625790792Sgshapiro if (p == NULL) 625890792Sgshapiro return false; 625990792Sgshapiro for (; *p != '\0'; p++) 626064562Sgshapiro { 626190792Sgshapiro /* look for just this one flag */ 626290792Sgshapiro if (*p == (char) flag) 626390792Sgshapiro return true; 626464562Sgshapiro } 626590792Sgshapiro return false; 626664562Sgshapiro} 626790792Sgshapiro# endif /* STARTTLS || SASL */ 6268