138032Speter/* 2261370Sgshapiro * Copyright (c) 1998-2010, 2012 Proofpoint, 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 17266711SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.1030 2013-11-22 20:51:55 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)); 3790792Sgshapirostatic int coloncmp __P((const char *, const char *)); 3864562Sgshapiro 3990792Sgshapiro#if STARTTLS 40244928Sgshapiro# include <openssl/err.h> 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, 386168515Sgshapiro 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)) 499173340Sgshapiro sm_dprintf("No deliveries: auto-queueing\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; 579203004Sgshapiro (void) 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; 584203004Sgshapiro (void) 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; 606203004Sgshapiro (void) 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; 619203004Sgshapiro (void) 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); 766203004Sgshapiro (void) 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); 773203004Sgshapiro (void) 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 887168515Sgshapiro (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 { 962168515Sgshapiro 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 1015168515Sgshapiro (void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf)); 1016168515Sgshapiro (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 1195168515Sgshapirostatic bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int)); 1196168515Sgshapiro 1197132943Sgshapirostatic bool 1198132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status) 1199132943Sgshapiro ENVELOPE *e; 1200132943Sgshapiro bool *tried_fallbacksmarthost; 1201132943Sgshapiro char *hostbuf; 1202132943Sgshapiro size_t hbsz; 1203132943Sgshapiro int status; 1204132943Sgshapiro{ 1205132943Sgshapiro /* 1206157001Sgshapiro ** If the host was not found or a temporary failure occurred 1207157001Sgshapiro ** and a FallbackSmartHost is defined (and we have not yet 1208157001Sgshapiro ** tried it), then make one last try with it as the host. 1209132943Sgshapiro */ 1210132943Sgshapiro 1211157001Sgshapiro if ((status == EX_NOHOST || status == EX_TEMPFAIL) && 1212157001Sgshapiro FallbackSmartHost != NULL && !*tried_fallbacksmarthost) 1213132943Sgshapiro { 1214132943Sgshapiro *tried_fallbacksmarthost = true; 1215132943Sgshapiro expand(FallbackSmartHost, hostbuf, hbsz, e); 1216132943Sgshapiro if (!wordinclass(hostbuf, 'w')) 1217132943Sgshapiro { 1218132943Sgshapiro if (tTd(11, 1)) 1219132943Sgshapiro sm_dprintf("one last try with FallbackSmartHost %s\n", 1220132943Sgshapiro hostbuf); 1221132943Sgshapiro return true; 1222132943Sgshapiro } 1223132943Sgshapiro } 1224132943Sgshapiro return false; 1225132943Sgshapiro} 1226132943Sgshapiro/* 122738032Speter** DELIVER -- Deliver a message to a list of addresses. 122838032Speter** 122938032Speter** This routine delivers to everyone on the same host as the 123038032Speter** user on the head of the list. It is clever about mailers 123138032Speter** that don't handle multiple users. It is NOT guaranteed 123238032Speter** that it will deliver to all these addresses however -- so 123338032Speter** deliver should be called once for each address on the 123438032Speter** list. 123590792Sgshapiro** Deliver tries to be as opportunistic as possible about piggybacking 123690792Sgshapiro** messages. Some definitions to make understanding easier follow below. 123790792Sgshapiro** Piggybacking occurs when an existing connection to a mail host can 123890792Sgshapiro** be used to send the same message to more than one recipient at the 123990792Sgshapiro** same time. So "no piggybacking" means one message for one recipient 124090792Sgshapiro** per connection. "Intentional piggybacking" happens when the 124190792Sgshapiro** recipients' host address (not the mail host address) is used to 124290792Sgshapiro** attempt piggybacking. Recipients with the same host address 124390792Sgshapiro** have the same mail host. "Coincidental piggybacking" relies on 124490792Sgshapiro** piggybacking based on all the mail host addresses in the MX-RR. This 124590792Sgshapiro** is "coincidental" in the fact it could not be predicted until the 124690792Sgshapiro** MX Resource Records for the hosts were obtained and examined. For 124790792Sgshapiro** example (preference order and equivalence is important, not values): 124890792Sgshapiro** domain1 IN MX 10 mxhost-A 124990792Sgshapiro** IN MX 20 mxhost-B 125090792Sgshapiro** domain2 IN MX 4 mxhost-A 125190792Sgshapiro** IN MX 8 mxhost-B 125290792Sgshapiro** Domain1 and domain2 can piggyback the same message to mxhost-A or 125390792Sgshapiro** mxhost-B (if mxhost-A cannot be reached). 125490792Sgshapiro** "Coattail piggybacking" relaxes the strictness of "coincidental 125590792Sgshapiro** piggybacking" in the hope that most significant (lowest value) 125690792Sgshapiro** MX preference host(s) can create more piggybacking. For example 125790792Sgshapiro** (again, preference order and equivalence is important, not values): 125890792Sgshapiro** domain3 IN MX 100 mxhost-C 125990792Sgshapiro** IN MX 100 mxhost-D 126090792Sgshapiro** IN MX 200 mxhost-E 126190792Sgshapiro** domain4 IN MX 50 mxhost-C 126290792Sgshapiro** IN MX 50 mxhost-D 126390792Sgshapiro** IN MX 80 mxhost-F 126490792Sgshapiro** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 126590792Sgshapiro** is available. Same with mxhost-D because in both RR's the preference 126690792Sgshapiro** value is the same as mxhost-C, respectively. 126790792Sgshapiro** So deliver attempts coattail piggybacking when possible. If the 126890792Sgshapiro** first MX preference level hosts cannot be used then the piggybacking 126990792Sgshapiro** reverts to coincidental piggybacking. Using the above example you 127090792Sgshapiro** cannot deliver to mxhost-F for domain3 regardless of preference value. 127190792Sgshapiro** ("Coattail" from "riding on the coattails of your predecessor" meaning 127290792Sgshapiro** gaining benefit from a predecessor effort with no or little addition 127390792Sgshapiro** effort. The predecessor here being the preceding MX RR). 127438032Speter** 127538032Speter** Parameters: 127638032Speter** e -- the envelope to deliver. 127738032Speter** firstto -- head of the address list to deliver to. 127838032Speter** 127938032Speter** Returns: 128038032Speter** zero -- successfully delivered. 128138032Speter** else -- some failure, see ExitStat for more info. 128238032Speter** 128338032Speter** Side Effects: 128438032Speter** The standard input is passed off to someone. 128538032Speter*/ 128638032Speter 128764562Sgshapirostatic int 128838032Speterdeliver(e, firstto) 128938032Speter register ENVELOPE *e; 129038032Speter ADDRESS *firstto; 129138032Speter{ 129238032Speter char *host; /* host being sent to */ 129338032Speter char *user; /* user being sent to */ 129438032Speter char **pvp; 129538032Speter register char **mvp; 129638032Speter register char *p; 129738032Speter register MAILER *m; /* mailer for this recipient */ 129838032Speter ADDRESS *volatile ctladdr; 129990792Sgshapiro#if HASSETUSERCONTEXT 130038032Speter ADDRESS *volatile contextaddr = NULL; 130190792Sgshapiro#endif /* HASSETUSERCONTEXT */ 130238032Speter register MCI *volatile mci; 130390792Sgshapiro register ADDRESS *SM_NONVOLATILE to = firstto; 130490792Sgshapiro volatile bool clever = false; /* running user smtp to this mailer */ 130538032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 130638032Speter int rcode; /* response code */ 130790792Sgshapiro SM_NONVOLATILE int lmtp_rcode = EX_OK; 130890792Sgshapiro SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 130990792Sgshapiro SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 131038032Speter char *firstsig; /* signature of firstto */ 131190792Sgshapiro volatile pid_t pid = -1; 131238032Speter char *volatile curhost; 131390792Sgshapiro SM_NONVOLATILE unsigned short port = 0; 131490792Sgshapiro SM_NONVOLATILE time_t enough = 0; 131564562Sgshapiro#if NETUNIX 131690792Sgshapiro char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 131764562Sgshapiro#endif /* NETUNIX */ 131838032Speter time_t xstart; 131938032Speter bool suidwarn; 132038032Speter bool anyok; /* at least one address was OK */ 132190792Sgshapiro SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 132264562Sgshapiro bool ovr; 132390792Sgshapiro bool quarantine; 132464562Sgshapiro int strsize; 132564562Sgshapiro int rcptcount; 132690792Sgshapiro int ret; 132764562Sgshapiro static int tobufsize = 0; 132864562Sgshapiro static char *tobuf = NULL; 132990792Sgshapiro char *rpath; /* translated return path */ 133038032Speter int mpvect[2]; 133138032Speter int rpvect[2]; 133264562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 133364562Sgshapiro char *pv[MAXPV + 1]; 133438032Speter char buf[MAXNAME + 1]; 133598121Sgshapiro char cbuf[MAXPATHLEN]; 133638032Speter 133738032Speter errno = 0; 1338168515Sgshapiro SM_REQUIRE(firstto != NULL); /* same as to */ 133964562Sgshapiro if (!QS_IS_OK(to->q_state)) 134064562Sgshapiro return 0; 134138032Speter 134238032Speter suidwarn = geteuid() == 0; 134338032Speter 1344168515Sgshapiro SM_REQUIRE(e != NULL); 134538032Speter m = to->q_mailer; 134638032Speter host = to->q_host; 134738032Speter CurEnv = e; /* just in case */ 134838032Speter e->e_statmsg = NULL; 134938032Speter SmtpError[0] = '\0'; 135038032Speter xstart = curtime(); 135138032Speter 135238032Speter if (tTd(10, 1)) 135390792Sgshapiro sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 135438032Speter e->e_id, m->m_name, host, to->q_user); 135538032Speter if (tTd(10, 100)) 135690792Sgshapiro printopenfds(false); 135738032Speter 135838032Speter /* 135990792Sgshapiro ** Clear {client_*} macros if this is a bounce message to 136038032Speter ** prevent rejection by check_compat ruleset. 136138032Speter */ 136264562Sgshapiro 136338032Speter if (bitset(EF_RESPONSE, e->e_flags)) 136438032Speter { 136590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 1366132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), ""); 136790792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 136890792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 136990792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 137038032Speter } 137164562Sgshapiro 137290792Sgshapiro SM_TRY 137390792Sgshapiro { 137490792Sgshapiro ADDRESS *skip_back = NULL; 137590792Sgshapiro 137638032Speter /* 137738032Speter ** Do initial argv setup. 137838032Speter ** Insert the mailer name. Notice that $x expansion is 137938032Speter ** NOT done on the mailer name. Then, if the mailer has 138038032Speter ** a picky -f flag, we insert it as appropriate. This 138138032Speter ** code does not check for 'pv' overflow; this places a 138238032Speter ** manifest lower limit of 4 for MAXPV. 138338032Speter ** The from address rewrite is expected to make 138438032Speter ** the address relative to the other end. 138538032Speter */ 138638032Speter 138738032Speter /* rewrite from address, using rewriting rules */ 138838032Speter rcode = EX_OK; 1389168515Sgshapiro SM_ASSERT(e->e_from.q_mailer != NULL); 139038032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 139138032Speter p = e->e_sender; 139238032Speter else 139338032Speter p = e->e_from.q_paddr; 139490792Sgshapiro rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 1395203004Sgshapiro if (strlen(rpath) > MAXNAME) 139638032Speter { 139790792Sgshapiro rpath = shortenstring(rpath, MAXSHORTSTR); 139890792Sgshapiro 139990792Sgshapiro /* avoid bogus errno */ 140090792Sgshapiro errno = 0; 140190792Sgshapiro syserr("remotename: huge return path %s", rpath); 140238032Speter } 140390792Sgshapiro rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 140490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', rpath); 140590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', host); 140638032Speter Errors = 0; 140738032Speter pvp = pv; 140838032Speter *pvp++ = m->m_argv[0]; 140938032Speter 1410132943Sgshapiro /* ignore long term host status information if mailer flag W is set */ 1411132943Sgshapiro if (bitnset(M_NOHOSTSTAT, m->m_flags)) 1412132943Sgshapiro IgnoreHostStatus = true; 1413132943Sgshapiro 141438032Speter /* insert -f or -r flag as appropriate */ 141564562Sgshapiro if (FromFlag && 141664562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 141764562Sgshapiro bitnset(M_ROPT, m->m_flags))) 141838032Speter { 141938032Speter if (bitnset(M_FOPT, m->m_flags)) 142038032Speter *pvp++ = "-f"; 142138032Speter else 142238032Speter *pvp++ = "-r"; 142390792Sgshapiro *pvp++ = rpath; 142438032Speter } 142538032Speter 142638032Speter /* 142738032Speter ** Append the other fixed parts of the argv. These run 142838032Speter ** up to the first entry containing "$u". There can only 142938032Speter ** be one of these, and there are only a few more slots 143038032Speter ** in the pv after it. 143138032Speter */ 143238032Speter 143338032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 143438032Speter { 143538032Speter /* can't use strchr here because of sign extension problems */ 143638032Speter while (*p != '\0') 143738032Speter { 143838032Speter if ((*p++ & 0377) == MACROEXPAND) 143938032Speter { 144038032Speter if (*p == 'u') 144138032Speter break; 144238032Speter } 144338032Speter } 144438032Speter 144538032Speter if (*p != '\0') 144638032Speter break; 144738032Speter 144838032Speter /* this entry is safe -- go ahead and process it */ 1449168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 145090792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 145138032Speter if (pvp >= &pv[MAXPV - 3]) 145238032Speter { 145364562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 145464562Sgshapiro pv[0]); 145590792Sgshapiro rcode = -1; 145690792Sgshapiro goto cleanup; 145738032Speter } 145838032Speter } 145938032Speter 146038032Speter /* 146138032Speter ** If we have no substitution for the user name in the argument 146238032Speter ** list, we know that we must supply the names otherwise -- and 146338032Speter ** SMTP is the answer!! 146438032Speter */ 146538032Speter 146638032Speter if (*mvp == NULL) 146738032Speter { 146873188Sgshapiro /* running LMTP or SMTP */ 146990792Sgshapiro clever = true; 147038032Speter *pvp = NULL; 147138032Speter } 147273188Sgshapiro else if (bitnset(M_LMTP, m->m_flags)) 147373188Sgshapiro { 147473188Sgshapiro /* not running LMTP */ 147573188Sgshapiro sm_syslog(LOG_ERR, NULL, 147673188Sgshapiro "Warning: mailer %s: LMTP flag (F=z) turned off", 147773188Sgshapiro m->m_name); 147873188Sgshapiro clrbitn(M_LMTP, m->m_flags); 147973188Sgshapiro } 148038032Speter 148138032Speter /* 148238032Speter ** At this point *mvp points to the argument with $u. We 148338032Speter ** run through our address list and append all the addresses 148438032Speter ** we can. If we run out of space, do not fret! We can 148538032Speter ** always send another copy later. 148638032Speter */ 148738032Speter 148864562Sgshapiro e->e_to = NULL; 148964562Sgshapiro strsize = 2; 149064562Sgshapiro rcptcount = 0; 149190792Sgshapiro ctladdr = NULL; 149290792Sgshapiro if (firstto->q_signature == NULL) 149390792Sgshapiro firstto->q_signature = hostsignature(firstto->q_mailer, 149490792Sgshapiro firstto->q_host); 149590792Sgshapiro firstsig = firstto->q_signature; 149664562Sgshapiro 149738032Speter for (; to != NULL; to = to->q_next) 149838032Speter { 149938032Speter /* avoid sending multiple recipients to dumb mailers */ 150064562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 150164562Sgshapiro break; 150238032Speter 150338032Speter /* if already sent or not for this host, don't send */ 150490792Sgshapiro if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 150538032Speter continue; 150638032Speter 150790792Sgshapiro /* 150890792Sgshapiro ** Must be same mailer to keep grouping rcpts. 150990792Sgshapiro ** If mailers don't match: continue; sendqueue is not 151090792Sgshapiro ** sorted by mailers, so don't break; 151190792Sgshapiro */ 151290792Sgshapiro 151390792Sgshapiro if (to->q_mailer != firstto->q_mailer) 151490792Sgshapiro continue; 151590792Sgshapiro 151690792Sgshapiro if (to->q_signature == NULL) /* for safety */ 151790792Sgshapiro to->q_signature = hostsignature(to->q_mailer, 151890792Sgshapiro to->q_host); 151990792Sgshapiro 152090792Sgshapiro /* 152190792Sgshapiro ** This is for coincidental and tailcoat piggybacking messages 152290792Sgshapiro ** to the same mail host. While the signatures are identical 152390792Sgshapiro ** (that's the MX-RR's are identical) we can do coincidental 152490792Sgshapiro ** piggybacking. We try hard for coattail piggybacking 152590792Sgshapiro ** with the same mail host when the next recipient has the 152690792Sgshapiro ** same host at lowest preference. It may be that this 152790792Sgshapiro ** won't work out, so 'skip_back' is maintained if a backup 152890792Sgshapiro ** to coincidental piggybacking or full signature must happen. 152990792Sgshapiro */ 153090792Sgshapiro 153190792Sgshapiro ret = firstto == to ? HS_MATCH_FULL : 153290792Sgshapiro coloncmp(to->q_signature, firstsig); 153390792Sgshapiro if (ret == HS_MATCH_FULL) 153490792Sgshapiro skip_back = to; 153590792Sgshapiro else if (ret == HS_MATCH_NO) 153664562Sgshapiro break; 153764562Sgshapiro 153890792Sgshapiro if (!clever) 153990792Sgshapiro { 154090792Sgshapiro /* avoid overflowing tobuf */ 154190792Sgshapiro strsize += strlen(to->q_paddr) + 1; 154290792Sgshapiro if (strsize > TOBUFSIZE) 154390792Sgshapiro break; 154490792Sgshapiro } 154590792Sgshapiro 154664562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 154764562Sgshapiro break; 154838032Speter 154938032Speter if (tTd(10, 1)) 155038032Speter { 155190792Sgshapiro sm_dprintf("\nsend to "); 1552132943Sgshapiro printaddr(sm_debug_file(), to, false); 155338032Speter } 155438032Speter 155538032Speter /* compute effective uid/gid when sending */ 155638032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 155790792Sgshapiro# if HASSETUSERCONTEXT 155838032Speter contextaddr = ctladdr = getctladdr(to); 155990792Sgshapiro# else /* HASSETUSERCONTEXT */ 156090792Sgshapiro ctladdr = getctladdr(to); 156190792Sgshapiro# endif /* HASSETUSERCONTEXT */ 156238032Speter 156338032Speter if (tTd(10, 2)) 156438032Speter { 156590792Sgshapiro sm_dprintf("ctladdr="); 1566132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 156738032Speter } 156838032Speter 156938032Speter user = to->q_user; 157038032Speter e->e_to = to->q_paddr; 157138032Speter 157238032Speter /* 157338032Speter ** Check to see that these people are allowed to 157438032Speter ** talk to each other. 157566494Sgshapiro ** Check also for overflow of e_msgsize. 157638032Speter */ 157738032Speter 157866494Sgshapiro if (m->m_maxsize != 0 && 157966494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 158038032Speter { 158138032Speter e->e_flags |= EF_NO_BODY_RETN; 158238032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 158338032Speter to->q_status = "5.2.3"; 158438032Speter else 158538032Speter to->q_status = "5.3.4"; 158690792Sgshapiro 158764562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 158864562Sgshapiro usrerrenh(to->q_status, 158964562Sgshapiro "552 Message is too large; %ld bytes max", 159064562Sgshapiro m->m_maxsize); 159190792Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, false); 159264562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 159390792Sgshapiro NULL, ctladdr, xstart, e, to); 159438032Speter continue; 159538032Speter } 159673188Sgshapiro SM_SET_H_ERRNO(0); 159790792Sgshapiro ovr = true; 159838032Speter 159938032Speter /* do config file checking of compatibility */ 160090792Sgshapiro quarantine = (e->e_quarmsg != NULL); 160164562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1602102528Sgshapiro e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1603168515Sgshapiro e->e_id, NULL); 160438032Speter if (rcode == EX_OK) 160538032Speter { 160642575Speter /* do in-code checking if not discarding */ 160742575Speter if (!bitset(EF_DISCARD, e->e_flags)) 160864562Sgshapiro { 160942575Speter rcode = checkcompat(to, e); 161090792Sgshapiro ovr = false; 161164562Sgshapiro } 161238032Speter } 161338032Speter if (rcode != EX_OK) 161438032Speter { 161564562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 161664562Sgshapiro giveresponse(rcode, to->q_status, m, 161790792Sgshapiro NULL, ctladdr, xstart, e, to); 161838032Speter continue; 161938032Speter } 162090792Sgshapiro if (!quarantine && e->e_quarmsg != NULL) 162190792Sgshapiro { 162290792Sgshapiro /* 162390792Sgshapiro ** check_compat or checkcompat() has tried 162490792Sgshapiro ** to quarantine but that isn't supported. 162590792Sgshapiro ** Revert the attempt. 162690792Sgshapiro */ 162790792Sgshapiro 162890792Sgshapiro e->e_quarmsg = NULL; 162990792Sgshapiro macdefine(&e->e_macro, A_PERM, 163090792Sgshapiro macid("{quarantine}"), ""); 163190792Sgshapiro } 163242575Speter if (bitset(EF_DISCARD, e->e_flags)) 163342575Speter { 163442575Speter if (tTd(10, 5)) 163542575Speter { 163690792Sgshapiro sm_dprintf("deliver: discarding recipient "); 1637132943Sgshapiro printaddr(sm_debug_file(), to, false); 163842575Speter } 163938032Speter 164064562Sgshapiro /* pretend the message was sent */ 164164562Sgshapiro /* XXX should we log something here? */ 164264562Sgshapiro to->q_state = QS_DISCARDED; 164364562Sgshapiro 164442575Speter /* 164542575Speter ** Remove discard bit to prevent discard of 164664562Sgshapiro ** future recipients. This is safe because the 164764562Sgshapiro ** true "global discard" has been handled before 164864562Sgshapiro ** we get here. 164942575Speter */ 165064562Sgshapiro 165142575Speter e->e_flags &= ~EF_DISCARD; 165242575Speter continue; 165342575Speter } 165442575Speter 165538032Speter /* 165638032Speter ** Strip quote bits from names if the mailer is dumb 165738032Speter ** about them. 165838032Speter */ 165938032Speter 166038032Speter if (bitnset(M_STRIPQ, m->m_flags)) 166138032Speter { 166238032Speter stripquotes(user); 166338032Speter stripquotes(host); 166438032Speter } 1665132943Sgshapiro 1666110560Sgshapiro /* 1667141858Sgshapiro ** Strip all leading backslashes if requested and the 1668110560Sgshapiro ** next character is alphanumerical (the latter can 1669110560Sgshapiro ** probably relaxed a bit, see RFC2821). 1670110560Sgshapiro */ 167138032Speter 1672110560Sgshapiro if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1673110560Sgshapiro stripbackslash(user); 1674110560Sgshapiro 167538032Speter /* hack attack -- delivermail compatibility */ 167638032Speter if (m == ProgMailer && *user == '|') 167738032Speter user++; 167838032Speter 167938032Speter /* 168038032Speter ** If an error message has already been given, don't 168138032Speter ** bother to send to this address. 168238032Speter ** 168338032Speter ** >>>>>>>>>> This clause assumes that the local mailer 168438032Speter ** >> NOTE >> cannot do any further aliasing; that 168538032Speter ** >>>>>>>>>> function is subsumed by sendmail. 168638032Speter */ 168738032Speter 168864562Sgshapiro if (!QS_IS_OK(to->q_state)) 168938032Speter continue; 169038032Speter 169138032Speter /* 169238032Speter ** See if this user name is "special". 169338032Speter ** If the user name has a slash in it, assume that this 169438032Speter ** is a file -- send it off without further ado. Note 169538032Speter ** that this type of addresses is not processed along 169638032Speter ** with the others, so we fudge on the To person. 169738032Speter */ 169838032Speter 169938032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 170038032Speter { 170190792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); 170238032Speter p = to->q_home; 170338032Speter if (p == NULL && ctladdr != NULL) 170438032Speter p = ctladdr->q_home; 170590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); 1706168515Sgshapiro expand(m->m_argv[1], buf, sizeof(buf), e); 170738032Speter if (strlen(buf) > 0) 170838032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 170938032Speter else 171038032Speter { 171138032Speter syserr("empty filename specification for mailer %s", 171238032Speter m->m_name); 171338032Speter rcode = EX_CONFIG; 171438032Speter } 171564562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 171690792Sgshapiro ctladdr, xstart, e, to); 171790792Sgshapiro markfailure(e, to, NULL, rcode, true); 171838032Speter e->e_nsent++; 171938032Speter if (rcode == EX_OK) 172038032Speter { 172164562Sgshapiro to->q_state = QS_SENT; 172238032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 172338032Speter bitset(QPINGONSUCCESS, to->q_flags)) 172438032Speter { 172538032Speter to->q_flags |= QDELIVERED; 172638032Speter to->q_status = "2.1.5"; 172790792Sgshapiro (void) sm_io_fprintf(e->e_xfp, 172890792Sgshapiro SM_TIME_DEFAULT, 172990792Sgshapiro "%s... Successfully delivered\n", 173090792Sgshapiro to->q_paddr); 173138032Speter } 173238032Speter } 173338032Speter to->q_statdate = curtime(); 173490792Sgshapiro markstats(e, to, STATS_NORMAL); 173538032Speter continue; 173638032Speter } 173738032Speter 173838032Speter /* 173938032Speter ** Address is verified -- add this user to mailer 174038032Speter ** argv, and add it to the print list of recipients. 174138032Speter */ 174238032Speter 174338032Speter /* link together the chain of recipients */ 174438032Speter to->q_tchain = tochain; 174538032Speter tochain = to; 174664562Sgshapiro e->e_to = "[CHAIN]"; 174764562Sgshapiro 174890792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 174938032Speter p = to->q_home; 175038032Speter if (p == NULL && ctladdr != NULL) 175138032Speter p = ctladdr->q_home; 175290792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 175338032Speter 175464562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 175564562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 175664562Sgshapiro { 175764562Sgshapiro char notify[MAXLINE]; 175864562Sgshapiro 175964562Sgshapiro notify[0] = '\0'; 176064562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 176190792Sgshapiro (void) sm_strlcat(notify, "SUCCESS,", 1762168515Sgshapiro sizeof(notify)); 176364562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 176490792Sgshapiro (void) sm_strlcat(notify, "FAILURE,", 1765168515Sgshapiro sizeof(notify)); 176664562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 176790792Sgshapiro (void) sm_strlcat(notify, "DELAY,", 1768168515Sgshapiro sizeof(notify)); 176964562Sgshapiro 177064562Sgshapiro /* Set to NEVER or drop trailing comma */ 177164562Sgshapiro if (notify[0] == '\0') 177290792Sgshapiro (void) sm_strlcat(notify, "NEVER", 1773168515Sgshapiro sizeof(notify)); 177464562Sgshapiro else 177564562Sgshapiro notify[strlen(notify) - 1] = '\0'; 177664562Sgshapiro 177790792Sgshapiro macdefine(&e->e_macro, A_TEMP, 177890792Sgshapiro macid("{dsn_notify}"), notify); 177964562Sgshapiro } 178064562Sgshapiro else 178190792Sgshapiro macdefine(&e->e_macro, A_PERM, 178290792Sgshapiro macid("{dsn_notify}"), NULL); 178364562Sgshapiro 178438032Speter /* 178538032Speter ** Expand out this user into argument list. 178638032Speter */ 178738032Speter 178838032Speter if (!clever) 178938032Speter { 1790168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 179190792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 179238032Speter if (pvp >= &pv[MAXPV - 2]) 179338032Speter { 179438032Speter /* allow some space for trailing parms */ 179538032Speter break; 179638032Speter } 179738032Speter } 179838032Speter } 179938032Speter 180038032Speter /* see if any addresses still exist */ 180164562Sgshapiro if (tochain == NULL) 180238032Speter { 180390792Sgshapiro rcode = 0; 180490792Sgshapiro goto cleanup; 180538032Speter } 180638032Speter 180738032Speter /* print out messages as full list */ 180890792Sgshapiro strsize = 1; 180990792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 181090792Sgshapiro strsize += strlen(to->q_paddr) + 1; 181190792Sgshapiro if (strsize < TOBUFSIZE) 181290792Sgshapiro strsize = TOBUFSIZE; 181390792Sgshapiro if (strsize > tobufsize) 181464562Sgshapiro { 181590792Sgshapiro SM_FREE_CLR(tobuf); 181690792Sgshapiro tobuf = sm_pmalloc_x(strsize); 181790792Sgshapiro tobufsize = strsize; 181864562Sgshapiro } 181990792Sgshapiro p = tobuf; 182090792Sgshapiro *p = '\0'; 182190792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 182290792Sgshapiro { 182390792Sgshapiro (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 182490792Sgshapiro ",", to->q_paddr); 182590792Sgshapiro p += strlen(p); 182690792Sgshapiro } 182738032Speter e->e_to = tobuf + 1; 182838032Speter 182938032Speter /* 183038032Speter ** Fill out any parameters after the $u parameter. 183138032Speter */ 183238032Speter 183390792Sgshapiro if (!clever) 183438032Speter { 183590792Sgshapiro while (*++mvp != NULL) 183690792Sgshapiro { 1837168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 183890792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 183990792Sgshapiro if (pvp >= &pv[MAXPV]) 184090792Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 184190792Sgshapiro pv[0]); 184290792Sgshapiro } 184338032Speter } 184438032Speter *pvp++ = NULL; 184538032Speter 184638032Speter /* 184738032Speter ** Call the mailer. 184838032Speter ** The argument vector gets built, pipes 184938032Speter ** are created as necessary, and we fork & exec as 185038032Speter ** appropriate. 185138032Speter ** If we are running SMTP, we just need to clean up. 185238032Speter */ 185338032Speter 1854223067Sgshapiro /* XXX this seems a bit weird */ 185538032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 185638032Speter bitset(QGOODUID, e->e_from.q_flags)) 185738032Speter ctladdr = &e->e_from; 185838032Speter 185938032Speter#if NAMED_BIND 186038032Speter if (ConfigLevel < 2) 186138032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 186264562Sgshapiro#endif /* NAMED_BIND */ 186338032Speter 186438032Speter if (tTd(11, 1)) 186538032Speter { 186690792Sgshapiro sm_dprintf("openmailer:"); 1867132943Sgshapiro printav(sm_debug_file(), pv); 186838032Speter } 186938032Speter errno = 0; 187073188Sgshapiro SM_SET_H_ERRNO(0); 187138032Speter CurHostName = NULL; 187238032Speter 187338032Speter /* 187438032Speter ** Deal with the special case of mail handled through an IPC 187538032Speter ** connection. 187638032Speter ** In this case we don't actually fork. We must be 187738032Speter ** running SMTP for this to work. We will return a 187838032Speter ** zero pid to indicate that we are running IPC. 187938032Speter ** We also handle a debug version that just talks to stdin/out. 188038032Speter */ 188138032Speter 188238032Speter curhost = NULL; 188338032Speter SmtpPhase = NULL; 188438032Speter mci = NULL; 188538032Speter 188638032Speter#if XDEBUG 188738032Speter { 188838032Speter char wbuf[MAXLINE]; 188938032Speter 189038032Speter /* make absolutely certain 0, 1, and 2 are in use */ 1891168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)", 189290792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 189390792Sgshapiro m->m_name); 189438032Speter checkfd012(wbuf); 189538032Speter } 189664562Sgshapiro#endif /* XDEBUG */ 189738032Speter 189838032Speter /* check for 8-bit available */ 189938032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 190038032Speter bitnset(M_7BITS, m->m_flags) && 190138032Speter (bitset(EF_DONT_MIME, e->e_flags) || 190238032Speter !(bitset(MM_MIME8BIT, MimeMode) || 190338032Speter (bitset(EF_IS_MIME, e->e_flags) && 190464562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 190538032Speter { 190664562Sgshapiro e->e_status = "5.6.3"; 190764562Sgshapiro usrerrenh(e->e_status, 190890792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 190938032Speter rcode = EX_DATAERR; 191038032Speter goto give_up; 191138032Speter } 191238032Speter 191338032Speter if (tTd(62, 8)) 191438032Speter checkfds("before delivery"); 191538032Speter 191638032Speter /* check for Local Person Communication -- not for mortals!!! */ 191738032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 191838032Speter { 191990792Sgshapiro if (clever) 192090792Sgshapiro { 192190792Sgshapiro /* flush any expired connections */ 192290792Sgshapiro (void) mci_scan(NULL); 192390792Sgshapiro 192490792Sgshapiro /* try to get a cached connection or just a slot */ 192590792Sgshapiro mci = mci_get(m->m_name, m); 192690792Sgshapiro if (mci->mci_host == NULL) 192790792Sgshapiro mci->mci_host = m->m_name; 192890792Sgshapiro CurHostName = mci->mci_host; 192990792Sgshapiro if (mci->mci_state != MCIS_CLOSED) 193090792Sgshapiro { 193190792Sgshapiro message("Using cached SMTP/LPC connection for %s...", 193290792Sgshapiro m->m_name); 193390792Sgshapiro mci->mci_deliveries++; 193490792Sgshapiro goto do_transfer; 193590792Sgshapiro } 193690792Sgshapiro } 193790792Sgshapiro else 193890792Sgshapiro { 193990792Sgshapiro mci = mci_new(e->e_rpool); 194090792Sgshapiro } 194190792Sgshapiro mci->mci_in = smioin; 194290792Sgshapiro mci->mci_out = smioout; 194390792Sgshapiro mci->mci_mailer = m; 194490792Sgshapiro mci->mci_host = m->m_name; 194590792Sgshapiro if (clever) 194690792Sgshapiro { 194790792Sgshapiro mci->mci_state = MCIS_OPENING; 194890792Sgshapiro mci_cache(mci); 194990792Sgshapiro } 195090792Sgshapiro else 195190792Sgshapiro mci->mci_state = MCIS_OPEN; 195238032Speter } 195390792Sgshapiro else if (strcmp(m->m_mailer, "[IPC]") == 0) 195438032Speter { 195538032Speter register int i; 195638032Speter 195738032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 195838032Speter { 195964562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 196038032Speter rcode = EX_CONFIG; 196138032Speter goto give_up; 196238032Speter } 196338032Speter 196464562Sgshapiro# if NETUNIX 196564562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 196664562Sgshapiro { 196764562Sgshapiro curhost = CurHostName = "localhost"; 196864562Sgshapiro mux_path = pv[1]; 196964562Sgshapiro } 197064562Sgshapiro else 197164562Sgshapiro# endif /* NETUNIX */ 197264562Sgshapiro { 197364562Sgshapiro CurHostName = pv[1]; 197464562Sgshapiro curhost = hostsignature(m, pv[1]); 197564562Sgshapiro } 197638032Speter 197738032Speter if (curhost == NULL || curhost[0] == '\0') 197838032Speter { 197938032Speter syserr("null host signature for %s", pv[1]); 198038032Speter rcode = EX_CONFIG; 198138032Speter goto give_up; 198238032Speter } 198338032Speter 198438032Speter if (!clever) 198538032Speter { 198664562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 198738032Speter rcode = EX_CONFIG; 198838032Speter goto give_up; 198938032Speter } 199064562Sgshapiro if (pv[2] != NULL 199164562Sgshapiro# if NETUNIX 199264562Sgshapiro && mux_path == NULL 199364562Sgshapiro# endif /* NETUNIX */ 199464562Sgshapiro ) 199538032Speter { 199690792Sgshapiro port = htons((unsigned short) atoi(pv[2])); 199738032Speter if (port == 0) 199838032Speter { 199964562Sgshapiro# ifdef NO_GETSERVBYNAME 200064562Sgshapiro syserr("Invalid port number: %s", pv[2]); 200164562Sgshapiro# else /* NO_GETSERVBYNAME */ 200238032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 200338032Speter 200438032Speter if (sp == NULL) 200538032Speter syserr("Service %s unknown", pv[2]); 200638032Speter else 200738032Speter port = sp->s_port; 200864562Sgshapiro# endif /* NO_GETSERVBYNAME */ 200938032Speter } 201038032Speter } 201164562Sgshapiro 201264562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 201390792Sgshapiro if (TimeOuts.to_aconnect > 0) 201490792Sgshapiro enough = curtime() + TimeOuts.to_aconnect; 201538032Spetertryhost: 201664562Sgshapiro while (hostnum < nummxhosts) 201738032Speter { 201864562Sgshapiro char sep = ':'; 201964562Sgshapiro char *endp; 202038032Speter static char hostbuf[MAXNAME + 1]; 2021132943Sgshapiro bool tried_fallbacksmarthost = false; 202238032Speter 202364562Sgshapiro# if NETINET6 202464562Sgshapiro if (*mxhosts[hostnum] == '[') 202538032Speter { 202664562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 202764562Sgshapiro if (endp != NULL) 202864562Sgshapiro endp = strpbrk(endp + 1, ":,"); 202964562Sgshapiro } 203064562Sgshapiro else 203164562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 203264562Sgshapiro# else /* NETINET6 */ 203364562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 203464562Sgshapiro# endif /* NETINET6 */ 203564562Sgshapiro if (endp != NULL) 203664562Sgshapiro { 203764562Sgshapiro sep = *endp; 203864562Sgshapiro *endp = '\0'; 203964562Sgshapiro } 204064562Sgshapiro 204190792Sgshapiro if (hostnum == 1 && skip_back != NULL) 204290792Sgshapiro { 204390792Sgshapiro /* 204490792Sgshapiro ** Coattail piggybacking is no longer an 204590792Sgshapiro ** option with the mail host next to be tried 204690792Sgshapiro ** no longer the lowest MX preference 204790792Sgshapiro ** (hostnum == 1 meaning we're on the second 204890792Sgshapiro ** preference). We do not try to coattail 204990792Sgshapiro ** piggyback more than the first MX preference. 205090792Sgshapiro ** Revert 'tochain' to last location for 205190792Sgshapiro ** coincidental piggybacking. This works this 205290792Sgshapiro ** easily because the q_tchain kept getting 205390792Sgshapiro ** added to the top of the linked list. 205490792Sgshapiro */ 205590792Sgshapiro 205690792Sgshapiro tochain = skip_back; 205790792Sgshapiro } 205890792Sgshapiro 205964562Sgshapiro if (*mxhosts[hostnum] == '\0') 206064562Sgshapiro { 206138032Speter syserr("deliver: null host name in signature"); 206264562Sgshapiro hostnum++; 206364562Sgshapiro if (endp != NULL) 206464562Sgshapiro *endp = sep; 206538032Speter continue; 206638032Speter } 206790792Sgshapiro (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 2068168515Sgshapiro sizeof(hostbuf)); 206964562Sgshapiro hostnum++; 207064562Sgshapiro if (endp != NULL) 207164562Sgshapiro *endp = sep; 207238032Speter 2073132943Sgshapiro one_last_try: 207438032Speter /* see if we already know that this host is fried */ 207538032Speter CurHostName = hostbuf; 207638032Speter mci = mci_get(hostbuf, m); 207738032Speter if (mci->mci_state != MCIS_CLOSED) 207838032Speter { 207990792Sgshapiro char *type; 208090792Sgshapiro 208138032Speter if (tTd(11, 1)) 208238032Speter { 208390792Sgshapiro sm_dprintf("openmailer: "); 2084132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 208538032Speter } 208638032Speter CurHostName = mci->mci_host; 208790792Sgshapiro if (bitnset(M_LMTP, m->m_flags)) 208890792Sgshapiro type = "L"; 208990792Sgshapiro else if (bitset(MCIF_ESMTP, mci->mci_flags)) 209090792Sgshapiro type = "ES"; 209190792Sgshapiro else 209290792Sgshapiro type = "S"; 209390792Sgshapiro message("Using cached %sMTP connection to %s via %s...", 209490792Sgshapiro type, hostbuf, m->m_name); 209564562Sgshapiro mci->mci_deliveries++; 209638032Speter break; 209738032Speter } 209838032Speter mci->mci_mailer = m; 209938032Speter if (mci->mci_exitstat != EX_OK) 210038032Speter { 210138032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 210290792Sgshapiro goodmxfound = true; 2103132943Sgshapiro 2104132943Sgshapiro /* Try FallbackSmartHost? */ 2105132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2106168515Sgshapiro hostbuf, sizeof(hostbuf), 2107132943Sgshapiro mci->mci_exitstat)) 2108132943Sgshapiro goto one_last_try; 2109132943Sgshapiro 211038032Speter continue; 211138032Speter } 211238032Speter 211338032Speter if (mci_lock_host(mci) != EX_OK) 211438032Speter { 211538032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 211690792Sgshapiro goodmxfound = true; 211738032Speter continue; 211838032Speter } 211938032Speter 212038032Speter /* try the connection */ 212190792Sgshapiro sm_setproctitle(true, e, "%s %s: %s", 212264562Sgshapiro qid_printname(e), 212364562Sgshapiro hostbuf, "user open"); 212464562Sgshapiro# if NETUNIX 212564562Sgshapiro if (mux_path != NULL) 212664562Sgshapiro { 212738032Speter message("Connecting to %s via %s...", 212864562Sgshapiro mux_path, m->m_name); 212990792Sgshapiro i = makeconnection_ds((char *) mux_path, mci); 213064562Sgshapiro } 213138032Speter else 213264562Sgshapiro# endif /* NETUNIX */ 213364562Sgshapiro { 213464562Sgshapiro if (port == 0) 213564562Sgshapiro message("Connecting to %s via %s...", 213664562Sgshapiro hostbuf, m->m_name); 213764562Sgshapiro else 213864562Sgshapiro message("Connecting to %s port %d via %s...", 213964562Sgshapiro hostbuf, ntohs(port), 214064562Sgshapiro m->m_name); 214190792Sgshapiro i = makeconnection(hostbuf, port, mci, e, 214290792Sgshapiro enough); 214364562Sgshapiro } 214477349Sgshapiro mci->mci_errno = errno; 214538032Speter mci->mci_lastuse = curtime(); 214664562Sgshapiro mci->mci_deliveries = 0; 214738032Speter mci->mci_exitstat = i; 2148223067Sgshapiro mci_clr_extensions(mci); 214964562Sgshapiro# if NAMED_BIND 215038032Speter mci->mci_herrno = h_errno; 215164562Sgshapiro# endif /* NAMED_BIND */ 215290792Sgshapiro 215390792Sgshapiro /* 215490792Sgshapiro ** Have we tried long enough to get a connection? 215590792Sgshapiro ** If yes, skip to the fallback MX hosts 215690792Sgshapiro ** (if existent). 215790792Sgshapiro */ 215890792Sgshapiro 215990792Sgshapiro if (enough > 0 && mci->mci_lastuse >= enough) 216090792Sgshapiro { 216190792Sgshapiro int h; 216290792Sgshapiro# if NAMED_BIND 2163132943Sgshapiro extern int NumFallbackMXHosts; 216490792Sgshapiro# else /* NAMED_BIND */ 2165132943Sgshapiro const int NumFallbackMXHosts = 0; 216690792Sgshapiro# endif /* NAMED_BIND */ 216790792Sgshapiro 216890792Sgshapiro if (hostnum < nummxhosts && LogLevel > 9) 216990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 217090792Sgshapiro "Timeout.to_aconnect occurred before exhausting all addresses"); 217190792Sgshapiro 217290792Sgshapiro /* turn off timeout if fallback available */ 2173132943Sgshapiro if (NumFallbackMXHosts > 0) 217490792Sgshapiro enough = 0; 217590792Sgshapiro 217690792Sgshapiro /* skip to a fallback MX host */ 2177132943Sgshapiro h = nummxhosts - NumFallbackMXHosts; 217890792Sgshapiro if (hostnum < h) 217990792Sgshapiro hostnum = h; 218090792Sgshapiro } 218138032Speter if (i == EX_OK) 218238032Speter { 218390792Sgshapiro goodmxfound = true; 218494334Sgshapiro markstats(e, firstto, STATS_CONNECT); 218538032Speter mci->mci_state = MCIS_OPENING; 218638032Speter mci_cache(mci); 218738032Speter if (TrafficLogFile != NULL) 218890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 218990792Sgshapiro SM_TIME_DEFAULT, 219090792Sgshapiro "%05d === CONNECT %s\n", 219190792Sgshapiro (int) CurrentPid, 219290792Sgshapiro hostbuf); 219338032Speter break; 219438032Speter } 219538032Speter else 219638032Speter { 2197132943Sgshapiro /* Try FallbackSmartHost? */ 2198132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2199168515Sgshapiro hostbuf, sizeof(hostbuf), i)) 2200132943Sgshapiro goto one_last_try; 2201132943Sgshapiro 220264562Sgshapiro if (tTd(11, 1)) 220390792Sgshapiro sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 220490792Sgshapiro i, errno); 220538032Speter if (i == EX_TEMPFAIL) 220690792Sgshapiro goodmxfound = true; 220738032Speter mci_unlock_host(mci); 220838032Speter } 220938032Speter 221038032Speter /* enter status of this host */ 221138032Speter setstat(i); 221238032Speter 221338032Speter /* should print some message here for -v mode */ 221438032Speter } 221538032Speter if (mci == NULL) 221638032Speter { 221738032Speter syserr("deliver: no host name"); 221838032Speter rcode = EX_SOFTWARE; 221938032Speter goto give_up; 222038032Speter } 222138032Speter mci->mci_pid = 0; 222238032Speter } 222338032Speter else 222438032Speter { 222538032Speter /* flush any expired connections */ 222638032Speter (void) mci_scan(NULL); 222738032Speter mci = NULL; 222838032Speter 222938032Speter if (bitnset(M_LMTP, m->m_flags)) 223038032Speter { 223138032Speter /* try to get a cached connection */ 223238032Speter mci = mci_get(m->m_name, m); 223338032Speter if (mci->mci_host == NULL) 223438032Speter mci->mci_host = m->m_name; 223538032Speter CurHostName = mci->mci_host; 223638032Speter if (mci->mci_state != MCIS_CLOSED) 223738032Speter { 223838032Speter message("Using cached LMTP connection for %s...", 223938032Speter m->m_name); 224064562Sgshapiro mci->mci_deliveries++; 224138032Speter goto do_transfer; 224238032Speter } 224338032Speter } 224438032Speter 224538032Speter /* announce the connection to verbose listeners */ 224638032Speter if (host == NULL || host[0] == '\0') 224738032Speter message("Connecting to %s...", m->m_name); 224838032Speter else 224938032Speter message("Connecting to %s via %s...", host, m->m_name); 225038032Speter if (TrafficLogFile != NULL) 225138032Speter { 225238032Speter char **av; 225338032Speter 225490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 225590792Sgshapiro "%05d === EXEC", (int) CurrentPid); 225638032Speter for (av = pv; *av != NULL; av++) 225790792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 225890792Sgshapiro SM_TIME_DEFAULT, " %s", 225990792Sgshapiro *av); 226090792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 226190792Sgshapiro "\n"); 226238032Speter } 226338032Speter 226438032Speter#if XDEBUG 226538032Speter checkfd012("before creating mail pipe"); 226664562Sgshapiro#endif /* XDEBUG */ 226738032Speter 226838032Speter /* create a pipe to shove the mail through */ 226938032Speter if (pipe(mpvect) < 0) 227038032Speter { 227138032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 227290792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 227338032Speter if (tTd(11, 1)) 227490792Sgshapiro sm_dprintf("openmailer: NULL\n"); 227538032Speter rcode = EX_OSERR; 227638032Speter goto give_up; 227738032Speter } 227838032Speter 227938032Speter#if XDEBUG 228038032Speter /* make sure we didn't get one of the standard I/O files */ 228138032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 228238032Speter { 228338032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 228490792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 228590792Sgshapiro mpvect[0], mpvect[1]); 228690792Sgshapiro printopenfds(true); 228738032Speter if (tTd(11, 1)) 228890792Sgshapiro sm_dprintf("openmailer: NULL\n"); 228938032Speter rcode = EX_OSERR; 229038032Speter goto give_up; 229138032Speter } 229238032Speter 229338032Speter /* make sure system call isn't dead meat */ 229438032Speter checkfdopen(mpvect[0], "mpvect[0]"); 229538032Speter checkfdopen(mpvect[1], "mpvect[1]"); 229638032Speter if (mpvect[0] == mpvect[1] || 229738032Speter (e->e_lockfp != NULL && 229890792Sgshapiro (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 229990792Sgshapiro NULL) || 230090792Sgshapiro mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 230190792Sgshapiro NULL)))) 230238032Speter { 230338032Speter if (e->e_lockfp == NULL) 230438032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 230590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 230690792Sgshapiro m->m_name, mpvect[0], mpvect[1]); 230738032Speter else 230838032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 230990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 231090792Sgshapiro m->m_name, mpvect[0], mpvect[1], 231190792Sgshapiro sm_io_getinfo(e->e_lockfp, 231290792Sgshapiro SM_IO_WHAT_FD, NULL)); 231338032Speter } 231464562Sgshapiro#endif /* XDEBUG */ 231538032Speter 231664562Sgshapiro /* create a return pipe */ 231764562Sgshapiro if (pipe(rpvect) < 0) 231838032Speter { 231964562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 232090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 232190792Sgshapiro m->m_name); 232264562Sgshapiro (void) close(mpvect[0]); 232364562Sgshapiro (void) close(mpvect[1]); 232464562Sgshapiro if (tTd(11, 1)) 232590792Sgshapiro sm_dprintf("openmailer: NULL\n"); 232664562Sgshapiro rcode = EX_OSERR; 232764562Sgshapiro goto give_up; 232838032Speter } 232964562Sgshapiro#if XDEBUG 233064562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 233164562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 233264562Sgshapiro#endif /* XDEBUG */ 233338032Speter 233438032Speter /* 233538032Speter ** Actually fork the mailer process. 233638032Speter ** DOFORK is clever about retrying. 233738032Speter ** 233838032Speter ** Dispose of SIGCHLD signal catchers that may be laying 233964562Sgshapiro ** around so that endmailer will get it. 234038032Speter */ 234138032Speter 234290792Sgshapiro if (e->e_xfp != NULL) /* for debugging */ 234390792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 234490792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 234590792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 234664562Sgshapiro 234764562Sgshapiro 234838032Speter DOFORK(FORK); 234938032Speter /* pid is set by DOFORK */ 235064562Sgshapiro 235138032Speter if (pid < 0) 235238032Speter { 235338032Speter /* failure */ 235438032Speter syserr("%s... openmailer(%s): cannot fork", 235590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 235638032Speter (void) close(mpvect[0]); 235738032Speter (void) close(mpvect[1]); 235864562Sgshapiro (void) close(rpvect[0]); 235964562Sgshapiro (void) close(rpvect[1]); 236038032Speter if (tTd(11, 1)) 236190792Sgshapiro sm_dprintf("openmailer: NULL\n"); 236238032Speter rcode = EX_OSERR; 236338032Speter goto give_up; 236438032Speter } 236538032Speter else if (pid == 0) 236638032Speter { 236764562Sgshapiro int save_errno; 236890792Sgshapiro int sff; 236938032Speter int new_euid = NO_UID; 237038032Speter int new_ruid = NO_UID; 237138032Speter int new_gid = NO_GID; 237290792Sgshapiro char *user = NULL; 237338032Speter struct stat stb; 237438032Speter extern int DtableSize; 237538032Speter 237690792Sgshapiro CurrentPid = getpid(); 237790792Sgshapiro 237880785Sgshapiro /* clear the events to turn off SIGALRMs */ 237990792Sgshapiro sm_clear_events(); 238080785Sgshapiro 238177349Sgshapiro /* Reset global flags */ 238277349Sgshapiro RestartRequest = NULL; 238390792Sgshapiro RestartWorkGroup = false; 238477349Sgshapiro ShutdownRequest = NULL; 238577349Sgshapiro PendingSignal = 0; 238677349Sgshapiro 238738032Speter if (e->e_lockfp != NULL) 238890792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, 238990792Sgshapiro SM_IO_WHAT_FD, 239090792Sgshapiro NULL)); 239138032Speter 239238032Speter /* child -- set up input & exec mailer */ 239390792Sgshapiro (void) sm_signal(SIGALRM, sm_signal_noop); 239490792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 239590792Sgshapiro (void) sm_signal(SIGHUP, SIG_IGN); 239690792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 239790792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 239880785Sgshapiro# ifdef SIGUSR1 239990792Sgshapiro (void) sm_signal(SIGUSR1, sm_signal_noop); 240080785Sgshapiro# endif /* SIGUSR1 */ 240138032Speter 240238032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 240338032Speter stb.st_mode = 0; 240438032Speter 240564562Sgshapiro# if HASSETUSERCONTEXT 240638032Speter /* 240738032Speter ** Set user resources. 240838032Speter */ 240938032Speter 241038032Speter if (contextaddr != NULL) 241138032Speter { 2412110560Sgshapiro int sucflags; 241338032Speter struct passwd *pwd; 241438032Speter 241538032Speter if (contextaddr->q_ruser != NULL) 241638032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 241738032Speter else 241838032Speter pwd = sm_getpwnam(contextaddr->q_user); 2419110560Sgshapiro sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2420223701Strasz#ifdef LOGIN_SETCPUMASK 2421223701Strasz sucflags |= LOGIN_SETCPUMASK; 2422223701Strasz#endif /* LOGIN_SETCPUMASK */ 2423223701Strasz#ifdef LOGIN_SETLOGINCLASS 2424223701Strasz sucflags |= LOGIN_SETLOGINCLASS; 2425223701Strasz#endif /* LOGIN_SETLOGINCLASS */ 2426110560Sgshapiro#ifdef LOGIN_SETMAC 2427110560Sgshapiro sucflags |= LOGIN_SETMAC; 2428110560Sgshapiro#endif /* LOGIN_SETMAC */ 2429102528Sgshapiro if (pwd != NULL && 2430102528Sgshapiro setusercontext(NULL, pwd, pwd->pw_uid, 2431110560Sgshapiro sucflags) == -1 && 2432102528Sgshapiro suidwarn) 2433102528Sgshapiro { 2434102528Sgshapiro syserr("openmailer: setusercontext() failed"); 2435102528Sgshapiro exit(EX_TEMPFAIL); 2436102528Sgshapiro } 243738032Speter } 243864562Sgshapiro# endif /* HASSETUSERCONTEXT */ 243938032Speter 244090792Sgshapiro#if HASNICE 244138032Speter /* tweak niceness */ 244238032Speter if (m->m_nice != 0) 244364562Sgshapiro (void) nice(m->m_nice); 244490792Sgshapiro#endif /* HASNICE */ 244538032Speter 244638032Speter /* reset group id */ 244738032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 2448132943Sgshapiro { 2449132943Sgshapiro if (m->m_gid == NO_GID) 2450132943Sgshapiro new_gid = RunAsGid; 2451132943Sgshapiro else 2452132943Sgshapiro new_gid = m->m_gid; 2453132943Sgshapiro } 245438032Speter else if (bitset(S_ISGID, stb.st_mode)) 245538032Speter new_gid = stb.st_gid; 245638032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 245738032Speter { 245838032Speter if (!DontInitGroups) 245938032Speter { 246090792Sgshapiro user = ctladdr->q_ruser; 246190792Sgshapiro if (user == NULL) 246290792Sgshapiro user = ctladdr->q_user; 246338032Speter 246490792Sgshapiro if (initgroups(user, 246590792Sgshapiro ctladdr->q_gid) == -1 246690792Sgshapiro && suidwarn) 246764562Sgshapiro { 246838032Speter syserr("openmailer: initgroups(%s, %d) failed", 246990792Sgshapiro user, ctladdr->q_gid); 247064562Sgshapiro exit(EX_TEMPFAIL); 247164562Sgshapiro } 247238032Speter } 247338032Speter else 247438032Speter { 247538032Speter GIDSET_T gidset[1]; 247638032Speter 247738032Speter gidset[0] = ctladdr->q_gid; 247890792Sgshapiro if (setgroups(1, gidset) == -1 247990792Sgshapiro && suidwarn) 248064562Sgshapiro { 248138032Speter syserr("openmailer: setgroups() failed"); 248264562Sgshapiro exit(EX_TEMPFAIL); 248364562Sgshapiro } 248438032Speter } 248538032Speter new_gid = ctladdr->q_gid; 248638032Speter } 248738032Speter else 248838032Speter { 248938032Speter if (!DontInitGroups) 249038032Speter { 249190792Sgshapiro user = DefUser; 249290792Sgshapiro if (initgroups(DefUser, DefGid) == -1 && 249390792Sgshapiro suidwarn) 249464562Sgshapiro { 249538032Speter syserr("openmailer: initgroups(%s, %d) failed", 249690792Sgshapiro DefUser, DefGid); 249764562Sgshapiro exit(EX_TEMPFAIL); 249864562Sgshapiro } 249938032Speter } 250038032Speter else 250138032Speter { 250238032Speter GIDSET_T gidset[1]; 250338032Speter 250438032Speter gidset[0] = DefGid; 250590792Sgshapiro if (setgroups(1, gidset) == -1 250690792Sgshapiro && suidwarn) 250764562Sgshapiro { 250838032Speter syserr("openmailer: setgroups() failed"); 250964562Sgshapiro exit(EX_TEMPFAIL); 251064562Sgshapiro } 251138032Speter } 2512132943Sgshapiro if (m->m_gid == NO_GID) 251338032Speter new_gid = DefGid; 251438032Speter else 251538032Speter new_gid = m->m_gid; 251638032Speter } 251764562Sgshapiro if (new_gid != NO_GID) 251864562Sgshapiro { 251964562Sgshapiro if (RunAsUid != 0 && 252064562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 252164562Sgshapiro new_gid != getgid() && 252264562Sgshapiro new_gid != getegid()) 252364562Sgshapiro { 252464562Sgshapiro /* Only root can change the gid */ 252590792Sgshapiro syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d", 252690792Sgshapiro (int) RunAsUid, (int) new_gid, 252790792Sgshapiro (int) getgid(), (int) getegid()); 252864562Sgshapiro exit(EX_TEMPFAIL); 252964562Sgshapiro } 253038032Speter 253164562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 253264562Sgshapiro { 253364562Sgshapiro syserr("openmailer: setgid(%ld) failed", 253464562Sgshapiro (long) new_gid); 253564562Sgshapiro exit(EX_TEMPFAIL); 253664562Sgshapiro } 253764562Sgshapiro } 253864562Sgshapiro 253964562Sgshapiro /* change root to some "safe" directory */ 254064562Sgshapiro if (m->m_rootdir != NULL) 254164562Sgshapiro { 2542168515Sgshapiro expand(m->m_rootdir, cbuf, sizeof(cbuf), e); 254364562Sgshapiro if (tTd(11, 20)) 254490792Sgshapiro sm_dprintf("openmailer: chroot %s\n", 254598121Sgshapiro cbuf); 254698121Sgshapiro if (chroot(cbuf) < 0) 254764562Sgshapiro { 254864562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 254998121Sgshapiro cbuf); 255064562Sgshapiro exit(EX_TEMPFAIL); 255164562Sgshapiro } 255264562Sgshapiro if (chdir("/") < 0) 255364562Sgshapiro { 255464562Sgshapiro syserr("openmailer: cannot chdir(/)"); 255564562Sgshapiro exit(EX_TEMPFAIL); 255664562Sgshapiro } 255764562Sgshapiro } 255864562Sgshapiro 255938032Speter /* reset user id */ 256038032Speter endpwent(); 256190792Sgshapiro sm_mbdb_terminate(); 256238032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 256380785Sgshapiro { 2564132943Sgshapiro if (m->m_uid == NO_UID) 2565132943Sgshapiro new_euid = RunAsUid; 2566132943Sgshapiro else 2567132943Sgshapiro new_euid = m->m_uid; 256880785Sgshapiro 256980785Sgshapiro /* 257080785Sgshapiro ** Undo the effects of the uid change in main 257180785Sgshapiro ** for signal handling. The real uid may 257280785Sgshapiro ** be used by mailer in adding a "From " 257380785Sgshapiro ** line. 257480785Sgshapiro */ 257580785Sgshapiro 257680785Sgshapiro if (RealUid != 0 && RealUid != getuid()) 257790792Sgshapiro { 257890792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 257990792Sgshapiro# if HASSETREUID 258090792Sgshapiro if (setreuid(RealUid, geteuid()) < 0) 258190792Sgshapiro { 258290792Sgshapiro syserr("openmailer: setreuid(%d, %d) failed", 258390792Sgshapiro (int) RealUid, (int) geteuid()); 258490792Sgshapiro exit(EX_OSERR); 258590792Sgshapiro } 258690792Sgshapiro# endif /* HASSETREUID */ 258790792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 258890792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 258980785Sgshapiro new_ruid = RealUid; 259090792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 259190792Sgshapiro } 259280785Sgshapiro } 259338032Speter else if (bitset(S_ISUID, stb.st_mode)) 259438032Speter new_ruid = stb.st_uid; 259538032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 259638032Speter new_ruid = ctladdr->q_uid; 2597132943Sgshapiro else if (m->m_uid != NO_UID) 259838032Speter new_ruid = m->m_uid; 259938032Speter else 260038032Speter new_ruid = DefUid; 260194334Sgshapiro 260294334Sgshapiro# if _FFR_USE_SETLOGIN 260394334Sgshapiro /* run disconnected from terminal and set login name */ 260494334Sgshapiro if (setsid() >= 0 && 260594334Sgshapiro ctladdr != NULL && ctladdr->q_uid != 0 && 260694334Sgshapiro new_euid == ctladdr->q_uid) 260794334Sgshapiro { 260894334Sgshapiro struct passwd *pwd; 260994334Sgshapiro 261094334Sgshapiro pwd = sm_getpwuid(ctladdr->q_uid); 261194334Sgshapiro if (pwd != NULL && suidwarn) 261294334Sgshapiro (void) setlogin(pwd->pw_name); 261394334Sgshapiro endpwent(); 261494334Sgshapiro } 261594334Sgshapiro# endif /* _FFR_USE_SETLOGIN */ 261694334Sgshapiro 261738032Speter if (new_euid != NO_UID) 261838032Speter { 261964562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 262064562Sgshapiro { 262164562Sgshapiro /* Only root can change the uid */ 262290792Sgshapiro syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d", 262390792Sgshapiro (int) new_euid, (int) RunAsUid); 262464562Sgshapiro exit(EX_TEMPFAIL); 262564562Sgshapiro } 262664562Sgshapiro 262738032Speter vendor_set_uid(new_euid); 262864562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 262938032Speter if (seteuid(new_euid) < 0 && suidwarn) 263064562Sgshapiro { 263138032Speter syserr("openmailer: seteuid(%ld) failed", 263290792Sgshapiro (long) new_euid); 263364562Sgshapiro exit(EX_TEMPFAIL); 263464562Sgshapiro } 263564562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 263664562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 263738032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 263864562Sgshapiro { 263938032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 264090792Sgshapiro (long) new_ruid, (long) new_euid); 264164562Sgshapiro exit(EX_TEMPFAIL); 264264562Sgshapiro } 264364562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 264464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID 264538032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 264664562Sgshapiro { 264738032Speter syserr("openmailer: setuid(%ld) failed", 264890792Sgshapiro (long) new_euid); 264964562Sgshapiro exit(EX_TEMPFAIL); 265064562Sgshapiro } 265164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */ 265238032Speter } 265338032Speter else if (new_ruid != NO_UID) 265438032Speter { 265538032Speter vendor_set_uid(new_ruid); 265638032Speter if (setuid(new_ruid) < 0 && suidwarn) 265764562Sgshapiro { 265838032Speter syserr("openmailer: setuid(%ld) failed", 265990792Sgshapiro (long) new_ruid); 266064562Sgshapiro exit(EX_TEMPFAIL); 266164562Sgshapiro } 266238032Speter } 266338032Speter 266438032Speter if (tTd(11, 2)) 266590792Sgshapiro sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 266690792Sgshapiro (int) getuid(), (int) geteuid(), 266790792Sgshapiro (int) getgid(), (int) getegid()); 266838032Speter 266938032Speter /* move into some "safe" directory */ 267038032Speter if (m->m_execdir != NULL) 267138032Speter { 267238032Speter char *q; 267338032Speter 267438032Speter for (p = m->m_execdir; p != NULL; p = q) 267538032Speter { 267638032Speter q = strchr(p, ':'); 267738032Speter if (q != NULL) 267838032Speter *q = '\0'; 2679168515Sgshapiro expand(p, cbuf, sizeof(cbuf), e); 268038032Speter if (q != NULL) 268138032Speter *q++ = ':'; 268238032Speter if (tTd(11, 20)) 268390792Sgshapiro sm_dprintf("openmailer: trydir %s\n", 268498121Sgshapiro cbuf); 268598121Sgshapiro if (cbuf[0] != '\0' && 268698121Sgshapiro chdir(cbuf) >= 0) 268738032Speter break; 268838032Speter } 268938032Speter } 269038032Speter 269190792Sgshapiro /* Check safety of program to be run */ 269290792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 269390792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, 269490792Sgshapiro DontBlameSendmail)) 269590792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 269690792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 269790792Sgshapiro DontBlameSendmail)) 269890792Sgshapiro sff |= SFF_NOPATHCHECK; 269990792Sgshapiro else 270090792Sgshapiro sff |= SFF_SAFEDIRPATH; 270190792Sgshapiro ret = safefile(m->m_mailer, getuid(), getgid(), 270290792Sgshapiro user, sff, 0, NULL); 270390792Sgshapiro if (ret != 0) 270490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 270590792Sgshapiro "Warning: program %s unsafe: %s", 270690792Sgshapiro m->m_mailer, sm_errstring(ret)); 270790792Sgshapiro 270838032Speter /* arrange to filter std & diag output of command */ 270964562Sgshapiro (void) close(rpvect[0]); 271064562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 271138032Speter { 271264562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 271364562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 271464562Sgshapiro m->m_name, rpvect[1]); 271564562Sgshapiro _exit(EX_OSERR); 271638032Speter } 271764562Sgshapiro (void) close(rpvect[1]); 271864562Sgshapiro 271938032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 272038032Speter { 272138032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 272290792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 272390792Sgshapiro m->m_name); 272438032Speter _exit(EX_OSERR); 272538032Speter } 272638032Speter 272738032Speter /* arrange to get standard input */ 272838032Speter (void) close(mpvect[1]); 272938032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 273038032Speter { 273138032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 273290792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 273390792Sgshapiro m->m_name, mpvect[0]); 273438032Speter _exit(EX_OSERR); 273538032Speter } 273638032Speter (void) close(mpvect[0]); 273738032Speter 273838032Speter /* arrange for all the files to be closed */ 2739132943Sgshapiro sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 274038032Speter 274194334Sgshapiro# if !_FFR_USE_SETLOGIN 274238032Speter /* run disconnected from terminal */ 274338032Speter (void) setsid(); 274494334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */ 274538032Speter 274638032Speter /* try to execute the mailer */ 274764562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 274864562Sgshapiro (ARGV_T) UserEnviron); 274964562Sgshapiro save_errno = errno; 275038032Speter syserr("Cannot exec %s", m->m_mailer); 275138032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 275264562Sgshapiro transienterror(save_errno)) 275338032Speter _exit(EX_OSERR); 275438032Speter _exit(EX_UNAVAILABLE); 275538032Speter } 275638032Speter 275738032Speter /* 275838032Speter ** Set up return value. 275938032Speter */ 276038032Speter 276138032Speter if (mci == NULL) 276238032Speter { 276390792Sgshapiro if (clever) 276490792Sgshapiro { 276590792Sgshapiro /* 276690792Sgshapiro ** Allocate from general heap, not 276790792Sgshapiro ** envelope rpool, because this mci 276890792Sgshapiro ** is going to be cached. 276990792Sgshapiro */ 277090792Sgshapiro 277190792Sgshapiro mci = mci_new(NULL); 277290792Sgshapiro } 277390792Sgshapiro else 277490792Sgshapiro { 277590792Sgshapiro /* 277690792Sgshapiro ** Prevent a storage leak by allocating 277790792Sgshapiro ** this from the envelope rpool. 277890792Sgshapiro */ 277990792Sgshapiro 278090792Sgshapiro mci = mci_new(e->e_rpool); 278190792Sgshapiro } 278238032Speter } 278338032Speter mci->mci_mailer = m; 278438032Speter if (clever) 278538032Speter { 278638032Speter mci->mci_state = MCIS_OPENING; 278738032Speter mci_cache(mci); 278838032Speter } 278938032Speter else 279038032Speter { 279138032Speter mci->mci_state = MCIS_OPEN; 279238032Speter } 279338032Speter mci->mci_pid = pid; 279438032Speter (void) close(mpvect[0]); 279590792Sgshapiro mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2796132943Sgshapiro (void *) &(mpvect[1]), SM_IO_WRONLY_B, 279790792Sgshapiro NULL); 279838032Speter if (mci->mci_out == NULL) 279938032Speter { 280038032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 280190792Sgshapiro mpvect[1]); 280238032Speter (void) close(mpvect[1]); 280364562Sgshapiro (void) close(rpvect[0]); 280464562Sgshapiro (void) close(rpvect[1]); 280538032Speter rcode = EX_OSERR; 280638032Speter goto give_up; 280738032Speter } 280864562Sgshapiro 280964562Sgshapiro (void) close(rpvect[1]); 281090792Sgshapiro mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2811132943Sgshapiro (void *) &(rpvect[0]), SM_IO_RDONLY_B, 281290792Sgshapiro NULL); 281364562Sgshapiro if (mci->mci_in == NULL) 281438032Speter { 281564562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 281664562Sgshapiro mpvect[1]); 281764562Sgshapiro (void) close(rpvect[0]); 281890792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 281964562Sgshapiro mci->mci_out = NULL; 282064562Sgshapiro rcode = EX_OSERR; 282164562Sgshapiro goto give_up; 282238032Speter } 282338032Speter } 282438032Speter 282538032Speter /* 282638032Speter ** If we are in SMTP opening state, send initial protocol. 282738032Speter */ 282838032Speter 282938032Speter if (bitnset(M_7BITS, m->m_flags) && 283038032Speter (!clever || mci->mci_state == MCIS_OPENING)) 283138032Speter mci->mci_flags |= MCIF_7BIT; 283238032Speter if (clever && mci->mci_state != MCIS_CLOSED) 283338032Speter { 283490792Sgshapiro# if STARTTLS || SASL 283590792Sgshapiro int dotpos; 283690792Sgshapiro char *srvname; 283790792Sgshapiro extern SOCKADDR CurHostAddr; 283890792Sgshapiro# endif /* STARTTLS || SASL */ 283990792Sgshapiro 284090792Sgshapiro# if SASL 284171345Sgshapiro# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 284290792Sgshapiro# endif /* SASL */ 284364562Sgshapiro# if STARTTLS 284471345Sgshapiro# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 284564562Sgshapiro# endif /* STARTTLS */ 284671345Sgshapiro# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 284771345Sgshapiro# define SET_HELO(f) f |= MCIF_ONLY_EHLO 284871345Sgshapiro# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 284938032Speter 285090792Sgshapiro# if STARTTLS || SASL 285190792Sgshapiro /* don't use CurHostName, it is changed in many places */ 285290792Sgshapiro if (mci->mci_host != NULL) 285390792Sgshapiro { 285490792Sgshapiro srvname = mci->mci_host; 285590792Sgshapiro dotpos = strlen(srvname) - 1; 285690792Sgshapiro if (dotpos >= 0) 285790792Sgshapiro { 285890792Sgshapiro if (srvname[dotpos] == '.') 285990792Sgshapiro srvname[dotpos] = '\0'; 286090792Sgshapiro else 286190792Sgshapiro dotpos = -1; 286290792Sgshapiro } 286390792Sgshapiro } 286490792Sgshapiro else if (mci->mci_mailer != NULL) 286590792Sgshapiro { 286690792Sgshapiro srvname = mci->mci_mailer->m_name; 286790792Sgshapiro dotpos = -1; 286890792Sgshapiro } 286990792Sgshapiro else 287090792Sgshapiro { 287190792Sgshapiro srvname = "local"; 287290792Sgshapiro dotpos = -1; 287390792Sgshapiro } 287471345Sgshapiro 287590792Sgshapiro /* don't set {server_name} to NULL or "": see getauth() */ 287690792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 287790792Sgshapiro srvname); 287864562Sgshapiro 287990792Sgshapiro /* CurHostAddr is set by makeconnection() and mci_get() */ 288090792Sgshapiro if (CurHostAddr.sa.sa_family != 0) 288190792Sgshapiro { 288290792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, 288390792Sgshapiro macid("{server_addr}"), 288490792Sgshapiro anynet_ntoa(&CurHostAddr)); 288590792Sgshapiro } 288690792Sgshapiro else if (mci->mci_mailer != NULL) 288790792Sgshapiro { 288890792Sgshapiro /* mailer name is unique, use it as address */ 288990792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 289090792Sgshapiro macid("{server_addr}"), 289190792Sgshapiro mci->mci_mailer->m_name); 289290792Sgshapiro } 289390792Sgshapiro else 289490792Sgshapiro { 289590792Sgshapiro /* don't set it to NULL or "": see getauth() */ 289690792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 289790792Sgshapiro macid("{server_addr}"), "0"); 289890792Sgshapiro } 289990792Sgshapiro 290090792Sgshapiro /* undo change of srvname (mci->mci_host) */ 290190792Sgshapiro if (dotpos >= 0) 290290792Sgshapiro srvname[dotpos] = '.'; 290390792Sgshapiro 290490792Sgshapiroreconnect: /* after switching to an encrypted connection */ 290590792Sgshapiro# endif /* STARTTLS || SASL */ 290690792Sgshapiro 290790792Sgshapiro /* set the current connection information */ 290890792Sgshapiro e->e_mci = mci; 290964562Sgshapiro# if SASL 291064562Sgshapiro mci->mci_saslcap = NULL; 291164562Sgshapiro# endif /* SASL */ 291271345Sgshapiro smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 291371345Sgshapiro CLR_HELO(mci->mci_flags); 291464562Sgshapiro 291590792Sgshapiro if (IS_DLVR_RETURN(e)) 291690792Sgshapiro { 291790792Sgshapiro /* 291890792Sgshapiro ** Check whether other side can deliver e-mail 291990792Sgshapiro ** fast enough 292090792Sgshapiro */ 292190792Sgshapiro 292290792Sgshapiro if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 292390792Sgshapiro { 292490792Sgshapiro e->e_status = "5.4.7"; 292590792Sgshapiro usrerrenh(e->e_status, 292690792Sgshapiro "554 Server does not support Deliver By"); 292790792Sgshapiro rcode = EX_UNAVAILABLE; 292890792Sgshapiro goto give_up; 292990792Sgshapiro } 293090792Sgshapiro if (e->e_deliver_by > 0 && 293190792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime) < 293290792Sgshapiro mci->mci_min_by) 293390792Sgshapiro { 293490792Sgshapiro e->e_status = "5.4.7"; 293590792Sgshapiro usrerrenh(e->e_status, 293690792Sgshapiro "554 Message can't be delivered in time; %ld < %ld", 293790792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime), 293890792Sgshapiro mci->mci_min_by); 293990792Sgshapiro rcode = EX_UNAVAILABLE; 294090792Sgshapiro goto give_up; 294190792Sgshapiro } 294290792Sgshapiro } 294390792Sgshapiro 294464562Sgshapiro# if STARTTLS 294564562Sgshapiro /* first TLS then AUTH to provide a security layer */ 294671345Sgshapiro if (mci->mci_state != MCIS_CLOSED && 294771345Sgshapiro !DONE_STARTTLS(mci->mci_flags)) 294864562Sgshapiro { 294964562Sgshapiro int olderrors; 295064562Sgshapiro bool usetls; 295164562Sgshapiro bool saveQuickAbort = QuickAbort; 295264562Sgshapiro bool saveSuprErrs = SuprErrs; 295371345Sgshapiro char *host = NULL; 295464562Sgshapiro 295564562Sgshapiro rcode = EX_OK; 295664562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 295790792Sgshapiro if (usetls) 295890792Sgshapiro usetls = !iscltflgset(e, D_NOTLS); 295966494Sgshapiro 2960168515Sgshapiro host = macvalue(macid("{server_name}"), e); 296164562Sgshapiro if (usetls) 296264562Sgshapiro { 296364562Sgshapiro olderrors = Errors; 296490792Sgshapiro QuickAbort = false; 296590792Sgshapiro SuprErrs = true; 2966102528Sgshapiro if (rscheck("try_tls", host, NULL, e, 2967168515Sgshapiro RSF_RMCOMM, 7, host, NOQID, NULL) 2968168515Sgshapiro != EX_OK 296964562Sgshapiro || Errors > olderrors) 2970168515Sgshapiro { 297190792Sgshapiro usetls = false; 2972168515Sgshapiro } 297364562Sgshapiro SuprErrs = saveSuprErrs; 297464562Sgshapiro QuickAbort = saveQuickAbort; 297564562Sgshapiro } 297664562Sgshapiro 297764562Sgshapiro if (usetls) 297864562Sgshapiro { 297964562Sgshapiro if ((rcode = starttls(m, mci, e)) == EX_OK) 298064562Sgshapiro { 298164562Sgshapiro /* start again without STARTTLS */ 298264562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 298364562Sgshapiro } 298464562Sgshapiro else 298564562Sgshapiro { 298664562Sgshapiro char *s; 298764562Sgshapiro 298864562Sgshapiro /* 2989203004Sgshapiro ** TLS negotiation failed, what to do? 299064562Sgshapiro ** fall back to unencrypted connection 299164562Sgshapiro ** or abort? How to decide? 299264562Sgshapiro ** set a macro and call a ruleset. 299364562Sgshapiro */ 299490792Sgshapiro 299564562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 299664562Sgshapiro switch (rcode) 299764562Sgshapiro { 299864562Sgshapiro case EX_TEMPFAIL: 299964562Sgshapiro s = "TEMP"; 300064562Sgshapiro break; 300164562Sgshapiro case EX_USAGE: 300264562Sgshapiro s = "USAGE"; 300364562Sgshapiro break; 300464562Sgshapiro case EX_PROTOCOL: 300564562Sgshapiro s = "PROTOCOL"; 300664562Sgshapiro break; 300764562Sgshapiro case EX_SOFTWARE: 300864562Sgshapiro s = "SOFTWARE"; 300964562Sgshapiro break; 3010157001Sgshapiro case EX_UNAVAILABLE: 3011157001Sgshapiro s = "NONE"; 3012157001Sgshapiro break; 301364562Sgshapiro 301464562Sgshapiro /* everything else is a failure */ 301564562Sgshapiro default: 301664562Sgshapiro s = "FAILURE"; 301764562Sgshapiro rcode = EX_TEMPFAIL; 301864562Sgshapiro } 301990792Sgshapiro macdefine(&e->e_macro, A_PERM, 302090792Sgshapiro macid("{verify}"), s); 302164562Sgshapiro } 302264562Sgshapiro } 302364562Sgshapiro else 302490792Sgshapiro macdefine(&e->e_macro, A_PERM, 302590792Sgshapiro macid("{verify}"), "NONE"); 302664562Sgshapiro olderrors = Errors; 302790792Sgshapiro QuickAbort = false; 302890792Sgshapiro SuprErrs = true; 302964562Sgshapiro 303064562Sgshapiro /* 303164562Sgshapiro ** rcode == EX_SOFTWARE is special: 3032203004Sgshapiro ** the TLS negotiation failed 303364562Sgshapiro ** we have to drop the connection no matter what 303464562Sgshapiro ** However, we call tls_server to give it the chance 303564562Sgshapiro ** to log the problem and return an appropriate 303664562Sgshapiro ** error code. 303764562Sgshapiro */ 303890792Sgshapiro 303964562Sgshapiro if (rscheck("tls_server", 304090792Sgshapiro macvalue(macid("{verify}"), e), 3041102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3042168515Sgshapiro host, NOQID, NULL) != EX_OK || 304364562Sgshapiro Errors > olderrors || 304464562Sgshapiro rcode == EX_SOFTWARE) 304564562Sgshapiro { 304664562Sgshapiro char enhsc[ENHSCLEN]; 304764562Sgshapiro extern char MsgBuf[]; 304864562Sgshapiro 304964562Sgshapiro if (ISSMTPCODE(MsgBuf) && 305064562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 305164562Sgshapiro { 305290792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 305390792Sgshapiro MsgBuf); 305464562Sgshapiro } 305564562Sgshapiro else 305664562Sgshapiro { 305764562Sgshapiro p = "403 4.7.0 server not authenticated."; 305890792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 3059168515Sgshapiro sizeof(enhsc)); 306064562Sgshapiro } 306164562Sgshapiro SuprErrs = saveSuprErrs; 306264562Sgshapiro QuickAbort = saveQuickAbort; 306364562Sgshapiro 306464562Sgshapiro if (rcode == EX_SOFTWARE) 306564562Sgshapiro { 306664562Sgshapiro /* drop the connection */ 306764562Sgshapiro mci->mci_state = MCIS_QUITING; 306864562Sgshapiro if (mci->mci_in != NULL) 306964562Sgshapiro { 307090792Sgshapiro (void) sm_io_close(mci->mci_in, 307190792Sgshapiro SM_TIME_DEFAULT); 307264562Sgshapiro mci->mci_in = NULL; 307364562Sgshapiro } 307464562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 307564562Sgshapiro (void) endmailer(mci, e, pv); 307664562Sgshapiro } 307764562Sgshapiro else 307864562Sgshapiro { 307964562Sgshapiro /* abort transfer */ 308064562Sgshapiro smtpquit(m, mci, e); 308164562Sgshapiro } 308264562Sgshapiro 308371345Sgshapiro /* avoid bogus error msg */ 308471345Sgshapiro mci->mci_errno = 0; 308571345Sgshapiro 308664562Sgshapiro /* temp or permanent failure? */ 308764562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 308864562Sgshapiro : EX_UNAVAILABLE; 308990792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 309064562Sgshapiro 309164562Sgshapiro /* 309264562Sgshapiro ** hack to get the error message into 309364562Sgshapiro ** the envelope (done in giveresponse()) 309464562Sgshapiro */ 309590792Sgshapiro 309690792Sgshapiro (void) sm_strlcpy(SmtpError, p, 3097168515Sgshapiro sizeof(SmtpError)); 309864562Sgshapiro } 3099168515Sgshapiro else if (mci->mci_state == MCIS_CLOSED) 3100168515Sgshapiro { 3101168515Sgshapiro /* connection close caused by 421 */ 3102168515Sgshapiro mci->mci_errno = 0; 3103168515Sgshapiro rcode = EX_TEMPFAIL; 3104168515Sgshapiro mci_setstat(mci, rcode, NULL, "421"); 3105168515Sgshapiro } 3106168515Sgshapiro else 3107168515Sgshapiro rcode = 0; 3108168515Sgshapiro 310964562Sgshapiro QuickAbort = saveQuickAbort; 311064562Sgshapiro SuprErrs = saveSuprErrs; 311171345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 311271345Sgshapiro mci->mci_state != MCIS_CLOSED) 311364562Sgshapiro { 311471345Sgshapiro SET_HELO(mci->mci_flags); 3115223067Sgshapiro mci_clr_extensions(mci); 311664562Sgshapiro goto reconnect; 311764562Sgshapiro } 311864562Sgshapiro } 311964562Sgshapiro# endif /* STARTTLS */ 312064562Sgshapiro# if SASL 312164562Sgshapiro /* if other server supports authentication let's authenticate */ 312264562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 312364562Sgshapiro mci->mci_saslcap != NULL && 312490792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 312564562Sgshapiro { 312690792Sgshapiro /* Should we require some minimum authentication? */ 312790792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 312864562Sgshapiro { 312964562Sgshapiro int result; 313090792Sgshapiro sasl_ssf_t *ssf = NULL; 313164562Sgshapiro 313290792Sgshapiro /* Get security strength (features) */ 313364562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 313498121Sgshapiro# if SASL >= 20000 313598121Sgshapiro (const void **) &ssf); 313698121Sgshapiro# else /* SASL >= 20000 */ 313764562Sgshapiro (void **) &ssf); 313898121Sgshapiro# endif /* SASL >= 20000 */ 313990792Sgshapiro 314090792Sgshapiro /* XXX authid? */ 314164562Sgshapiro if (LogLevel > 9) 314264562Sgshapiro sm_syslog(LOG_INFO, NOQID, 314390792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 314464562Sgshapiro mci->mci_host, 314590792Sgshapiro macvalue(macid("{auth_type}"), e), 314690792Sgshapiro result == SASL_OK ? *ssf : 0); 314777349Sgshapiro 314864562Sgshapiro /* 314990792Sgshapiro ** Only switch to encrypted connection 315064562Sgshapiro ** if a security layer has been negotiated 315164562Sgshapiro */ 315290792Sgshapiro 315364562Sgshapiro if (result == SASL_OK && *ssf > 0) 315464562Sgshapiro { 3155159609Sgshapiro int tmo; 3156159609Sgshapiro 315764562Sgshapiro /* 315890792Sgshapiro ** Convert I/O layer to use SASL. 315990792Sgshapiro ** If the call fails, the connection 316090792Sgshapiro ** is aborted. 316164562Sgshapiro */ 316290792Sgshapiro 3163159609Sgshapiro tmo = DATA_PROGRESS_TIMEOUT * 1000; 316490792Sgshapiro if (sfdcsasl(&mci->mci_in, 316590792Sgshapiro &mci->mci_out, 3166159609Sgshapiro mci->mci_conn, tmo) == 0) 316764562Sgshapiro { 3168223067Sgshapiro mci_clr_extensions(mci); 316990792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 317090792Sgshapiro MCIF_ONLY_EHLO; 317164562Sgshapiro goto reconnect; 317264562Sgshapiro } 317390792Sgshapiro syserr("AUTH TLS switch failed in client"); 317464562Sgshapiro } 317564562Sgshapiro /* else? XXX */ 317664562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 317764562Sgshapiro 317864562Sgshapiro } 317990792Sgshapiro else if (ret == EX_TEMPFAIL) 318090792Sgshapiro { 318190792Sgshapiro if (LogLevel > 8) 318290792Sgshapiro sm_syslog(LOG_ERR, NOQID, 318390792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 318490792Sgshapiro mci->mci_host); 318590792Sgshapiro smtpquit(m, mci, e); 318690792Sgshapiro 318790792Sgshapiro /* avoid bogus error msg */ 318890792Sgshapiro mci->mci_errno = 0; 318990792Sgshapiro rcode = EX_TEMPFAIL; 3190132943Sgshapiro mci_setstat(mci, rcode, "4.3.0", p); 319190792Sgshapiro 319290792Sgshapiro /* 319390792Sgshapiro ** hack to get the error message into 319490792Sgshapiro ** the envelope (done in giveresponse()) 319590792Sgshapiro */ 319690792Sgshapiro 319790792Sgshapiro (void) sm_strlcpy(SmtpError, 319890792Sgshapiro "Temporary AUTH failure", 3199168515Sgshapiro sizeof(SmtpError)); 320090792Sgshapiro } 320164562Sgshapiro } 320264562Sgshapiro# endif /* SASL */ 320338032Speter } 320438032Speter 320564562Sgshapiro 320638032Speterdo_transfer: 320738032Speter /* clear out per-message flags from connection structure */ 320838032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 320938032Speter 321038032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 321138032Speter !bitset(EF_DONT_MIME, e->e_flags) && 321238032Speter bitnset(M_7BITS, m->m_flags)) 321338032Speter mci->mci_flags |= MCIF_CVT8TO7; 321438032Speter 321538032Speter#if MIME7TO8 321638032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 321738032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 321838032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 321990792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 322090792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 322138032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 322238032Speter { 322338032Speter /* may want to convert 7 -> 8 */ 322438032Speter /* XXX should really parse it here -- and use a class XXX */ 322590792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 322638032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 322738032Speter mci->mci_flags |= MCIF_CVT7TO8; 322838032Speter } 322964562Sgshapiro#endif /* MIME7TO8 */ 323038032Speter 323138032Speter if (tTd(11, 1)) 323238032Speter { 323390792Sgshapiro sm_dprintf("openmailer: "); 3234132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 323538032Speter } 323638032Speter 323790792Sgshapiro#if _FFR_CLIENT_SIZE 323890792Sgshapiro /* 323990792Sgshapiro ** See if we know the maximum size and 324090792Sgshapiro ** abort if the message is too big. 324190792Sgshapiro ** 324290792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 324390792Sgshapiro */ 324490792Sgshapiro 324590792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 324690792Sgshapiro mci->mci_maxsize > 0 && 324790792Sgshapiro e->e_msgsize > mci->mci_maxsize) 324890792Sgshapiro { 324990792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 325090792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 325190792Sgshapiro e->e_status = "5.2.3"; 325290792Sgshapiro else 325390792Sgshapiro e->e_status = "5.3.4"; 325490792Sgshapiro 325590792Sgshapiro usrerrenh(e->e_status, 325690792Sgshapiro "552 Message is too large; %ld bytes max", 325790792Sgshapiro mci->mci_maxsize); 325890792Sgshapiro rcode = EX_DATAERR; 325990792Sgshapiro 326090792Sgshapiro /* Need an e_message for error */ 3261168515Sgshapiro (void) sm_snprintf(SmtpError, sizeof(SmtpError), 326290792Sgshapiro "Message is too large; %ld bytes max", 326390792Sgshapiro mci->mci_maxsize); 326490792Sgshapiro goto give_up; 326590792Sgshapiro } 326690792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 326790792Sgshapiro 326838032Speter if (mci->mci_state != MCIS_OPEN) 326938032Speter { 327038032Speter /* couldn't open the mailer */ 327138032Speter rcode = mci->mci_exitstat; 327238032Speter errno = mci->mci_errno; 327373188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 327438032Speter if (rcode == EX_OK) 327538032Speter { 327638032Speter /* shouldn't happen */ 327764562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 327890792Sgshapiro (unsigned long) mci, rcode, errno, 327990792Sgshapiro mci->mci_state, firstsig); 3280132943Sgshapiro mci_dump_all(smioout, true); 328138032Speter rcode = EX_SOFTWARE; 328238032Speter } 328364562Sgshapiro else if (nummxhosts > hostnum) 328438032Speter { 328538032Speter /* try next MX site */ 328638032Speter goto tryhost; 328738032Speter } 328838032Speter } 328938032Speter else if (!clever) 329038032Speter { 3291157001Sgshapiro bool ok; 3292157001Sgshapiro 329338032Speter /* 329438032Speter ** Format and send message. 329538032Speter */ 329638032Speter 3297157001Sgshapiro rcode = EX_OK; 3298157001Sgshapiro errno = 0; 3299157001Sgshapiro ok = putfromline(mci, e); 3300157001Sgshapiro if (ok) 3301157001Sgshapiro ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 3302157001Sgshapiro if (ok) 3303157001Sgshapiro ok = (*e->e_putbody)(mci, e, NULL); 3304173340Sgshapiro if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags)) 3305173340Sgshapiro ok = putline("", mci); 330638032Speter 3307157001Sgshapiro /* 3308157001Sgshapiro ** Ignore an I/O error that was caused by EPIPE. 3309157001Sgshapiro ** Some broken mailers don't read the entire body 3310157001Sgshapiro ** but just exit() thus causing an I/O error. 3311157001Sgshapiro */ 3312157001Sgshapiro 3313157001Sgshapiro if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE)) 3314157001Sgshapiro ok = true; 3315157001Sgshapiro 3316157001Sgshapiro /* (always) get the exit status */ 331738032Speter rcode = endmailer(mci, e, pv); 3318157001Sgshapiro if (!ok) 3319157001Sgshapiro rcode = EX_TEMPFAIL; 332090792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 332173188Sgshapiro { 332273188Sgshapiro /* 332373188Sgshapiro ** Need an e_message for mailq display. 332473188Sgshapiro ** We set SmtpError as 332573188Sgshapiro */ 332673188Sgshapiro 3327168515Sgshapiro (void) sm_snprintf(SmtpError, sizeof(SmtpError), 332890792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 332990792Sgshapiro m->m_name, m->m_mailer); 333073188Sgshapiro } 333138032Speter } 333238032Speter else 333338032Speter { 333438032Speter /* 333538032Speter ** Send the MAIL FROM: protocol 333638032Speter */ 333738032Speter 333890792Sgshapiro /* XXX this isn't pipelined... */ 333938032Speter rcode = smtpmailfrom(m, mci, e); 334038032Speter if (rcode == EX_OK) 334138032Speter { 334238032Speter register int i; 334390792Sgshapiro# if PIPELINING 334490792Sgshapiro ADDRESS *volatile pchain; 334590792Sgshapiro# endif /* PIPELINING */ 334638032Speter 334738032Speter /* send the recipient list */ 334838032Speter tobuf[0] = '\0'; 334990792Sgshapiro mci->mci_retryrcpt = false; 335090792Sgshapiro mci->mci_tolist = tobuf; 335190792Sgshapiro# if PIPELINING 335290792Sgshapiro pchain = NULL; 335390792Sgshapiro mci->mci_nextaddr = NULL; 335490792Sgshapiro# endif /* PIPELINING */ 335564562Sgshapiro 335638032Speter for (to = tochain; to != NULL; to = to->q_tchain) 335738032Speter { 335890792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 335938032Speter continue; 336064562Sgshapiro 336190792Sgshapiro /* mark recipient state as "ok so far" */ 336290792Sgshapiro to->q_state = QS_OK; 336390792Sgshapiro e->e_to = to->q_paddr; 336464562Sgshapiro# if STARTTLS 336564562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3366102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3367168515Sgshapiro mci->mci_host, e->e_id, NULL); 336864562Sgshapiro if (i != EX_OK) 336938032Speter { 337090792Sgshapiro markfailure(e, to, mci, i, false); 337190792Sgshapiro giveresponse(i, to->q_status, m, mci, 337290792Sgshapiro ctladdr, xstart, e, to); 337390792Sgshapiro if (i == EX_TEMPFAIL) 337490792Sgshapiro { 337590792Sgshapiro mci->mci_retryrcpt = true; 337690792Sgshapiro to->q_state = QS_RETRY; 337790792Sgshapiro } 337864562Sgshapiro continue; 337938032Speter } 338064562Sgshapiro# endif /* STARTTLS */ 338164562Sgshapiro 338290792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 338390792Sgshapiro# if PIPELINING 338490792Sgshapiro if (i == EX_OK && 338590792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 338664562Sgshapiro { 338790792Sgshapiro /* 338890792Sgshapiro ** Add new element to list of 338990792Sgshapiro ** recipients for pipelining. 339090792Sgshapiro */ 339190792Sgshapiro 339290792Sgshapiro to->q_pchain = NULL; 339390792Sgshapiro if (mci->mci_nextaddr == NULL) 339490792Sgshapiro mci->mci_nextaddr = to; 339590792Sgshapiro if (pchain == NULL) 339690792Sgshapiro pchain = to; 339790792Sgshapiro else 339890792Sgshapiro { 339990792Sgshapiro pchain->q_pchain = to; 340090792Sgshapiro pchain = pchain->q_pchain; 340190792Sgshapiro } 340264562Sgshapiro } 340390792Sgshapiro# endif /* PIPELINING */ 340490792Sgshapiro if (i != EX_OK) 340538032Speter { 340690792Sgshapiro markfailure(e, to, mci, i, false); 340798841Sgshapiro giveresponse(i, to->q_status, m, mci, 340890792Sgshapiro ctladdr, xstart, e, to); 340990792Sgshapiro if (i == EX_TEMPFAIL) 341090792Sgshapiro to->q_state = QS_RETRY; 341138032Speter } 341238032Speter } 341338032Speter 341490792Sgshapiro /* No recipients in list and no missing responses? */ 341590792Sgshapiro if (tobuf[0] == '\0' 341690792Sgshapiro# if PIPELINING 3417173340Sgshapiro && bitset(MCIF_PIPELINED, mci->mci_flags) 341890792Sgshapiro && mci->mci_nextaddr == NULL 341990792Sgshapiro# endif /* PIPELINING */ 342090792Sgshapiro ) 342138032Speter { 342238032Speter rcode = EX_OK; 342338032Speter e->e_to = NULL; 342438032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 342538032Speter smtprset(m, mci, e); 342638032Speter } 342738032Speter else 342838032Speter { 342938032Speter e->e_to = tobuf + 1; 343090792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 343138032Speter } 343238032Speter } 343364562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 343438032Speter { 343538032Speter /* try next MX site */ 343638032Speter goto tryhost; 343738032Speter } 343838032Speter } 343938032Speter#if NAMED_BIND 344038032Speter if (ConfigLevel < 2) 344138032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 344264562Sgshapiro#endif /* NAMED_BIND */ 344338032Speter 344438032Speter if (tTd(62, 1)) 344538032Speter checkfds("after delivery"); 344638032Speter 344738032Speter /* 344838032Speter ** Do final status disposal. 344938032Speter ** We check for something in tobuf for the SMTP case. 345038032Speter ** If we got a temporary failure, arrange to queue the 345138032Speter ** addressees. 345238032Speter */ 345338032Speter 345438032Speter give_up: 345538032Speter if (bitnset(M_LMTP, m->m_flags)) 345638032Speter { 345738032Speter lmtp_rcode = rcode; 345838032Speter tobuf[0] = '\0'; 345990792Sgshapiro anyok = false; 346090792Sgshapiro strsize = 0; 346138032Speter } 346238032Speter else 346338032Speter anyok = rcode == EX_OK; 346438032Speter 346538032Speter for (to = tochain; to != NULL; to = to->q_tchain) 346638032Speter { 346738032Speter /* see if address already marked */ 346864562Sgshapiro if (!QS_IS_OK(to->q_state)) 346938032Speter continue; 347038032Speter 347138032Speter /* if running LMTP, get the status for each address */ 347238032Speter if (bitnset(M_LMTP, m->m_flags)) 347338032Speter { 347438032Speter if (lmtp_rcode == EX_OK) 347538032Speter rcode = smtpgetstat(m, mci, e); 347638032Speter if (rcode == EX_OK) 347738032Speter { 347890792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 347990792Sgshapiro to->q_paddr, 348090792Sgshapiro tobufsize - strsize); 348190792Sgshapiro SM_ASSERT(strsize < tobufsize); 348290792Sgshapiro anyok = true; 348338032Speter } 348438032Speter else 348538032Speter { 348638032Speter e->e_to = to->q_paddr; 348790792Sgshapiro markfailure(e, to, mci, rcode, true); 348864562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 348990792Sgshapiro ctladdr, xstart, e, to); 349038032Speter e->e_to = tobuf + 1; 349138032Speter continue; 349238032Speter } 349338032Speter } 349438032Speter else 349538032Speter { 349638032Speter /* mark bad addresses */ 349738032Speter if (rcode != EX_OK) 349838032Speter { 349938032Speter if (goodmxfound && rcode == EX_NOHOST) 350038032Speter rcode = EX_TEMPFAIL; 350190792Sgshapiro markfailure(e, to, mci, rcode, true); 350238032Speter continue; 350338032Speter } 350438032Speter } 350538032Speter 350638032Speter /* successful delivery */ 350764562Sgshapiro to->q_state = QS_SENT; 350838032Speter to->q_statdate = curtime(); 350938032Speter e->e_nsent++; 351064562Sgshapiro 351164562Sgshapiro /* 351264562Sgshapiro ** Checkpoint the send list every few addresses 351364562Sgshapiro */ 351464562Sgshapiro 351566494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 351664562Sgshapiro { 351790792Sgshapiro queueup(e, false, false); 351864562Sgshapiro e->e_nsent = 0; 351964562Sgshapiro } 352064562Sgshapiro 352138032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 352238032Speter bitset(QPINGONSUCCESS, to->q_flags)) 352338032Speter { 352438032Speter to->q_flags |= QDELIVERED; 352538032Speter to->q_status = "2.1.5"; 352690792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 352790792Sgshapiro "%s... Successfully delivered\n", 352890792Sgshapiro to->q_paddr); 352938032Speter } 353038032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 353138032Speter bitset(QPRIMARY, to->q_flags) && 353238032Speter !bitset(MCIF_DSN, mci->mci_flags)) 353338032Speter { 353438032Speter to->q_flags |= QRELAYED; 353590792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 353690792Sgshapiro "%s... relayed; expect no further notifications\n", 353790792Sgshapiro to->q_paddr); 353838032Speter } 353990792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 354090792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 354190792Sgshapiro bitset(QPRIMARY, to->q_flags) && 354290792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 354390792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 354490792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 354590792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 354690792Sgshapiro { 354790792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 354890792Sgshapiro to->q_flags |= QBYNRELAY; 354990792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 355090792Sgshapiro "%s... Deliver-by notify: relayed\n", 355190792Sgshapiro to->q_paddr); 355290792Sgshapiro } 355390792Sgshapiro else if (IS_DLVR_TRACE(e) && 355490792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 355590792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 355690792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 355790792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 355890792Sgshapiro bitset(QPRIMARY, to->q_flags)) 355990792Sgshapiro { 356090792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 356190792Sgshapiro to->q_flags |= QBYTRACE; 356290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 356390792Sgshapiro "%s... Deliver-By trace: relayed\n", 356490792Sgshapiro to->q_paddr); 356590792Sgshapiro } 356638032Speter } 356738032Speter 356838032Speter if (bitnset(M_LMTP, m->m_flags)) 356938032Speter { 357038032Speter /* 357138032Speter ** Global information applies to the last recipient only; 357238032Speter ** clear it out to avoid bogus errors. 357338032Speter */ 357438032Speter 357538032Speter rcode = EX_OK; 357638032Speter e->e_statmsg = NULL; 357738032Speter 357838032Speter /* reset the mci state for the next transaction */ 357990792Sgshapiro if (mci != NULL && 358090792Sgshapiro (mci->mci_state == MCIS_MAIL || 358190792Sgshapiro mci->mci_state == MCIS_RCPT || 358290792Sgshapiro mci->mci_state == MCIS_DATA)) 3583125820Sgshapiro { 358438032Speter mci->mci_state = MCIS_OPEN; 3585125820Sgshapiro SmtpPhase = mci->mci_phase = "idle"; 3586125820Sgshapiro sm_setproctitle(true, e, "%s: %s", CurHostName, 3587125820Sgshapiro mci->mci_phase); 3588125820Sgshapiro } 358938032Speter } 359038032Speter 359138032Speter if (tobuf[0] != '\0') 359290792Sgshapiro { 359390792Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 359490792Sgshapiro#if 0 359590792Sgshapiro /* 359690792Sgshapiro ** This code is disabled for now because I am not 359790792Sgshapiro ** sure that copying status from the first recipient 359890792Sgshapiro ** to all non-status'ed recipients is a good idea. 359990792Sgshapiro */ 360090792Sgshapiro 360190792Sgshapiro if (tochain->q_message != NULL && 360290792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 360390792Sgshapiro { 360490792Sgshapiro for (to = tochain->q_tchain; to != NULL; 360590792Sgshapiro to = to->q_tchain) 360690792Sgshapiro { 360790792Sgshapiro /* see if address already marked */ 360890792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 360990792Sgshapiro to->q_message == NULL) 361090792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 361190792Sgshapiro tochain->q_message); 361290792Sgshapiro } 361390792Sgshapiro } 361490792Sgshapiro#endif /* 0 */ 361590792Sgshapiro } 361638032Speter if (anyok) 361790792Sgshapiro markstats(e, tochain, STATS_NORMAL); 361838032Speter mci_store_persistent(mci); 361938032Speter 362090792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 362190792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 362290792Sgshapiro { 362390792Sgshapiro /* try next MX site */ 362490792Sgshapiro goto tryhost; 362590792Sgshapiro } 362690792Sgshapiro 362738032Speter /* now close the connection */ 362838032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 362938032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 363038032Speter smtpquit(m, mci, e); 363138032Speter 363290792Sgshapirocleanup: ; 363390792Sgshapiro } 363490792Sgshapiro SM_FINALLY 363590792Sgshapiro { 363690792Sgshapiro /* 363790792Sgshapiro ** Restore state and return. 363890792Sgshapiro */ 363938032Speter#if XDEBUG 364038032Speter char wbuf[MAXLINE]; 364138032Speter 364238032Speter /* make absolutely certain 0, 1, and 2 are in use */ 3643168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), 364490792Sgshapiro "%s... end of deliver(%s)", 364590792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 364690792Sgshapiro : shortenstring(e->e_to, 364790792Sgshapiro MAXSHORTSTR), 364890792Sgshapiro m->m_name); 364938032Speter checkfd012(wbuf); 365064562Sgshapiro#endif /* XDEBUG */ 365138032Speter 365290792Sgshapiro errno = 0; 365390792Sgshapiro 365490792Sgshapiro /* 365590792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 365690792Sgshapiro ** because it previously pointed to an auto buffer. 365790792Sgshapiro ** We don't do this any more, so this may be unnecessary. 365890792Sgshapiro */ 365990792Sgshapiro 366090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 366190792Sgshapiro e->e_to = NULL; 366290792Sgshapiro } 366390792Sgshapiro SM_END_TRY 366464562Sgshapiro return rcode; 366538032Speter} 366664562Sgshapiro 366790792Sgshapiro/* 366838032Speter** MARKFAILURE -- mark a failure on a specific address. 366938032Speter** 367038032Speter** Parameters: 367138032Speter** e -- the envelope we are sending. 367238032Speter** q -- the address to mark. 367338032Speter** mci -- mailer connection information. 367438032Speter** rcode -- the code signifying the particular failure. 367564562Sgshapiro** ovr -- override an existing code? 367638032Speter** 367738032Speter** Returns: 367838032Speter** none. 367938032Speter** 368038032Speter** Side Effects: 368138032Speter** marks the address (and possibly the envelope) with the 368238032Speter** failure so that an error will be returned or 368338032Speter** the message will be queued, as appropriate. 368438032Speter*/ 368538032Speter 368690792Sgshapirovoid 368764562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 368838032Speter register ENVELOPE *e; 368938032Speter register ADDRESS *q; 369038032Speter register MCI *mci; 369138032Speter int rcode; 369264562Sgshapiro bool ovr; 369338032Speter{ 369490792Sgshapiro int save_errno = errno; 369564562Sgshapiro char *status = NULL; 369664562Sgshapiro char *rstatus = NULL; 369738032Speter 369838032Speter switch (rcode) 369938032Speter { 370038032Speter case EX_OK: 370138032Speter break; 370238032Speter 370338032Speter case EX_TEMPFAIL: 370438032Speter case EX_IOERR: 370538032Speter case EX_OSERR: 370664562Sgshapiro q->q_state = QS_QUEUEUP; 370738032Speter break; 370838032Speter 370938032Speter default: 371064562Sgshapiro q->q_state = QS_BADADDR; 371138032Speter break; 371238032Speter } 371338032Speter 371438032Speter /* find most specific error code possible */ 371538032Speter if (mci != NULL && mci->mci_status != NULL) 371638032Speter { 371790792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 371838032Speter if (mci->mci_rstatus != NULL) 371990792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 372090792Sgshapiro mci->mci_rstatus); 372138032Speter else 372264562Sgshapiro rstatus = NULL; 372338032Speter } 372438032Speter else if (e->e_status != NULL) 372538032Speter { 372664562Sgshapiro status = e->e_status; 372764562Sgshapiro rstatus = NULL; 372838032Speter } 372938032Speter else 373038032Speter { 373138032Speter switch (rcode) 373238032Speter { 373338032Speter case EX_USAGE: 373464562Sgshapiro status = "5.5.4"; 373538032Speter break; 373638032Speter 373738032Speter case EX_DATAERR: 373864562Sgshapiro status = "5.5.2"; 373938032Speter break; 374038032Speter 374138032Speter case EX_NOUSER: 374264562Sgshapiro status = "5.1.1"; 374338032Speter break; 374438032Speter 374538032Speter case EX_NOHOST: 374664562Sgshapiro status = "5.1.2"; 374738032Speter break; 374838032Speter 374938032Speter case EX_NOINPUT: 375038032Speter case EX_CANTCREAT: 375138032Speter case EX_NOPERM: 375264562Sgshapiro status = "5.3.0"; 375338032Speter break; 375438032Speter 375538032Speter case EX_UNAVAILABLE: 375638032Speter case EX_SOFTWARE: 375738032Speter case EX_OSFILE: 375838032Speter case EX_PROTOCOL: 375938032Speter case EX_CONFIG: 376064562Sgshapiro status = "5.5.0"; 376138032Speter break; 376238032Speter 376338032Speter case EX_OSERR: 376438032Speter case EX_IOERR: 376564562Sgshapiro status = "4.5.0"; 376638032Speter break; 376738032Speter 376838032Speter case EX_TEMPFAIL: 376964562Sgshapiro status = "4.2.0"; 377038032Speter break; 377138032Speter } 377238032Speter } 377338032Speter 377464562Sgshapiro /* new status? */ 377564562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 377664562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 377764562Sgshapiro { 377864562Sgshapiro q->q_status = status; 377964562Sgshapiro q->q_rstatus = rstatus; 378064562Sgshapiro } 378138032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 378238032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 378390792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 378438032Speter { 378564562Sgshapiro char buf[16]; 378638032Speter 3787168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%d", rcode); 378890792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 378938032Speter } 379064562Sgshapiro 379164562Sgshapiro q->q_statdate = curtime(); 379264562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 379364562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 379490792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 379590792Sgshapiro 379690792Sgshapiro /* restore errno */ 379790792Sgshapiro errno = save_errno; 379838032Speter} 379990792Sgshapiro/* 380038032Speter** ENDMAILER -- Wait for mailer to terminate. 380138032Speter** 380238032Speter** We should never get fatal errors (e.g., segmentation 380338032Speter** violation), so we report those specially. For other 380438032Speter** errors, we choose a status message (into statmsg), 380538032Speter** and if it represents an error, we print it. 380638032Speter** 380738032Speter** Parameters: 380880785Sgshapiro** mci -- the mailer connection info. 380938032Speter** e -- the current envelope. 381038032Speter** pv -- the parameter vector that invoked the mailer 381138032Speter** (for error messages). 381238032Speter** 381338032Speter** Returns: 381438032Speter** exit code of mailer. 381538032Speter** 381638032Speter** Side Effects: 381738032Speter** none. 381838032Speter*/ 381938032Speter 382064562Sgshapirostatic jmp_buf EndWaitTimeout; 382164562Sgshapiro 382264562Sgshapirostatic void 3823141858Sgshapiroendwaittimeout(ignore) 3824141858Sgshapiro int ignore; 382564562Sgshapiro{ 382677349Sgshapiro /* 382777349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 382877349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 382977349Sgshapiro ** DOING. 383077349Sgshapiro */ 383177349Sgshapiro 383264562Sgshapiro errno = ETIMEDOUT; 383364562Sgshapiro longjmp(EndWaitTimeout, 1); 383464562Sgshapiro} 383564562Sgshapiro 383638032Speterint 383738032Speterendmailer(mci, e, pv) 383838032Speter register MCI *mci; 383938032Speter register ENVELOPE *e; 384038032Speter char **pv; 384138032Speter{ 384238032Speter int st; 384364562Sgshapiro int save_errno = errno; 384464562Sgshapiro char buf[MAXLINE]; 384590792Sgshapiro SM_EVENT *ev = NULL; 384638032Speter 384764562Sgshapiro 384838032Speter mci_unlock_host(mci); 384938032Speter 385077349Sgshapiro /* close output to mailer */ 385177349Sgshapiro if (mci->mci_out != NULL) 3852141858Sgshapiro { 385390792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 3854141858Sgshapiro mci->mci_out = NULL; 3855141858Sgshapiro } 385677349Sgshapiro 385777349Sgshapiro /* copy any remaining input to transcript */ 385877349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 385977349Sgshapiro e->e_xfp != NULL) 386077349Sgshapiro { 3861168515Sgshapiro while (sfgets(buf, sizeof(buf), mci->mci_in, 386290792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 386390792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 386477349Sgshapiro } 386577349Sgshapiro 386664562Sgshapiro#if SASL 386790792Sgshapiro /* close SASL connection */ 386864562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 386964562Sgshapiro { 387064562Sgshapiro sasl_dispose(&mci->mci_conn); 387164562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 387264562Sgshapiro } 387364562Sgshapiro#endif /* SASL */ 387464562Sgshapiro 387564562Sgshapiro#if STARTTLS 387664562Sgshapiro /* shutdown TLS */ 387764562Sgshapiro (void) endtlsclt(mci); 387864562Sgshapiro#endif /* STARTTLS */ 387964562Sgshapiro 388064562Sgshapiro /* now close the input */ 388138032Speter if (mci->mci_in != NULL) 3882141858Sgshapiro { 388390792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 3884141858Sgshapiro mci->mci_in = NULL; 3885141858Sgshapiro } 388638032Speter mci->mci_state = MCIS_CLOSED; 388738032Speter 388864562Sgshapiro errno = save_errno; 388964562Sgshapiro 389038032Speter /* in the IPC case there is nothing to wait for */ 389138032Speter if (mci->mci_pid == 0) 389264562Sgshapiro return EX_OK; 389338032Speter 389464562Sgshapiro /* put a timeout around the wait */ 389564562Sgshapiro if (mci->mci_mailer->m_wait > 0) 389664562Sgshapiro { 389764562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 389890792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 389990792Sgshapiro endwaittimeout, 0); 390064562Sgshapiro else 390164562Sgshapiro { 390266494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 390364562Sgshapiro mci->mci_mailer->m_name, 390466494Sgshapiro (long) mci->mci_mailer->m_wait); 390564562Sgshapiro return EX_TEMPFAIL; 390664562Sgshapiro } 390764562Sgshapiro } 390838032Speter 390964562Sgshapiro /* wait for the mailer process, collect status */ 391038032Speter st = waitfor(mci->mci_pid); 391164562Sgshapiro save_errno = errno; 391264562Sgshapiro if (ev != NULL) 391390792Sgshapiro sm_clrevent(ev); 391464562Sgshapiro errno = save_errno; 391564562Sgshapiro 391638032Speter if (st == -1) 391738032Speter { 391838032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 391964562Sgshapiro return EX_SOFTWARE; 392038032Speter } 392138032Speter 392238032Speter if (WIFEXITED(st)) 392338032Speter { 392438032Speter /* normal death -- return status */ 392538032Speter return (WEXITSTATUS(st)); 392638032Speter } 392738032Speter 392838032Speter /* it died a horrid death */ 392964562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 393064562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 393164562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 393264562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 393338032Speter 393438032Speter /* log the arguments */ 393538032Speter if (pv != NULL && e->e_xfp != NULL) 393638032Speter { 393738032Speter register char **av; 393838032Speter 393990792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 394038032Speter for (av = pv; *av != NULL; av++) 394190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 394290792Sgshapiro *av); 394390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 394438032Speter } 394538032Speter 394638032Speter ExitStat = EX_TEMPFAIL; 394764562Sgshapiro return EX_TEMPFAIL; 394838032Speter} 394990792Sgshapiro/* 395038032Speter** GIVERESPONSE -- Interpret an error response from a mailer 395138032Speter** 395238032Speter** Parameters: 395364562Sgshapiro** status -- the status code from the mailer (high byte 395438032Speter** only; core dumps must have been taken care of 395538032Speter** already). 395664562Sgshapiro** dsn -- the DSN associated with the address, if any. 395738032Speter** m -- the mailer info for this mailer. 395838032Speter** mci -- the mailer connection info -- can be NULL if the 395938032Speter** response is given before the connection is made. 396038032Speter** ctladdr -- the controlling address for the recipient 396138032Speter** address(es). 396238032Speter** xstart -- the transaction start time, for computing 396338032Speter** transaction delays. 396438032Speter** e -- the current envelope. 396590792Sgshapiro** to -- the current recipient (NULL if none). 396638032Speter** 396738032Speter** Returns: 396838032Speter** none. 396938032Speter** 397038032Speter** Side Effects: 397138032Speter** Errors may be incremented. 397238032Speter** ExitStat may be set. 397338032Speter*/ 397438032Speter 397538032Spetervoid 397690792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 397764562Sgshapiro int status; 397864562Sgshapiro char *dsn; 397938032Speter register MAILER *m; 398038032Speter register MCI *mci; 398138032Speter ADDRESS *ctladdr; 398238032Speter time_t xstart; 398338032Speter ENVELOPE *e; 398490792Sgshapiro ADDRESS *to; 398538032Speter{ 398638032Speter register const char *statmsg; 398764562Sgshapiro int errnum = errno; 398864562Sgshapiro int off = 4; 398990792Sgshapiro bool usestat = false; 399064562Sgshapiro char dsnbuf[ENHSCLEN]; 399138032Speter char buf[MAXLINE]; 399290792Sgshapiro char *exmsg; 399338032Speter 399438032Speter if (e == NULL) 3995159609Sgshapiro { 399638032Speter syserr("giveresponse: null envelope"); 3997159609Sgshapiro /* NOTREACHED */ 3998159609Sgshapiro SM_ASSERT(0); 3999159609Sgshapiro } 400038032Speter 400138032Speter /* 400238032Speter ** Compute status message from code. 400338032Speter */ 400438032Speter 400590792Sgshapiro exmsg = sm_sysexmsg(status); 400664562Sgshapiro if (status == 0) 400738032Speter { 400864562Sgshapiro statmsg = "250 2.0.0 Sent"; 400938032Speter if (e->e_statmsg != NULL) 401038032Speter { 4011168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", 401290792Sgshapiro statmsg, 401390792Sgshapiro shortenstring(e->e_statmsg, 403)); 401438032Speter statmsg = buf; 401538032Speter } 401638032Speter } 401790792Sgshapiro else if (exmsg == NULL) 401838032Speter { 4019168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 402090792Sgshapiro "554 5.3.0 unknown mailer error %d", 402190792Sgshapiro status); 402264562Sgshapiro status = EX_UNAVAILABLE; 402338032Speter statmsg = buf; 402490792Sgshapiro usestat = true; 402538032Speter } 402664562Sgshapiro else if (status == EX_TEMPFAIL) 402738032Speter { 402838032Speter char *bp = buf; 402938032Speter 403090792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 403138032Speter bp += strlen(bp); 403238032Speter#if NAMED_BIND 403338032Speter if (h_errno == TRY_AGAIN) 403490792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 403538032Speter else 403664562Sgshapiro#endif /* NAMED_BIND */ 403738032Speter { 403864562Sgshapiro if (errnum != 0) 403990792Sgshapiro statmsg = sm_errstring(errnum); 404038032Speter else 404138032Speter statmsg = SmtpError; 404238032Speter } 404338032Speter if (statmsg != NULL && statmsg[0] != '\0') 404464562Sgshapiro { 404564562Sgshapiro switch (errnum) 404664562Sgshapiro { 404764562Sgshapiro#ifdef ENETDOWN 404864562Sgshapiro case ENETDOWN: /* Network is down */ 404964562Sgshapiro#endif /* ENETDOWN */ 405064562Sgshapiro#ifdef ENETUNREACH 405164562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 405264562Sgshapiro#endif /* ENETUNREACH */ 405364562Sgshapiro#ifdef ENETRESET 405464562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 405564562Sgshapiro#endif /* ENETRESET */ 405664562Sgshapiro#ifdef ECONNABORTED 405764562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 405864562Sgshapiro#endif /* ECONNABORTED */ 405964562Sgshapiro#ifdef EHOSTDOWN 406064562Sgshapiro case EHOSTDOWN: /* Host is down */ 406164562Sgshapiro#endif /* EHOSTDOWN */ 406264562Sgshapiro#ifdef EHOSTUNREACH 406364562Sgshapiro case EHOSTUNREACH: /* No route to host */ 406464562Sgshapiro#endif /* EHOSTUNREACH */ 4065141858Sgshapiro if (mci != NULL && mci->mci_host != NULL) 406664562Sgshapiro { 406790792Sgshapiro (void) sm_strlcpyn(bp, 406890792Sgshapiro SPACELEFT(buf, bp), 406990792Sgshapiro 2, ": ", 407090792Sgshapiro mci->mci_host); 407164562Sgshapiro bp += strlen(bp); 407264562Sgshapiro } 407364562Sgshapiro break; 407464562Sgshapiro } 407590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 407690792Sgshapiro statmsg); 407790792Sgshapiro usestat = true; 407864562Sgshapiro } 407938032Speter statmsg = buf; 408038032Speter } 408138032Speter#if NAMED_BIND 408264562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 408338032Speter { 408490792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 4085168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1, 408690792Sgshapiro statmsg); 408738032Speter statmsg = buf; 408890792Sgshapiro usestat = true; 408938032Speter } 409064562Sgshapiro#endif /* NAMED_BIND */ 409138032Speter else 409238032Speter { 409390792Sgshapiro statmsg = exmsg; 409464562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 409538032Speter { 4096168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg, 409790792Sgshapiro sm_errstring(errnum)); 409838032Speter statmsg = buf; 409990792Sgshapiro usestat = true; 410038032Speter } 410194334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 410294334Sgshapiro { 4103168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg, 410494334Sgshapiro shortenstring(e->e_statmsg, 403)); 410594334Sgshapiro statmsg = buf; 410694334Sgshapiro usestat = true; 410794334Sgshapiro } 410838032Speter } 410938032Speter 411038032Speter /* 411138032Speter ** Print the message as appropriate 411238032Speter */ 411338032Speter 411464562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 411538032Speter { 411638032Speter extern char MsgBuf[]; 411738032Speter 411864562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 411964562Sgshapiro { 412064562Sgshapiro if (dsn == NULL) 412164562Sgshapiro { 4122168515Sgshapiro (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 412390792Sgshapiro "%.*s", off, statmsg + 4); 412464562Sgshapiro dsn = dsnbuf; 412564562Sgshapiro } 412664562Sgshapiro off += 5; 412764562Sgshapiro } 412864562Sgshapiro else 412964562Sgshapiro { 413064562Sgshapiro off = 4; 413164562Sgshapiro } 413264562Sgshapiro message("%s", statmsg + off); 413364562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 413490792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 413590792Sgshapiro &MsgBuf[4]); 413638032Speter } 413738032Speter else 413838032Speter { 413964562Sgshapiro char mbuf[ENHSCLEN + 4]; 414038032Speter 414138032Speter Errors++; 414264562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 4143168515Sgshapiro off < sizeof(mbuf) - 4) 414464562Sgshapiro { 414564562Sgshapiro if (dsn == NULL) 414664562Sgshapiro { 4147168515Sgshapiro (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 414890792Sgshapiro "%.*s", off, statmsg + 4); 414964562Sgshapiro dsn = dsnbuf; 415064562Sgshapiro } 415164562Sgshapiro off += 5; 415290792Sgshapiro 415390792Sgshapiro /* copy only part of statmsg to mbuf */ 415490792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 4155168515Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof(mbuf)); 415664562Sgshapiro } 415764562Sgshapiro else 415864562Sgshapiro { 415964562Sgshapiro dsnbuf[0] = '\0'; 4160168515Sgshapiro (void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s", 416190792Sgshapiro statmsg); 416264562Sgshapiro off = 4; 416364562Sgshapiro } 416464562Sgshapiro usrerr(mbuf, &statmsg[off]); 416538032Speter } 416638032Speter 416738032Speter /* 416838032Speter ** Final cleanup. 416938032Speter ** Log a record of the transaction. Compute the new 417038032Speter ** ExitStat -- if we already had an error, stick with 417138032Speter ** that. 417238032Speter */ 417338032Speter 417438032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 417564562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 417664562Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 417738032Speter 417838032Speter if (tTd(11, 2)) 417990792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 418090792Sgshapiro status, 418190792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 418290792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 418390792Sgshapiro errnum); 418438032Speter 418564562Sgshapiro if (status != EX_TEMPFAIL) 418664562Sgshapiro setstat(status); 418764562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 418890792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 418990792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 419038032Speter { 419190792Sgshapiro if (!usestat && e->e_message != NULL) 419290792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 419390792Sgshapiro e->e_message); 419490792Sgshapiro else 419590792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 419690792Sgshapiro statmsg + off); 419738032Speter } 419838032Speter errno = 0; 419973188Sgshapiro SM_SET_H_ERRNO(0); 420038032Speter} 420190792Sgshapiro/* 420238032Speter** LOGDELIVERY -- log the delivery in the system log 420338032Speter** 420438032Speter** Care is taken to avoid logging lines that are too long, because 420538032Speter** some versions of syslog have an unfortunate proclivity for core 420638032Speter** dumping. This is a hack, to be sure, that is at best empirical. 420738032Speter** 420838032Speter** Parameters: 420938032Speter** m -- the mailer info. Can be NULL for initial queue. 421038032Speter** mci -- the mailer connection info -- can be NULL if the 421164562Sgshapiro** log is occurring when no connection is active. 421264562Sgshapiro** dsn -- the DSN attached to the status. 421364562Sgshapiro** status -- the message to print for the status. 421438032Speter** ctladdr -- the controlling address for the to list. 421538032Speter** xstart -- the transaction start time, used for 421638032Speter** computing transaction delay. 421738032Speter** e -- the current envelope. 421838032Speter** 421938032Speter** Returns: 422038032Speter** none 422138032Speter** 422238032Speter** Side Effects: 422338032Speter** none 422438032Speter*/ 422538032Speter 422638032Spetervoid 422764562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e) 422838032Speter MAILER *m; 422938032Speter register MCI *mci; 423064562Sgshapiro char *dsn; 423164562Sgshapiro const char *status; 423238032Speter ADDRESS *ctladdr; 423338032Speter time_t xstart; 423438032Speter register ENVELOPE *e; 423538032Speter{ 423638032Speter register char *bp; 423738032Speter register char *p; 423838032Speter int l; 423990792Sgshapiro time_t now = curtime(); 424038032Speter char buf[1024]; 424138032Speter 424264562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 424338032Speter /* ctladdr: max 106 bytes */ 424438032Speter bp = buf; 424538032Speter if (ctladdr != NULL) 424638032Speter { 424790792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 424890792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 424938032Speter bp += strlen(bp); 425038032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 425138032Speter { 425290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 425390792Sgshapiro (int) ctladdr->q_uid, 425490792Sgshapiro (int) ctladdr->q_gid); 425538032Speter bp += strlen(bp); 425638032Speter } 425738032Speter } 425838032Speter 425938032Speter /* delay & xdelay: max 41 bytes */ 426090792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 426190792Sgshapiro pintvl(now - e->e_ctime, true)); 426238032Speter bp += strlen(bp); 426338032Speter 426438032Speter if (xstart != (time_t) 0) 426538032Speter { 426690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 426790792Sgshapiro pintvl(now - xstart, true)); 426838032Speter bp += strlen(bp); 426938032Speter } 427038032Speter 427138032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 427238032Speter if (m != NULL) 427338032Speter { 427490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 427590792Sgshapiro m->m_name); 427638032Speter bp += strlen(bp); 427738032Speter } 427838032Speter 427964562Sgshapiro /* pri: changes with each delivery attempt */ 428090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 4281244928Sgshapiro PRT_NONNEGL(e->e_msgpriority)); 428264562Sgshapiro bp += strlen(bp); 428364562Sgshapiro 428438032Speter /* relay: max 66 bytes for IPv4 addresses */ 428538032Speter if (mci != NULL && mci->mci_host != NULL) 428638032Speter { 428738032Speter extern SOCKADDR CurHostAddr; 428838032Speter 428990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 429090792Sgshapiro shortenstring(mci->mci_host, 40)); 429138032Speter bp += strlen(bp); 429238032Speter 429338032Speter if (CurHostAddr.sa.sa_family != 0) 429438032Speter { 429590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 429690792Sgshapiro anynet_ntoa(&CurHostAddr)); 429738032Speter } 429838032Speter } 429990792Sgshapiro else if (strcmp(status, "quarantined") == 0) 430090792Sgshapiro { 430190792Sgshapiro if (e->e_quarmsg != NULL) 430290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 430390792Sgshapiro ", quarantine=%s", 430490792Sgshapiro shortenstring(e->e_quarmsg, 40)); 430590792Sgshapiro } 430664562Sgshapiro else if (strcmp(status, "queued") != 0) 430738032Speter { 430838032Speter p = macvalue('h', e); 430938032Speter if (p != NULL && p[0] != '\0') 431038032Speter { 431190792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 431290792Sgshapiro ", relay=%s", shortenstring(p, 40)); 431338032Speter } 431438032Speter } 431538032Speter bp += strlen(bp); 431638032Speter 431764562Sgshapiro /* dsn */ 431864562Sgshapiro if (dsn != NULL && *dsn != '\0') 431964562Sgshapiro { 432090792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 432190792Sgshapiro shortenstring(dsn, ENHSCLEN)); 432264562Sgshapiro bp += strlen(bp); 432364562Sgshapiro } 432438032Speter 4325147078Sgshapiro#if _FFR_LOG_NTRIES 4326147078Sgshapiro /* ntries */ 4327147078Sgshapiro if (e->e_ntries >= 0) 4328147078Sgshapiro { 4329147078Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4330147078Sgshapiro ", ntries=%d", e->e_ntries + 1); 4331147078Sgshapiro bp += strlen(bp); 4332147078Sgshapiro } 4333147078Sgshapiro#endif /* _FFR_LOG_NTRIES */ 4334147078Sgshapiro 433564562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 433664562Sgshapiro# if (STATLEN) < 63 433764562Sgshapiro# undef STATLEN 433864562Sgshapiro# define STATLEN 63 433964562Sgshapiro# endif /* (STATLEN) < 63 */ 434064562Sgshapiro# if (STATLEN) > 203 434164562Sgshapiro# undef STATLEN 434264562Sgshapiro# define STATLEN 203 434364562Sgshapiro# endif /* (STATLEN) > 203 */ 434464562Sgshapiro 434538032Speter /* stat: max 210 bytes */ 4346168515Sgshapiro if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20))) 434738032Speter { 434838032Speter /* desperation move -- truncate data */ 4349168515Sgshapiro bp = buf + sizeof(buf) - ((STATLEN) + 17); 435090792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 435138032Speter bp += 3; 435238032Speter } 435338032Speter 435490792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 435538032Speter bp += strlen(bp); 435638032Speter 435790792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 435890792Sgshapiro SPACELEFT(buf, bp)); 435938032Speter 436038032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 436138032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 436290792Sgshapiro if (l < 0) 436390792Sgshapiro l = 0; 436464562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 436590792Sgshapiro while (strlen(p) >= l) 436638032Speter { 436764562Sgshapiro register char *q; 436838032Speter 436964562Sgshapiro for (q = p + l; q > p; q--) 437064562Sgshapiro { 437164562Sgshapiro if (*q == ',') 437264562Sgshapiro break; 437364562Sgshapiro } 437464562Sgshapiro if (p == q) 437564562Sgshapiro break; 437690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 437766494Sgshapiro (int) (++q - p), p, buf); 437838032Speter p = q; 437938032Speter } 438064562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 438138032Speter 438264562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 438338032Speter 438438032Speter l = SYSLOG_BUFSIZE - 85; 438590792Sgshapiro if (l < 0) 438690792Sgshapiro l = 0; 438764562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 438890792Sgshapiro while (strlen(p) >= l) 438938032Speter { 439064562Sgshapiro register char *q; 439138032Speter 439264562Sgshapiro for (q = p + l; q > p; q--) 439364562Sgshapiro { 439464562Sgshapiro if (*q == ',') 439564562Sgshapiro break; 439664562Sgshapiro } 439764562Sgshapiro if (p == q) 439864562Sgshapiro break; 439964562Sgshapiro 440090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 440166494Sgshapiro (int) (++q - p), p); 440238032Speter p = q; 440338032Speter } 440464562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 440538032Speter 440638032Speter if (ctladdr != NULL) 440738032Speter { 440838032Speter bp = buf; 440990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 441090792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 441138032Speter bp += strlen(bp); 441238032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 441338032Speter { 441490792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 441590792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 441638032Speter bp += strlen(bp); 441738032Speter } 441838032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 441938032Speter } 442038032Speter bp = buf; 442190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 442290792Sgshapiro pintvl(now - e->e_ctime, true)); 442338032Speter bp += strlen(bp); 442438032Speter if (xstart != (time_t) 0) 442538032Speter { 442690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 442790792Sgshapiro pintvl(now - xstart, true)); 442838032Speter bp += strlen(bp); 442938032Speter } 443038032Speter 443138032Speter if (m != NULL) 443238032Speter { 443390792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 443490792Sgshapiro m->m_name); 443538032Speter bp += strlen(bp); 443638032Speter } 443738032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 443838032Speter 443938032Speter buf[0] = '\0'; 444038032Speter bp = buf; 444138032Speter if (mci != NULL && mci->mci_host != NULL) 444238032Speter { 444338032Speter extern SOCKADDR CurHostAddr; 444438032Speter 444590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 444690792Sgshapiro mci->mci_host); 444738032Speter bp += strlen(bp); 444838032Speter 444938032Speter if (CurHostAddr.sa.sa_family != 0) 445090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 445190792Sgshapiro " [%.100s]", 445290792Sgshapiro anynet_ntoa(&CurHostAddr)); 445338032Speter } 445490792Sgshapiro else if (strcmp(status, "quarantined") == 0) 445590792Sgshapiro { 445690792Sgshapiro if (e->e_quarmsg != NULL) 445790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 445890792Sgshapiro ", quarantine=%.100s", 445990792Sgshapiro e->e_quarmsg); 446090792Sgshapiro } 446164562Sgshapiro else if (strcmp(status, "queued") != 0) 446238032Speter { 446338032Speter p = macvalue('h', e); 446438032Speter if (p != NULL && p[0] != '\0') 4465168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p); 446638032Speter } 446738032Speter if (buf[0] != '\0') 446838032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 446938032Speter 447064562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 447164562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 447238032Speter} 447390792Sgshapiro/* 447438032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 447538032Speter** 447638032Speter** This can be made an arbitrary message separator by changing $l 447738032Speter** 447838032Speter** One of the ugliest hacks seen by human eyes is contained herein: 447938032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 448038032Speter** does a well-meaning programmer such as myself have to deal with 448138032Speter** this kind of antique garbage???? 448238032Speter** 448338032Speter** Parameters: 448438032Speter** mci -- the connection information. 448538032Speter** e -- the envelope. 448638032Speter** 448738032Speter** Returns: 4488157001Sgshapiro** true iff line was written successfully 448938032Speter** 449038032Speter** Side Effects: 449138032Speter** outputs some text to fp. 449238032Speter*/ 449338032Speter 4494157001Sgshapirobool 449538032Speterputfromline(mci, e) 449638032Speter register MCI *mci; 449738032Speter ENVELOPE *e; 449838032Speter{ 449938032Speter char *template = UnixFromLine; 450038032Speter char buf[MAXLINE]; 450138032Speter char xbuf[MAXLINE]; 450238032Speter 450338032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 4504157001Sgshapiro return true; 450538032Speter 450638032Speter mci->mci_flags |= MCIF_INHEADER; 450738032Speter 450838032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 450938032Speter { 451038032Speter char *bang; 451138032Speter 4512168515Sgshapiro expand("\201g", buf, sizeof(buf), e); 451338032Speter bang = strchr(buf, '!'); 451438032Speter if (bang == NULL) 451538032Speter { 451638032Speter char *at; 451738032Speter char hname[MAXNAME]; 451838032Speter 451964562Sgshapiro /* 452042575Speter ** If we can construct a UUCP path, do so 452138032Speter */ 452238032Speter 452338032Speter at = strrchr(buf, '@'); 452438032Speter if (at == NULL) 452538032Speter { 4526168515Sgshapiro expand("\201k", hname, sizeof(hname), e); 452738032Speter at = hname; 452838032Speter } 452938032Speter else 453038032Speter *at++ = '\0'; 4531168515Sgshapiro (void) sm_snprintf(xbuf, sizeof(xbuf), 453290792Sgshapiro "From %.800s \201d remote from %.100s\n", 453390792Sgshapiro buf, at); 453438032Speter } 453538032Speter else 453638032Speter { 453738032Speter *bang++ = '\0'; 4538168515Sgshapiro (void) sm_snprintf(xbuf, sizeof(xbuf), 453990792Sgshapiro "From %.800s \201d remote from %.100s\n", 454090792Sgshapiro bang, buf); 454138032Speter template = xbuf; 454238032Speter } 454338032Speter } 4544168515Sgshapiro expand(template, buf, sizeof(buf), e); 4545157001Sgshapiro return putxline(buf, strlen(buf), mci, PXLF_HEADER); 454638032Speter} 4547157001Sgshapiro 454890792Sgshapiro/* 454938032Speter** PUTBODY -- put the body of a message. 455038032Speter** 455138032Speter** Parameters: 455238032Speter** mci -- the connection information. 455338032Speter** e -- the envelope to put out. 455438032Speter** separator -- if non-NULL, a message separator that must 455538032Speter** not be permitted in the resulting message. 455638032Speter** 455738032Speter** Returns: 4558157001Sgshapiro** true iff message was written successfully 455938032Speter** 456038032Speter** Side Effects: 456138032Speter** The message is written onto fp. 456238032Speter*/ 456338032Speter 456438032Speter/* values for output state variable */ 4565157001Sgshapiro#define OSTATE_HEAD 0 /* at beginning of line */ 4566157001Sgshapiro#define OSTATE_CR 1 /* read a carriage return */ 4567157001Sgshapiro#define OSTATE_INLINE 2 /* putting rest of line */ 456838032Speter 4569157001Sgshapirobool 457038032Speterputbody(mci, e, separator) 457138032Speter register MCI *mci; 457238032Speter register ENVELOPE *e; 457338032Speter char *separator; 457438032Speter{ 457590792Sgshapiro bool dead = false; 4576157001Sgshapiro bool ioerr = false; 4577157001Sgshapiro int save_errno; 457838032Speter char buf[MAXLINE]; 457990792Sgshapiro#if MIME8TO7 458042575Speter char *boundaries[MAXMIMENESTING + 1]; 458190792Sgshapiro#endif /* MIME8TO7 */ 458238032Speter 458338032Speter /* 458438032Speter ** Output the body of the message 458538032Speter */ 458638032Speter 458738032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 458838032Speter { 458990792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 459038032Speter 459190792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4592120256Sgshapiro SM_IO_RDONLY_B, NULL); 459338032Speter if (e->e_dfp == NULL) 459464562Sgshapiro { 459564562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 459664562Sgshapiro 459764562Sgshapiro if (errno == ENOENT) 459864562Sgshapiro msg++; 459964562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 460064562Sgshapiro } 460190792Sgshapiro 460238032Speter } 460338032Speter if (e->e_dfp == NULL) 460438032Speter { 460538032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 460638032Speter { 4607157001Sgshapiro if (!putline("", mci)) 4608157001Sgshapiro goto writeerr; 460938032Speter mci->mci_flags &= ~MCIF_INHEADER; 461038032Speter } 4611157001Sgshapiro if (!putline("<<< No Message Collected >>>", mci)) 4612157001Sgshapiro goto writeerr; 461338032Speter goto endofmessage; 461438032Speter } 461564562Sgshapiro 461638032Speter if (e->e_dfino == (ino_t) 0) 461738032Speter { 461838032Speter struct stat stbuf; 461938032Speter 462090792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 462190792Sgshapiro < 0) 462238032Speter e->e_dfino = -1; 462338032Speter else 462438032Speter { 462538032Speter e->e_dfdev = stbuf.st_dev; 462638032Speter e->e_dfino = stbuf.st_ino; 462738032Speter } 462838032Speter } 462938032Speter 463090792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 463164562Sgshapiro (void) bfrewind(e->e_dfp); 463264562Sgshapiro 4633157001Sgshapiro /* simulate an I/O timeout when used as source */ 4634157001Sgshapiro if (tTd(84, 101)) 4635157001Sgshapiro sleep(319); 4636157001Sgshapiro 463738032Speter#if MIME8TO7 463838032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 463938032Speter { 464038032Speter /* 464138032Speter ** Do 8 to 7 bit MIME conversion. 464238032Speter */ 464338032Speter 464438032Speter /* make sure it looks like a MIME message */ 4645157001Sgshapiro if (hvalue("MIME-Version", e->e_header) == NULL && 4646157001Sgshapiro !putline("MIME-Version: 1.0", mci)) 4647157001Sgshapiro goto writeerr; 464838032Speter 464938032Speter if (hvalue("Content-Type", e->e_header) == NULL) 465038032Speter { 4651168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 465290792Sgshapiro "Content-Type: text/plain; charset=%s", 465390792Sgshapiro defcharset(e)); 4654157001Sgshapiro if (!putline(buf, mci)) 4655157001Sgshapiro goto writeerr; 465638032Speter } 465738032Speter 465838032Speter /* now do the hard work */ 465938032Speter boundaries[0] = NULL; 466038032Speter mci->mci_flags |= MCIF_INHEADER; 4661159609Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) == 4662157001Sgshapiro SM_IO_EOF) 4663157001Sgshapiro goto writeerr; 466438032Speter } 466538032Speter# if MIME7TO8 466638032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 466738032Speter { 4668157001Sgshapiro if (!mime7to8(mci, e->e_header, e)) 4669157001Sgshapiro goto writeerr; 467038032Speter } 467164562Sgshapiro# endif /* MIME7TO8 */ 467242575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 467342575Speter { 467464562Sgshapiro bool oldsuprerrs = SuprErrs; 467564562Sgshapiro 467642575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 467742575Speter boundaries[0] = NULL; 467842575Speter mci->mci_flags |= MCIF_INHEADER; 467964562Sgshapiro 468064562Sgshapiro /* 468164562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 468264562Sgshapiro ** and don't want to generate a new bounce message whose 468364562Sgshapiro ** body propagates the broken MIME. We can't just not call 468464562Sgshapiro ** mime8to7() as is done above since we need the security 468564562Sgshapiro ** checks. The best we can do is suppress the errors. 468664562Sgshapiro */ 468764562Sgshapiro 468864562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 468990792Sgshapiro SuprErrs = true; 469064562Sgshapiro 4691157001Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, 4692159609Sgshapiro M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF) 4693157001Sgshapiro goto writeerr; 469464562Sgshapiro 469564562Sgshapiro /* restore SuprErrs */ 469664562Sgshapiro SuprErrs = oldsuprerrs; 469742575Speter } 469838032Speter else 469964562Sgshapiro#endif /* MIME8TO7 */ 470038032Speter { 470138032Speter int ostate; 470238032Speter register char *bp; 470338032Speter register char *pbp; 470438032Speter register int c; 470538032Speter register char *xp; 470638032Speter int padc; 470738032Speter char *buflim; 470838032Speter int pos = 0; 470964562Sgshapiro char peekbuf[12]; 471038032Speter 471138032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 471238032Speter { 4713157001Sgshapiro if (!putline("", mci)) 4714157001Sgshapiro goto writeerr; 471538032Speter mci->mci_flags &= ~MCIF_INHEADER; 471638032Speter } 471738032Speter 471838032Speter /* determine end of buffer; allow for short mailer lines */ 4719168515Sgshapiro buflim = &buf[sizeof(buf) - 1]; 472038032Speter if (mci->mci_mailer->m_linelimit > 0 && 4721168515Sgshapiro mci->mci_mailer->m_linelimit < sizeof(buf) - 1) 472238032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 472338032Speter 472438032Speter /* copy temp file to output with mapping */ 4725157001Sgshapiro ostate = OSTATE_HEAD; 472638032Speter bp = buf; 472738032Speter pbp = peekbuf; 472890792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 472938032Speter { 473038032Speter if (pbp > peekbuf) 473138032Speter c = *--pbp; 473290792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 473390792Sgshapiro == SM_IO_EOF) 473438032Speter break; 473538032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 473638032Speter c &= 0x7f; 473738032Speter switch (ostate) 473838032Speter { 4739157001Sgshapiro case OSTATE_HEAD: 474038032Speter if (c == '\0' && 474190792Sgshapiro bitnset(M_NONULLS, 474290792Sgshapiro mci->mci_mailer->m_flags)) 474338032Speter break; 474438032Speter if (c != '\r' && c != '\n' && bp < buflim) 474538032Speter { 474638032Speter *bp++ = c; 474738032Speter break; 474838032Speter } 474938032Speter 475038032Speter /* check beginning of line for special cases */ 475138032Speter *bp = '\0'; 475238032Speter pos = 0; 475390792Sgshapiro padc = SM_IO_EOF; 475438032Speter if (buf[0] == 'F' && 475590792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 475690792Sgshapiro && strncmp(buf, "From ", 5) == 0) 475738032Speter { 475838032Speter padc = '>'; 475938032Speter } 476038032Speter if (buf[0] == '-' && buf[1] == '-' && 476138032Speter separator != NULL) 476238032Speter { 476338032Speter /* possible separator */ 476438032Speter int sl = strlen(separator); 476538032Speter 476690792Sgshapiro if (strncmp(&buf[2], separator, sl) 476790792Sgshapiro == 0) 476838032Speter padc = ' '; 476938032Speter } 477038032Speter if (buf[0] == '.' && 477138032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 477238032Speter { 477338032Speter padc = '.'; 477438032Speter } 477538032Speter 477638032Speter /* now copy out saved line */ 477738032Speter if (TrafficLogFile != NULL) 477838032Speter { 477990792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 478090792Sgshapiro SM_TIME_DEFAULT, 478190792Sgshapiro "%05d >>> ", 478290792Sgshapiro (int) CurrentPid); 478390792Sgshapiro if (padc != SM_IO_EOF) 478490792Sgshapiro (void) sm_io_putc(TrafficLogFile, 478590792Sgshapiro SM_TIME_DEFAULT, 478690792Sgshapiro padc); 478738032Speter for (xp = buf; xp < bp; xp++) 478890792Sgshapiro (void) sm_io_putc(TrafficLogFile, 478990792Sgshapiro SM_TIME_DEFAULT, 479090792Sgshapiro (unsigned char) *xp); 479138032Speter if (c == '\n') 479290792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 479390792Sgshapiro SM_TIME_DEFAULT, 479490792Sgshapiro mci->mci_mailer->m_eol); 479538032Speter } 479690792Sgshapiro if (padc != SM_IO_EOF) 479738032Speter { 479890792Sgshapiro if (sm_io_putc(mci->mci_out, 479990792Sgshapiro SM_TIME_DEFAULT, padc) 480090792Sgshapiro == SM_IO_EOF) 480164562Sgshapiro { 480290792Sgshapiro dead = true; 480364562Sgshapiro continue; 480464562Sgshapiro } 480538032Speter pos++; 480638032Speter } 480738032Speter for (xp = buf; xp < bp; xp++) 480838032Speter { 480990792Sgshapiro if (sm_io_putc(mci->mci_out, 481090792Sgshapiro SM_TIME_DEFAULT, 481190792Sgshapiro (unsigned char) *xp) 481290792Sgshapiro == SM_IO_EOF) 481364562Sgshapiro { 481490792Sgshapiro dead = true; 481564562Sgshapiro break; 481664562Sgshapiro } 481738032Speter } 481864562Sgshapiro if (dead) 481964562Sgshapiro continue; 482038032Speter if (c == '\n') 482138032Speter { 482290792Sgshapiro if (sm_io_fputs(mci->mci_out, 482390792Sgshapiro SM_TIME_DEFAULT, 482490792Sgshapiro mci->mci_mailer->m_eol) 482590792Sgshapiro == SM_IO_EOF) 482664562Sgshapiro break; 482738032Speter pos = 0; 482838032Speter } 482938032Speter else 483038032Speter { 483138032Speter pos += bp - buf; 483238032Speter if (c != '\r') 4833112810Sgshapiro { 4834112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4835112810Sgshapiro sizeof(peekbuf)); 483638032Speter *pbp++ = c; 4837112810Sgshapiro } 483838032Speter } 483964562Sgshapiro 484038032Speter bp = buf; 484138032Speter 484238032Speter /* determine next state */ 484338032Speter if (c == '\n') 4844157001Sgshapiro ostate = OSTATE_HEAD; 484538032Speter else if (c == '\r') 4846157001Sgshapiro ostate = OSTATE_CR; 484738032Speter else 4848157001Sgshapiro ostate = OSTATE_INLINE; 484938032Speter continue; 485038032Speter 4851157001Sgshapiro case OSTATE_CR: 485238032Speter if (c == '\n') 485338032Speter { 485438032Speter /* got CRLF */ 485590792Sgshapiro if (sm_io_fputs(mci->mci_out, 485690792Sgshapiro SM_TIME_DEFAULT, 485790792Sgshapiro mci->mci_mailer->m_eol) 485890792Sgshapiro == SM_IO_EOF) 485964562Sgshapiro continue; 486064562Sgshapiro 486138032Speter if (TrafficLogFile != NULL) 486238032Speter { 486390792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 486490792Sgshapiro SM_TIME_DEFAULT, 486590792Sgshapiro mci->mci_mailer->m_eol); 486638032Speter } 4867168515Sgshapiro pos = 0; 4868157001Sgshapiro ostate = OSTATE_HEAD; 486938032Speter continue; 487038032Speter } 487138032Speter 487238032Speter /* had a naked carriage return */ 4873112810Sgshapiro SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 487438032Speter *pbp++ = c; 487538032Speter c = '\r'; 4876157001Sgshapiro ostate = OSTATE_INLINE; 487738032Speter goto putch; 487838032Speter 4879157001Sgshapiro case OSTATE_INLINE: 488038032Speter if (c == '\r') 488138032Speter { 4882157001Sgshapiro ostate = OSTATE_CR; 488338032Speter continue; 488438032Speter } 488538032Speter if (c == '\0' && 488690792Sgshapiro bitnset(M_NONULLS, 488790792Sgshapiro mci->mci_mailer->m_flags)) 488838032Speter break; 488938032Speterputch: 489038032Speter if (mci->mci_mailer->m_linelimit > 0 && 489164562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 489238032Speter c != '\n') 489338032Speter { 489464562Sgshapiro int d; 489564562Sgshapiro 489664562Sgshapiro /* check next character for EOL */ 489764562Sgshapiro if (pbp > peekbuf) 489864562Sgshapiro d = *(pbp - 1); 489990792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 490090792Sgshapiro SM_TIME_DEFAULT)) 490190792Sgshapiro != SM_IO_EOF) 4902112810Sgshapiro { 4903112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4904112810Sgshapiro sizeof(peekbuf)); 490564562Sgshapiro *pbp++ = d; 4906112810Sgshapiro } 490764562Sgshapiro 490890792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 490964562Sgshapiro { 491064562Sgshapiro if (TrafficLogFile != NULL) 491190792Sgshapiro (void) sm_io_putc(TrafficLogFile, 491290792Sgshapiro SM_TIME_DEFAULT, 491390792Sgshapiro (unsigned char) c); 491490792Sgshapiro if (sm_io_putc(mci->mci_out, 491590792Sgshapiro SM_TIME_DEFAULT, 491690792Sgshapiro (unsigned char) c) 491790792Sgshapiro == SM_IO_EOF) 491864562Sgshapiro { 491990792Sgshapiro dead = true; 492064562Sgshapiro continue; 492164562Sgshapiro } 492264562Sgshapiro pos++; 492364562Sgshapiro continue; 492464562Sgshapiro } 492564562Sgshapiro 492690792Sgshapiro if (sm_io_putc(mci->mci_out, 492790792Sgshapiro SM_TIME_DEFAULT, '!') 492890792Sgshapiro == SM_IO_EOF || 492990792Sgshapiro sm_io_fputs(mci->mci_out, 493090792Sgshapiro SM_TIME_DEFAULT, 493190792Sgshapiro mci->mci_mailer->m_eol) 493290792Sgshapiro == SM_IO_EOF) 493364562Sgshapiro { 493490792Sgshapiro dead = true; 493564562Sgshapiro continue; 493664562Sgshapiro } 493764562Sgshapiro 493838032Speter if (TrafficLogFile != NULL) 493938032Speter { 494090792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 494190792Sgshapiro SM_TIME_DEFAULT, 494290792Sgshapiro "!%s", 494390792Sgshapiro mci->mci_mailer->m_eol); 494438032Speter } 4945157001Sgshapiro ostate = OSTATE_HEAD; 4946112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4947112810Sgshapiro sizeof(peekbuf)); 494838032Speter *pbp++ = c; 494938032Speter continue; 495038032Speter } 495138032Speter if (c == '\n') 495238032Speter { 495338032Speter if (TrafficLogFile != NULL) 495490792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 495590792Sgshapiro SM_TIME_DEFAULT, 495690792Sgshapiro mci->mci_mailer->m_eol); 495790792Sgshapiro if (sm_io_fputs(mci->mci_out, 495890792Sgshapiro SM_TIME_DEFAULT, 495990792Sgshapiro mci->mci_mailer->m_eol) 496090792Sgshapiro == SM_IO_EOF) 496164562Sgshapiro continue; 496238032Speter pos = 0; 4963157001Sgshapiro ostate = OSTATE_HEAD; 496438032Speter } 496538032Speter else 496638032Speter { 496738032Speter if (TrafficLogFile != NULL) 496890792Sgshapiro (void) sm_io_putc(TrafficLogFile, 496990792Sgshapiro SM_TIME_DEFAULT, 497090792Sgshapiro (unsigned char) c); 497190792Sgshapiro if (sm_io_putc(mci->mci_out, 497290792Sgshapiro SM_TIME_DEFAULT, 497390792Sgshapiro (unsigned char) c) 497490792Sgshapiro == SM_IO_EOF) 497564562Sgshapiro { 497690792Sgshapiro dead = true; 497764562Sgshapiro continue; 497864562Sgshapiro } 497938032Speter pos++; 4980157001Sgshapiro ostate = OSTATE_INLINE; 498138032Speter } 498238032Speter break; 498338032Speter } 498438032Speter } 498538032Speter 498638032Speter /* make sure we are at the beginning of a line */ 498738032Speter if (bp > buf) 498838032Speter { 498938032Speter if (TrafficLogFile != NULL) 499038032Speter { 499138032Speter for (xp = buf; xp < bp; xp++) 499290792Sgshapiro (void) sm_io_putc(TrafficLogFile, 499390792Sgshapiro SM_TIME_DEFAULT, 499490792Sgshapiro (unsigned char) *xp); 499538032Speter } 499638032Speter for (xp = buf; xp < bp; xp++) 499738032Speter { 499890792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 499990792Sgshapiro (unsigned char) *xp) 500090792Sgshapiro == SM_IO_EOF) 500164562Sgshapiro { 500290792Sgshapiro dead = true; 500364562Sgshapiro break; 500464562Sgshapiro } 500538032Speter } 500638032Speter pos += bp - buf; 500738032Speter } 500864562Sgshapiro if (!dead && pos > 0) 500938032Speter { 501038032Speter if (TrafficLogFile != NULL) 501190792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 501290792Sgshapiro SM_TIME_DEFAULT, 501390792Sgshapiro mci->mci_mailer->m_eol); 5014157001Sgshapiro if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 5015157001Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF) 5016157001Sgshapiro goto writeerr; 501738032Speter } 501838032Speter } 501938032Speter 502090792Sgshapiro if (sm_io_error(e->e_dfp)) 502138032Speter { 502290792Sgshapiro syserr("putbody: %s/%cf%s: read error", 502390792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 502490792Sgshapiro DATAFL_LETTER, e->e_id); 502538032Speter ExitStat = EX_IOERR; 5026157001Sgshapiro ioerr = true; 502738032Speter } 502838032Speter 502938032Speterendofmessage: 503064562Sgshapiro /* 503164562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 503264562Sgshapiro ** the file offset in the stdio library for the 503364562Sgshapiro ** parent process will not agree with the in-kernel 503464562Sgshapiro ** file offset since the file descriptor is shared 503564562Sgshapiro ** between the processes. Therefore, it is vital 503664562Sgshapiro ** that the file always be rewound. This forces the 503764562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 503864562Sgshapiro ** offset to match. 503964562Sgshapiro */ 504064562Sgshapiro 5041157001Sgshapiro save_errno = errno; 504264562Sgshapiro if (e->e_dfp != NULL) 504364562Sgshapiro (void) bfrewind(e->e_dfp); 504464562Sgshapiro 504538032Speter /* some mailers want extra blank line at end of message */ 504664562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 504738032Speter buf[0] != '\0' && buf[0] != '\n') 5048157001Sgshapiro { 5049157001Sgshapiro if (!putline("", mci)) 5050157001Sgshapiro goto writeerr; 5051157001Sgshapiro } 505238032Speter 5053157001Sgshapiro if (!dead && 5054157001Sgshapiro (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF || 5055157001Sgshapiro (sm_io_error(mci->mci_out) && errno != EPIPE))) 505638032Speter { 5057157001Sgshapiro save_errno = errno; 505838032Speter syserr("putbody: write error"); 505938032Speter ExitStat = EX_IOERR; 5060157001Sgshapiro ioerr = true; 506138032Speter } 506264562Sgshapiro 5063157001Sgshapiro errno = save_errno; 5064157001Sgshapiro return !dead && !ioerr; 5065157001Sgshapiro 5066157001Sgshapiro writeerr: 5067157001Sgshapiro return false; 506838032Speter} 5069157001Sgshapiro 507090792Sgshapiro/* 507138032Speter** MAILFILE -- Send a message to a file. 507238032Speter** 507390792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 507490792Sgshapiro** execute bits, sendmail will try to become the owner of that file 507538032Speter** rather than the real user. Obviously, this only works if 507638032Speter** sendmail runs as root. 507738032Speter** 507838032Speter** This could be done as a subordinate mailer, except that it 507938032Speter** is used implicitly to save messages in ~/dead.letter. We 508038032Speter** view this as being sufficiently important as to include it 508138032Speter** here. For example, if the system is dying, we shouldn't have 508238032Speter** to create another process plus some pipes to save the message. 508338032Speter** 508438032Speter** Parameters: 508538032Speter** filename -- the name of the file to send to. 508638032Speter** mailer -- mailer definition for recipient -- if NULL, 508738032Speter** use FileMailer. 508838032Speter** ctladdr -- the controlling address header -- includes 508938032Speter** the userid/groupid to be when sending. 509038032Speter** sfflags -- flags for opening. 509138032Speter** e -- the current envelope. 509238032Speter** 509338032Speter** Returns: 509438032Speter** The exit code associated with the operation. 509538032Speter** 509638032Speter** Side Effects: 509738032Speter** none. 509838032Speter*/ 509938032Speter 510090792Sgshapiro# define RETURN(st) exit(st); 510190792Sgshapiro 510238032Speterstatic jmp_buf CtxMailfileTimeout; 510338032Speter 510438032Speterint 510538032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 510638032Speter char *volatile filename; 510738032Speter MAILER *volatile mailer; 510838032Speter ADDRESS *ctladdr; 510964562Sgshapiro volatile long sfflags; 511038032Speter register ENVELOPE *e; 511138032Speter{ 511290792Sgshapiro register SM_FILE_T *f; 511338032Speter register pid_t pid = -1; 511464562Sgshapiro volatile int mode; 511564562Sgshapiro int len; 511664562Sgshapiro off_t curoff; 511738032Speter bool suidwarn = geteuid() == 0; 511838032Speter char *p; 511964562Sgshapiro char *volatile realfile; 512090792Sgshapiro SM_EVENT *ev; 512198121Sgshapiro char buf[MAXPATHLEN]; 512298121Sgshapiro char targetfile[MAXPATHLEN]; 512338032Speter 512438032Speter if (tTd(11, 1)) 512538032Speter { 512690792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 5127132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 512838032Speter } 512938032Speter 513038032Speter if (mailer == NULL) 513138032Speter mailer = FileMailer; 513238032Speter 513338032Speter if (e->e_xfp != NULL) 513490792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 513538032Speter 513638032Speter /* 513738032Speter ** Special case /dev/null. This allows us to restrict file 513838032Speter ** delivery to regular files only. 513938032Speter */ 514038032Speter 514190792Sgshapiro if (sm_path_isdevnull(filename)) 514238032Speter return EX_OK; 514338032Speter 514438032Speter /* check for 8-bit available */ 514538032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 514638032Speter bitnset(M_7BITS, mailer->m_flags) && 514738032Speter (bitset(EF_DONT_MIME, e->e_flags) || 514838032Speter !(bitset(MM_MIME8BIT, MimeMode) || 514938032Speter (bitset(EF_IS_MIME, e->e_flags) && 515038032Speter bitset(MM_CVTMIME, MimeMode))))) 515138032Speter { 515238032Speter e->e_status = "5.6.3"; 515364562Sgshapiro usrerrenh(e->e_status, 515490792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 515590792Sgshapiro errno = 0; 515664562Sgshapiro return EX_DATAERR; 515738032Speter } 515838032Speter 515964562Sgshapiro /* Find the actual file */ 516064562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 516164562Sgshapiro { 516264562Sgshapiro len = strlen(SafeFileEnv); 516364562Sgshapiro 516464562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 516564562Sgshapiro filename += len; 516664562Sgshapiro 5167168515Sgshapiro if (len + strlen(filename) + 1 >= sizeof(targetfile)) 516864562Sgshapiro { 516964562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 517064562Sgshapiro SafeFileEnv, filename); 517164562Sgshapiro return EX_CANTCREAT; 517264562Sgshapiro } 5173168515Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile)); 517464562Sgshapiro realfile = targetfile + len; 517564562Sgshapiro if (*filename == '/') 517664562Sgshapiro filename++; 517794334Sgshapiro if (*filename != '\0') 517894334Sgshapiro { 517994334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 518094334Sgshapiro if (targetfile[len - 1] != '/') 518194334Sgshapiro (void) sm_strlcat(targetfile, 5182168515Sgshapiro "/", sizeof(targetfile)); 518394334Sgshapiro (void) sm_strlcat(targetfile, filename, 5184168515Sgshapiro sizeof(targetfile)); 518594334Sgshapiro } 518664562Sgshapiro } 518764562Sgshapiro else if (mailer->m_rootdir != NULL) 518864562Sgshapiro { 5189168515Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e); 519064562Sgshapiro len = strlen(targetfile); 519164562Sgshapiro 519264562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 519364562Sgshapiro filename += len; 519464562Sgshapiro 5195168515Sgshapiro if (len + strlen(filename) + 1 >= sizeof(targetfile)) 519664562Sgshapiro { 519764562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 519864562Sgshapiro targetfile, filename); 519964562Sgshapiro return EX_CANTCREAT; 520064562Sgshapiro } 520164562Sgshapiro realfile = targetfile + len; 520264562Sgshapiro if (targetfile[len - 1] != '/') 5203168515Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof(targetfile)); 520464562Sgshapiro if (*filename == '/') 520590792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 5206168515Sgshapiro sizeof(targetfile)); 520764562Sgshapiro else 520890792Sgshapiro (void) sm_strlcat(targetfile, filename, 5209168515Sgshapiro sizeof(targetfile)); 521064562Sgshapiro } 521164562Sgshapiro else 521264562Sgshapiro { 5213168515Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >= 5214168515Sgshapiro sizeof(targetfile)) 521564562Sgshapiro { 521664562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 521764562Sgshapiro return EX_CANTCREAT; 521864562Sgshapiro } 521964562Sgshapiro realfile = targetfile; 522064562Sgshapiro } 522164562Sgshapiro 522238032Speter /* 522338032Speter ** Fork so we can change permissions here. 522438032Speter ** Note that we MUST use fork, not vfork, because of 522538032Speter ** the complications of calling subroutines, etc. 522638032Speter */ 522738032Speter 522894334Sgshapiro 522994334Sgshapiro /* 523094334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 523194334Sgshapiro ** around so that the waitfor() below will get it. 523294334Sgshapiro */ 523394334Sgshapiro 523494334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 523594334Sgshapiro 523638032Speter DOFORK(fork); 523738032Speter 523838032Speter if (pid < 0) 523964562Sgshapiro return EX_OSERR; 524038032Speter else if (pid == 0) 524138032Speter { 524238032Speter /* child -- actually write to file */ 524338032Speter struct stat stb; 524438032Speter MCI mcibuf; 524542575Speter int err; 524638032Speter volatile int oflags = O_WRONLY|O_APPEND; 524738032Speter 524877349Sgshapiro /* Reset global flags */ 524977349Sgshapiro RestartRequest = NULL; 525090792Sgshapiro RestartWorkGroup = false; 525177349Sgshapiro ShutdownRequest = NULL; 525277349Sgshapiro PendingSignal = 0; 525390792Sgshapiro CurrentPid = getpid(); 525477349Sgshapiro 525538032Speter if (e->e_lockfp != NULL) 5256159609Sgshapiro { 5257159609Sgshapiro int fd; 525838032Speter 5259159609Sgshapiro fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 5260159609Sgshapiro /* SM_ASSERT(fd >= 0); */ 5261159609Sgshapiro if (fd >= 0) 5262159609Sgshapiro (void) close(fd); 5263159609Sgshapiro } 5264159609Sgshapiro 526590792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 526690792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 526790792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 526838032Speter (void) umask(OldUmask); 526938032Speter e->e_to = filename; 527038032Speter ExitStat = EX_OK; 527138032Speter 527238032Speter if (setjmp(CtxMailfileTimeout) != 0) 527338032Speter { 527490792Sgshapiro RETURN(EX_TEMPFAIL); 527538032Speter } 527638032Speter 527738032Speter if (TimeOuts.to_fileopen > 0) 527890792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 527990792Sgshapiro 0); 528038032Speter else 528138032Speter ev = NULL; 528238032Speter 528390792Sgshapiro /* check file mode to see if set-user-ID */ 528464562Sgshapiro if (stat(targetfile, &stb) < 0) 528564562Sgshapiro mode = FileMode; 528642575Speter else 528738032Speter mode = stb.st_mode; 528838032Speter 528938032Speter /* limit the errors to those actually caused in the child */ 529038032Speter errno = 0; 529138032Speter ExitStat = EX_OK; 529238032Speter 529364562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 529464562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 529564562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 529638032Speter { 529790792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 529838032Speter mode &= ~(S_ISGID|S_ISUID); 529964562Sgshapiro if (tTd(11, 20)) 530090792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 530138032Speter } 530238032Speter 530390792Sgshapiro /* we have to open the data file BEFORE setuid() */ 530438032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 530538032Speter { 530690792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 530738032Speter 530890792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5309120256Sgshapiro SM_IO_RDONLY_B, NULL); 531038032Speter if (e->e_dfp == NULL) 531138032Speter { 531238032Speter syserr("mailfile: Cannot open %s for %s from %s", 531338032Speter df, e->e_to, e->e_from.q_paddr); 531438032Speter } 531538032Speter } 531638032Speter 531738032Speter /* select a new user to run as */ 531838032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 531938032Speter { 532038032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 532138032Speter { 532238032Speter RealUserName = NULL; 5323132943Sgshapiro if (mailer->m_uid == NO_UID) 5324132943Sgshapiro RealUid = RunAsUid; 5325132943Sgshapiro else 5326132943Sgshapiro RealUid = mailer->m_uid; 532764562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 532864562Sgshapiro { 532964562Sgshapiro /* Only root can change the uid */ 533090792Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 533190792Sgshapiro (int) RunAsUid, (int) RealUid); 533290792Sgshapiro RETURN(EX_TEMPFAIL); 533364562Sgshapiro } 533438032Speter } 533538032Speter else if (bitset(S_ISUID, mode)) 533638032Speter { 533738032Speter RealUserName = NULL; 533838032Speter RealUid = stb.st_uid; 533938032Speter } 534038032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 534138032Speter { 534238032Speter if (ctladdr->q_ruser != NULL) 534338032Speter RealUserName = ctladdr->q_ruser; 534438032Speter else 534538032Speter RealUserName = ctladdr->q_user; 534638032Speter RealUid = ctladdr->q_uid; 534738032Speter } 5348132943Sgshapiro else if (mailer != NULL && mailer->m_uid != NO_UID) 534938032Speter { 535038032Speter RealUserName = DefUser; 535138032Speter RealUid = mailer->m_uid; 535238032Speter } 535338032Speter else 535438032Speter { 535538032Speter RealUserName = DefUser; 535638032Speter RealUid = DefUid; 535738032Speter } 535838032Speter 535938032Speter /* select a new group to run as */ 536038032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 536164562Sgshapiro { 5362132943Sgshapiro if (mailer->m_gid == NO_GID) 5363132943Sgshapiro RealGid = RunAsGid; 5364132943Sgshapiro else 5365132943Sgshapiro RealGid = mailer->m_gid; 536664562Sgshapiro if (RunAsUid != 0 && 536764562Sgshapiro (RealGid != getgid() || 536864562Sgshapiro RealGid != getegid())) 536964562Sgshapiro { 537064562Sgshapiro /* Only root can change the gid */ 537190792Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 537290792Sgshapiro (int) RealGid, (int) RunAsUid, 537390792Sgshapiro (int) getgid(), (int) getegid()); 537490792Sgshapiro RETURN(EX_TEMPFAIL); 537564562Sgshapiro } 537664562Sgshapiro } 537738032Speter else if (bitset(S_ISGID, mode)) 537838032Speter RealGid = stb.st_gid; 537964562Sgshapiro else if (ctladdr != NULL && 538064562Sgshapiro ctladdr->q_uid == DefUid && 538164562Sgshapiro ctladdr->q_gid == 0) 538271345Sgshapiro { 538371345Sgshapiro /* 538471345Sgshapiro ** Special case: This means it is an 538571345Sgshapiro ** alias and we should act as DefaultUser. 538671345Sgshapiro ** See alias()'s comments. 538771345Sgshapiro */ 538871345Sgshapiro 538964562Sgshapiro RealGid = DefGid; 539071345Sgshapiro RealUserName = DefUser; 539171345Sgshapiro } 539271345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 539371345Sgshapiro RealGid = ctladdr->q_gid; 5394132943Sgshapiro else if (mailer != NULL && mailer->m_gid != NO_GID) 539538032Speter RealGid = mailer->m_gid; 539638032Speter else 539738032Speter RealGid = DefGid; 539838032Speter } 539938032Speter 540038032Speter /* last ditch */ 540138032Speter if (!bitset(SFF_ROOTOK, sfflags)) 540238032Speter { 540338032Speter if (RealUid == 0) 540438032Speter RealUid = DefUid; 540538032Speter if (RealGid == 0) 540638032Speter RealGid = DefGid; 540738032Speter } 540838032Speter 540938032Speter /* set group id list (needs /etc/group access) */ 541038032Speter if (RealUserName != NULL && !DontInitGroups) 541138032Speter { 541238032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 541364562Sgshapiro { 541438032Speter syserr("mailfile: initgroups(%s, %d) failed", 541538032Speter RealUserName, RealGid); 541690792Sgshapiro RETURN(EX_TEMPFAIL); 541764562Sgshapiro } 541838032Speter } 541938032Speter else 542038032Speter { 542138032Speter GIDSET_T gidset[1]; 542238032Speter 542338032Speter gidset[0] = RealGid; 542438032Speter if (setgroups(1, gidset) == -1 && suidwarn) 542564562Sgshapiro { 542638032Speter syserr("mailfile: setgroups() failed"); 542790792Sgshapiro RETURN(EX_TEMPFAIL); 542864562Sgshapiro } 542938032Speter } 543038032Speter 543164562Sgshapiro /* 543264562Sgshapiro ** If you have a safe environment, go into it. 543364562Sgshapiro */ 543464562Sgshapiro 543564562Sgshapiro if (realfile != targetfile) 543638032Speter { 543794334Sgshapiro char save; 543894334Sgshapiro 543994334Sgshapiro save = *realfile; 544064562Sgshapiro *realfile = '\0'; 544164562Sgshapiro if (tTd(11, 20)) 544290792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 544364562Sgshapiro if (chroot(targetfile) < 0) 544438032Speter { 544538032Speter syserr("mailfile: Cannot chroot(%s)", 544664562Sgshapiro targetfile); 544790792Sgshapiro RETURN(EX_CANTCREAT); 544838032Speter } 544994334Sgshapiro *realfile = save; 545038032Speter } 545164562Sgshapiro 545264562Sgshapiro if (tTd(11, 40)) 545390792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 545464562Sgshapiro 545538032Speter if (chdir("/") < 0) 545664562Sgshapiro { 545738032Speter syserr("mailfile: cannot chdir(/)"); 545890792Sgshapiro RETURN(EX_CANTCREAT); 545964562Sgshapiro } 546038032Speter 546138032Speter /* now reset the group and user ids */ 546238032Speter endpwent(); 546390792Sgshapiro sm_mbdb_terminate(); 546438032Speter if (setgid(RealGid) < 0 && suidwarn) 546564562Sgshapiro { 546638032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 546790792Sgshapiro RETURN(EX_TEMPFAIL); 546864562Sgshapiro } 546938032Speter vendor_set_uid(RealUid); 547038032Speter if (setuid(RealUid) < 0 && suidwarn) 547164562Sgshapiro { 547238032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 547390792Sgshapiro RETURN(EX_TEMPFAIL); 547464562Sgshapiro } 547538032Speter 547664562Sgshapiro if (tTd(11, 2)) 547790792Sgshapiro sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 547864562Sgshapiro (int) getuid(), (int) geteuid(), 547964562Sgshapiro (int) getgid(), (int) getegid()); 548064562Sgshapiro 548164562Sgshapiro 548238032Speter /* move into some "safe" directory */ 548338032Speter if (mailer->m_execdir != NULL) 548438032Speter { 548538032Speter char *q; 548638032Speter 548738032Speter for (p = mailer->m_execdir; p != NULL; p = q) 548838032Speter { 548938032Speter q = strchr(p, ':'); 549038032Speter if (q != NULL) 549138032Speter *q = '\0'; 5492168515Sgshapiro expand(p, buf, sizeof(buf), e); 549338032Speter if (q != NULL) 549438032Speter *q++ = ':'; 549538032Speter if (tTd(11, 20)) 549690792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 549790792Sgshapiro buf); 549838032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 549938032Speter break; 550038032Speter } 550138032Speter } 550238032Speter 550364562Sgshapiro /* 550464562Sgshapiro ** Recheck the file after we have assumed the ID of the 550564562Sgshapiro ** delivery user to make sure we can deliver to it as 550664562Sgshapiro ** that user. This is necessary if sendmail is running 550764562Sgshapiro ** as root and the file is on an NFS mount which treats 550864562Sgshapiro ** root as nobody. 550964562Sgshapiro */ 551064562Sgshapiro 551164562Sgshapiro#if HASLSTAT 551264562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 551364562Sgshapiro err = stat(realfile, &stb); 551464562Sgshapiro else 551564562Sgshapiro err = lstat(realfile, &stb); 551664562Sgshapiro#else /* HASLSTAT */ 551764562Sgshapiro err = stat(realfile, &stb); 551864562Sgshapiro#endif /* HASLSTAT */ 551964562Sgshapiro 552064562Sgshapiro if (err < 0) 552164562Sgshapiro { 552264562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 552364562Sgshapiro mode = FileMode; 552464562Sgshapiro oflags |= O_CREAT|O_EXCL; 552564562Sgshapiro } 552664562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 552764562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 552864562Sgshapiro DontBlameSendmail) && 552964562Sgshapiro stb.st_nlink != 1) || 553064562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 553164562Sgshapiro exit(EX_CANTCREAT); 553264562Sgshapiro else 553364562Sgshapiro mode = stb.st_mode; 553464562Sgshapiro 553564562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 553638032Speter sfflags |= SFF_NOSLINK; 553764562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 553838032Speter sfflags |= SFF_NOHLINK; 553938032Speter sfflags &= ~SFF_OPENASROOT; 554064562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 554138032Speter if (f == NULL) 554238032Speter { 554364562Sgshapiro if (transienterror(errno)) 554464562Sgshapiro { 554564562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 554664562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 554790792Sgshapiro sm_errstring(errno)); 554890792Sgshapiro RETURN(EX_TEMPFAIL); 554964562Sgshapiro } 555064562Sgshapiro else 555164562Sgshapiro { 555264562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 555364562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 555490792Sgshapiro sm_errstring(errno)); 555590792Sgshapiro RETURN(EX_CANTCREAT); 555664562Sgshapiro } 555738032Speter } 555890792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 555990792Sgshapiro &stb)) 556038032Speter { 556164562Sgshapiro syserr("554 5.3.0 file changed after open"); 556290792Sgshapiro RETURN(EX_CANTCREAT); 556338032Speter } 556490792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 556538032Speter { 556690792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 556790792Sgshapiro sm_errstring(errno)); 556890792Sgshapiro RETURN(EX_CANTCREAT); 556938032Speter } 557038032Speter 557164562Sgshapiro curoff = stb.st_size; 557264562Sgshapiro 557338032Speter if (ev != NULL) 557490792Sgshapiro sm_clrevent(ev); 557538032Speter 5576168515Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 557738032Speter mcibuf.mci_mailer = mailer; 557838032Speter mcibuf.mci_out = f; 557938032Speter if (bitnset(M_7BITS, mailer->m_flags)) 558038032Speter mcibuf.mci_flags |= MCIF_7BIT; 558138032Speter 558238032Speter /* clear out per-message flags from connection structure */ 558338032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 558438032Speter 558538032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 558638032Speter !bitset(EF_DONT_MIME, e->e_flags) && 558738032Speter bitnset(M_7BITS, mailer->m_flags)) 558838032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 558938032Speter 559038032Speter#if MIME7TO8 559138032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 559238032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 559338032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 559490792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 559590792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 559638032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 559738032Speter { 559838032Speter /* may want to convert 7 -> 8 */ 559938032Speter /* XXX should really parse it here -- and use a class XXX */ 560090792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 560164562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 560238032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 560338032Speter } 560464562Sgshapiro#endif /* MIME7TO8 */ 560538032Speter 5606157001Sgshapiro if (!putfromline(&mcibuf, e) || 5607157001Sgshapiro !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) || 5608157001Sgshapiro !(*e->e_putbody)(&mcibuf, e, NULL) || 5609157001Sgshapiro !putline("\n", &mcibuf) || 5610157001Sgshapiro (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 561190792Sgshapiro (SuperSafe != SAFE_NO && 561290792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 5613157001Sgshapiro sm_io_error(f))) 561438032Speter { 561538032Speter setstat(EX_IOERR); 561664562Sgshapiro#if !NOFTRUNCATE 561790792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 561890792Sgshapiro curoff); 561964562Sgshapiro#endif /* !NOFTRUNCATE */ 562038032Speter } 562138032Speter 562238032Speter /* reset ISUID & ISGID bits for paranoid systems */ 562338032Speter#if HASFCHMOD 562490792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 562590792Sgshapiro (MODE_T) mode); 562664562Sgshapiro#else /* HASFCHMOD */ 562764562Sgshapiro (void) chmod(filename, (MODE_T) mode); 562864562Sgshapiro#endif /* HASFCHMOD */ 562990792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 563064562Sgshapiro setstat(EX_IOERR); 563190792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 563264562Sgshapiro (void) setuid(RealUid); 563338032Speter exit(ExitStat); 563464562Sgshapiro /* NOTREACHED */ 563538032Speter } 563638032Speter else 563738032Speter { 563838032Speter /* parent -- wait for exit status */ 563938032Speter int st; 564038032Speter 564138032Speter st = waitfor(pid); 564238032Speter if (st == -1) 564338032Speter { 564438032Speter syserr("mailfile: %s: wait", mailer->m_name); 564564562Sgshapiro return EX_SOFTWARE; 564638032Speter } 564738032Speter if (WIFEXITED(st)) 564890792Sgshapiro { 564990792Sgshapiro errno = 0; 565038032Speter return (WEXITSTATUS(st)); 565190792Sgshapiro } 565238032Speter else 565338032Speter { 565438032Speter syserr("mailfile: %s: child died on signal %d", 565538032Speter mailer->m_name, st); 565664562Sgshapiro return EX_UNAVAILABLE; 565738032Speter } 565864562Sgshapiro /* NOTREACHED */ 565938032Speter } 566038032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 566138032Speter} 566238032Speter 566338032Speterstatic void 5664141858Sgshapiromailfiletimeout(ignore) 5665141858Sgshapiro int ignore; 566638032Speter{ 566777349Sgshapiro /* 566877349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 566977349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 567077349Sgshapiro ** DOING. 567177349Sgshapiro */ 567277349Sgshapiro 567377349Sgshapiro errno = ETIMEDOUT; 567438032Speter longjmp(CtxMailfileTimeout, 1); 567538032Speter} 567690792Sgshapiro/* 567738032Speter** HOSTSIGNATURE -- return the "signature" for a host. 567838032Speter** 567938032Speter** The signature describes how we are going to send this -- it 568038032Speter** can be just the hostname (for non-Internet hosts) or can be 568138032Speter** an ordered list of MX hosts. 568238032Speter** 568338032Speter** Parameters: 568438032Speter** m -- the mailer describing this host. 568538032Speter** host -- the host name. 568638032Speter** 568738032Speter** Returns: 568838032Speter** The signature for this host. 568938032Speter** 569038032Speter** Side Effects: 569138032Speter** Can tweak the symbol table. 569238032Speter*/ 569390792Sgshapiro 569464562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 569538032Speter 569690792Sgshapirochar * 569764562Sgshapirohostsignature(m, host) 569838032Speter register MAILER *m; 569938032Speter char *host; 570038032Speter{ 570138032Speter register char *p; 570238032Speter register STAB *s; 570390792Sgshapiro time_t now; 570464562Sgshapiro#if NAMED_BIND 570564562Sgshapiro char sep = ':'; 570664562Sgshapiro char prevsep = ':'; 570738032Speter int i; 570838032Speter int len; 570938032Speter int nmx; 571064562Sgshapiro int hl; 571138032Speter char *hp; 571238032Speter char *endp; 571338032Speter int oldoptions = _res.options; 571438032Speter char *mxhosts[MAXMXHOSTS + 1]; 571590792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 571664562Sgshapiro#endif /* NAMED_BIND */ 571738032Speter 571864562Sgshapiro if (tTd(17, 3)) 571990792Sgshapiro sm_dprintf("hostsignature(%s)\n", host); 572064562Sgshapiro 572138032Speter /* 572277349Sgshapiro ** If local delivery (and not remote), just return a constant. 572364562Sgshapiro */ 572464562Sgshapiro 572577349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 572690792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 572790792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 572864562Sgshapiro return "localhost"; 572964562Sgshapiro 5730147078Sgshapiro /* an empty host does not have MX records */ 5731147078Sgshapiro if (*host == '\0') 5732147078Sgshapiro return "_empty_"; 5733147078Sgshapiro 573464562Sgshapiro /* 573538032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 573638032Speter */ 573738032Speter 573890792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 573990792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 574038032Speter { 574190792Sgshapiro /* just an ordinary mailer or deferred mode */ 574238032Speter return host; 574338032Speter } 574464562Sgshapiro#if NETUNIX 574564562Sgshapiro else if (m->m_argv[0] != NULL && 574664562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 574764562Sgshapiro { 574864562Sgshapiro /* rendezvous in the file system, no MX records */ 574964562Sgshapiro return host; 575064562Sgshapiro } 575164562Sgshapiro#endif /* NETUNIX */ 575238032Speter 575338032Speter /* 575438032Speter ** Look it up in the symbol table. 575538032Speter */ 575638032Speter 575790792Sgshapiro now = curtime(); 575838032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 575990792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 576064562Sgshapiro { 576190792Sgshapiro if (s->s_hostsig.hs_exp >= now) 576290792Sgshapiro { 576390792Sgshapiro if (tTd(17, 3)) 576490792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 576590792Sgshapiro s->s_hostsig.hs_sig); 576690792Sgshapiro return s->s_hostsig.hs_sig; 576790792Sgshapiro } 576890792Sgshapiro 576990792Sgshapiro /* signature is expired: clear it */ 577090792Sgshapiro sm_free(s->s_hostsig.hs_sig); 577190792Sgshapiro s->s_hostsig.hs_sig = NULL; 577264562Sgshapiro } 577338032Speter 577490792Sgshapiro /* set default TTL */ 577590792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 577690792Sgshapiro 577738032Speter /* 577890792Sgshapiro ** Not already there or expired -- create a signature. 577938032Speter */ 578038032Speter 578138032Speter#if NAMED_BIND 578238032Speter if (ConfigLevel < 2) 578338032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 578438032Speter 578538032Speter for (hp = host; hp != NULL; hp = endp) 578638032Speter { 578764562Sgshapiro#if NETINET6 578864562Sgshapiro if (*hp == '[') 578964562Sgshapiro { 579064562Sgshapiro endp = strchr(hp + 1, ']'); 579164562Sgshapiro if (endp != NULL) 579264562Sgshapiro endp = strpbrk(endp + 1, ":,"); 579364562Sgshapiro } 579464562Sgshapiro else 579564562Sgshapiro endp = strpbrk(hp, ":,"); 579664562Sgshapiro#else /* NETINET6 */ 579764562Sgshapiro endp = strpbrk(hp, ":,"); 579864562Sgshapiro#endif /* NETINET6 */ 579938032Speter if (endp != NULL) 580064562Sgshapiro { 580164562Sgshapiro sep = *endp; 580238032Speter *endp = '\0'; 580364562Sgshapiro } 580438032Speter 580538032Speter if (bitnset(M_NOMX, m->m_flags)) 580638032Speter { 580738032Speter /* skip MX lookups */ 580838032Speter nmx = 1; 580938032Speter mxhosts[0] = hp; 581038032Speter } 581138032Speter else 581238032Speter { 581338032Speter auto int rcode; 581490792Sgshapiro int ttl; 581538032Speter 581690792Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 581790792Sgshapiro &ttl); 581838032Speter if (nmx <= 0) 581938032Speter { 582080785Sgshapiro int save_errno; 582138032Speter register MCI *mci; 582238032Speter 582338032Speter /* update the connection info for this host */ 582480785Sgshapiro save_errno = errno; 582538032Speter mci = mci_get(hp, m); 582680785Sgshapiro mci->mci_errno = save_errno; 582738032Speter mci->mci_herrno = h_errno; 582871345Sgshapiro mci->mci_lastuse = now; 582964562Sgshapiro if (rcode == EX_NOHOST) 583064562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 583190792Sgshapiro "550 Host unknown"); 583264562Sgshapiro else 583364562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 583438032Speter 583538032Speter /* use the original host name as signature */ 583638032Speter nmx = 1; 583738032Speter mxhosts[0] = hp; 583838032Speter } 583964562Sgshapiro if (tTd(17, 3)) 584090792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 584190792Sgshapiro nmx, mxhosts[0]); 584290792Sgshapiro 584390792Sgshapiro /* 584490792Sgshapiro ** Set new TTL: we use only one! 584590792Sgshapiro ** We could try to use the minimum instead. 584690792Sgshapiro */ 584790792Sgshapiro 584890792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 584938032Speter } 585038032Speter 585138032Speter len = 0; 585238032Speter for (i = 0; i < nmx; i++) 585338032Speter len += strlen(mxhosts[i]) + 1; 585490792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 585590792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 585690792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 585764562Sgshapiro { 585864562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 585964562Sgshapiro host, MAXHOSTSIGNATURE, len); 586064562Sgshapiro len = MAXHOSTSIGNATURE; 586164562Sgshapiro } 586290792Sgshapiro p = sm_pmalloc_x(len); 586390792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 586438032Speter { 586590792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 586690792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 586790792Sgshapiro s->s_hostsig.hs_sig = p; 586864562Sgshapiro hl = strlen(p); 586964562Sgshapiro p += hl; 587064562Sgshapiro *p++ = prevsep; 587164562Sgshapiro len -= hl + 1; 587238032Speter } 587338032Speter else 587490792Sgshapiro s->s_hostsig.hs_sig = p; 587538032Speter for (i = 0; i < nmx; i++) 587638032Speter { 587764562Sgshapiro hl = strlen(mxhosts[i]); 587864562Sgshapiro if (len - 1 < hl || len <= 1) 587964562Sgshapiro { 588064562Sgshapiro /* force to drop out of outer loop */ 588164562Sgshapiro len = -1; 588264562Sgshapiro break; 588364562Sgshapiro } 588438032Speter if (i != 0) 588564562Sgshapiro { 588664562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 588764562Sgshapiro *p++ = ','; 588864562Sgshapiro else 588964562Sgshapiro *p++ = ':'; 589064562Sgshapiro len--; 589164562Sgshapiro } 589290792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 589364562Sgshapiro p += hl; 589464562Sgshapiro len -= hl; 589538032Speter } 589664562Sgshapiro 589764562Sgshapiro /* 589864562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 589964562Sgshapiro ** because we won't have more space for further hosts 590064562Sgshapiro ** anyway (separated by : in the .cf file). 590164562Sgshapiro */ 590264562Sgshapiro 590364562Sgshapiro if (len < 0) 590464562Sgshapiro break; 590538032Speter if (endp != NULL) 590664562Sgshapiro *endp++ = sep; 590764562Sgshapiro prevsep = sep; 590838032Speter } 590990792Sgshapiro makelower(s->s_hostsig.hs_sig); 591038032Speter if (ConfigLevel < 2) 591138032Speter _res.options = oldoptions; 591264562Sgshapiro#else /* NAMED_BIND */ 591338032Speter /* not using BIND -- the signature is just the host name */ 591490792Sgshapiro /* 591590792Sgshapiro ** 'host' points to storage that will be freed after we are 591690792Sgshapiro ** done processing the current envelope, so we copy it. 591790792Sgshapiro */ 591890792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 591964562Sgshapiro#endif /* NAMED_BIND */ 592038032Speter if (tTd(17, 1)) 592190792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 592290792Sgshapiro return s->s_hostsig.hs_sig; 592338032Speter} 592490792Sgshapiro/* 592564562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 592664562Sgshapiro** 592764562Sgshapiro** The signature describes how we are going to send this -- it 592864562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 592964562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 593064562Sgshapiro** MX preference values. 593164562Sgshapiro** 593264562Sgshapiro** Parameters: 593364562Sgshapiro** sig -- the host signature. 593464562Sgshapiro** mxhosts -- array to populate. 593590792Sgshapiro** mailer -- mailer. 593664562Sgshapiro** 593764562Sgshapiro** Returns: 593864562Sgshapiro** The number of hosts inserted into mxhosts array. 593964562Sgshapiro** 594064562Sgshapiro** Side Effects: 594164562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 594264562Sgshapiro*/ 594364562Sgshapiro 594464562Sgshapirostatic int 594564562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 594664562Sgshapiro char *sig; 594764562Sgshapiro char **mxhosts; 594864562Sgshapiro MAILER *mailer; 594964562Sgshapiro{ 595090792Sgshapiro unsigned short curpref = 0; 595190792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 595264562Sgshapiro char *hp, *endp; 595390792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 595464562Sgshapiro long rndm[MAXMXHOSTS]; 595564562Sgshapiro 595664562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 595764562Sgshapiro { 595864562Sgshapiro char sep = ':'; 595964562Sgshapiro 596064562Sgshapiro#if NETINET6 596164562Sgshapiro if (*hp == '[') 596264562Sgshapiro { 596364562Sgshapiro endp = strchr(hp + 1, ']'); 596464562Sgshapiro if (endp != NULL) 596564562Sgshapiro endp = strpbrk(endp + 1, ":,"); 596664562Sgshapiro } 596764562Sgshapiro else 596864562Sgshapiro endp = strpbrk(hp, ":,"); 596964562Sgshapiro#else /* NETINET6 */ 597064562Sgshapiro endp = strpbrk(hp, ":,"); 597164562Sgshapiro#endif /* NETINET6 */ 597264562Sgshapiro if (endp != NULL) 597364562Sgshapiro { 597464562Sgshapiro sep = *endp; 597564562Sgshapiro *endp = '\0'; 597664562Sgshapiro } 597764562Sgshapiro 597864562Sgshapiro mxhosts[nmx] = hp; 597964562Sgshapiro prefer[nmx] = curpref; 598064562Sgshapiro if (mci_match(hp, mailer)) 598164562Sgshapiro rndm[nmx] = 0; 598264562Sgshapiro else 598364562Sgshapiro rndm[nmx] = get_random(); 598464562Sgshapiro 598564562Sgshapiro if (endp != NULL) 598664562Sgshapiro { 598764562Sgshapiro /* 598864562Sgshapiro ** Since we don't have the original MX prefs, 598964562Sgshapiro ** make our own. If the separator is a ':', that 599064562Sgshapiro ** means the preference for the next host will be 599164562Sgshapiro ** higher than this one, so simply increment curpref. 599264562Sgshapiro */ 599364562Sgshapiro 599464562Sgshapiro if (sep == ':') 599564562Sgshapiro curpref++; 599664562Sgshapiro 599764562Sgshapiro *endp++ = sep; 599864562Sgshapiro } 599964562Sgshapiro if (++nmx >= MAXMXHOSTS) 600064562Sgshapiro break; 600164562Sgshapiro } 600264562Sgshapiro 600364562Sgshapiro /* sort the records using the random factor for equal preferences */ 600464562Sgshapiro for (i = 0; i < nmx; i++) 600564562Sgshapiro { 600664562Sgshapiro for (j = i + 1; j < nmx; j++) 600764562Sgshapiro { 600864562Sgshapiro /* 600964562Sgshapiro ** List is already sorted by MX preference, only 601064562Sgshapiro ** need to look for equal preference MX records 601164562Sgshapiro */ 601264562Sgshapiro 601364562Sgshapiro if (prefer[i] < prefer[j]) 601464562Sgshapiro break; 601564562Sgshapiro 601664562Sgshapiro if (prefer[i] > prefer[j] || 601764562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 601864562Sgshapiro { 601990792Sgshapiro register unsigned short tempp; 602064562Sgshapiro register long tempr; 602164562Sgshapiro register char *temp1; 602264562Sgshapiro 602364562Sgshapiro tempp = prefer[i]; 602464562Sgshapiro prefer[i] = prefer[j]; 602564562Sgshapiro prefer[j] = tempp; 602664562Sgshapiro temp1 = mxhosts[i]; 602764562Sgshapiro mxhosts[i] = mxhosts[j]; 602864562Sgshapiro mxhosts[j] = temp1; 602964562Sgshapiro tempr = rndm[i]; 603064562Sgshapiro rndm[i] = rndm[j]; 603164562Sgshapiro rndm[j] = tempr; 603264562Sgshapiro } 603364562Sgshapiro } 603464562Sgshapiro } 603564562Sgshapiro return nmx; 603664562Sgshapiro} 603764562Sgshapiro 603864562Sgshapiro# if STARTTLS 603964562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 604090792Sgshapirostatic bool tls_ok_clt = true; 604164562Sgshapiro 604290792Sgshapiro/* 604390792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 604464562Sgshapiro** 604564562Sgshapiro** Parameters: 604690792Sgshapiro** tls_ok -- should tls be done? 604790792Sgshapiro** 604890792Sgshapiro** Returns: 604964562Sgshapiro** none. 605064562Sgshapiro** 605190792Sgshapiro** Side Effects: 605290792Sgshapiro** sets tls_ok_clt (static variable in this module) 605390792Sgshapiro*/ 605490792Sgshapiro 605590792Sgshapirovoid 605690792Sgshapirosetclttls(tls_ok) 605790792Sgshapiro bool tls_ok; 605890792Sgshapiro{ 605990792Sgshapiro tls_ok_clt = tls_ok; 606090792Sgshapiro return; 606190792Sgshapiro} 606290792Sgshapiro/* 606390792Sgshapiro** INITCLTTLS -- initialize client side TLS 606490792Sgshapiro** 606590792Sgshapiro** Parameters: 606690792Sgshapiro** tls_ok -- should tls initialization be done? 606790792Sgshapiro** 606864562Sgshapiro** Returns: 606964562Sgshapiro** succeeded? 607090792Sgshapiro** 607190792Sgshapiro** Side Effects: 607290792Sgshapiro** sets tls_ok_clt (static variable in this module) 607364562Sgshapiro*/ 607464562Sgshapiro 607564562Sgshapirobool 607690792Sgshapiroinitclttls(tls_ok) 607790792Sgshapiro bool tls_ok; 607864562Sgshapiro{ 607990792Sgshapiro if (!tls_ok_clt) 608090792Sgshapiro return false; 608190792Sgshapiro tls_ok_clt = tls_ok; 608290792Sgshapiro if (!tls_ok_clt) 608390792Sgshapiro return false; 608464562Sgshapiro if (clt_ctx != NULL) 608590792Sgshapiro return true; /* already done */ 6086203004Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, Clt_SSL_Options, false, 6087203004Sgshapiro CltCertFile, CltKeyFile, 6088203004Sgshapiro CACertPath, CACertFile, DHParams); 608990792Sgshapiro return tls_ok_clt; 609064562Sgshapiro} 609164562Sgshapiro 609290792Sgshapiro/* 609364562Sgshapiro** STARTTLS -- try to start secure connection (client side) 609464562Sgshapiro** 609564562Sgshapiro** Parameters: 609664562Sgshapiro** m -- the mailer. 609764562Sgshapiro** mci -- the mailer connection info. 609864562Sgshapiro** e -- the envelope. 609964562Sgshapiro** 610064562Sgshapiro** Returns: 610164562Sgshapiro** success? 610264562Sgshapiro** (maybe this should be some other code than EX_ 610364562Sgshapiro** that denotes which stage failed.) 610464562Sgshapiro*/ 610564562Sgshapiro 610664562Sgshapirostatic int 610764562Sgshapirostarttls(m, mci, e) 610864562Sgshapiro MAILER *m; 610964562Sgshapiro MCI *mci; 611064562Sgshapiro ENVELOPE *e; 611164562Sgshapiro{ 611264562Sgshapiro int smtpresult; 611366494Sgshapiro int result = 0; 611466494Sgshapiro int rfd, wfd; 611564562Sgshapiro SSL *clt_ssl = NULL; 611690792Sgshapiro time_t tlsstart; 611764562Sgshapiro 611890792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 611966494Sgshapiro return EX_TEMPFAIL; 6120203004Sgshapiro 6121203004Sgshapiro# if USE_OPENSSL_ENGINE 6122223067Sgshapiro if (!SSLEngineInitialized && !SSL_set_engine(NULL)) 6123203004Sgshapiro { 6124203004Sgshapiro sm_syslog(LOG_ERR, NOQID, 6125203004Sgshapiro "STARTTLS=client, SSL_set_engine=failed"); 6126203004Sgshapiro return EX_TEMPFAIL; 6127203004Sgshapiro } 6128223067Sgshapiro SSLEngineInitialized = true; 6129203004Sgshapiro# endif /* USE_OPENSSL_ENGINE */ 6130203004Sgshapiro 613164562Sgshapiro smtpmessage("STARTTLS", m, mci); 613264562Sgshapiro 613364562Sgshapiro /* get the reply */ 6134132943Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 6135132943Sgshapiro XS_STARTTLS); 613664562Sgshapiro 613764562Sgshapiro /* check return code from server */ 6138157001Sgshapiro if (REPLYTYPE(smtpresult) == 4) 613964562Sgshapiro return EX_TEMPFAIL; 614064562Sgshapiro if (smtpresult == 501) 614164562Sgshapiro return EX_USAGE; 614264562Sgshapiro if (smtpresult == -1) 614364562Sgshapiro return smtpresult; 6144157001Sgshapiro 6145157001Sgshapiro /* not an expected reply but we have to deal with it */ 6146157001Sgshapiro if (REPLYTYPE(smtpresult) == 5) 6147157001Sgshapiro return EX_UNAVAILABLE; 614864562Sgshapiro if (smtpresult != 220) 614964562Sgshapiro return EX_PROTOCOL; 615064562Sgshapiro 615164562Sgshapiro if (LogLevel > 13) 615290792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 615364562Sgshapiro 615464562Sgshapiro /* start connection */ 615564562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 615664562Sgshapiro { 615764562Sgshapiro if (LogLevel > 5) 615864562Sgshapiro { 615990792Sgshapiro sm_syslog(LOG_ERR, NOQID, 616090792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 616164562Sgshapiro if (LogLevel > 9) 6162249865Sgshapiro tlslogerr(LOG_WARNING, "client"); 616364562Sgshapiro } 616464562Sgshapiro return EX_SOFTWARE; 616564562Sgshapiro } 616664562Sgshapiro 616790792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 616890792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 616966494Sgshapiro 617064562Sgshapiro /* SSL_clear(clt_ssl); ? */ 617166494Sgshapiro if (rfd < 0 || wfd < 0 || 617290792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 617390792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 617464562Sgshapiro { 617564562Sgshapiro if (LogLevel > 5) 617664562Sgshapiro { 617790792Sgshapiro sm_syslog(LOG_ERR, NOQID, 617890792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 617990792Sgshapiro result); 618064562Sgshapiro if (LogLevel > 9) 6181249865Sgshapiro tlslogerr(LOG_WARNING, "client"); 618264562Sgshapiro } 618364562Sgshapiro return EX_SOFTWARE; 618464562Sgshapiro } 618564562Sgshapiro SSL_set_connect_state(clt_ssl); 618690792Sgshapiro tlsstart = curtime(); 618790792Sgshapiro 618890792Sgshapirossl_retry: 618964562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 619064562Sgshapiro { 6191157001Sgshapiro int i, ssl_err; 619264562Sgshapiro 6193157001Sgshapiro ssl_err = SSL_get_error(clt_ssl, result); 6194157001Sgshapiro i = tls_retry(clt_ssl, rfd, wfd, tlsstart, 6195157001Sgshapiro TimeOuts.to_starttls, ssl_err, "client"); 6196157001Sgshapiro if (i > 0) 6197157001Sgshapiro goto ssl_retry; 619890792Sgshapiro 6199157001Sgshapiro if (LogLevel > 5) 620090792Sgshapiro { 6201244928Sgshapiro unsigned long l; 6202244928Sgshapiro const char *sr; 6203244928Sgshapiro 6204244928Sgshapiro l = ERR_peek_error(); 6205244928Sgshapiro sr = ERR_reason_error_string(l); 6206157001Sgshapiro sm_syslog(LOG_WARNING, NOQID, 6207244928Sgshapiro "STARTTLS=client, error: connect failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d", 6208244928Sgshapiro result, sr == NULL ? "unknown" : sr, ssl_err, 6209244928Sgshapiro errno, i); 6210244928Sgshapiro if (LogLevel > 9) 6211249865Sgshapiro tlslogerr(LOG_WARNING, "client"); 6212110560Sgshapiro } 621390792Sgshapiro 621464562Sgshapiro SSL_free(clt_ssl); 621564562Sgshapiro clt_ssl = NULL; 621664562Sgshapiro return EX_SOFTWARE; 621764562Sgshapiro } 621864562Sgshapiro mci->mci_ssl = clt_ssl; 621990792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 622090792Sgshapiro &mci->mci_macro, true); 622164562Sgshapiro 622290792Sgshapiro /* switch to use TLS... */ 622364562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 622464562Sgshapiro return EX_OK; 622564562Sgshapiro 622664562Sgshapiro /* failure */ 622764562Sgshapiro SSL_free(clt_ssl); 622864562Sgshapiro clt_ssl = NULL; 622964562Sgshapiro return EX_SOFTWARE; 623064562Sgshapiro} 623190792Sgshapiro/* 623264562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 623364562Sgshapiro** 623464562Sgshapiro** Parameters: 623564562Sgshapiro** mci -- the mailer connection info. 623664562Sgshapiro** 623764562Sgshapiro** Returns: 623864562Sgshapiro** success? 623964562Sgshapiro*/ 624090792Sgshapiro 624190792Sgshapirostatic int 624264562Sgshapiroendtlsclt(mci) 624364562Sgshapiro MCI *mci; 624464562Sgshapiro{ 624564562Sgshapiro int r; 624664562Sgshapiro 624764562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 624864562Sgshapiro return EX_OK; 624964562Sgshapiro r = endtls(mci->mci_ssl, "client"); 625064562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 625164562Sgshapiro return r; 625264562Sgshapiro} 625390792Sgshapiro# endif /* STARTTLS */ 625490792Sgshapiro# if STARTTLS || SASL 625590792Sgshapiro/* 625690792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 625764562Sgshapiro** 625864562Sgshapiro** Parameters: 625990792Sgshapiro** e -- envelope. 626090792Sgshapiro** flag -- flag to check in {client_flags} 626164562Sgshapiro** 626264562Sgshapiro** Returns: 626390792Sgshapiro** true iff flag is set. 626464562Sgshapiro*/ 626564562Sgshapiro 626690792Sgshapirostatic bool 626790792Sgshapiroiscltflgset(e, flag) 626890792Sgshapiro ENVELOPE *e; 626990792Sgshapiro int flag; 627064562Sgshapiro{ 627190792Sgshapiro char *p; 627273188Sgshapiro 627390792Sgshapiro p = macvalue(macid("{client_flags}"), e); 627490792Sgshapiro if (p == NULL) 627590792Sgshapiro return false; 627690792Sgshapiro for (; *p != '\0'; p++) 627764562Sgshapiro { 627890792Sgshapiro /* look for just this one flag */ 627990792Sgshapiro if (*p == (char) flag) 628090792Sgshapiro return true; 628164562Sgshapiro } 628290792Sgshapiro return false; 628364562Sgshapiro} 628490792Sgshapiro# endif /* STARTTLS || SASL */ 6285