deliver.c revision 159609
138032Speter/* 2157001Sgshapiro * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 15157001Sgshapiro#include <sm/time.h> 1638032Speter 17159609SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.1003.2.1 2006/05/23 01:32:08 ca Exp $") 1864562Sgshapiro 1938032Speter#if HASSETUSERCONTEXT 2038032Speter# include <login_cap.h> 2164562Sgshapiro#endif /* HASSETUSERCONTEXT */ 2238032Speter 2394334Sgshapiro#if NETINET || NETINET6 2494334Sgshapiro# include <arpa/inet.h> 2594334Sgshapiro#endif /* NETINET || NETINET6 */ 2694334Sgshapiro 2790792Sgshapiro#if STARTTLS || SASL 2864562Sgshapiro# include "sfsasl.h" 2990792Sgshapiro#endif /* STARTTLS || SASL */ 3064562Sgshapiro 3164562Sgshapirostatic int deliver __P((ENVELOPE *, ADDRESS *)); 3264562Sgshapirostatic void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 33141858Sgshapirostatic void mailfiletimeout __P((int)); 34141858Sgshapirostatic void endwaittimeout __P((int)); 3564562Sgshapirostatic int parse_hostsignature __P((char *, char **, MAILER *)); 3664562Sgshapirostatic void sendenvelope __P((ENVELOPE *, int)); 3790792Sgshapiroextern MCI *mci_new __P((SM_RPOOL_T *)); 3890792Sgshapirostatic int coloncmp __P((const char *, const char *)); 3964562Sgshapiro 4090792Sgshapiro#if STARTTLS 4164562Sgshapirostatic int starttls __P((MAILER *, MCI *, ENVELOPE *)); 4290792Sgshapirostatic int endtlsclt __P((MCI *)); 4390792Sgshapiro#endif /* STARTTLS */ 4490792Sgshapiro# if STARTTLS || SASL 4590792Sgshapirostatic bool iscltflgset __P((ENVELOPE *, int)); 4690792Sgshapiro# endif /* STARTTLS || SASL */ 4738032Speter 4838032Speter/* 4938032Speter** SENDALL -- actually send all the messages. 5038032Speter** 5138032Speter** Parameters: 5238032Speter** e -- the envelope to send. 5338032Speter** mode -- the delivery mode to use. If SM_DEFAULT, use 5438032Speter** the current e->e_sendmode. 5538032Speter** 5638032Speter** Returns: 5738032Speter** none. 5838032Speter** 5938032Speter** Side Effects: 6038032Speter** Scans the send lists and sends everything it finds. 6138032Speter** Delivers any appropriate error messages. 6238032Speter** If we are running in a non-interactive mode, takes the 6338032Speter** appropriate action. 6438032Speter*/ 6538032Speter 6638032Spetervoid 6738032Spetersendall(e, mode) 6838032Speter ENVELOPE *e; 6938032Speter int mode; 7038032Speter{ 7138032Speter register ADDRESS *q; 7238032Speter char *owner; 7338032Speter int otherowners; 7464562Sgshapiro int save_errno; 7538032Speter register ENVELOPE *ee; 7638032Speter ENVELOPE *splitenv = NULL; 7738032Speter int oldverbose = Verbose; 7890792Sgshapiro bool somedeliveries = false, expensive = false; 7938032Speter pid_t pid; 8038032Speter 8138032Speter /* 8238032Speter ** If this message is to be discarded, don't bother sending 8338032Speter ** the message at all. 8438032Speter */ 8538032Speter 8638032Speter if (bitset(EF_DISCARD, e->e_flags)) 8738032Speter { 8838032Speter if (tTd(13, 1)) 8990792Sgshapiro sm_dprintf("sendall: discarding id %s\n", e->e_id); 9038032Speter e->e_flags |= EF_CLRQUEUE; 9190792Sgshapiro if (LogLevel > 9) 9290792Sgshapiro logundelrcpts(e, "discarded", 9, true); 9390792Sgshapiro else if (LogLevel > 4) 9438032Speter sm_syslog(LOG_INFO, e->e_id, "discarded"); 9590792Sgshapiro markstats(e, NULL, STATS_REJECT); 9638032Speter return; 9738032Speter } 9838032Speter 9938032Speter /* 10038032Speter ** If we have had global, fatal errors, don't bother sending 10138032Speter ** the message at all if we are in SMTP mode. Local errors 10238032Speter ** (e.g., a single address failing) will still cause the other 10338032Speter ** addresses to be sent. 10438032Speter */ 10538032Speter 10638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 10738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 10838032Speter { 10938032Speter e->e_flags |= EF_CLRQUEUE; 11038032Speter return; 11138032Speter } 11238032Speter 11338032Speter /* determine actual delivery mode */ 11438032Speter if (mode == SM_DEFAULT) 11538032Speter { 11638032Speter mode = e->e_sendmode; 11738032Speter if (mode != SM_VERIFY && mode != SM_DEFER && 11838032Speter shouldqueue(e->e_msgpriority, e->e_ctime)) 11938032Speter mode = SM_QUEUE; 12038032Speter } 12138032Speter 12238032Speter if (tTd(13, 1)) 12338032Speter { 12490792Sgshapiro sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 12538032Speter mode, e->e_id); 126132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 12790792Sgshapiro sm_dprintf("\te_flags = "); 12838032Speter printenvflags(e); 12990792Sgshapiro sm_dprintf("sendqueue:\n"); 130132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 13138032Speter } 13238032Speter 13338032Speter /* 13438032Speter ** Do any preprocessing necessary for the mode we are running. 13538032Speter ** Check to make sure the hop count is reasonable. 13638032Speter ** Delete sends to the sender in mailing lists. 13738032Speter */ 13838032Speter 13938032Speter CurEnv = e; 14038032Speter if (tTd(62, 1)) 14138032Speter checkfds(NULL); 14238032Speter 14338032Speter if (e->e_hopcount > MaxHopCount) 14438032Speter { 14577349Sgshapiro char *recip; 14677349Sgshapiro 14777349Sgshapiro if (e->e_sendqueue != NULL && 14877349Sgshapiro e->e_sendqueue->q_paddr != NULL) 14977349Sgshapiro recip = e->e_sendqueue->q_paddr; 15077349Sgshapiro else 15177349Sgshapiro recip = "(nobody)"; 15277349Sgshapiro 15338032Speter errno = 0; 15490792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), false); 15538032Speter e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; 15664562Sgshapiro ExitStat = EX_UNAVAILABLE; 15777349Sgshapiro syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s", 15877349Sgshapiro e->e_hopcount, MaxHopCount, e->e_from.q_paddr, 15977349Sgshapiro RealHostName == NULL ? "localhost" : RealHostName, 16077349Sgshapiro recip); 16164562Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 16264562Sgshapiro { 16364562Sgshapiro if (QS_IS_DEAD(q->q_state)) 16464562Sgshapiro continue; 16564562Sgshapiro q->q_state = QS_BADADDR; 16664562Sgshapiro q->q_status = "5.4.6"; 16777349Sgshapiro q->q_rstatus = "554 5.4.6 Too many hops"; 16864562Sgshapiro } 16938032Speter return; 17038032Speter } 17138032Speter 17238032Speter /* 17338032Speter ** Do sender deletion. 17438032Speter ** 17564562Sgshapiro ** If the sender should be queued up, skip this. 17638032Speter ** This can happen if the name server is hosed when you 17738032Speter ** are trying to send mail. The result is that the sender 17838032Speter ** is instantiated in the queue as a recipient. 17938032Speter */ 18038032Speter 18138032Speter if (!bitset(EF_METOO, e->e_flags) && 18264562Sgshapiro !QS_IS_QUEUEUP(e->e_from.q_state)) 18338032Speter { 18438032Speter if (tTd(13, 5)) 18538032Speter { 18690792Sgshapiro sm_dprintf("sendall: QS_SENDER "); 187132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 18838032Speter } 18964562Sgshapiro e->e_from.q_state = QS_SENDER; 19038032Speter (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); 19138032Speter } 19238032Speter 19338032Speter /* 19438032Speter ** Handle alias owners. 19538032Speter ** 19638032Speter ** We scan up the q_alias chain looking for owners. 19738032Speter ** We discard owners that are the same as the return path. 19838032Speter */ 19938032Speter 20038032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 20138032Speter { 20238032Speter register struct address *a; 20338032Speter 20438032Speter for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) 20538032Speter continue; 20638032Speter if (a != NULL) 20738032Speter q->q_owner = a->q_owner; 20838032Speter 20938032Speter if (q->q_owner != NULL && 21064562Sgshapiro !QS_IS_DEAD(q->q_state) && 21138032Speter strcmp(q->q_owner, e->e_from.q_paddr) == 0) 21238032Speter q->q_owner = NULL; 21338032Speter } 21438032Speter 21538032Speter if (tTd(13, 25)) 21638032Speter { 21790792Sgshapiro sm_dprintf("\nAfter first owner pass, sendq =\n"); 218132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 21938032Speter } 22038032Speter 22138032Speter owner = ""; 22238032Speter otherowners = 1; 22338032Speter while (owner != NULL && otherowners > 0) 22438032Speter { 22538032Speter if (tTd(13, 28)) 22690792Sgshapiro sm_dprintf("owner = \"%s\", otherowners = %d\n", 22790792Sgshapiro owner, otherowners); 22838032Speter owner = NULL; 22938032Speter otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; 23038032Speter 23138032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 23238032Speter { 23338032Speter if (tTd(13, 30)) 23438032Speter { 23590792Sgshapiro sm_dprintf("Checking "); 236132943Sgshapiro printaddr(sm_debug_file(), q, false); 23738032Speter } 23864562Sgshapiro if (QS_IS_DEAD(q->q_state)) 23938032Speter { 24038032Speter if (tTd(13, 30)) 24190792Sgshapiro sm_dprintf(" ... QS_IS_DEAD\n"); 24238032Speter continue; 24338032Speter } 24438032Speter if (tTd(13, 29) && !tTd(13, 30)) 24538032Speter { 24690792Sgshapiro sm_dprintf("Checking "); 247132943Sgshapiro printaddr(sm_debug_file(), q, false); 24838032Speter } 24938032Speter 25038032Speter if (q->q_owner != NULL) 25138032Speter { 25238032Speter if (owner == NULL) 25338032Speter { 25438032Speter if (tTd(13, 40)) 25590792Sgshapiro sm_dprintf(" ... First owner = \"%s\"\n", 25690792Sgshapiro q->q_owner); 25738032Speter owner = q->q_owner; 25838032Speter } 25938032Speter else if (owner != q->q_owner) 26038032Speter { 26138032Speter if (strcmp(owner, q->q_owner) == 0) 26238032Speter { 26338032Speter if (tTd(13, 40)) 26490792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 26590792Sgshapiro owner); 26638032Speter 26738032Speter /* make future comparisons cheap */ 26838032Speter q->q_owner = owner; 26938032Speter } 27038032Speter else 27138032Speter { 27238032Speter if (tTd(13, 40)) 27390792Sgshapiro sm_dprintf(" ... Another owner \"%s\"\n", 27490792Sgshapiro q->q_owner); 27538032Speter otherowners++; 27638032Speter } 27738032Speter owner = q->q_owner; 27838032Speter } 27938032Speter else if (tTd(13, 40)) 28090792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 28190792Sgshapiro owner); 28238032Speter } 28338032Speter else 28438032Speter { 28538032Speter if (tTd(13, 40)) 28690792Sgshapiro sm_dprintf(" ... Null owner\n"); 28738032Speter otherowners++; 28838032Speter } 28938032Speter 29064562Sgshapiro if (QS_IS_BADADDR(q->q_state)) 29164562Sgshapiro { 29264562Sgshapiro if (tTd(13, 30)) 29390792Sgshapiro sm_dprintf(" ... QS_IS_BADADDR\n"); 29464562Sgshapiro continue; 29564562Sgshapiro } 29664562Sgshapiro 29764562Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 29864562Sgshapiro { 29964562Sgshapiro MAILER *m = q->q_mailer; 30064562Sgshapiro 30164562Sgshapiro /* 30264562Sgshapiro ** If we have temporary address failures 30364562Sgshapiro ** (e.g., dns failure) and a fallback MX is 30464562Sgshapiro ** set, send directly to the fallback MX host. 30564562Sgshapiro */ 30664562Sgshapiro 307132943Sgshapiro if (FallbackMX != NULL && 308132943Sgshapiro !wordinclass(FallbackMX, 'w') && 30964562Sgshapiro mode != SM_VERIFY && 31090792Sgshapiro !bitnset(M_NOMX, m->m_flags) && 31190792Sgshapiro strcmp(m->m_mailer, "[IPC]") == 0 && 31264562Sgshapiro m->m_argv[0] != NULL && 31390792Sgshapiro strcmp(m->m_argv[0], "TCP") == 0) 31464562Sgshapiro { 31564562Sgshapiro int len; 31664562Sgshapiro char *p; 31764562Sgshapiro 31864562Sgshapiro if (tTd(13, 30)) 319132943Sgshapiro sm_dprintf(" ... FallbackMX\n"); 32064562Sgshapiro 321132943Sgshapiro len = strlen(FallbackMX) + 1; 32290792Sgshapiro p = sm_rpool_malloc_x(e->e_rpool, len); 323132943Sgshapiro (void) sm_strlcpy(p, FallbackMX, len); 32464562Sgshapiro q->q_state = QS_OK; 32564562Sgshapiro q->q_host = p; 32664562Sgshapiro } 32764562Sgshapiro else 32864562Sgshapiro { 32964562Sgshapiro if (tTd(13, 30)) 33090792Sgshapiro sm_dprintf(" ... QS_IS_QUEUEUP\n"); 33164562Sgshapiro continue; 33264562Sgshapiro } 33364562Sgshapiro } 33464562Sgshapiro 33538032Speter /* 33638032Speter ** If this mailer is expensive, and if we don't 33738032Speter ** want to make connections now, just mark these 33838032Speter ** addresses and return. This is useful if we 33938032Speter ** want to batch connections to reduce load. This 34038032Speter ** will cause the messages to be queued up, and a 34138032Speter ** daemon will come along to send the messages later. 34238032Speter */ 34338032Speter 34464562Sgshapiro if (NoConnect && !Verbose && 34564562Sgshapiro bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) 34638032Speter { 34738032Speter if (tTd(13, 30)) 34890792Sgshapiro sm_dprintf(" ... expensive\n"); 34964562Sgshapiro q->q_state = QS_QUEUEUP; 35090792Sgshapiro expensive = true; 35138032Speter } 35264562Sgshapiro else if (bitnset(M_HOLD, q->q_mailer->m_flags) && 35364562Sgshapiro QueueLimitId == NULL && 35464562Sgshapiro QueueLimitSender == NULL && 35564562Sgshapiro QueueLimitRecipient == NULL) 35638032Speter { 35738032Speter if (tTd(13, 30)) 35890792Sgshapiro sm_dprintf(" ... hold\n"); 35964562Sgshapiro q->q_state = QS_QUEUEUP; 36090792Sgshapiro expensive = true; 36138032Speter } 36290792Sgshapiro else if (QueueMode != QM_QUARANTINE && 36390792Sgshapiro e->e_quarmsg != NULL) 36490792Sgshapiro { 36590792Sgshapiro if (tTd(13, 30)) 36690792Sgshapiro sm_dprintf(" ... quarantine: %s\n", 36790792Sgshapiro e->e_quarmsg); 36890792Sgshapiro q->q_state = QS_QUEUEUP; 36990792Sgshapiro expensive = true; 37090792Sgshapiro } 37138032Speter else 37238032Speter { 37338032Speter if (tTd(13, 30)) 37490792Sgshapiro sm_dprintf(" ... deliverable\n"); 37590792Sgshapiro somedeliveries = true; 37638032Speter } 37738032Speter } 37838032Speter 37938032Speter if (owner != NULL && otherowners > 0) 38038032Speter { 38138032Speter /* 38238032Speter ** Split this envelope into two. 38338032Speter */ 38438032Speter 38590792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 38690792Sgshapiro sizeof *ee); 38790792Sgshapiro STRUCTCOPY(*e, *ee); 38864562Sgshapiro ee->e_message = NULL; 38938032Speter ee->e_id = NULL; 39064562Sgshapiro assign_queueid(ee); 39138032Speter 39238032Speter if (tTd(13, 1)) 39390792Sgshapiro sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 39490792Sgshapiro e->e_id, ee->e_id, owner, 39590792Sgshapiro otherowners); 39638032Speter 39790792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 39890792Sgshapiro ee->e_sendqueue = copyqueue(e->e_sendqueue, 39990792Sgshapiro ee->e_rpool); 40090792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, 40190792Sgshapiro ee->e_rpool); 40238032Speter ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 40338032Speter ee->e_flags |= EF_NORECEIPT; 40490792Sgshapiro setsender(owner, ee, NULL, '\0', true); 40538032Speter if (tTd(13, 5)) 40638032Speter { 40790792Sgshapiro sm_dprintf("sendall(split): QS_SENDER "); 408132943Sgshapiro printaddr(sm_debug_file(), &ee->e_from, false); 40938032Speter } 41064562Sgshapiro ee->e_from.q_state = QS_SENDER; 41138032Speter ee->e_dfp = NULL; 41264562Sgshapiro ee->e_lockfp = NULL; 41338032Speter ee->e_xfp = NULL; 41490792Sgshapiro ee->e_qgrp = e->e_qgrp; 41590792Sgshapiro ee->e_qdir = e->e_qdir; 41638032Speter ee->e_errormode = EM_MAIL; 41738032Speter ee->e_sibling = splitenv; 41864562Sgshapiro ee->e_statmsg = NULL; 41990792Sgshapiro if (e->e_quarmsg != NULL) 42090792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 42190792Sgshapiro e->e_quarmsg); 42238032Speter splitenv = ee; 42338032Speter 42438032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 42538032Speter { 42638032Speter if (q->q_owner == owner) 42738032Speter { 42864562Sgshapiro q->q_state = QS_CLONED; 42938032Speter if (tTd(13, 6)) 43090792Sgshapiro sm_dprintf("\t... stripping %s from original envelope\n", 43190792Sgshapiro q->q_paddr); 43238032Speter } 43338032Speter } 43438032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 43538032Speter { 43638032Speter if (q->q_owner != owner) 43738032Speter { 43864562Sgshapiro q->q_state = QS_CLONED; 43938032Speter if (tTd(13, 6)) 44090792Sgshapiro sm_dprintf("\t... dropping %s from cloned envelope\n", 44190792Sgshapiro q->q_paddr); 44238032Speter } 44338032Speter else 44438032Speter { 44538032Speter /* clear DSN parameters */ 44638032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 44738032Speter q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 44838032Speter if (tTd(13, 6)) 44990792Sgshapiro sm_dprintf("\t... moving %s to cloned envelope\n", 45090792Sgshapiro q->q_paddr); 45138032Speter } 45238032Speter } 45338032Speter 45438032Speter if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 45590792Sgshapiro dup_queue_file(e, ee, DATAFL_LETTER); 45664562Sgshapiro 45764562Sgshapiro /* 45864562Sgshapiro ** Give the split envelope access to the parent 45964562Sgshapiro ** transcript file for errors obtained while 46064562Sgshapiro ** processing the recipients (done before the 46164562Sgshapiro ** envelope splitting). 46264562Sgshapiro */ 46364562Sgshapiro 46464562Sgshapiro if (e->e_xfp != NULL) 46590792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 46664562Sgshapiro 46764562Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 46864562Sgshapiro if (ee->e_xfp == NULL) 46964562Sgshapiro openxscript(ee); 47064562Sgshapiro 47142575Speter if (mode != SM_VERIFY && LogLevel > 4) 47290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 47390792Sgshapiro "%s: clone: owner=%s", 47490792Sgshapiro ee->e_id, owner); 47538032Speter } 47638032Speter } 47738032Speter 47838032Speter if (owner != NULL) 47938032Speter { 48090792Sgshapiro setsender(owner, e, NULL, '\0', true); 48138032Speter if (tTd(13, 5)) 48238032Speter { 48390792Sgshapiro sm_dprintf("sendall(owner): QS_SENDER "); 484132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 48538032Speter } 48664562Sgshapiro e->e_from.q_state = QS_SENDER; 48738032Speter e->e_errormode = EM_MAIL; 48838032Speter e->e_flags |= EF_NORECEIPT; 48938032Speter e->e_flags &= ~EF_FATALERRS; 49038032Speter } 49138032Speter 49238032Speter /* if nothing to be delivered, just queue up everything */ 49390792Sgshapiro if (!somedeliveries && !WILL_BE_QUEUED(mode) && 49438032Speter mode != SM_VERIFY) 49538032Speter { 49690792Sgshapiro time_t now; 49771345Sgshapiro 49838032Speter if (tTd(13, 29)) 49990792Sgshapiro sm_dprintf("No deliveries: auto-queuing\n"); 50038032Speter mode = SM_QUEUE; 50190792Sgshapiro now = curtime(); 50238032Speter 50338032Speter /* treat this as a delivery in terms of counting tries */ 50471345Sgshapiro e->e_dtime = now; 50538032Speter if (!expensive) 50638032Speter e->e_ntries++; 50738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 50838032Speter { 50971345Sgshapiro ee->e_dtime = now; 51038032Speter if (!expensive) 51138032Speter ee->e_ntries++; 51238032Speter } 51338032Speter } 51438032Speter 51590792Sgshapiro if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 516132943Sgshapiro (mode != SM_VERIFY && 517132943Sgshapiro (SuperSafe == SAFE_REALLY || 518132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER))) && 51938032Speter (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 52038032Speter { 52190792Sgshapiro bool msync; 52290792Sgshapiro 52366494Sgshapiro /* 52466494Sgshapiro ** Be sure everything is instantiated in the queue. 52566494Sgshapiro ** Split envelopes first in case the machine crashes. 52666494Sgshapiro ** If the original were done first, we may lose 52766494Sgshapiro ** recipients. 52866494Sgshapiro */ 52966494Sgshapiro 53090792Sgshapiro#if !HASFLOCK 53190792Sgshapiro msync = false; 53290792Sgshapiro#else /* !HASFLOCK */ 53390792Sgshapiro msync = mode == SM_FORK; 53490792Sgshapiro#endif /* !HASFLOCK */ 53590792Sgshapiro 53638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 53790792Sgshapiro queueup(ee, WILL_BE_QUEUED(mode), msync); 53890792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), msync); 53938032Speter } 54038032Speter 54138032Speter if (tTd(62, 10)) 54238032Speter checkfds("after envelope splitting"); 54338032Speter 54438032Speter /* 54538032Speter ** If we belong in background, fork now. 54638032Speter */ 54738032Speter 54838032Speter if (tTd(13, 20)) 54938032Speter { 55090792Sgshapiro sm_dprintf("sendall: final mode = %c\n", mode); 55138032Speter if (tTd(13, 21)) 55238032Speter { 55390792Sgshapiro sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 55490792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 55590792Sgshapiro e->e_id, e->e_from.q_paddr); 556132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 55738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 55838032Speter { 55990792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 56090792Sgshapiro ee->e_id, ee->e_from.q_paddr); 561132943Sgshapiro printaddr(sm_debug_file(), ee->e_sendqueue, true); 56238032Speter } 56390792Sgshapiro sm_dprintf("==========================================================\n\n"); 56438032Speter } 56538032Speter } 56638032Speter switch (mode) 56738032Speter { 56838032Speter case SM_VERIFY: 56938032Speter Verbose = 2; 57038032Speter break; 57138032Speter 57238032Speter case SM_QUEUE: 57338032Speter case SM_DEFER: 57464562Sgshapiro#if HASFLOCK 57538032Speter queueonly: 57664562Sgshapiro#endif /* HASFLOCK */ 57738032Speter if (e->e_nrcpts > 0) 57838032Speter e->e_flags |= EF_INQUEUE; 57990792Sgshapiro dropenvelope(e, splitenv != NULL, true); 58038032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 58138032Speter { 58238032Speter if (ee->e_nrcpts > 0) 58338032Speter ee->e_flags |= EF_INQUEUE; 58490792Sgshapiro dropenvelope(ee, false, true); 58538032Speter } 58638032Speter return; 58738032Speter 58838032Speter case SM_FORK: 58938032Speter if (e->e_xfp != NULL) 59090792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 59138032Speter 59264562Sgshapiro#if !HASFLOCK 59338032Speter /* 59438032Speter ** Since fcntl locking has the interesting semantic that 59538032Speter ** the lock is owned by a process, not by an open file 59638032Speter ** descriptor, we have to flush this to the queue, and 59738032Speter ** then restart from scratch in the child. 59838032Speter */ 59938032Speter 60038032Speter { 60138032Speter /* save id for future use */ 60238032Speter char *qid = e->e_id; 60338032Speter 60438032Speter /* now drop the envelope in the parent */ 60538032Speter e->e_flags |= EF_INQUEUE; 60690792Sgshapiro dropenvelope(e, splitenv != NULL, false); 60738032Speter 60838032Speter /* arrange to reacquire lock after fork */ 60938032Speter e->e_id = qid; 61038032Speter } 61138032Speter 61238032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 61338032Speter { 61438032Speter /* save id for future use */ 61538032Speter char *qid = ee->e_id; 61638032Speter 61738032Speter /* drop envelope in parent */ 61838032Speter ee->e_flags |= EF_INQUEUE; 61990792Sgshapiro dropenvelope(ee, false, false); 62038032Speter 62138032Speter /* and save qid for reacquisition */ 62238032Speter ee->e_id = qid; 62338032Speter } 624132943Sgshapiro 62564562Sgshapiro#endif /* !HASFLOCK */ 62638032Speter 62764562Sgshapiro /* 62864562Sgshapiro ** Since the delivery may happen in a child and the parent 62964562Sgshapiro ** does not wait, the parent may close the maps thereby 63064562Sgshapiro ** removing any shared memory used by the map. Therefore, 63164562Sgshapiro ** close the maps now so the child will dynamically open 63264562Sgshapiro ** them if necessary. 63364562Sgshapiro */ 63464562Sgshapiro 63590792Sgshapiro closemaps(false); 63664562Sgshapiro 63738032Speter pid = fork(); 63838032Speter if (pid < 0) 63938032Speter { 64064562Sgshapiro syserr("deliver: fork 1"); 64164562Sgshapiro#if HASFLOCK 64238032Speter goto queueonly; 64364562Sgshapiro#else /* HASFLOCK */ 64438032Speter e->e_id = NULL; 64538032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 64638032Speter ee->e_id = NULL; 64738032Speter return; 64864562Sgshapiro#endif /* HASFLOCK */ 64938032Speter } 65038032Speter else if (pid > 0) 65138032Speter { 65264562Sgshapiro#if HASFLOCK 65338032Speter /* be sure we leave the temp files to our child */ 65438032Speter /* close any random open files in the envelope */ 65538032Speter closexscript(e); 65638032Speter if (e->e_dfp != NULL) 65790792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 65838032Speter e->e_dfp = NULL; 65938032Speter e->e_flags &= ~EF_HAS_DF; 66038032Speter 66138032Speter /* can't call unlockqueue to avoid unlink of xfp */ 66238032Speter if (e->e_lockfp != NULL) 66390792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 66464562Sgshapiro else 66564562Sgshapiro syserr("%s: sendall: null lockfp", e->e_id); 66638032Speter e->e_lockfp = NULL; 66764562Sgshapiro#endif /* HASFLOCK */ 66838032Speter 66938032Speter /* make sure the parent doesn't own the envelope */ 67038032Speter e->e_id = NULL; 67138032Speter 67290792Sgshapiro#if USE_DOUBLE_FORK 67338032Speter /* catch intermediate zombie */ 67438032Speter (void) waitfor(pid); 67590792Sgshapiro#endif /* USE_DOUBLE_FORK */ 67638032Speter return; 67738032Speter } 67838032Speter 67977349Sgshapiro /* Reset global flags */ 68077349Sgshapiro RestartRequest = NULL; 68190792Sgshapiro RestartWorkGroup = false; 68277349Sgshapiro ShutdownRequest = NULL; 68377349Sgshapiro PendingSignal = 0; 68477349Sgshapiro 68566494Sgshapiro /* 68690792Sgshapiro ** Initialize exception stack and default exception 68790792Sgshapiro ** handler for child process. 68890792Sgshapiro */ 68990792Sgshapiro 69090792Sgshapiro sm_exc_newthread(fatal_error); 69190792Sgshapiro 69290792Sgshapiro /* 69366494Sgshapiro ** Since we have accepted responsbility for the message, 69466494Sgshapiro ** change the SIGTERM handler. intsig() (the old handler) 69566494Sgshapiro ** would remove the envelope if this was a command line 69666494Sgshapiro ** message submission. 69766494Sgshapiro */ 69866494Sgshapiro 69990792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 70066494Sgshapiro 70190792Sgshapiro#if USE_DOUBLE_FORK 70238032Speter /* double fork to avoid zombies */ 70338032Speter pid = fork(); 70438032Speter if (pid > 0) 70538032Speter exit(EX_OK); 70664562Sgshapiro save_errno = errno; 70790792Sgshapiro#endif /* USE_DOUBLE_FORK */ 70838032Speter 70990792Sgshapiro CurrentPid = getpid(); 71090792Sgshapiro 71138032Speter /* be sure we are immune from the terminal */ 71238032Speter disconnect(2, e); 71364562Sgshapiro clearstats(); 71438032Speter 71538032Speter /* prevent parent from waiting if there was an error */ 71638032Speter if (pid < 0) 71738032Speter { 71864562Sgshapiro errno = save_errno; 71964562Sgshapiro syserr("deliver: fork 2"); 72064562Sgshapiro#if HASFLOCK 72138032Speter e->e_flags |= EF_INQUEUE; 72264562Sgshapiro#else /* HASFLOCK */ 72338032Speter e->e_id = NULL; 72464562Sgshapiro#endif /* HASFLOCK */ 72590792Sgshapiro finis(true, true, ExitStat); 72638032Speter } 72738032Speter 72838032Speter /* be sure to give error messages in child */ 72990792Sgshapiro QuickAbort = false; 73038032Speter 73138032Speter /* 73238032Speter ** Close any cached connections. 73338032Speter ** 73438032Speter ** We don't send the QUIT protocol because the parent 73538032Speter ** still knows about the connection. 73638032Speter ** 73738032Speter ** This should only happen when delivering an error 73838032Speter ** message. 73938032Speter */ 74038032Speter 74190792Sgshapiro mci_flush(false, NULL); 74238032Speter 74364562Sgshapiro#if HASFLOCK 74438032Speter break; 74564562Sgshapiro#else /* HASFLOCK */ 74638032Speter 74738032Speter /* 74838032Speter ** Now reacquire and run the various queue files. 74938032Speter */ 75038032Speter 75138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 75238032Speter { 75338032Speter ENVELOPE *sibling = ee->e_sibling; 75438032Speter 75590792Sgshapiro (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 75690792Sgshapiro false, false, ee); 75738032Speter ee->e_sibling = sibling; 75838032Speter } 75990792Sgshapiro (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 76090792Sgshapiro false, false, e); 76190792Sgshapiro finis(true, true, ExitStat); 76264562Sgshapiro#endif /* HASFLOCK */ 76338032Speter } 76438032Speter 76538032Speter sendenvelope(e, mode); 76690792Sgshapiro dropenvelope(e, true, true); 76738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 76838032Speter { 76938032Speter CurEnv = ee; 77038032Speter if (mode != SM_VERIFY) 77138032Speter openxscript(ee); 77238032Speter sendenvelope(ee, mode); 77390792Sgshapiro dropenvelope(ee, true, true); 77438032Speter } 77538032Speter CurEnv = e; 77638032Speter 77738032Speter Verbose = oldverbose; 77838032Speter if (mode == SM_FORK) 77990792Sgshapiro finis(true, true, ExitStat); 78038032Speter} 78138032Speter 78264562Sgshapirostatic void 78338032Spetersendenvelope(e, mode) 78438032Speter register ENVELOPE *e; 78538032Speter int mode; 78638032Speter{ 78738032Speter register ADDRESS *q; 78838032Speter bool didany; 78938032Speter 79038032Speter if (tTd(13, 10)) 79190792Sgshapiro sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 79290792Sgshapiro e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 79390792Sgshapiro e->e_flags); 79438032Speter if (LogLevel > 80) 79538032Speter sm_syslog(LOG_DEBUG, e->e_id, 79664562Sgshapiro "sendenvelope, flags=0x%lx", 79764562Sgshapiro e->e_flags); 79838032Speter 79938032Speter /* 80038032Speter ** If we have had global, fatal errors, don't bother sending 80138032Speter ** the message at all if we are in SMTP mode. Local errors 80238032Speter ** (e.g., a single address failing) will still cause the other 80338032Speter ** addresses to be sent. 80438032Speter */ 80538032Speter 80638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 80738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 80838032Speter { 80938032Speter e->e_flags |= EF_CLRQUEUE; 81038032Speter return; 81138032Speter } 81238032Speter 81390792Sgshapiro /* 81490792Sgshapiro ** Don't attempt deliveries if we want to bounce now 81590792Sgshapiro ** or if deliver-by time is exceeded. 81690792Sgshapiro */ 81790792Sgshapiro 81864562Sgshapiro if (!bitset(EF_RESPONSE, e->e_flags) && 81990792Sgshapiro (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 82090792Sgshapiro (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 82190792Sgshapiro curtime() > e->e_ctime + e->e_deliver_by))) 82264562Sgshapiro return; 82364562Sgshapiro 82438032Speter /* 82538032Speter ** Run through the list and send everything. 82638032Speter ** 82738032Speter ** Set EF_GLOBALERRS so that error messages during delivery 82838032Speter ** result in returned mail. 82938032Speter */ 83038032Speter 83138032Speter e->e_nsent = 0; 83238032Speter e->e_flags |= EF_GLOBALERRS; 83364562Sgshapiro 83490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 83590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 83690792Sgshapiro didany = false; 83738032Speter 83890792Sgshapiro if (!bitset(EF_SPLIT, e->e_flags)) 83990792Sgshapiro { 84090792Sgshapiro ENVELOPE *oldsib; 84190792Sgshapiro ENVELOPE *ee; 84290792Sgshapiro 84390792Sgshapiro /* 84490792Sgshapiro ** Save old sibling and set it to NULL to avoid 84590792Sgshapiro ** queueing up the same envelopes again. 84690792Sgshapiro ** This requires that envelopes in that list have 84790792Sgshapiro ** been take care of before (or at some other place). 84890792Sgshapiro */ 84990792Sgshapiro 85090792Sgshapiro oldsib = e->e_sibling; 85190792Sgshapiro e->e_sibling = NULL; 85290792Sgshapiro if (!split_by_recipient(e) && 85390792Sgshapiro bitset(EF_FATALERRS, e->e_flags)) 85490792Sgshapiro { 85590792Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 85690792Sgshapiro e->e_flags |= EF_CLRQUEUE; 85790792Sgshapiro return; 85890792Sgshapiro } 85990792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86090792Sgshapiro queueup(ee, false, true); 86190792Sgshapiro 86290792Sgshapiro /* clean up */ 86390792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86490792Sgshapiro { 86590792Sgshapiro /* now unlock the job */ 86690792Sgshapiro closexscript(ee); 86790792Sgshapiro unlockqueue(ee); 86890792Sgshapiro 86990792Sgshapiro /* this envelope is marked unused */ 87090792Sgshapiro if (ee->e_dfp != NULL) 87190792Sgshapiro { 87290792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 87390792Sgshapiro ee->e_dfp = NULL; 87490792Sgshapiro } 87590792Sgshapiro ee->e_id = NULL; 87690792Sgshapiro ee->e_flags &= ~EF_HAS_DF; 87790792Sgshapiro } 87890792Sgshapiro e->e_sibling = oldsib; 87990792Sgshapiro } 88090792Sgshapiro 88138032Speter /* now run through the queue */ 88238032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 88338032Speter { 88438032Speter#if XDEBUG 88538032Speter char wbuf[MAXNAME + 20]; 88638032Speter 88790792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", 88890792Sgshapiro MAXNAME, q->q_paddr); 88938032Speter checkfd012(wbuf); 89064562Sgshapiro#endif /* XDEBUG */ 89138032Speter if (mode == SM_VERIFY) 89238032Speter { 89338032Speter e->e_to = q->q_paddr; 89464562Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 89538032Speter { 89638032Speter if (q->q_host != NULL && q->q_host[0] != '\0') 89738032Speter message("deliverable: mailer %s, host %s, user %s", 89838032Speter q->q_mailer->m_name, 89938032Speter q->q_host, 90038032Speter q->q_user); 90138032Speter else 90238032Speter message("deliverable: mailer %s, user %s", 90338032Speter q->q_mailer->m_name, 90438032Speter q->q_user); 90538032Speter } 90638032Speter } 90764562Sgshapiro else if (QS_IS_OK(q->q_state)) 90838032Speter { 90938032Speter /* 91038032Speter ** Checkpoint the send list every few addresses 91138032Speter */ 91238032Speter 91366494Sgshapiro if (CheckpointInterval > 0 && 91466494Sgshapiro e->e_nsent >= CheckpointInterval) 91538032Speter { 91690792Sgshapiro queueup(e, false, false); 91738032Speter e->e_nsent = 0; 91838032Speter } 91938032Speter (void) deliver(e, q); 92090792Sgshapiro didany = true; 92138032Speter } 92238032Speter } 92338032Speter if (didany) 92438032Speter { 92538032Speter e->e_dtime = curtime(); 92638032Speter e->e_ntries++; 92738032Speter } 92838032Speter 92938032Speter#if XDEBUG 93038032Speter checkfd012("end of sendenvelope"); 93164562Sgshapiro#endif /* XDEBUG */ 93238032Speter} 93390792Sgshapiro 93490792Sgshapiro#if REQUIRES_DIR_FSYNC 93590792Sgshapiro/* 93690792Sgshapiro** SYNC_DIR -- fsync a directory based on a filename 93790792Sgshapiro** 93890792Sgshapiro** Parameters: 93990792Sgshapiro** filename -- path of file 94090792Sgshapiro** panic -- panic? 94190792Sgshapiro** 94290792Sgshapiro** Returns: 94390792Sgshapiro** none 94490792Sgshapiro*/ 94590792Sgshapiro 94690792Sgshapirovoid 94790792Sgshapirosync_dir(filename, panic) 94890792Sgshapiro char *filename; 94990792Sgshapiro bool panic; 95090792Sgshapiro{ 95190792Sgshapiro int dirfd; 95290792Sgshapiro char *dirp; 95390792Sgshapiro char dir[MAXPATHLEN]; 95490792Sgshapiro 955110560Sgshapiro if (!RequiresDirfsync) 956110560Sgshapiro return; 957110560Sgshapiro 95890792Sgshapiro /* filesystems which require the directory be synced */ 95990792Sgshapiro dirp = strrchr(filename, '/'); 96090792Sgshapiro if (dirp != NULL) 96190792Sgshapiro { 96290792Sgshapiro if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir) 96390792Sgshapiro return; 96490792Sgshapiro dir[dirp - filename] = '\0'; 96590792Sgshapiro dirp = dir; 96690792Sgshapiro } 96790792Sgshapiro else 96890792Sgshapiro dirp = "."; 96990792Sgshapiro dirfd = open(dirp, O_RDONLY, 0700); 97090792Sgshapiro if (tTd(40,32)) 97190792Sgshapiro sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 97290792Sgshapiro dirp, dirfd); 97390792Sgshapiro if (dirfd >= 0) 97490792Sgshapiro { 97590792Sgshapiro if (fsync(dirfd) < 0) 97690792Sgshapiro { 97790792Sgshapiro if (panic) 97890792Sgshapiro syserr("!sync_dir: cannot fsync directory %s", 97990792Sgshapiro dirp); 98090792Sgshapiro else if (LogLevel > 1) 98190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 98290792Sgshapiro "sync_dir: cannot fsync directory %s: %s", 98390792Sgshapiro dirp, sm_errstring(errno)); 98490792Sgshapiro } 98590792Sgshapiro (void) close(dirfd); 98690792Sgshapiro } 98790792Sgshapiro} 98890792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */ 98990792Sgshapiro/* 99038032Speter** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 99138032Speter** 99238032Speter** Parameters: 99338032Speter** e -- the existing envelope 99438032Speter** ee -- the new envelope 99590792Sgshapiro** type -- the queue file type (e.g., DATAFL_LETTER) 99638032Speter** 99738032Speter** Returns: 99838032Speter** none 99938032Speter*/ 100038032Speter 100164562Sgshapirostatic void 100238032Speterdup_queue_file(e, ee, type) 100390792Sgshapiro ENVELOPE *e, *ee; 100438032Speter int type; 100538032Speter{ 100664562Sgshapiro char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 100738032Speter 100838032Speter ee->e_dfp = NULL; 100938032Speter ee->e_xfp = NULL; 101064562Sgshapiro 101164562Sgshapiro /* 101264562Sgshapiro ** Make sure both are in the same directory. 101364562Sgshapiro */ 101464562Sgshapiro 101590792Sgshapiro (void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf); 101690792Sgshapiro (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf); 1017102528Sgshapiro 1018102528Sgshapiro /* Force the df to disk if it's not there yet */ 1019102528Sgshapiro if (type == DATAFL_LETTER && e->e_dfp != NULL && 1020102528Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 1021102528Sgshapiro errno != EINVAL) 1022102528Sgshapiro { 1023102528Sgshapiro syserr("!dup_queue_file: can't commit %s", f1buf); 1024102528Sgshapiro /* NOTREACHED */ 1025102528Sgshapiro } 1026102528Sgshapiro 102738032Speter if (link(f1buf, f2buf) < 0) 102838032Speter { 102964562Sgshapiro int save_errno = errno; 103038032Speter 103138032Speter syserr("sendall: link(%s, %s)", f1buf, f2buf); 103264562Sgshapiro if (save_errno == EEXIST) 103338032Speter { 103438032Speter if (unlink(f2buf) < 0) 103538032Speter { 103638032Speter syserr("!sendall: unlink(%s): permanent", 103790792Sgshapiro f2buf); 103864562Sgshapiro /* NOTREACHED */ 103938032Speter } 104038032Speter if (link(f1buf, f2buf) < 0) 104138032Speter { 104238032Speter syserr("!sendall: link(%s, %s): permanent", 104390792Sgshapiro f1buf, f2buf); 104464562Sgshapiro /* NOTREACHED */ 104538032Speter } 104638032Speter } 104738032Speter } 104890792Sgshapiro SYNC_DIR(f2buf, true); 104938032Speter} 105090792Sgshapiro/* 105138032Speter** DOFORK -- do a fork, retrying a couple of times on failure. 105238032Speter** 105338032Speter** This MUST be a macro, since after a vfork we are running 105438032Speter** two processes on the same stack!!! 105538032Speter** 105638032Speter** Parameters: 105738032Speter** none. 105838032Speter** 105938032Speter** Returns: 106038032Speter** From a macro??? You've got to be kidding! 106138032Speter** 106238032Speter** Side Effects: 106338032Speter** Modifies the ==> LOCAL <== variable 'pid', leaving: 106438032Speter** pid of child in parent, zero in child. 106538032Speter** -1 on unrecoverable error. 106638032Speter** 106738032Speter** Notes: 106838032Speter** I'm awfully sorry this looks so awful. That's 106938032Speter** vfork for you..... 107038032Speter*/ 107138032Speter 107264562Sgshapiro#define NFORKTRIES 5 107338032Speter 107464562Sgshapiro#ifndef FORK 107538032Speter# define FORK fork 107664562Sgshapiro#endif /* ! FORK */ 107738032Speter 107864562Sgshapiro#define DOFORK(fORKfN) \ 107938032Speter{\ 108038032Speter register int i;\ 108138032Speter\ 108238032Speter for (i = NFORKTRIES; --i >= 0; )\ 108338032Speter {\ 108438032Speter pid = fORKfN();\ 108538032Speter if (pid >= 0)\ 108638032Speter break;\ 108738032Speter if (i > 0)\ 108864562Sgshapiro (void) sleep((unsigned) NFORKTRIES - i);\ 108938032Speter }\ 109038032Speter} 109190792Sgshapiro/* 109238032Speter** DOFORK -- simple fork interface to DOFORK. 109338032Speter** 109438032Speter** Parameters: 109538032Speter** none. 109638032Speter** 109738032Speter** Returns: 109838032Speter** pid of child in parent. 109938032Speter** zero in child. 110038032Speter** -1 on error. 110138032Speter** 110238032Speter** Side Effects: 110338032Speter** returns twice, once in parent and once in child. 110438032Speter*/ 110538032Speter 110677349Sgshapiropid_t 110738032Speterdofork() 110838032Speter{ 110938032Speter register pid_t pid = -1; 111038032Speter 111138032Speter DOFORK(fork); 111264562Sgshapiro return pid; 111338032Speter} 111490792Sgshapiro 111590792Sgshapiro/* 111690792Sgshapiro** COLONCMP -- compare host-signatures up to first ':' or EOS 111790792Sgshapiro** 111890792Sgshapiro** This takes two strings which happen to be host-signatures and 111990792Sgshapiro** compares them. If the lowest preference portions of the MX-RR's 112090792Sgshapiro** match (up to ':' or EOS, whichever is first), then we have 112190792Sgshapiro** match. This is used for coattail-piggybacking messages during 112290792Sgshapiro** message delivery. 112390792Sgshapiro** If the signatures are the same up to the first ':' the remainder of 112490792Sgshapiro** the signatures are then compared with a normal strcmp(). This saves 112590792Sgshapiro** re-examining the first part of the signatures. 112690792Sgshapiro** 112790792Sgshapiro** Parameters: 112890792Sgshapiro** a - first host-signature 112990792Sgshapiro** b - second host-signature 113090792Sgshapiro** 113190792Sgshapiro** Returns: 113290792Sgshapiro** HS_MATCH_NO -- no "match". 113390792Sgshapiro** HS_MATCH_FIRST -- "match" for the first MX preference 113490792Sgshapiro** (up to the first colon (':')). 113590792Sgshapiro** HS_MATCH_FULL -- match for the entire MX record. 113690792Sgshapiro** 113790792Sgshapiro** Side Effects: 113890792Sgshapiro** none. 113990792Sgshapiro*/ 114090792Sgshapiro 114190792Sgshapiro#define HS_MATCH_NO 0 114290792Sgshapiro#define HS_MATCH_FIRST 1 114390792Sgshapiro#define HS_MATCH_FULL 2 114490792Sgshapiro 114590792Sgshapirostatic int 114690792Sgshapirocoloncmp(a, b) 114790792Sgshapiro register const char *a; 114890792Sgshapiro register const char *b; 114990792Sgshapiro{ 115090792Sgshapiro int ret = HS_MATCH_NO; 115190792Sgshapiro int braclev = 0; 115290792Sgshapiro 115390792Sgshapiro while (*a == *b++) 115490792Sgshapiro { 115590792Sgshapiro /* Need to account for IPv6 bracketed addresses */ 115690792Sgshapiro if (*a == '[') 115790792Sgshapiro braclev++; 1158112810Sgshapiro else if (*a == ']' && braclev > 0) 115990792Sgshapiro braclev--; 116090792Sgshapiro else if (*a == ':' && braclev <= 0) 116190792Sgshapiro { 116290792Sgshapiro ret = HS_MATCH_FIRST; 116390792Sgshapiro a++; 116490792Sgshapiro break; 116590792Sgshapiro } 116690792Sgshapiro else if (*a == '\0') 116790792Sgshapiro return HS_MATCH_FULL; /* a full match */ 116890792Sgshapiro a++; 116990792Sgshapiro } 117090792Sgshapiro if (ret == HS_MATCH_NO && 117190792Sgshapiro braclev <= 0 && 117290792Sgshapiro ((*a == '\0' && *(b - 1) == ':') || 117390792Sgshapiro (*a == ':' && *(b - 1) == '\0'))) 117490792Sgshapiro return HS_MATCH_FIRST; 117590792Sgshapiro if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 117690792Sgshapiro return HS_MATCH_FULL; 117790792Sgshapiro 117890792Sgshapiro return ret; 117990792Sgshapiro} 1180132943Sgshapiro 118190792Sgshapiro/* 1182132943Sgshapiro** SHOULD_TRY_FBSH -- Should try FallbackSmartHost? 1183132943Sgshapiro** 1184132943Sgshapiro** Parameters: 1185132943Sgshapiro** e -- envelope 1186132943Sgshapiro** tried_fallbacksmarthost -- has been tried already? (in/out) 1187132943Sgshapiro** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out) 1188132943Sgshapiro** hbsz -- size of hostbuf 1189132943Sgshapiro** status -- current delivery status 1190132943Sgshapiro** 1191132943Sgshapiro** Returns: 1192132943Sgshapiro** true iff FallbackSmartHost should be tried. 1193132943Sgshapiro*/ 1194132943Sgshapiro 1195132943Sgshapirostatic bool 1196132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status) 1197132943Sgshapiro ENVELOPE *e; 1198132943Sgshapiro bool *tried_fallbacksmarthost; 1199132943Sgshapiro char *hostbuf; 1200132943Sgshapiro size_t hbsz; 1201132943Sgshapiro int status; 1202132943Sgshapiro{ 1203132943Sgshapiro /* 1204157001Sgshapiro ** If the host was not found or a temporary failure occurred 1205157001Sgshapiro ** and a FallbackSmartHost is defined (and we have not yet 1206157001Sgshapiro ** tried it), then make one last try with it as the host. 1207132943Sgshapiro */ 1208132943Sgshapiro 1209157001Sgshapiro if ((status == EX_NOHOST || status == EX_TEMPFAIL) && 1210157001Sgshapiro FallbackSmartHost != NULL && !*tried_fallbacksmarthost) 1211132943Sgshapiro { 1212132943Sgshapiro *tried_fallbacksmarthost = true; 1213132943Sgshapiro expand(FallbackSmartHost, hostbuf, hbsz, e); 1214132943Sgshapiro if (!wordinclass(hostbuf, 'w')) 1215132943Sgshapiro { 1216132943Sgshapiro if (tTd(11, 1)) 1217132943Sgshapiro sm_dprintf("one last try with FallbackSmartHost %s\n", 1218132943Sgshapiro hostbuf); 1219132943Sgshapiro return true; 1220132943Sgshapiro } 1221132943Sgshapiro } 1222132943Sgshapiro return false; 1223132943Sgshapiro} 1224132943Sgshapiro/* 122538032Speter** DELIVER -- Deliver a message to a list of addresses. 122638032Speter** 122738032Speter** This routine delivers to everyone on the same host as the 122838032Speter** user on the head of the list. It is clever about mailers 122938032Speter** that don't handle multiple users. It is NOT guaranteed 123038032Speter** that it will deliver to all these addresses however -- so 123138032Speter** deliver should be called once for each address on the 123238032Speter** list. 123390792Sgshapiro** Deliver tries to be as opportunistic as possible about piggybacking 123490792Sgshapiro** messages. Some definitions to make understanding easier follow below. 123590792Sgshapiro** Piggybacking occurs when an existing connection to a mail host can 123690792Sgshapiro** be used to send the same message to more than one recipient at the 123790792Sgshapiro** same time. So "no piggybacking" means one message for one recipient 123890792Sgshapiro** per connection. "Intentional piggybacking" happens when the 123990792Sgshapiro** recipients' host address (not the mail host address) is used to 124090792Sgshapiro** attempt piggybacking. Recipients with the same host address 124190792Sgshapiro** have the same mail host. "Coincidental piggybacking" relies on 124290792Sgshapiro** piggybacking based on all the mail host addresses in the MX-RR. This 124390792Sgshapiro** is "coincidental" in the fact it could not be predicted until the 124490792Sgshapiro** MX Resource Records for the hosts were obtained and examined. For 124590792Sgshapiro** example (preference order and equivalence is important, not values): 124690792Sgshapiro** domain1 IN MX 10 mxhost-A 124790792Sgshapiro** IN MX 20 mxhost-B 124890792Sgshapiro** domain2 IN MX 4 mxhost-A 124990792Sgshapiro** IN MX 8 mxhost-B 125090792Sgshapiro** Domain1 and domain2 can piggyback the same message to mxhost-A or 125190792Sgshapiro** mxhost-B (if mxhost-A cannot be reached). 125290792Sgshapiro** "Coattail piggybacking" relaxes the strictness of "coincidental 125390792Sgshapiro** piggybacking" in the hope that most significant (lowest value) 125490792Sgshapiro** MX preference host(s) can create more piggybacking. For example 125590792Sgshapiro** (again, preference order and equivalence is important, not values): 125690792Sgshapiro** domain3 IN MX 100 mxhost-C 125790792Sgshapiro** IN MX 100 mxhost-D 125890792Sgshapiro** IN MX 200 mxhost-E 125990792Sgshapiro** domain4 IN MX 50 mxhost-C 126090792Sgshapiro** IN MX 50 mxhost-D 126190792Sgshapiro** IN MX 80 mxhost-F 126290792Sgshapiro** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 126390792Sgshapiro** is available. Same with mxhost-D because in both RR's the preference 126490792Sgshapiro** value is the same as mxhost-C, respectively. 126590792Sgshapiro** So deliver attempts coattail piggybacking when possible. If the 126690792Sgshapiro** first MX preference level hosts cannot be used then the piggybacking 126790792Sgshapiro** reverts to coincidental piggybacking. Using the above example you 126890792Sgshapiro** cannot deliver to mxhost-F for domain3 regardless of preference value. 126990792Sgshapiro** ("Coattail" from "riding on the coattails of your predecessor" meaning 127090792Sgshapiro** gaining benefit from a predecessor effort with no or little addition 127190792Sgshapiro** effort. The predecessor here being the preceding MX RR). 127238032Speter** 127338032Speter** Parameters: 127438032Speter** e -- the envelope to deliver. 127538032Speter** firstto -- head of the address list to deliver to. 127638032Speter** 127738032Speter** Returns: 127838032Speter** zero -- successfully delivered. 127938032Speter** else -- some failure, see ExitStat for more info. 128038032Speter** 128138032Speter** Side Effects: 128238032Speter** The standard input is passed off to someone. 128338032Speter*/ 128438032Speter 128564562Sgshapirostatic int 128638032Speterdeliver(e, firstto) 128738032Speter register ENVELOPE *e; 128838032Speter ADDRESS *firstto; 128938032Speter{ 129038032Speter char *host; /* host being sent to */ 129138032Speter char *user; /* user being sent to */ 129238032Speter char **pvp; 129338032Speter register char **mvp; 129438032Speter register char *p; 129538032Speter register MAILER *m; /* mailer for this recipient */ 129638032Speter ADDRESS *volatile ctladdr; 129790792Sgshapiro#if HASSETUSERCONTEXT 129838032Speter ADDRESS *volatile contextaddr = NULL; 129990792Sgshapiro#endif /* HASSETUSERCONTEXT */ 130038032Speter register MCI *volatile mci; 130190792Sgshapiro register ADDRESS *SM_NONVOLATILE to = firstto; 130290792Sgshapiro volatile bool clever = false; /* running user smtp to this mailer */ 130338032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 130438032Speter int rcode; /* response code */ 130590792Sgshapiro SM_NONVOLATILE int lmtp_rcode = EX_OK; 130690792Sgshapiro SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 130790792Sgshapiro SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 130838032Speter char *firstsig; /* signature of firstto */ 130990792Sgshapiro volatile pid_t pid = -1; 131038032Speter char *volatile curhost; 131190792Sgshapiro SM_NONVOLATILE unsigned short port = 0; 131290792Sgshapiro SM_NONVOLATILE time_t enough = 0; 131364562Sgshapiro#if NETUNIX 131490792Sgshapiro char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 131564562Sgshapiro#endif /* NETUNIX */ 131638032Speter time_t xstart; 131738032Speter bool suidwarn; 131838032Speter bool anyok; /* at least one address was OK */ 131990792Sgshapiro SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 132064562Sgshapiro bool ovr; 132190792Sgshapiro bool quarantine; 132264562Sgshapiro int strsize; 132364562Sgshapiro int rcptcount; 132490792Sgshapiro int ret; 132564562Sgshapiro static int tobufsize = 0; 132664562Sgshapiro static char *tobuf = NULL; 132790792Sgshapiro char *rpath; /* translated return path */ 132838032Speter int mpvect[2]; 132938032Speter int rpvect[2]; 133064562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 133164562Sgshapiro char *pv[MAXPV + 1]; 133238032Speter char buf[MAXNAME + 1]; 133398121Sgshapiro char cbuf[MAXPATHLEN]; 133438032Speter 133538032Speter errno = 0; 133664562Sgshapiro if (!QS_IS_OK(to->q_state)) 133764562Sgshapiro return 0; 133838032Speter 133938032Speter suidwarn = geteuid() == 0; 134038032Speter 134138032Speter m = to->q_mailer; 134238032Speter host = to->q_host; 134338032Speter CurEnv = e; /* just in case */ 134438032Speter e->e_statmsg = NULL; 134538032Speter SmtpError[0] = '\0'; 134638032Speter xstart = curtime(); 134738032Speter 134838032Speter if (tTd(10, 1)) 134990792Sgshapiro sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 135038032Speter e->e_id, m->m_name, host, to->q_user); 135138032Speter if (tTd(10, 100)) 135290792Sgshapiro printopenfds(false); 135338032Speter 135438032Speter /* 135590792Sgshapiro ** Clear {client_*} macros if this is a bounce message to 135638032Speter ** prevent rejection by check_compat ruleset. 135738032Speter */ 135864562Sgshapiro 135938032Speter if (bitset(EF_RESPONSE, e->e_flags)) 136038032Speter { 136190792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 1362132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), ""); 136390792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 136490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 136590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 136638032Speter } 136764562Sgshapiro 136890792Sgshapiro SM_TRY 136990792Sgshapiro { 137090792Sgshapiro ADDRESS *skip_back = NULL; 137190792Sgshapiro 137238032Speter /* 137338032Speter ** Do initial argv setup. 137438032Speter ** Insert the mailer name. Notice that $x expansion is 137538032Speter ** NOT done on the mailer name. Then, if the mailer has 137638032Speter ** a picky -f flag, we insert it as appropriate. This 137738032Speter ** code does not check for 'pv' overflow; this places a 137838032Speter ** manifest lower limit of 4 for MAXPV. 137938032Speter ** The from address rewrite is expected to make 138038032Speter ** the address relative to the other end. 138138032Speter */ 138238032Speter 138338032Speter /* rewrite from address, using rewriting rules */ 138438032Speter rcode = EX_OK; 138538032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 138638032Speter p = e->e_sender; 138738032Speter else 138838032Speter p = e->e_from.q_paddr; 138990792Sgshapiro rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 139090792Sgshapiro if (strlen(rpath) > MAXSHORTSTR) 139138032Speter { 139290792Sgshapiro rpath = shortenstring(rpath, MAXSHORTSTR); 139390792Sgshapiro 139490792Sgshapiro /* avoid bogus errno */ 139590792Sgshapiro errno = 0; 139690792Sgshapiro syserr("remotename: huge return path %s", rpath); 139738032Speter } 139890792Sgshapiro rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 139990792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', rpath); 140090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', host); 140138032Speter Errors = 0; 140238032Speter pvp = pv; 140338032Speter *pvp++ = m->m_argv[0]; 140438032Speter 1405132943Sgshapiro /* ignore long term host status information if mailer flag W is set */ 1406132943Sgshapiro if (bitnset(M_NOHOSTSTAT, m->m_flags)) 1407132943Sgshapiro IgnoreHostStatus = true; 1408132943Sgshapiro 140938032Speter /* insert -f or -r flag as appropriate */ 141064562Sgshapiro if (FromFlag && 141164562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 141264562Sgshapiro bitnset(M_ROPT, m->m_flags))) 141338032Speter { 141438032Speter if (bitnset(M_FOPT, m->m_flags)) 141538032Speter *pvp++ = "-f"; 141638032Speter else 141738032Speter *pvp++ = "-r"; 141890792Sgshapiro *pvp++ = rpath; 141938032Speter } 142038032Speter 142138032Speter /* 142238032Speter ** Append the other fixed parts of the argv. These run 142338032Speter ** up to the first entry containing "$u". There can only 142438032Speter ** be one of these, and there are only a few more slots 142538032Speter ** in the pv after it. 142638032Speter */ 142738032Speter 142838032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 142938032Speter { 143038032Speter /* can't use strchr here because of sign extension problems */ 143138032Speter while (*p != '\0') 143238032Speter { 143338032Speter if ((*p++ & 0377) == MACROEXPAND) 143438032Speter { 143538032Speter if (*p == 'u') 143638032Speter break; 143738032Speter } 143838032Speter } 143938032Speter 144038032Speter if (*p != '\0') 144138032Speter break; 144238032Speter 144338032Speter /* this entry is safe -- go ahead and process it */ 144438032Speter expand(*mvp, buf, sizeof buf, e); 144590792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 144638032Speter if (pvp >= &pv[MAXPV - 3]) 144738032Speter { 144864562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 144964562Sgshapiro pv[0]); 145090792Sgshapiro rcode = -1; 145190792Sgshapiro goto cleanup; 145238032Speter } 145338032Speter } 145438032Speter 145538032Speter /* 145638032Speter ** If we have no substitution for the user name in the argument 145738032Speter ** list, we know that we must supply the names otherwise -- and 145838032Speter ** SMTP is the answer!! 145938032Speter */ 146038032Speter 146138032Speter if (*mvp == NULL) 146238032Speter { 146373188Sgshapiro /* running LMTP or SMTP */ 146490792Sgshapiro clever = true; 146538032Speter *pvp = NULL; 146638032Speter } 146773188Sgshapiro else if (bitnset(M_LMTP, m->m_flags)) 146873188Sgshapiro { 146973188Sgshapiro /* not running LMTP */ 147073188Sgshapiro sm_syslog(LOG_ERR, NULL, 147173188Sgshapiro "Warning: mailer %s: LMTP flag (F=z) turned off", 147273188Sgshapiro m->m_name); 147373188Sgshapiro clrbitn(M_LMTP, m->m_flags); 147473188Sgshapiro } 147538032Speter 147638032Speter /* 147738032Speter ** At this point *mvp points to the argument with $u. We 147838032Speter ** run through our address list and append all the addresses 147938032Speter ** we can. If we run out of space, do not fret! We can 148038032Speter ** always send another copy later. 148138032Speter */ 148238032Speter 148364562Sgshapiro e->e_to = NULL; 148464562Sgshapiro strsize = 2; 148564562Sgshapiro rcptcount = 0; 148690792Sgshapiro ctladdr = NULL; 148790792Sgshapiro if (firstto->q_signature == NULL) 148890792Sgshapiro firstto->q_signature = hostsignature(firstto->q_mailer, 148990792Sgshapiro firstto->q_host); 149090792Sgshapiro firstsig = firstto->q_signature; 149164562Sgshapiro 149238032Speter for (; to != NULL; to = to->q_next) 149338032Speter { 149438032Speter /* avoid sending multiple recipients to dumb mailers */ 149564562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 149664562Sgshapiro break; 149738032Speter 149838032Speter /* if already sent or not for this host, don't send */ 149990792Sgshapiro if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 150038032Speter continue; 150138032Speter 150290792Sgshapiro /* 150390792Sgshapiro ** Must be same mailer to keep grouping rcpts. 150490792Sgshapiro ** If mailers don't match: continue; sendqueue is not 150590792Sgshapiro ** sorted by mailers, so don't break; 150690792Sgshapiro */ 150790792Sgshapiro 150890792Sgshapiro if (to->q_mailer != firstto->q_mailer) 150990792Sgshapiro continue; 151090792Sgshapiro 151190792Sgshapiro if (to->q_signature == NULL) /* for safety */ 151290792Sgshapiro to->q_signature = hostsignature(to->q_mailer, 151390792Sgshapiro to->q_host); 151490792Sgshapiro 151590792Sgshapiro /* 151690792Sgshapiro ** This is for coincidental and tailcoat piggybacking messages 151790792Sgshapiro ** to the same mail host. While the signatures are identical 151890792Sgshapiro ** (that's the MX-RR's are identical) we can do coincidental 151990792Sgshapiro ** piggybacking. We try hard for coattail piggybacking 152090792Sgshapiro ** with the same mail host when the next recipient has the 152190792Sgshapiro ** same host at lowest preference. It may be that this 152290792Sgshapiro ** won't work out, so 'skip_back' is maintained if a backup 152390792Sgshapiro ** to coincidental piggybacking or full signature must happen. 152490792Sgshapiro */ 152590792Sgshapiro 152690792Sgshapiro ret = firstto == to ? HS_MATCH_FULL : 152790792Sgshapiro coloncmp(to->q_signature, firstsig); 152890792Sgshapiro if (ret == HS_MATCH_FULL) 152990792Sgshapiro skip_back = to; 153090792Sgshapiro else if (ret == HS_MATCH_NO) 153164562Sgshapiro break; 153264562Sgshapiro 153390792Sgshapiro if (!clever) 153490792Sgshapiro { 153590792Sgshapiro /* avoid overflowing tobuf */ 153690792Sgshapiro strsize += strlen(to->q_paddr) + 1; 153790792Sgshapiro if (strsize > TOBUFSIZE) 153890792Sgshapiro break; 153990792Sgshapiro } 154090792Sgshapiro 154164562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 154264562Sgshapiro break; 154338032Speter 154438032Speter if (tTd(10, 1)) 154538032Speter { 154690792Sgshapiro sm_dprintf("\nsend to "); 1547132943Sgshapiro printaddr(sm_debug_file(), to, false); 154838032Speter } 154938032Speter 155038032Speter /* compute effective uid/gid when sending */ 155138032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 155290792Sgshapiro# if HASSETUSERCONTEXT 155338032Speter contextaddr = ctladdr = getctladdr(to); 155490792Sgshapiro# else /* HASSETUSERCONTEXT */ 155590792Sgshapiro ctladdr = getctladdr(to); 155690792Sgshapiro# endif /* HASSETUSERCONTEXT */ 155738032Speter 155838032Speter if (tTd(10, 2)) 155938032Speter { 156090792Sgshapiro sm_dprintf("ctladdr="); 1561132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 156238032Speter } 156338032Speter 156438032Speter user = to->q_user; 156538032Speter e->e_to = to->q_paddr; 156638032Speter 156738032Speter /* 156838032Speter ** Check to see that these people are allowed to 156938032Speter ** talk to each other. 157066494Sgshapiro ** Check also for overflow of e_msgsize. 157138032Speter */ 157238032Speter 157366494Sgshapiro if (m->m_maxsize != 0 && 157466494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 157538032Speter { 157638032Speter e->e_flags |= EF_NO_BODY_RETN; 157738032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 157838032Speter to->q_status = "5.2.3"; 157938032Speter else 158038032Speter to->q_status = "5.3.4"; 158190792Sgshapiro 158264562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 158364562Sgshapiro usrerrenh(to->q_status, 158464562Sgshapiro "552 Message is too large; %ld bytes max", 158564562Sgshapiro m->m_maxsize); 158690792Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, false); 158764562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 158890792Sgshapiro NULL, ctladdr, xstart, e, to); 158938032Speter continue; 159038032Speter } 159173188Sgshapiro SM_SET_H_ERRNO(0); 159290792Sgshapiro ovr = true; 159338032Speter 159438032Speter /* do config file checking of compatibility */ 159590792Sgshapiro quarantine = (e->e_quarmsg != NULL); 159664562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1597102528Sgshapiro e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1598102528Sgshapiro e->e_id); 159938032Speter if (rcode == EX_OK) 160038032Speter { 160142575Speter /* do in-code checking if not discarding */ 160242575Speter if (!bitset(EF_DISCARD, e->e_flags)) 160364562Sgshapiro { 160442575Speter rcode = checkcompat(to, e); 160590792Sgshapiro ovr = false; 160664562Sgshapiro } 160738032Speter } 160838032Speter if (rcode != EX_OK) 160938032Speter { 161064562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 161164562Sgshapiro giveresponse(rcode, to->q_status, m, 161290792Sgshapiro NULL, ctladdr, xstart, e, to); 161338032Speter continue; 161438032Speter } 161590792Sgshapiro if (!quarantine && e->e_quarmsg != NULL) 161690792Sgshapiro { 161790792Sgshapiro /* 161890792Sgshapiro ** check_compat or checkcompat() has tried 161990792Sgshapiro ** to quarantine but that isn't supported. 162090792Sgshapiro ** Revert the attempt. 162190792Sgshapiro */ 162290792Sgshapiro 162390792Sgshapiro e->e_quarmsg = NULL; 162490792Sgshapiro macdefine(&e->e_macro, A_PERM, 162590792Sgshapiro macid("{quarantine}"), ""); 162690792Sgshapiro } 162742575Speter if (bitset(EF_DISCARD, e->e_flags)) 162842575Speter { 162942575Speter if (tTd(10, 5)) 163042575Speter { 163190792Sgshapiro sm_dprintf("deliver: discarding recipient "); 1632132943Sgshapiro printaddr(sm_debug_file(), to, false); 163342575Speter } 163438032Speter 163564562Sgshapiro /* pretend the message was sent */ 163664562Sgshapiro /* XXX should we log something here? */ 163764562Sgshapiro to->q_state = QS_DISCARDED; 163864562Sgshapiro 163942575Speter /* 164042575Speter ** Remove discard bit to prevent discard of 164164562Sgshapiro ** future recipients. This is safe because the 164264562Sgshapiro ** true "global discard" has been handled before 164364562Sgshapiro ** we get here. 164442575Speter */ 164564562Sgshapiro 164642575Speter e->e_flags &= ~EF_DISCARD; 164742575Speter continue; 164842575Speter } 164942575Speter 165038032Speter /* 165138032Speter ** Strip quote bits from names if the mailer is dumb 165238032Speter ** about them. 165338032Speter */ 165438032Speter 165538032Speter if (bitnset(M_STRIPQ, m->m_flags)) 165638032Speter { 165738032Speter stripquotes(user); 165838032Speter stripquotes(host); 165938032Speter } 1660132943Sgshapiro 1661110560Sgshapiro /* 1662141858Sgshapiro ** Strip all leading backslashes if requested and the 1663110560Sgshapiro ** next character is alphanumerical (the latter can 1664110560Sgshapiro ** probably relaxed a bit, see RFC2821). 1665110560Sgshapiro */ 166638032Speter 1667110560Sgshapiro if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1668110560Sgshapiro stripbackslash(user); 1669110560Sgshapiro 167038032Speter /* hack attack -- delivermail compatibility */ 167138032Speter if (m == ProgMailer && *user == '|') 167238032Speter user++; 167338032Speter 167438032Speter /* 167538032Speter ** If an error message has already been given, don't 167638032Speter ** bother to send to this address. 167738032Speter ** 167838032Speter ** >>>>>>>>>> This clause assumes that the local mailer 167938032Speter ** >> NOTE >> cannot do any further aliasing; that 168038032Speter ** >>>>>>>>>> function is subsumed by sendmail. 168138032Speter */ 168238032Speter 168364562Sgshapiro if (!QS_IS_OK(to->q_state)) 168438032Speter continue; 168538032Speter 168638032Speter /* 168738032Speter ** See if this user name is "special". 168838032Speter ** If the user name has a slash in it, assume that this 168938032Speter ** is a file -- send it off without further ado. Note 169038032Speter ** that this type of addresses is not processed along 169138032Speter ** with the others, so we fudge on the To person. 169238032Speter */ 169338032Speter 169438032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 169538032Speter { 169690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); 169738032Speter p = to->q_home; 169838032Speter if (p == NULL && ctladdr != NULL) 169938032Speter p = ctladdr->q_home; 170090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); 170138032Speter expand(m->m_argv[1], buf, sizeof buf, e); 170238032Speter if (strlen(buf) > 0) 170338032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 170438032Speter else 170538032Speter { 170638032Speter syserr("empty filename specification for mailer %s", 170738032Speter m->m_name); 170838032Speter rcode = EX_CONFIG; 170938032Speter } 171064562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 171190792Sgshapiro ctladdr, xstart, e, to); 171290792Sgshapiro markfailure(e, to, NULL, rcode, true); 171338032Speter e->e_nsent++; 171438032Speter if (rcode == EX_OK) 171538032Speter { 171664562Sgshapiro to->q_state = QS_SENT; 171738032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 171838032Speter bitset(QPINGONSUCCESS, to->q_flags)) 171938032Speter { 172038032Speter to->q_flags |= QDELIVERED; 172138032Speter to->q_status = "2.1.5"; 172290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, 172390792Sgshapiro SM_TIME_DEFAULT, 172490792Sgshapiro "%s... Successfully delivered\n", 172590792Sgshapiro to->q_paddr); 172638032Speter } 172738032Speter } 172838032Speter to->q_statdate = curtime(); 172990792Sgshapiro markstats(e, to, STATS_NORMAL); 173038032Speter continue; 173138032Speter } 173238032Speter 173338032Speter /* 173438032Speter ** Address is verified -- add this user to mailer 173538032Speter ** argv, and add it to the print list of recipients. 173638032Speter */ 173738032Speter 173838032Speter /* link together the chain of recipients */ 173938032Speter to->q_tchain = tochain; 174038032Speter tochain = to; 174164562Sgshapiro e->e_to = "[CHAIN]"; 174264562Sgshapiro 174390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 174438032Speter p = to->q_home; 174538032Speter if (p == NULL && ctladdr != NULL) 174638032Speter p = ctladdr->q_home; 174790792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 174838032Speter 174964562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 175064562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 175164562Sgshapiro { 175264562Sgshapiro char notify[MAXLINE]; 175364562Sgshapiro 175464562Sgshapiro notify[0] = '\0'; 175564562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 175690792Sgshapiro (void) sm_strlcat(notify, "SUCCESS,", 175790792Sgshapiro sizeof notify); 175864562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 175990792Sgshapiro (void) sm_strlcat(notify, "FAILURE,", 176090792Sgshapiro sizeof notify); 176164562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 176290792Sgshapiro (void) sm_strlcat(notify, "DELAY,", 176390792Sgshapiro sizeof notify); 176464562Sgshapiro 176564562Sgshapiro /* Set to NEVER or drop trailing comma */ 176664562Sgshapiro if (notify[0] == '\0') 176790792Sgshapiro (void) sm_strlcat(notify, "NEVER", 176890792Sgshapiro sizeof notify); 176964562Sgshapiro else 177064562Sgshapiro notify[strlen(notify) - 1] = '\0'; 177164562Sgshapiro 177290792Sgshapiro macdefine(&e->e_macro, A_TEMP, 177390792Sgshapiro macid("{dsn_notify}"), notify); 177464562Sgshapiro } 177564562Sgshapiro else 177690792Sgshapiro macdefine(&e->e_macro, A_PERM, 177790792Sgshapiro macid("{dsn_notify}"), NULL); 177864562Sgshapiro 177938032Speter /* 178038032Speter ** Expand out this user into argument list. 178138032Speter */ 178238032Speter 178338032Speter if (!clever) 178438032Speter { 178538032Speter expand(*mvp, buf, sizeof buf, e); 178690792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 178738032Speter if (pvp >= &pv[MAXPV - 2]) 178838032Speter { 178938032Speter /* allow some space for trailing parms */ 179038032Speter break; 179138032Speter } 179238032Speter } 179338032Speter } 179438032Speter 179538032Speter /* see if any addresses still exist */ 179664562Sgshapiro if (tochain == NULL) 179738032Speter { 179890792Sgshapiro rcode = 0; 179990792Sgshapiro goto cleanup; 180038032Speter } 180138032Speter 180238032Speter /* print out messages as full list */ 180390792Sgshapiro strsize = 1; 180490792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 180590792Sgshapiro strsize += strlen(to->q_paddr) + 1; 180690792Sgshapiro if (strsize < TOBUFSIZE) 180790792Sgshapiro strsize = TOBUFSIZE; 180890792Sgshapiro if (strsize > tobufsize) 180964562Sgshapiro { 181090792Sgshapiro SM_FREE_CLR(tobuf); 181190792Sgshapiro tobuf = sm_pmalloc_x(strsize); 181290792Sgshapiro tobufsize = strsize; 181364562Sgshapiro } 181490792Sgshapiro p = tobuf; 181590792Sgshapiro *p = '\0'; 181690792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 181790792Sgshapiro { 181890792Sgshapiro (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 181990792Sgshapiro ",", to->q_paddr); 182090792Sgshapiro p += strlen(p); 182190792Sgshapiro } 182238032Speter e->e_to = tobuf + 1; 182338032Speter 182438032Speter /* 182538032Speter ** Fill out any parameters after the $u parameter. 182638032Speter */ 182738032Speter 182890792Sgshapiro if (!clever) 182938032Speter { 183090792Sgshapiro while (*++mvp != NULL) 183190792Sgshapiro { 183290792Sgshapiro expand(*mvp, buf, sizeof buf, e); 183390792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 183490792Sgshapiro if (pvp >= &pv[MAXPV]) 183590792Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 183690792Sgshapiro pv[0]); 183790792Sgshapiro } 183838032Speter } 183938032Speter *pvp++ = NULL; 184038032Speter 184138032Speter /* 184238032Speter ** Call the mailer. 184338032Speter ** The argument vector gets built, pipes 184438032Speter ** are created as necessary, and we fork & exec as 184538032Speter ** appropriate. 184638032Speter ** If we are running SMTP, we just need to clean up. 184738032Speter */ 184838032Speter 184964562Sgshapiro /* XXX this seems a bit wierd */ 185038032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 185138032Speter bitset(QGOODUID, e->e_from.q_flags)) 185238032Speter ctladdr = &e->e_from; 185338032Speter 185438032Speter#if NAMED_BIND 185538032Speter if (ConfigLevel < 2) 185638032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 185764562Sgshapiro#endif /* NAMED_BIND */ 185838032Speter 185938032Speter if (tTd(11, 1)) 186038032Speter { 186190792Sgshapiro sm_dprintf("openmailer:"); 1862132943Sgshapiro printav(sm_debug_file(), pv); 186338032Speter } 186438032Speter errno = 0; 186573188Sgshapiro SM_SET_H_ERRNO(0); 186638032Speter CurHostName = NULL; 186738032Speter 186838032Speter /* 186938032Speter ** Deal with the special case of mail handled through an IPC 187038032Speter ** connection. 187138032Speter ** In this case we don't actually fork. We must be 187238032Speter ** running SMTP for this to work. We will return a 187338032Speter ** zero pid to indicate that we are running IPC. 187438032Speter ** We also handle a debug version that just talks to stdin/out. 187538032Speter */ 187638032Speter 187738032Speter curhost = NULL; 187838032Speter SmtpPhase = NULL; 187938032Speter mci = NULL; 188038032Speter 188138032Speter#if XDEBUG 188238032Speter { 188338032Speter char wbuf[MAXLINE]; 188438032Speter 188538032Speter /* make absolutely certain 0, 1, and 2 are in use */ 188690792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", 188790792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 188890792Sgshapiro m->m_name); 188938032Speter checkfd012(wbuf); 189038032Speter } 189164562Sgshapiro#endif /* XDEBUG */ 189238032Speter 189338032Speter /* check for 8-bit available */ 189438032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 189538032Speter bitnset(M_7BITS, m->m_flags) && 189638032Speter (bitset(EF_DONT_MIME, e->e_flags) || 189738032Speter !(bitset(MM_MIME8BIT, MimeMode) || 189838032Speter (bitset(EF_IS_MIME, e->e_flags) && 189964562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 190038032Speter { 190164562Sgshapiro e->e_status = "5.6.3"; 190264562Sgshapiro usrerrenh(e->e_status, 190390792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 190438032Speter rcode = EX_DATAERR; 190538032Speter goto give_up; 190638032Speter } 190738032Speter 190838032Speter if (tTd(62, 8)) 190938032Speter checkfds("before delivery"); 191038032Speter 191138032Speter /* check for Local Person Communication -- not for mortals!!! */ 191238032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 191338032Speter { 191490792Sgshapiro if (clever) 191590792Sgshapiro { 191690792Sgshapiro /* flush any expired connections */ 191790792Sgshapiro (void) mci_scan(NULL); 191890792Sgshapiro 191990792Sgshapiro /* try to get a cached connection or just a slot */ 192090792Sgshapiro mci = mci_get(m->m_name, m); 192190792Sgshapiro if (mci->mci_host == NULL) 192290792Sgshapiro mci->mci_host = m->m_name; 192390792Sgshapiro CurHostName = mci->mci_host; 192490792Sgshapiro if (mci->mci_state != MCIS_CLOSED) 192590792Sgshapiro { 192690792Sgshapiro message("Using cached SMTP/LPC connection for %s...", 192790792Sgshapiro m->m_name); 192890792Sgshapiro mci->mci_deliveries++; 192990792Sgshapiro goto do_transfer; 193090792Sgshapiro } 193190792Sgshapiro } 193290792Sgshapiro else 193390792Sgshapiro { 193490792Sgshapiro mci = mci_new(e->e_rpool); 193590792Sgshapiro } 193690792Sgshapiro mci->mci_in = smioin; 193790792Sgshapiro mci->mci_out = smioout; 193890792Sgshapiro mci->mci_mailer = m; 193990792Sgshapiro mci->mci_host = m->m_name; 194090792Sgshapiro if (clever) 194190792Sgshapiro { 194290792Sgshapiro mci->mci_state = MCIS_OPENING; 194390792Sgshapiro mci_cache(mci); 194490792Sgshapiro } 194590792Sgshapiro else 194690792Sgshapiro mci->mci_state = MCIS_OPEN; 194738032Speter } 194890792Sgshapiro else if (strcmp(m->m_mailer, "[IPC]") == 0) 194938032Speter { 195038032Speter register int i; 195138032Speter 195238032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 195338032Speter { 195464562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 195538032Speter rcode = EX_CONFIG; 195638032Speter goto give_up; 195738032Speter } 195838032Speter 195964562Sgshapiro# if NETUNIX 196064562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 196164562Sgshapiro { 196264562Sgshapiro curhost = CurHostName = "localhost"; 196364562Sgshapiro mux_path = pv[1]; 196464562Sgshapiro } 196564562Sgshapiro else 196664562Sgshapiro# endif /* NETUNIX */ 196764562Sgshapiro { 196864562Sgshapiro CurHostName = pv[1]; 196964562Sgshapiro curhost = hostsignature(m, pv[1]); 197064562Sgshapiro } 197138032Speter 197238032Speter if (curhost == NULL || curhost[0] == '\0') 197338032Speter { 197438032Speter syserr("null host signature for %s", pv[1]); 197538032Speter rcode = EX_CONFIG; 197638032Speter goto give_up; 197738032Speter } 197838032Speter 197938032Speter if (!clever) 198038032Speter { 198164562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 198238032Speter rcode = EX_CONFIG; 198338032Speter goto give_up; 198438032Speter } 198564562Sgshapiro if (pv[2] != NULL 198664562Sgshapiro# if NETUNIX 198764562Sgshapiro && mux_path == NULL 198864562Sgshapiro# endif /* NETUNIX */ 198964562Sgshapiro ) 199038032Speter { 199190792Sgshapiro port = htons((unsigned short) atoi(pv[2])); 199238032Speter if (port == 0) 199338032Speter { 199464562Sgshapiro# ifdef NO_GETSERVBYNAME 199564562Sgshapiro syserr("Invalid port number: %s", pv[2]); 199664562Sgshapiro# else /* NO_GETSERVBYNAME */ 199738032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 199838032Speter 199938032Speter if (sp == NULL) 200038032Speter syserr("Service %s unknown", pv[2]); 200138032Speter else 200238032Speter port = sp->s_port; 200364562Sgshapiro# endif /* NO_GETSERVBYNAME */ 200438032Speter } 200538032Speter } 200664562Sgshapiro 200764562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 200890792Sgshapiro if (TimeOuts.to_aconnect > 0) 200990792Sgshapiro enough = curtime() + TimeOuts.to_aconnect; 201038032Spetertryhost: 201164562Sgshapiro while (hostnum < nummxhosts) 201238032Speter { 201364562Sgshapiro char sep = ':'; 201464562Sgshapiro char *endp; 201538032Speter static char hostbuf[MAXNAME + 1]; 2016132943Sgshapiro bool tried_fallbacksmarthost = false; 201738032Speter 201864562Sgshapiro# if NETINET6 201964562Sgshapiro if (*mxhosts[hostnum] == '[') 202038032Speter { 202164562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 202264562Sgshapiro if (endp != NULL) 202364562Sgshapiro endp = strpbrk(endp + 1, ":,"); 202464562Sgshapiro } 202564562Sgshapiro else 202664562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 202764562Sgshapiro# else /* NETINET6 */ 202864562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 202964562Sgshapiro# endif /* NETINET6 */ 203064562Sgshapiro if (endp != NULL) 203164562Sgshapiro { 203264562Sgshapiro sep = *endp; 203364562Sgshapiro *endp = '\0'; 203464562Sgshapiro } 203564562Sgshapiro 203690792Sgshapiro if (hostnum == 1 && skip_back != NULL) 203790792Sgshapiro { 203890792Sgshapiro /* 203990792Sgshapiro ** Coattail piggybacking is no longer an 204090792Sgshapiro ** option with the mail host next to be tried 204190792Sgshapiro ** no longer the lowest MX preference 204290792Sgshapiro ** (hostnum == 1 meaning we're on the second 204390792Sgshapiro ** preference). We do not try to coattail 204490792Sgshapiro ** piggyback more than the first MX preference. 204590792Sgshapiro ** Revert 'tochain' to last location for 204690792Sgshapiro ** coincidental piggybacking. This works this 204790792Sgshapiro ** easily because the q_tchain kept getting 204890792Sgshapiro ** added to the top of the linked list. 204990792Sgshapiro */ 205090792Sgshapiro 205190792Sgshapiro tochain = skip_back; 205290792Sgshapiro } 205390792Sgshapiro 205464562Sgshapiro if (*mxhosts[hostnum] == '\0') 205564562Sgshapiro { 205638032Speter syserr("deliver: null host name in signature"); 205764562Sgshapiro hostnum++; 205864562Sgshapiro if (endp != NULL) 205964562Sgshapiro *endp = sep; 206038032Speter continue; 206138032Speter } 206290792Sgshapiro (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 206390792Sgshapiro sizeof hostbuf); 206464562Sgshapiro hostnum++; 206564562Sgshapiro if (endp != NULL) 206664562Sgshapiro *endp = sep; 206738032Speter 2068132943Sgshapiro one_last_try: 206938032Speter /* see if we already know that this host is fried */ 207038032Speter CurHostName = hostbuf; 207138032Speter mci = mci_get(hostbuf, m); 207238032Speter if (mci->mci_state != MCIS_CLOSED) 207338032Speter { 207490792Sgshapiro char *type; 207590792Sgshapiro 207638032Speter if (tTd(11, 1)) 207738032Speter { 207890792Sgshapiro sm_dprintf("openmailer: "); 2079132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 208038032Speter } 208138032Speter CurHostName = mci->mci_host; 208290792Sgshapiro if (bitnset(M_LMTP, m->m_flags)) 208390792Sgshapiro type = "L"; 208490792Sgshapiro else if (bitset(MCIF_ESMTP, mci->mci_flags)) 208590792Sgshapiro type = "ES"; 208690792Sgshapiro else 208790792Sgshapiro type = "S"; 208890792Sgshapiro message("Using cached %sMTP connection to %s via %s...", 208990792Sgshapiro type, hostbuf, m->m_name); 209064562Sgshapiro mci->mci_deliveries++; 209138032Speter break; 209238032Speter } 209338032Speter mci->mci_mailer = m; 209438032Speter if (mci->mci_exitstat != EX_OK) 209538032Speter { 209638032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 209790792Sgshapiro goodmxfound = true; 2098132943Sgshapiro 2099132943Sgshapiro /* Try FallbackSmartHost? */ 2100132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2101132943Sgshapiro hostbuf, sizeof hostbuf, 2102132943Sgshapiro mci->mci_exitstat)) 2103132943Sgshapiro goto one_last_try; 2104132943Sgshapiro 210538032Speter continue; 210638032Speter } 210738032Speter 210838032Speter if (mci_lock_host(mci) != EX_OK) 210938032Speter { 211038032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 211190792Sgshapiro goodmxfound = true; 211238032Speter continue; 211338032Speter } 211438032Speter 211538032Speter /* try the connection */ 211690792Sgshapiro sm_setproctitle(true, e, "%s %s: %s", 211764562Sgshapiro qid_printname(e), 211864562Sgshapiro hostbuf, "user open"); 211964562Sgshapiro# if NETUNIX 212064562Sgshapiro if (mux_path != NULL) 212164562Sgshapiro { 212238032Speter message("Connecting to %s via %s...", 212364562Sgshapiro mux_path, m->m_name); 212490792Sgshapiro i = makeconnection_ds((char *) mux_path, mci); 212564562Sgshapiro } 212638032Speter else 212764562Sgshapiro# endif /* NETUNIX */ 212864562Sgshapiro { 212964562Sgshapiro if (port == 0) 213064562Sgshapiro message("Connecting to %s via %s...", 213164562Sgshapiro hostbuf, m->m_name); 213264562Sgshapiro else 213364562Sgshapiro message("Connecting to %s port %d via %s...", 213464562Sgshapiro hostbuf, ntohs(port), 213564562Sgshapiro m->m_name); 213690792Sgshapiro i = makeconnection(hostbuf, port, mci, e, 213790792Sgshapiro enough); 213864562Sgshapiro } 213977349Sgshapiro mci->mci_errno = errno; 214038032Speter mci->mci_lastuse = curtime(); 214164562Sgshapiro mci->mci_deliveries = 0; 214238032Speter mci->mci_exitstat = i; 214364562Sgshapiro# if NAMED_BIND 214438032Speter mci->mci_herrno = h_errno; 214564562Sgshapiro# endif /* NAMED_BIND */ 214690792Sgshapiro 214790792Sgshapiro /* 214890792Sgshapiro ** Have we tried long enough to get a connection? 214990792Sgshapiro ** If yes, skip to the fallback MX hosts 215090792Sgshapiro ** (if existent). 215190792Sgshapiro */ 215290792Sgshapiro 215390792Sgshapiro if (enough > 0 && mci->mci_lastuse >= enough) 215490792Sgshapiro { 215590792Sgshapiro int h; 215690792Sgshapiro# if NAMED_BIND 2157132943Sgshapiro extern int NumFallbackMXHosts; 215890792Sgshapiro# else /* NAMED_BIND */ 2159132943Sgshapiro const int NumFallbackMXHosts = 0; 216090792Sgshapiro# endif /* NAMED_BIND */ 216190792Sgshapiro 216290792Sgshapiro if (hostnum < nummxhosts && LogLevel > 9) 216390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 216490792Sgshapiro "Timeout.to_aconnect occurred before exhausting all addresses"); 216590792Sgshapiro 216690792Sgshapiro /* turn off timeout if fallback available */ 2167132943Sgshapiro if (NumFallbackMXHosts > 0) 216890792Sgshapiro enough = 0; 216990792Sgshapiro 217090792Sgshapiro /* skip to a fallback MX host */ 2171132943Sgshapiro h = nummxhosts - NumFallbackMXHosts; 217290792Sgshapiro if (hostnum < h) 217390792Sgshapiro hostnum = h; 217490792Sgshapiro } 217538032Speter if (i == EX_OK) 217638032Speter { 217790792Sgshapiro goodmxfound = true; 217894334Sgshapiro markstats(e, firstto, STATS_CONNECT); 217938032Speter mci->mci_state = MCIS_OPENING; 218038032Speter mci_cache(mci); 218138032Speter if (TrafficLogFile != NULL) 218290792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 218390792Sgshapiro SM_TIME_DEFAULT, 218490792Sgshapiro "%05d === CONNECT %s\n", 218590792Sgshapiro (int) CurrentPid, 218690792Sgshapiro hostbuf); 218738032Speter break; 218838032Speter } 218938032Speter else 219038032Speter { 2191132943Sgshapiro /* Try FallbackSmartHost? */ 2192132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2193132943Sgshapiro hostbuf, sizeof hostbuf, i)) 2194132943Sgshapiro goto one_last_try; 2195132943Sgshapiro 219664562Sgshapiro if (tTd(11, 1)) 219790792Sgshapiro sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 219890792Sgshapiro i, errno); 219938032Speter if (i == EX_TEMPFAIL) 220090792Sgshapiro goodmxfound = true; 220138032Speter mci_unlock_host(mci); 220238032Speter } 220338032Speter 220438032Speter /* enter status of this host */ 220538032Speter setstat(i); 220638032Speter 220738032Speter /* should print some message here for -v mode */ 220838032Speter } 220938032Speter if (mci == NULL) 221038032Speter { 221138032Speter syserr("deliver: no host name"); 221238032Speter rcode = EX_SOFTWARE; 221338032Speter goto give_up; 221438032Speter } 221538032Speter mci->mci_pid = 0; 221638032Speter } 221738032Speter else 221838032Speter { 221938032Speter /* flush any expired connections */ 222038032Speter (void) mci_scan(NULL); 222138032Speter mci = NULL; 222238032Speter 222338032Speter if (bitnset(M_LMTP, m->m_flags)) 222438032Speter { 222538032Speter /* try to get a cached connection */ 222638032Speter mci = mci_get(m->m_name, m); 222738032Speter if (mci->mci_host == NULL) 222838032Speter mci->mci_host = m->m_name; 222938032Speter CurHostName = mci->mci_host; 223038032Speter if (mci->mci_state != MCIS_CLOSED) 223138032Speter { 223238032Speter message("Using cached LMTP connection for %s...", 223338032Speter m->m_name); 223464562Sgshapiro mci->mci_deliveries++; 223538032Speter goto do_transfer; 223638032Speter } 223738032Speter } 223838032Speter 223938032Speter /* announce the connection to verbose listeners */ 224038032Speter if (host == NULL || host[0] == '\0') 224138032Speter message("Connecting to %s...", m->m_name); 224238032Speter else 224338032Speter message("Connecting to %s via %s...", host, m->m_name); 224438032Speter if (TrafficLogFile != NULL) 224538032Speter { 224638032Speter char **av; 224738032Speter 224890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 224990792Sgshapiro "%05d === EXEC", (int) CurrentPid); 225038032Speter for (av = pv; *av != NULL; av++) 225190792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 225290792Sgshapiro SM_TIME_DEFAULT, " %s", 225390792Sgshapiro *av); 225490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 225590792Sgshapiro "\n"); 225638032Speter } 225738032Speter 225838032Speter#if XDEBUG 225938032Speter checkfd012("before creating mail pipe"); 226064562Sgshapiro#endif /* XDEBUG */ 226138032Speter 226238032Speter /* create a pipe to shove the mail through */ 226338032Speter if (pipe(mpvect) < 0) 226438032Speter { 226538032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 226690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 226738032Speter if (tTd(11, 1)) 226890792Sgshapiro sm_dprintf("openmailer: NULL\n"); 226938032Speter rcode = EX_OSERR; 227038032Speter goto give_up; 227138032Speter } 227238032Speter 227338032Speter#if XDEBUG 227438032Speter /* make sure we didn't get one of the standard I/O files */ 227538032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 227638032Speter { 227738032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 227890792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 227990792Sgshapiro mpvect[0], mpvect[1]); 228090792Sgshapiro printopenfds(true); 228138032Speter if (tTd(11, 1)) 228290792Sgshapiro sm_dprintf("openmailer: NULL\n"); 228338032Speter rcode = EX_OSERR; 228438032Speter goto give_up; 228538032Speter } 228638032Speter 228738032Speter /* make sure system call isn't dead meat */ 228838032Speter checkfdopen(mpvect[0], "mpvect[0]"); 228938032Speter checkfdopen(mpvect[1], "mpvect[1]"); 229038032Speter if (mpvect[0] == mpvect[1] || 229138032Speter (e->e_lockfp != NULL && 229290792Sgshapiro (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 229390792Sgshapiro NULL) || 229490792Sgshapiro mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 229590792Sgshapiro NULL)))) 229638032Speter { 229738032Speter if (e->e_lockfp == NULL) 229838032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 229990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 230090792Sgshapiro m->m_name, mpvect[0], mpvect[1]); 230138032Speter else 230238032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 230390792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 230490792Sgshapiro m->m_name, mpvect[0], mpvect[1], 230590792Sgshapiro sm_io_getinfo(e->e_lockfp, 230690792Sgshapiro SM_IO_WHAT_FD, NULL)); 230738032Speter } 230864562Sgshapiro#endif /* XDEBUG */ 230938032Speter 231064562Sgshapiro /* create a return pipe */ 231164562Sgshapiro if (pipe(rpvect) < 0) 231238032Speter { 231364562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 231490792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 231590792Sgshapiro m->m_name); 231664562Sgshapiro (void) close(mpvect[0]); 231764562Sgshapiro (void) close(mpvect[1]); 231864562Sgshapiro if (tTd(11, 1)) 231990792Sgshapiro sm_dprintf("openmailer: NULL\n"); 232064562Sgshapiro rcode = EX_OSERR; 232164562Sgshapiro goto give_up; 232238032Speter } 232364562Sgshapiro#if XDEBUG 232464562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 232564562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 232664562Sgshapiro#endif /* XDEBUG */ 232738032Speter 232838032Speter /* 232938032Speter ** Actually fork the mailer process. 233038032Speter ** DOFORK is clever about retrying. 233138032Speter ** 233238032Speter ** Dispose of SIGCHLD signal catchers that may be laying 233364562Sgshapiro ** around so that endmailer will get it. 233438032Speter */ 233538032Speter 233690792Sgshapiro if (e->e_xfp != NULL) /* for debugging */ 233790792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 233890792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 233990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 234064562Sgshapiro 234164562Sgshapiro 234238032Speter DOFORK(FORK); 234338032Speter /* pid is set by DOFORK */ 234464562Sgshapiro 234538032Speter if (pid < 0) 234638032Speter { 234738032Speter /* failure */ 234838032Speter syserr("%s... openmailer(%s): cannot fork", 234990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 235038032Speter (void) close(mpvect[0]); 235138032Speter (void) close(mpvect[1]); 235264562Sgshapiro (void) close(rpvect[0]); 235364562Sgshapiro (void) close(rpvect[1]); 235438032Speter if (tTd(11, 1)) 235590792Sgshapiro sm_dprintf("openmailer: NULL\n"); 235638032Speter rcode = EX_OSERR; 235738032Speter goto give_up; 235838032Speter } 235938032Speter else if (pid == 0) 236038032Speter { 236164562Sgshapiro int save_errno; 236290792Sgshapiro int sff; 236338032Speter int new_euid = NO_UID; 236438032Speter int new_ruid = NO_UID; 236538032Speter int new_gid = NO_GID; 236690792Sgshapiro char *user = NULL; 236738032Speter struct stat stb; 236838032Speter extern int DtableSize; 236938032Speter 237090792Sgshapiro CurrentPid = getpid(); 237190792Sgshapiro 237280785Sgshapiro /* clear the events to turn off SIGALRMs */ 237390792Sgshapiro sm_clear_events(); 237480785Sgshapiro 237577349Sgshapiro /* Reset global flags */ 237677349Sgshapiro RestartRequest = NULL; 237790792Sgshapiro RestartWorkGroup = false; 237877349Sgshapiro ShutdownRequest = NULL; 237977349Sgshapiro PendingSignal = 0; 238077349Sgshapiro 238138032Speter if (e->e_lockfp != NULL) 238290792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, 238390792Sgshapiro SM_IO_WHAT_FD, 238490792Sgshapiro NULL)); 238538032Speter 238638032Speter /* child -- set up input & exec mailer */ 238790792Sgshapiro (void) sm_signal(SIGALRM, sm_signal_noop); 238890792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 238990792Sgshapiro (void) sm_signal(SIGHUP, SIG_IGN); 239090792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 239190792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 239280785Sgshapiro# ifdef SIGUSR1 239390792Sgshapiro (void) sm_signal(SIGUSR1, sm_signal_noop); 239480785Sgshapiro# endif /* SIGUSR1 */ 239538032Speter 239638032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 239738032Speter stb.st_mode = 0; 239838032Speter 239964562Sgshapiro# if HASSETUSERCONTEXT 240038032Speter /* 240138032Speter ** Set user resources. 240238032Speter */ 240338032Speter 240438032Speter if (contextaddr != NULL) 240538032Speter { 2406110560Sgshapiro int sucflags; 240738032Speter struct passwd *pwd; 240838032Speter 240938032Speter if (contextaddr->q_ruser != NULL) 241038032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 241138032Speter else 241238032Speter pwd = sm_getpwnam(contextaddr->q_user); 2413110560Sgshapiro sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2414110560Sgshapiro#ifdef LOGIN_SETMAC 2415110560Sgshapiro sucflags |= LOGIN_SETMAC; 2416110560Sgshapiro#endif /* LOGIN_SETMAC */ 2417102528Sgshapiro if (pwd != NULL && 2418102528Sgshapiro setusercontext(NULL, pwd, pwd->pw_uid, 2419110560Sgshapiro sucflags) == -1 && 2420102528Sgshapiro suidwarn) 2421102528Sgshapiro { 2422102528Sgshapiro syserr("openmailer: setusercontext() failed"); 2423102528Sgshapiro exit(EX_TEMPFAIL); 2424102528Sgshapiro } 242538032Speter } 242664562Sgshapiro# endif /* HASSETUSERCONTEXT */ 242738032Speter 242890792Sgshapiro#if HASNICE 242938032Speter /* tweak niceness */ 243038032Speter if (m->m_nice != 0) 243164562Sgshapiro (void) nice(m->m_nice); 243290792Sgshapiro#endif /* HASNICE */ 243338032Speter 243438032Speter /* reset group id */ 243538032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 2436132943Sgshapiro { 2437132943Sgshapiro if (m->m_gid == NO_GID) 2438132943Sgshapiro new_gid = RunAsGid; 2439132943Sgshapiro else 2440132943Sgshapiro new_gid = m->m_gid; 2441132943Sgshapiro } 244238032Speter else if (bitset(S_ISGID, stb.st_mode)) 244338032Speter new_gid = stb.st_gid; 244438032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 244538032Speter { 244638032Speter if (!DontInitGroups) 244738032Speter { 244890792Sgshapiro user = ctladdr->q_ruser; 244990792Sgshapiro if (user == NULL) 245090792Sgshapiro user = ctladdr->q_user; 245138032Speter 245290792Sgshapiro if (initgroups(user, 245390792Sgshapiro ctladdr->q_gid) == -1 245490792Sgshapiro && suidwarn) 245564562Sgshapiro { 245638032Speter syserr("openmailer: initgroups(%s, %d) failed", 245790792Sgshapiro user, ctladdr->q_gid); 245864562Sgshapiro exit(EX_TEMPFAIL); 245964562Sgshapiro } 246038032Speter } 246138032Speter else 246238032Speter { 246338032Speter GIDSET_T gidset[1]; 246438032Speter 246538032Speter gidset[0] = ctladdr->q_gid; 246690792Sgshapiro if (setgroups(1, gidset) == -1 246790792Sgshapiro && suidwarn) 246864562Sgshapiro { 246938032Speter syserr("openmailer: setgroups() failed"); 247064562Sgshapiro exit(EX_TEMPFAIL); 247164562Sgshapiro } 247238032Speter } 247338032Speter new_gid = ctladdr->q_gid; 247438032Speter } 247538032Speter else 247638032Speter { 247738032Speter if (!DontInitGroups) 247838032Speter { 247990792Sgshapiro user = DefUser; 248090792Sgshapiro if (initgroups(DefUser, DefGid) == -1 && 248190792Sgshapiro suidwarn) 248264562Sgshapiro { 248338032Speter syserr("openmailer: initgroups(%s, %d) failed", 248490792Sgshapiro DefUser, DefGid); 248564562Sgshapiro exit(EX_TEMPFAIL); 248664562Sgshapiro } 248738032Speter } 248838032Speter else 248938032Speter { 249038032Speter GIDSET_T gidset[1]; 249138032Speter 249238032Speter gidset[0] = DefGid; 249390792Sgshapiro if (setgroups(1, gidset) == -1 249490792Sgshapiro && suidwarn) 249564562Sgshapiro { 249638032Speter syserr("openmailer: setgroups() failed"); 249764562Sgshapiro exit(EX_TEMPFAIL); 249864562Sgshapiro } 249938032Speter } 2500132943Sgshapiro if (m->m_gid == NO_GID) 250138032Speter new_gid = DefGid; 250238032Speter else 250338032Speter new_gid = m->m_gid; 250438032Speter } 250564562Sgshapiro if (new_gid != NO_GID) 250664562Sgshapiro { 250764562Sgshapiro if (RunAsUid != 0 && 250864562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 250964562Sgshapiro new_gid != getgid() && 251064562Sgshapiro new_gid != getegid()) 251164562Sgshapiro { 251264562Sgshapiro /* Only root can change the gid */ 251390792Sgshapiro syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d", 251490792Sgshapiro (int) RunAsUid, (int) new_gid, 251590792Sgshapiro (int) getgid(), (int) getegid()); 251664562Sgshapiro exit(EX_TEMPFAIL); 251764562Sgshapiro } 251838032Speter 251964562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 252064562Sgshapiro { 252164562Sgshapiro syserr("openmailer: setgid(%ld) failed", 252264562Sgshapiro (long) new_gid); 252364562Sgshapiro exit(EX_TEMPFAIL); 252464562Sgshapiro } 252564562Sgshapiro } 252664562Sgshapiro 252764562Sgshapiro /* change root to some "safe" directory */ 252864562Sgshapiro if (m->m_rootdir != NULL) 252964562Sgshapiro { 253098121Sgshapiro expand(m->m_rootdir, cbuf, sizeof cbuf, e); 253164562Sgshapiro if (tTd(11, 20)) 253290792Sgshapiro sm_dprintf("openmailer: chroot %s\n", 253398121Sgshapiro cbuf); 253498121Sgshapiro if (chroot(cbuf) < 0) 253564562Sgshapiro { 253664562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 253798121Sgshapiro cbuf); 253864562Sgshapiro exit(EX_TEMPFAIL); 253964562Sgshapiro } 254064562Sgshapiro if (chdir("/") < 0) 254164562Sgshapiro { 254264562Sgshapiro syserr("openmailer: cannot chdir(/)"); 254364562Sgshapiro exit(EX_TEMPFAIL); 254464562Sgshapiro } 254564562Sgshapiro } 254664562Sgshapiro 254738032Speter /* reset user id */ 254838032Speter endpwent(); 254990792Sgshapiro sm_mbdb_terminate(); 255038032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 255180785Sgshapiro { 2552132943Sgshapiro if (m->m_uid == NO_UID) 2553132943Sgshapiro new_euid = RunAsUid; 2554132943Sgshapiro else 2555132943Sgshapiro new_euid = m->m_uid; 255680785Sgshapiro 255780785Sgshapiro /* 255880785Sgshapiro ** Undo the effects of the uid change in main 255980785Sgshapiro ** for signal handling. The real uid may 256080785Sgshapiro ** be used by mailer in adding a "From " 256180785Sgshapiro ** line. 256280785Sgshapiro */ 256380785Sgshapiro 256480785Sgshapiro if (RealUid != 0 && RealUid != getuid()) 256590792Sgshapiro { 256690792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 256790792Sgshapiro# if HASSETREUID 256890792Sgshapiro if (setreuid(RealUid, geteuid()) < 0) 256990792Sgshapiro { 257090792Sgshapiro syserr("openmailer: setreuid(%d, %d) failed", 257190792Sgshapiro (int) RealUid, (int) geteuid()); 257290792Sgshapiro exit(EX_OSERR); 257390792Sgshapiro } 257490792Sgshapiro# endif /* HASSETREUID */ 257590792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 257690792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 257780785Sgshapiro new_ruid = RealUid; 257890792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 257990792Sgshapiro } 258080785Sgshapiro } 258138032Speter else if (bitset(S_ISUID, stb.st_mode)) 258238032Speter new_ruid = stb.st_uid; 258338032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 258438032Speter new_ruid = ctladdr->q_uid; 2585132943Sgshapiro else if (m->m_uid != NO_UID) 258638032Speter new_ruid = m->m_uid; 258738032Speter else 258838032Speter new_ruid = DefUid; 258994334Sgshapiro 259094334Sgshapiro# if _FFR_USE_SETLOGIN 259194334Sgshapiro /* run disconnected from terminal and set login name */ 259294334Sgshapiro if (setsid() >= 0 && 259394334Sgshapiro ctladdr != NULL && ctladdr->q_uid != 0 && 259494334Sgshapiro new_euid == ctladdr->q_uid) 259594334Sgshapiro { 259694334Sgshapiro struct passwd *pwd; 259794334Sgshapiro 259894334Sgshapiro pwd = sm_getpwuid(ctladdr->q_uid); 259994334Sgshapiro if (pwd != NULL && suidwarn) 260094334Sgshapiro (void) setlogin(pwd->pw_name); 260194334Sgshapiro endpwent(); 260294334Sgshapiro } 260394334Sgshapiro# endif /* _FFR_USE_SETLOGIN */ 260494334Sgshapiro 260538032Speter if (new_euid != NO_UID) 260638032Speter { 260764562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 260864562Sgshapiro { 260964562Sgshapiro /* Only root can change the uid */ 261090792Sgshapiro syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d", 261190792Sgshapiro (int) new_euid, (int) RunAsUid); 261264562Sgshapiro exit(EX_TEMPFAIL); 261364562Sgshapiro } 261464562Sgshapiro 261538032Speter vendor_set_uid(new_euid); 261664562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 261738032Speter if (seteuid(new_euid) < 0 && suidwarn) 261864562Sgshapiro { 261938032Speter syserr("openmailer: seteuid(%ld) failed", 262090792Sgshapiro (long) new_euid); 262164562Sgshapiro exit(EX_TEMPFAIL); 262264562Sgshapiro } 262364562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 262464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 262538032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 262664562Sgshapiro { 262738032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 262890792Sgshapiro (long) new_ruid, (long) new_euid); 262964562Sgshapiro exit(EX_TEMPFAIL); 263064562Sgshapiro } 263164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 263264562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID 263338032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 263464562Sgshapiro { 263538032Speter syserr("openmailer: setuid(%ld) failed", 263690792Sgshapiro (long) new_euid); 263764562Sgshapiro exit(EX_TEMPFAIL); 263864562Sgshapiro } 263964562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */ 264038032Speter } 264138032Speter else if (new_ruid != NO_UID) 264238032Speter { 264338032Speter vendor_set_uid(new_ruid); 264438032Speter if (setuid(new_ruid) < 0 && suidwarn) 264564562Sgshapiro { 264638032Speter syserr("openmailer: setuid(%ld) failed", 264790792Sgshapiro (long) new_ruid); 264864562Sgshapiro exit(EX_TEMPFAIL); 264964562Sgshapiro } 265038032Speter } 265138032Speter 265238032Speter if (tTd(11, 2)) 265390792Sgshapiro sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 265490792Sgshapiro (int) getuid(), (int) geteuid(), 265590792Sgshapiro (int) getgid(), (int) getegid()); 265638032Speter 265738032Speter /* move into some "safe" directory */ 265838032Speter if (m->m_execdir != NULL) 265938032Speter { 266038032Speter char *q; 266138032Speter 266238032Speter for (p = m->m_execdir; p != NULL; p = q) 266338032Speter { 266438032Speter q = strchr(p, ':'); 266538032Speter if (q != NULL) 266638032Speter *q = '\0'; 266798121Sgshapiro expand(p, cbuf, sizeof cbuf, e); 266838032Speter if (q != NULL) 266938032Speter *q++ = ':'; 267038032Speter if (tTd(11, 20)) 267190792Sgshapiro sm_dprintf("openmailer: trydir %s\n", 267298121Sgshapiro cbuf); 267398121Sgshapiro if (cbuf[0] != '\0' && 267498121Sgshapiro chdir(cbuf) >= 0) 267538032Speter break; 267638032Speter } 267738032Speter } 267838032Speter 267990792Sgshapiro /* Check safety of program to be run */ 268090792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 268190792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, 268290792Sgshapiro DontBlameSendmail)) 268390792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 268490792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 268590792Sgshapiro DontBlameSendmail)) 268690792Sgshapiro sff |= SFF_NOPATHCHECK; 268790792Sgshapiro else 268890792Sgshapiro sff |= SFF_SAFEDIRPATH; 268990792Sgshapiro ret = safefile(m->m_mailer, getuid(), getgid(), 269090792Sgshapiro user, sff, 0, NULL); 269190792Sgshapiro if (ret != 0) 269290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 269390792Sgshapiro "Warning: program %s unsafe: %s", 269490792Sgshapiro m->m_mailer, sm_errstring(ret)); 269590792Sgshapiro 269638032Speter /* arrange to filter std & diag output of command */ 269764562Sgshapiro (void) close(rpvect[0]); 269864562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 269938032Speter { 270064562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 270164562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 270264562Sgshapiro m->m_name, rpvect[1]); 270364562Sgshapiro _exit(EX_OSERR); 270438032Speter } 270564562Sgshapiro (void) close(rpvect[1]); 270664562Sgshapiro 270738032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 270838032Speter { 270938032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 271090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 271190792Sgshapiro m->m_name); 271238032Speter _exit(EX_OSERR); 271338032Speter } 271438032Speter 271538032Speter /* arrange to get standard input */ 271638032Speter (void) close(mpvect[1]); 271738032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 271838032Speter { 271938032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 272090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 272190792Sgshapiro m->m_name, mpvect[0]); 272238032Speter _exit(EX_OSERR); 272338032Speter } 272438032Speter (void) close(mpvect[0]); 272538032Speter 272638032Speter /* arrange for all the files to be closed */ 2727132943Sgshapiro sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 272838032Speter 272994334Sgshapiro# if !_FFR_USE_SETLOGIN 273038032Speter /* run disconnected from terminal */ 273138032Speter (void) setsid(); 273294334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */ 273338032Speter 273438032Speter /* try to execute the mailer */ 273564562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 273664562Sgshapiro (ARGV_T) UserEnviron); 273764562Sgshapiro save_errno = errno; 273838032Speter syserr("Cannot exec %s", m->m_mailer); 273938032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 274064562Sgshapiro transienterror(save_errno)) 274138032Speter _exit(EX_OSERR); 274238032Speter _exit(EX_UNAVAILABLE); 274338032Speter } 274438032Speter 274538032Speter /* 274638032Speter ** Set up return value. 274738032Speter */ 274838032Speter 274938032Speter if (mci == NULL) 275038032Speter { 275190792Sgshapiro if (clever) 275290792Sgshapiro { 275390792Sgshapiro /* 275490792Sgshapiro ** Allocate from general heap, not 275590792Sgshapiro ** envelope rpool, because this mci 275690792Sgshapiro ** is going to be cached. 275790792Sgshapiro */ 275890792Sgshapiro 275990792Sgshapiro mci = mci_new(NULL); 276090792Sgshapiro } 276190792Sgshapiro else 276290792Sgshapiro { 276390792Sgshapiro /* 276490792Sgshapiro ** Prevent a storage leak by allocating 276590792Sgshapiro ** this from the envelope rpool. 276690792Sgshapiro */ 276790792Sgshapiro 276890792Sgshapiro mci = mci_new(e->e_rpool); 276990792Sgshapiro } 277038032Speter } 277138032Speter mci->mci_mailer = m; 277238032Speter if (clever) 277338032Speter { 277438032Speter mci->mci_state = MCIS_OPENING; 277538032Speter mci_cache(mci); 277638032Speter } 277738032Speter else 277838032Speter { 277938032Speter mci->mci_state = MCIS_OPEN; 278038032Speter } 278138032Speter mci->mci_pid = pid; 278238032Speter (void) close(mpvect[0]); 278390792Sgshapiro mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2784132943Sgshapiro (void *) &(mpvect[1]), SM_IO_WRONLY_B, 278590792Sgshapiro NULL); 278638032Speter if (mci->mci_out == NULL) 278738032Speter { 278838032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 278990792Sgshapiro mpvect[1]); 279038032Speter (void) close(mpvect[1]); 279164562Sgshapiro (void) close(rpvect[0]); 279264562Sgshapiro (void) close(rpvect[1]); 279338032Speter rcode = EX_OSERR; 279438032Speter goto give_up; 279538032Speter } 279664562Sgshapiro 279764562Sgshapiro (void) close(rpvect[1]); 279890792Sgshapiro mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2799132943Sgshapiro (void *) &(rpvect[0]), SM_IO_RDONLY_B, 280090792Sgshapiro NULL); 280164562Sgshapiro if (mci->mci_in == NULL) 280238032Speter { 280364562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 280464562Sgshapiro mpvect[1]); 280564562Sgshapiro (void) close(rpvect[0]); 280690792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 280764562Sgshapiro mci->mci_out = NULL; 280864562Sgshapiro rcode = EX_OSERR; 280964562Sgshapiro goto give_up; 281038032Speter } 281138032Speter } 281238032Speter 281338032Speter /* 281438032Speter ** If we are in SMTP opening state, send initial protocol. 281538032Speter */ 281638032Speter 281738032Speter if (bitnset(M_7BITS, m->m_flags) && 281838032Speter (!clever || mci->mci_state == MCIS_OPENING)) 281938032Speter mci->mci_flags |= MCIF_7BIT; 282038032Speter if (clever && mci->mci_state != MCIS_CLOSED) 282138032Speter { 282290792Sgshapiro# if STARTTLS || SASL 282390792Sgshapiro int dotpos; 282490792Sgshapiro char *srvname; 282590792Sgshapiro extern SOCKADDR CurHostAddr; 282690792Sgshapiro# endif /* STARTTLS || SASL */ 282790792Sgshapiro 282890792Sgshapiro# if SASL 282971345Sgshapiro# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 283090792Sgshapiro# endif /* SASL */ 283164562Sgshapiro# if STARTTLS 283271345Sgshapiro# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 283364562Sgshapiro# endif /* STARTTLS */ 283471345Sgshapiro# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 283571345Sgshapiro# define SET_HELO(f) f |= MCIF_ONLY_EHLO 283671345Sgshapiro# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 283738032Speter 283890792Sgshapiro# if STARTTLS || SASL 283990792Sgshapiro /* don't use CurHostName, it is changed in many places */ 284090792Sgshapiro if (mci->mci_host != NULL) 284190792Sgshapiro { 284290792Sgshapiro srvname = mci->mci_host; 284390792Sgshapiro dotpos = strlen(srvname) - 1; 284490792Sgshapiro if (dotpos >= 0) 284590792Sgshapiro { 284690792Sgshapiro if (srvname[dotpos] == '.') 284790792Sgshapiro srvname[dotpos] = '\0'; 284890792Sgshapiro else 284990792Sgshapiro dotpos = -1; 285090792Sgshapiro } 285190792Sgshapiro } 285290792Sgshapiro else if (mci->mci_mailer != NULL) 285390792Sgshapiro { 285490792Sgshapiro srvname = mci->mci_mailer->m_name; 285590792Sgshapiro dotpos = -1; 285690792Sgshapiro } 285790792Sgshapiro else 285890792Sgshapiro { 285990792Sgshapiro srvname = "local"; 286090792Sgshapiro dotpos = -1; 286190792Sgshapiro } 286271345Sgshapiro 286390792Sgshapiro /* don't set {server_name} to NULL or "": see getauth() */ 286490792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 286590792Sgshapiro srvname); 286664562Sgshapiro 286790792Sgshapiro /* CurHostAddr is set by makeconnection() and mci_get() */ 286890792Sgshapiro if (CurHostAddr.sa.sa_family != 0) 286990792Sgshapiro { 287090792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, 287190792Sgshapiro macid("{server_addr}"), 287290792Sgshapiro anynet_ntoa(&CurHostAddr)); 287390792Sgshapiro } 287490792Sgshapiro else if (mci->mci_mailer != NULL) 287590792Sgshapiro { 287690792Sgshapiro /* mailer name is unique, use it as address */ 287790792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 287890792Sgshapiro macid("{server_addr}"), 287990792Sgshapiro mci->mci_mailer->m_name); 288090792Sgshapiro } 288190792Sgshapiro else 288290792Sgshapiro { 288390792Sgshapiro /* don't set it to NULL or "": see getauth() */ 288490792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 288590792Sgshapiro macid("{server_addr}"), "0"); 288690792Sgshapiro } 288790792Sgshapiro 288890792Sgshapiro /* undo change of srvname (mci->mci_host) */ 288990792Sgshapiro if (dotpos >= 0) 289090792Sgshapiro srvname[dotpos] = '.'; 289190792Sgshapiro 289290792Sgshapiroreconnect: /* after switching to an encrypted connection */ 289390792Sgshapiro# endif /* STARTTLS || SASL */ 289490792Sgshapiro 289590792Sgshapiro /* set the current connection information */ 289690792Sgshapiro e->e_mci = mci; 289764562Sgshapiro# if SASL 289864562Sgshapiro mci->mci_saslcap = NULL; 289964562Sgshapiro# endif /* SASL */ 290071345Sgshapiro smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 290171345Sgshapiro CLR_HELO(mci->mci_flags); 290264562Sgshapiro 290390792Sgshapiro if (IS_DLVR_RETURN(e)) 290490792Sgshapiro { 290590792Sgshapiro /* 290690792Sgshapiro ** Check whether other side can deliver e-mail 290790792Sgshapiro ** fast enough 290890792Sgshapiro */ 290990792Sgshapiro 291090792Sgshapiro if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 291190792Sgshapiro { 291290792Sgshapiro e->e_status = "5.4.7"; 291390792Sgshapiro usrerrenh(e->e_status, 291490792Sgshapiro "554 Server does not support Deliver By"); 291590792Sgshapiro rcode = EX_UNAVAILABLE; 291690792Sgshapiro goto give_up; 291790792Sgshapiro } 291890792Sgshapiro if (e->e_deliver_by > 0 && 291990792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime) < 292090792Sgshapiro mci->mci_min_by) 292190792Sgshapiro { 292290792Sgshapiro e->e_status = "5.4.7"; 292390792Sgshapiro usrerrenh(e->e_status, 292490792Sgshapiro "554 Message can't be delivered in time; %ld < %ld", 292590792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime), 292690792Sgshapiro mci->mci_min_by); 292790792Sgshapiro rcode = EX_UNAVAILABLE; 292890792Sgshapiro goto give_up; 292990792Sgshapiro } 293090792Sgshapiro } 293190792Sgshapiro 293264562Sgshapiro# if STARTTLS 293364562Sgshapiro /* first TLS then AUTH to provide a security layer */ 293471345Sgshapiro if (mci->mci_state != MCIS_CLOSED && 293571345Sgshapiro !DONE_STARTTLS(mci->mci_flags)) 293664562Sgshapiro { 293764562Sgshapiro int olderrors; 293864562Sgshapiro bool usetls; 293964562Sgshapiro bool saveQuickAbort = QuickAbort; 294064562Sgshapiro bool saveSuprErrs = SuprErrs; 294171345Sgshapiro char *host = NULL; 294264562Sgshapiro 294364562Sgshapiro rcode = EX_OK; 294464562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 294590792Sgshapiro if (usetls) 294690792Sgshapiro usetls = !iscltflgset(e, D_NOTLS); 294766494Sgshapiro 294864562Sgshapiro if (usetls) 294964562Sgshapiro { 295090792Sgshapiro host = macvalue(macid("{server_name}"), e); 295164562Sgshapiro olderrors = Errors; 295290792Sgshapiro QuickAbort = false; 295390792Sgshapiro SuprErrs = true; 2954102528Sgshapiro if (rscheck("try_tls", host, NULL, e, 2955102528Sgshapiro RSF_RMCOMM, 7, host, NOQID) != EX_OK 295664562Sgshapiro || Errors > olderrors) 295790792Sgshapiro usetls = false; 295864562Sgshapiro SuprErrs = saveSuprErrs; 295964562Sgshapiro QuickAbort = saveQuickAbort; 296064562Sgshapiro } 296164562Sgshapiro 296264562Sgshapiro if (usetls) 296364562Sgshapiro { 296464562Sgshapiro if ((rcode = starttls(m, mci, e)) == EX_OK) 296564562Sgshapiro { 296664562Sgshapiro /* start again without STARTTLS */ 296764562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 296864562Sgshapiro } 296964562Sgshapiro else 297064562Sgshapiro { 297164562Sgshapiro char *s; 297264562Sgshapiro 297364562Sgshapiro /* 297464562Sgshapiro ** TLS negotation failed, what to do? 297564562Sgshapiro ** fall back to unencrypted connection 297664562Sgshapiro ** or abort? How to decide? 297764562Sgshapiro ** set a macro and call a ruleset. 297864562Sgshapiro */ 297990792Sgshapiro 298064562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 298164562Sgshapiro switch (rcode) 298264562Sgshapiro { 298364562Sgshapiro case EX_TEMPFAIL: 298464562Sgshapiro s = "TEMP"; 298564562Sgshapiro break; 298664562Sgshapiro case EX_USAGE: 298764562Sgshapiro s = "USAGE"; 298864562Sgshapiro break; 298964562Sgshapiro case EX_PROTOCOL: 299064562Sgshapiro s = "PROTOCOL"; 299164562Sgshapiro break; 299264562Sgshapiro case EX_SOFTWARE: 299364562Sgshapiro s = "SOFTWARE"; 299464562Sgshapiro break; 2995157001Sgshapiro case EX_UNAVAILABLE: 2996157001Sgshapiro s = "NONE"; 2997157001Sgshapiro break; 299864562Sgshapiro 299964562Sgshapiro /* everything else is a failure */ 300064562Sgshapiro default: 300164562Sgshapiro s = "FAILURE"; 300264562Sgshapiro rcode = EX_TEMPFAIL; 300364562Sgshapiro } 300490792Sgshapiro macdefine(&e->e_macro, A_PERM, 300590792Sgshapiro macid("{verify}"), s); 300664562Sgshapiro } 300764562Sgshapiro } 300864562Sgshapiro else 300990792Sgshapiro macdefine(&e->e_macro, A_PERM, 301090792Sgshapiro macid("{verify}"), "NONE"); 301164562Sgshapiro olderrors = Errors; 301290792Sgshapiro QuickAbort = false; 301390792Sgshapiro SuprErrs = true; 301464562Sgshapiro 301564562Sgshapiro /* 301664562Sgshapiro ** rcode == EX_SOFTWARE is special: 301764562Sgshapiro ** the TLS negotation failed 301864562Sgshapiro ** we have to drop the connection no matter what 301964562Sgshapiro ** However, we call tls_server to give it the chance 302064562Sgshapiro ** to log the problem and return an appropriate 302164562Sgshapiro ** error code. 302264562Sgshapiro */ 302390792Sgshapiro 302464562Sgshapiro if (rscheck("tls_server", 302590792Sgshapiro macvalue(macid("{verify}"), e), 3026102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3027102528Sgshapiro host, NOQID) != EX_OK || 302864562Sgshapiro Errors > olderrors || 302964562Sgshapiro rcode == EX_SOFTWARE) 303064562Sgshapiro { 303164562Sgshapiro char enhsc[ENHSCLEN]; 303264562Sgshapiro extern char MsgBuf[]; 303364562Sgshapiro 303464562Sgshapiro if (ISSMTPCODE(MsgBuf) && 303564562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 303664562Sgshapiro { 303790792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 303890792Sgshapiro MsgBuf); 303964562Sgshapiro } 304064562Sgshapiro else 304164562Sgshapiro { 304264562Sgshapiro p = "403 4.7.0 server not authenticated."; 304390792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 304490792Sgshapiro sizeof enhsc); 304564562Sgshapiro } 304664562Sgshapiro SuprErrs = saveSuprErrs; 304764562Sgshapiro QuickAbort = saveQuickAbort; 304864562Sgshapiro 304964562Sgshapiro if (rcode == EX_SOFTWARE) 305064562Sgshapiro { 305164562Sgshapiro /* drop the connection */ 305264562Sgshapiro mci->mci_state = MCIS_QUITING; 305364562Sgshapiro if (mci->mci_in != NULL) 305464562Sgshapiro { 305590792Sgshapiro (void) sm_io_close(mci->mci_in, 305690792Sgshapiro SM_TIME_DEFAULT); 305764562Sgshapiro mci->mci_in = NULL; 305864562Sgshapiro } 305964562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 306064562Sgshapiro (void) endmailer(mci, e, pv); 306164562Sgshapiro } 306264562Sgshapiro else 306364562Sgshapiro { 306464562Sgshapiro /* abort transfer */ 306564562Sgshapiro smtpquit(m, mci, e); 306664562Sgshapiro } 306764562Sgshapiro 306871345Sgshapiro /* avoid bogus error msg */ 306971345Sgshapiro mci->mci_errno = 0; 307071345Sgshapiro 307164562Sgshapiro /* temp or permanent failure? */ 307264562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 307364562Sgshapiro : EX_UNAVAILABLE; 307490792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 307564562Sgshapiro 307664562Sgshapiro /* 307764562Sgshapiro ** hack to get the error message into 307864562Sgshapiro ** the envelope (done in giveresponse()) 307964562Sgshapiro */ 308090792Sgshapiro 308190792Sgshapiro (void) sm_strlcpy(SmtpError, p, 308290792Sgshapiro sizeof SmtpError); 308364562Sgshapiro } 308464562Sgshapiro QuickAbort = saveQuickAbort; 308564562Sgshapiro SuprErrs = saveSuprErrs; 308671345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 308771345Sgshapiro mci->mci_state != MCIS_CLOSED) 308864562Sgshapiro { 308971345Sgshapiro SET_HELO(mci->mci_flags); 309064562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 309164562Sgshapiro goto reconnect; 309264562Sgshapiro } 309364562Sgshapiro } 309464562Sgshapiro# endif /* STARTTLS */ 309564562Sgshapiro# if SASL 309664562Sgshapiro /* if other server supports authentication let's authenticate */ 309764562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 309864562Sgshapiro mci->mci_saslcap != NULL && 309990792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 310064562Sgshapiro { 310190792Sgshapiro /* Should we require some minimum authentication? */ 310290792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 310364562Sgshapiro { 310464562Sgshapiro int result; 310590792Sgshapiro sasl_ssf_t *ssf = NULL; 310664562Sgshapiro 310790792Sgshapiro /* Get security strength (features) */ 310864562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 310998121Sgshapiro# if SASL >= 20000 311098121Sgshapiro (const void **) &ssf); 311198121Sgshapiro# else /* SASL >= 20000 */ 311264562Sgshapiro (void **) &ssf); 311398121Sgshapiro# endif /* SASL >= 20000 */ 311490792Sgshapiro 311590792Sgshapiro /* XXX authid? */ 311664562Sgshapiro if (LogLevel > 9) 311764562Sgshapiro sm_syslog(LOG_INFO, NOQID, 311890792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 311964562Sgshapiro mci->mci_host, 312090792Sgshapiro macvalue(macid("{auth_type}"), e), 312190792Sgshapiro result == SASL_OK ? *ssf : 0); 312277349Sgshapiro 312364562Sgshapiro /* 312490792Sgshapiro ** Only switch to encrypted connection 312564562Sgshapiro ** if a security layer has been negotiated 312664562Sgshapiro */ 312790792Sgshapiro 312864562Sgshapiro if (result == SASL_OK && *ssf > 0) 312964562Sgshapiro { 3130159609Sgshapiro int tmo; 3131159609Sgshapiro 313264562Sgshapiro /* 313390792Sgshapiro ** Convert I/O layer to use SASL. 313490792Sgshapiro ** If the call fails, the connection 313590792Sgshapiro ** is aborted. 313664562Sgshapiro */ 313790792Sgshapiro 3138159609Sgshapiro tmo = DATA_PROGRESS_TIMEOUT * 1000; 313990792Sgshapiro if (sfdcsasl(&mci->mci_in, 314090792Sgshapiro &mci->mci_out, 3141159609Sgshapiro mci->mci_conn, tmo) == 0) 314264562Sgshapiro { 314364562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 314490792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 314590792Sgshapiro MCIF_ONLY_EHLO; 314664562Sgshapiro goto reconnect; 314764562Sgshapiro } 314890792Sgshapiro syserr("AUTH TLS switch failed in client"); 314964562Sgshapiro } 315064562Sgshapiro /* else? XXX */ 315164562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 315264562Sgshapiro 315364562Sgshapiro } 315490792Sgshapiro else if (ret == EX_TEMPFAIL) 315590792Sgshapiro { 315690792Sgshapiro if (LogLevel > 8) 315790792Sgshapiro sm_syslog(LOG_ERR, NOQID, 315890792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 315990792Sgshapiro mci->mci_host); 316090792Sgshapiro smtpquit(m, mci, e); 316190792Sgshapiro 316290792Sgshapiro /* avoid bogus error msg */ 316390792Sgshapiro mci->mci_errno = 0; 316490792Sgshapiro rcode = EX_TEMPFAIL; 3165132943Sgshapiro mci_setstat(mci, rcode, "4.3.0", p); 316690792Sgshapiro 316790792Sgshapiro /* 316890792Sgshapiro ** hack to get the error message into 316990792Sgshapiro ** the envelope (done in giveresponse()) 317090792Sgshapiro */ 317190792Sgshapiro 317290792Sgshapiro (void) sm_strlcpy(SmtpError, 317390792Sgshapiro "Temporary AUTH failure", 317490792Sgshapiro sizeof SmtpError); 317590792Sgshapiro } 317664562Sgshapiro } 317764562Sgshapiro# endif /* SASL */ 317838032Speter } 317938032Speter 318064562Sgshapiro 318138032Speterdo_transfer: 318238032Speter /* clear out per-message flags from connection structure */ 318338032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 318438032Speter 318538032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 318638032Speter !bitset(EF_DONT_MIME, e->e_flags) && 318738032Speter bitnset(M_7BITS, m->m_flags)) 318838032Speter mci->mci_flags |= MCIF_CVT8TO7; 318938032Speter 319038032Speter#if MIME7TO8 319138032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 319238032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 319338032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 319490792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 319590792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 319638032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 319738032Speter { 319838032Speter /* may want to convert 7 -> 8 */ 319938032Speter /* XXX should really parse it here -- and use a class XXX */ 320090792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 320138032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 320238032Speter mci->mci_flags |= MCIF_CVT7TO8; 320338032Speter } 320464562Sgshapiro#endif /* MIME7TO8 */ 320538032Speter 320638032Speter if (tTd(11, 1)) 320738032Speter { 320890792Sgshapiro sm_dprintf("openmailer: "); 3209132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 321038032Speter } 321138032Speter 321290792Sgshapiro#if _FFR_CLIENT_SIZE 321390792Sgshapiro /* 321490792Sgshapiro ** See if we know the maximum size and 321590792Sgshapiro ** abort if the message is too big. 321690792Sgshapiro ** 321790792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 321890792Sgshapiro */ 321990792Sgshapiro 322090792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 322190792Sgshapiro mci->mci_maxsize > 0 && 322290792Sgshapiro e->e_msgsize > mci->mci_maxsize) 322390792Sgshapiro { 322490792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 322590792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 322690792Sgshapiro e->e_status = "5.2.3"; 322790792Sgshapiro else 322890792Sgshapiro e->e_status = "5.3.4"; 322990792Sgshapiro 323090792Sgshapiro usrerrenh(e->e_status, 323190792Sgshapiro "552 Message is too large; %ld bytes max", 323290792Sgshapiro mci->mci_maxsize); 323390792Sgshapiro rcode = EX_DATAERR; 323490792Sgshapiro 323590792Sgshapiro /* Need an e_message for error */ 323690792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 323790792Sgshapiro "Message is too large; %ld bytes max", 323890792Sgshapiro mci->mci_maxsize); 323990792Sgshapiro goto give_up; 324090792Sgshapiro } 324190792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 324290792Sgshapiro 324338032Speter if (mci->mci_state != MCIS_OPEN) 324438032Speter { 324538032Speter /* couldn't open the mailer */ 324638032Speter rcode = mci->mci_exitstat; 324738032Speter errno = mci->mci_errno; 324873188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 324938032Speter if (rcode == EX_OK) 325038032Speter { 325138032Speter /* shouldn't happen */ 325264562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 325390792Sgshapiro (unsigned long) mci, rcode, errno, 325490792Sgshapiro mci->mci_state, firstsig); 3255132943Sgshapiro mci_dump_all(smioout, true); 325638032Speter rcode = EX_SOFTWARE; 325738032Speter } 325864562Sgshapiro else if (nummxhosts > hostnum) 325938032Speter { 326038032Speter /* try next MX site */ 326138032Speter goto tryhost; 326238032Speter } 326338032Speter } 326438032Speter else if (!clever) 326538032Speter { 3266157001Sgshapiro bool ok; 3267157001Sgshapiro 326838032Speter /* 326938032Speter ** Format and send message. 327038032Speter */ 327138032Speter 3272157001Sgshapiro rcode = EX_OK; 3273157001Sgshapiro errno = 0; 3274157001Sgshapiro ok = putfromline(mci, e); 3275157001Sgshapiro if (ok) 3276157001Sgshapiro ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 3277157001Sgshapiro if (ok) 3278157001Sgshapiro ok = (*e->e_putbody)(mci, e, NULL); 327938032Speter 3280157001Sgshapiro /* 3281157001Sgshapiro ** Ignore an I/O error that was caused by EPIPE. 3282157001Sgshapiro ** Some broken mailers don't read the entire body 3283157001Sgshapiro ** but just exit() thus causing an I/O error. 3284157001Sgshapiro */ 3285157001Sgshapiro 3286157001Sgshapiro if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE)) 3287157001Sgshapiro ok = true; 3288157001Sgshapiro 3289157001Sgshapiro /* (always) get the exit status */ 329038032Speter rcode = endmailer(mci, e, pv); 3291157001Sgshapiro if (!ok) 3292157001Sgshapiro rcode = EX_TEMPFAIL; 329390792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 329473188Sgshapiro { 329573188Sgshapiro /* 329673188Sgshapiro ** Need an e_message for mailq display. 329773188Sgshapiro ** We set SmtpError as 329873188Sgshapiro */ 329973188Sgshapiro 330090792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 330190792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 330290792Sgshapiro m->m_name, m->m_mailer); 330373188Sgshapiro } 330438032Speter } 330538032Speter else 330638032Speter { 330738032Speter /* 330838032Speter ** Send the MAIL FROM: protocol 330938032Speter */ 331038032Speter 331190792Sgshapiro /* XXX this isn't pipelined... */ 331238032Speter rcode = smtpmailfrom(m, mci, e); 331338032Speter if (rcode == EX_OK) 331438032Speter { 331538032Speter register int i; 331690792Sgshapiro# if PIPELINING 331790792Sgshapiro ADDRESS *volatile pchain; 331890792Sgshapiro# endif /* PIPELINING */ 331938032Speter 332038032Speter /* send the recipient list */ 332138032Speter tobuf[0] = '\0'; 332290792Sgshapiro mci->mci_retryrcpt = false; 332390792Sgshapiro mci->mci_tolist = tobuf; 332490792Sgshapiro# if PIPELINING 332590792Sgshapiro pchain = NULL; 332690792Sgshapiro mci->mci_nextaddr = NULL; 332790792Sgshapiro# endif /* PIPELINING */ 332864562Sgshapiro 332938032Speter for (to = tochain; to != NULL; to = to->q_tchain) 333038032Speter { 333190792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 333238032Speter continue; 333364562Sgshapiro 333490792Sgshapiro /* mark recipient state as "ok so far" */ 333590792Sgshapiro to->q_state = QS_OK; 333690792Sgshapiro e->e_to = to->q_paddr; 333764562Sgshapiro# if STARTTLS 333864562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3339102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3340102528Sgshapiro mci->mci_host, e->e_id); 334164562Sgshapiro if (i != EX_OK) 334238032Speter { 334390792Sgshapiro markfailure(e, to, mci, i, false); 334490792Sgshapiro giveresponse(i, to->q_status, m, mci, 334590792Sgshapiro ctladdr, xstart, e, to); 334690792Sgshapiro if (i == EX_TEMPFAIL) 334790792Sgshapiro { 334890792Sgshapiro mci->mci_retryrcpt = true; 334990792Sgshapiro to->q_state = QS_RETRY; 335090792Sgshapiro } 335164562Sgshapiro continue; 335238032Speter } 335364562Sgshapiro# endif /* STARTTLS */ 335464562Sgshapiro 335590792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 335690792Sgshapiro# if PIPELINING 335790792Sgshapiro if (i == EX_OK && 335890792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 335964562Sgshapiro { 336090792Sgshapiro /* 336190792Sgshapiro ** Add new element to list of 336290792Sgshapiro ** recipients for pipelining. 336390792Sgshapiro */ 336490792Sgshapiro 336590792Sgshapiro to->q_pchain = NULL; 336690792Sgshapiro if (mci->mci_nextaddr == NULL) 336790792Sgshapiro mci->mci_nextaddr = to; 336890792Sgshapiro if (pchain == NULL) 336990792Sgshapiro pchain = to; 337090792Sgshapiro else 337190792Sgshapiro { 337290792Sgshapiro pchain->q_pchain = to; 337390792Sgshapiro pchain = pchain->q_pchain; 337490792Sgshapiro } 337564562Sgshapiro } 337690792Sgshapiro# endif /* PIPELINING */ 337790792Sgshapiro if (i != EX_OK) 337838032Speter { 337990792Sgshapiro markfailure(e, to, mci, i, false); 338098841Sgshapiro giveresponse(i, to->q_status, m, mci, 338190792Sgshapiro ctladdr, xstart, e, to); 338290792Sgshapiro if (i == EX_TEMPFAIL) 338390792Sgshapiro to->q_state = QS_RETRY; 338438032Speter } 338538032Speter } 338638032Speter 338790792Sgshapiro /* No recipients in list and no missing responses? */ 338890792Sgshapiro if (tobuf[0] == '\0' 338990792Sgshapiro# if PIPELINING 339090792Sgshapiro && mci->mci_nextaddr == NULL 339190792Sgshapiro# endif /* PIPELINING */ 339290792Sgshapiro ) 339338032Speter { 339438032Speter rcode = EX_OK; 339538032Speter e->e_to = NULL; 339638032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 339738032Speter smtprset(m, mci, e); 339838032Speter } 339938032Speter else 340038032Speter { 340138032Speter e->e_to = tobuf + 1; 340290792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 340338032Speter } 340438032Speter } 340564562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 340638032Speter { 340738032Speter /* try next MX site */ 340838032Speter goto tryhost; 340938032Speter } 341038032Speter } 341138032Speter#if NAMED_BIND 341238032Speter if (ConfigLevel < 2) 341338032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 341464562Sgshapiro#endif /* NAMED_BIND */ 341538032Speter 341638032Speter if (tTd(62, 1)) 341738032Speter checkfds("after delivery"); 341838032Speter 341938032Speter /* 342038032Speter ** Do final status disposal. 342138032Speter ** We check for something in tobuf for the SMTP case. 342238032Speter ** If we got a temporary failure, arrange to queue the 342338032Speter ** addressees. 342438032Speter */ 342538032Speter 342638032Speter give_up: 342738032Speter if (bitnset(M_LMTP, m->m_flags)) 342838032Speter { 342938032Speter lmtp_rcode = rcode; 343038032Speter tobuf[0] = '\0'; 343190792Sgshapiro anyok = false; 343290792Sgshapiro strsize = 0; 343338032Speter } 343438032Speter else 343538032Speter anyok = rcode == EX_OK; 343638032Speter 343738032Speter for (to = tochain; to != NULL; to = to->q_tchain) 343838032Speter { 343938032Speter /* see if address already marked */ 344064562Sgshapiro if (!QS_IS_OK(to->q_state)) 344138032Speter continue; 344238032Speter 344338032Speter /* if running LMTP, get the status for each address */ 344438032Speter if (bitnset(M_LMTP, m->m_flags)) 344538032Speter { 344638032Speter if (lmtp_rcode == EX_OK) 344738032Speter rcode = smtpgetstat(m, mci, e); 344838032Speter if (rcode == EX_OK) 344938032Speter { 345090792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 345190792Sgshapiro to->q_paddr, 345290792Sgshapiro tobufsize - strsize); 345390792Sgshapiro SM_ASSERT(strsize < tobufsize); 345490792Sgshapiro anyok = true; 345538032Speter } 345638032Speter else 345738032Speter { 345838032Speter e->e_to = to->q_paddr; 345990792Sgshapiro markfailure(e, to, mci, rcode, true); 346064562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 346190792Sgshapiro ctladdr, xstart, e, to); 346238032Speter e->e_to = tobuf + 1; 346338032Speter continue; 346438032Speter } 346538032Speter } 346638032Speter else 346738032Speter { 346838032Speter /* mark bad addresses */ 346938032Speter if (rcode != EX_OK) 347038032Speter { 347138032Speter if (goodmxfound && rcode == EX_NOHOST) 347238032Speter rcode = EX_TEMPFAIL; 347390792Sgshapiro markfailure(e, to, mci, rcode, true); 347438032Speter continue; 347538032Speter } 347638032Speter } 347738032Speter 347838032Speter /* successful delivery */ 347964562Sgshapiro to->q_state = QS_SENT; 348038032Speter to->q_statdate = curtime(); 348138032Speter e->e_nsent++; 348264562Sgshapiro 348364562Sgshapiro /* 348464562Sgshapiro ** Checkpoint the send list every few addresses 348564562Sgshapiro */ 348664562Sgshapiro 348766494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 348864562Sgshapiro { 348990792Sgshapiro queueup(e, false, false); 349064562Sgshapiro e->e_nsent = 0; 349164562Sgshapiro } 349264562Sgshapiro 349338032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 349438032Speter bitset(QPINGONSUCCESS, to->q_flags)) 349538032Speter { 349638032Speter to->q_flags |= QDELIVERED; 349738032Speter to->q_status = "2.1.5"; 349890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 349990792Sgshapiro "%s... Successfully delivered\n", 350090792Sgshapiro to->q_paddr); 350138032Speter } 350238032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 350338032Speter bitset(QPRIMARY, to->q_flags) && 350438032Speter !bitset(MCIF_DSN, mci->mci_flags)) 350538032Speter { 350638032Speter to->q_flags |= QRELAYED; 350790792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 350890792Sgshapiro "%s... relayed; expect no further notifications\n", 350990792Sgshapiro to->q_paddr); 351038032Speter } 351190792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 351290792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 351390792Sgshapiro bitset(QPRIMARY, to->q_flags) && 351490792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 351590792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 351690792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 351790792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 351890792Sgshapiro { 351990792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 352090792Sgshapiro to->q_flags |= QBYNRELAY; 352190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 352290792Sgshapiro "%s... Deliver-by notify: relayed\n", 352390792Sgshapiro to->q_paddr); 352490792Sgshapiro } 352590792Sgshapiro else if (IS_DLVR_TRACE(e) && 352690792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 352790792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 352890792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 352990792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 353090792Sgshapiro bitset(QPRIMARY, to->q_flags)) 353190792Sgshapiro { 353290792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 353390792Sgshapiro to->q_flags |= QBYTRACE; 353490792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 353590792Sgshapiro "%s... Deliver-By trace: relayed\n", 353690792Sgshapiro to->q_paddr); 353790792Sgshapiro } 353838032Speter } 353938032Speter 354038032Speter if (bitnset(M_LMTP, m->m_flags)) 354138032Speter { 354238032Speter /* 354338032Speter ** Global information applies to the last recipient only; 354438032Speter ** clear it out to avoid bogus errors. 354538032Speter */ 354638032Speter 354738032Speter rcode = EX_OK; 354838032Speter e->e_statmsg = NULL; 354938032Speter 355038032Speter /* reset the mci state for the next transaction */ 355190792Sgshapiro if (mci != NULL && 355290792Sgshapiro (mci->mci_state == MCIS_MAIL || 355390792Sgshapiro mci->mci_state == MCIS_RCPT || 355490792Sgshapiro mci->mci_state == MCIS_DATA)) 3555125820Sgshapiro { 355638032Speter mci->mci_state = MCIS_OPEN; 3557125820Sgshapiro SmtpPhase = mci->mci_phase = "idle"; 3558125820Sgshapiro sm_setproctitle(true, e, "%s: %s", CurHostName, 3559125820Sgshapiro mci->mci_phase); 3560125820Sgshapiro } 356138032Speter } 356238032Speter 356338032Speter if (tobuf[0] != '\0') 356490792Sgshapiro { 356590792Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 356690792Sgshapiro#if 0 356790792Sgshapiro /* 356890792Sgshapiro ** This code is disabled for now because I am not 356990792Sgshapiro ** sure that copying status from the first recipient 357090792Sgshapiro ** to all non-status'ed recipients is a good idea. 357190792Sgshapiro */ 357290792Sgshapiro 357390792Sgshapiro if (tochain->q_message != NULL && 357490792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 357590792Sgshapiro { 357690792Sgshapiro for (to = tochain->q_tchain; to != NULL; 357790792Sgshapiro to = to->q_tchain) 357890792Sgshapiro { 357990792Sgshapiro /* see if address already marked */ 358090792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 358190792Sgshapiro to->q_message == NULL) 358290792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 358390792Sgshapiro tochain->q_message); 358490792Sgshapiro } 358590792Sgshapiro } 358690792Sgshapiro#endif /* 0 */ 358790792Sgshapiro } 358838032Speter if (anyok) 358990792Sgshapiro markstats(e, tochain, STATS_NORMAL); 359038032Speter mci_store_persistent(mci); 359138032Speter 359290792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 359390792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 359490792Sgshapiro { 359590792Sgshapiro /* try next MX site */ 359690792Sgshapiro goto tryhost; 359790792Sgshapiro } 359890792Sgshapiro 359938032Speter /* now close the connection */ 360038032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 360138032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 360238032Speter smtpquit(m, mci, e); 360338032Speter 360490792Sgshapirocleanup: ; 360590792Sgshapiro } 360690792Sgshapiro SM_FINALLY 360790792Sgshapiro { 360890792Sgshapiro /* 360990792Sgshapiro ** Restore state and return. 361090792Sgshapiro */ 361138032Speter#if XDEBUG 361238032Speter char wbuf[MAXLINE]; 361338032Speter 361438032Speter /* make absolutely certain 0, 1, and 2 are in use */ 361590792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, 361690792Sgshapiro "%s... end of deliver(%s)", 361790792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 361890792Sgshapiro : shortenstring(e->e_to, 361990792Sgshapiro MAXSHORTSTR), 362090792Sgshapiro m->m_name); 362138032Speter checkfd012(wbuf); 362264562Sgshapiro#endif /* XDEBUG */ 362338032Speter 362490792Sgshapiro errno = 0; 362590792Sgshapiro 362690792Sgshapiro /* 362790792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 362890792Sgshapiro ** because it previously pointed to an auto buffer. 362990792Sgshapiro ** We don't do this any more, so this may be unnecessary. 363090792Sgshapiro */ 363190792Sgshapiro 363290792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 363390792Sgshapiro e->e_to = NULL; 363490792Sgshapiro } 363590792Sgshapiro SM_END_TRY 363664562Sgshapiro return rcode; 363738032Speter} 363864562Sgshapiro 363990792Sgshapiro/* 364038032Speter** MARKFAILURE -- mark a failure on a specific address. 364138032Speter** 364238032Speter** Parameters: 364338032Speter** e -- the envelope we are sending. 364438032Speter** q -- the address to mark. 364538032Speter** mci -- mailer connection information. 364638032Speter** rcode -- the code signifying the particular failure. 364764562Sgshapiro** ovr -- override an existing code? 364838032Speter** 364938032Speter** Returns: 365038032Speter** none. 365138032Speter** 365238032Speter** Side Effects: 365338032Speter** marks the address (and possibly the envelope) with the 365438032Speter** failure so that an error will be returned or 365538032Speter** the message will be queued, as appropriate. 365638032Speter*/ 365738032Speter 365890792Sgshapirovoid 365964562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 366038032Speter register ENVELOPE *e; 366138032Speter register ADDRESS *q; 366238032Speter register MCI *mci; 366338032Speter int rcode; 366464562Sgshapiro bool ovr; 366538032Speter{ 366690792Sgshapiro int save_errno = errno; 366764562Sgshapiro char *status = NULL; 366864562Sgshapiro char *rstatus = NULL; 366938032Speter 367038032Speter switch (rcode) 367138032Speter { 367238032Speter case EX_OK: 367338032Speter break; 367438032Speter 367538032Speter case EX_TEMPFAIL: 367638032Speter case EX_IOERR: 367738032Speter case EX_OSERR: 367864562Sgshapiro q->q_state = QS_QUEUEUP; 367938032Speter break; 368038032Speter 368138032Speter default: 368264562Sgshapiro q->q_state = QS_BADADDR; 368338032Speter break; 368438032Speter } 368538032Speter 368638032Speter /* find most specific error code possible */ 368738032Speter if (mci != NULL && mci->mci_status != NULL) 368838032Speter { 368990792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 369038032Speter if (mci->mci_rstatus != NULL) 369190792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 369290792Sgshapiro mci->mci_rstatus); 369338032Speter else 369464562Sgshapiro rstatus = NULL; 369538032Speter } 369638032Speter else if (e->e_status != NULL) 369738032Speter { 369864562Sgshapiro status = e->e_status; 369964562Sgshapiro rstatus = NULL; 370038032Speter } 370138032Speter else 370238032Speter { 370338032Speter switch (rcode) 370438032Speter { 370538032Speter case EX_USAGE: 370664562Sgshapiro status = "5.5.4"; 370738032Speter break; 370838032Speter 370938032Speter case EX_DATAERR: 371064562Sgshapiro status = "5.5.2"; 371138032Speter break; 371238032Speter 371338032Speter case EX_NOUSER: 371464562Sgshapiro status = "5.1.1"; 371538032Speter break; 371638032Speter 371738032Speter case EX_NOHOST: 371864562Sgshapiro status = "5.1.2"; 371938032Speter break; 372038032Speter 372138032Speter case EX_NOINPUT: 372238032Speter case EX_CANTCREAT: 372338032Speter case EX_NOPERM: 372464562Sgshapiro status = "5.3.0"; 372538032Speter break; 372638032Speter 372738032Speter case EX_UNAVAILABLE: 372838032Speter case EX_SOFTWARE: 372938032Speter case EX_OSFILE: 373038032Speter case EX_PROTOCOL: 373138032Speter case EX_CONFIG: 373264562Sgshapiro status = "5.5.0"; 373338032Speter break; 373438032Speter 373538032Speter case EX_OSERR: 373638032Speter case EX_IOERR: 373764562Sgshapiro status = "4.5.0"; 373838032Speter break; 373938032Speter 374038032Speter case EX_TEMPFAIL: 374164562Sgshapiro status = "4.2.0"; 374238032Speter break; 374338032Speter } 374438032Speter } 374538032Speter 374664562Sgshapiro /* new status? */ 374764562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 374864562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 374964562Sgshapiro { 375064562Sgshapiro q->q_status = status; 375164562Sgshapiro q->q_rstatus = rstatus; 375264562Sgshapiro } 375338032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 375438032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 375590792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 375638032Speter { 375764562Sgshapiro char buf[16]; 375838032Speter 375990792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", rcode); 376090792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 376138032Speter } 376264562Sgshapiro 376364562Sgshapiro q->q_statdate = curtime(); 376464562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 376564562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 376690792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 376790792Sgshapiro 376890792Sgshapiro /* restore errno */ 376990792Sgshapiro errno = save_errno; 377038032Speter} 377190792Sgshapiro/* 377238032Speter** ENDMAILER -- Wait for mailer to terminate. 377338032Speter** 377438032Speter** We should never get fatal errors (e.g., segmentation 377538032Speter** violation), so we report those specially. For other 377638032Speter** errors, we choose a status message (into statmsg), 377738032Speter** and if it represents an error, we print it. 377838032Speter** 377938032Speter** Parameters: 378080785Sgshapiro** mci -- the mailer connection info. 378138032Speter** e -- the current envelope. 378238032Speter** pv -- the parameter vector that invoked the mailer 378338032Speter** (for error messages). 378438032Speter** 378538032Speter** Returns: 378638032Speter** exit code of mailer. 378738032Speter** 378838032Speter** Side Effects: 378938032Speter** none. 379038032Speter*/ 379138032Speter 379264562Sgshapirostatic jmp_buf EndWaitTimeout; 379364562Sgshapiro 379464562Sgshapirostatic void 3795141858Sgshapiroendwaittimeout(ignore) 3796141858Sgshapiro int ignore; 379764562Sgshapiro{ 379877349Sgshapiro /* 379977349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 380077349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 380177349Sgshapiro ** DOING. 380277349Sgshapiro */ 380377349Sgshapiro 380464562Sgshapiro errno = ETIMEDOUT; 380564562Sgshapiro longjmp(EndWaitTimeout, 1); 380664562Sgshapiro} 380764562Sgshapiro 380838032Speterint 380938032Speterendmailer(mci, e, pv) 381038032Speter register MCI *mci; 381138032Speter register ENVELOPE *e; 381238032Speter char **pv; 381338032Speter{ 381438032Speter int st; 381564562Sgshapiro int save_errno = errno; 381664562Sgshapiro char buf[MAXLINE]; 381790792Sgshapiro SM_EVENT *ev = NULL; 381838032Speter 381964562Sgshapiro 382038032Speter mci_unlock_host(mci); 382138032Speter 382277349Sgshapiro /* close output to mailer */ 382377349Sgshapiro if (mci->mci_out != NULL) 3824141858Sgshapiro { 382590792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 3826141858Sgshapiro mci->mci_out = NULL; 3827141858Sgshapiro } 382877349Sgshapiro 382977349Sgshapiro /* copy any remaining input to transcript */ 383077349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 383177349Sgshapiro e->e_xfp != NULL) 383277349Sgshapiro { 383377349Sgshapiro while (sfgets(buf, sizeof buf, mci->mci_in, 383490792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 383590792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 383677349Sgshapiro } 383777349Sgshapiro 383864562Sgshapiro#if SASL 383990792Sgshapiro /* close SASL connection */ 384064562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 384164562Sgshapiro { 384264562Sgshapiro sasl_dispose(&mci->mci_conn); 384364562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 384464562Sgshapiro } 384564562Sgshapiro#endif /* SASL */ 384664562Sgshapiro 384764562Sgshapiro#if STARTTLS 384864562Sgshapiro /* shutdown TLS */ 384964562Sgshapiro (void) endtlsclt(mci); 385064562Sgshapiro#endif /* STARTTLS */ 385164562Sgshapiro 385264562Sgshapiro /* now close the input */ 385338032Speter if (mci->mci_in != NULL) 3854141858Sgshapiro { 385590792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 3856141858Sgshapiro mci->mci_in = NULL; 3857141858Sgshapiro } 385838032Speter mci->mci_state = MCIS_CLOSED; 385938032Speter 386064562Sgshapiro errno = save_errno; 386164562Sgshapiro 386238032Speter /* in the IPC case there is nothing to wait for */ 386338032Speter if (mci->mci_pid == 0) 386464562Sgshapiro return EX_OK; 386538032Speter 386664562Sgshapiro /* put a timeout around the wait */ 386764562Sgshapiro if (mci->mci_mailer->m_wait > 0) 386864562Sgshapiro { 386964562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 387090792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 387190792Sgshapiro endwaittimeout, 0); 387264562Sgshapiro else 387364562Sgshapiro { 387466494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 387564562Sgshapiro mci->mci_mailer->m_name, 387666494Sgshapiro (long) mci->mci_mailer->m_wait); 387764562Sgshapiro return EX_TEMPFAIL; 387864562Sgshapiro } 387964562Sgshapiro } 388038032Speter 388164562Sgshapiro /* wait for the mailer process, collect status */ 388238032Speter st = waitfor(mci->mci_pid); 388364562Sgshapiro save_errno = errno; 388464562Sgshapiro if (ev != NULL) 388590792Sgshapiro sm_clrevent(ev); 388664562Sgshapiro errno = save_errno; 388764562Sgshapiro 388838032Speter if (st == -1) 388938032Speter { 389038032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 389164562Sgshapiro return EX_SOFTWARE; 389238032Speter } 389338032Speter 389438032Speter if (WIFEXITED(st)) 389538032Speter { 389638032Speter /* normal death -- return status */ 389738032Speter return (WEXITSTATUS(st)); 389838032Speter } 389938032Speter 390038032Speter /* it died a horrid death */ 390164562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 390264562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 390364562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 390464562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 390538032Speter 390638032Speter /* log the arguments */ 390738032Speter if (pv != NULL && e->e_xfp != NULL) 390838032Speter { 390938032Speter register char **av; 391038032Speter 391190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 391238032Speter for (av = pv; *av != NULL; av++) 391390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 391490792Sgshapiro *av); 391590792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 391638032Speter } 391738032Speter 391838032Speter ExitStat = EX_TEMPFAIL; 391964562Sgshapiro return EX_TEMPFAIL; 392038032Speter} 392190792Sgshapiro/* 392238032Speter** GIVERESPONSE -- Interpret an error response from a mailer 392338032Speter** 392438032Speter** Parameters: 392564562Sgshapiro** status -- the status code from the mailer (high byte 392638032Speter** only; core dumps must have been taken care of 392738032Speter** already). 392864562Sgshapiro** dsn -- the DSN associated with the address, if any. 392938032Speter** m -- the mailer info for this mailer. 393038032Speter** mci -- the mailer connection info -- can be NULL if the 393138032Speter** response is given before the connection is made. 393238032Speter** ctladdr -- the controlling address for the recipient 393338032Speter** address(es). 393438032Speter** xstart -- the transaction start time, for computing 393538032Speter** transaction delays. 393638032Speter** e -- the current envelope. 393790792Sgshapiro** to -- the current recipient (NULL if none). 393838032Speter** 393938032Speter** Returns: 394038032Speter** none. 394138032Speter** 394238032Speter** Side Effects: 394338032Speter** Errors may be incremented. 394438032Speter** ExitStat may be set. 394538032Speter*/ 394638032Speter 394738032Spetervoid 394890792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 394964562Sgshapiro int status; 395064562Sgshapiro char *dsn; 395138032Speter register MAILER *m; 395238032Speter register MCI *mci; 395338032Speter ADDRESS *ctladdr; 395438032Speter time_t xstart; 395538032Speter ENVELOPE *e; 395690792Sgshapiro ADDRESS *to; 395738032Speter{ 395838032Speter register const char *statmsg; 395964562Sgshapiro int errnum = errno; 396064562Sgshapiro int off = 4; 396190792Sgshapiro bool usestat = false; 396264562Sgshapiro char dsnbuf[ENHSCLEN]; 396338032Speter char buf[MAXLINE]; 396490792Sgshapiro char *exmsg; 396538032Speter 396638032Speter if (e == NULL) 3967159609Sgshapiro { 396838032Speter syserr("giveresponse: null envelope"); 3969159609Sgshapiro /* NOTREACHED */ 3970159609Sgshapiro SM_ASSERT(0); 3971159609Sgshapiro } 397238032Speter 397338032Speter /* 397438032Speter ** Compute status message from code. 397538032Speter */ 397638032Speter 397790792Sgshapiro exmsg = sm_sysexmsg(status); 397864562Sgshapiro if (status == 0) 397938032Speter { 398064562Sgshapiro statmsg = "250 2.0.0 Sent"; 398138032Speter if (e->e_statmsg != NULL) 398238032Speter { 398390792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", 398490792Sgshapiro statmsg, 398590792Sgshapiro shortenstring(e->e_statmsg, 403)); 398638032Speter statmsg = buf; 398738032Speter } 398838032Speter } 398990792Sgshapiro else if (exmsg == NULL) 399038032Speter { 399190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 399290792Sgshapiro "554 5.3.0 unknown mailer error %d", 399390792Sgshapiro status); 399464562Sgshapiro status = EX_UNAVAILABLE; 399538032Speter statmsg = buf; 399690792Sgshapiro usestat = true; 399738032Speter } 399864562Sgshapiro else if (status == EX_TEMPFAIL) 399938032Speter { 400038032Speter char *bp = buf; 400138032Speter 400290792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 400338032Speter bp += strlen(bp); 400438032Speter#if NAMED_BIND 400538032Speter if (h_errno == TRY_AGAIN) 400690792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 400738032Speter else 400864562Sgshapiro#endif /* NAMED_BIND */ 400938032Speter { 401064562Sgshapiro if (errnum != 0) 401190792Sgshapiro statmsg = sm_errstring(errnum); 401238032Speter else 401338032Speter statmsg = SmtpError; 401438032Speter } 401538032Speter if (statmsg != NULL && statmsg[0] != '\0') 401664562Sgshapiro { 401764562Sgshapiro switch (errnum) 401864562Sgshapiro { 401964562Sgshapiro#ifdef ENETDOWN 402064562Sgshapiro case ENETDOWN: /* Network is down */ 402164562Sgshapiro#endif /* ENETDOWN */ 402264562Sgshapiro#ifdef ENETUNREACH 402364562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 402464562Sgshapiro#endif /* ENETUNREACH */ 402564562Sgshapiro#ifdef ENETRESET 402664562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 402764562Sgshapiro#endif /* ENETRESET */ 402864562Sgshapiro#ifdef ECONNABORTED 402964562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 403064562Sgshapiro#endif /* ECONNABORTED */ 403164562Sgshapiro#ifdef EHOSTDOWN 403264562Sgshapiro case EHOSTDOWN: /* Host is down */ 403364562Sgshapiro#endif /* EHOSTDOWN */ 403464562Sgshapiro#ifdef EHOSTUNREACH 403564562Sgshapiro case EHOSTUNREACH: /* No route to host */ 403664562Sgshapiro#endif /* EHOSTUNREACH */ 4037141858Sgshapiro if (mci != NULL && mci->mci_host != NULL) 403864562Sgshapiro { 403990792Sgshapiro (void) sm_strlcpyn(bp, 404090792Sgshapiro SPACELEFT(buf, bp), 404190792Sgshapiro 2, ": ", 404290792Sgshapiro mci->mci_host); 404364562Sgshapiro bp += strlen(bp); 404464562Sgshapiro } 404564562Sgshapiro break; 404664562Sgshapiro } 404790792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 404890792Sgshapiro statmsg); 404990792Sgshapiro usestat = true; 405064562Sgshapiro } 405138032Speter statmsg = buf; 405238032Speter } 405338032Speter#if NAMED_BIND 405464562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 405538032Speter { 405690792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 405790792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1, 405890792Sgshapiro statmsg); 405938032Speter statmsg = buf; 406090792Sgshapiro usestat = true; 406138032Speter } 406264562Sgshapiro#endif /* NAMED_BIND */ 406338032Speter else 406438032Speter { 406590792Sgshapiro statmsg = exmsg; 406664562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 406738032Speter { 406890792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg, 406990792Sgshapiro sm_errstring(errnum)); 407038032Speter statmsg = buf; 407190792Sgshapiro usestat = true; 407238032Speter } 407394334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 407494334Sgshapiro { 407594334Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg, 407694334Sgshapiro shortenstring(e->e_statmsg, 403)); 407794334Sgshapiro statmsg = buf; 407894334Sgshapiro usestat = true; 407994334Sgshapiro } 408038032Speter } 408138032Speter 408238032Speter /* 408338032Speter ** Print the message as appropriate 408438032Speter */ 408538032Speter 408664562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 408738032Speter { 408838032Speter extern char MsgBuf[]; 408938032Speter 409064562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 409164562Sgshapiro { 409264562Sgshapiro if (dsn == NULL) 409364562Sgshapiro { 409490792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 409590792Sgshapiro "%.*s", off, statmsg + 4); 409664562Sgshapiro dsn = dsnbuf; 409764562Sgshapiro } 409864562Sgshapiro off += 5; 409964562Sgshapiro } 410064562Sgshapiro else 410164562Sgshapiro { 410264562Sgshapiro off = 4; 410364562Sgshapiro } 410464562Sgshapiro message("%s", statmsg + off); 410564562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 410690792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 410790792Sgshapiro &MsgBuf[4]); 410838032Speter } 410938032Speter else 411038032Speter { 411164562Sgshapiro char mbuf[ENHSCLEN + 4]; 411238032Speter 411338032Speter Errors++; 411464562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 411564562Sgshapiro off < sizeof mbuf - 4) 411664562Sgshapiro { 411764562Sgshapiro if (dsn == NULL) 411864562Sgshapiro { 411990792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 412090792Sgshapiro "%.*s", off, statmsg + 4); 412164562Sgshapiro dsn = dsnbuf; 412264562Sgshapiro } 412364562Sgshapiro off += 5; 412490792Sgshapiro 412590792Sgshapiro /* copy only part of statmsg to mbuf */ 412690792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 412790792Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof mbuf); 412864562Sgshapiro } 412964562Sgshapiro else 413064562Sgshapiro { 413164562Sgshapiro dsnbuf[0] = '\0'; 413290792Sgshapiro (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s", 413390792Sgshapiro statmsg); 413464562Sgshapiro off = 4; 413564562Sgshapiro } 413664562Sgshapiro usrerr(mbuf, &statmsg[off]); 413738032Speter } 413838032Speter 413938032Speter /* 414038032Speter ** Final cleanup. 414138032Speter ** Log a record of the transaction. Compute the new 414238032Speter ** ExitStat -- if we already had an error, stick with 414338032Speter ** that. 414438032Speter */ 414538032Speter 414638032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 414764562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 414864562Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 414938032Speter 415038032Speter if (tTd(11, 2)) 415190792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 415290792Sgshapiro status, 415390792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 415490792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 415590792Sgshapiro errnum); 415638032Speter 415764562Sgshapiro if (status != EX_TEMPFAIL) 415864562Sgshapiro setstat(status); 415964562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 416090792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 416190792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 416238032Speter { 416390792Sgshapiro if (!usestat && e->e_message != NULL) 416490792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 416590792Sgshapiro e->e_message); 416690792Sgshapiro else 416790792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 416890792Sgshapiro statmsg + off); 416938032Speter } 417038032Speter errno = 0; 417173188Sgshapiro SM_SET_H_ERRNO(0); 417238032Speter} 417390792Sgshapiro/* 417438032Speter** LOGDELIVERY -- log the delivery in the system log 417538032Speter** 417638032Speter** Care is taken to avoid logging lines that are too long, because 417738032Speter** some versions of syslog have an unfortunate proclivity for core 417838032Speter** dumping. This is a hack, to be sure, that is at best empirical. 417938032Speter** 418038032Speter** Parameters: 418138032Speter** m -- the mailer info. Can be NULL for initial queue. 418238032Speter** mci -- the mailer connection info -- can be NULL if the 418364562Sgshapiro** log is occurring when no connection is active. 418464562Sgshapiro** dsn -- the DSN attached to the status. 418564562Sgshapiro** status -- the message to print for the status. 418638032Speter** ctladdr -- the controlling address for the to list. 418738032Speter** xstart -- the transaction start time, used for 418838032Speter** computing transaction delay. 418938032Speter** e -- the current envelope. 419038032Speter** 419138032Speter** Returns: 419238032Speter** none 419338032Speter** 419438032Speter** Side Effects: 419538032Speter** none 419638032Speter*/ 419738032Speter 419838032Spetervoid 419964562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e) 420038032Speter MAILER *m; 420138032Speter register MCI *mci; 420264562Sgshapiro char *dsn; 420364562Sgshapiro const char *status; 420438032Speter ADDRESS *ctladdr; 420538032Speter time_t xstart; 420638032Speter register ENVELOPE *e; 420738032Speter{ 420838032Speter register char *bp; 420938032Speter register char *p; 421038032Speter int l; 421190792Sgshapiro time_t now = curtime(); 421238032Speter char buf[1024]; 421338032Speter 421464562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 421538032Speter /* ctladdr: max 106 bytes */ 421638032Speter bp = buf; 421738032Speter if (ctladdr != NULL) 421838032Speter { 421990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 422090792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 422138032Speter bp += strlen(bp); 422238032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 422338032Speter { 422490792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 422590792Sgshapiro (int) ctladdr->q_uid, 422690792Sgshapiro (int) ctladdr->q_gid); 422738032Speter bp += strlen(bp); 422838032Speter } 422938032Speter } 423038032Speter 423138032Speter /* delay & xdelay: max 41 bytes */ 423290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 423390792Sgshapiro pintvl(now - e->e_ctime, true)); 423438032Speter bp += strlen(bp); 423538032Speter 423638032Speter if (xstart != (time_t) 0) 423738032Speter { 423890792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 423990792Sgshapiro pintvl(now - xstart, true)); 424038032Speter bp += strlen(bp); 424138032Speter } 424238032Speter 424338032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 424438032Speter if (m != NULL) 424538032Speter { 424690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 424790792Sgshapiro m->m_name); 424838032Speter bp += strlen(bp); 424938032Speter } 425038032Speter 425164562Sgshapiro /* pri: changes with each delivery attempt */ 425290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 425390792Sgshapiro e->e_msgpriority); 425464562Sgshapiro bp += strlen(bp); 425564562Sgshapiro 425638032Speter /* relay: max 66 bytes for IPv4 addresses */ 425738032Speter if (mci != NULL && mci->mci_host != NULL) 425838032Speter { 425938032Speter extern SOCKADDR CurHostAddr; 426038032Speter 426190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 426290792Sgshapiro shortenstring(mci->mci_host, 40)); 426338032Speter bp += strlen(bp); 426438032Speter 426538032Speter if (CurHostAddr.sa.sa_family != 0) 426638032Speter { 426790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 426890792Sgshapiro anynet_ntoa(&CurHostAddr)); 426938032Speter } 427038032Speter } 427190792Sgshapiro else if (strcmp(status, "quarantined") == 0) 427290792Sgshapiro { 427390792Sgshapiro if (e->e_quarmsg != NULL) 427490792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 427590792Sgshapiro ", quarantine=%s", 427690792Sgshapiro shortenstring(e->e_quarmsg, 40)); 427790792Sgshapiro } 427864562Sgshapiro else if (strcmp(status, "queued") != 0) 427938032Speter { 428038032Speter p = macvalue('h', e); 428138032Speter if (p != NULL && p[0] != '\0') 428238032Speter { 428390792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 428490792Sgshapiro ", relay=%s", shortenstring(p, 40)); 428538032Speter } 428638032Speter } 428738032Speter bp += strlen(bp); 428838032Speter 428964562Sgshapiro /* dsn */ 429064562Sgshapiro if (dsn != NULL && *dsn != '\0') 429164562Sgshapiro { 429290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 429390792Sgshapiro shortenstring(dsn, ENHSCLEN)); 429464562Sgshapiro bp += strlen(bp); 429564562Sgshapiro } 429638032Speter 4297147078Sgshapiro#if _FFR_LOG_NTRIES 4298147078Sgshapiro /* ntries */ 4299147078Sgshapiro if (e->e_ntries >= 0) 4300147078Sgshapiro { 4301147078Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4302147078Sgshapiro ", ntries=%d", e->e_ntries + 1); 4303147078Sgshapiro bp += strlen(bp); 4304147078Sgshapiro } 4305147078Sgshapiro#endif /* _FFR_LOG_NTRIES */ 4306147078Sgshapiro 430764562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 430864562Sgshapiro# if (STATLEN) < 63 430964562Sgshapiro# undef STATLEN 431064562Sgshapiro# define STATLEN 63 431164562Sgshapiro# endif /* (STATLEN) < 63 */ 431264562Sgshapiro# if (STATLEN) > 203 431364562Sgshapiro# undef STATLEN 431464562Sgshapiro# define STATLEN 203 431564562Sgshapiro# endif /* (STATLEN) > 203 */ 431664562Sgshapiro 431738032Speter /* stat: max 210 bytes */ 431838032Speter if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) 431938032Speter { 432038032Speter /* desperation move -- truncate data */ 432138032Speter bp = buf + sizeof buf - ((STATLEN) + 17); 432290792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 432338032Speter bp += 3; 432438032Speter } 432538032Speter 432690792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 432738032Speter bp += strlen(bp); 432838032Speter 432990792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 433090792Sgshapiro SPACELEFT(buf, bp)); 433138032Speter 433238032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 433338032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 433490792Sgshapiro if (l < 0) 433590792Sgshapiro l = 0; 433664562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 433790792Sgshapiro while (strlen(p) >= l) 433838032Speter { 433964562Sgshapiro register char *q; 434038032Speter 434164562Sgshapiro for (q = p + l; q > p; q--) 434264562Sgshapiro { 434364562Sgshapiro if (*q == ',') 434464562Sgshapiro break; 434564562Sgshapiro } 434664562Sgshapiro if (p == q) 434764562Sgshapiro break; 434890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 434966494Sgshapiro (int) (++q - p), p, buf); 435038032Speter p = q; 435138032Speter } 435264562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 435338032Speter 435464562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 435538032Speter 435638032Speter l = SYSLOG_BUFSIZE - 85; 435790792Sgshapiro if (l < 0) 435890792Sgshapiro l = 0; 435964562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 436090792Sgshapiro while (strlen(p) >= l) 436138032Speter { 436264562Sgshapiro register char *q; 436338032Speter 436464562Sgshapiro for (q = p + l; q > p; q--) 436564562Sgshapiro { 436664562Sgshapiro if (*q == ',') 436764562Sgshapiro break; 436864562Sgshapiro } 436964562Sgshapiro if (p == q) 437064562Sgshapiro break; 437164562Sgshapiro 437290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 437366494Sgshapiro (int) (++q - p), p); 437438032Speter p = q; 437538032Speter } 437664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 437738032Speter 437838032Speter if (ctladdr != NULL) 437938032Speter { 438038032Speter bp = buf; 438190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 438290792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 438338032Speter bp += strlen(bp); 438438032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 438538032Speter { 438690792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 438790792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 438838032Speter bp += strlen(bp); 438938032Speter } 439038032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 439138032Speter } 439238032Speter bp = buf; 439390792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 439490792Sgshapiro pintvl(now - e->e_ctime, true)); 439538032Speter bp += strlen(bp); 439638032Speter if (xstart != (time_t) 0) 439738032Speter { 439890792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 439990792Sgshapiro pintvl(now - xstart, true)); 440038032Speter bp += strlen(bp); 440138032Speter } 440238032Speter 440338032Speter if (m != NULL) 440438032Speter { 440590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 440690792Sgshapiro m->m_name); 440738032Speter bp += strlen(bp); 440838032Speter } 440938032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 441038032Speter 441138032Speter buf[0] = '\0'; 441238032Speter bp = buf; 441338032Speter if (mci != NULL && mci->mci_host != NULL) 441438032Speter { 441538032Speter extern SOCKADDR CurHostAddr; 441638032Speter 441790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 441890792Sgshapiro mci->mci_host); 441938032Speter bp += strlen(bp); 442038032Speter 442138032Speter if (CurHostAddr.sa.sa_family != 0) 442290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 442390792Sgshapiro " [%.100s]", 442490792Sgshapiro anynet_ntoa(&CurHostAddr)); 442538032Speter } 442690792Sgshapiro else if (strcmp(status, "quarantined") == 0) 442790792Sgshapiro { 442890792Sgshapiro if (e->e_quarmsg != NULL) 442990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 443090792Sgshapiro ", quarantine=%.100s", 443190792Sgshapiro e->e_quarmsg); 443290792Sgshapiro } 443364562Sgshapiro else if (strcmp(status, "queued") != 0) 443438032Speter { 443538032Speter p = macvalue('h', e); 443638032Speter if (p != NULL && p[0] != '\0') 443790792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p); 443838032Speter } 443938032Speter if (buf[0] != '\0') 444038032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 444138032Speter 444264562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 444364562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 444438032Speter} 444590792Sgshapiro/* 444638032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 444738032Speter** 444838032Speter** This can be made an arbitrary message separator by changing $l 444938032Speter** 445038032Speter** One of the ugliest hacks seen by human eyes is contained herein: 445138032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 445238032Speter** does a well-meaning programmer such as myself have to deal with 445338032Speter** this kind of antique garbage???? 445438032Speter** 445538032Speter** Parameters: 445638032Speter** mci -- the connection information. 445738032Speter** e -- the envelope. 445838032Speter** 445938032Speter** Returns: 4460157001Sgshapiro** true iff line was written successfully 446138032Speter** 446238032Speter** Side Effects: 446338032Speter** outputs some text to fp. 446438032Speter*/ 446538032Speter 4466157001Sgshapirobool 446738032Speterputfromline(mci, e) 446838032Speter register MCI *mci; 446938032Speter ENVELOPE *e; 447038032Speter{ 447138032Speter char *template = UnixFromLine; 447238032Speter char buf[MAXLINE]; 447338032Speter char xbuf[MAXLINE]; 447438032Speter 447538032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 4476157001Sgshapiro return true; 447738032Speter 447838032Speter mci->mci_flags |= MCIF_INHEADER; 447938032Speter 448038032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 448138032Speter { 448238032Speter char *bang; 448338032Speter 448438032Speter expand("\201g", buf, sizeof buf, e); 448538032Speter bang = strchr(buf, '!'); 448638032Speter if (bang == NULL) 448738032Speter { 448838032Speter char *at; 448938032Speter char hname[MAXNAME]; 449038032Speter 449164562Sgshapiro /* 449242575Speter ** If we can construct a UUCP path, do so 449338032Speter */ 449438032Speter 449538032Speter at = strrchr(buf, '@'); 449638032Speter if (at == NULL) 449738032Speter { 449864562Sgshapiro expand("\201k", hname, sizeof hname, e); 449938032Speter at = hname; 450038032Speter } 450138032Speter else 450238032Speter *at++ = '\0'; 450390792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 450490792Sgshapiro "From %.800s \201d remote from %.100s\n", 450590792Sgshapiro buf, at); 450638032Speter } 450738032Speter else 450838032Speter { 450938032Speter *bang++ = '\0'; 451090792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 451190792Sgshapiro "From %.800s \201d remote from %.100s\n", 451290792Sgshapiro bang, buf); 451338032Speter template = xbuf; 451438032Speter } 451538032Speter } 451638032Speter expand(template, buf, sizeof buf, e); 4517157001Sgshapiro return putxline(buf, strlen(buf), mci, PXLF_HEADER); 451838032Speter} 4519157001Sgshapiro 452090792Sgshapiro/* 452138032Speter** PUTBODY -- put the body of a message. 452238032Speter** 452338032Speter** Parameters: 452438032Speter** mci -- the connection information. 452538032Speter** e -- the envelope to put out. 452638032Speter** separator -- if non-NULL, a message separator that must 452738032Speter** not be permitted in the resulting message. 452838032Speter** 452938032Speter** Returns: 4530157001Sgshapiro** true iff message was written successfully 453138032Speter** 453238032Speter** Side Effects: 453338032Speter** The message is written onto fp. 453438032Speter*/ 453538032Speter 453638032Speter/* values for output state variable */ 4537157001Sgshapiro#define OSTATE_HEAD 0 /* at beginning of line */ 4538157001Sgshapiro#define OSTATE_CR 1 /* read a carriage return */ 4539157001Sgshapiro#define OSTATE_INLINE 2 /* putting rest of line */ 454038032Speter 4541157001Sgshapirobool 454238032Speterputbody(mci, e, separator) 454338032Speter register MCI *mci; 454438032Speter register ENVELOPE *e; 454538032Speter char *separator; 454638032Speter{ 454790792Sgshapiro bool dead = false; 4548157001Sgshapiro bool ioerr = false; 4549157001Sgshapiro int save_errno; 455038032Speter char buf[MAXLINE]; 455190792Sgshapiro#if MIME8TO7 455242575Speter char *boundaries[MAXMIMENESTING + 1]; 455390792Sgshapiro#endif /* MIME8TO7 */ 455438032Speter 455538032Speter /* 455638032Speter ** Output the body of the message 455738032Speter */ 455838032Speter 455938032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 456038032Speter { 456190792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 456238032Speter 456390792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4564120256Sgshapiro SM_IO_RDONLY_B, NULL); 456538032Speter if (e->e_dfp == NULL) 456664562Sgshapiro { 456764562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 456864562Sgshapiro 456964562Sgshapiro if (errno == ENOENT) 457064562Sgshapiro msg++; 457164562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 457264562Sgshapiro } 457390792Sgshapiro 457438032Speter } 457538032Speter if (e->e_dfp == NULL) 457638032Speter { 457738032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 457838032Speter { 4579157001Sgshapiro if (!putline("", mci)) 4580157001Sgshapiro goto writeerr; 458138032Speter mci->mci_flags &= ~MCIF_INHEADER; 458238032Speter } 4583157001Sgshapiro if (!putline("<<< No Message Collected >>>", mci)) 4584157001Sgshapiro goto writeerr; 458538032Speter goto endofmessage; 458638032Speter } 458764562Sgshapiro 458838032Speter if (e->e_dfino == (ino_t) 0) 458938032Speter { 459038032Speter struct stat stbuf; 459138032Speter 459290792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 459390792Sgshapiro < 0) 459438032Speter e->e_dfino = -1; 459538032Speter else 459638032Speter { 459738032Speter e->e_dfdev = stbuf.st_dev; 459838032Speter e->e_dfino = stbuf.st_ino; 459938032Speter } 460038032Speter } 460138032Speter 460290792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 460364562Sgshapiro (void) bfrewind(e->e_dfp); 460464562Sgshapiro 4605157001Sgshapiro /* simulate an I/O timeout when used as source */ 4606157001Sgshapiro if (tTd(84, 101)) 4607157001Sgshapiro sleep(319); 4608157001Sgshapiro 460938032Speter#if MIME8TO7 461038032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 461138032Speter { 461238032Speter /* 461338032Speter ** Do 8 to 7 bit MIME conversion. 461438032Speter */ 461538032Speter 461638032Speter /* make sure it looks like a MIME message */ 4617157001Sgshapiro if (hvalue("MIME-Version", e->e_header) == NULL && 4618157001Sgshapiro !putline("MIME-Version: 1.0", mci)) 4619157001Sgshapiro goto writeerr; 462038032Speter 462138032Speter if (hvalue("Content-Type", e->e_header) == NULL) 462238032Speter { 462390792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 462490792Sgshapiro "Content-Type: text/plain; charset=%s", 462590792Sgshapiro defcharset(e)); 4626157001Sgshapiro if (!putline(buf, mci)) 4627157001Sgshapiro goto writeerr; 462838032Speter } 462938032Speter 463038032Speter /* now do the hard work */ 463138032Speter boundaries[0] = NULL; 463238032Speter mci->mci_flags |= MCIF_INHEADER; 4633159609Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) == 4634157001Sgshapiro SM_IO_EOF) 4635157001Sgshapiro goto writeerr; 463638032Speter } 463738032Speter# if MIME7TO8 463838032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 463938032Speter { 4640157001Sgshapiro if (!mime7to8(mci, e->e_header, e)) 4641157001Sgshapiro goto writeerr; 464238032Speter } 464364562Sgshapiro# endif /* MIME7TO8 */ 464442575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 464542575Speter { 464664562Sgshapiro bool oldsuprerrs = SuprErrs; 464764562Sgshapiro 464842575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 464942575Speter boundaries[0] = NULL; 465042575Speter mci->mci_flags |= MCIF_INHEADER; 465164562Sgshapiro 465264562Sgshapiro /* 465364562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 465464562Sgshapiro ** and don't want to generate a new bounce message whose 465564562Sgshapiro ** body propagates the broken MIME. We can't just not call 465664562Sgshapiro ** mime8to7() as is done above since we need the security 465764562Sgshapiro ** checks. The best we can do is suppress the errors. 465864562Sgshapiro */ 465964562Sgshapiro 466064562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 466190792Sgshapiro SuprErrs = true; 466264562Sgshapiro 4663157001Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, 4664159609Sgshapiro M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF) 4665157001Sgshapiro goto writeerr; 466664562Sgshapiro 466764562Sgshapiro /* restore SuprErrs */ 466864562Sgshapiro SuprErrs = oldsuprerrs; 466942575Speter } 467038032Speter else 467164562Sgshapiro#endif /* MIME8TO7 */ 467238032Speter { 467338032Speter int ostate; 467438032Speter register char *bp; 467538032Speter register char *pbp; 467638032Speter register int c; 467738032Speter register char *xp; 467838032Speter int padc; 467938032Speter char *buflim; 468038032Speter int pos = 0; 468164562Sgshapiro char peekbuf[12]; 468238032Speter 468338032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 468438032Speter { 4685157001Sgshapiro if (!putline("", mci)) 4686157001Sgshapiro goto writeerr; 468738032Speter mci->mci_flags &= ~MCIF_INHEADER; 468838032Speter } 468938032Speter 469038032Speter /* determine end of buffer; allow for short mailer lines */ 469138032Speter buflim = &buf[sizeof buf - 1]; 469238032Speter if (mci->mci_mailer->m_linelimit > 0 && 469338032Speter mci->mci_mailer->m_linelimit < sizeof buf - 1) 469438032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 469538032Speter 469638032Speter /* copy temp file to output with mapping */ 4697157001Sgshapiro ostate = OSTATE_HEAD; 469838032Speter bp = buf; 469938032Speter pbp = peekbuf; 470090792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 470138032Speter { 470238032Speter if (pbp > peekbuf) 470338032Speter c = *--pbp; 470490792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 470590792Sgshapiro == SM_IO_EOF) 470638032Speter break; 470738032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 470838032Speter c &= 0x7f; 470938032Speter switch (ostate) 471038032Speter { 4711157001Sgshapiro case OSTATE_HEAD: 471238032Speter if (c == '\0' && 471390792Sgshapiro bitnset(M_NONULLS, 471490792Sgshapiro mci->mci_mailer->m_flags)) 471538032Speter break; 471638032Speter if (c != '\r' && c != '\n' && bp < buflim) 471738032Speter { 471838032Speter *bp++ = c; 471938032Speter break; 472038032Speter } 472138032Speter 472238032Speter /* check beginning of line for special cases */ 472338032Speter *bp = '\0'; 472438032Speter pos = 0; 472590792Sgshapiro padc = SM_IO_EOF; 472638032Speter if (buf[0] == 'F' && 472790792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 472890792Sgshapiro && strncmp(buf, "From ", 5) == 0) 472938032Speter { 473038032Speter padc = '>'; 473138032Speter } 473238032Speter if (buf[0] == '-' && buf[1] == '-' && 473338032Speter separator != NULL) 473438032Speter { 473538032Speter /* possible separator */ 473638032Speter int sl = strlen(separator); 473738032Speter 473890792Sgshapiro if (strncmp(&buf[2], separator, sl) 473990792Sgshapiro == 0) 474038032Speter padc = ' '; 474138032Speter } 474238032Speter if (buf[0] == '.' && 474338032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 474438032Speter { 474538032Speter padc = '.'; 474638032Speter } 474738032Speter 474838032Speter /* now copy out saved line */ 474938032Speter if (TrafficLogFile != NULL) 475038032Speter { 475190792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 475290792Sgshapiro SM_TIME_DEFAULT, 475390792Sgshapiro "%05d >>> ", 475490792Sgshapiro (int) CurrentPid); 475590792Sgshapiro if (padc != SM_IO_EOF) 475690792Sgshapiro (void) sm_io_putc(TrafficLogFile, 475790792Sgshapiro SM_TIME_DEFAULT, 475890792Sgshapiro padc); 475938032Speter for (xp = buf; xp < bp; xp++) 476090792Sgshapiro (void) sm_io_putc(TrafficLogFile, 476190792Sgshapiro SM_TIME_DEFAULT, 476290792Sgshapiro (unsigned char) *xp); 476338032Speter if (c == '\n') 476490792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 476590792Sgshapiro SM_TIME_DEFAULT, 476690792Sgshapiro mci->mci_mailer->m_eol); 476738032Speter } 476890792Sgshapiro if (padc != SM_IO_EOF) 476938032Speter { 477090792Sgshapiro if (sm_io_putc(mci->mci_out, 477190792Sgshapiro SM_TIME_DEFAULT, padc) 477290792Sgshapiro == SM_IO_EOF) 477364562Sgshapiro { 477490792Sgshapiro dead = true; 477564562Sgshapiro continue; 477664562Sgshapiro } 477738032Speter pos++; 477838032Speter } 477938032Speter for (xp = buf; xp < bp; xp++) 478038032Speter { 478190792Sgshapiro if (sm_io_putc(mci->mci_out, 478290792Sgshapiro SM_TIME_DEFAULT, 478390792Sgshapiro (unsigned char) *xp) 478490792Sgshapiro == SM_IO_EOF) 478564562Sgshapiro { 478690792Sgshapiro dead = true; 478764562Sgshapiro break; 478864562Sgshapiro } 478938032Speter } 479064562Sgshapiro if (dead) 479164562Sgshapiro continue; 479238032Speter if (c == '\n') 479338032Speter { 479490792Sgshapiro if (sm_io_fputs(mci->mci_out, 479590792Sgshapiro SM_TIME_DEFAULT, 479690792Sgshapiro mci->mci_mailer->m_eol) 479790792Sgshapiro == SM_IO_EOF) 479864562Sgshapiro break; 479938032Speter pos = 0; 480038032Speter } 480138032Speter else 480238032Speter { 480338032Speter pos += bp - buf; 480438032Speter if (c != '\r') 4805112810Sgshapiro { 4806112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4807112810Sgshapiro sizeof(peekbuf)); 480838032Speter *pbp++ = c; 4809112810Sgshapiro } 481038032Speter } 481164562Sgshapiro 481238032Speter bp = buf; 481338032Speter 481438032Speter /* determine next state */ 481538032Speter if (c == '\n') 4816157001Sgshapiro ostate = OSTATE_HEAD; 481738032Speter else if (c == '\r') 4818157001Sgshapiro ostate = OSTATE_CR; 481938032Speter else 4820157001Sgshapiro ostate = OSTATE_INLINE; 482138032Speter continue; 482238032Speter 4823157001Sgshapiro case OSTATE_CR: 482438032Speter if (c == '\n') 482538032Speter { 482638032Speter /* got CRLF */ 482790792Sgshapiro if (sm_io_fputs(mci->mci_out, 482890792Sgshapiro SM_TIME_DEFAULT, 482990792Sgshapiro mci->mci_mailer->m_eol) 483090792Sgshapiro == SM_IO_EOF) 483164562Sgshapiro continue; 483264562Sgshapiro 483338032Speter if (TrafficLogFile != NULL) 483438032Speter { 483590792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 483690792Sgshapiro SM_TIME_DEFAULT, 483790792Sgshapiro mci->mci_mailer->m_eol); 483838032Speter } 4839157001Sgshapiro ostate = OSTATE_HEAD; 484038032Speter continue; 484138032Speter } 484238032Speter 484338032Speter /* had a naked carriage return */ 4844112810Sgshapiro SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 484538032Speter *pbp++ = c; 484638032Speter c = '\r'; 4847157001Sgshapiro ostate = OSTATE_INLINE; 484838032Speter goto putch; 484938032Speter 4850157001Sgshapiro case OSTATE_INLINE: 485138032Speter if (c == '\r') 485238032Speter { 4853157001Sgshapiro ostate = OSTATE_CR; 485438032Speter continue; 485538032Speter } 485638032Speter if (c == '\0' && 485790792Sgshapiro bitnset(M_NONULLS, 485890792Sgshapiro mci->mci_mailer->m_flags)) 485938032Speter break; 486038032Speterputch: 486138032Speter if (mci->mci_mailer->m_linelimit > 0 && 486264562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 486338032Speter c != '\n') 486438032Speter { 486564562Sgshapiro int d; 486664562Sgshapiro 486764562Sgshapiro /* check next character for EOL */ 486864562Sgshapiro if (pbp > peekbuf) 486964562Sgshapiro d = *(pbp - 1); 487090792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 487190792Sgshapiro SM_TIME_DEFAULT)) 487290792Sgshapiro != SM_IO_EOF) 4873112810Sgshapiro { 4874112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4875112810Sgshapiro sizeof(peekbuf)); 487664562Sgshapiro *pbp++ = d; 4877112810Sgshapiro } 487864562Sgshapiro 487990792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 488064562Sgshapiro { 488164562Sgshapiro if (TrafficLogFile != NULL) 488290792Sgshapiro (void) sm_io_putc(TrafficLogFile, 488390792Sgshapiro SM_TIME_DEFAULT, 488490792Sgshapiro (unsigned char) c); 488590792Sgshapiro if (sm_io_putc(mci->mci_out, 488690792Sgshapiro SM_TIME_DEFAULT, 488790792Sgshapiro (unsigned char) c) 488890792Sgshapiro == SM_IO_EOF) 488964562Sgshapiro { 489090792Sgshapiro dead = true; 489164562Sgshapiro continue; 489264562Sgshapiro } 489364562Sgshapiro pos++; 489464562Sgshapiro continue; 489564562Sgshapiro } 489664562Sgshapiro 489790792Sgshapiro if (sm_io_putc(mci->mci_out, 489890792Sgshapiro SM_TIME_DEFAULT, '!') 489990792Sgshapiro == SM_IO_EOF || 490090792Sgshapiro sm_io_fputs(mci->mci_out, 490190792Sgshapiro SM_TIME_DEFAULT, 490290792Sgshapiro mci->mci_mailer->m_eol) 490390792Sgshapiro == SM_IO_EOF) 490464562Sgshapiro { 490590792Sgshapiro dead = true; 490664562Sgshapiro continue; 490764562Sgshapiro } 490864562Sgshapiro 490938032Speter if (TrafficLogFile != NULL) 491038032Speter { 491190792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 491290792Sgshapiro SM_TIME_DEFAULT, 491390792Sgshapiro "!%s", 491490792Sgshapiro mci->mci_mailer->m_eol); 491538032Speter } 4916157001Sgshapiro ostate = OSTATE_HEAD; 4917112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4918112810Sgshapiro sizeof(peekbuf)); 491938032Speter *pbp++ = c; 492038032Speter continue; 492138032Speter } 492238032Speter if (c == '\n') 492338032Speter { 492438032Speter if (TrafficLogFile != NULL) 492590792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 492690792Sgshapiro SM_TIME_DEFAULT, 492790792Sgshapiro mci->mci_mailer->m_eol); 492890792Sgshapiro if (sm_io_fputs(mci->mci_out, 492990792Sgshapiro SM_TIME_DEFAULT, 493090792Sgshapiro mci->mci_mailer->m_eol) 493190792Sgshapiro == SM_IO_EOF) 493264562Sgshapiro continue; 493338032Speter pos = 0; 4934157001Sgshapiro ostate = OSTATE_HEAD; 493538032Speter } 493638032Speter else 493738032Speter { 493838032Speter if (TrafficLogFile != NULL) 493990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 494090792Sgshapiro SM_TIME_DEFAULT, 494190792Sgshapiro (unsigned char) c); 494290792Sgshapiro if (sm_io_putc(mci->mci_out, 494390792Sgshapiro SM_TIME_DEFAULT, 494490792Sgshapiro (unsigned char) c) 494590792Sgshapiro == SM_IO_EOF) 494664562Sgshapiro { 494790792Sgshapiro dead = true; 494864562Sgshapiro continue; 494964562Sgshapiro } 495038032Speter pos++; 4951157001Sgshapiro ostate = OSTATE_INLINE; 495238032Speter } 495338032Speter break; 495438032Speter } 495538032Speter } 495638032Speter 495738032Speter /* make sure we are at the beginning of a line */ 495838032Speter if (bp > buf) 495938032Speter { 496038032Speter if (TrafficLogFile != NULL) 496138032Speter { 496238032Speter for (xp = buf; xp < bp; xp++) 496390792Sgshapiro (void) sm_io_putc(TrafficLogFile, 496490792Sgshapiro SM_TIME_DEFAULT, 496590792Sgshapiro (unsigned char) *xp); 496638032Speter } 496738032Speter for (xp = buf; xp < bp; xp++) 496838032Speter { 496990792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 497090792Sgshapiro (unsigned char) *xp) 497190792Sgshapiro == SM_IO_EOF) 497264562Sgshapiro { 497390792Sgshapiro dead = true; 497464562Sgshapiro break; 497564562Sgshapiro } 497638032Speter } 497738032Speter pos += bp - buf; 497838032Speter } 497964562Sgshapiro if (!dead && pos > 0) 498038032Speter { 498138032Speter if (TrafficLogFile != NULL) 498290792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 498390792Sgshapiro SM_TIME_DEFAULT, 498490792Sgshapiro mci->mci_mailer->m_eol); 4985157001Sgshapiro if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 4986157001Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF) 4987157001Sgshapiro goto writeerr; 498838032Speter } 498938032Speter } 499038032Speter 499190792Sgshapiro if (sm_io_error(e->e_dfp)) 499238032Speter { 499390792Sgshapiro syserr("putbody: %s/%cf%s: read error", 499490792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 499590792Sgshapiro DATAFL_LETTER, e->e_id); 499638032Speter ExitStat = EX_IOERR; 4997157001Sgshapiro ioerr = true; 499838032Speter } 499938032Speter 500038032Speterendofmessage: 500164562Sgshapiro /* 500264562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 500364562Sgshapiro ** the file offset in the stdio library for the 500464562Sgshapiro ** parent process will not agree with the in-kernel 500564562Sgshapiro ** file offset since the file descriptor is shared 500664562Sgshapiro ** between the processes. Therefore, it is vital 500764562Sgshapiro ** that the file always be rewound. This forces the 500864562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 500964562Sgshapiro ** offset to match. 501064562Sgshapiro */ 501164562Sgshapiro 5012157001Sgshapiro save_errno = errno; 501364562Sgshapiro if (e->e_dfp != NULL) 501464562Sgshapiro (void) bfrewind(e->e_dfp); 501564562Sgshapiro 501638032Speter /* some mailers want extra blank line at end of message */ 501764562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 501838032Speter buf[0] != '\0' && buf[0] != '\n') 5019157001Sgshapiro { 5020157001Sgshapiro if (!putline("", mci)) 5021157001Sgshapiro goto writeerr; 5022157001Sgshapiro } 502338032Speter 5024157001Sgshapiro if (!dead && 5025157001Sgshapiro (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF || 5026157001Sgshapiro (sm_io_error(mci->mci_out) && errno != EPIPE))) 502738032Speter { 5028157001Sgshapiro save_errno = errno; 502938032Speter syserr("putbody: write error"); 503038032Speter ExitStat = EX_IOERR; 5031157001Sgshapiro ioerr = true; 503238032Speter } 503364562Sgshapiro 5034157001Sgshapiro errno = save_errno; 5035157001Sgshapiro return !dead && !ioerr; 5036157001Sgshapiro 5037157001Sgshapiro writeerr: 5038157001Sgshapiro return false; 503938032Speter} 5040157001Sgshapiro 504190792Sgshapiro/* 504238032Speter** MAILFILE -- Send a message to a file. 504338032Speter** 504490792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 504590792Sgshapiro** execute bits, sendmail will try to become the owner of that file 504638032Speter** rather than the real user. Obviously, this only works if 504738032Speter** sendmail runs as root. 504838032Speter** 504938032Speter** This could be done as a subordinate mailer, except that it 505038032Speter** is used implicitly to save messages in ~/dead.letter. We 505138032Speter** view this as being sufficiently important as to include it 505238032Speter** here. For example, if the system is dying, we shouldn't have 505338032Speter** to create another process plus some pipes to save the message. 505438032Speter** 505538032Speter** Parameters: 505638032Speter** filename -- the name of the file to send to. 505738032Speter** mailer -- mailer definition for recipient -- if NULL, 505838032Speter** use FileMailer. 505938032Speter** ctladdr -- the controlling address header -- includes 506038032Speter** the userid/groupid to be when sending. 506138032Speter** sfflags -- flags for opening. 506238032Speter** e -- the current envelope. 506338032Speter** 506438032Speter** Returns: 506538032Speter** The exit code associated with the operation. 506638032Speter** 506738032Speter** Side Effects: 506838032Speter** none. 506938032Speter*/ 507038032Speter 507190792Sgshapiro# define RETURN(st) exit(st); 507290792Sgshapiro 507338032Speterstatic jmp_buf CtxMailfileTimeout; 507438032Speter 507538032Speterint 507638032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 507738032Speter char *volatile filename; 507838032Speter MAILER *volatile mailer; 507938032Speter ADDRESS *ctladdr; 508064562Sgshapiro volatile long sfflags; 508138032Speter register ENVELOPE *e; 508238032Speter{ 508390792Sgshapiro register SM_FILE_T *f; 508438032Speter register pid_t pid = -1; 508564562Sgshapiro volatile int mode; 508664562Sgshapiro int len; 508764562Sgshapiro off_t curoff; 508838032Speter bool suidwarn = geteuid() == 0; 508938032Speter char *p; 509064562Sgshapiro char *volatile realfile; 509190792Sgshapiro SM_EVENT *ev; 509298121Sgshapiro char buf[MAXPATHLEN]; 509398121Sgshapiro char targetfile[MAXPATHLEN]; 509438032Speter 509538032Speter if (tTd(11, 1)) 509638032Speter { 509790792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 5098132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 509938032Speter } 510038032Speter 510138032Speter if (mailer == NULL) 510238032Speter mailer = FileMailer; 510338032Speter 510438032Speter if (e->e_xfp != NULL) 510590792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 510638032Speter 510738032Speter /* 510838032Speter ** Special case /dev/null. This allows us to restrict file 510938032Speter ** delivery to regular files only. 511038032Speter */ 511138032Speter 511290792Sgshapiro if (sm_path_isdevnull(filename)) 511338032Speter return EX_OK; 511438032Speter 511538032Speter /* check for 8-bit available */ 511638032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 511738032Speter bitnset(M_7BITS, mailer->m_flags) && 511838032Speter (bitset(EF_DONT_MIME, e->e_flags) || 511938032Speter !(bitset(MM_MIME8BIT, MimeMode) || 512038032Speter (bitset(EF_IS_MIME, e->e_flags) && 512138032Speter bitset(MM_CVTMIME, MimeMode))))) 512238032Speter { 512338032Speter e->e_status = "5.6.3"; 512464562Sgshapiro usrerrenh(e->e_status, 512590792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 512690792Sgshapiro errno = 0; 512764562Sgshapiro return EX_DATAERR; 512838032Speter } 512938032Speter 513064562Sgshapiro /* Find the actual file */ 513164562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 513264562Sgshapiro { 513364562Sgshapiro len = strlen(SafeFileEnv); 513464562Sgshapiro 513564562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 513664562Sgshapiro filename += len; 513764562Sgshapiro 513890792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 513964562Sgshapiro { 514064562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 514164562Sgshapiro SafeFileEnv, filename); 514264562Sgshapiro return EX_CANTCREAT; 514364562Sgshapiro } 514490792Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile); 514564562Sgshapiro realfile = targetfile + len; 514664562Sgshapiro if (*filename == '/') 514764562Sgshapiro filename++; 514894334Sgshapiro if (*filename != '\0') 514994334Sgshapiro { 515094334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 515194334Sgshapiro if (targetfile[len - 1] != '/') 515294334Sgshapiro (void) sm_strlcat(targetfile, 515394334Sgshapiro "/", sizeof targetfile); 515494334Sgshapiro (void) sm_strlcat(targetfile, filename, 515594334Sgshapiro sizeof targetfile); 515694334Sgshapiro } 515764562Sgshapiro } 515864562Sgshapiro else if (mailer->m_rootdir != NULL) 515964562Sgshapiro { 516064562Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof targetfile, e); 516164562Sgshapiro len = strlen(targetfile); 516264562Sgshapiro 516364562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 516464562Sgshapiro filename += len; 516564562Sgshapiro 516690792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 516764562Sgshapiro { 516864562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 516964562Sgshapiro targetfile, filename); 517064562Sgshapiro return EX_CANTCREAT; 517164562Sgshapiro } 517264562Sgshapiro realfile = targetfile + len; 517364562Sgshapiro if (targetfile[len - 1] != '/') 517490792Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof targetfile); 517564562Sgshapiro if (*filename == '/') 517690792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 517790792Sgshapiro sizeof targetfile); 517864562Sgshapiro else 517990792Sgshapiro (void) sm_strlcat(targetfile, filename, 518090792Sgshapiro sizeof targetfile); 518164562Sgshapiro } 518264562Sgshapiro else 518364562Sgshapiro { 518490792Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof targetfile) >= 518590792Sgshapiro sizeof targetfile) 518664562Sgshapiro { 518764562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 518864562Sgshapiro return EX_CANTCREAT; 518964562Sgshapiro } 519064562Sgshapiro realfile = targetfile; 519164562Sgshapiro } 519264562Sgshapiro 519338032Speter /* 519438032Speter ** Fork so we can change permissions here. 519538032Speter ** Note that we MUST use fork, not vfork, because of 519638032Speter ** the complications of calling subroutines, etc. 519738032Speter */ 519838032Speter 519994334Sgshapiro 520094334Sgshapiro /* 520194334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 520294334Sgshapiro ** around so that the waitfor() below will get it. 520394334Sgshapiro */ 520494334Sgshapiro 520594334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 520694334Sgshapiro 520738032Speter DOFORK(fork); 520838032Speter 520938032Speter if (pid < 0) 521064562Sgshapiro return EX_OSERR; 521138032Speter else if (pid == 0) 521238032Speter { 521338032Speter /* child -- actually write to file */ 521438032Speter struct stat stb; 521538032Speter MCI mcibuf; 521642575Speter int err; 521738032Speter volatile int oflags = O_WRONLY|O_APPEND; 521838032Speter 521977349Sgshapiro /* Reset global flags */ 522077349Sgshapiro RestartRequest = NULL; 522190792Sgshapiro RestartWorkGroup = false; 522277349Sgshapiro ShutdownRequest = NULL; 522377349Sgshapiro PendingSignal = 0; 522490792Sgshapiro CurrentPid = getpid(); 522577349Sgshapiro 522638032Speter if (e->e_lockfp != NULL) 5227159609Sgshapiro { 5228159609Sgshapiro int fd; 522938032Speter 5230159609Sgshapiro fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 5231159609Sgshapiro /* SM_ASSERT(fd >= 0); */ 5232159609Sgshapiro if (fd >= 0) 5233159609Sgshapiro (void) close(fd); 5234159609Sgshapiro } 5235159609Sgshapiro 523690792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 523790792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 523890792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 523938032Speter (void) umask(OldUmask); 524038032Speter e->e_to = filename; 524138032Speter ExitStat = EX_OK; 524238032Speter 524338032Speter if (setjmp(CtxMailfileTimeout) != 0) 524438032Speter { 524590792Sgshapiro RETURN(EX_TEMPFAIL); 524638032Speter } 524738032Speter 524838032Speter if (TimeOuts.to_fileopen > 0) 524990792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 525090792Sgshapiro 0); 525138032Speter else 525238032Speter ev = NULL; 525338032Speter 525490792Sgshapiro /* check file mode to see if set-user-ID */ 525564562Sgshapiro if (stat(targetfile, &stb) < 0) 525664562Sgshapiro mode = FileMode; 525742575Speter else 525838032Speter mode = stb.st_mode; 525938032Speter 526038032Speter /* limit the errors to those actually caused in the child */ 526138032Speter errno = 0; 526238032Speter ExitStat = EX_OK; 526338032Speter 526464562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 526564562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 526664562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 526738032Speter { 526890792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 526938032Speter mode &= ~(S_ISGID|S_ISUID); 527064562Sgshapiro if (tTd(11, 20)) 527190792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 527238032Speter } 527338032Speter 527490792Sgshapiro /* we have to open the data file BEFORE setuid() */ 527538032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 527638032Speter { 527790792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 527838032Speter 527990792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5280120256Sgshapiro SM_IO_RDONLY_B, NULL); 528138032Speter if (e->e_dfp == NULL) 528238032Speter { 528338032Speter syserr("mailfile: Cannot open %s for %s from %s", 528438032Speter df, e->e_to, e->e_from.q_paddr); 528538032Speter } 528638032Speter } 528738032Speter 528838032Speter /* select a new user to run as */ 528938032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 529038032Speter { 529138032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 529238032Speter { 529338032Speter RealUserName = NULL; 5294132943Sgshapiro if (mailer->m_uid == NO_UID) 5295132943Sgshapiro RealUid = RunAsUid; 5296132943Sgshapiro else 5297132943Sgshapiro RealUid = mailer->m_uid; 529864562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 529964562Sgshapiro { 530064562Sgshapiro /* Only root can change the uid */ 530190792Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 530290792Sgshapiro (int) RunAsUid, (int) RealUid); 530390792Sgshapiro RETURN(EX_TEMPFAIL); 530464562Sgshapiro } 530538032Speter } 530638032Speter else if (bitset(S_ISUID, mode)) 530738032Speter { 530838032Speter RealUserName = NULL; 530938032Speter RealUid = stb.st_uid; 531038032Speter } 531138032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 531238032Speter { 531338032Speter if (ctladdr->q_ruser != NULL) 531438032Speter RealUserName = ctladdr->q_ruser; 531538032Speter else 531638032Speter RealUserName = ctladdr->q_user; 531738032Speter RealUid = ctladdr->q_uid; 531838032Speter } 5319132943Sgshapiro else if (mailer != NULL && mailer->m_uid != NO_UID) 532038032Speter { 532138032Speter RealUserName = DefUser; 532238032Speter RealUid = mailer->m_uid; 532338032Speter } 532438032Speter else 532538032Speter { 532638032Speter RealUserName = DefUser; 532738032Speter RealUid = DefUid; 532838032Speter } 532938032Speter 533038032Speter /* select a new group to run as */ 533138032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 533264562Sgshapiro { 5333132943Sgshapiro if (mailer->m_gid == NO_GID) 5334132943Sgshapiro RealGid = RunAsGid; 5335132943Sgshapiro else 5336132943Sgshapiro RealGid = mailer->m_gid; 533764562Sgshapiro if (RunAsUid != 0 && 533864562Sgshapiro (RealGid != getgid() || 533964562Sgshapiro RealGid != getegid())) 534064562Sgshapiro { 534164562Sgshapiro /* Only root can change the gid */ 534290792Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 534390792Sgshapiro (int) RealGid, (int) RunAsUid, 534490792Sgshapiro (int) getgid(), (int) getegid()); 534590792Sgshapiro RETURN(EX_TEMPFAIL); 534664562Sgshapiro } 534764562Sgshapiro } 534838032Speter else if (bitset(S_ISGID, mode)) 534938032Speter RealGid = stb.st_gid; 535064562Sgshapiro else if (ctladdr != NULL && 535164562Sgshapiro ctladdr->q_uid == DefUid && 535264562Sgshapiro ctladdr->q_gid == 0) 535371345Sgshapiro { 535471345Sgshapiro /* 535571345Sgshapiro ** Special case: This means it is an 535671345Sgshapiro ** alias and we should act as DefaultUser. 535771345Sgshapiro ** See alias()'s comments. 535871345Sgshapiro */ 535971345Sgshapiro 536064562Sgshapiro RealGid = DefGid; 536171345Sgshapiro RealUserName = DefUser; 536271345Sgshapiro } 536371345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 536471345Sgshapiro RealGid = ctladdr->q_gid; 5365132943Sgshapiro else if (mailer != NULL && mailer->m_gid != NO_GID) 536638032Speter RealGid = mailer->m_gid; 536738032Speter else 536838032Speter RealGid = DefGid; 536938032Speter } 537038032Speter 537138032Speter /* last ditch */ 537238032Speter if (!bitset(SFF_ROOTOK, sfflags)) 537338032Speter { 537438032Speter if (RealUid == 0) 537538032Speter RealUid = DefUid; 537638032Speter if (RealGid == 0) 537738032Speter RealGid = DefGid; 537838032Speter } 537938032Speter 538038032Speter /* set group id list (needs /etc/group access) */ 538138032Speter if (RealUserName != NULL && !DontInitGroups) 538238032Speter { 538338032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 538464562Sgshapiro { 538538032Speter syserr("mailfile: initgroups(%s, %d) failed", 538638032Speter RealUserName, RealGid); 538790792Sgshapiro RETURN(EX_TEMPFAIL); 538864562Sgshapiro } 538938032Speter } 539038032Speter else 539138032Speter { 539238032Speter GIDSET_T gidset[1]; 539338032Speter 539438032Speter gidset[0] = RealGid; 539538032Speter if (setgroups(1, gidset) == -1 && suidwarn) 539664562Sgshapiro { 539738032Speter syserr("mailfile: setgroups() failed"); 539890792Sgshapiro RETURN(EX_TEMPFAIL); 539964562Sgshapiro } 540038032Speter } 540138032Speter 540264562Sgshapiro /* 540364562Sgshapiro ** If you have a safe environment, go into it. 540464562Sgshapiro */ 540564562Sgshapiro 540664562Sgshapiro if (realfile != targetfile) 540738032Speter { 540894334Sgshapiro char save; 540994334Sgshapiro 541094334Sgshapiro save = *realfile; 541164562Sgshapiro *realfile = '\0'; 541264562Sgshapiro if (tTd(11, 20)) 541390792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 541464562Sgshapiro if (chroot(targetfile) < 0) 541538032Speter { 541638032Speter syserr("mailfile: Cannot chroot(%s)", 541764562Sgshapiro targetfile); 541890792Sgshapiro RETURN(EX_CANTCREAT); 541938032Speter } 542094334Sgshapiro *realfile = save; 542138032Speter } 542264562Sgshapiro 542364562Sgshapiro if (tTd(11, 40)) 542490792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 542564562Sgshapiro 542638032Speter if (chdir("/") < 0) 542764562Sgshapiro { 542838032Speter syserr("mailfile: cannot chdir(/)"); 542990792Sgshapiro RETURN(EX_CANTCREAT); 543064562Sgshapiro } 543138032Speter 543238032Speter /* now reset the group and user ids */ 543338032Speter endpwent(); 543490792Sgshapiro sm_mbdb_terminate(); 543538032Speter if (setgid(RealGid) < 0 && suidwarn) 543664562Sgshapiro { 543738032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 543890792Sgshapiro RETURN(EX_TEMPFAIL); 543964562Sgshapiro } 544038032Speter vendor_set_uid(RealUid); 544138032Speter if (setuid(RealUid) < 0 && suidwarn) 544264562Sgshapiro { 544338032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 544490792Sgshapiro RETURN(EX_TEMPFAIL); 544564562Sgshapiro } 544638032Speter 544764562Sgshapiro if (tTd(11, 2)) 544890792Sgshapiro sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 544964562Sgshapiro (int) getuid(), (int) geteuid(), 545064562Sgshapiro (int) getgid(), (int) getegid()); 545164562Sgshapiro 545264562Sgshapiro 545338032Speter /* move into some "safe" directory */ 545438032Speter if (mailer->m_execdir != NULL) 545538032Speter { 545638032Speter char *q; 545738032Speter 545838032Speter for (p = mailer->m_execdir; p != NULL; p = q) 545938032Speter { 546038032Speter q = strchr(p, ':'); 546138032Speter if (q != NULL) 546238032Speter *q = '\0'; 546338032Speter expand(p, buf, sizeof buf, e); 546438032Speter if (q != NULL) 546538032Speter *q++ = ':'; 546638032Speter if (tTd(11, 20)) 546790792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 546890792Sgshapiro buf); 546938032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 547038032Speter break; 547138032Speter } 547238032Speter } 547338032Speter 547464562Sgshapiro /* 547564562Sgshapiro ** Recheck the file after we have assumed the ID of the 547664562Sgshapiro ** delivery user to make sure we can deliver to it as 547764562Sgshapiro ** that user. This is necessary if sendmail is running 547864562Sgshapiro ** as root and the file is on an NFS mount which treats 547964562Sgshapiro ** root as nobody. 548064562Sgshapiro */ 548164562Sgshapiro 548264562Sgshapiro#if HASLSTAT 548364562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 548464562Sgshapiro err = stat(realfile, &stb); 548564562Sgshapiro else 548664562Sgshapiro err = lstat(realfile, &stb); 548764562Sgshapiro#else /* HASLSTAT */ 548864562Sgshapiro err = stat(realfile, &stb); 548964562Sgshapiro#endif /* HASLSTAT */ 549064562Sgshapiro 549164562Sgshapiro if (err < 0) 549264562Sgshapiro { 549364562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 549464562Sgshapiro mode = FileMode; 549564562Sgshapiro oflags |= O_CREAT|O_EXCL; 549664562Sgshapiro } 549764562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 549864562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 549964562Sgshapiro DontBlameSendmail) && 550064562Sgshapiro stb.st_nlink != 1) || 550164562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 550264562Sgshapiro exit(EX_CANTCREAT); 550364562Sgshapiro else 550464562Sgshapiro mode = stb.st_mode; 550564562Sgshapiro 550664562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 550738032Speter sfflags |= SFF_NOSLINK; 550864562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 550938032Speter sfflags |= SFF_NOHLINK; 551038032Speter sfflags &= ~SFF_OPENASROOT; 551164562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 551238032Speter if (f == NULL) 551338032Speter { 551464562Sgshapiro if (transienterror(errno)) 551564562Sgshapiro { 551664562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 551764562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 551890792Sgshapiro sm_errstring(errno)); 551990792Sgshapiro RETURN(EX_TEMPFAIL); 552064562Sgshapiro } 552164562Sgshapiro else 552264562Sgshapiro { 552364562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 552464562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 552590792Sgshapiro sm_errstring(errno)); 552690792Sgshapiro RETURN(EX_CANTCREAT); 552764562Sgshapiro } 552838032Speter } 552990792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 553090792Sgshapiro &stb)) 553138032Speter { 553264562Sgshapiro syserr("554 5.3.0 file changed after open"); 553390792Sgshapiro RETURN(EX_CANTCREAT); 553438032Speter } 553590792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 553638032Speter { 553790792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 553890792Sgshapiro sm_errstring(errno)); 553990792Sgshapiro RETURN(EX_CANTCREAT); 554038032Speter } 554138032Speter 554264562Sgshapiro curoff = stb.st_size; 554364562Sgshapiro 554438032Speter if (ev != NULL) 554590792Sgshapiro sm_clrevent(ev); 554638032Speter 554764562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 554838032Speter mcibuf.mci_mailer = mailer; 554938032Speter mcibuf.mci_out = f; 555038032Speter if (bitnset(M_7BITS, mailer->m_flags)) 555138032Speter mcibuf.mci_flags |= MCIF_7BIT; 555238032Speter 555338032Speter /* clear out per-message flags from connection structure */ 555438032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 555538032Speter 555638032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 555738032Speter !bitset(EF_DONT_MIME, e->e_flags) && 555838032Speter bitnset(M_7BITS, mailer->m_flags)) 555938032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 556038032Speter 556138032Speter#if MIME7TO8 556238032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 556338032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 556438032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 556590792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 556690792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 556738032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 556838032Speter { 556938032Speter /* may want to convert 7 -> 8 */ 557038032Speter /* XXX should really parse it here -- and use a class XXX */ 557190792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 557264562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 557338032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 557438032Speter } 557564562Sgshapiro#endif /* MIME7TO8 */ 557638032Speter 5577157001Sgshapiro if (!putfromline(&mcibuf, e) || 5578157001Sgshapiro !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) || 5579157001Sgshapiro !(*e->e_putbody)(&mcibuf, e, NULL) || 5580157001Sgshapiro !putline("\n", &mcibuf) || 5581157001Sgshapiro (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 558290792Sgshapiro (SuperSafe != SAFE_NO && 558390792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 5584157001Sgshapiro sm_io_error(f))) 558538032Speter { 558638032Speter setstat(EX_IOERR); 558764562Sgshapiro#if !NOFTRUNCATE 558890792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 558990792Sgshapiro curoff); 559064562Sgshapiro#endif /* !NOFTRUNCATE */ 559138032Speter } 559238032Speter 559338032Speter /* reset ISUID & ISGID bits for paranoid systems */ 559438032Speter#if HASFCHMOD 559590792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 559690792Sgshapiro (MODE_T) mode); 559764562Sgshapiro#else /* HASFCHMOD */ 559864562Sgshapiro (void) chmod(filename, (MODE_T) mode); 559964562Sgshapiro#endif /* HASFCHMOD */ 560090792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 560164562Sgshapiro setstat(EX_IOERR); 560290792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 560364562Sgshapiro (void) setuid(RealUid); 560438032Speter exit(ExitStat); 560564562Sgshapiro /* NOTREACHED */ 560638032Speter } 560738032Speter else 560838032Speter { 560938032Speter /* parent -- wait for exit status */ 561038032Speter int st; 561138032Speter 561238032Speter st = waitfor(pid); 561338032Speter if (st == -1) 561438032Speter { 561538032Speter syserr("mailfile: %s: wait", mailer->m_name); 561664562Sgshapiro return EX_SOFTWARE; 561738032Speter } 561838032Speter if (WIFEXITED(st)) 561990792Sgshapiro { 562090792Sgshapiro errno = 0; 562138032Speter return (WEXITSTATUS(st)); 562290792Sgshapiro } 562338032Speter else 562438032Speter { 562538032Speter syserr("mailfile: %s: child died on signal %d", 562638032Speter mailer->m_name, st); 562764562Sgshapiro return EX_UNAVAILABLE; 562838032Speter } 562964562Sgshapiro /* NOTREACHED */ 563038032Speter } 563138032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 563238032Speter} 563338032Speter 563438032Speterstatic void 5635141858Sgshapiromailfiletimeout(ignore) 5636141858Sgshapiro int ignore; 563738032Speter{ 563877349Sgshapiro /* 563977349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 564077349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 564177349Sgshapiro ** DOING. 564277349Sgshapiro */ 564377349Sgshapiro 564477349Sgshapiro errno = ETIMEDOUT; 564538032Speter longjmp(CtxMailfileTimeout, 1); 564638032Speter} 564790792Sgshapiro/* 564838032Speter** HOSTSIGNATURE -- return the "signature" for a host. 564938032Speter** 565038032Speter** The signature describes how we are going to send this -- it 565138032Speter** can be just the hostname (for non-Internet hosts) or can be 565238032Speter** an ordered list of MX hosts. 565338032Speter** 565438032Speter** Parameters: 565538032Speter** m -- the mailer describing this host. 565638032Speter** host -- the host name. 565738032Speter** 565838032Speter** Returns: 565938032Speter** The signature for this host. 566038032Speter** 566138032Speter** Side Effects: 566238032Speter** Can tweak the symbol table. 566338032Speter*/ 566490792Sgshapiro 566564562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 566638032Speter 566790792Sgshapirochar * 566864562Sgshapirohostsignature(m, host) 566938032Speter register MAILER *m; 567038032Speter char *host; 567138032Speter{ 567238032Speter register char *p; 567338032Speter register STAB *s; 567490792Sgshapiro time_t now; 567564562Sgshapiro#if NAMED_BIND 567664562Sgshapiro char sep = ':'; 567764562Sgshapiro char prevsep = ':'; 567838032Speter int i; 567938032Speter int len; 568038032Speter int nmx; 568164562Sgshapiro int hl; 568238032Speter char *hp; 568338032Speter char *endp; 568438032Speter int oldoptions = _res.options; 568538032Speter char *mxhosts[MAXMXHOSTS + 1]; 568690792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 568764562Sgshapiro#endif /* NAMED_BIND */ 568838032Speter 568964562Sgshapiro if (tTd(17, 3)) 569090792Sgshapiro sm_dprintf("hostsignature(%s)\n", host); 569164562Sgshapiro 569238032Speter /* 569377349Sgshapiro ** If local delivery (and not remote), just return a constant. 569464562Sgshapiro */ 569564562Sgshapiro 569677349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 569790792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 569890792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 569964562Sgshapiro return "localhost"; 570064562Sgshapiro 5701147078Sgshapiro /* an empty host does not have MX records */ 5702147078Sgshapiro if (*host == '\0') 5703147078Sgshapiro return "_empty_"; 5704147078Sgshapiro 570564562Sgshapiro /* 570638032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 570738032Speter */ 570838032Speter 570990792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 571090792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 571138032Speter { 571290792Sgshapiro /* just an ordinary mailer or deferred mode */ 571338032Speter return host; 571438032Speter } 571564562Sgshapiro#if NETUNIX 571664562Sgshapiro else if (m->m_argv[0] != NULL && 571764562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 571864562Sgshapiro { 571964562Sgshapiro /* rendezvous in the file system, no MX records */ 572064562Sgshapiro return host; 572164562Sgshapiro } 572264562Sgshapiro#endif /* NETUNIX */ 572338032Speter 572438032Speter /* 572538032Speter ** Look it up in the symbol table. 572638032Speter */ 572738032Speter 572890792Sgshapiro now = curtime(); 572938032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 573090792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 573164562Sgshapiro { 573290792Sgshapiro if (s->s_hostsig.hs_exp >= now) 573390792Sgshapiro { 573490792Sgshapiro if (tTd(17, 3)) 573590792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 573690792Sgshapiro s->s_hostsig.hs_sig); 573790792Sgshapiro return s->s_hostsig.hs_sig; 573890792Sgshapiro } 573990792Sgshapiro 574090792Sgshapiro /* signature is expired: clear it */ 574190792Sgshapiro sm_free(s->s_hostsig.hs_sig); 574290792Sgshapiro s->s_hostsig.hs_sig = NULL; 574364562Sgshapiro } 574438032Speter 574590792Sgshapiro /* set default TTL */ 574690792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 574790792Sgshapiro 574838032Speter /* 574990792Sgshapiro ** Not already there or expired -- create a signature. 575038032Speter */ 575138032Speter 575238032Speter#if NAMED_BIND 575338032Speter if (ConfigLevel < 2) 575438032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 575538032Speter 575638032Speter for (hp = host; hp != NULL; hp = endp) 575738032Speter { 575864562Sgshapiro#if NETINET6 575964562Sgshapiro if (*hp == '[') 576064562Sgshapiro { 576164562Sgshapiro endp = strchr(hp + 1, ']'); 576264562Sgshapiro if (endp != NULL) 576364562Sgshapiro endp = strpbrk(endp + 1, ":,"); 576464562Sgshapiro } 576564562Sgshapiro else 576664562Sgshapiro endp = strpbrk(hp, ":,"); 576764562Sgshapiro#else /* NETINET6 */ 576864562Sgshapiro endp = strpbrk(hp, ":,"); 576964562Sgshapiro#endif /* NETINET6 */ 577038032Speter if (endp != NULL) 577164562Sgshapiro { 577264562Sgshapiro sep = *endp; 577338032Speter *endp = '\0'; 577464562Sgshapiro } 577538032Speter 577638032Speter if (bitnset(M_NOMX, m->m_flags)) 577738032Speter { 577838032Speter /* skip MX lookups */ 577938032Speter nmx = 1; 578038032Speter mxhosts[0] = hp; 578138032Speter } 578238032Speter else 578338032Speter { 578438032Speter auto int rcode; 578590792Sgshapiro int ttl; 578638032Speter 578790792Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 578890792Sgshapiro &ttl); 578938032Speter if (nmx <= 0) 579038032Speter { 579180785Sgshapiro int save_errno; 579238032Speter register MCI *mci; 579338032Speter 579438032Speter /* update the connection info for this host */ 579580785Sgshapiro save_errno = errno; 579638032Speter mci = mci_get(hp, m); 579780785Sgshapiro mci->mci_errno = save_errno; 579838032Speter mci->mci_herrno = h_errno; 579971345Sgshapiro mci->mci_lastuse = now; 580064562Sgshapiro if (rcode == EX_NOHOST) 580164562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 580290792Sgshapiro "550 Host unknown"); 580364562Sgshapiro else 580464562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 580538032Speter 580638032Speter /* use the original host name as signature */ 580738032Speter nmx = 1; 580838032Speter mxhosts[0] = hp; 580938032Speter } 581064562Sgshapiro if (tTd(17, 3)) 581190792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 581290792Sgshapiro nmx, mxhosts[0]); 581390792Sgshapiro 581490792Sgshapiro /* 581590792Sgshapiro ** Set new TTL: we use only one! 581690792Sgshapiro ** We could try to use the minimum instead. 581790792Sgshapiro */ 581890792Sgshapiro 581990792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 582038032Speter } 582138032Speter 582238032Speter len = 0; 582338032Speter for (i = 0; i < nmx; i++) 582438032Speter len += strlen(mxhosts[i]) + 1; 582590792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 582690792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 582790792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 582864562Sgshapiro { 582964562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 583064562Sgshapiro host, MAXHOSTSIGNATURE, len); 583164562Sgshapiro len = MAXHOSTSIGNATURE; 583264562Sgshapiro } 583390792Sgshapiro p = sm_pmalloc_x(len); 583490792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 583538032Speter { 583690792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 583790792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 583890792Sgshapiro s->s_hostsig.hs_sig = p; 583964562Sgshapiro hl = strlen(p); 584064562Sgshapiro p += hl; 584164562Sgshapiro *p++ = prevsep; 584264562Sgshapiro len -= hl + 1; 584338032Speter } 584438032Speter else 584590792Sgshapiro s->s_hostsig.hs_sig = p; 584638032Speter for (i = 0; i < nmx; i++) 584738032Speter { 584864562Sgshapiro hl = strlen(mxhosts[i]); 584964562Sgshapiro if (len - 1 < hl || len <= 1) 585064562Sgshapiro { 585164562Sgshapiro /* force to drop out of outer loop */ 585264562Sgshapiro len = -1; 585364562Sgshapiro break; 585464562Sgshapiro } 585538032Speter if (i != 0) 585664562Sgshapiro { 585764562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 585864562Sgshapiro *p++ = ','; 585964562Sgshapiro else 586064562Sgshapiro *p++ = ':'; 586164562Sgshapiro len--; 586264562Sgshapiro } 586390792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 586464562Sgshapiro p += hl; 586564562Sgshapiro len -= hl; 586638032Speter } 586764562Sgshapiro 586864562Sgshapiro /* 586964562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 587064562Sgshapiro ** because we won't have more space for further hosts 587164562Sgshapiro ** anyway (separated by : in the .cf file). 587264562Sgshapiro */ 587364562Sgshapiro 587464562Sgshapiro if (len < 0) 587564562Sgshapiro break; 587638032Speter if (endp != NULL) 587764562Sgshapiro *endp++ = sep; 587864562Sgshapiro prevsep = sep; 587938032Speter } 588090792Sgshapiro makelower(s->s_hostsig.hs_sig); 588138032Speter if (ConfigLevel < 2) 588238032Speter _res.options = oldoptions; 588364562Sgshapiro#else /* NAMED_BIND */ 588438032Speter /* not using BIND -- the signature is just the host name */ 588590792Sgshapiro /* 588690792Sgshapiro ** 'host' points to storage that will be freed after we are 588790792Sgshapiro ** done processing the current envelope, so we copy it. 588890792Sgshapiro */ 588990792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 589064562Sgshapiro#endif /* NAMED_BIND */ 589138032Speter if (tTd(17, 1)) 589290792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 589390792Sgshapiro return s->s_hostsig.hs_sig; 589438032Speter} 589590792Sgshapiro/* 589664562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 589764562Sgshapiro** 589864562Sgshapiro** The signature describes how we are going to send this -- it 589964562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 590064562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 590164562Sgshapiro** MX preference values. 590264562Sgshapiro** 590364562Sgshapiro** Parameters: 590464562Sgshapiro** sig -- the host signature. 590564562Sgshapiro** mxhosts -- array to populate. 590690792Sgshapiro** mailer -- mailer. 590764562Sgshapiro** 590864562Sgshapiro** Returns: 590964562Sgshapiro** The number of hosts inserted into mxhosts array. 591064562Sgshapiro** 591164562Sgshapiro** Side Effects: 591264562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 591364562Sgshapiro*/ 591464562Sgshapiro 591564562Sgshapirostatic int 591664562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 591764562Sgshapiro char *sig; 591864562Sgshapiro char **mxhosts; 591964562Sgshapiro MAILER *mailer; 592064562Sgshapiro{ 592190792Sgshapiro unsigned short curpref = 0; 592290792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 592364562Sgshapiro char *hp, *endp; 592490792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 592564562Sgshapiro long rndm[MAXMXHOSTS]; 592664562Sgshapiro 592764562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 592864562Sgshapiro { 592964562Sgshapiro char sep = ':'; 593064562Sgshapiro 593164562Sgshapiro#if NETINET6 593264562Sgshapiro if (*hp == '[') 593364562Sgshapiro { 593464562Sgshapiro endp = strchr(hp + 1, ']'); 593564562Sgshapiro if (endp != NULL) 593664562Sgshapiro endp = strpbrk(endp + 1, ":,"); 593764562Sgshapiro } 593864562Sgshapiro else 593964562Sgshapiro endp = strpbrk(hp, ":,"); 594064562Sgshapiro#else /* NETINET6 */ 594164562Sgshapiro endp = strpbrk(hp, ":,"); 594264562Sgshapiro#endif /* NETINET6 */ 594364562Sgshapiro if (endp != NULL) 594464562Sgshapiro { 594564562Sgshapiro sep = *endp; 594664562Sgshapiro *endp = '\0'; 594764562Sgshapiro } 594864562Sgshapiro 594964562Sgshapiro mxhosts[nmx] = hp; 595064562Sgshapiro prefer[nmx] = curpref; 595164562Sgshapiro if (mci_match(hp, mailer)) 595264562Sgshapiro rndm[nmx] = 0; 595364562Sgshapiro else 595464562Sgshapiro rndm[nmx] = get_random(); 595564562Sgshapiro 595664562Sgshapiro if (endp != NULL) 595764562Sgshapiro { 595864562Sgshapiro /* 595964562Sgshapiro ** Since we don't have the original MX prefs, 596064562Sgshapiro ** make our own. If the separator is a ':', that 596164562Sgshapiro ** means the preference for the next host will be 596264562Sgshapiro ** higher than this one, so simply increment curpref. 596364562Sgshapiro */ 596464562Sgshapiro 596564562Sgshapiro if (sep == ':') 596664562Sgshapiro curpref++; 596764562Sgshapiro 596864562Sgshapiro *endp++ = sep; 596964562Sgshapiro } 597064562Sgshapiro if (++nmx >= MAXMXHOSTS) 597164562Sgshapiro break; 597264562Sgshapiro } 597364562Sgshapiro 597464562Sgshapiro /* sort the records using the random factor for equal preferences */ 597564562Sgshapiro for (i = 0; i < nmx; i++) 597664562Sgshapiro { 597764562Sgshapiro for (j = i + 1; j < nmx; j++) 597864562Sgshapiro { 597964562Sgshapiro /* 598064562Sgshapiro ** List is already sorted by MX preference, only 598164562Sgshapiro ** need to look for equal preference MX records 598264562Sgshapiro */ 598364562Sgshapiro 598464562Sgshapiro if (prefer[i] < prefer[j]) 598564562Sgshapiro break; 598664562Sgshapiro 598764562Sgshapiro if (prefer[i] > prefer[j] || 598864562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 598964562Sgshapiro { 599090792Sgshapiro register unsigned short tempp; 599164562Sgshapiro register long tempr; 599264562Sgshapiro register char *temp1; 599364562Sgshapiro 599464562Sgshapiro tempp = prefer[i]; 599564562Sgshapiro prefer[i] = prefer[j]; 599664562Sgshapiro prefer[j] = tempp; 599764562Sgshapiro temp1 = mxhosts[i]; 599864562Sgshapiro mxhosts[i] = mxhosts[j]; 599964562Sgshapiro mxhosts[j] = temp1; 600064562Sgshapiro tempr = rndm[i]; 600164562Sgshapiro rndm[i] = rndm[j]; 600264562Sgshapiro rndm[j] = tempr; 600364562Sgshapiro } 600464562Sgshapiro } 600564562Sgshapiro } 600664562Sgshapiro return nmx; 600764562Sgshapiro} 600864562Sgshapiro 600964562Sgshapiro# if STARTTLS 601064562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 601190792Sgshapirostatic bool tls_ok_clt = true; 601264562Sgshapiro 601390792Sgshapiro/* 601490792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 601564562Sgshapiro** 601664562Sgshapiro** Parameters: 601790792Sgshapiro** tls_ok -- should tls be done? 601890792Sgshapiro** 601990792Sgshapiro** Returns: 602064562Sgshapiro** none. 602164562Sgshapiro** 602290792Sgshapiro** Side Effects: 602390792Sgshapiro** sets tls_ok_clt (static variable in this module) 602490792Sgshapiro*/ 602590792Sgshapiro 602690792Sgshapirovoid 602790792Sgshapirosetclttls(tls_ok) 602890792Sgshapiro bool tls_ok; 602990792Sgshapiro{ 603090792Sgshapiro tls_ok_clt = tls_ok; 603190792Sgshapiro return; 603290792Sgshapiro} 603390792Sgshapiro/* 603490792Sgshapiro** INITCLTTLS -- initialize client side TLS 603590792Sgshapiro** 603690792Sgshapiro** Parameters: 603790792Sgshapiro** tls_ok -- should tls initialization be done? 603890792Sgshapiro** 603964562Sgshapiro** Returns: 604064562Sgshapiro** succeeded? 604190792Sgshapiro** 604290792Sgshapiro** Side Effects: 604390792Sgshapiro** sets tls_ok_clt (static variable in this module) 604464562Sgshapiro*/ 604564562Sgshapiro 604664562Sgshapirobool 604790792Sgshapiroinitclttls(tls_ok) 604890792Sgshapiro bool tls_ok; 604964562Sgshapiro{ 605090792Sgshapiro if (!tls_ok_clt) 605190792Sgshapiro return false; 605290792Sgshapiro tls_ok_clt = tls_ok; 605390792Sgshapiro if (!tls_ok_clt) 605490792Sgshapiro return false; 605564562Sgshapiro if (clt_ctx != NULL) 605690792Sgshapiro return true; /* already done */ 6057110560Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile, 6058110560Sgshapiro CltKeyFile, CACertPath, CACertFile, DHParams); 605990792Sgshapiro return tls_ok_clt; 606064562Sgshapiro} 606164562Sgshapiro 606290792Sgshapiro/* 606364562Sgshapiro** STARTTLS -- try to start secure connection (client side) 606464562Sgshapiro** 606564562Sgshapiro** Parameters: 606664562Sgshapiro** m -- the mailer. 606764562Sgshapiro** mci -- the mailer connection info. 606864562Sgshapiro** e -- the envelope. 606964562Sgshapiro** 607064562Sgshapiro** Returns: 607164562Sgshapiro** success? 607264562Sgshapiro** (maybe this should be some other code than EX_ 607364562Sgshapiro** that denotes which stage failed.) 607464562Sgshapiro*/ 607564562Sgshapiro 607664562Sgshapirostatic int 607764562Sgshapirostarttls(m, mci, e) 607864562Sgshapiro MAILER *m; 607964562Sgshapiro MCI *mci; 608064562Sgshapiro ENVELOPE *e; 608164562Sgshapiro{ 608264562Sgshapiro int smtpresult; 608366494Sgshapiro int result = 0; 608466494Sgshapiro int rfd, wfd; 608564562Sgshapiro SSL *clt_ssl = NULL; 608690792Sgshapiro time_t tlsstart; 608764562Sgshapiro 608890792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 608966494Sgshapiro return EX_TEMPFAIL; 609064562Sgshapiro smtpmessage("STARTTLS", m, mci); 609164562Sgshapiro 609264562Sgshapiro /* get the reply */ 6093132943Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 6094132943Sgshapiro XS_STARTTLS); 609564562Sgshapiro 609664562Sgshapiro /* check return code from server */ 6097157001Sgshapiro if (REPLYTYPE(smtpresult) == 4) 609864562Sgshapiro return EX_TEMPFAIL; 609964562Sgshapiro if (smtpresult == 501) 610064562Sgshapiro return EX_USAGE; 610164562Sgshapiro if (smtpresult == -1) 610264562Sgshapiro return smtpresult; 6103157001Sgshapiro 6104157001Sgshapiro /* not an expected reply but we have to deal with it */ 6105157001Sgshapiro if (REPLYTYPE(smtpresult) == 5) 6106157001Sgshapiro return EX_UNAVAILABLE; 610764562Sgshapiro if (smtpresult != 220) 610864562Sgshapiro return EX_PROTOCOL; 610964562Sgshapiro 611064562Sgshapiro if (LogLevel > 13) 611190792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 611264562Sgshapiro 611364562Sgshapiro /* start connection */ 611464562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 611564562Sgshapiro { 611664562Sgshapiro if (LogLevel > 5) 611764562Sgshapiro { 611890792Sgshapiro sm_syslog(LOG_ERR, NOQID, 611990792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 612064562Sgshapiro if (LogLevel > 9) 612190792Sgshapiro tlslogerr("client"); 612264562Sgshapiro } 612364562Sgshapiro return EX_SOFTWARE; 612464562Sgshapiro } 612564562Sgshapiro 612690792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 612790792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 612866494Sgshapiro 612964562Sgshapiro /* SSL_clear(clt_ssl); ? */ 613066494Sgshapiro if (rfd < 0 || wfd < 0 || 613190792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 613290792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 613364562Sgshapiro { 613464562Sgshapiro if (LogLevel > 5) 613564562Sgshapiro { 613690792Sgshapiro sm_syslog(LOG_ERR, NOQID, 613790792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 613890792Sgshapiro result); 613964562Sgshapiro if (LogLevel > 9) 614090792Sgshapiro tlslogerr("client"); 614164562Sgshapiro } 614264562Sgshapiro return EX_SOFTWARE; 614364562Sgshapiro } 614464562Sgshapiro SSL_set_connect_state(clt_ssl); 614590792Sgshapiro tlsstart = curtime(); 614690792Sgshapiro 614790792Sgshapirossl_retry: 614864562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 614964562Sgshapiro { 6150157001Sgshapiro int i, ssl_err; 615164562Sgshapiro 6152157001Sgshapiro ssl_err = SSL_get_error(clt_ssl, result); 6153157001Sgshapiro i = tls_retry(clt_ssl, rfd, wfd, tlsstart, 6154157001Sgshapiro TimeOuts.to_starttls, ssl_err, "client"); 6155157001Sgshapiro if (i > 0) 6156157001Sgshapiro goto ssl_retry; 615790792Sgshapiro 6158157001Sgshapiro if (LogLevel > 5) 615990792Sgshapiro { 6160157001Sgshapiro sm_syslog(LOG_WARNING, NOQID, 6161157001Sgshapiro "STARTTLS=client, error: connect failed=%d, SSL_error=%d, errno=%d, retry=%d", 6162157001Sgshapiro result, ssl_err, errno, i); 6163110560Sgshapiro if (LogLevel > 8) 6164110560Sgshapiro tlslogerr("client"); 6165110560Sgshapiro } 616690792Sgshapiro 616764562Sgshapiro SSL_free(clt_ssl); 616864562Sgshapiro clt_ssl = NULL; 616964562Sgshapiro return EX_SOFTWARE; 617064562Sgshapiro } 617164562Sgshapiro mci->mci_ssl = clt_ssl; 617290792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 617390792Sgshapiro &mci->mci_macro, true); 617464562Sgshapiro 617590792Sgshapiro /* switch to use TLS... */ 617664562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 617764562Sgshapiro return EX_OK; 617864562Sgshapiro 617964562Sgshapiro /* failure */ 618064562Sgshapiro SSL_free(clt_ssl); 618164562Sgshapiro clt_ssl = NULL; 618264562Sgshapiro return EX_SOFTWARE; 618364562Sgshapiro} 618490792Sgshapiro/* 618564562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 618664562Sgshapiro** 618764562Sgshapiro** Parameters: 618864562Sgshapiro** mci -- the mailer connection info. 618964562Sgshapiro** 619064562Sgshapiro** Returns: 619164562Sgshapiro** success? 619264562Sgshapiro*/ 619390792Sgshapiro 619490792Sgshapirostatic int 619564562Sgshapiroendtlsclt(mci) 619664562Sgshapiro MCI *mci; 619764562Sgshapiro{ 619864562Sgshapiro int r; 619964562Sgshapiro 620064562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 620164562Sgshapiro return EX_OK; 620264562Sgshapiro r = endtls(mci->mci_ssl, "client"); 620364562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 620464562Sgshapiro return r; 620564562Sgshapiro} 620690792Sgshapiro# endif /* STARTTLS */ 620790792Sgshapiro# if STARTTLS || SASL 620890792Sgshapiro/* 620990792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 621064562Sgshapiro** 621164562Sgshapiro** Parameters: 621290792Sgshapiro** e -- envelope. 621390792Sgshapiro** flag -- flag to check in {client_flags} 621464562Sgshapiro** 621564562Sgshapiro** Returns: 621690792Sgshapiro** true iff flag is set. 621764562Sgshapiro*/ 621864562Sgshapiro 621990792Sgshapirostatic bool 622090792Sgshapiroiscltflgset(e, flag) 622190792Sgshapiro ENVELOPE *e; 622290792Sgshapiro int flag; 622364562Sgshapiro{ 622490792Sgshapiro char *p; 622573188Sgshapiro 622690792Sgshapiro p = macvalue(macid("{client_flags}"), e); 622790792Sgshapiro if (p == NULL) 622890792Sgshapiro return false; 622990792Sgshapiro for (; *p != '\0'; p++) 623064562Sgshapiro { 623190792Sgshapiro /* look for just this one flag */ 623290792Sgshapiro if (*p == (char) flag) 623390792Sgshapiro return true; 623464562Sgshapiro } 623590792Sgshapiro return false; 623664562Sgshapiro} 623790792Sgshapiro# endif /* STARTTLS || SASL */ 6238