deliver.c revision 157001
138032Speter/* 2157001Sgshapiro * Copyright (c) 1998-2006 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> 15157001Sgshapiro#include <sm/time.h> 1638032Speter 17157001SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.1000 2006/03/02 01:37:39 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 /* 1204157001Sgshapiro ** If the host was not found or a temporary failure occurred 1205157001Sgshapiro ** and a FallbackSmartHost is defined (and we have not yet 1206157001Sgshapiro ** tried it), then make one last try with it as the host. 1207132943Sgshapiro */ 1208132943Sgshapiro 1209157001Sgshapiro if ((status == EX_NOHOST || status == EX_TEMPFAIL) && 1210157001Sgshapiro FallbackSmartHost != NULL && !*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; 2995157001Sgshapiro case EX_UNAVAILABLE: 2996157001Sgshapiro s = "NONE"; 2997157001Sgshapiro break; 299864562Sgshapiro 299964562Sgshapiro /* everything else is a failure */ 300064562Sgshapiro default: 300164562Sgshapiro s = "FAILURE"; 300264562Sgshapiro rcode = EX_TEMPFAIL; 300364562Sgshapiro } 300490792Sgshapiro macdefine(&e->e_macro, A_PERM, 300590792Sgshapiro macid("{verify}"), s); 300664562Sgshapiro } 300764562Sgshapiro } 300864562Sgshapiro else 300990792Sgshapiro macdefine(&e->e_macro, A_PERM, 301090792Sgshapiro macid("{verify}"), "NONE"); 301164562Sgshapiro olderrors = Errors; 301290792Sgshapiro QuickAbort = false; 301390792Sgshapiro SuprErrs = true; 301464562Sgshapiro 301564562Sgshapiro /* 301664562Sgshapiro ** rcode == EX_SOFTWARE is special: 301764562Sgshapiro ** the TLS negotation failed 301864562Sgshapiro ** we have to drop the connection no matter what 301964562Sgshapiro ** However, we call tls_server to give it the chance 302064562Sgshapiro ** to log the problem and return an appropriate 302164562Sgshapiro ** error code. 302264562Sgshapiro */ 302390792Sgshapiro 302464562Sgshapiro if (rscheck("tls_server", 302590792Sgshapiro macvalue(macid("{verify}"), e), 3026102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3027102528Sgshapiro host, NOQID) != EX_OK || 302864562Sgshapiro Errors > olderrors || 302964562Sgshapiro rcode == EX_SOFTWARE) 303064562Sgshapiro { 303164562Sgshapiro char enhsc[ENHSCLEN]; 303264562Sgshapiro extern char MsgBuf[]; 303364562Sgshapiro 303464562Sgshapiro if (ISSMTPCODE(MsgBuf) && 303564562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 303664562Sgshapiro { 303790792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 303890792Sgshapiro MsgBuf); 303964562Sgshapiro } 304064562Sgshapiro else 304164562Sgshapiro { 304264562Sgshapiro p = "403 4.7.0 server not authenticated."; 304390792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 304490792Sgshapiro sizeof enhsc); 304564562Sgshapiro } 304664562Sgshapiro SuprErrs = saveSuprErrs; 304764562Sgshapiro QuickAbort = saveQuickAbort; 304864562Sgshapiro 304964562Sgshapiro if (rcode == EX_SOFTWARE) 305064562Sgshapiro { 305164562Sgshapiro /* drop the connection */ 305264562Sgshapiro mci->mci_state = MCIS_QUITING; 305364562Sgshapiro if (mci->mci_in != NULL) 305464562Sgshapiro { 305590792Sgshapiro (void) sm_io_close(mci->mci_in, 305690792Sgshapiro SM_TIME_DEFAULT); 305764562Sgshapiro mci->mci_in = NULL; 305864562Sgshapiro } 305964562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 306064562Sgshapiro (void) endmailer(mci, e, pv); 306164562Sgshapiro } 306264562Sgshapiro else 306364562Sgshapiro { 306464562Sgshapiro /* abort transfer */ 306564562Sgshapiro smtpquit(m, mci, e); 306664562Sgshapiro } 306764562Sgshapiro 306871345Sgshapiro /* avoid bogus error msg */ 306971345Sgshapiro mci->mci_errno = 0; 307071345Sgshapiro 307164562Sgshapiro /* temp or permanent failure? */ 307264562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 307364562Sgshapiro : EX_UNAVAILABLE; 307490792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 307564562Sgshapiro 307664562Sgshapiro /* 307764562Sgshapiro ** hack to get the error message into 307864562Sgshapiro ** the envelope (done in giveresponse()) 307964562Sgshapiro */ 308090792Sgshapiro 308190792Sgshapiro (void) sm_strlcpy(SmtpError, p, 308290792Sgshapiro sizeof SmtpError); 308364562Sgshapiro } 308464562Sgshapiro QuickAbort = saveQuickAbort; 308564562Sgshapiro SuprErrs = saveSuprErrs; 308671345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 308771345Sgshapiro mci->mci_state != MCIS_CLOSED) 308864562Sgshapiro { 308971345Sgshapiro SET_HELO(mci->mci_flags); 309064562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 309164562Sgshapiro goto reconnect; 309264562Sgshapiro } 309364562Sgshapiro } 309464562Sgshapiro# endif /* STARTTLS */ 309564562Sgshapiro# if SASL 309664562Sgshapiro /* if other server supports authentication let's authenticate */ 309764562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 309864562Sgshapiro mci->mci_saslcap != NULL && 309990792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 310064562Sgshapiro { 310190792Sgshapiro /* Should we require some minimum authentication? */ 310290792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 310364562Sgshapiro { 310464562Sgshapiro int result; 310590792Sgshapiro sasl_ssf_t *ssf = NULL; 310664562Sgshapiro 310790792Sgshapiro /* Get security strength (features) */ 310864562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 310998121Sgshapiro# if SASL >= 20000 311098121Sgshapiro (const void **) &ssf); 311198121Sgshapiro# else /* SASL >= 20000 */ 311264562Sgshapiro (void **) &ssf); 311398121Sgshapiro# endif /* SASL >= 20000 */ 311490792Sgshapiro 311590792Sgshapiro /* XXX authid? */ 311664562Sgshapiro if (LogLevel > 9) 311764562Sgshapiro sm_syslog(LOG_INFO, NOQID, 311890792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 311964562Sgshapiro mci->mci_host, 312090792Sgshapiro macvalue(macid("{auth_type}"), e), 312190792Sgshapiro result == SASL_OK ? *ssf : 0); 312277349Sgshapiro 312364562Sgshapiro /* 312490792Sgshapiro ** Only switch to encrypted connection 312564562Sgshapiro ** if a security layer has been negotiated 312664562Sgshapiro */ 312790792Sgshapiro 312864562Sgshapiro if (result == SASL_OK && *ssf > 0) 312964562Sgshapiro { 313064562Sgshapiro /* 313190792Sgshapiro ** Convert I/O layer to use SASL. 313290792Sgshapiro ** If the call fails, the connection 313390792Sgshapiro ** is aborted. 313464562Sgshapiro */ 313590792Sgshapiro 313690792Sgshapiro if (sfdcsasl(&mci->mci_in, 313790792Sgshapiro &mci->mci_out, 313864562Sgshapiro mci->mci_conn) == 0) 313964562Sgshapiro { 314064562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 314190792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 314290792Sgshapiro MCIF_ONLY_EHLO; 314364562Sgshapiro goto reconnect; 314464562Sgshapiro } 314590792Sgshapiro syserr("AUTH TLS switch failed in client"); 314664562Sgshapiro } 314764562Sgshapiro /* else? XXX */ 314864562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 314964562Sgshapiro 315064562Sgshapiro } 315190792Sgshapiro else if (ret == EX_TEMPFAIL) 315290792Sgshapiro { 315390792Sgshapiro if (LogLevel > 8) 315490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 315590792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 315690792Sgshapiro mci->mci_host); 315790792Sgshapiro smtpquit(m, mci, e); 315890792Sgshapiro 315990792Sgshapiro /* avoid bogus error msg */ 316090792Sgshapiro mci->mci_errno = 0; 316190792Sgshapiro rcode = EX_TEMPFAIL; 3162132943Sgshapiro mci_setstat(mci, rcode, "4.3.0", p); 316390792Sgshapiro 316490792Sgshapiro /* 316590792Sgshapiro ** hack to get the error message into 316690792Sgshapiro ** the envelope (done in giveresponse()) 316790792Sgshapiro */ 316890792Sgshapiro 316990792Sgshapiro (void) sm_strlcpy(SmtpError, 317090792Sgshapiro "Temporary AUTH failure", 317190792Sgshapiro sizeof SmtpError); 317290792Sgshapiro } 317364562Sgshapiro } 317464562Sgshapiro# endif /* SASL */ 317538032Speter } 317638032Speter 317764562Sgshapiro 317838032Speterdo_transfer: 317938032Speter /* clear out per-message flags from connection structure */ 318038032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 318138032Speter 318238032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 318338032Speter !bitset(EF_DONT_MIME, e->e_flags) && 318438032Speter bitnset(M_7BITS, m->m_flags)) 318538032Speter mci->mci_flags |= MCIF_CVT8TO7; 318638032Speter 318738032Speter#if MIME7TO8 318838032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 318938032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 319038032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 319190792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 319290792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 319338032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 319438032Speter { 319538032Speter /* may want to convert 7 -> 8 */ 319638032Speter /* XXX should really parse it here -- and use a class XXX */ 319790792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 319838032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 319938032Speter mci->mci_flags |= MCIF_CVT7TO8; 320038032Speter } 320164562Sgshapiro#endif /* MIME7TO8 */ 320238032Speter 320338032Speter if (tTd(11, 1)) 320438032Speter { 320590792Sgshapiro sm_dprintf("openmailer: "); 3206132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 320738032Speter } 320838032Speter 320990792Sgshapiro#if _FFR_CLIENT_SIZE 321090792Sgshapiro /* 321190792Sgshapiro ** See if we know the maximum size and 321290792Sgshapiro ** abort if the message is too big. 321390792Sgshapiro ** 321490792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 321590792Sgshapiro */ 321690792Sgshapiro 321790792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 321890792Sgshapiro mci->mci_maxsize > 0 && 321990792Sgshapiro e->e_msgsize > mci->mci_maxsize) 322090792Sgshapiro { 322190792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 322290792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 322390792Sgshapiro e->e_status = "5.2.3"; 322490792Sgshapiro else 322590792Sgshapiro e->e_status = "5.3.4"; 322690792Sgshapiro 322790792Sgshapiro usrerrenh(e->e_status, 322890792Sgshapiro "552 Message is too large; %ld bytes max", 322990792Sgshapiro mci->mci_maxsize); 323090792Sgshapiro rcode = EX_DATAERR; 323190792Sgshapiro 323290792Sgshapiro /* Need an e_message for error */ 323390792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 323490792Sgshapiro "Message is too large; %ld bytes max", 323590792Sgshapiro mci->mci_maxsize); 323690792Sgshapiro goto give_up; 323790792Sgshapiro } 323890792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 323990792Sgshapiro 324038032Speter if (mci->mci_state != MCIS_OPEN) 324138032Speter { 324238032Speter /* couldn't open the mailer */ 324338032Speter rcode = mci->mci_exitstat; 324438032Speter errno = mci->mci_errno; 324573188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 324638032Speter if (rcode == EX_OK) 324738032Speter { 324838032Speter /* shouldn't happen */ 324964562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 325090792Sgshapiro (unsigned long) mci, rcode, errno, 325190792Sgshapiro mci->mci_state, firstsig); 3252132943Sgshapiro mci_dump_all(smioout, true); 325338032Speter rcode = EX_SOFTWARE; 325438032Speter } 325564562Sgshapiro else if (nummxhosts > hostnum) 325638032Speter { 325738032Speter /* try next MX site */ 325838032Speter goto tryhost; 325938032Speter } 326038032Speter } 326138032Speter else if (!clever) 326238032Speter { 3263157001Sgshapiro bool ok; 3264157001Sgshapiro 326538032Speter /* 326638032Speter ** Format and send message. 326738032Speter */ 326838032Speter 3269157001Sgshapiro rcode = EX_OK; 3270157001Sgshapiro errno = 0; 3271157001Sgshapiro ok = putfromline(mci, e); 3272157001Sgshapiro if (ok) 3273157001Sgshapiro ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 3274157001Sgshapiro if (ok) 3275157001Sgshapiro ok = (*e->e_putbody)(mci, e, NULL); 327638032Speter 3277157001Sgshapiro /* 3278157001Sgshapiro ** Ignore an I/O error that was caused by EPIPE. 3279157001Sgshapiro ** Some broken mailers don't read the entire body 3280157001Sgshapiro ** but just exit() thus causing an I/O error. 3281157001Sgshapiro */ 3282157001Sgshapiro 3283157001Sgshapiro if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE)) 3284157001Sgshapiro ok = true; 3285157001Sgshapiro 3286157001Sgshapiro /* (always) get the exit status */ 328738032Speter rcode = endmailer(mci, e, pv); 3288157001Sgshapiro if (!ok) 3289157001Sgshapiro rcode = EX_TEMPFAIL; 329090792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 329173188Sgshapiro { 329273188Sgshapiro /* 329373188Sgshapiro ** Need an e_message for mailq display. 329473188Sgshapiro ** We set SmtpError as 329573188Sgshapiro */ 329673188Sgshapiro 329790792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 329890792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 329990792Sgshapiro m->m_name, m->m_mailer); 330073188Sgshapiro } 330138032Speter } 330238032Speter else 330338032Speter { 330438032Speter /* 330538032Speter ** Send the MAIL FROM: protocol 330638032Speter */ 330738032Speter 330890792Sgshapiro /* XXX this isn't pipelined... */ 330938032Speter rcode = smtpmailfrom(m, mci, e); 331038032Speter if (rcode == EX_OK) 331138032Speter { 331238032Speter register int i; 331390792Sgshapiro# if PIPELINING 331490792Sgshapiro ADDRESS *volatile pchain; 331590792Sgshapiro# endif /* PIPELINING */ 331638032Speter 331738032Speter /* send the recipient list */ 331838032Speter tobuf[0] = '\0'; 331990792Sgshapiro mci->mci_retryrcpt = false; 332090792Sgshapiro mci->mci_tolist = tobuf; 332190792Sgshapiro# if PIPELINING 332290792Sgshapiro pchain = NULL; 332390792Sgshapiro mci->mci_nextaddr = NULL; 332490792Sgshapiro# endif /* PIPELINING */ 332564562Sgshapiro 332638032Speter for (to = tochain; to != NULL; to = to->q_tchain) 332738032Speter { 332890792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 332938032Speter continue; 333064562Sgshapiro 333190792Sgshapiro /* mark recipient state as "ok so far" */ 333290792Sgshapiro to->q_state = QS_OK; 333390792Sgshapiro e->e_to = to->q_paddr; 333464562Sgshapiro# if STARTTLS 333564562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3336102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3337102528Sgshapiro mci->mci_host, e->e_id); 333864562Sgshapiro if (i != EX_OK) 333938032Speter { 334090792Sgshapiro markfailure(e, to, mci, i, false); 334190792Sgshapiro giveresponse(i, to->q_status, m, mci, 334290792Sgshapiro ctladdr, xstart, e, to); 334390792Sgshapiro if (i == EX_TEMPFAIL) 334490792Sgshapiro { 334590792Sgshapiro mci->mci_retryrcpt = true; 334690792Sgshapiro to->q_state = QS_RETRY; 334790792Sgshapiro } 334864562Sgshapiro continue; 334938032Speter } 335064562Sgshapiro# endif /* STARTTLS */ 335164562Sgshapiro 335290792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 335390792Sgshapiro# if PIPELINING 335490792Sgshapiro if (i == EX_OK && 335590792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 335664562Sgshapiro { 335790792Sgshapiro /* 335890792Sgshapiro ** Add new element to list of 335990792Sgshapiro ** recipients for pipelining. 336090792Sgshapiro */ 336190792Sgshapiro 336290792Sgshapiro to->q_pchain = NULL; 336390792Sgshapiro if (mci->mci_nextaddr == NULL) 336490792Sgshapiro mci->mci_nextaddr = to; 336590792Sgshapiro if (pchain == NULL) 336690792Sgshapiro pchain = to; 336790792Sgshapiro else 336890792Sgshapiro { 336990792Sgshapiro pchain->q_pchain = to; 337090792Sgshapiro pchain = pchain->q_pchain; 337190792Sgshapiro } 337264562Sgshapiro } 337390792Sgshapiro# endif /* PIPELINING */ 337490792Sgshapiro if (i != EX_OK) 337538032Speter { 337690792Sgshapiro markfailure(e, to, mci, i, false); 337798841Sgshapiro giveresponse(i, to->q_status, m, mci, 337890792Sgshapiro ctladdr, xstart, e, to); 337990792Sgshapiro if (i == EX_TEMPFAIL) 338090792Sgshapiro to->q_state = QS_RETRY; 338138032Speter } 338238032Speter } 338338032Speter 338490792Sgshapiro /* No recipients in list and no missing responses? */ 338590792Sgshapiro if (tobuf[0] == '\0' 338690792Sgshapiro# if PIPELINING 338790792Sgshapiro && mci->mci_nextaddr == NULL 338890792Sgshapiro# endif /* PIPELINING */ 338990792Sgshapiro ) 339038032Speter { 339138032Speter rcode = EX_OK; 339238032Speter e->e_to = NULL; 339338032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 339438032Speter smtprset(m, mci, e); 339538032Speter } 339638032Speter else 339738032Speter { 339838032Speter e->e_to = tobuf + 1; 339990792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 340038032Speter } 340138032Speter } 340264562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 340338032Speter { 340438032Speter /* try next MX site */ 340538032Speter goto tryhost; 340638032Speter } 340738032Speter } 340838032Speter#if NAMED_BIND 340938032Speter if (ConfigLevel < 2) 341038032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 341164562Sgshapiro#endif /* NAMED_BIND */ 341238032Speter 341338032Speter if (tTd(62, 1)) 341438032Speter checkfds("after delivery"); 341538032Speter 341638032Speter /* 341738032Speter ** Do final status disposal. 341838032Speter ** We check for something in tobuf for the SMTP case. 341938032Speter ** If we got a temporary failure, arrange to queue the 342038032Speter ** addressees. 342138032Speter */ 342238032Speter 342338032Speter give_up: 342438032Speter if (bitnset(M_LMTP, m->m_flags)) 342538032Speter { 342638032Speter lmtp_rcode = rcode; 342738032Speter tobuf[0] = '\0'; 342890792Sgshapiro anyok = false; 342990792Sgshapiro strsize = 0; 343038032Speter } 343138032Speter else 343238032Speter anyok = rcode == EX_OK; 343338032Speter 343438032Speter for (to = tochain; to != NULL; to = to->q_tchain) 343538032Speter { 343638032Speter /* see if address already marked */ 343764562Sgshapiro if (!QS_IS_OK(to->q_state)) 343838032Speter continue; 343938032Speter 344038032Speter /* if running LMTP, get the status for each address */ 344138032Speter if (bitnset(M_LMTP, m->m_flags)) 344238032Speter { 344338032Speter if (lmtp_rcode == EX_OK) 344438032Speter rcode = smtpgetstat(m, mci, e); 344538032Speter if (rcode == EX_OK) 344638032Speter { 344790792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 344890792Sgshapiro to->q_paddr, 344990792Sgshapiro tobufsize - strsize); 345090792Sgshapiro SM_ASSERT(strsize < tobufsize); 345190792Sgshapiro anyok = true; 345238032Speter } 345338032Speter else 345438032Speter { 345538032Speter e->e_to = to->q_paddr; 345690792Sgshapiro markfailure(e, to, mci, rcode, true); 345764562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 345890792Sgshapiro ctladdr, xstart, e, to); 345938032Speter e->e_to = tobuf + 1; 346038032Speter continue; 346138032Speter } 346238032Speter } 346338032Speter else 346438032Speter { 346538032Speter /* mark bad addresses */ 346638032Speter if (rcode != EX_OK) 346738032Speter { 346838032Speter if (goodmxfound && rcode == EX_NOHOST) 346938032Speter rcode = EX_TEMPFAIL; 347090792Sgshapiro markfailure(e, to, mci, rcode, true); 347138032Speter continue; 347238032Speter } 347338032Speter } 347438032Speter 347538032Speter /* successful delivery */ 347664562Sgshapiro to->q_state = QS_SENT; 347738032Speter to->q_statdate = curtime(); 347838032Speter e->e_nsent++; 347964562Sgshapiro 348064562Sgshapiro /* 348164562Sgshapiro ** Checkpoint the send list every few addresses 348264562Sgshapiro */ 348364562Sgshapiro 348466494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 348564562Sgshapiro { 348690792Sgshapiro queueup(e, false, false); 348764562Sgshapiro e->e_nsent = 0; 348864562Sgshapiro } 348964562Sgshapiro 349038032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 349138032Speter bitset(QPINGONSUCCESS, to->q_flags)) 349238032Speter { 349338032Speter to->q_flags |= QDELIVERED; 349438032Speter to->q_status = "2.1.5"; 349590792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 349690792Sgshapiro "%s... Successfully delivered\n", 349790792Sgshapiro to->q_paddr); 349838032Speter } 349938032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 350038032Speter bitset(QPRIMARY, to->q_flags) && 350138032Speter !bitset(MCIF_DSN, mci->mci_flags)) 350238032Speter { 350338032Speter to->q_flags |= QRELAYED; 350490792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 350590792Sgshapiro "%s... relayed; expect no further notifications\n", 350690792Sgshapiro to->q_paddr); 350738032Speter } 350890792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 350990792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 351090792Sgshapiro bitset(QPRIMARY, to->q_flags) && 351190792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 351290792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 351390792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 351490792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 351590792Sgshapiro { 351690792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 351790792Sgshapiro to->q_flags |= QBYNRELAY; 351890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 351990792Sgshapiro "%s... Deliver-by notify: relayed\n", 352090792Sgshapiro to->q_paddr); 352190792Sgshapiro } 352290792Sgshapiro else if (IS_DLVR_TRACE(e) && 352390792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 352490792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 352590792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 352690792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 352790792Sgshapiro bitset(QPRIMARY, to->q_flags)) 352890792Sgshapiro { 352990792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 353090792Sgshapiro to->q_flags |= QBYTRACE; 353190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 353290792Sgshapiro "%s... Deliver-By trace: relayed\n", 353390792Sgshapiro to->q_paddr); 353490792Sgshapiro } 353538032Speter } 353638032Speter 353738032Speter if (bitnset(M_LMTP, m->m_flags)) 353838032Speter { 353938032Speter /* 354038032Speter ** Global information applies to the last recipient only; 354138032Speter ** clear it out to avoid bogus errors. 354238032Speter */ 354338032Speter 354438032Speter rcode = EX_OK; 354538032Speter e->e_statmsg = NULL; 354638032Speter 354738032Speter /* reset the mci state for the next transaction */ 354890792Sgshapiro if (mci != NULL && 354990792Sgshapiro (mci->mci_state == MCIS_MAIL || 355090792Sgshapiro mci->mci_state == MCIS_RCPT || 355190792Sgshapiro mci->mci_state == MCIS_DATA)) 3552125820Sgshapiro { 355338032Speter mci->mci_state = MCIS_OPEN; 3554125820Sgshapiro SmtpPhase = mci->mci_phase = "idle"; 3555125820Sgshapiro sm_setproctitle(true, e, "%s: %s", CurHostName, 3556125820Sgshapiro mci->mci_phase); 3557125820Sgshapiro } 355838032Speter } 355938032Speter 356038032Speter if (tobuf[0] != '\0') 356190792Sgshapiro { 356290792Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 356390792Sgshapiro#if 0 356490792Sgshapiro /* 356590792Sgshapiro ** This code is disabled for now because I am not 356690792Sgshapiro ** sure that copying status from the first recipient 356790792Sgshapiro ** to all non-status'ed recipients is a good idea. 356890792Sgshapiro */ 356990792Sgshapiro 357090792Sgshapiro if (tochain->q_message != NULL && 357190792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 357290792Sgshapiro { 357390792Sgshapiro for (to = tochain->q_tchain; to != NULL; 357490792Sgshapiro to = to->q_tchain) 357590792Sgshapiro { 357690792Sgshapiro /* see if address already marked */ 357790792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 357890792Sgshapiro to->q_message == NULL) 357990792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 358090792Sgshapiro tochain->q_message); 358190792Sgshapiro } 358290792Sgshapiro } 358390792Sgshapiro#endif /* 0 */ 358490792Sgshapiro } 358538032Speter if (anyok) 358690792Sgshapiro markstats(e, tochain, STATS_NORMAL); 358738032Speter mci_store_persistent(mci); 358838032Speter 358990792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 359090792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 359190792Sgshapiro { 359290792Sgshapiro /* try next MX site */ 359390792Sgshapiro goto tryhost; 359490792Sgshapiro } 359590792Sgshapiro 359638032Speter /* now close the connection */ 359738032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 359838032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 359938032Speter smtpquit(m, mci, e); 360038032Speter 360190792Sgshapirocleanup: ; 360290792Sgshapiro } 360390792Sgshapiro SM_FINALLY 360490792Sgshapiro { 360590792Sgshapiro /* 360690792Sgshapiro ** Restore state and return. 360790792Sgshapiro */ 360838032Speter#if XDEBUG 360938032Speter char wbuf[MAXLINE]; 361038032Speter 361138032Speter /* make absolutely certain 0, 1, and 2 are in use */ 361290792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, 361390792Sgshapiro "%s... end of deliver(%s)", 361490792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 361590792Sgshapiro : shortenstring(e->e_to, 361690792Sgshapiro MAXSHORTSTR), 361790792Sgshapiro m->m_name); 361838032Speter checkfd012(wbuf); 361964562Sgshapiro#endif /* XDEBUG */ 362038032Speter 362190792Sgshapiro errno = 0; 362290792Sgshapiro 362390792Sgshapiro /* 362490792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 362590792Sgshapiro ** because it previously pointed to an auto buffer. 362690792Sgshapiro ** We don't do this any more, so this may be unnecessary. 362790792Sgshapiro */ 362890792Sgshapiro 362990792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 363090792Sgshapiro e->e_to = NULL; 363190792Sgshapiro } 363290792Sgshapiro SM_END_TRY 363364562Sgshapiro return rcode; 363438032Speter} 363564562Sgshapiro 363690792Sgshapiro/* 363738032Speter** MARKFAILURE -- mark a failure on a specific address. 363838032Speter** 363938032Speter** Parameters: 364038032Speter** e -- the envelope we are sending. 364138032Speter** q -- the address to mark. 364238032Speter** mci -- mailer connection information. 364338032Speter** rcode -- the code signifying the particular failure. 364464562Sgshapiro** ovr -- override an existing code? 364538032Speter** 364638032Speter** Returns: 364738032Speter** none. 364838032Speter** 364938032Speter** Side Effects: 365038032Speter** marks the address (and possibly the envelope) with the 365138032Speter** failure so that an error will be returned or 365238032Speter** the message will be queued, as appropriate. 365338032Speter*/ 365438032Speter 365590792Sgshapirovoid 365664562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 365738032Speter register ENVELOPE *e; 365838032Speter register ADDRESS *q; 365938032Speter register MCI *mci; 366038032Speter int rcode; 366164562Sgshapiro bool ovr; 366238032Speter{ 366390792Sgshapiro int save_errno = errno; 366464562Sgshapiro char *status = NULL; 366564562Sgshapiro char *rstatus = NULL; 366638032Speter 366738032Speter switch (rcode) 366838032Speter { 366938032Speter case EX_OK: 367038032Speter break; 367138032Speter 367238032Speter case EX_TEMPFAIL: 367338032Speter case EX_IOERR: 367438032Speter case EX_OSERR: 367564562Sgshapiro q->q_state = QS_QUEUEUP; 367638032Speter break; 367738032Speter 367838032Speter default: 367964562Sgshapiro q->q_state = QS_BADADDR; 368038032Speter break; 368138032Speter } 368238032Speter 368338032Speter /* find most specific error code possible */ 368438032Speter if (mci != NULL && mci->mci_status != NULL) 368538032Speter { 368690792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 368738032Speter if (mci->mci_rstatus != NULL) 368890792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 368990792Sgshapiro mci->mci_rstatus); 369038032Speter else 369164562Sgshapiro rstatus = NULL; 369238032Speter } 369338032Speter else if (e->e_status != NULL) 369438032Speter { 369564562Sgshapiro status = e->e_status; 369664562Sgshapiro rstatus = NULL; 369738032Speter } 369838032Speter else 369938032Speter { 370038032Speter switch (rcode) 370138032Speter { 370238032Speter case EX_USAGE: 370364562Sgshapiro status = "5.5.4"; 370438032Speter break; 370538032Speter 370638032Speter case EX_DATAERR: 370764562Sgshapiro status = "5.5.2"; 370838032Speter break; 370938032Speter 371038032Speter case EX_NOUSER: 371164562Sgshapiro status = "5.1.1"; 371238032Speter break; 371338032Speter 371438032Speter case EX_NOHOST: 371564562Sgshapiro status = "5.1.2"; 371638032Speter break; 371738032Speter 371838032Speter case EX_NOINPUT: 371938032Speter case EX_CANTCREAT: 372038032Speter case EX_NOPERM: 372164562Sgshapiro status = "5.3.0"; 372238032Speter break; 372338032Speter 372438032Speter case EX_UNAVAILABLE: 372538032Speter case EX_SOFTWARE: 372638032Speter case EX_OSFILE: 372738032Speter case EX_PROTOCOL: 372838032Speter case EX_CONFIG: 372964562Sgshapiro status = "5.5.0"; 373038032Speter break; 373138032Speter 373238032Speter case EX_OSERR: 373338032Speter case EX_IOERR: 373464562Sgshapiro status = "4.5.0"; 373538032Speter break; 373638032Speter 373738032Speter case EX_TEMPFAIL: 373864562Sgshapiro status = "4.2.0"; 373938032Speter break; 374038032Speter } 374138032Speter } 374238032Speter 374364562Sgshapiro /* new status? */ 374464562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 374564562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 374664562Sgshapiro { 374764562Sgshapiro q->q_status = status; 374864562Sgshapiro q->q_rstatus = rstatus; 374964562Sgshapiro } 375038032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 375138032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 375290792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 375338032Speter { 375464562Sgshapiro char buf[16]; 375538032Speter 375690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", rcode); 375790792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 375838032Speter } 375964562Sgshapiro 376064562Sgshapiro q->q_statdate = curtime(); 376164562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 376264562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 376390792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 376490792Sgshapiro 376590792Sgshapiro /* restore errno */ 376690792Sgshapiro errno = save_errno; 376738032Speter} 376890792Sgshapiro/* 376938032Speter** ENDMAILER -- Wait for mailer to terminate. 377038032Speter** 377138032Speter** We should never get fatal errors (e.g., segmentation 377238032Speter** violation), so we report those specially. For other 377338032Speter** errors, we choose a status message (into statmsg), 377438032Speter** and if it represents an error, we print it. 377538032Speter** 377638032Speter** Parameters: 377780785Sgshapiro** mci -- the mailer connection info. 377838032Speter** e -- the current envelope. 377938032Speter** pv -- the parameter vector that invoked the mailer 378038032Speter** (for error messages). 378138032Speter** 378238032Speter** Returns: 378338032Speter** exit code of mailer. 378438032Speter** 378538032Speter** Side Effects: 378638032Speter** none. 378738032Speter*/ 378838032Speter 378964562Sgshapirostatic jmp_buf EndWaitTimeout; 379064562Sgshapiro 379164562Sgshapirostatic void 3792141858Sgshapiroendwaittimeout(ignore) 3793141858Sgshapiro int ignore; 379464562Sgshapiro{ 379577349Sgshapiro /* 379677349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 379777349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 379877349Sgshapiro ** DOING. 379977349Sgshapiro */ 380077349Sgshapiro 380164562Sgshapiro errno = ETIMEDOUT; 380264562Sgshapiro longjmp(EndWaitTimeout, 1); 380364562Sgshapiro} 380464562Sgshapiro 380538032Speterint 380638032Speterendmailer(mci, e, pv) 380738032Speter register MCI *mci; 380838032Speter register ENVELOPE *e; 380938032Speter char **pv; 381038032Speter{ 381138032Speter int st; 381264562Sgshapiro int save_errno = errno; 381364562Sgshapiro char buf[MAXLINE]; 381490792Sgshapiro SM_EVENT *ev = NULL; 381538032Speter 381664562Sgshapiro 381738032Speter mci_unlock_host(mci); 381838032Speter 381977349Sgshapiro /* close output to mailer */ 382077349Sgshapiro if (mci->mci_out != NULL) 3821141858Sgshapiro { 382290792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 3823141858Sgshapiro mci->mci_out = NULL; 3824141858Sgshapiro } 382577349Sgshapiro 382677349Sgshapiro /* copy any remaining input to transcript */ 382777349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 382877349Sgshapiro e->e_xfp != NULL) 382977349Sgshapiro { 383077349Sgshapiro while (sfgets(buf, sizeof buf, mci->mci_in, 383190792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 383290792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 383377349Sgshapiro } 383477349Sgshapiro 383564562Sgshapiro#if SASL 383690792Sgshapiro /* close SASL connection */ 383764562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 383864562Sgshapiro { 383964562Sgshapiro sasl_dispose(&mci->mci_conn); 384064562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 384164562Sgshapiro } 384264562Sgshapiro#endif /* SASL */ 384364562Sgshapiro 384464562Sgshapiro#if STARTTLS 384564562Sgshapiro /* shutdown TLS */ 384664562Sgshapiro (void) endtlsclt(mci); 384764562Sgshapiro#endif /* STARTTLS */ 384864562Sgshapiro 384964562Sgshapiro /* now close the input */ 385038032Speter if (mci->mci_in != NULL) 3851141858Sgshapiro { 385290792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 3853141858Sgshapiro mci->mci_in = NULL; 3854141858Sgshapiro } 385538032Speter mci->mci_state = MCIS_CLOSED; 385638032Speter 385764562Sgshapiro errno = save_errno; 385864562Sgshapiro 385938032Speter /* in the IPC case there is nothing to wait for */ 386038032Speter if (mci->mci_pid == 0) 386164562Sgshapiro return EX_OK; 386238032Speter 386364562Sgshapiro /* put a timeout around the wait */ 386464562Sgshapiro if (mci->mci_mailer->m_wait > 0) 386564562Sgshapiro { 386664562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 386790792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 386890792Sgshapiro endwaittimeout, 0); 386964562Sgshapiro else 387064562Sgshapiro { 387166494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 387264562Sgshapiro mci->mci_mailer->m_name, 387366494Sgshapiro (long) mci->mci_mailer->m_wait); 387464562Sgshapiro return EX_TEMPFAIL; 387564562Sgshapiro } 387664562Sgshapiro } 387738032Speter 387864562Sgshapiro /* wait for the mailer process, collect status */ 387938032Speter st = waitfor(mci->mci_pid); 388064562Sgshapiro save_errno = errno; 388164562Sgshapiro if (ev != NULL) 388290792Sgshapiro sm_clrevent(ev); 388364562Sgshapiro errno = save_errno; 388464562Sgshapiro 388538032Speter if (st == -1) 388638032Speter { 388738032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 388864562Sgshapiro return EX_SOFTWARE; 388938032Speter } 389038032Speter 389138032Speter if (WIFEXITED(st)) 389238032Speter { 389338032Speter /* normal death -- return status */ 389438032Speter return (WEXITSTATUS(st)); 389538032Speter } 389638032Speter 389738032Speter /* it died a horrid death */ 389864562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 389964562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 390064562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 390164562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 390238032Speter 390338032Speter /* log the arguments */ 390438032Speter if (pv != NULL && e->e_xfp != NULL) 390538032Speter { 390638032Speter register char **av; 390738032Speter 390890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 390938032Speter for (av = pv; *av != NULL; av++) 391090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 391190792Sgshapiro *av); 391290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 391338032Speter } 391438032Speter 391538032Speter ExitStat = EX_TEMPFAIL; 391664562Sgshapiro return EX_TEMPFAIL; 391738032Speter} 391890792Sgshapiro/* 391938032Speter** GIVERESPONSE -- Interpret an error response from a mailer 392038032Speter** 392138032Speter** Parameters: 392264562Sgshapiro** status -- the status code from the mailer (high byte 392338032Speter** only; core dumps must have been taken care of 392438032Speter** already). 392564562Sgshapiro** dsn -- the DSN associated with the address, if any. 392638032Speter** m -- the mailer info for this mailer. 392738032Speter** mci -- the mailer connection info -- can be NULL if the 392838032Speter** response is given before the connection is made. 392938032Speter** ctladdr -- the controlling address for the recipient 393038032Speter** address(es). 393138032Speter** xstart -- the transaction start time, for computing 393238032Speter** transaction delays. 393338032Speter** e -- the current envelope. 393490792Sgshapiro** to -- the current recipient (NULL if none). 393538032Speter** 393638032Speter** Returns: 393738032Speter** none. 393838032Speter** 393938032Speter** Side Effects: 394038032Speter** Errors may be incremented. 394138032Speter** ExitStat may be set. 394238032Speter*/ 394338032Speter 394438032Spetervoid 394590792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 394664562Sgshapiro int status; 394764562Sgshapiro char *dsn; 394838032Speter register MAILER *m; 394938032Speter register MCI *mci; 395038032Speter ADDRESS *ctladdr; 395138032Speter time_t xstart; 395238032Speter ENVELOPE *e; 395390792Sgshapiro ADDRESS *to; 395438032Speter{ 395538032Speter register const char *statmsg; 395664562Sgshapiro int errnum = errno; 395764562Sgshapiro int off = 4; 395890792Sgshapiro bool usestat = false; 395964562Sgshapiro char dsnbuf[ENHSCLEN]; 396038032Speter char buf[MAXLINE]; 396190792Sgshapiro char *exmsg; 396238032Speter 396338032Speter if (e == NULL) 396438032Speter syserr("giveresponse: null envelope"); 396538032Speter 396638032Speter /* 396738032Speter ** Compute status message from code. 396838032Speter */ 396938032Speter 397090792Sgshapiro exmsg = sm_sysexmsg(status); 397164562Sgshapiro if (status == 0) 397238032Speter { 397364562Sgshapiro statmsg = "250 2.0.0 Sent"; 397438032Speter if (e->e_statmsg != NULL) 397538032Speter { 397690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", 397790792Sgshapiro statmsg, 397890792Sgshapiro shortenstring(e->e_statmsg, 403)); 397938032Speter statmsg = buf; 398038032Speter } 398138032Speter } 398290792Sgshapiro else if (exmsg == NULL) 398338032Speter { 398490792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 398590792Sgshapiro "554 5.3.0 unknown mailer error %d", 398690792Sgshapiro status); 398764562Sgshapiro status = EX_UNAVAILABLE; 398838032Speter statmsg = buf; 398990792Sgshapiro usestat = true; 399038032Speter } 399164562Sgshapiro else if (status == EX_TEMPFAIL) 399238032Speter { 399338032Speter char *bp = buf; 399438032Speter 399590792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 399638032Speter bp += strlen(bp); 399738032Speter#if NAMED_BIND 399838032Speter if (h_errno == TRY_AGAIN) 399990792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 400038032Speter else 400164562Sgshapiro#endif /* NAMED_BIND */ 400238032Speter { 400364562Sgshapiro if (errnum != 0) 400490792Sgshapiro statmsg = sm_errstring(errnum); 400538032Speter else 400638032Speter statmsg = SmtpError; 400738032Speter } 400838032Speter if (statmsg != NULL && statmsg[0] != '\0') 400964562Sgshapiro { 401064562Sgshapiro switch (errnum) 401164562Sgshapiro { 401264562Sgshapiro#ifdef ENETDOWN 401364562Sgshapiro case ENETDOWN: /* Network is down */ 401464562Sgshapiro#endif /* ENETDOWN */ 401564562Sgshapiro#ifdef ENETUNREACH 401664562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 401764562Sgshapiro#endif /* ENETUNREACH */ 401864562Sgshapiro#ifdef ENETRESET 401964562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 402064562Sgshapiro#endif /* ENETRESET */ 402164562Sgshapiro#ifdef ECONNABORTED 402264562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 402364562Sgshapiro#endif /* ECONNABORTED */ 402464562Sgshapiro#ifdef EHOSTDOWN 402564562Sgshapiro case EHOSTDOWN: /* Host is down */ 402664562Sgshapiro#endif /* EHOSTDOWN */ 402764562Sgshapiro#ifdef EHOSTUNREACH 402864562Sgshapiro case EHOSTUNREACH: /* No route to host */ 402964562Sgshapiro#endif /* EHOSTUNREACH */ 4030141858Sgshapiro if (mci != NULL && mci->mci_host != NULL) 403164562Sgshapiro { 403290792Sgshapiro (void) sm_strlcpyn(bp, 403390792Sgshapiro SPACELEFT(buf, bp), 403490792Sgshapiro 2, ": ", 403590792Sgshapiro mci->mci_host); 403664562Sgshapiro bp += strlen(bp); 403764562Sgshapiro } 403864562Sgshapiro break; 403964562Sgshapiro } 404090792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 404190792Sgshapiro statmsg); 404290792Sgshapiro usestat = true; 404364562Sgshapiro } 404438032Speter statmsg = buf; 404538032Speter } 404638032Speter#if NAMED_BIND 404764562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 404838032Speter { 404990792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 405090792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1, 405190792Sgshapiro statmsg); 405238032Speter statmsg = buf; 405390792Sgshapiro usestat = true; 405438032Speter } 405564562Sgshapiro#endif /* NAMED_BIND */ 405638032Speter else 405738032Speter { 405890792Sgshapiro statmsg = exmsg; 405964562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 406038032Speter { 406190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg, 406290792Sgshapiro sm_errstring(errnum)); 406338032Speter statmsg = buf; 406490792Sgshapiro usestat = true; 406538032Speter } 406694334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 406794334Sgshapiro { 406894334Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg, 406994334Sgshapiro shortenstring(e->e_statmsg, 403)); 407094334Sgshapiro statmsg = buf; 407194334Sgshapiro usestat = true; 407294334Sgshapiro } 407338032Speter } 407438032Speter 407538032Speter /* 407638032Speter ** Print the message as appropriate 407738032Speter */ 407838032Speter 407964562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 408038032Speter { 408138032Speter extern char MsgBuf[]; 408238032Speter 408364562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 408464562Sgshapiro { 408564562Sgshapiro if (dsn == NULL) 408664562Sgshapiro { 408790792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 408890792Sgshapiro "%.*s", off, statmsg + 4); 408964562Sgshapiro dsn = dsnbuf; 409064562Sgshapiro } 409164562Sgshapiro off += 5; 409264562Sgshapiro } 409364562Sgshapiro else 409464562Sgshapiro { 409564562Sgshapiro off = 4; 409664562Sgshapiro } 409764562Sgshapiro message("%s", statmsg + off); 409864562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 409990792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 410090792Sgshapiro &MsgBuf[4]); 410138032Speter } 410238032Speter else 410338032Speter { 410464562Sgshapiro char mbuf[ENHSCLEN + 4]; 410538032Speter 410638032Speter Errors++; 410764562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 410864562Sgshapiro off < sizeof mbuf - 4) 410964562Sgshapiro { 411064562Sgshapiro if (dsn == NULL) 411164562Sgshapiro { 411290792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 411390792Sgshapiro "%.*s", off, statmsg + 4); 411464562Sgshapiro dsn = dsnbuf; 411564562Sgshapiro } 411664562Sgshapiro off += 5; 411790792Sgshapiro 411890792Sgshapiro /* copy only part of statmsg to mbuf */ 411990792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 412090792Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof mbuf); 412164562Sgshapiro } 412264562Sgshapiro else 412364562Sgshapiro { 412464562Sgshapiro dsnbuf[0] = '\0'; 412590792Sgshapiro (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s", 412690792Sgshapiro statmsg); 412764562Sgshapiro off = 4; 412864562Sgshapiro } 412964562Sgshapiro usrerr(mbuf, &statmsg[off]); 413038032Speter } 413138032Speter 413238032Speter /* 413338032Speter ** Final cleanup. 413438032Speter ** Log a record of the transaction. Compute the new 413538032Speter ** ExitStat -- if we already had an error, stick with 413638032Speter ** that. 413738032Speter */ 413838032Speter 413938032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 414064562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 414164562Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 414238032Speter 414338032Speter if (tTd(11, 2)) 414490792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 414590792Sgshapiro status, 414690792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 414790792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 414890792Sgshapiro errnum); 414938032Speter 415064562Sgshapiro if (status != EX_TEMPFAIL) 415164562Sgshapiro setstat(status); 415264562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 415390792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 415490792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 415538032Speter { 415690792Sgshapiro if (!usestat && e->e_message != NULL) 415790792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 415890792Sgshapiro e->e_message); 415990792Sgshapiro else 416090792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 416190792Sgshapiro statmsg + off); 416238032Speter } 416338032Speter errno = 0; 416473188Sgshapiro SM_SET_H_ERRNO(0); 416538032Speter} 416690792Sgshapiro/* 416738032Speter** LOGDELIVERY -- log the delivery in the system log 416838032Speter** 416938032Speter** Care is taken to avoid logging lines that are too long, because 417038032Speter** some versions of syslog have an unfortunate proclivity for core 417138032Speter** dumping. This is a hack, to be sure, that is at best empirical. 417238032Speter** 417338032Speter** Parameters: 417438032Speter** m -- the mailer info. Can be NULL for initial queue. 417538032Speter** mci -- the mailer connection info -- can be NULL if the 417664562Sgshapiro** log is occurring when no connection is active. 417764562Sgshapiro** dsn -- the DSN attached to the status. 417864562Sgshapiro** status -- the message to print for the status. 417938032Speter** ctladdr -- the controlling address for the to list. 418038032Speter** xstart -- the transaction start time, used for 418138032Speter** computing transaction delay. 418238032Speter** e -- the current envelope. 418338032Speter** 418438032Speter** Returns: 418538032Speter** none 418638032Speter** 418738032Speter** Side Effects: 418838032Speter** none 418938032Speter*/ 419038032Speter 419138032Spetervoid 419264562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e) 419338032Speter MAILER *m; 419438032Speter register MCI *mci; 419564562Sgshapiro char *dsn; 419664562Sgshapiro const char *status; 419738032Speter ADDRESS *ctladdr; 419838032Speter time_t xstart; 419938032Speter register ENVELOPE *e; 420038032Speter{ 420138032Speter register char *bp; 420238032Speter register char *p; 420338032Speter int l; 420490792Sgshapiro time_t now = curtime(); 420538032Speter char buf[1024]; 420638032Speter 420764562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 420838032Speter /* ctladdr: max 106 bytes */ 420938032Speter bp = buf; 421038032Speter if (ctladdr != NULL) 421138032Speter { 421290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 421390792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 421438032Speter bp += strlen(bp); 421538032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 421638032Speter { 421790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 421890792Sgshapiro (int) ctladdr->q_uid, 421990792Sgshapiro (int) ctladdr->q_gid); 422038032Speter bp += strlen(bp); 422138032Speter } 422238032Speter } 422338032Speter 422438032Speter /* delay & xdelay: max 41 bytes */ 422590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 422690792Sgshapiro pintvl(now - e->e_ctime, true)); 422738032Speter bp += strlen(bp); 422838032Speter 422938032Speter if (xstart != (time_t) 0) 423038032Speter { 423190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 423290792Sgshapiro pintvl(now - xstart, true)); 423338032Speter bp += strlen(bp); 423438032Speter } 423538032Speter 423638032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 423738032Speter if (m != NULL) 423838032Speter { 423990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 424090792Sgshapiro m->m_name); 424138032Speter bp += strlen(bp); 424238032Speter } 424338032Speter 424464562Sgshapiro /* pri: changes with each delivery attempt */ 424590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 424690792Sgshapiro e->e_msgpriority); 424764562Sgshapiro bp += strlen(bp); 424864562Sgshapiro 424938032Speter /* relay: max 66 bytes for IPv4 addresses */ 425038032Speter if (mci != NULL && mci->mci_host != NULL) 425138032Speter { 425238032Speter extern SOCKADDR CurHostAddr; 425338032Speter 425490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 425590792Sgshapiro shortenstring(mci->mci_host, 40)); 425638032Speter bp += strlen(bp); 425738032Speter 425838032Speter if (CurHostAddr.sa.sa_family != 0) 425938032Speter { 426090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 426190792Sgshapiro anynet_ntoa(&CurHostAddr)); 426238032Speter } 426338032Speter } 426490792Sgshapiro else if (strcmp(status, "quarantined") == 0) 426590792Sgshapiro { 426690792Sgshapiro if (e->e_quarmsg != NULL) 426790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 426890792Sgshapiro ", quarantine=%s", 426990792Sgshapiro shortenstring(e->e_quarmsg, 40)); 427090792Sgshapiro } 427164562Sgshapiro else if (strcmp(status, "queued") != 0) 427238032Speter { 427338032Speter p = macvalue('h', e); 427438032Speter if (p != NULL && p[0] != '\0') 427538032Speter { 427690792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 427790792Sgshapiro ", relay=%s", shortenstring(p, 40)); 427838032Speter } 427938032Speter } 428038032Speter bp += strlen(bp); 428138032Speter 428264562Sgshapiro /* dsn */ 428364562Sgshapiro if (dsn != NULL && *dsn != '\0') 428464562Sgshapiro { 428590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 428690792Sgshapiro shortenstring(dsn, ENHSCLEN)); 428764562Sgshapiro bp += strlen(bp); 428864562Sgshapiro } 428938032Speter 4290147078Sgshapiro#if _FFR_LOG_NTRIES 4291147078Sgshapiro /* ntries */ 4292147078Sgshapiro if (e->e_ntries >= 0) 4293147078Sgshapiro { 4294147078Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4295147078Sgshapiro ", ntries=%d", e->e_ntries + 1); 4296147078Sgshapiro bp += strlen(bp); 4297147078Sgshapiro } 4298147078Sgshapiro#endif /* _FFR_LOG_NTRIES */ 4299147078Sgshapiro 430064562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 430164562Sgshapiro# if (STATLEN) < 63 430264562Sgshapiro# undef STATLEN 430364562Sgshapiro# define STATLEN 63 430464562Sgshapiro# endif /* (STATLEN) < 63 */ 430564562Sgshapiro# if (STATLEN) > 203 430664562Sgshapiro# undef STATLEN 430764562Sgshapiro# define STATLEN 203 430864562Sgshapiro# endif /* (STATLEN) > 203 */ 430964562Sgshapiro 431038032Speter /* stat: max 210 bytes */ 431138032Speter if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) 431238032Speter { 431338032Speter /* desperation move -- truncate data */ 431438032Speter bp = buf + sizeof buf - ((STATLEN) + 17); 431590792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 431638032Speter bp += 3; 431738032Speter } 431838032Speter 431990792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 432038032Speter bp += strlen(bp); 432138032Speter 432290792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 432390792Sgshapiro SPACELEFT(buf, bp)); 432438032Speter 432538032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 432638032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 432790792Sgshapiro if (l < 0) 432890792Sgshapiro l = 0; 432964562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 433090792Sgshapiro while (strlen(p) >= l) 433138032Speter { 433264562Sgshapiro register char *q; 433338032Speter 433464562Sgshapiro for (q = p + l; q > p; q--) 433564562Sgshapiro { 433664562Sgshapiro if (*q == ',') 433764562Sgshapiro break; 433864562Sgshapiro } 433964562Sgshapiro if (p == q) 434064562Sgshapiro break; 434190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 434266494Sgshapiro (int) (++q - p), p, buf); 434338032Speter p = q; 434438032Speter } 434564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 434638032Speter 434764562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 434838032Speter 434938032Speter l = SYSLOG_BUFSIZE - 85; 435090792Sgshapiro if (l < 0) 435190792Sgshapiro l = 0; 435264562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 435390792Sgshapiro while (strlen(p) >= l) 435438032Speter { 435564562Sgshapiro register char *q; 435638032Speter 435764562Sgshapiro for (q = p + l; q > p; q--) 435864562Sgshapiro { 435964562Sgshapiro if (*q == ',') 436064562Sgshapiro break; 436164562Sgshapiro } 436264562Sgshapiro if (p == q) 436364562Sgshapiro break; 436464562Sgshapiro 436590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 436666494Sgshapiro (int) (++q - p), p); 436738032Speter p = q; 436838032Speter } 436964562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 437038032Speter 437138032Speter if (ctladdr != NULL) 437238032Speter { 437338032Speter bp = buf; 437490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 437590792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 437638032Speter bp += strlen(bp); 437738032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 437838032Speter { 437990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 438090792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 438138032Speter bp += strlen(bp); 438238032Speter } 438338032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 438438032Speter } 438538032Speter bp = buf; 438690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 438790792Sgshapiro pintvl(now - e->e_ctime, true)); 438838032Speter bp += strlen(bp); 438938032Speter if (xstart != (time_t) 0) 439038032Speter { 439190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 439290792Sgshapiro pintvl(now - xstart, true)); 439338032Speter bp += strlen(bp); 439438032Speter } 439538032Speter 439638032Speter if (m != NULL) 439738032Speter { 439890792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 439990792Sgshapiro m->m_name); 440038032Speter bp += strlen(bp); 440138032Speter } 440238032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 440338032Speter 440438032Speter buf[0] = '\0'; 440538032Speter bp = buf; 440638032Speter if (mci != NULL && mci->mci_host != NULL) 440738032Speter { 440838032Speter extern SOCKADDR CurHostAddr; 440938032Speter 441090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 441190792Sgshapiro mci->mci_host); 441238032Speter bp += strlen(bp); 441338032Speter 441438032Speter if (CurHostAddr.sa.sa_family != 0) 441590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 441690792Sgshapiro " [%.100s]", 441790792Sgshapiro anynet_ntoa(&CurHostAddr)); 441838032Speter } 441990792Sgshapiro else if (strcmp(status, "quarantined") == 0) 442090792Sgshapiro { 442190792Sgshapiro if (e->e_quarmsg != NULL) 442290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 442390792Sgshapiro ", quarantine=%.100s", 442490792Sgshapiro e->e_quarmsg); 442590792Sgshapiro } 442664562Sgshapiro else if (strcmp(status, "queued") != 0) 442738032Speter { 442838032Speter p = macvalue('h', e); 442938032Speter if (p != NULL && p[0] != '\0') 443090792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p); 443138032Speter } 443238032Speter if (buf[0] != '\0') 443338032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 443438032Speter 443564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 443664562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 443738032Speter} 443890792Sgshapiro/* 443938032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 444038032Speter** 444138032Speter** This can be made an arbitrary message separator by changing $l 444238032Speter** 444338032Speter** One of the ugliest hacks seen by human eyes is contained herein: 444438032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 444538032Speter** does a well-meaning programmer such as myself have to deal with 444638032Speter** this kind of antique garbage???? 444738032Speter** 444838032Speter** Parameters: 444938032Speter** mci -- the connection information. 445038032Speter** e -- the envelope. 445138032Speter** 445238032Speter** Returns: 4453157001Sgshapiro** true iff line was written successfully 445438032Speter** 445538032Speter** Side Effects: 445638032Speter** outputs some text to fp. 445738032Speter*/ 445838032Speter 4459157001Sgshapirobool 446038032Speterputfromline(mci, e) 446138032Speter register MCI *mci; 446238032Speter ENVELOPE *e; 446338032Speter{ 446438032Speter char *template = UnixFromLine; 446538032Speter char buf[MAXLINE]; 446638032Speter char xbuf[MAXLINE]; 446738032Speter 446838032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 4469157001Sgshapiro return true; 447038032Speter 447138032Speter mci->mci_flags |= MCIF_INHEADER; 447238032Speter 447338032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 447438032Speter { 447538032Speter char *bang; 447638032Speter 447738032Speter expand("\201g", buf, sizeof buf, e); 447838032Speter bang = strchr(buf, '!'); 447938032Speter if (bang == NULL) 448038032Speter { 448138032Speter char *at; 448238032Speter char hname[MAXNAME]; 448338032Speter 448464562Sgshapiro /* 448542575Speter ** If we can construct a UUCP path, do so 448638032Speter */ 448738032Speter 448838032Speter at = strrchr(buf, '@'); 448938032Speter if (at == NULL) 449038032Speter { 449164562Sgshapiro expand("\201k", hname, sizeof hname, e); 449238032Speter at = hname; 449338032Speter } 449438032Speter else 449538032Speter *at++ = '\0'; 449690792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 449790792Sgshapiro "From %.800s \201d remote from %.100s\n", 449890792Sgshapiro buf, at); 449938032Speter } 450038032Speter else 450138032Speter { 450238032Speter *bang++ = '\0'; 450390792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 450490792Sgshapiro "From %.800s \201d remote from %.100s\n", 450590792Sgshapiro bang, buf); 450638032Speter template = xbuf; 450738032Speter } 450838032Speter } 450938032Speter expand(template, buf, sizeof buf, e); 4510157001Sgshapiro return putxline(buf, strlen(buf), mci, PXLF_HEADER); 451138032Speter} 4512157001Sgshapiro 451390792Sgshapiro/* 451438032Speter** PUTBODY -- put the body of a message. 451538032Speter** 451638032Speter** Parameters: 451738032Speter** mci -- the connection information. 451838032Speter** e -- the envelope to put out. 451938032Speter** separator -- if non-NULL, a message separator that must 452038032Speter** not be permitted in the resulting message. 452138032Speter** 452238032Speter** Returns: 4523157001Sgshapiro** true iff message was written successfully 452438032Speter** 452538032Speter** Side Effects: 452638032Speter** The message is written onto fp. 452738032Speter*/ 452838032Speter 452938032Speter/* values for output state variable */ 4530157001Sgshapiro#define OSTATE_HEAD 0 /* at beginning of line */ 4531157001Sgshapiro#define OSTATE_CR 1 /* read a carriage return */ 4532157001Sgshapiro#define OSTATE_INLINE 2 /* putting rest of line */ 453338032Speter 4534157001Sgshapirobool 453538032Speterputbody(mci, e, separator) 453638032Speter register MCI *mci; 453738032Speter register ENVELOPE *e; 453838032Speter char *separator; 453938032Speter{ 454090792Sgshapiro bool dead = false; 4541157001Sgshapiro bool ioerr = false; 4542157001Sgshapiro int save_errno; 454338032Speter char buf[MAXLINE]; 454490792Sgshapiro#if MIME8TO7 454542575Speter char *boundaries[MAXMIMENESTING + 1]; 454690792Sgshapiro#endif /* MIME8TO7 */ 454738032Speter 454838032Speter /* 454938032Speter ** Output the body of the message 455038032Speter */ 455138032Speter 455238032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 455338032Speter { 455490792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 455538032Speter 455690792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4557120256Sgshapiro SM_IO_RDONLY_B, NULL); 455838032Speter if (e->e_dfp == NULL) 455964562Sgshapiro { 456064562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 456164562Sgshapiro 456264562Sgshapiro if (errno == ENOENT) 456364562Sgshapiro msg++; 456464562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 456564562Sgshapiro } 456690792Sgshapiro 456738032Speter } 456838032Speter if (e->e_dfp == NULL) 456938032Speter { 457038032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 457138032Speter { 4572157001Sgshapiro if (!putline("", mci)) 4573157001Sgshapiro goto writeerr; 457438032Speter mci->mci_flags &= ~MCIF_INHEADER; 457538032Speter } 4576157001Sgshapiro if (!putline("<<< No Message Collected >>>", mci)) 4577157001Sgshapiro goto writeerr; 457838032Speter goto endofmessage; 457938032Speter } 458064562Sgshapiro 458138032Speter if (e->e_dfino == (ino_t) 0) 458238032Speter { 458338032Speter struct stat stbuf; 458438032Speter 458590792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 458690792Sgshapiro < 0) 458738032Speter e->e_dfino = -1; 458838032Speter else 458938032Speter { 459038032Speter e->e_dfdev = stbuf.st_dev; 459138032Speter e->e_dfino = stbuf.st_ino; 459238032Speter } 459338032Speter } 459438032Speter 459590792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 459664562Sgshapiro (void) bfrewind(e->e_dfp); 459764562Sgshapiro 4598157001Sgshapiro /* simulate an I/O timeout when used as source */ 4599157001Sgshapiro if (tTd(84, 101)) 4600157001Sgshapiro sleep(319); 4601157001Sgshapiro 460238032Speter#if MIME8TO7 460338032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 460438032Speter { 460538032Speter /* 460638032Speter ** Do 8 to 7 bit MIME conversion. 460738032Speter */ 460838032Speter 460938032Speter /* make sure it looks like a MIME message */ 4610157001Sgshapiro if (hvalue("MIME-Version", e->e_header) == NULL && 4611157001Sgshapiro !putline("MIME-Version: 1.0", mci)) 4612157001Sgshapiro goto writeerr; 461338032Speter 461438032Speter if (hvalue("Content-Type", e->e_header) == NULL) 461538032Speter { 461690792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 461790792Sgshapiro "Content-Type: text/plain; charset=%s", 461890792Sgshapiro defcharset(e)); 4619157001Sgshapiro if (!putline(buf, mci)) 4620157001Sgshapiro goto writeerr; 462138032Speter } 462238032Speter 462338032Speter /* now do the hard work */ 462438032Speter boundaries[0] = NULL; 462538032Speter mci->mci_flags |= MCIF_INHEADER; 4626157001Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER) == 4627157001Sgshapiro SM_IO_EOF) 4628157001Sgshapiro goto writeerr; 462938032Speter } 463038032Speter# if MIME7TO8 463138032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 463238032Speter { 4633157001Sgshapiro if (!mime7to8(mci, e->e_header, e)) 4634157001Sgshapiro goto writeerr; 463538032Speter } 463664562Sgshapiro# endif /* MIME7TO8 */ 463742575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 463842575Speter { 463964562Sgshapiro bool oldsuprerrs = SuprErrs; 464064562Sgshapiro 464142575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 464242575Speter boundaries[0] = NULL; 464342575Speter mci->mci_flags |= MCIF_INHEADER; 464464562Sgshapiro 464564562Sgshapiro /* 464664562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 464764562Sgshapiro ** and don't want to generate a new bounce message whose 464864562Sgshapiro ** body propagates the broken MIME. We can't just not call 464964562Sgshapiro ** mime8to7() as is done above since we need the security 465064562Sgshapiro ** checks. The best we can do is suppress the errors. 465164562Sgshapiro */ 465264562Sgshapiro 465364562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 465490792Sgshapiro SuprErrs = true; 465564562Sgshapiro 4656157001Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, 4657157001Sgshapiro M87F_OUTER|M87F_NO8TO7) == SM_IO_EOF) 4658157001Sgshapiro goto writeerr; 465964562Sgshapiro 466064562Sgshapiro /* restore SuprErrs */ 466164562Sgshapiro SuprErrs = oldsuprerrs; 466242575Speter } 466338032Speter else 466464562Sgshapiro#endif /* MIME8TO7 */ 466538032Speter { 466638032Speter int ostate; 466738032Speter register char *bp; 466838032Speter register char *pbp; 466938032Speter register int c; 467038032Speter register char *xp; 467138032Speter int padc; 467238032Speter char *buflim; 467338032Speter int pos = 0; 467464562Sgshapiro char peekbuf[12]; 467538032Speter 467638032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 467738032Speter { 4678157001Sgshapiro if (!putline("", mci)) 4679157001Sgshapiro goto writeerr; 468038032Speter mci->mci_flags &= ~MCIF_INHEADER; 468138032Speter } 468238032Speter 468338032Speter /* determine end of buffer; allow for short mailer lines */ 468438032Speter buflim = &buf[sizeof buf - 1]; 468538032Speter if (mci->mci_mailer->m_linelimit > 0 && 468638032Speter mci->mci_mailer->m_linelimit < sizeof buf - 1) 468738032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 468838032Speter 468938032Speter /* copy temp file to output with mapping */ 4690157001Sgshapiro ostate = OSTATE_HEAD; 469138032Speter bp = buf; 469238032Speter pbp = peekbuf; 469390792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 469438032Speter { 469538032Speter if (pbp > peekbuf) 469638032Speter c = *--pbp; 469790792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 469890792Sgshapiro == SM_IO_EOF) 469938032Speter break; 470038032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 470138032Speter c &= 0x7f; 470238032Speter switch (ostate) 470338032Speter { 4704157001Sgshapiro case OSTATE_HEAD: 470538032Speter if (c == '\0' && 470690792Sgshapiro bitnset(M_NONULLS, 470790792Sgshapiro mci->mci_mailer->m_flags)) 470838032Speter break; 470938032Speter if (c != '\r' && c != '\n' && bp < buflim) 471038032Speter { 471138032Speter *bp++ = c; 471238032Speter break; 471338032Speter } 471438032Speter 471538032Speter /* check beginning of line for special cases */ 471638032Speter *bp = '\0'; 471738032Speter pos = 0; 471890792Sgshapiro padc = SM_IO_EOF; 471938032Speter if (buf[0] == 'F' && 472090792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 472190792Sgshapiro && strncmp(buf, "From ", 5) == 0) 472238032Speter { 472338032Speter padc = '>'; 472438032Speter } 472538032Speter if (buf[0] == '-' && buf[1] == '-' && 472638032Speter separator != NULL) 472738032Speter { 472838032Speter /* possible separator */ 472938032Speter int sl = strlen(separator); 473038032Speter 473190792Sgshapiro if (strncmp(&buf[2], separator, sl) 473290792Sgshapiro == 0) 473338032Speter padc = ' '; 473438032Speter } 473538032Speter if (buf[0] == '.' && 473638032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 473738032Speter { 473838032Speter padc = '.'; 473938032Speter } 474038032Speter 474138032Speter /* now copy out saved line */ 474238032Speter if (TrafficLogFile != NULL) 474338032Speter { 474490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 474590792Sgshapiro SM_TIME_DEFAULT, 474690792Sgshapiro "%05d >>> ", 474790792Sgshapiro (int) CurrentPid); 474890792Sgshapiro if (padc != SM_IO_EOF) 474990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 475090792Sgshapiro SM_TIME_DEFAULT, 475190792Sgshapiro padc); 475238032Speter for (xp = buf; xp < bp; xp++) 475390792Sgshapiro (void) sm_io_putc(TrafficLogFile, 475490792Sgshapiro SM_TIME_DEFAULT, 475590792Sgshapiro (unsigned char) *xp); 475638032Speter if (c == '\n') 475790792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 475890792Sgshapiro SM_TIME_DEFAULT, 475990792Sgshapiro mci->mci_mailer->m_eol); 476038032Speter } 476190792Sgshapiro if (padc != SM_IO_EOF) 476238032Speter { 476390792Sgshapiro if (sm_io_putc(mci->mci_out, 476490792Sgshapiro SM_TIME_DEFAULT, padc) 476590792Sgshapiro == SM_IO_EOF) 476664562Sgshapiro { 476790792Sgshapiro dead = true; 476864562Sgshapiro continue; 476964562Sgshapiro } 477038032Speter pos++; 477138032Speter } 477238032Speter for (xp = buf; xp < bp; xp++) 477338032Speter { 477490792Sgshapiro if (sm_io_putc(mci->mci_out, 477590792Sgshapiro SM_TIME_DEFAULT, 477690792Sgshapiro (unsigned char) *xp) 477790792Sgshapiro == SM_IO_EOF) 477864562Sgshapiro { 477990792Sgshapiro dead = true; 478064562Sgshapiro break; 478164562Sgshapiro } 478238032Speter } 478364562Sgshapiro if (dead) 478464562Sgshapiro continue; 478538032Speter if (c == '\n') 478638032Speter { 478790792Sgshapiro if (sm_io_fputs(mci->mci_out, 478890792Sgshapiro SM_TIME_DEFAULT, 478990792Sgshapiro mci->mci_mailer->m_eol) 479090792Sgshapiro == SM_IO_EOF) 479164562Sgshapiro break; 479238032Speter pos = 0; 479338032Speter } 479438032Speter else 479538032Speter { 479638032Speter pos += bp - buf; 479738032Speter if (c != '\r') 4798112810Sgshapiro { 4799112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4800112810Sgshapiro sizeof(peekbuf)); 480138032Speter *pbp++ = c; 4802112810Sgshapiro } 480338032Speter } 480464562Sgshapiro 480538032Speter bp = buf; 480638032Speter 480738032Speter /* determine next state */ 480838032Speter if (c == '\n') 4809157001Sgshapiro ostate = OSTATE_HEAD; 481038032Speter else if (c == '\r') 4811157001Sgshapiro ostate = OSTATE_CR; 481238032Speter else 4813157001Sgshapiro ostate = OSTATE_INLINE; 481438032Speter continue; 481538032Speter 4816157001Sgshapiro case OSTATE_CR: 481738032Speter if (c == '\n') 481838032Speter { 481938032Speter /* got CRLF */ 482090792Sgshapiro if (sm_io_fputs(mci->mci_out, 482190792Sgshapiro SM_TIME_DEFAULT, 482290792Sgshapiro mci->mci_mailer->m_eol) 482390792Sgshapiro == SM_IO_EOF) 482464562Sgshapiro continue; 482564562Sgshapiro 482638032Speter if (TrafficLogFile != NULL) 482738032Speter { 482890792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 482990792Sgshapiro SM_TIME_DEFAULT, 483090792Sgshapiro mci->mci_mailer->m_eol); 483138032Speter } 4832157001Sgshapiro ostate = OSTATE_HEAD; 483338032Speter continue; 483438032Speter } 483538032Speter 483638032Speter /* had a naked carriage return */ 4837112810Sgshapiro SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 483838032Speter *pbp++ = c; 483938032Speter c = '\r'; 4840157001Sgshapiro ostate = OSTATE_INLINE; 484138032Speter goto putch; 484238032Speter 4843157001Sgshapiro case OSTATE_INLINE: 484438032Speter if (c == '\r') 484538032Speter { 4846157001Sgshapiro ostate = OSTATE_CR; 484738032Speter continue; 484838032Speter } 484938032Speter if (c == '\0' && 485090792Sgshapiro bitnset(M_NONULLS, 485190792Sgshapiro mci->mci_mailer->m_flags)) 485238032Speter break; 485338032Speterputch: 485438032Speter if (mci->mci_mailer->m_linelimit > 0 && 485564562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 485638032Speter c != '\n') 485738032Speter { 485864562Sgshapiro int d; 485964562Sgshapiro 486064562Sgshapiro /* check next character for EOL */ 486164562Sgshapiro if (pbp > peekbuf) 486264562Sgshapiro d = *(pbp - 1); 486390792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 486490792Sgshapiro SM_TIME_DEFAULT)) 486590792Sgshapiro != SM_IO_EOF) 4866112810Sgshapiro { 4867112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4868112810Sgshapiro sizeof(peekbuf)); 486964562Sgshapiro *pbp++ = d; 4870112810Sgshapiro } 487164562Sgshapiro 487290792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 487364562Sgshapiro { 487464562Sgshapiro if (TrafficLogFile != NULL) 487590792Sgshapiro (void) sm_io_putc(TrafficLogFile, 487690792Sgshapiro SM_TIME_DEFAULT, 487790792Sgshapiro (unsigned char) c); 487890792Sgshapiro if (sm_io_putc(mci->mci_out, 487990792Sgshapiro SM_TIME_DEFAULT, 488090792Sgshapiro (unsigned char) c) 488190792Sgshapiro == SM_IO_EOF) 488264562Sgshapiro { 488390792Sgshapiro dead = true; 488464562Sgshapiro continue; 488564562Sgshapiro } 488664562Sgshapiro pos++; 488764562Sgshapiro continue; 488864562Sgshapiro } 488964562Sgshapiro 489090792Sgshapiro if (sm_io_putc(mci->mci_out, 489190792Sgshapiro SM_TIME_DEFAULT, '!') 489290792Sgshapiro == SM_IO_EOF || 489390792Sgshapiro sm_io_fputs(mci->mci_out, 489490792Sgshapiro SM_TIME_DEFAULT, 489590792Sgshapiro mci->mci_mailer->m_eol) 489690792Sgshapiro == SM_IO_EOF) 489764562Sgshapiro { 489890792Sgshapiro dead = true; 489964562Sgshapiro continue; 490064562Sgshapiro } 490164562Sgshapiro 490238032Speter if (TrafficLogFile != NULL) 490338032Speter { 490490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 490590792Sgshapiro SM_TIME_DEFAULT, 490690792Sgshapiro "!%s", 490790792Sgshapiro mci->mci_mailer->m_eol); 490838032Speter } 4909157001Sgshapiro ostate = OSTATE_HEAD; 4910112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4911112810Sgshapiro sizeof(peekbuf)); 491238032Speter *pbp++ = c; 491338032Speter continue; 491438032Speter } 491538032Speter if (c == '\n') 491638032Speter { 491738032Speter if (TrafficLogFile != NULL) 491890792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 491990792Sgshapiro SM_TIME_DEFAULT, 492090792Sgshapiro mci->mci_mailer->m_eol); 492190792Sgshapiro if (sm_io_fputs(mci->mci_out, 492290792Sgshapiro SM_TIME_DEFAULT, 492390792Sgshapiro mci->mci_mailer->m_eol) 492490792Sgshapiro == SM_IO_EOF) 492564562Sgshapiro continue; 492638032Speter pos = 0; 4927157001Sgshapiro ostate = OSTATE_HEAD; 492838032Speter } 492938032Speter else 493038032Speter { 493138032Speter if (TrafficLogFile != NULL) 493290792Sgshapiro (void) sm_io_putc(TrafficLogFile, 493390792Sgshapiro SM_TIME_DEFAULT, 493490792Sgshapiro (unsigned char) c); 493590792Sgshapiro if (sm_io_putc(mci->mci_out, 493690792Sgshapiro SM_TIME_DEFAULT, 493790792Sgshapiro (unsigned char) c) 493890792Sgshapiro == SM_IO_EOF) 493964562Sgshapiro { 494090792Sgshapiro dead = true; 494164562Sgshapiro continue; 494264562Sgshapiro } 494338032Speter pos++; 4944157001Sgshapiro ostate = OSTATE_INLINE; 494538032Speter } 494638032Speter break; 494738032Speter } 494838032Speter } 494938032Speter 495038032Speter /* make sure we are at the beginning of a line */ 495138032Speter if (bp > buf) 495238032Speter { 495338032Speter if (TrafficLogFile != NULL) 495438032Speter { 495538032Speter for (xp = buf; xp < bp; xp++) 495690792Sgshapiro (void) sm_io_putc(TrafficLogFile, 495790792Sgshapiro SM_TIME_DEFAULT, 495890792Sgshapiro (unsigned char) *xp); 495938032Speter } 496038032Speter for (xp = buf; xp < bp; xp++) 496138032Speter { 496290792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 496390792Sgshapiro (unsigned char) *xp) 496490792Sgshapiro == SM_IO_EOF) 496564562Sgshapiro { 496690792Sgshapiro dead = true; 496764562Sgshapiro break; 496864562Sgshapiro } 496938032Speter } 497038032Speter pos += bp - buf; 497138032Speter } 497264562Sgshapiro if (!dead && pos > 0) 497338032Speter { 497438032Speter if (TrafficLogFile != NULL) 497590792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 497690792Sgshapiro SM_TIME_DEFAULT, 497790792Sgshapiro mci->mci_mailer->m_eol); 4978157001Sgshapiro if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 4979157001Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF) 4980157001Sgshapiro goto writeerr; 498138032Speter } 498238032Speter } 498338032Speter 498490792Sgshapiro if (sm_io_error(e->e_dfp)) 498538032Speter { 498690792Sgshapiro syserr("putbody: %s/%cf%s: read error", 498790792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 498890792Sgshapiro DATAFL_LETTER, e->e_id); 498938032Speter ExitStat = EX_IOERR; 4990157001Sgshapiro ioerr = true; 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 5005157001Sgshapiro save_errno = errno; 500664562Sgshapiro if (e->e_dfp != NULL) 500764562Sgshapiro (void) bfrewind(e->e_dfp); 500864562Sgshapiro 500938032Speter /* some mailers want extra blank line at end of message */ 501064562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 501138032Speter buf[0] != '\0' && buf[0] != '\n') 5012157001Sgshapiro { 5013157001Sgshapiro if (!putline("", mci)) 5014157001Sgshapiro goto writeerr; 5015157001Sgshapiro } 501638032Speter 5017157001Sgshapiro if (!dead && 5018157001Sgshapiro (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF || 5019157001Sgshapiro (sm_io_error(mci->mci_out) && errno != EPIPE))) 502038032Speter { 5021157001Sgshapiro save_errno = errno; 502238032Speter syserr("putbody: write error"); 502338032Speter ExitStat = EX_IOERR; 5024157001Sgshapiro ioerr = true; 502538032Speter } 502664562Sgshapiro 5027157001Sgshapiro errno = save_errno; 5028157001Sgshapiro return !dead && !ioerr; 5029157001Sgshapiro 5030157001Sgshapiro writeerr: 5031157001Sgshapiro return false; 503238032Speter} 5033157001Sgshapiro 503490792Sgshapiro/* 503538032Speter** MAILFILE -- Send a message to a file. 503638032Speter** 503790792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 503890792Sgshapiro** execute bits, sendmail will try to become the owner of that file 503938032Speter** rather than the real user. Obviously, this only works if 504038032Speter** sendmail runs as root. 504138032Speter** 504238032Speter** This could be done as a subordinate mailer, except that it 504338032Speter** is used implicitly to save messages in ~/dead.letter. We 504438032Speter** view this as being sufficiently important as to include it 504538032Speter** here. For example, if the system is dying, we shouldn't have 504638032Speter** to create another process plus some pipes to save the message. 504738032Speter** 504838032Speter** Parameters: 504938032Speter** filename -- the name of the file to send to. 505038032Speter** mailer -- mailer definition for recipient -- if NULL, 505138032Speter** use FileMailer. 505238032Speter** ctladdr -- the controlling address header -- includes 505338032Speter** the userid/groupid to be when sending. 505438032Speter** sfflags -- flags for opening. 505538032Speter** e -- the current envelope. 505638032Speter** 505738032Speter** Returns: 505838032Speter** The exit code associated with the operation. 505938032Speter** 506038032Speter** Side Effects: 506138032Speter** none. 506238032Speter*/ 506338032Speter 506490792Sgshapiro# define RETURN(st) exit(st); 506590792Sgshapiro 506638032Speterstatic jmp_buf CtxMailfileTimeout; 506738032Speter 506838032Speterint 506938032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 507038032Speter char *volatile filename; 507138032Speter MAILER *volatile mailer; 507238032Speter ADDRESS *ctladdr; 507364562Sgshapiro volatile long sfflags; 507438032Speter register ENVELOPE *e; 507538032Speter{ 507690792Sgshapiro register SM_FILE_T *f; 507738032Speter register pid_t pid = -1; 507864562Sgshapiro volatile int mode; 507964562Sgshapiro int len; 508064562Sgshapiro off_t curoff; 508138032Speter bool suidwarn = geteuid() == 0; 508238032Speter char *p; 508364562Sgshapiro char *volatile realfile; 508490792Sgshapiro SM_EVENT *ev; 508598121Sgshapiro char buf[MAXPATHLEN]; 508698121Sgshapiro char targetfile[MAXPATHLEN]; 508738032Speter 508838032Speter if (tTd(11, 1)) 508938032Speter { 509090792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 5091132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 509238032Speter } 509338032Speter 509438032Speter if (mailer == NULL) 509538032Speter mailer = FileMailer; 509638032Speter 509738032Speter if (e->e_xfp != NULL) 509890792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 509938032Speter 510038032Speter /* 510138032Speter ** Special case /dev/null. This allows us to restrict file 510238032Speter ** delivery to regular files only. 510338032Speter */ 510438032Speter 510590792Sgshapiro if (sm_path_isdevnull(filename)) 510638032Speter return EX_OK; 510738032Speter 510838032Speter /* check for 8-bit available */ 510938032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 511038032Speter bitnset(M_7BITS, mailer->m_flags) && 511138032Speter (bitset(EF_DONT_MIME, e->e_flags) || 511238032Speter !(bitset(MM_MIME8BIT, MimeMode) || 511338032Speter (bitset(EF_IS_MIME, e->e_flags) && 511438032Speter bitset(MM_CVTMIME, MimeMode))))) 511538032Speter { 511638032Speter e->e_status = "5.6.3"; 511764562Sgshapiro usrerrenh(e->e_status, 511890792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 511990792Sgshapiro errno = 0; 512064562Sgshapiro return EX_DATAERR; 512138032Speter } 512238032Speter 512364562Sgshapiro /* Find the actual file */ 512464562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 512564562Sgshapiro { 512664562Sgshapiro len = strlen(SafeFileEnv); 512764562Sgshapiro 512864562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 512964562Sgshapiro filename += len; 513064562Sgshapiro 513190792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 513264562Sgshapiro { 513364562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 513464562Sgshapiro SafeFileEnv, filename); 513564562Sgshapiro return EX_CANTCREAT; 513664562Sgshapiro } 513790792Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile); 513864562Sgshapiro realfile = targetfile + len; 513964562Sgshapiro if (*filename == '/') 514064562Sgshapiro filename++; 514194334Sgshapiro if (*filename != '\0') 514294334Sgshapiro { 514394334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 514494334Sgshapiro if (targetfile[len - 1] != '/') 514594334Sgshapiro (void) sm_strlcat(targetfile, 514694334Sgshapiro "/", sizeof targetfile); 514794334Sgshapiro (void) sm_strlcat(targetfile, filename, 514894334Sgshapiro sizeof targetfile); 514994334Sgshapiro } 515064562Sgshapiro } 515164562Sgshapiro else if (mailer->m_rootdir != NULL) 515264562Sgshapiro { 515364562Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof targetfile, e); 515464562Sgshapiro len = strlen(targetfile); 515564562Sgshapiro 515664562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 515764562Sgshapiro filename += len; 515864562Sgshapiro 515990792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 516064562Sgshapiro { 516164562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 516264562Sgshapiro targetfile, filename); 516364562Sgshapiro return EX_CANTCREAT; 516464562Sgshapiro } 516564562Sgshapiro realfile = targetfile + len; 516664562Sgshapiro if (targetfile[len - 1] != '/') 516790792Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof targetfile); 516864562Sgshapiro if (*filename == '/') 516990792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 517090792Sgshapiro sizeof targetfile); 517164562Sgshapiro else 517290792Sgshapiro (void) sm_strlcat(targetfile, filename, 517390792Sgshapiro sizeof targetfile); 517464562Sgshapiro } 517564562Sgshapiro else 517664562Sgshapiro { 517790792Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof targetfile) >= 517890792Sgshapiro sizeof targetfile) 517964562Sgshapiro { 518064562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 518164562Sgshapiro return EX_CANTCREAT; 518264562Sgshapiro } 518364562Sgshapiro realfile = targetfile; 518464562Sgshapiro } 518564562Sgshapiro 518638032Speter /* 518738032Speter ** Fork so we can change permissions here. 518838032Speter ** Note that we MUST use fork, not vfork, because of 518938032Speter ** the complications of calling subroutines, etc. 519038032Speter */ 519138032Speter 519294334Sgshapiro 519394334Sgshapiro /* 519494334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 519594334Sgshapiro ** around so that the waitfor() below will get it. 519694334Sgshapiro */ 519794334Sgshapiro 519894334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 519994334Sgshapiro 520038032Speter DOFORK(fork); 520138032Speter 520238032Speter if (pid < 0) 520364562Sgshapiro return EX_OSERR; 520438032Speter else if (pid == 0) 520538032Speter { 520638032Speter /* child -- actually write to file */ 520738032Speter struct stat stb; 520838032Speter MCI mcibuf; 520942575Speter int err; 521038032Speter volatile int oflags = O_WRONLY|O_APPEND; 521138032Speter 521277349Sgshapiro /* Reset global flags */ 521377349Sgshapiro RestartRequest = NULL; 521490792Sgshapiro RestartWorkGroup = false; 521577349Sgshapiro ShutdownRequest = NULL; 521677349Sgshapiro PendingSignal = 0; 521790792Sgshapiro CurrentPid = getpid(); 521877349Sgshapiro 521938032Speter if (e->e_lockfp != NULL) 522090792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 522190792Sgshapiro NULL)); 522238032Speter 522390792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 522490792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 522590792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 522638032Speter (void) umask(OldUmask); 522738032Speter e->e_to = filename; 522838032Speter ExitStat = EX_OK; 522938032Speter 523038032Speter if (setjmp(CtxMailfileTimeout) != 0) 523138032Speter { 523290792Sgshapiro RETURN(EX_TEMPFAIL); 523338032Speter } 523438032Speter 523538032Speter if (TimeOuts.to_fileopen > 0) 523690792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 523790792Sgshapiro 0); 523838032Speter else 523938032Speter ev = NULL; 524038032Speter 524190792Sgshapiro /* check file mode to see if set-user-ID */ 524264562Sgshapiro if (stat(targetfile, &stb) < 0) 524364562Sgshapiro mode = FileMode; 524442575Speter else 524538032Speter mode = stb.st_mode; 524638032Speter 524738032Speter /* limit the errors to those actually caused in the child */ 524838032Speter errno = 0; 524938032Speter ExitStat = EX_OK; 525038032Speter 525164562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 525264562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 525364562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 525438032Speter { 525590792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 525638032Speter mode &= ~(S_ISGID|S_ISUID); 525764562Sgshapiro if (tTd(11, 20)) 525890792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 525938032Speter } 526038032Speter 526190792Sgshapiro /* we have to open the data file BEFORE setuid() */ 526238032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 526338032Speter { 526490792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 526538032Speter 526690792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5267120256Sgshapiro SM_IO_RDONLY_B, NULL); 526838032Speter if (e->e_dfp == NULL) 526938032Speter { 527038032Speter syserr("mailfile: Cannot open %s for %s from %s", 527138032Speter df, e->e_to, e->e_from.q_paddr); 527238032Speter } 527338032Speter } 527438032Speter 527538032Speter /* select a new user to run as */ 527638032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 527738032Speter { 527838032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 527938032Speter { 528038032Speter RealUserName = NULL; 5281132943Sgshapiro if (mailer->m_uid == NO_UID) 5282132943Sgshapiro RealUid = RunAsUid; 5283132943Sgshapiro else 5284132943Sgshapiro RealUid = mailer->m_uid; 528564562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 528664562Sgshapiro { 528764562Sgshapiro /* Only root can change the uid */ 528890792Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 528990792Sgshapiro (int) RunAsUid, (int) RealUid); 529090792Sgshapiro RETURN(EX_TEMPFAIL); 529164562Sgshapiro } 529238032Speter } 529338032Speter else if (bitset(S_ISUID, mode)) 529438032Speter { 529538032Speter RealUserName = NULL; 529638032Speter RealUid = stb.st_uid; 529738032Speter } 529838032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 529938032Speter { 530038032Speter if (ctladdr->q_ruser != NULL) 530138032Speter RealUserName = ctladdr->q_ruser; 530238032Speter else 530338032Speter RealUserName = ctladdr->q_user; 530438032Speter RealUid = ctladdr->q_uid; 530538032Speter } 5306132943Sgshapiro else if (mailer != NULL && mailer->m_uid != NO_UID) 530738032Speter { 530838032Speter RealUserName = DefUser; 530938032Speter RealUid = mailer->m_uid; 531038032Speter } 531138032Speter else 531238032Speter { 531338032Speter RealUserName = DefUser; 531438032Speter RealUid = DefUid; 531538032Speter } 531638032Speter 531738032Speter /* select a new group to run as */ 531838032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 531964562Sgshapiro { 5320132943Sgshapiro if (mailer->m_gid == NO_GID) 5321132943Sgshapiro RealGid = RunAsGid; 5322132943Sgshapiro else 5323132943Sgshapiro RealGid = mailer->m_gid; 532464562Sgshapiro if (RunAsUid != 0 && 532564562Sgshapiro (RealGid != getgid() || 532664562Sgshapiro RealGid != getegid())) 532764562Sgshapiro { 532864562Sgshapiro /* Only root can change the gid */ 532990792Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 533090792Sgshapiro (int) RealGid, (int) RunAsUid, 533190792Sgshapiro (int) getgid(), (int) getegid()); 533290792Sgshapiro RETURN(EX_TEMPFAIL); 533364562Sgshapiro } 533464562Sgshapiro } 533538032Speter else if (bitset(S_ISGID, mode)) 533638032Speter RealGid = stb.st_gid; 533764562Sgshapiro else if (ctladdr != NULL && 533864562Sgshapiro ctladdr->q_uid == DefUid && 533964562Sgshapiro ctladdr->q_gid == 0) 534071345Sgshapiro { 534171345Sgshapiro /* 534271345Sgshapiro ** Special case: This means it is an 534371345Sgshapiro ** alias and we should act as DefaultUser. 534471345Sgshapiro ** See alias()'s comments. 534571345Sgshapiro */ 534671345Sgshapiro 534764562Sgshapiro RealGid = DefGid; 534871345Sgshapiro RealUserName = DefUser; 534971345Sgshapiro } 535071345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 535171345Sgshapiro RealGid = ctladdr->q_gid; 5352132943Sgshapiro else if (mailer != NULL && mailer->m_gid != NO_GID) 535338032Speter RealGid = mailer->m_gid; 535438032Speter else 535538032Speter RealGid = DefGid; 535638032Speter } 535738032Speter 535838032Speter /* last ditch */ 535938032Speter if (!bitset(SFF_ROOTOK, sfflags)) 536038032Speter { 536138032Speter if (RealUid == 0) 536238032Speter RealUid = DefUid; 536338032Speter if (RealGid == 0) 536438032Speter RealGid = DefGid; 536538032Speter } 536638032Speter 536738032Speter /* set group id list (needs /etc/group access) */ 536838032Speter if (RealUserName != NULL && !DontInitGroups) 536938032Speter { 537038032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 537164562Sgshapiro { 537238032Speter syserr("mailfile: initgroups(%s, %d) failed", 537338032Speter RealUserName, RealGid); 537490792Sgshapiro RETURN(EX_TEMPFAIL); 537564562Sgshapiro } 537638032Speter } 537738032Speter else 537838032Speter { 537938032Speter GIDSET_T gidset[1]; 538038032Speter 538138032Speter gidset[0] = RealGid; 538238032Speter if (setgroups(1, gidset) == -1 && suidwarn) 538364562Sgshapiro { 538438032Speter syserr("mailfile: setgroups() failed"); 538590792Sgshapiro RETURN(EX_TEMPFAIL); 538664562Sgshapiro } 538738032Speter } 538838032Speter 538964562Sgshapiro /* 539064562Sgshapiro ** If you have a safe environment, go into it. 539164562Sgshapiro */ 539264562Sgshapiro 539364562Sgshapiro if (realfile != targetfile) 539438032Speter { 539594334Sgshapiro char save; 539694334Sgshapiro 539794334Sgshapiro save = *realfile; 539864562Sgshapiro *realfile = '\0'; 539964562Sgshapiro if (tTd(11, 20)) 540090792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 540164562Sgshapiro if (chroot(targetfile) < 0) 540238032Speter { 540338032Speter syserr("mailfile: Cannot chroot(%s)", 540464562Sgshapiro targetfile); 540590792Sgshapiro RETURN(EX_CANTCREAT); 540638032Speter } 540794334Sgshapiro *realfile = save; 540838032Speter } 540964562Sgshapiro 541064562Sgshapiro if (tTd(11, 40)) 541190792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 541264562Sgshapiro 541338032Speter if (chdir("/") < 0) 541464562Sgshapiro { 541538032Speter syserr("mailfile: cannot chdir(/)"); 541690792Sgshapiro RETURN(EX_CANTCREAT); 541764562Sgshapiro } 541838032Speter 541938032Speter /* now reset the group and user ids */ 542038032Speter endpwent(); 542190792Sgshapiro sm_mbdb_terminate(); 542238032Speter if (setgid(RealGid) < 0 && suidwarn) 542364562Sgshapiro { 542438032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 542590792Sgshapiro RETURN(EX_TEMPFAIL); 542664562Sgshapiro } 542738032Speter vendor_set_uid(RealUid); 542838032Speter if (setuid(RealUid) < 0 && suidwarn) 542964562Sgshapiro { 543038032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 543190792Sgshapiro RETURN(EX_TEMPFAIL); 543264562Sgshapiro } 543338032Speter 543464562Sgshapiro if (tTd(11, 2)) 543590792Sgshapiro sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 543664562Sgshapiro (int) getuid(), (int) geteuid(), 543764562Sgshapiro (int) getgid(), (int) getegid()); 543864562Sgshapiro 543964562Sgshapiro 544038032Speter /* move into some "safe" directory */ 544138032Speter if (mailer->m_execdir != NULL) 544238032Speter { 544338032Speter char *q; 544438032Speter 544538032Speter for (p = mailer->m_execdir; p != NULL; p = q) 544638032Speter { 544738032Speter q = strchr(p, ':'); 544838032Speter if (q != NULL) 544938032Speter *q = '\0'; 545038032Speter expand(p, buf, sizeof buf, e); 545138032Speter if (q != NULL) 545238032Speter *q++ = ':'; 545338032Speter if (tTd(11, 20)) 545490792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 545590792Sgshapiro buf); 545638032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 545738032Speter break; 545838032Speter } 545938032Speter } 546038032Speter 546164562Sgshapiro /* 546264562Sgshapiro ** Recheck the file after we have assumed the ID of the 546364562Sgshapiro ** delivery user to make sure we can deliver to it as 546464562Sgshapiro ** that user. This is necessary if sendmail is running 546564562Sgshapiro ** as root and the file is on an NFS mount which treats 546664562Sgshapiro ** root as nobody. 546764562Sgshapiro */ 546864562Sgshapiro 546964562Sgshapiro#if HASLSTAT 547064562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 547164562Sgshapiro err = stat(realfile, &stb); 547264562Sgshapiro else 547364562Sgshapiro err = lstat(realfile, &stb); 547464562Sgshapiro#else /* HASLSTAT */ 547564562Sgshapiro err = stat(realfile, &stb); 547664562Sgshapiro#endif /* HASLSTAT */ 547764562Sgshapiro 547864562Sgshapiro if (err < 0) 547964562Sgshapiro { 548064562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 548164562Sgshapiro mode = FileMode; 548264562Sgshapiro oflags |= O_CREAT|O_EXCL; 548364562Sgshapiro } 548464562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 548564562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 548664562Sgshapiro DontBlameSendmail) && 548764562Sgshapiro stb.st_nlink != 1) || 548864562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 548964562Sgshapiro exit(EX_CANTCREAT); 549064562Sgshapiro else 549164562Sgshapiro mode = stb.st_mode; 549264562Sgshapiro 549364562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 549438032Speter sfflags |= SFF_NOSLINK; 549564562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 549638032Speter sfflags |= SFF_NOHLINK; 549738032Speter sfflags &= ~SFF_OPENASROOT; 549864562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 549938032Speter if (f == NULL) 550038032Speter { 550164562Sgshapiro if (transienterror(errno)) 550264562Sgshapiro { 550364562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 550464562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 550590792Sgshapiro sm_errstring(errno)); 550690792Sgshapiro RETURN(EX_TEMPFAIL); 550764562Sgshapiro } 550864562Sgshapiro else 550964562Sgshapiro { 551064562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 551164562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 551290792Sgshapiro sm_errstring(errno)); 551390792Sgshapiro RETURN(EX_CANTCREAT); 551464562Sgshapiro } 551538032Speter } 551690792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 551790792Sgshapiro &stb)) 551838032Speter { 551964562Sgshapiro syserr("554 5.3.0 file changed after open"); 552090792Sgshapiro RETURN(EX_CANTCREAT); 552138032Speter } 552290792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 552338032Speter { 552490792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 552590792Sgshapiro sm_errstring(errno)); 552690792Sgshapiro RETURN(EX_CANTCREAT); 552738032Speter } 552838032Speter 552964562Sgshapiro curoff = stb.st_size; 553064562Sgshapiro 553138032Speter if (ev != NULL) 553290792Sgshapiro sm_clrevent(ev); 553338032Speter 553464562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 553538032Speter mcibuf.mci_mailer = mailer; 553638032Speter mcibuf.mci_out = f; 553738032Speter if (bitnset(M_7BITS, mailer->m_flags)) 553838032Speter mcibuf.mci_flags |= MCIF_7BIT; 553938032Speter 554038032Speter /* clear out per-message flags from connection structure */ 554138032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 554238032Speter 554338032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 554438032Speter !bitset(EF_DONT_MIME, e->e_flags) && 554538032Speter bitnset(M_7BITS, mailer->m_flags)) 554638032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 554738032Speter 554838032Speter#if MIME7TO8 554938032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 555038032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 555138032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 555290792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 555390792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 555438032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 555538032Speter { 555638032Speter /* may want to convert 7 -> 8 */ 555738032Speter /* XXX should really parse it here -- and use a class XXX */ 555890792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 555964562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 556038032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 556138032Speter } 556264562Sgshapiro#endif /* MIME7TO8 */ 556338032Speter 5564157001Sgshapiro if (!putfromline(&mcibuf, e) || 5565157001Sgshapiro !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) || 5566157001Sgshapiro !(*e->e_putbody)(&mcibuf, e, NULL) || 5567157001Sgshapiro !putline("\n", &mcibuf) || 5568157001Sgshapiro (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 556990792Sgshapiro (SuperSafe != SAFE_NO && 557090792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 5571157001Sgshapiro sm_io_error(f))) 557238032Speter { 557338032Speter setstat(EX_IOERR); 557464562Sgshapiro#if !NOFTRUNCATE 557590792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 557690792Sgshapiro curoff); 557764562Sgshapiro#endif /* !NOFTRUNCATE */ 557838032Speter } 557938032Speter 558038032Speter /* reset ISUID & ISGID bits for paranoid systems */ 558138032Speter#if HASFCHMOD 558290792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 558390792Sgshapiro (MODE_T) mode); 558464562Sgshapiro#else /* HASFCHMOD */ 558564562Sgshapiro (void) chmod(filename, (MODE_T) mode); 558664562Sgshapiro#endif /* HASFCHMOD */ 558790792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 558864562Sgshapiro setstat(EX_IOERR); 558990792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 559064562Sgshapiro (void) setuid(RealUid); 559138032Speter exit(ExitStat); 559264562Sgshapiro /* NOTREACHED */ 559338032Speter } 559438032Speter else 559538032Speter { 559638032Speter /* parent -- wait for exit status */ 559738032Speter int st; 559838032Speter 559938032Speter st = waitfor(pid); 560038032Speter if (st == -1) 560138032Speter { 560238032Speter syserr("mailfile: %s: wait", mailer->m_name); 560364562Sgshapiro return EX_SOFTWARE; 560438032Speter } 560538032Speter if (WIFEXITED(st)) 560690792Sgshapiro { 560790792Sgshapiro errno = 0; 560838032Speter return (WEXITSTATUS(st)); 560990792Sgshapiro } 561038032Speter else 561138032Speter { 561238032Speter syserr("mailfile: %s: child died on signal %d", 561338032Speter mailer->m_name, st); 561464562Sgshapiro return EX_UNAVAILABLE; 561538032Speter } 561664562Sgshapiro /* NOTREACHED */ 561738032Speter } 561838032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 561938032Speter} 562038032Speter 562138032Speterstatic void 5622141858Sgshapiromailfiletimeout(ignore) 5623141858Sgshapiro int ignore; 562438032Speter{ 562577349Sgshapiro /* 562677349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 562777349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 562877349Sgshapiro ** DOING. 562977349Sgshapiro */ 563077349Sgshapiro 563177349Sgshapiro errno = ETIMEDOUT; 563238032Speter longjmp(CtxMailfileTimeout, 1); 563338032Speter} 563490792Sgshapiro/* 563538032Speter** HOSTSIGNATURE -- return the "signature" for a host. 563638032Speter** 563738032Speter** The signature describes how we are going to send this -- it 563838032Speter** can be just the hostname (for non-Internet hosts) or can be 563938032Speter** an ordered list of MX hosts. 564038032Speter** 564138032Speter** Parameters: 564238032Speter** m -- the mailer describing this host. 564338032Speter** host -- the host name. 564438032Speter** 564538032Speter** Returns: 564638032Speter** The signature for this host. 564738032Speter** 564838032Speter** Side Effects: 564938032Speter** Can tweak the symbol table. 565038032Speter*/ 565190792Sgshapiro 565264562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 565338032Speter 565490792Sgshapirochar * 565564562Sgshapirohostsignature(m, host) 565638032Speter register MAILER *m; 565738032Speter char *host; 565838032Speter{ 565938032Speter register char *p; 566038032Speter register STAB *s; 566190792Sgshapiro time_t now; 566264562Sgshapiro#if NAMED_BIND 566364562Sgshapiro char sep = ':'; 566464562Sgshapiro char prevsep = ':'; 566538032Speter int i; 566638032Speter int len; 566738032Speter int nmx; 566864562Sgshapiro int hl; 566938032Speter char *hp; 567038032Speter char *endp; 567138032Speter int oldoptions = _res.options; 567238032Speter char *mxhosts[MAXMXHOSTS + 1]; 567390792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 567464562Sgshapiro#endif /* NAMED_BIND */ 567538032Speter 567664562Sgshapiro if (tTd(17, 3)) 567790792Sgshapiro sm_dprintf("hostsignature(%s)\n", host); 567864562Sgshapiro 567938032Speter /* 568077349Sgshapiro ** If local delivery (and not remote), just return a constant. 568164562Sgshapiro */ 568264562Sgshapiro 568377349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 568490792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 568590792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 568664562Sgshapiro return "localhost"; 568764562Sgshapiro 5688147078Sgshapiro /* an empty host does not have MX records */ 5689147078Sgshapiro if (*host == '\0') 5690147078Sgshapiro return "_empty_"; 5691147078Sgshapiro 569264562Sgshapiro /* 569338032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 569438032Speter */ 569538032Speter 569690792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 569790792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 569838032Speter { 569990792Sgshapiro /* just an ordinary mailer or deferred mode */ 570038032Speter return host; 570138032Speter } 570264562Sgshapiro#if NETUNIX 570364562Sgshapiro else if (m->m_argv[0] != NULL && 570464562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 570564562Sgshapiro { 570664562Sgshapiro /* rendezvous in the file system, no MX records */ 570764562Sgshapiro return host; 570864562Sgshapiro } 570964562Sgshapiro#endif /* NETUNIX */ 571038032Speter 571138032Speter /* 571238032Speter ** Look it up in the symbol table. 571338032Speter */ 571438032Speter 571590792Sgshapiro now = curtime(); 571638032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 571790792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 571864562Sgshapiro { 571990792Sgshapiro if (s->s_hostsig.hs_exp >= now) 572090792Sgshapiro { 572190792Sgshapiro if (tTd(17, 3)) 572290792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 572390792Sgshapiro s->s_hostsig.hs_sig); 572490792Sgshapiro return s->s_hostsig.hs_sig; 572590792Sgshapiro } 572690792Sgshapiro 572790792Sgshapiro /* signature is expired: clear it */ 572890792Sgshapiro sm_free(s->s_hostsig.hs_sig); 572990792Sgshapiro s->s_hostsig.hs_sig = NULL; 573064562Sgshapiro } 573138032Speter 573290792Sgshapiro /* set default TTL */ 573390792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 573490792Sgshapiro 573538032Speter /* 573690792Sgshapiro ** Not already there or expired -- create a signature. 573738032Speter */ 573838032Speter 573938032Speter#if NAMED_BIND 574038032Speter if (ConfigLevel < 2) 574138032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 574238032Speter 574338032Speter for (hp = host; hp != NULL; hp = endp) 574438032Speter { 574564562Sgshapiro#if NETINET6 574664562Sgshapiro if (*hp == '[') 574764562Sgshapiro { 574864562Sgshapiro endp = strchr(hp + 1, ']'); 574964562Sgshapiro if (endp != NULL) 575064562Sgshapiro endp = strpbrk(endp + 1, ":,"); 575164562Sgshapiro } 575264562Sgshapiro else 575364562Sgshapiro endp = strpbrk(hp, ":,"); 575464562Sgshapiro#else /* NETINET6 */ 575564562Sgshapiro endp = strpbrk(hp, ":,"); 575664562Sgshapiro#endif /* NETINET6 */ 575738032Speter if (endp != NULL) 575864562Sgshapiro { 575964562Sgshapiro sep = *endp; 576038032Speter *endp = '\0'; 576164562Sgshapiro } 576238032Speter 576338032Speter if (bitnset(M_NOMX, m->m_flags)) 576438032Speter { 576538032Speter /* skip MX lookups */ 576638032Speter nmx = 1; 576738032Speter mxhosts[0] = hp; 576838032Speter } 576938032Speter else 577038032Speter { 577138032Speter auto int rcode; 577290792Sgshapiro int ttl; 577338032Speter 577490792Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 577590792Sgshapiro &ttl); 577638032Speter if (nmx <= 0) 577738032Speter { 577880785Sgshapiro int save_errno; 577938032Speter register MCI *mci; 578038032Speter 578138032Speter /* update the connection info for this host */ 578280785Sgshapiro save_errno = errno; 578338032Speter mci = mci_get(hp, m); 578480785Sgshapiro mci->mci_errno = save_errno; 578538032Speter mci->mci_herrno = h_errno; 578671345Sgshapiro mci->mci_lastuse = now; 578764562Sgshapiro if (rcode == EX_NOHOST) 578864562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 578990792Sgshapiro "550 Host unknown"); 579064562Sgshapiro else 579164562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 579238032Speter 579338032Speter /* use the original host name as signature */ 579438032Speter nmx = 1; 579538032Speter mxhosts[0] = hp; 579638032Speter } 579764562Sgshapiro if (tTd(17, 3)) 579890792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 579990792Sgshapiro nmx, mxhosts[0]); 580090792Sgshapiro 580190792Sgshapiro /* 580290792Sgshapiro ** Set new TTL: we use only one! 580390792Sgshapiro ** We could try to use the minimum instead. 580490792Sgshapiro */ 580590792Sgshapiro 580690792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 580738032Speter } 580838032Speter 580938032Speter len = 0; 581038032Speter for (i = 0; i < nmx; i++) 581138032Speter len += strlen(mxhosts[i]) + 1; 581290792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 581390792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 581490792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 581564562Sgshapiro { 581664562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 581764562Sgshapiro host, MAXHOSTSIGNATURE, len); 581864562Sgshapiro len = MAXHOSTSIGNATURE; 581964562Sgshapiro } 582090792Sgshapiro p = sm_pmalloc_x(len); 582190792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 582238032Speter { 582390792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 582490792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 582590792Sgshapiro s->s_hostsig.hs_sig = p; 582664562Sgshapiro hl = strlen(p); 582764562Sgshapiro p += hl; 582864562Sgshapiro *p++ = prevsep; 582964562Sgshapiro len -= hl + 1; 583038032Speter } 583138032Speter else 583290792Sgshapiro s->s_hostsig.hs_sig = p; 583338032Speter for (i = 0; i < nmx; i++) 583438032Speter { 583564562Sgshapiro hl = strlen(mxhosts[i]); 583664562Sgshapiro if (len - 1 < hl || len <= 1) 583764562Sgshapiro { 583864562Sgshapiro /* force to drop out of outer loop */ 583964562Sgshapiro len = -1; 584064562Sgshapiro break; 584164562Sgshapiro } 584238032Speter if (i != 0) 584364562Sgshapiro { 584464562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 584564562Sgshapiro *p++ = ','; 584664562Sgshapiro else 584764562Sgshapiro *p++ = ':'; 584864562Sgshapiro len--; 584964562Sgshapiro } 585090792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 585164562Sgshapiro p += hl; 585264562Sgshapiro len -= hl; 585338032Speter } 585464562Sgshapiro 585564562Sgshapiro /* 585664562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 585764562Sgshapiro ** because we won't have more space for further hosts 585864562Sgshapiro ** anyway (separated by : in the .cf file). 585964562Sgshapiro */ 586064562Sgshapiro 586164562Sgshapiro if (len < 0) 586264562Sgshapiro break; 586338032Speter if (endp != NULL) 586464562Sgshapiro *endp++ = sep; 586564562Sgshapiro prevsep = sep; 586638032Speter } 586790792Sgshapiro makelower(s->s_hostsig.hs_sig); 586838032Speter if (ConfigLevel < 2) 586938032Speter _res.options = oldoptions; 587064562Sgshapiro#else /* NAMED_BIND */ 587138032Speter /* not using BIND -- the signature is just the host name */ 587290792Sgshapiro /* 587390792Sgshapiro ** 'host' points to storage that will be freed after we are 587490792Sgshapiro ** done processing the current envelope, so we copy it. 587590792Sgshapiro */ 587690792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 587764562Sgshapiro#endif /* NAMED_BIND */ 587838032Speter if (tTd(17, 1)) 587990792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 588090792Sgshapiro return s->s_hostsig.hs_sig; 588138032Speter} 588290792Sgshapiro/* 588364562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 588464562Sgshapiro** 588564562Sgshapiro** The signature describes how we are going to send this -- it 588664562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 588764562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 588864562Sgshapiro** MX preference values. 588964562Sgshapiro** 589064562Sgshapiro** Parameters: 589164562Sgshapiro** sig -- the host signature. 589264562Sgshapiro** mxhosts -- array to populate. 589390792Sgshapiro** mailer -- mailer. 589464562Sgshapiro** 589564562Sgshapiro** Returns: 589664562Sgshapiro** The number of hosts inserted into mxhosts array. 589764562Sgshapiro** 589864562Sgshapiro** Side Effects: 589964562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 590064562Sgshapiro*/ 590164562Sgshapiro 590264562Sgshapirostatic int 590364562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 590464562Sgshapiro char *sig; 590564562Sgshapiro char **mxhosts; 590664562Sgshapiro MAILER *mailer; 590764562Sgshapiro{ 590890792Sgshapiro unsigned short curpref = 0; 590990792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 591064562Sgshapiro char *hp, *endp; 591190792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 591264562Sgshapiro long rndm[MAXMXHOSTS]; 591364562Sgshapiro 591464562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 591564562Sgshapiro { 591664562Sgshapiro char sep = ':'; 591764562Sgshapiro 591864562Sgshapiro#if NETINET6 591964562Sgshapiro if (*hp == '[') 592064562Sgshapiro { 592164562Sgshapiro endp = strchr(hp + 1, ']'); 592264562Sgshapiro if (endp != NULL) 592364562Sgshapiro endp = strpbrk(endp + 1, ":,"); 592464562Sgshapiro } 592564562Sgshapiro else 592664562Sgshapiro endp = strpbrk(hp, ":,"); 592764562Sgshapiro#else /* NETINET6 */ 592864562Sgshapiro endp = strpbrk(hp, ":,"); 592964562Sgshapiro#endif /* NETINET6 */ 593064562Sgshapiro if (endp != NULL) 593164562Sgshapiro { 593264562Sgshapiro sep = *endp; 593364562Sgshapiro *endp = '\0'; 593464562Sgshapiro } 593564562Sgshapiro 593664562Sgshapiro mxhosts[nmx] = hp; 593764562Sgshapiro prefer[nmx] = curpref; 593864562Sgshapiro if (mci_match(hp, mailer)) 593964562Sgshapiro rndm[nmx] = 0; 594064562Sgshapiro else 594164562Sgshapiro rndm[nmx] = get_random(); 594264562Sgshapiro 594364562Sgshapiro if (endp != NULL) 594464562Sgshapiro { 594564562Sgshapiro /* 594664562Sgshapiro ** Since we don't have the original MX prefs, 594764562Sgshapiro ** make our own. If the separator is a ':', that 594864562Sgshapiro ** means the preference for the next host will be 594964562Sgshapiro ** higher than this one, so simply increment curpref. 595064562Sgshapiro */ 595164562Sgshapiro 595264562Sgshapiro if (sep == ':') 595364562Sgshapiro curpref++; 595464562Sgshapiro 595564562Sgshapiro *endp++ = sep; 595664562Sgshapiro } 595764562Sgshapiro if (++nmx >= MAXMXHOSTS) 595864562Sgshapiro break; 595964562Sgshapiro } 596064562Sgshapiro 596164562Sgshapiro /* sort the records using the random factor for equal preferences */ 596264562Sgshapiro for (i = 0; i < nmx; i++) 596364562Sgshapiro { 596464562Sgshapiro for (j = i + 1; j < nmx; j++) 596564562Sgshapiro { 596664562Sgshapiro /* 596764562Sgshapiro ** List is already sorted by MX preference, only 596864562Sgshapiro ** need to look for equal preference MX records 596964562Sgshapiro */ 597064562Sgshapiro 597164562Sgshapiro if (prefer[i] < prefer[j]) 597264562Sgshapiro break; 597364562Sgshapiro 597464562Sgshapiro if (prefer[i] > prefer[j] || 597564562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 597664562Sgshapiro { 597790792Sgshapiro register unsigned short tempp; 597864562Sgshapiro register long tempr; 597964562Sgshapiro register char *temp1; 598064562Sgshapiro 598164562Sgshapiro tempp = prefer[i]; 598264562Sgshapiro prefer[i] = prefer[j]; 598364562Sgshapiro prefer[j] = tempp; 598464562Sgshapiro temp1 = mxhosts[i]; 598564562Sgshapiro mxhosts[i] = mxhosts[j]; 598664562Sgshapiro mxhosts[j] = temp1; 598764562Sgshapiro tempr = rndm[i]; 598864562Sgshapiro rndm[i] = rndm[j]; 598964562Sgshapiro rndm[j] = tempr; 599064562Sgshapiro } 599164562Sgshapiro } 599264562Sgshapiro } 599364562Sgshapiro return nmx; 599464562Sgshapiro} 599564562Sgshapiro 599664562Sgshapiro# if STARTTLS 599764562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 599890792Sgshapirostatic bool tls_ok_clt = true; 599964562Sgshapiro 600090792Sgshapiro/* 600190792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 600264562Sgshapiro** 600364562Sgshapiro** Parameters: 600490792Sgshapiro** tls_ok -- should tls be done? 600590792Sgshapiro** 600690792Sgshapiro** Returns: 600764562Sgshapiro** none. 600864562Sgshapiro** 600990792Sgshapiro** Side Effects: 601090792Sgshapiro** sets tls_ok_clt (static variable in this module) 601190792Sgshapiro*/ 601290792Sgshapiro 601390792Sgshapirovoid 601490792Sgshapirosetclttls(tls_ok) 601590792Sgshapiro bool tls_ok; 601690792Sgshapiro{ 601790792Sgshapiro tls_ok_clt = tls_ok; 601890792Sgshapiro return; 601990792Sgshapiro} 602090792Sgshapiro/* 602190792Sgshapiro** INITCLTTLS -- initialize client side TLS 602290792Sgshapiro** 602390792Sgshapiro** Parameters: 602490792Sgshapiro** tls_ok -- should tls initialization be done? 602590792Sgshapiro** 602664562Sgshapiro** Returns: 602764562Sgshapiro** succeeded? 602890792Sgshapiro** 602990792Sgshapiro** Side Effects: 603090792Sgshapiro** sets tls_ok_clt (static variable in this module) 603164562Sgshapiro*/ 603264562Sgshapiro 603364562Sgshapirobool 603490792Sgshapiroinitclttls(tls_ok) 603590792Sgshapiro bool tls_ok; 603664562Sgshapiro{ 603790792Sgshapiro if (!tls_ok_clt) 603890792Sgshapiro return false; 603990792Sgshapiro tls_ok_clt = tls_ok; 604090792Sgshapiro if (!tls_ok_clt) 604190792Sgshapiro return false; 604264562Sgshapiro if (clt_ctx != NULL) 604390792Sgshapiro return true; /* already done */ 6044110560Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile, 6045110560Sgshapiro CltKeyFile, CACertPath, CACertFile, DHParams); 604690792Sgshapiro return tls_ok_clt; 604764562Sgshapiro} 604864562Sgshapiro 604990792Sgshapiro/* 605064562Sgshapiro** STARTTLS -- try to start secure connection (client side) 605164562Sgshapiro** 605264562Sgshapiro** Parameters: 605364562Sgshapiro** m -- the mailer. 605464562Sgshapiro** mci -- the mailer connection info. 605564562Sgshapiro** e -- the envelope. 605664562Sgshapiro** 605764562Sgshapiro** Returns: 605864562Sgshapiro** success? 605964562Sgshapiro** (maybe this should be some other code than EX_ 606064562Sgshapiro** that denotes which stage failed.) 606164562Sgshapiro*/ 606264562Sgshapiro 606364562Sgshapirostatic int 606464562Sgshapirostarttls(m, mci, e) 606564562Sgshapiro MAILER *m; 606664562Sgshapiro MCI *mci; 606764562Sgshapiro ENVELOPE *e; 606864562Sgshapiro{ 606964562Sgshapiro int smtpresult; 607066494Sgshapiro int result = 0; 607166494Sgshapiro int rfd, wfd; 607264562Sgshapiro SSL *clt_ssl = NULL; 607390792Sgshapiro time_t tlsstart; 607464562Sgshapiro 607590792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 607666494Sgshapiro return EX_TEMPFAIL; 607764562Sgshapiro smtpmessage("STARTTLS", m, mci); 607864562Sgshapiro 607964562Sgshapiro /* get the reply */ 6080132943Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 6081132943Sgshapiro XS_STARTTLS); 608264562Sgshapiro 608364562Sgshapiro /* check return code from server */ 6084157001Sgshapiro if (REPLYTYPE(smtpresult) == 4) 608564562Sgshapiro return EX_TEMPFAIL; 608664562Sgshapiro if (smtpresult == 501) 608764562Sgshapiro return EX_USAGE; 608864562Sgshapiro if (smtpresult == -1) 608964562Sgshapiro return smtpresult; 6090157001Sgshapiro 6091157001Sgshapiro /* not an expected reply but we have to deal with it */ 6092157001Sgshapiro if (REPLYTYPE(smtpresult) == 5) 6093157001Sgshapiro return EX_UNAVAILABLE; 609464562Sgshapiro if (smtpresult != 220) 609564562Sgshapiro return EX_PROTOCOL; 609664562Sgshapiro 609764562Sgshapiro if (LogLevel > 13) 609890792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 609964562Sgshapiro 610064562Sgshapiro /* start connection */ 610164562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 610264562Sgshapiro { 610364562Sgshapiro if (LogLevel > 5) 610464562Sgshapiro { 610590792Sgshapiro sm_syslog(LOG_ERR, NOQID, 610690792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 610764562Sgshapiro if (LogLevel > 9) 610890792Sgshapiro tlslogerr("client"); 610964562Sgshapiro } 611064562Sgshapiro return EX_SOFTWARE; 611164562Sgshapiro } 611264562Sgshapiro 611390792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 611490792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 611566494Sgshapiro 611664562Sgshapiro /* SSL_clear(clt_ssl); ? */ 611766494Sgshapiro if (rfd < 0 || wfd < 0 || 611890792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 611990792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 612064562Sgshapiro { 612164562Sgshapiro if (LogLevel > 5) 612264562Sgshapiro { 612390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 612490792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 612590792Sgshapiro result); 612664562Sgshapiro if (LogLevel > 9) 612790792Sgshapiro tlslogerr("client"); 612864562Sgshapiro } 612964562Sgshapiro return EX_SOFTWARE; 613064562Sgshapiro } 613164562Sgshapiro SSL_set_connect_state(clt_ssl); 613290792Sgshapiro tlsstart = curtime(); 613390792Sgshapiro 613490792Sgshapirossl_retry: 613564562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 613664562Sgshapiro { 6137157001Sgshapiro int i, ssl_err; 613864562Sgshapiro 6139157001Sgshapiro ssl_err = SSL_get_error(clt_ssl, result); 6140157001Sgshapiro i = tls_retry(clt_ssl, rfd, wfd, tlsstart, 6141157001Sgshapiro TimeOuts.to_starttls, ssl_err, "client"); 6142157001Sgshapiro if (i > 0) 6143157001Sgshapiro goto ssl_retry; 614490792Sgshapiro 6145157001Sgshapiro if (LogLevel > 5) 614690792Sgshapiro { 6147157001Sgshapiro sm_syslog(LOG_WARNING, NOQID, 6148157001Sgshapiro "STARTTLS=client, error: connect failed=%d, SSL_error=%d, errno=%d, retry=%d", 6149157001Sgshapiro result, ssl_err, errno, i); 6150110560Sgshapiro if (LogLevel > 8) 6151110560Sgshapiro tlslogerr("client"); 6152110560Sgshapiro } 615390792Sgshapiro 615464562Sgshapiro SSL_free(clt_ssl); 615564562Sgshapiro clt_ssl = NULL; 615664562Sgshapiro return EX_SOFTWARE; 615764562Sgshapiro } 615864562Sgshapiro mci->mci_ssl = clt_ssl; 615990792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 616090792Sgshapiro &mci->mci_macro, true); 616164562Sgshapiro 616290792Sgshapiro /* switch to use TLS... */ 616364562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 616464562Sgshapiro return EX_OK; 616564562Sgshapiro 616664562Sgshapiro /* failure */ 616764562Sgshapiro SSL_free(clt_ssl); 616864562Sgshapiro clt_ssl = NULL; 616964562Sgshapiro return EX_SOFTWARE; 617064562Sgshapiro} 617190792Sgshapiro/* 617264562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 617364562Sgshapiro** 617464562Sgshapiro** Parameters: 617564562Sgshapiro** mci -- the mailer connection info. 617664562Sgshapiro** 617764562Sgshapiro** Returns: 617864562Sgshapiro** success? 617964562Sgshapiro*/ 618090792Sgshapiro 618190792Sgshapirostatic int 618264562Sgshapiroendtlsclt(mci) 618364562Sgshapiro MCI *mci; 618464562Sgshapiro{ 618564562Sgshapiro int r; 618664562Sgshapiro 618764562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 618864562Sgshapiro return EX_OK; 618964562Sgshapiro r = endtls(mci->mci_ssl, "client"); 619064562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 619164562Sgshapiro return r; 619264562Sgshapiro} 619390792Sgshapiro# endif /* STARTTLS */ 619490792Sgshapiro# if STARTTLS || SASL 619590792Sgshapiro/* 619690792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 619764562Sgshapiro** 619864562Sgshapiro** Parameters: 619990792Sgshapiro** e -- envelope. 620090792Sgshapiro** flag -- flag to check in {client_flags} 620164562Sgshapiro** 620264562Sgshapiro** Returns: 620390792Sgshapiro** true iff flag is set. 620464562Sgshapiro*/ 620564562Sgshapiro 620690792Sgshapirostatic bool 620790792Sgshapiroiscltflgset(e, flag) 620890792Sgshapiro ENVELOPE *e; 620990792Sgshapiro int flag; 621064562Sgshapiro{ 621190792Sgshapiro char *p; 621273188Sgshapiro 621390792Sgshapiro p = macvalue(macid("{client_flags}"), e); 621490792Sgshapiro if (p == NULL) 621590792Sgshapiro return false; 621690792Sgshapiro for (; *p != '\0'; p++) 621764562Sgshapiro { 621890792Sgshapiro /* look for just this one flag */ 621990792Sgshapiro if (*p == (char) flag) 622090792Sgshapiro return true; 622164562Sgshapiro } 622290792Sgshapiro return false; 622364562Sgshapiro} 622490792Sgshapiro# endif /* STARTTLS || SASL */ 6225