138032Speter/* 2261363Sgshapiro * Copyright (c) 1998-2010, 2012 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 15157001Sgshapiro#include <sm/time.h> 1638032Speter 17266692SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.1030 2013-11-22 20:51:55 ca Exp $") 1864562Sgshapiro 1938032Speter#if HASSETUSERCONTEXT 2038032Speter# include <login_cap.h> 2164562Sgshapiro#endif /* HASSETUSERCONTEXT */ 2238032Speter 2394334Sgshapiro#if NETINET || NETINET6 2494334Sgshapiro# include <arpa/inet.h> 2594334Sgshapiro#endif /* NETINET || NETINET6 */ 2694334Sgshapiro 2790792Sgshapiro#if STARTTLS || SASL 2864562Sgshapiro# include "sfsasl.h" 2990792Sgshapiro#endif /* STARTTLS || SASL */ 3064562Sgshapiro 3164562Sgshapirostatic int deliver __P((ENVELOPE *, ADDRESS *)); 3264562Sgshapirostatic void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 33141858Sgshapirostatic void mailfiletimeout __P((int)); 34141858Sgshapirostatic void endwaittimeout __P((int)); 3564562Sgshapirostatic int parse_hostsignature __P((char *, char **, MAILER *)); 3664562Sgshapirostatic void sendenvelope __P((ENVELOPE *, int)); 3790792Sgshapirostatic int coloncmp __P((const char *, const char *)); 3864562Sgshapiro 3990792Sgshapiro#if STARTTLS 40285303Sgshapiro# include <openssl/err.h> 4164562Sgshapirostatic int starttls __P((MAILER *, MCI *, ENVELOPE *)); 4290792Sgshapirostatic int endtlsclt __P((MCI *)); 4390792Sgshapiro#endif /* STARTTLS */ 4490792Sgshapiro# if STARTTLS || SASL 4590792Sgshapirostatic bool iscltflgset __P((ENVELOPE *, int)); 4690792Sgshapiro# endif /* STARTTLS || SASL */ 4738032Speter 4838032Speter/* 4938032Speter** SENDALL -- actually send all the messages. 5038032Speter** 5138032Speter** Parameters: 5238032Speter** e -- the envelope to send. 5338032Speter** mode -- the delivery mode to use. If SM_DEFAULT, use 5438032Speter** the current e->e_sendmode. 5538032Speter** 5638032Speter** Returns: 5738032Speter** none. 5838032Speter** 5938032Speter** Side Effects: 6038032Speter** Scans the send lists and sends everything it finds. 6138032Speter** Delivers any appropriate error messages. 6238032Speter** If we are running in a non-interactive mode, takes the 6338032Speter** appropriate action. 6438032Speter*/ 6538032Speter 6638032Spetervoid 6738032Spetersendall(e, mode) 6838032Speter ENVELOPE *e; 6938032Speter int mode; 7038032Speter{ 7138032Speter register ADDRESS *q; 7238032Speter char *owner; 7338032Speter int otherowners; 7464562Sgshapiro int save_errno; 7538032Speter register ENVELOPE *ee; 7638032Speter ENVELOPE *splitenv = NULL; 7738032Speter int oldverbose = Verbose; 7890792Sgshapiro bool somedeliveries = false, expensive = false; 7938032Speter pid_t pid; 8038032Speter 8138032Speter /* 8238032Speter ** If this message is to be discarded, don't bother sending 8338032Speter ** the message at all. 8438032Speter */ 8538032Speter 8638032Speter if (bitset(EF_DISCARD, e->e_flags)) 8738032Speter { 8838032Speter if (tTd(13, 1)) 8990792Sgshapiro sm_dprintf("sendall: discarding id %s\n", e->e_id); 9038032Speter e->e_flags |= EF_CLRQUEUE; 9190792Sgshapiro if (LogLevel > 9) 9290792Sgshapiro logundelrcpts(e, "discarded", 9, true); 9390792Sgshapiro else if (LogLevel > 4) 9438032Speter sm_syslog(LOG_INFO, e->e_id, "discarded"); 9590792Sgshapiro markstats(e, NULL, STATS_REJECT); 9638032Speter return; 9738032Speter } 9838032Speter 9938032Speter /* 10038032Speter ** If we have had global, fatal errors, don't bother sending 10138032Speter ** the message at all if we are in SMTP mode. Local errors 10238032Speter ** (e.g., a single address failing) will still cause the other 10338032Speter ** addresses to be sent. 10438032Speter */ 10538032Speter 10638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 10738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 10838032Speter { 10938032Speter e->e_flags |= EF_CLRQUEUE; 11038032Speter return; 11138032Speter } 11238032Speter 11338032Speter /* determine actual delivery mode */ 11438032Speter if (mode == SM_DEFAULT) 11538032Speter { 11638032Speter mode = e->e_sendmode; 11738032Speter if (mode != SM_VERIFY && mode != SM_DEFER && 11838032Speter shouldqueue(e->e_msgpriority, e->e_ctime)) 11938032Speter mode = SM_QUEUE; 12038032Speter } 12138032Speter 12238032Speter if (tTd(13, 1)) 12338032Speter { 12490792Sgshapiro sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 12538032Speter mode, e->e_id); 126132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 12790792Sgshapiro sm_dprintf("\te_flags = "); 12838032Speter printenvflags(e); 12990792Sgshapiro sm_dprintf("sendqueue:\n"); 130132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 13138032Speter } 13238032Speter 13338032Speter /* 13438032Speter ** Do any preprocessing necessary for the mode we are running. 13538032Speter ** Check to make sure the hop count is reasonable. 13638032Speter ** Delete sends to the sender in mailing lists. 13738032Speter */ 13838032Speter 13938032Speter CurEnv = e; 14038032Speter if (tTd(62, 1)) 14138032Speter checkfds(NULL); 14238032Speter 14338032Speter if (e->e_hopcount > MaxHopCount) 14438032Speter { 14577349Sgshapiro char *recip; 14677349Sgshapiro 14777349Sgshapiro if (e->e_sendqueue != NULL && 14877349Sgshapiro e->e_sendqueue->q_paddr != NULL) 14977349Sgshapiro recip = e->e_sendqueue->q_paddr; 15077349Sgshapiro else 15177349Sgshapiro recip = "(nobody)"; 15277349Sgshapiro 15338032Speter errno = 0; 15490792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), false); 15538032Speter e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; 15664562Sgshapiro ExitStat = EX_UNAVAILABLE; 15777349Sgshapiro syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s", 15877349Sgshapiro e->e_hopcount, MaxHopCount, e->e_from.q_paddr, 15977349Sgshapiro RealHostName == NULL ? "localhost" : RealHostName, 16077349Sgshapiro recip); 16164562Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 16264562Sgshapiro { 16364562Sgshapiro if (QS_IS_DEAD(q->q_state)) 16464562Sgshapiro continue; 16564562Sgshapiro q->q_state = QS_BADADDR; 16664562Sgshapiro q->q_status = "5.4.6"; 16777349Sgshapiro q->q_rstatus = "554 5.4.6 Too many hops"; 16864562Sgshapiro } 16938032Speter return; 17038032Speter } 17138032Speter 17238032Speter /* 17338032Speter ** Do sender deletion. 17438032Speter ** 17564562Sgshapiro ** If the sender should be queued up, skip this. 17638032Speter ** This can happen if the name server is hosed when you 17738032Speter ** are trying to send mail. The result is that the sender 17838032Speter ** is instantiated in the queue as a recipient. 17938032Speter */ 18038032Speter 18138032Speter if (!bitset(EF_METOO, e->e_flags) && 18264562Sgshapiro !QS_IS_QUEUEUP(e->e_from.q_state)) 18338032Speter { 18438032Speter if (tTd(13, 5)) 18538032Speter { 18690792Sgshapiro sm_dprintf("sendall: QS_SENDER "); 187132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 18838032Speter } 18964562Sgshapiro e->e_from.q_state = QS_SENDER; 19038032Speter (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); 19138032Speter } 19238032Speter 19338032Speter /* 19438032Speter ** Handle alias owners. 19538032Speter ** 19638032Speter ** We scan up the q_alias chain looking for owners. 19738032Speter ** We discard owners that are the same as the return path. 19838032Speter */ 19938032Speter 20038032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 20138032Speter { 20238032Speter register struct address *a; 20338032Speter 20438032Speter for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) 20538032Speter continue; 20638032Speter if (a != NULL) 20738032Speter q->q_owner = a->q_owner; 20838032Speter 20938032Speter if (q->q_owner != NULL && 21064562Sgshapiro !QS_IS_DEAD(q->q_state) && 21138032Speter strcmp(q->q_owner, e->e_from.q_paddr) == 0) 21238032Speter q->q_owner = NULL; 21338032Speter } 21438032Speter 21538032Speter if (tTd(13, 25)) 21638032Speter { 21790792Sgshapiro sm_dprintf("\nAfter first owner pass, sendq =\n"); 218132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 21938032Speter } 22038032Speter 22138032Speter owner = ""; 22238032Speter otherowners = 1; 22338032Speter while (owner != NULL && otherowners > 0) 22438032Speter { 22538032Speter if (tTd(13, 28)) 22690792Sgshapiro sm_dprintf("owner = \"%s\", otherowners = %d\n", 22790792Sgshapiro owner, otherowners); 22838032Speter owner = NULL; 22938032Speter otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; 23038032Speter 23138032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 23238032Speter { 23338032Speter if (tTd(13, 30)) 23438032Speter { 23590792Sgshapiro sm_dprintf("Checking "); 236132943Sgshapiro printaddr(sm_debug_file(), q, false); 23738032Speter } 23864562Sgshapiro if (QS_IS_DEAD(q->q_state)) 23938032Speter { 24038032Speter if (tTd(13, 30)) 24190792Sgshapiro sm_dprintf(" ... QS_IS_DEAD\n"); 24238032Speter continue; 24338032Speter } 24438032Speter if (tTd(13, 29) && !tTd(13, 30)) 24538032Speter { 24690792Sgshapiro sm_dprintf("Checking "); 247132943Sgshapiro printaddr(sm_debug_file(), q, false); 24838032Speter } 24938032Speter 25038032Speter if (q->q_owner != NULL) 25138032Speter { 25238032Speter if (owner == NULL) 25338032Speter { 25438032Speter if (tTd(13, 40)) 25590792Sgshapiro sm_dprintf(" ... First owner = \"%s\"\n", 25690792Sgshapiro q->q_owner); 25738032Speter owner = q->q_owner; 25838032Speter } 25938032Speter else if (owner != q->q_owner) 26038032Speter { 26138032Speter if (strcmp(owner, q->q_owner) == 0) 26238032Speter { 26338032Speter if (tTd(13, 40)) 26490792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 26590792Sgshapiro owner); 26638032Speter 26738032Speter /* make future comparisons cheap */ 26838032Speter q->q_owner = owner; 26938032Speter } 27038032Speter else 27138032Speter { 27238032Speter if (tTd(13, 40)) 27390792Sgshapiro sm_dprintf(" ... Another owner \"%s\"\n", 27490792Sgshapiro q->q_owner); 27538032Speter otherowners++; 27638032Speter } 27738032Speter owner = q->q_owner; 27838032Speter } 27938032Speter else if (tTd(13, 40)) 28090792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 28190792Sgshapiro owner); 28238032Speter } 28338032Speter else 28438032Speter { 28538032Speter if (tTd(13, 40)) 28690792Sgshapiro sm_dprintf(" ... Null owner\n"); 28738032Speter otherowners++; 28838032Speter } 28938032Speter 29064562Sgshapiro if (QS_IS_BADADDR(q->q_state)) 29164562Sgshapiro { 29264562Sgshapiro if (tTd(13, 30)) 29390792Sgshapiro sm_dprintf(" ... QS_IS_BADADDR\n"); 29464562Sgshapiro continue; 29564562Sgshapiro } 29664562Sgshapiro 29764562Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 29864562Sgshapiro { 29964562Sgshapiro MAILER *m = q->q_mailer; 30064562Sgshapiro 30164562Sgshapiro /* 30264562Sgshapiro ** If we have temporary address failures 30364562Sgshapiro ** (e.g., dns failure) and a fallback MX is 30464562Sgshapiro ** set, send directly to the fallback MX host. 30564562Sgshapiro */ 30664562Sgshapiro 307132943Sgshapiro if (FallbackMX != NULL && 308132943Sgshapiro !wordinclass(FallbackMX, 'w') && 30964562Sgshapiro mode != SM_VERIFY && 31090792Sgshapiro !bitnset(M_NOMX, m->m_flags) && 31190792Sgshapiro strcmp(m->m_mailer, "[IPC]") == 0 && 31264562Sgshapiro m->m_argv[0] != NULL && 31390792Sgshapiro strcmp(m->m_argv[0], "TCP") == 0) 31464562Sgshapiro { 31564562Sgshapiro int len; 31664562Sgshapiro char *p; 31764562Sgshapiro 31864562Sgshapiro if (tTd(13, 30)) 319132943Sgshapiro sm_dprintf(" ... FallbackMX\n"); 32064562Sgshapiro 321132943Sgshapiro len = strlen(FallbackMX) + 1; 32290792Sgshapiro p = sm_rpool_malloc_x(e->e_rpool, len); 323132943Sgshapiro (void) sm_strlcpy(p, FallbackMX, len); 32464562Sgshapiro q->q_state = QS_OK; 32564562Sgshapiro q->q_host = p; 32664562Sgshapiro } 32764562Sgshapiro else 32864562Sgshapiro { 32964562Sgshapiro if (tTd(13, 30)) 33090792Sgshapiro sm_dprintf(" ... QS_IS_QUEUEUP\n"); 33164562Sgshapiro continue; 33264562Sgshapiro } 33364562Sgshapiro } 33464562Sgshapiro 33538032Speter /* 33638032Speter ** If this mailer is expensive, and if we don't 33738032Speter ** want to make connections now, just mark these 33838032Speter ** addresses and return. This is useful if we 33938032Speter ** want to batch connections to reduce load. This 34038032Speter ** will cause the messages to be queued up, and a 34138032Speter ** daemon will come along to send the messages later. 34238032Speter */ 34338032Speter 34464562Sgshapiro if (NoConnect && !Verbose && 34564562Sgshapiro bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) 34638032Speter { 34738032Speter if (tTd(13, 30)) 34890792Sgshapiro sm_dprintf(" ... expensive\n"); 34964562Sgshapiro q->q_state = QS_QUEUEUP; 35090792Sgshapiro expensive = true; 35138032Speter } 35264562Sgshapiro else if (bitnset(M_HOLD, q->q_mailer->m_flags) && 35364562Sgshapiro QueueLimitId == NULL && 35464562Sgshapiro QueueLimitSender == NULL && 35564562Sgshapiro QueueLimitRecipient == NULL) 35638032Speter { 35738032Speter if (tTd(13, 30)) 35890792Sgshapiro sm_dprintf(" ... hold\n"); 35964562Sgshapiro q->q_state = QS_QUEUEUP; 36090792Sgshapiro expensive = true; 36138032Speter } 36290792Sgshapiro else if (QueueMode != QM_QUARANTINE && 36390792Sgshapiro e->e_quarmsg != NULL) 36490792Sgshapiro { 36590792Sgshapiro if (tTd(13, 30)) 36690792Sgshapiro sm_dprintf(" ... quarantine: %s\n", 36790792Sgshapiro e->e_quarmsg); 36890792Sgshapiro q->q_state = QS_QUEUEUP; 36990792Sgshapiro expensive = true; 37090792Sgshapiro } 37138032Speter else 37238032Speter { 37338032Speter if (tTd(13, 30)) 37490792Sgshapiro sm_dprintf(" ... deliverable\n"); 37590792Sgshapiro somedeliveries = true; 37638032Speter } 37738032Speter } 37838032Speter 37938032Speter if (owner != NULL && otherowners > 0) 38038032Speter { 38138032Speter /* 38238032Speter ** Split this envelope into two. 38338032Speter */ 38438032Speter 38590792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 386168515Sgshapiro sizeof(*ee)); 38790792Sgshapiro STRUCTCOPY(*e, *ee); 38864562Sgshapiro ee->e_message = NULL; 38938032Speter ee->e_id = NULL; 39064562Sgshapiro assign_queueid(ee); 39138032Speter 39238032Speter if (tTd(13, 1)) 39390792Sgshapiro sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 39490792Sgshapiro e->e_id, ee->e_id, owner, 39590792Sgshapiro otherowners); 39638032Speter 39790792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 39890792Sgshapiro ee->e_sendqueue = copyqueue(e->e_sendqueue, 39990792Sgshapiro ee->e_rpool); 40090792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, 40190792Sgshapiro ee->e_rpool); 40238032Speter ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 40338032Speter ee->e_flags |= EF_NORECEIPT; 40490792Sgshapiro setsender(owner, ee, NULL, '\0', true); 40538032Speter if (tTd(13, 5)) 40638032Speter { 40790792Sgshapiro sm_dprintf("sendall(split): QS_SENDER "); 408132943Sgshapiro printaddr(sm_debug_file(), &ee->e_from, false); 40938032Speter } 41064562Sgshapiro ee->e_from.q_state = QS_SENDER; 41138032Speter ee->e_dfp = NULL; 41264562Sgshapiro ee->e_lockfp = NULL; 41338032Speter ee->e_xfp = NULL; 41490792Sgshapiro ee->e_qgrp = e->e_qgrp; 41590792Sgshapiro ee->e_qdir = e->e_qdir; 41638032Speter ee->e_errormode = EM_MAIL; 41738032Speter ee->e_sibling = splitenv; 41864562Sgshapiro ee->e_statmsg = NULL; 41990792Sgshapiro if (e->e_quarmsg != NULL) 42090792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 42190792Sgshapiro e->e_quarmsg); 42238032Speter splitenv = ee; 42338032Speter 42438032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 42538032Speter { 42638032Speter if (q->q_owner == owner) 42738032Speter { 42864562Sgshapiro q->q_state = QS_CLONED; 42938032Speter if (tTd(13, 6)) 43090792Sgshapiro sm_dprintf("\t... stripping %s from original envelope\n", 43190792Sgshapiro q->q_paddr); 43238032Speter } 43338032Speter } 43438032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 43538032Speter { 43638032Speter if (q->q_owner != owner) 43738032Speter { 43864562Sgshapiro q->q_state = QS_CLONED; 43938032Speter if (tTd(13, 6)) 44090792Sgshapiro sm_dprintf("\t... dropping %s from cloned envelope\n", 44190792Sgshapiro q->q_paddr); 44238032Speter } 44338032Speter else 44438032Speter { 44538032Speter /* clear DSN parameters */ 44638032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 44738032Speter q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 44838032Speter if (tTd(13, 6)) 44990792Sgshapiro sm_dprintf("\t... moving %s to cloned envelope\n", 45090792Sgshapiro q->q_paddr); 45138032Speter } 45238032Speter } 45338032Speter 45438032Speter if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 45590792Sgshapiro dup_queue_file(e, ee, DATAFL_LETTER); 45664562Sgshapiro 45764562Sgshapiro /* 45864562Sgshapiro ** Give the split envelope access to the parent 45964562Sgshapiro ** transcript file for errors obtained while 46064562Sgshapiro ** processing the recipients (done before the 46164562Sgshapiro ** envelope splitting). 46264562Sgshapiro */ 46364562Sgshapiro 46464562Sgshapiro if (e->e_xfp != NULL) 46590792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 46664562Sgshapiro 46764562Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 46864562Sgshapiro if (ee->e_xfp == NULL) 46964562Sgshapiro openxscript(ee); 47064562Sgshapiro 47142575Speter if (mode != SM_VERIFY && LogLevel > 4) 47290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 47390792Sgshapiro "%s: clone: owner=%s", 47490792Sgshapiro ee->e_id, owner); 47538032Speter } 47638032Speter } 47738032Speter 47838032Speter if (owner != NULL) 47938032Speter { 48090792Sgshapiro setsender(owner, e, NULL, '\0', true); 48138032Speter if (tTd(13, 5)) 48238032Speter { 48390792Sgshapiro sm_dprintf("sendall(owner): QS_SENDER "); 484132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 48538032Speter } 48664562Sgshapiro e->e_from.q_state = QS_SENDER; 48738032Speter e->e_errormode = EM_MAIL; 48838032Speter e->e_flags |= EF_NORECEIPT; 48938032Speter e->e_flags &= ~EF_FATALERRS; 49038032Speter } 49138032Speter 49238032Speter /* if nothing to be delivered, just queue up everything */ 49390792Sgshapiro if (!somedeliveries && !WILL_BE_QUEUED(mode) && 49438032Speter mode != SM_VERIFY) 49538032Speter { 49690792Sgshapiro time_t now; 49771345Sgshapiro 49838032Speter if (tTd(13, 29)) 499173340Sgshapiro sm_dprintf("No deliveries: auto-queueing\n"); 50038032Speter mode = SM_QUEUE; 50190792Sgshapiro now = curtime(); 50238032Speter 50338032Speter /* treat this as a delivery in terms of counting tries */ 50471345Sgshapiro e->e_dtime = now; 50538032Speter if (!expensive) 50638032Speter e->e_ntries++; 50738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 50838032Speter { 50971345Sgshapiro ee->e_dtime = now; 51038032Speter if (!expensive) 51138032Speter ee->e_ntries++; 51238032Speter } 51338032Speter } 51438032Speter 51590792Sgshapiro if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 516132943Sgshapiro (mode != SM_VERIFY && 517132943Sgshapiro (SuperSafe == SAFE_REALLY || 518132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER))) && 51938032Speter (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 52038032Speter { 52190792Sgshapiro bool msync; 52290792Sgshapiro 52366494Sgshapiro /* 52466494Sgshapiro ** Be sure everything is instantiated in the queue. 52566494Sgshapiro ** Split envelopes first in case the machine crashes. 52666494Sgshapiro ** If the original were done first, we may lose 52766494Sgshapiro ** recipients. 52866494Sgshapiro */ 52966494Sgshapiro 53090792Sgshapiro#if !HASFLOCK 53190792Sgshapiro msync = false; 53290792Sgshapiro#else /* !HASFLOCK */ 53390792Sgshapiro msync = mode == SM_FORK; 53490792Sgshapiro#endif /* !HASFLOCK */ 53590792Sgshapiro 53638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 53790792Sgshapiro queueup(ee, WILL_BE_QUEUED(mode), msync); 53890792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), msync); 53938032Speter } 54038032Speter 54138032Speter if (tTd(62, 10)) 54238032Speter checkfds("after envelope splitting"); 54338032Speter 54438032Speter /* 54538032Speter ** If we belong in background, fork now. 54638032Speter */ 54738032Speter 54838032Speter if (tTd(13, 20)) 54938032Speter { 55090792Sgshapiro sm_dprintf("sendall: final mode = %c\n", mode); 55138032Speter if (tTd(13, 21)) 55238032Speter { 55390792Sgshapiro sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 55490792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 55590792Sgshapiro e->e_id, e->e_from.q_paddr); 556132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 55738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 55838032Speter { 55990792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 56090792Sgshapiro ee->e_id, ee->e_from.q_paddr); 561132943Sgshapiro printaddr(sm_debug_file(), ee->e_sendqueue, true); 56238032Speter } 56390792Sgshapiro sm_dprintf("==========================================================\n\n"); 56438032Speter } 56538032Speter } 56638032Speter switch (mode) 56738032Speter { 56838032Speter case SM_VERIFY: 56938032Speter Verbose = 2; 57038032Speter break; 57138032Speter 57238032Speter case SM_QUEUE: 57338032Speter case SM_DEFER: 57464562Sgshapiro#if HASFLOCK 57538032Speter queueonly: 57664562Sgshapiro#endif /* HASFLOCK */ 57738032Speter if (e->e_nrcpts > 0) 57838032Speter e->e_flags |= EF_INQUEUE; 579203004Sgshapiro (void) dropenvelope(e, splitenv != NULL, true); 58038032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 58138032Speter { 58238032Speter if (ee->e_nrcpts > 0) 58338032Speter ee->e_flags |= EF_INQUEUE; 584203004Sgshapiro (void) dropenvelope(ee, false, true); 58538032Speter } 58638032Speter return; 58738032Speter 58838032Speter case SM_FORK: 58938032Speter if (e->e_xfp != NULL) 59090792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 59138032Speter 59264562Sgshapiro#if !HASFLOCK 59338032Speter /* 59438032Speter ** Since fcntl locking has the interesting semantic that 59538032Speter ** the lock is owned by a process, not by an open file 59638032Speter ** descriptor, we have to flush this to the queue, and 59738032Speter ** then restart from scratch in the child. 59838032Speter */ 59938032Speter 60038032Speter { 60138032Speter /* save id for future use */ 60238032Speter char *qid = e->e_id; 60338032Speter 60438032Speter /* now drop the envelope in the parent */ 60538032Speter e->e_flags |= EF_INQUEUE; 606203004Sgshapiro (void) dropenvelope(e, splitenv != NULL, false); 60738032Speter 60838032Speter /* arrange to reacquire lock after fork */ 60938032Speter e->e_id = qid; 61038032Speter } 61138032Speter 61238032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 61338032Speter { 61438032Speter /* save id for future use */ 61538032Speter char *qid = ee->e_id; 61638032Speter 61738032Speter /* drop envelope in parent */ 61838032Speter ee->e_flags |= EF_INQUEUE; 619203004Sgshapiro (void) dropenvelope(ee, false, false); 62038032Speter 62138032Speter /* and save qid for reacquisition */ 62238032Speter ee->e_id = qid; 62338032Speter } 624132943Sgshapiro 62564562Sgshapiro#endif /* !HASFLOCK */ 62638032Speter 62764562Sgshapiro /* 62864562Sgshapiro ** Since the delivery may happen in a child and the parent 62964562Sgshapiro ** does not wait, the parent may close the maps thereby 63064562Sgshapiro ** removing any shared memory used by the map. Therefore, 63164562Sgshapiro ** close the maps now so the child will dynamically open 63264562Sgshapiro ** them if necessary. 63364562Sgshapiro */ 63464562Sgshapiro 63590792Sgshapiro closemaps(false); 63664562Sgshapiro 63738032Speter pid = fork(); 63838032Speter if (pid < 0) 63938032Speter { 64064562Sgshapiro syserr("deliver: fork 1"); 64164562Sgshapiro#if HASFLOCK 64238032Speter goto queueonly; 64364562Sgshapiro#else /* HASFLOCK */ 64438032Speter e->e_id = NULL; 64538032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 64638032Speter ee->e_id = NULL; 64738032Speter return; 64864562Sgshapiro#endif /* HASFLOCK */ 64938032Speter } 65038032Speter else if (pid > 0) 65138032Speter { 65264562Sgshapiro#if HASFLOCK 65338032Speter /* be sure we leave the temp files to our child */ 65438032Speter /* close any random open files in the envelope */ 65538032Speter closexscript(e); 65638032Speter if (e->e_dfp != NULL) 65790792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 65838032Speter e->e_dfp = NULL; 65938032Speter e->e_flags &= ~EF_HAS_DF; 66038032Speter 66138032Speter /* can't call unlockqueue to avoid unlink of xfp */ 66238032Speter if (e->e_lockfp != NULL) 66390792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 66464562Sgshapiro else 66564562Sgshapiro syserr("%s: sendall: null lockfp", e->e_id); 66638032Speter e->e_lockfp = NULL; 66764562Sgshapiro#endif /* HASFLOCK */ 66838032Speter 66938032Speter /* make sure the parent doesn't own the envelope */ 67038032Speter e->e_id = NULL; 67138032Speter 67290792Sgshapiro#if USE_DOUBLE_FORK 67338032Speter /* catch intermediate zombie */ 67438032Speter (void) waitfor(pid); 67590792Sgshapiro#endif /* USE_DOUBLE_FORK */ 67638032Speter return; 67738032Speter } 67838032Speter 67977349Sgshapiro /* Reset global flags */ 68077349Sgshapiro RestartRequest = NULL; 68190792Sgshapiro RestartWorkGroup = false; 68277349Sgshapiro ShutdownRequest = NULL; 68377349Sgshapiro PendingSignal = 0; 68477349Sgshapiro 68566494Sgshapiro /* 68690792Sgshapiro ** Initialize exception stack and default exception 68790792Sgshapiro ** handler for child process. 68890792Sgshapiro */ 68990792Sgshapiro 69090792Sgshapiro sm_exc_newthread(fatal_error); 69190792Sgshapiro 69290792Sgshapiro /* 69366494Sgshapiro ** Since we have accepted responsbility for the message, 69466494Sgshapiro ** change the SIGTERM handler. intsig() (the old handler) 69566494Sgshapiro ** would remove the envelope if this was a command line 69666494Sgshapiro ** message submission. 69766494Sgshapiro */ 69866494Sgshapiro 69990792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 70066494Sgshapiro 70190792Sgshapiro#if USE_DOUBLE_FORK 70238032Speter /* double fork to avoid zombies */ 70338032Speter pid = fork(); 70438032Speter if (pid > 0) 70538032Speter exit(EX_OK); 70664562Sgshapiro save_errno = errno; 70790792Sgshapiro#endif /* USE_DOUBLE_FORK */ 70838032Speter 70990792Sgshapiro CurrentPid = getpid(); 71090792Sgshapiro 71138032Speter /* be sure we are immune from the terminal */ 71238032Speter disconnect(2, e); 71364562Sgshapiro clearstats(); 71438032Speter 71538032Speter /* prevent parent from waiting if there was an error */ 71638032Speter if (pid < 0) 71738032Speter { 71864562Sgshapiro errno = save_errno; 71964562Sgshapiro syserr("deliver: fork 2"); 72064562Sgshapiro#if HASFLOCK 72138032Speter e->e_flags |= EF_INQUEUE; 72264562Sgshapiro#else /* HASFLOCK */ 72338032Speter e->e_id = NULL; 72464562Sgshapiro#endif /* HASFLOCK */ 72590792Sgshapiro finis(true, true, ExitStat); 72638032Speter } 72738032Speter 72838032Speter /* be sure to give error messages in child */ 72990792Sgshapiro QuickAbort = false; 73038032Speter 73138032Speter /* 73238032Speter ** Close any cached connections. 73338032Speter ** 73438032Speter ** We don't send the QUIT protocol because the parent 73538032Speter ** still knows about the connection. 73638032Speter ** 73738032Speter ** This should only happen when delivering an error 73838032Speter ** message. 73938032Speter */ 74038032Speter 74190792Sgshapiro mci_flush(false, NULL); 74238032Speter 74364562Sgshapiro#if HASFLOCK 74438032Speter break; 74564562Sgshapiro#else /* HASFLOCK */ 74638032Speter 74738032Speter /* 74838032Speter ** Now reacquire and run the various queue files. 74938032Speter */ 75038032Speter 75138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 75238032Speter { 75338032Speter ENVELOPE *sibling = ee->e_sibling; 75438032Speter 75590792Sgshapiro (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 75690792Sgshapiro false, false, ee); 75738032Speter ee->e_sibling = sibling; 75838032Speter } 75990792Sgshapiro (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 76090792Sgshapiro false, false, e); 76190792Sgshapiro finis(true, true, ExitStat); 76264562Sgshapiro#endif /* HASFLOCK */ 76338032Speter } 76438032Speter 76538032Speter sendenvelope(e, mode); 766203004Sgshapiro (void) dropenvelope(e, true, true); 76738032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 76838032Speter { 76938032Speter CurEnv = ee; 77038032Speter if (mode != SM_VERIFY) 77138032Speter openxscript(ee); 77238032Speter sendenvelope(ee, mode); 773203004Sgshapiro (void) dropenvelope(ee, true, true); 77438032Speter } 77538032Speter CurEnv = e; 77638032Speter 77738032Speter Verbose = oldverbose; 77838032Speter if (mode == SM_FORK) 77990792Sgshapiro finis(true, true, ExitStat); 78038032Speter} 78138032Speter 78264562Sgshapirostatic void 78338032Spetersendenvelope(e, mode) 78438032Speter register ENVELOPE *e; 78538032Speter int mode; 78638032Speter{ 78738032Speter register ADDRESS *q; 78838032Speter bool didany; 78938032Speter 79038032Speter if (tTd(13, 10)) 79190792Sgshapiro sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 79290792Sgshapiro e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 79390792Sgshapiro e->e_flags); 79438032Speter if (LogLevel > 80) 79538032Speter sm_syslog(LOG_DEBUG, e->e_id, 79664562Sgshapiro "sendenvelope, flags=0x%lx", 79764562Sgshapiro e->e_flags); 79838032Speter 79938032Speter /* 80038032Speter ** If we have had global, fatal errors, don't bother sending 80138032Speter ** the message at all if we are in SMTP mode. Local errors 80238032Speter ** (e.g., a single address failing) will still cause the other 80338032Speter ** addresses to be sent. 80438032Speter */ 80538032Speter 80638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 80738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 80838032Speter { 80938032Speter e->e_flags |= EF_CLRQUEUE; 81038032Speter return; 81138032Speter } 81238032Speter 81390792Sgshapiro /* 81490792Sgshapiro ** Don't attempt deliveries if we want to bounce now 81590792Sgshapiro ** or if deliver-by time is exceeded. 81690792Sgshapiro */ 81790792Sgshapiro 81864562Sgshapiro if (!bitset(EF_RESPONSE, e->e_flags) && 81990792Sgshapiro (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 82090792Sgshapiro (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 82190792Sgshapiro curtime() > e->e_ctime + e->e_deliver_by))) 82264562Sgshapiro return; 82364562Sgshapiro 82438032Speter /* 82538032Speter ** Run through the list and send everything. 82638032Speter ** 82738032Speter ** Set EF_GLOBALERRS so that error messages during delivery 82838032Speter ** result in returned mail. 82938032Speter */ 83038032Speter 83138032Speter e->e_nsent = 0; 83238032Speter e->e_flags |= EF_GLOBALERRS; 83364562Sgshapiro 83490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 83590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 83690792Sgshapiro didany = false; 83738032Speter 83890792Sgshapiro if (!bitset(EF_SPLIT, e->e_flags)) 83990792Sgshapiro { 84090792Sgshapiro ENVELOPE *oldsib; 84190792Sgshapiro ENVELOPE *ee; 84290792Sgshapiro 84390792Sgshapiro /* 84490792Sgshapiro ** Save old sibling and set it to NULL to avoid 84590792Sgshapiro ** queueing up the same envelopes again. 84690792Sgshapiro ** This requires that envelopes in that list have 84790792Sgshapiro ** been take care of before (or at some other place). 84890792Sgshapiro */ 84990792Sgshapiro 85090792Sgshapiro oldsib = e->e_sibling; 85190792Sgshapiro e->e_sibling = NULL; 85290792Sgshapiro if (!split_by_recipient(e) && 85390792Sgshapiro bitset(EF_FATALERRS, e->e_flags)) 85490792Sgshapiro { 85590792Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 85690792Sgshapiro e->e_flags |= EF_CLRQUEUE; 85790792Sgshapiro return; 85890792Sgshapiro } 85990792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86090792Sgshapiro queueup(ee, false, true); 86190792Sgshapiro 86290792Sgshapiro /* clean up */ 86390792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86490792Sgshapiro { 86590792Sgshapiro /* now unlock the job */ 86690792Sgshapiro closexscript(ee); 86790792Sgshapiro unlockqueue(ee); 86890792Sgshapiro 86990792Sgshapiro /* this envelope is marked unused */ 87090792Sgshapiro if (ee->e_dfp != NULL) 87190792Sgshapiro { 87290792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 87390792Sgshapiro ee->e_dfp = NULL; 87490792Sgshapiro } 87590792Sgshapiro ee->e_id = NULL; 87690792Sgshapiro ee->e_flags &= ~EF_HAS_DF; 87790792Sgshapiro } 87890792Sgshapiro e->e_sibling = oldsib; 87990792Sgshapiro } 88090792Sgshapiro 88138032Speter /* now run through the queue */ 88238032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 88338032Speter { 88438032Speter#if XDEBUG 88538032Speter char wbuf[MAXNAME + 20]; 88638032Speter 887168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)", 88890792Sgshapiro MAXNAME, q->q_paddr); 88938032Speter checkfd012(wbuf); 89064562Sgshapiro#endif /* XDEBUG */ 89138032Speter if (mode == SM_VERIFY) 89238032Speter { 89338032Speter e->e_to = q->q_paddr; 89464562Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 89538032Speter { 89638032Speter if (q->q_host != NULL && q->q_host[0] != '\0') 89738032Speter message("deliverable: mailer %s, host %s, user %s", 89838032Speter q->q_mailer->m_name, 89938032Speter q->q_host, 90038032Speter q->q_user); 90138032Speter else 90238032Speter message("deliverable: mailer %s, user %s", 90338032Speter q->q_mailer->m_name, 90438032Speter q->q_user); 90538032Speter } 90638032Speter } 90764562Sgshapiro else if (QS_IS_OK(q->q_state)) 90838032Speter { 90938032Speter /* 91038032Speter ** Checkpoint the send list every few addresses 91138032Speter */ 91238032Speter 91366494Sgshapiro if (CheckpointInterval > 0 && 91466494Sgshapiro e->e_nsent >= CheckpointInterval) 91538032Speter { 91690792Sgshapiro queueup(e, false, false); 91738032Speter e->e_nsent = 0; 91838032Speter } 91938032Speter (void) deliver(e, q); 92090792Sgshapiro didany = true; 92138032Speter } 92238032Speter } 92338032Speter if (didany) 92438032Speter { 92538032Speter e->e_dtime = curtime(); 92638032Speter e->e_ntries++; 92738032Speter } 92838032Speter 92938032Speter#if XDEBUG 93038032Speter checkfd012("end of sendenvelope"); 93164562Sgshapiro#endif /* XDEBUG */ 93238032Speter} 93390792Sgshapiro 93490792Sgshapiro#if REQUIRES_DIR_FSYNC 93590792Sgshapiro/* 93690792Sgshapiro** SYNC_DIR -- fsync a directory based on a filename 93790792Sgshapiro** 93890792Sgshapiro** Parameters: 93990792Sgshapiro** filename -- path of file 94090792Sgshapiro** panic -- panic? 94190792Sgshapiro** 94290792Sgshapiro** Returns: 94390792Sgshapiro** none 94490792Sgshapiro*/ 94590792Sgshapiro 94690792Sgshapirovoid 94790792Sgshapirosync_dir(filename, panic) 94890792Sgshapiro char *filename; 94990792Sgshapiro bool panic; 95090792Sgshapiro{ 95190792Sgshapiro int dirfd; 95290792Sgshapiro char *dirp; 95390792Sgshapiro char dir[MAXPATHLEN]; 95490792Sgshapiro 955110560Sgshapiro if (!RequiresDirfsync) 956110560Sgshapiro return; 957110560Sgshapiro 95890792Sgshapiro /* filesystems which require the directory be synced */ 95990792Sgshapiro dirp = strrchr(filename, '/'); 96090792Sgshapiro if (dirp != NULL) 96190792Sgshapiro { 962168515Sgshapiro if (sm_strlcpy(dir, filename, sizeof(dir)) >= sizeof(dir)) 96390792Sgshapiro return; 96490792Sgshapiro dir[dirp - filename] = '\0'; 96590792Sgshapiro dirp = dir; 96690792Sgshapiro } 96790792Sgshapiro else 96890792Sgshapiro dirp = "."; 96990792Sgshapiro dirfd = open(dirp, O_RDONLY, 0700); 97090792Sgshapiro if (tTd(40,32)) 97190792Sgshapiro sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 97290792Sgshapiro dirp, dirfd); 97390792Sgshapiro if (dirfd >= 0) 97490792Sgshapiro { 97590792Sgshapiro if (fsync(dirfd) < 0) 97690792Sgshapiro { 97790792Sgshapiro if (panic) 97890792Sgshapiro syserr("!sync_dir: cannot fsync directory %s", 97990792Sgshapiro dirp); 98090792Sgshapiro else if (LogLevel > 1) 98190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 98290792Sgshapiro "sync_dir: cannot fsync directory %s: %s", 98390792Sgshapiro dirp, sm_errstring(errno)); 98490792Sgshapiro } 98590792Sgshapiro (void) close(dirfd); 98690792Sgshapiro } 98790792Sgshapiro} 98890792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */ 98990792Sgshapiro/* 99038032Speter** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 99138032Speter** 99238032Speter** Parameters: 99338032Speter** e -- the existing envelope 99438032Speter** ee -- the new envelope 99590792Sgshapiro** type -- the queue file type (e.g., DATAFL_LETTER) 99638032Speter** 99738032Speter** Returns: 99838032Speter** none 99938032Speter*/ 100038032Speter 100164562Sgshapirostatic void 100238032Speterdup_queue_file(e, ee, type) 100390792Sgshapiro ENVELOPE *e, *ee; 100438032Speter int type; 100538032Speter{ 100664562Sgshapiro char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 100738032Speter 100838032Speter ee->e_dfp = NULL; 100938032Speter ee->e_xfp = NULL; 101064562Sgshapiro 101164562Sgshapiro /* 101264562Sgshapiro ** Make sure both are in the same directory. 101364562Sgshapiro */ 101464562Sgshapiro 1015168515Sgshapiro (void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf)); 1016168515Sgshapiro (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof(f2buf)); 1017102528Sgshapiro 1018102528Sgshapiro /* Force the df to disk if it's not there yet */ 1019102528Sgshapiro if (type == DATAFL_LETTER && e->e_dfp != NULL && 1020102528Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 1021102528Sgshapiro errno != EINVAL) 1022102528Sgshapiro { 1023102528Sgshapiro syserr("!dup_queue_file: can't commit %s", f1buf); 1024102528Sgshapiro /* NOTREACHED */ 1025102528Sgshapiro } 1026102528Sgshapiro 102738032Speter if (link(f1buf, f2buf) < 0) 102838032Speter { 102964562Sgshapiro int save_errno = errno; 103038032Speter 103138032Speter syserr("sendall: link(%s, %s)", f1buf, f2buf); 103264562Sgshapiro if (save_errno == EEXIST) 103338032Speter { 103438032Speter if (unlink(f2buf) < 0) 103538032Speter { 103638032Speter syserr("!sendall: unlink(%s): permanent", 103790792Sgshapiro f2buf); 103864562Sgshapiro /* NOTREACHED */ 103938032Speter } 104038032Speter if (link(f1buf, f2buf) < 0) 104138032Speter { 104238032Speter syserr("!sendall: link(%s, %s): permanent", 104390792Sgshapiro f1buf, f2buf); 104464562Sgshapiro /* NOTREACHED */ 104538032Speter } 104638032Speter } 104738032Speter } 104890792Sgshapiro SYNC_DIR(f2buf, true); 104938032Speter} 105090792Sgshapiro/* 105138032Speter** DOFORK -- do a fork, retrying a couple of times on failure. 105238032Speter** 105338032Speter** This MUST be a macro, since after a vfork we are running 105438032Speter** two processes on the same stack!!! 105538032Speter** 105638032Speter** Parameters: 105738032Speter** none. 105838032Speter** 105938032Speter** Returns: 106038032Speter** From a macro??? You've got to be kidding! 106138032Speter** 106238032Speter** Side Effects: 106338032Speter** Modifies the ==> LOCAL <== variable 'pid', leaving: 106438032Speter** pid of child in parent, zero in child. 106538032Speter** -1 on unrecoverable error. 106638032Speter** 106738032Speter** Notes: 106838032Speter** I'm awfully sorry this looks so awful. That's 106938032Speter** vfork for you..... 107038032Speter*/ 107138032Speter 107264562Sgshapiro#define NFORKTRIES 5 107338032Speter 107464562Sgshapiro#ifndef FORK 107538032Speter# define FORK fork 107664562Sgshapiro#endif /* ! FORK */ 107738032Speter 107864562Sgshapiro#define DOFORK(fORKfN) \ 107938032Speter{\ 108038032Speter register int i;\ 108138032Speter\ 108238032Speter for (i = NFORKTRIES; --i >= 0; )\ 108338032Speter {\ 108438032Speter pid = fORKfN();\ 108538032Speter if (pid >= 0)\ 108638032Speter break;\ 108738032Speter if (i > 0)\ 108864562Sgshapiro (void) sleep((unsigned) NFORKTRIES - i);\ 108938032Speter }\ 109038032Speter} 109190792Sgshapiro/* 109238032Speter** DOFORK -- simple fork interface to DOFORK. 109338032Speter** 109438032Speter** Parameters: 109538032Speter** none. 109638032Speter** 109738032Speter** Returns: 109838032Speter** pid of child in parent. 109938032Speter** zero in child. 110038032Speter** -1 on error. 110138032Speter** 110238032Speter** Side Effects: 110338032Speter** returns twice, once in parent and once in child. 110438032Speter*/ 110538032Speter 110677349Sgshapiropid_t 110738032Speterdofork() 110838032Speter{ 110938032Speter register pid_t pid = -1; 111038032Speter 111138032Speter DOFORK(fork); 111264562Sgshapiro return pid; 111338032Speter} 111490792Sgshapiro 111590792Sgshapiro/* 111690792Sgshapiro** COLONCMP -- compare host-signatures up to first ':' or EOS 111790792Sgshapiro** 111890792Sgshapiro** This takes two strings which happen to be host-signatures and 111990792Sgshapiro** compares them. If the lowest preference portions of the MX-RR's 112090792Sgshapiro** match (up to ':' or EOS, whichever is first), then we have 112190792Sgshapiro** match. This is used for coattail-piggybacking messages during 112290792Sgshapiro** message delivery. 112390792Sgshapiro** If the signatures are the same up to the first ':' the remainder of 112490792Sgshapiro** the signatures are then compared with a normal strcmp(). This saves 112590792Sgshapiro** re-examining the first part of the signatures. 112690792Sgshapiro** 112790792Sgshapiro** Parameters: 112890792Sgshapiro** a - first host-signature 112990792Sgshapiro** b - second host-signature 113090792Sgshapiro** 113190792Sgshapiro** Returns: 113290792Sgshapiro** HS_MATCH_NO -- no "match". 113390792Sgshapiro** HS_MATCH_FIRST -- "match" for the first MX preference 113490792Sgshapiro** (up to the first colon (':')). 113590792Sgshapiro** HS_MATCH_FULL -- match for the entire MX record. 113690792Sgshapiro** 113790792Sgshapiro** Side Effects: 113890792Sgshapiro** none. 113990792Sgshapiro*/ 114090792Sgshapiro 114190792Sgshapiro#define HS_MATCH_NO 0 114290792Sgshapiro#define HS_MATCH_FIRST 1 114390792Sgshapiro#define HS_MATCH_FULL 2 114490792Sgshapiro 114590792Sgshapirostatic int 114690792Sgshapirocoloncmp(a, b) 114790792Sgshapiro register const char *a; 114890792Sgshapiro register const char *b; 114990792Sgshapiro{ 115090792Sgshapiro int ret = HS_MATCH_NO; 115190792Sgshapiro int braclev = 0; 115290792Sgshapiro 115390792Sgshapiro while (*a == *b++) 115490792Sgshapiro { 115590792Sgshapiro /* Need to account for IPv6 bracketed addresses */ 115690792Sgshapiro if (*a == '[') 115790792Sgshapiro braclev++; 1158112810Sgshapiro else if (*a == ']' && braclev > 0) 115990792Sgshapiro braclev--; 116090792Sgshapiro else if (*a == ':' && braclev <= 0) 116190792Sgshapiro { 116290792Sgshapiro ret = HS_MATCH_FIRST; 116390792Sgshapiro a++; 116490792Sgshapiro break; 116590792Sgshapiro } 116690792Sgshapiro else if (*a == '\0') 116790792Sgshapiro return HS_MATCH_FULL; /* a full match */ 116890792Sgshapiro a++; 116990792Sgshapiro } 117090792Sgshapiro if (ret == HS_MATCH_NO && 117190792Sgshapiro braclev <= 0 && 117290792Sgshapiro ((*a == '\0' && *(b - 1) == ':') || 117390792Sgshapiro (*a == ':' && *(b - 1) == '\0'))) 117490792Sgshapiro return HS_MATCH_FIRST; 117590792Sgshapiro if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 117690792Sgshapiro return HS_MATCH_FULL; 117790792Sgshapiro 117890792Sgshapiro return ret; 117990792Sgshapiro} 1180132943Sgshapiro 118190792Sgshapiro/* 1182132943Sgshapiro** SHOULD_TRY_FBSH -- Should try FallbackSmartHost? 1183132943Sgshapiro** 1184132943Sgshapiro** Parameters: 1185132943Sgshapiro** e -- envelope 1186132943Sgshapiro** tried_fallbacksmarthost -- has been tried already? (in/out) 1187132943Sgshapiro** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out) 1188132943Sgshapiro** hbsz -- size of hostbuf 1189132943Sgshapiro** status -- current delivery status 1190132943Sgshapiro** 1191132943Sgshapiro** Returns: 1192132943Sgshapiro** true iff FallbackSmartHost should be tried. 1193132943Sgshapiro*/ 1194132943Sgshapiro 1195168515Sgshapirostatic bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int)); 1196168515Sgshapiro 1197132943Sgshapirostatic bool 1198132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status) 1199132943Sgshapiro ENVELOPE *e; 1200132943Sgshapiro bool *tried_fallbacksmarthost; 1201132943Sgshapiro char *hostbuf; 1202132943Sgshapiro size_t hbsz; 1203132943Sgshapiro int status; 1204132943Sgshapiro{ 1205132943Sgshapiro /* 1206157001Sgshapiro ** If the host was not found or a temporary failure occurred 1207157001Sgshapiro ** and a FallbackSmartHost is defined (and we have not yet 1208157001Sgshapiro ** tried it), then make one last try with it as the host. 1209132943Sgshapiro */ 1210132943Sgshapiro 1211157001Sgshapiro if ((status == EX_NOHOST || status == EX_TEMPFAIL) && 1212157001Sgshapiro FallbackSmartHost != NULL && !*tried_fallbacksmarthost) 1213132943Sgshapiro { 1214132943Sgshapiro *tried_fallbacksmarthost = true; 1215132943Sgshapiro expand(FallbackSmartHost, hostbuf, hbsz, e); 1216132943Sgshapiro if (!wordinclass(hostbuf, 'w')) 1217132943Sgshapiro { 1218132943Sgshapiro if (tTd(11, 1)) 1219132943Sgshapiro sm_dprintf("one last try with FallbackSmartHost %s\n", 1220132943Sgshapiro hostbuf); 1221132943Sgshapiro return true; 1222132943Sgshapiro } 1223132943Sgshapiro } 1224132943Sgshapiro return false; 1225132943Sgshapiro} 1226285303Sgshapiro 1227132943Sgshapiro/* 122838032Speter** DELIVER -- Deliver a message to a list of addresses. 122938032Speter** 123038032Speter** This routine delivers to everyone on the same host as the 123138032Speter** user on the head of the list. It is clever about mailers 123238032Speter** that don't handle multiple users. It is NOT guaranteed 123338032Speter** that it will deliver to all these addresses however -- so 123438032Speter** deliver should be called once for each address on the 123538032Speter** list. 123690792Sgshapiro** Deliver tries to be as opportunistic as possible about piggybacking 123790792Sgshapiro** messages. Some definitions to make understanding easier follow below. 123890792Sgshapiro** Piggybacking occurs when an existing connection to a mail host can 123990792Sgshapiro** be used to send the same message to more than one recipient at the 124090792Sgshapiro** same time. So "no piggybacking" means one message for one recipient 124190792Sgshapiro** per connection. "Intentional piggybacking" happens when the 124290792Sgshapiro** recipients' host address (not the mail host address) is used to 124390792Sgshapiro** attempt piggybacking. Recipients with the same host address 124490792Sgshapiro** have the same mail host. "Coincidental piggybacking" relies on 124590792Sgshapiro** piggybacking based on all the mail host addresses in the MX-RR. This 124690792Sgshapiro** is "coincidental" in the fact it could not be predicted until the 124790792Sgshapiro** MX Resource Records for the hosts were obtained and examined. For 124890792Sgshapiro** example (preference order and equivalence is important, not values): 124990792Sgshapiro** domain1 IN MX 10 mxhost-A 125090792Sgshapiro** IN MX 20 mxhost-B 125190792Sgshapiro** domain2 IN MX 4 mxhost-A 125290792Sgshapiro** IN MX 8 mxhost-B 125390792Sgshapiro** Domain1 and domain2 can piggyback the same message to mxhost-A or 125490792Sgshapiro** mxhost-B (if mxhost-A cannot be reached). 125590792Sgshapiro** "Coattail piggybacking" relaxes the strictness of "coincidental 125690792Sgshapiro** piggybacking" in the hope that most significant (lowest value) 125790792Sgshapiro** MX preference host(s) can create more piggybacking. For example 125890792Sgshapiro** (again, preference order and equivalence is important, not values): 125990792Sgshapiro** domain3 IN MX 100 mxhost-C 126090792Sgshapiro** IN MX 100 mxhost-D 126190792Sgshapiro** IN MX 200 mxhost-E 126290792Sgshapiro** domain4 IN MX 50 mxhost-C 126390792Sgshapiro** IN MX 50 mxhost-D 126490792Sgshapiro** IN MX 80 mxhost-F 126590792Sgshapiro** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 126690792Sgshapiro** is available. Same with mxhost-D because in both RR's the preference 126790792Sgshapiro** value is the same as mxhost-C, respectively. 126890792Sgshapiro** So deliver attempts coattail piggybacking when possible. If the 126990792Sgshapiro** first MX preference level hosts cannot be used then the piggybacking 127090792Sgshapiro** reverts to coincidental piggybacking. Using the above example you 127190792Sgshapiro** cannot deliver to mxhost-F for domain3 regardless of preference value. 127290792Sgshapiro** ("Coattail" from "riding on the coattails of your predecessor" meaning 127390792Sgshapiro** gaining benefit from a predecessor effort with no or little addition 127490792Sgshapiro** effort. The predecessor here being the preceding MX RR). 127538032Speter** 127638032Speter** Parameters: 127738032Speter** e -- the envelope to deliver. 127838032Speter** firstto -- head of the address list to deliver to. 127938032Speter** 128038032Speter** Returns: 128138032Speter** zero -- successfully delivered. 128238032Speter** else -- some failure, see ExitStat for more info. 128338032Speter** 128438032Speter** Side Effects: 128538032Speter** The standard input is passed off to someone. 128638032Speter*/ 128738032Speter 128864562Sgshapirostatic int 128938032Speterdeliver(e, firstto) 129038032Speter register ENVELOPE *e; 129138032Speter ADDRESS *firstto; 129238032Speter{ 129338032Speter char *host; /* host being sent to */ 129438032Speter char *user; /* user being sent to */ 129538032Speter char **pvp; 129638032Speter register char **mvp; 129738032Speter register char *p; 129838032Speter register MAILER *m; /* mailer for this recipient */ 129938032Speter ADDRESS *volatile ctladdr; 130090792Sgshapiro#if HASSETUSERCONTEXT 130138032Speter ADDRESS *volatile contextaddr = NULL; 130290792Sgshapiro#endif /* HASSETUSERCONTEXT */ 130338032Speter register MCI *volatile mci; 130490792Sgshapiro register ADDRESS *SM_NONVOLATILE to = firstto; 130590792Sgshapiro volatile bool clever = false; /* running user smtp to this mailer */ 130638032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 130738032Speter int rcode; /* response code */ 130890792Sgshapiro SM_NONVOLATILE int lmtp_rcode = EX_OK; 130990792Sgshapiro SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 131090792Sgshapiro SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 131138032Speter char *firstsig; /* signature of firstto */ 131290792Sgshapiro volatile pid_t pid = -1; 131338032Speter char *volatile curhost; 131490792Sgshapiro SM_NONVOLATILE unsigned short port = 0; 131590792Sgshapiro SM_NONVOLATILE time_t enough = 0; 131664562Sgshapiro#if NETUNIX 131790792Sgshapiro char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 131864562Sgshapiro#endif /* NETUNIX */ 131938032Speter time_t xstart; 132038032Speter bool suidwarn; 132138032Speter bool anyok; /* at least one address was OK */ 132290792Sgshapiro SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 132364562Sgshapiro bool ovr; 132490792Sgshapiro bool quarantine; 132564562Sgshapiro int strsize; 132664562Sgshapiro int rcptcount; 132790792Sgshapiro int ret; 132864562Sgshapiro static int tobufsize = 0; 132964562Sgshapiro static char *tobuf = NULL; 133090792Sgshapiro char *rpath; /* translated return path */ 133138032Speter int mpvect[2]; 133238032Speter int rpvect[2]; 133364562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 133464562Sgshapiro char *pv[MAXPV + 1]; 133538032Speter char buf[MAXNAME + 1]; 133698121Sgshapiro char cbuf[MAXPATHLEN]; 133738032Speter 133838032Speter errno = 0; 1339168515Sgshapiro SM_REQUIRE(firstto != NULL); /* same as to */ 134064562Sgshapiro if (!QS_IS_OK(to->q_state)) 134164562Sgshapiro return 0; 134238032Speter 134338032Speter suidwarn = geteuid() == 0; 134438032Speter 1345168515Sgshapiro SM_REQUIRE(e != NULL); 134638032Speter m = to->q_mailer; 134738032Speter host = to->q_host; 134838032Speter CurEnv = e; /* just in case */ 134938032Speter e->e_statmsg = NULL; 135038032Speter SmtpError[0] = '\0'; 135138032Speter xstart = curtime(); 135238032Speter 135338032Speter if (tTd(10, 1)) 135490792Sgshapiro sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 135538032Speter e->e_id, m->m_name, host, to->q_user); 135638032Speter if (tTd(10, 100)) 135790792Sgshapiro printopenfds(false); 135838032Speter 135938032Speter /* 136090792Sgshapiro ** Clear {client_*} macros if this is a bounce message to 136138032Speter ** prevent rejection by check_compat ruleset. 136238032Speter */ 136364562Sgshapiro 136438032Speter if (bitset(EF_RESPONSE, e->e_flags)) 136538032Speter { 136690792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 1367132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), ""); 136890792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 136990792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 137090792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 137138032Speter } 137264562Sgshapiro 137390792Sgshapiro SM_TRY 137490792Sgshapiro { 137590792Sgshapiro ADDRESS *skip_back = NULL; 137690792Sgshapiro 137738032Speter /* 137838032Speter ** Do initial argv setup. 137938032Speter ** Insert the mailer name. Notice that $x expansion is 138038032Speter ** NOT done on the mailer name. Then, if the mailer has 138138032Speter ** a picky -f flag, we insert it as appropriate. This 138238032Speter ** code does not check for 'pv' overflow; this places a 138338032Speter ** manifest lower limit of 4 for MAXPV. 138438032Speter ** The from address rewrite is expected to make 138538032Speter ** the address relative to the other end. 138638032Speter */ 138738032Speter 138838032Speter /* rewrite from address, using rewriting rules */ 138938032Speter rcode = EX_OK; 1390168515Sgshapiro SM_ASSERT(e->e_from.q_mailer != NULL); 139138032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 139238032Speter p = e->e_sender; 139338032Speter else 139438032Speter p = e->e_from.q_paddr; 139590792Sgshapiro rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 1396285303Sgshapiro if (rcode != EX_OK && bitnset(M_xSMTP, m->m_flags)) 1397285303Sgshapiro goto cleanup; 1398203004Sgshapiro if (strlen(rpath) > MAXNAME) 139938032Speter { 140090792Sgshapiro rpath = shortenstring(rpath, MAXSHORTSTR); 140190792Sgshapiro 140290792Sgshapiro /* avoid bogus errno */ 140390792Sgshapiro errno = 0; 140490792Sgshapiro syserr("remotename: huge return path %s", rpath); 140538032Speter } 140690792Sgshapiro rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 140790792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', rpath); 140890792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', host); 140938032Speter Errors = 0; 141038032Speter pvp = pv; 141138032Speter *pvp++ = m->m_argv[0]; 141238032Speter 1413132943Sgshapiro /* ignore long term host status information if mailer flag W is set */ 1414132943Sgshapiro if (bitnset(M_NOHOSTSTAT, m->m_flags)) 1415132943Sgshapiro IgnoreHostStatus = true; 1416132943Sgshapiro 141738032Speter /* insert -f or -r flag as appropriate */ 141864562Sgshapiro if (FromFlag && 141964562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 142064562Sgshapiro bitnset(M_ROPT, m->m_flags))) 142138032Speter { 142238032Speter if (bitnset(M_FOPT, m->m_flags)) 142338032Speter *pvp++ = "-f"; 142438032Speter else 142538032Speter *pvp++ = "-r"; 142690792Sgshapiro *pvp++ = rpath; 142738032Speter } 142838032Speter 142938032Speter /* 143038032Speter ** Append the other fixed parts of the argv. These run 143138032Speter ** up to the first entry containing "$u". There can only 143238032Speter ** be one of these, and there are only a few more slots 143338032Speter ** in the pv after it. 143438032Speter */ 143538032Speter 143638032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 143738032Speter { 143838032Speter /* can't use strchr here because of sign extension problems */ 143938032Speter while (*p != '\0') 144038032Speter { 144138032Speter if ((*p++ & 0377) == MACROEXPAND) 144238032Speter { 144338032Speter if (*p == 'u') 144438032Speter break; 144538032Speter } 144638032Speter } 144738032Speter 144838032Speter if (*p != '\0') 144938032Speter break; 145038032Speter 145138032Speter /* this entry is safe -- go ahead and process it */ 1452168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 145390792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 145438032Speter if (pvp >= &pv[MAXPV - 3]) 145538032Speter { 145664562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 145764562Sgshapiro pv[0]); 145890792Sgshapiro rcode = -1; 145990792Sgshapiro goto cleanup; 146038032Speter } 146138032Speter } 146238032Speter 146338032Speter /* 146438032Speter ** If we have no substitution for the user name in the argument 146538032Speter ** list, we know that we must supply the names otherwise -- and 146638032Speter ** SMTP is the answer!! 146738032Speter */ 146838032Speter 146938032Speter if (*mvp == NULL) 147038032Speter { 147173188Sgshapiro /* running LMTP or SMTP */ 147290792Sgshapiro clever = true; 147338032Speter *pvp = NULL; 1474285303Sgshapiro setbitn(M_xSMTP, m->m_flags); 147538032Speter } 147673188Sgshapiro else if (bitnset(M_LMTP, m->m_flags)) 147773188Sgshapiro { 147873188Sgshapiro /* not running LMTP */ 147973188Sgshapiro sm_syslog(LOG_ERR, NULL, 148073188Sgshapiro "Warning: mailer %s: LMTP flag (F=z) turned off", 148173188Sgshapiro m->m_name); 148273188Sgshapiro clrbitn(M_LMTP, m->m_flags); 148373188Sgshapiro } 148438032Speter 148538032Speter /* 148638032Speter ** At this point *mvp points to the argument with $u. We 148738032Speter ** run through our address list and append all the addresses 148838032Speter ** we can. If we run out of space, do not fret! We can 148938032Speter ** always send another copy later. 149038032Speter */ 149138032Speter 149264562Sgshapiro e->e_to = NULL; 149364562Sgshapiro strsize = 2; 149464562Sgshapiro rcptcount = 0; 149590792Sgshapiro ctladdr = NULL; 149690792Sgshapiro if (firstto->q_signature == NULL) 149790792Sgshapiro firstto->q_signature = hostsignature(firstto->q_mailer, 149890792Sgshapiro firstto->q_host); 149990792Sgshapiro firstsig = firstto->q_signature; 150064562Sgshapiro 150138032Speter for (; to != NULL; to = to->q_next) 150238032Speter { 150338032Speter /* avoid sending multiple recipients to dumb mailers */ 150464562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 150564562Sgshapiro break; 150638032Speter 150738032Speter /* if already sent or not for this host, don't send */ 150890792Sgshapiro if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 150938032Speter continue; 151038032Speter 151190792Sgshapiro /* 151290792Sgshapiro ** Must be same mailer to keep grouping rcpts. 151390792Sgshapiro ** If mailers don't match: continue; sendqueue is not 151490792Sgshapiro ** sorted by mailers, so don't break; 151590792Sgshapiro */ 151690792Sgshapiro 151790792Sgshapiro if (to->q_mailer != firstto->q_mailer) 151890792Sgshapiro continue; 151990792Sgshapiro 152090792Sgshapiro if (to->q_signature == NULL) /* for safety */ 152190792Sgshapiro to->q_signature = hostsignature(to->q_mailer, 152290792Sgshapiro to->q_host); 152390792Sgshapiro 152490792Sgshapiro /* 152590792Sgshapiro ** This is for coincidental and tailcoat piggybacking messages 152690792Sgshapiro ** to the same mail host. While the signatures are identical 152790792Sgshapiro ** (that's the MX-RR's are identical) we can do coincidental 152890792Sgshapiro ** piggybacking. We try hard for coattail piggybacking 152990792Sgshapiro ** with the same mail host when the next recipient has the 153090792Sgshapiro ** same host at lowest preference. It may be that this 153190792Sgshapiro ** won't work out, so 'skip_back' is maintained if a backup 153290792Sgshapiro ** to coincidental piggybacking or full signature must happen. 153390792Sgshapiro */ 153490792Sgshapiro 153590792Sgshapiro ret = firstto == to ? HS_MATCH_FULL : 153690792Sgshapiro coloncmp(to->q_signature, firstsig); 153790792Sgshapiro if (ret == HS_MATCH_FULL) 153890792Sgshapiro skip_back = to; 153990792Sgshapiro else if (ret == HS_MATCH_NO) 154064562Sgshapiro break; 154164562Sgshapiro 154290792Sgshapiro if (!clever) 154390792Sgshapiro { 154490792Sgshapiro /* avoid overflowing tobuf */ 154590792Sgshapiro strsize += strlen(to->q_paddr) + 1; 154690792Sgshapiro if (strsize > TOBUFSIZE) 154790792Sgshapiro break; 154890792Sgshapiro } 154990792Sgshapiro 155064562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 155164562Sgshapiro break; 155238032Speter 155338032Speter if (tTd(10, 1)) 155438032Speter { 155590792Sgshapiro sm_dprintf("\nsend to "); 1556132943Sgshapiro printaddr(sm_debug_file(), to, false); 155738032Speter } 155838032Speter 155938032Speter /* compute effective uid/gid when sending */ 156038032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 156190792Sgshapiro# if HASSETUSERCONTEXT 156238032Speter contextaddr = ctladdr = getctladdr(to); 156390792Sgshapiro# else /* HASSETUSERCONTEXT */ 156490792Sgshapiro ctladdr = getctladdr(to); 156590792Sgshapiro# endif /* HASSETUSERCONTEXT */ 156638032Speter 156738032Speter if (tTd(10, 2)) 156838032Speter { 156990792Sgshapiro sm_dprintf("ctladdr="); 1570132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 157138032Speter } 157238032Speter 157338032Speter user = to->q_user; 157438032Speter e->e_to = to->q_paddr; 157538032Speter 157638032Speter /* 157738032Speter ** Check to see that these people are allowed to 157838032Speter ** talk to each other. 157966494Sgshapiro ** Check also for overflow of e_msgsize. 158038032Speter */ 158138032Speter 158266494Sgshapiro if (m->m_maxsize != 0 && 158366494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 158438032Speter { 158538032Speter e->e_flags |= EF_NO_BODY_RETN; 158638032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 158738032Speter to->q_status = "5.2.3"; 158838032Speter else 158938032Speter to->q_status = "5.3.4"; 159090792Sgshapiro 159164562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 159264562Sgshapiro usrerrenh(to->q_status, 159364562Sgshapiro "552 Message is too large; %ld bytes max", 159464562Sgshapiro m->m_maxsize); 159590792Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, false); 159664562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 159790792Sgshapiro NULL, ctladdr, xstart, e, to); 159838032Speter continue; 159938032Speter } 160073188Sgshapiro SM_SET_H_ERRNO(0); 160190792Sgshapiro ovr = true; 160238032Speter 160338032Speter /* do config file checking of compatibility */ 160490792Sgshapiro quarantine = (e->e_quarmsg != NULL); 160564562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1606102528Sgshapiro e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1607285303Sgshapiro e->e_id, NULL, NULL); 160838032Speter if (rcode == EX_OK) 160938032Speter { 161042575Speter /* do in-code checking if not discarding */ 161142575Speter if (!bitset(EF_DISCARD, e->e_flags)) 161264562Sgshapiro { 161342575Speter rcode = checkcompat(to, e); 161490792Sgshapiro ovr = false; 161564562Sgshapiro } 161638032Speter } 161738032Speter if (rcode != EX_OK) 161838032Speter { 161964562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 162064562Sgshapiro giveresponse(rcode, to->q_status, m, 162190792Sgshapiro NULL, ctladdr, xstart, e, to); 162238032Speter continue; 162338032Speter } 162490792Sgshapiro if (!quarantine && e->e_quarmsg != NULL) 162590792Sgshapiro { 162690792Sgshapiro /* 162790792Sgshapiro ** check_compat or checkcompat() has tried 162890792Sgshapiro ** to quarantine but that isn't supported. 162990792Sgshapiro ** Revert the attempt. 163090792Sgshapiro */ 163190792Sgshapiro 163290792Sgshapiro e->e_quarmsg = NULL; 163390792Sgshapiro macdefine(&e->e_macro, A_PERM, 163490792Sgshapiro macid("{quarantine}"), ""); 163590792Sgshapiro } 163642575Speter if (bitset(EF_DISCARD, e->e_flags)) 163742575Speter { 163842575Speter if (tTd(10, 5)) 163942575Speter { 164090792Sgshapiro sm_dprintf("deliver: discarding recipient "); 1641132943Sgshapiro printaddr(sm_debug_file(), to, false); 164242575Speter } 164338032Speter 164464562Sgshapiro /* pretend the message was sent */ 164564562Sgshapiro /* XXX should we log something here? */ 164664562Sgshapiro to->q_state = QS_DISCARDED; 164764562Sgshapiro 164842575Speter /* 164942575Speter ** Remove discard bit to prevent discard of 165064562Sgshapiro ** future recipients. This is safe because the 165164562Sgshapiro ** true "global discard" has been handled before 165264562Sgshapiro ** we get here. 165342575Speter */ 165464562Sgshapiro 165542575Speter e->e_flags &= ~EF_DISCARD; 165642575Speter continue; 165742575Speter } 165842575Speter 165938032Speter /* 166038032Speter ** Strip quote bits from names if the mailer is dumb 166138032Speter ** about them. 166238032Speter */ 166338032Speter 166438032Speter if (bitnset(M_STRIPQ, m->m_flags)) 166538032Speter { 166638032Speter stripquotes(user); 166738032Speter stripquotes(host); 166838032Speter } 1669132943Sgshapiro 1670110560Sgshapiro /* 1671141858Sgshapiro ** Strip all leading backslashes if requested and the 1672110560Sgshapiro ** next character is alphanumerical (the latter can 1673110560Sgshapiro ** probably relaxed a bit, see RFC2821). 1674110560Sgshapiro */ 167538032Speter 1676110560Sgshapiro if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1677110560Sgshapiro stripbackslash(user); 1678110560Sgshapiro 167938032Speter /* hack attack -- delivermail compatibility */ 168038032Speter if (m == ProgMailer && *user == '|') 168138032Speter user++; 168238032Speter 168338032Speter /* 168438032Speter ** If an error message has already been given, don't 168538032Speter ** bother to send to this address. 168638032Speter ** 168738032Speter ** >>>>>>>>>> This clause assumes that the local mailer 168838032Speter ** >> NOTE >> cannot do any further aliasing; that 168938032Speter ** >>>>>>>>>> function is subsumed by sendmail. 169038032Speter */ 169138032Speter 169264562Sgshapiro if (!QS_IS_OK(to->q_state)) 169338032Speter continue; 169438032Speter 169538032Speter /* 169638032Speter ** See if this user name is "special". 169738032Speter ** If the user name has a slash in it, assume that this 169838032Speter ** is a file -- send it off without further ado. Note 169938032Speter ** that this type of addresses is not processed along 170038032Speter ** with the others, so we fudge on the To person. 170138032Speter */ 170238032Speter 170338032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 170438032Speter { 170590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); 170638032Speter p = to->q_home; 170738032Speter if (p == NULL && ctladdr != NULL) 170838032Speter p = ctladdr->q_home; 170990792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); 1710168515Sgshapiro expand(m->m_argv[1], buf, sizeof(buf), e); 171138032Speter if (strlen(buf) > 0) 171238032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 171338032Speter else 171438032Speter { 171538032Speter syserr("empty filename specification for mailer %s", 171638032Speter m->m_name); 171738032Speter rcode = EX_CONFIG; 171838032Speter } 171964562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 172090792Sgshapiro ctladdr, xstart, e, to); 172190792Sgshapiro markfailure(e, to, NULL, rcode, true); 172238032Speter e->e_nsent++; 172338032Speter if (rcode == EX_OK) 172438032Speter { 172564562Sgshapiro to->q_state = QS_SENT; 172638032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 172738032Speter bitset(QPINGONSUCCESS, to->q_flags)) 172838032Speter { 172938032Speter to->q_flags |= QDELIVERED; 173038032Speter to->q_status = "2.1.5"; 173190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, 173290792Sgshapiro SM_TIME_DEFAULT, 173390792Sgshapiro "%s... Successfully delivered\n", 173490792Sgshapiro to->q_paddr); 173538032Speter } 173638032Speter } 173738032Speter to->q_statdate = curtime(); 173890792Sgshapiro markstats(e, to, STATS_NORMAL); 173938032Speter continue; 174038032Speter } 174138032Speter 174238032Speter /* 174338032Speter ** Address is verified -- add this user to mailer 174438032Speter ** argv, and add it to the print list of recipients. 174538032Speter */ 174638032Speter 174738032Speter /* link together the chain of recipients */ 174838032Speter to->q_tchain = tochain; 174938032Speter tochain = to; 175064562Sgshapiro e->e_to = "[CHAIN]"; 175164562Sgshapiro 175290792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 175338032Speter p = to->q_home; 175438032Speter if (p == NULL && ctladdr != NULL) 175538032Speter p = ctladdr->q_home; 175690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 175738032Speter 175864562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 175964562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 176064562Sgshapiro { 176164562Sgshapiro char notify[MAXLINE]; 176264562Sgshapiro 176364562Sgshapiro notify[0] = '\0'; 176464562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 176590792Sgshapiro (void) sm_strlcat(notify, "SUCCESS,", 1766168515Sgshapiro sizeof(notify)); 176764562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 176890792Sgshapiro (void) sm_strlcat(notify, "FAILURE,", 1769168515Sgshapiro sizeof(notify)); 177064562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 177190792Sgshapiro (void) sm_strlcat(notify, "DELAY,", 1772168515Sgshapiro sizeof(notify)); 177364562Sgshapiro 177464562Sgshapiro /* Set to NEVER or drop trailing comma */ 177564562Sgshapiro if (notify[0] == '\0') 177690792Sgshapiro (void) sm_strlcat(notify, "NEVER", 1777168515Sgshapiro sizeof(notify)); 177864562Sgshapiro else 177964562Sgshapiro notify[strlen(notify) - 1] = '\0'; 178064562Sgshapiro 178190792Sgshapiro macdefine(&e->e_macro, A_TEMP, 178290792Sgshapiro macid("{dsn_notify}"), notify); 178364562Sgshapiro } 178464562Sgshapiro else 178590792Sgshapiro macdefine(&e->e_macro, A_PERM, 178690792Sgshapiro macid("{dsn_notify}"), NULL); 178764562Sgshapiro 178838032Speter /* 178938032Speter ** Expand out this user into argument list. 179038032Speter */ 179138032Speter 179238032Speter if (!clever) 179338032Speter { 1794168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 179590792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 179638032Speter if (pvp >= &pv[MAXPV - 2]) 179738032Speter { 179838032Speter /* allow some space for trailing parms */ 179938032Speter break; 180038032Speter } 180138032Speter } 180238032Speter } 180338032Speter 180438032Speter /* see if any addresses still exist */ 180564562Sgshapiro if (tochain == NULL) 180638032Speter { 180790792Sgshapiro rcode = 0; 180890792Sgshapiro goto cleanup; 180938032Speter } 181038032Speter 181138032Speter /* print out messages as full list */ 181290792Sgshapiro strsize = 1; 181390792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 181490792Sgshapiro strsize += strlen(to->q_paddr) + 1; 181590792Sgshapiro if (strsize < TOBUFSIZE) 181690792Sgshapiro strsize = TOBUFSIZE; 181790792Sgshapiro if (strsize > tobufsize) 181864562Sgshapiro { 181990792Sgshapiro SM_FREE_CLR(tobuf); 182090792Sgshapiro tobuf = sm_pmalloc_x(strsize); 182190792Sgshapiro tobufsize = strsize; 182264562Sgshapiro } 182390792Sgshapiro p = tobuf; 182490792Sgshapiro *p = '\0'; 182590792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 182690792Sgshapiro { 182790792Sgshapiro (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 182890792Sgshapiro ",", to->q_paddr); 182990792Sgshapiro p += strlen(p); 183090792Sgshapiro } 183138032Speter e->e_to = tobuf + 1; 183238032Speter 183338032Speter /* 183438032Speter ** Fill out any parameters after the $u parameter. 183538032Speter */ 183638032Speter 183790792Sgshapiro if (!clever) 183838032Speter { 183990792Sgshapiro while (*++mvp != NULL) 184090792Sgshapiro { 1841168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 184290792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 184390792Sgshapiro if (pvp >= &pv[MAXPV]) 184490792Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 184590792Sgshapiro pv[0]); 184690792Sgshapiro } 184738032Speter } 184838032Speter *pvp++ = NULL; 184938032Speter 185038032Speter /* 185138032Speter ** Call the mailer. 185238032Speter ** The argument vector gets built, pipes 185338032Speter ** are created as necessary, and we fork & exec as 185438032Speter ** appropriate. 185538032Speter ** If we are running SMTP, we just need to clean up. 185638032Speter */ 185738032Speter 1858223067Sgshapiro /* XXX this seems a bit weird */ 185938032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 186038032Speter bitset(QGOODUID, e->e_from.q_flags)) 186138032Speter ctladdr = &e->e_from; 186238032Speter 186338032Speter#if NAMED_BIND 186438032Speter if (ConfigLevel < 2) 186538032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 186664562Sgshapiro#endif /* NAMED_BIND */ 186738032Speter 186838032Speter if (tTd(11, 1)) 186938032Speter { 187090792Sgshapiro sm_dprintf("openmailer:"); 1871132943Sgshapiro printav(sm_debug_file(), pv); 187238032Speter } 187338032Speter errno = 0; 187473188Sgshapiro SM_SET_H_ERRNO(0); 187538032Speter CurHostName = NULL; 187638032Speter 187738032Speter /* 187838032Speter ** Deal with the special case of mail handled through an IPC 187938032Speter ** connection. 188038032Speter ** In this case we don't actually fork. We must be 188138032Speter ** running SMTP for this to work. We will return a 188238032Speter ** zero pid to indicate that we are running IPC. 188338032Speter ** We also handle a debug version that just talks to stdin/out. 188438032Speter */ 188538032Speter 188638032Speter curhost = NULL; 188738032Speter SmtpPhase = NULL; 188838032Speter mci = NULL; 188938032Speter 189038032Speter#if XDEBUG 189138032Speter { 189238032Speter char wbuf[MAXLINE]; 189338032Speter 189438032Speter /* make absolutely certain 0, 1, and 2 are in use */ 1895168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)", 189690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 189790792Sgshapiro m->m_name); 189838032Speter checkfd012(wbuf); 189938032Speter } 190064562Sgshapiro#endif /* XDEBUG */ 190138032Speter 190238032Speter /* check for 8-bit available */ 190338032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 190438032Speter bitnset(M_7BITS, m->m_flags) && 190538032Speter (bitset(EF_DONT_MIME, e->e_flags) || 190638032Speter !(bitset(MM_MIME8BIT, MimeMode) || 190738032Speter (bitset(EF_IS_MIME, e->e_flags) && 190864562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 190938032Speter { 191064562Sgshapiro e->e_status = "5.6.3"; 191164562Sgshapiro usrerrenh(e->e_status, 191290792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 191338032Speter rcode = EX_DATAERR; 191438032Speter goto give_up; 191538032Speter } 191638032Speter 191738032Speter if (tTd(62, 8)) 191838032Speter checkfds("before delivery"); 191938032Speter 192038032Speter /* check for Local Person Communication -- not for mortals!!! */ 192138032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 192238032Speter { 192390792Sgshapiro if (clever) 192490792Sgshapiro { 192590792Sgshapiro /* flush any expired connections */ 192690792Sgshapiro (void) mci_scan(NULL); 192790792Sgshapiro 192890792Sgshapiro /* try to get a cached connection or just a slot */ 192990792Sgshapiro mci = mci_get(m->m_name, m); 193090792Sgshapiro if (mci->mci_host == NULL) 193190792Sgshapiro mci->mci_host = m->m_name; 193290792Sgshapiro CurHostName = mci->mci_host; 193390792Sgshapiro if (mci->mci_state != MCIS_CLOSED) 193490792Sgshapiro { 193590792Sgshapiro message("Using cached SMTP/LPC connection for %s...", 193690792Sgshapiro m->m_name); 193790792Sgshapiro mci->mci_deliveries++; 193890792Sgshapiro goto do_transfer; 193990792Sgshapiro } 194090792Sgshapiro } 194190792Sgshapiro else 194290792Sgshapiro { 194390792Sgshapiro mci = mci_new(e->e_rpool); 194490792Sgshapiro } 194590792Sgshapiro mci->mci_in = smioin; 194690792Sgshapiro mci->mci_out = smioout; 194790792Sgshapiro mci->mci_mailer = m; 194890792Sgshapiro mci->mci_host = m->m_name; 194990792Sgshapiro if (clever) 195090792Sgshapiro { 195190792Sgshapiro mci->mci_state = MCIS_OPENING; 195290792Sgshapiro mci_cache(mci); 195390792Sgshapiro } 195490792Sgshapiro else 195590792Sgshapiro mci->mci_state = MCIS_OPEN; 195638032Speter } 195790792Sgshapiro else if (strcmp(m->m_mailer, "[IPC]") == 0) 195838032Speter { 195938032Speter register int i; 196038032Speter 196138032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 196238032Speter { 196364562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 196438032Speter rcode = EX_CONFIG; 196538032Speter goto give_up; 196638032Speter } 196738032Speter 196864562Sgshapiro# if NETUNIX 196964562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 197064562Sgshapiro { 197164562Sgshapiro curhost = CurHostName = "localhost"; 197264562Sgshapiro mux_path = pv[1]; 197364562Sgshapiro } 197464562Sgshapiro else 197564562Sgshapiro# endif /* NETUNIX */ 197664562Sgshapiro { 197764562Sgshapiro CurHostName = pv[1]; 197864562Sgshapiro curhost = hostsignature(m, pv[1]); 197964562Sgshapiro } 198038032Speter 198138032Speter if (curhost == NULL || curhost[0] == '\0') 198238032Speter { 198338032Speter syserr("null host signature for %s", pv[1]); 198438032Speter rcode = EX_CONFIG; 198538032Speter goto give_up; 198638032Speter } 198738032Speter 198838032Speter if (!clever) 198938032Speter { 199064562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 199138032Speter rcode = EX_CONFIG; 199238032Speter goto give_up; 199338032Speter } 199464562Sgshapiro if (pv[2] != NULL 199564562Sgshapiro# if NETUNIX 199664562Sgshapiro && mux_path == NULL 199764562Sgshapiro# endif /* NETUNIX */ 199864562Sgshapiro ) 199938032Speter { 200090792Sgshapiro port = htons((unsigned short) atoi(pv[2])); 200138032Speter if (port == 0) 200238032Speter { 200364562Sgshapiro# ifdef NO_GETSERVBYNAME 200464562Sgshapiro syserr("Invalid port number: %s", pv[2]); 200564562Sgshapiro# else /* NO_GETSERVBYNAME */ 200638032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 200738032Speter 200838032Speter if (sp == NULL) 200938032Speter syserr("Service %s unknown", pv[2]); 201038032Speter else 201138032Speter port = sp->s_port; 201264562Sgshapiro# endif /* NO_GETSERVBYNAME */ 201338032Speter } 201438032Speter } 201564562Sgshapiro 201664562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 201790792Sgshapiro if (TimeOuts.to_aconnect > 0) 201890792Sgshapiro enough = curtime() + TimeOuts.to_aconnect; 201938032Spetertryhost: 202064562Sgshapiro while (hostnum < nummxhosts) 202138032Speter { 202264562Sgshapiro char sep = ':'; 202364562Sgshapiro char *endp; 202438032Speter static char hostbuf[MAXNAME + 1]; 2025132943Sgshapiro bool tried_fallbacksmarthost = false; 202638032Speter 202764562Sgshapiro# if NETINET6 202864562Sgshapiro if (*mxhosts[hostnum] == '[') 202938032Speter { 203064562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 203164562Sgshapiro if (endp != NULL) 203264562Sgshapiro endp = strpbrk(endp + 1, ":,"); 203364562Sgshapiro } 203464562Sgshapiro else 203564562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 203664562Sgshapiro# else /* NETINET6 */ 203764562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 203864562Sgshapiro# endif /* NETINET6 */ 203964562Sgshapiro if (endp != NULL) 204064562Sgshapiro { 204164562Sgshapiro sep = *endp; 204264562Sgshapiro *endp = '\0'; 204364562Sgshapiro } 204464562Sgshapiro 204590792Sgshapiro if (hostnum == 1 && skip_back != NULL) 204690792Sgshapiro { 204790792Sgshapiro /* 204890792Sgshapiro ** Coattail piggybacking is no longer an 204990792Sgshapiro ** option with the mail host next to be tried 205090792Sgshapiro ** no longer the lowest MX preference 205190792Sgshapiro ** (hostnum == 1 meaning we're on the second 205290792Sgshapiro ** preference). We do not try to coattail 205390792Sgshapiro ** piggyback more than the first MX preference. 205490792Sgshapiro ** Revert 'tochain' to last location for 205590792Sgshapiro ** coincidental piggybacking. This works this 205690792Sgshapiro ** easily because the q_tchain kept getting 205790792Sgshapiro ** added to the top of the linked list. 205890792Sgshapiro */ 205990792Sgshapiro 206090792Sgshapiro tochain = skip_back; 206190792Sgshapiro } 206290792Sgshapiro 206364562Sgshapiro if (*mxhosts[hostnum] == '\0') 206464562Sgshapiro { 206538032Speter syserr("deliver: null host name in signature"); 206664562Sgshapiro hostnum++; 206764562Sgshapiro if (endp != NULL) 206864562Sgshapiro *endp = sep; 206938032Speter continue; 207038032Speter } 207190792Sgshapiro (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 2072168515Sgshapiro sizeof(hostbuf)); 207364562Sgshapiro hostnum++; 207464562Sgshapiro if (endp != NULL) 207564562Sgshapiro *endp = sep; 207638032Speter 2077132943Sgshapiro one_last_try: 207838032Speter /* see if we already know that this host is fried */ 207938032Speter CurHostName = hostbuf; 208038032Speter mci = mci_get(hostbuf, m); 208138032Speter if (mci->mci_state != MCIS_CLOSED) 208238032Speter { 208390792Sgshapiro char *type; 208490792Sgshapiro 208538032Speter if (tTd(11, 1)) 208638032Speter { 208790792Sgshapiro sm_dprintf("openmailer: "); 2088132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 208938032Speter } 209038032Speter CurHostName = mci->mci_host; 209190792Sgshapiro if (bitnset(M_LMTP, m->m_flags)) 209290792Sgshapiro type = "L"; 209390792Sgshapiro else if (bitset(MCIF_ESMTP, mci->mci_flags)) 209490792Sgshapiro type = "ES"; 209590792Sgshapiro else 209690792Sgshapiro type = "S"; 209790792Sgshapiro message("Using cached %sMTP connection to %s via %s...", 209890792Sgshapiro type, hostbuf, m->m_name); 209964562Sgshapiro mci->mci_deliveries++; 210038032Speter break; 210138032Speter } 210238032Speter mci->mci_mailer = m; 210338032Speter if (mci->mci_exitstat != EX_OK) 210438032Speter { 210538032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 210690792Sgshapiro goodmxfound = true; 2107132943Sgshapiro 2108132943Sgshapiro /* Try FallbackSmartHost? */ 2109132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2110168515Sgshapiro hostbuf, sizeof(hostbuf), 2111132943Sgshapiro mci->mci_exitstat)) 2112132943Sgshapiro goto one_last_try; 2113132943Sgshapiro 211438032Speter continue; 211538032Speter } 211638032Speter 211738032Speter if (mci_lock_host(mci) != EX_OK) 211838032Speter { 211938032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 212090792Sgshapiro goodmxfound = true; 212138032Speter continue; 212238032Speter } 212338032Speter 212438032Speter /* try the connection */ 212590792Sgshapiro sm_setproctitle(true, e, "%s %s: %s", 212664562Sgshapiro qid_printname(e), 212764562Sgshapiro hostbuf, "user open"); 212864562Sgshapiro# if NETUNIX 212964562Sgshapiro if (mux_path != NULL) 213064562Sgshapiro { 213138032Speter message("Connecting to %s via %s...", 213264562Sgshapiro mux_path, m->m_name); 213390792Sgshapiro i = makeconnection_ds((char *) mux_path, mci); 213464562Sgshapiro } 213538032Speter else 213664562Sgshapiro# endif /* NETUNIX */ 213764562Sgshapiro { 213864562Sgshapiro if (port == 0) 213964562Sgshapiro message("Connecting to %s via %s...", 214064562Sgshapiro hostbuf, m->m_name); 214164562Sgshapiro else 214264562Sgshapiro message("Connecting to %s port %d via %s...", 214364562Sgshapiro hostbuf, ntohs(port), 214464562Sgshapiro m->m_name); 214590792Sgshapiro i = makeconnection(hostbuf, port, mci, e, 214690792Sgshapiro enough); 214764562Sgshapiro } 214877349Sgshapiro mci->mci_errno = errno; 214938032Speter mci->mci_lastuse = curtime(); 215064562Sgshapiro mci->mci_deliveries = 0; 215138032Speter mci->mci_exitstat = i; 2152223067Sgshapiro mci_clr_extensions(mci); 215364562Sgshapiro# if NAMED_BIND 215438032Speter mci->mci_herrno = h_errno; 215564562Sgshapiro# endif /* NAMED_BIND */ 215690792Sgshapiro 215790792Sgshapiro /* 215890792Sgshapiro ** Have we tried long enough to get a connection? 215990792Sgshapiro ** If yes, skip to the fallback MX hosts 216090792Sgshapiro ** (if existent). 216190792Sgshapiro */ 216290792Sgshapiro 216390792Sgshapiro if (enough > 0 && mci->mci_lastuse >= enough) 216490792Sgshapiro { 216590792Sgshapiro int h; 216690792Sgshapiro# if NAMED_BIND 2167132943Sgshapiro extern int NumFallbackMXHosts; 216890792Sgshapiro# else /* NAMED_BIND */ 2169132943Sgshapiro const int NumFallbackMXHosts = 0; 217090792Sgshapiro# endif /* NAMED_BIND */ 217190792Sgshapiro 217290792Sgshapiro if (hostnum < nummxhosts && LogLevel > 9) 217390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 217490792Sgshapiro "Timeout.to_aconnect occurred before exhausting all addresses"); 217590792Sgshapiro 217690792Sgshapiro /* turn off timeout if fallback available */ 2177132943Sgshapiro if (NumFallbackMXHosts > 0) 217890792Sgshapiro enough = 0; 217990792Sgshapiro 218090792Sgshapiro /* skip to a fallback MX host */ 2181132943Sgshapiro h = nummxhosts - NumFallbackMXHosts; 218290792Sgshapiro if (hostnum < h) 218390792Sgshapiro hostnum = h; 218490792Sgshapiro } 218538032Speter if (i == EX_OK) 218638032Speter { 218790792Sgshapiro goodmxfound = true; 218894334Sgshapiro markstats(e, firstto, STATS_CONNECT); 218938032Speter mci->mci_state = MCIS_OPENING; 219038032Speter mci_cache(mci); 219138032Speter if (TrafficLogFile != NULL) 219290792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 219390792Sgshapiro SM_TIME_DEFAULT, 219490792Sgshapiro "%05d === CONNECT %s\n", 219590792Sgshapiro (int) CurrentPid, 219690792Sgshapiro hostbuf); 219738032Speter break; 219838032Speter } 219938032Speter else 220038032Speter { 2201132943Sgshapiro /* Try FallbackSmartHost? */ 2202132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2203168515Sgshapiro hostbuf, sizeof(hostbuf), i)) 2204132943Sgshapiro goto one_last_try; 2205132943Sgshapiro 220664562Sgshapiro if (tTd(11, 1)) 220790792Sgshapiro sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 220890792Sgshapiro i, errno); 220938032Speter if (i == EX_TEMPFAIL) 221090792Sgshapiro goodmxfound = true; 221138032Speter mci_unlock_host(mci); 221238032Speter } 221338032Speter 221438032Speter /* enter status of this host */ 221538032Speter setstat(i); 221638032Speter 221738032Speter /* should print some message here for -v mode */ 221838032Speter } 221938032Speter if (mci == NULL) 222038032Speter { 222138032Speter syserr("deliver: no host name"); 222238032Speter rcode = EX_SOFTWARE; 222338032Speter goto give_up; 222438032Speter } 222538032Speter mci->mci_pid = 0; 222638032Speter } 222738032Speter else 222838032Speter { 222938032Speter /* flush any expired connections */ 223038032Speter (void) mci_scan(NULL); 223138032Speter mci = NULL; 223238032Speter 223338032Speter if (bitnset(M_LMTP, m->m_flags)) 223438032Speter { 223538032Speter /* try to get a cached connection */ 223638032Speter mci = mci_get(m->m_name, m); 223738032Speter if (mci->mci_host == NULL) 223838032Speter mci->mci_host = m->m_name; 223938032Speter CurHostName = mci->mci_host; 224038032Speter if (mci->mci_state != MCIS_CLOSED) 224138032Speter { 224238032Speter message("Using cached LMTP connection for %s...", 224338032Speter m->m_name); 224464562Sgshapiro mci->mci_deliveries++; 224538032Speter goto do_transfer; 224638032Speter } 224738032Speter } 224838032Speter 224938032Speter /* announce the connection to verbose listeners */ 225038032Speter if (host == NULL || host[0] == '\0') 225138032Speter message("Connecting to %s...", m->m_name); 225238032Speter else 225338032Speter message("Connecting to %s via %s...", host, m->m_name); 225438032Speter if (TrafficLogFile != NULL) 225538032Speter { 225638032Speter char **av; 225738032Speter 225890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 225990792Sgshapiro "%05d === EXEC", (int) CurrentPid); 226038032Speter for (av = pv; *av != NULL; av++) 226190792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 226290792Sgshapiro SM_TIME_DEFAULT, " %s", 226390792Sgshapiro *av); 226490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 226590792Sgshapiro "\n"); 226638032Speter } 226738032Speter 226838032Speter#if XDEBUG 226938032Speter checkfd012("before creating mail pipe"); 227064562Sgshapiro#endif /* XDEBUG */ 227138032Speter 227238032Speter /* create a pipe to shove the mail through */ 227338032Speter if (pipe(mpvect) < 0) 227438032Speter { 227538032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 227690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 227738032Speter if (tTd(11, 1)) 227890792Sgshapiro sm_dprintf("openmailer: NULL\n"); 227938032Speter rcode = EX_OSERR; 228038032Speter goto give_up; 228138032Speter } 228238032Speter 228338032Speter#if XDEBUG 228438032Speter /* make sure we didn't get one of the standard I/O files */ 228538032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 228638032Speter { 228738032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 228890792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 228990792Sgshapiro mpvect[0], mpvect[1]); 229090792Sgshapiro printopenfds(true); 229138032Speter if (tTd(11, 1)) 229290792Sgshapiro sm_dprintf("openmailer: NULL\n"); 229338032Speter rcode = EX_OSERR; 229438032Speter goto give_up; 229538032Speter } 229638032Speter 229738032Speter /* make sure system call isn't dead meat */ 229838032Speter checkfdopen(mpvect[0], "mpvect[0]"); 229938032Speter checkfdopen(mpvect[1], "mpvect[1]"); 230038032Speter if (mpvect[0] == mpvect[1] || 230138032Speter (e->e_lockfp != NULL && 230290792Sgshapiro (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 230390792Sgshapiro NULL) || 230490792Sgshapiro mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 230590792Sgshapiro NULL)))) 230638032Speter { 230738032Speter if (e->e_lockfp == NULL) 230838032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 230990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 231090792Sgshapiro m->m_name, mpvect[0], mpvect[1]); 231138032Speter else 231238032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 231390792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 231490792Sgshapiro m->m_name, mpvect[0], mpvect[1], 231590792Sgshapiro sm_io_getinfo(e->e_lockfp, 231690792Sgshapiro SM_IO_WHAT_FD, NULL)); 231738032Speter } 231864562Sgshapiro#endif /* XDEBUG */ 231938032Speter 232064562Sgshapiro /* create a return pipe */ 232164562Sgshapiro if (pipe(rpvect) < 0) 232238032Speter { 232364562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 232490792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 232590792Sgshapiro m->m_name); 232664562Sgshapiro (void) close(mpvect[0]); 232764562Sgshapiro (void) close(mpvect[1]); 232864562Sgshapiro if (tTd(11, 1)) 232990792Sgshapiro sm_dprintf("openmailer: NULL\n"); 233064562Sgshapiro rcode = EX_OSERR; 233164562Sgshapiro goto give_up; 233238032Speter } 233364562Sgshapiro#if XDEBUG 233464562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 233564562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 233664562Sgshapiro#endif /* XDEBUG */ 233738032Speter 233838032Speter /* 233938032Speter ** Actually fork the mailer process. 234038032Speter ** DOFORK is clever about retrying. 234138032Speter ** 234238032Speter ** Dispose of SIGCHLD signal catchers that may be laying 234364562Sgshapiro ** around so that endmailer will get it. 234438032Speter */ 234538032Speter 234690792Sgshapiro if (e->e_xfp != NULL) /* for debugging */ 234790792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 234890792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 234990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 235064562Sgshapiro 235164562Sgshapiro 235238032Speter DOFORK(FORK); 235338032Speter /* pid is set by DOFORK */ 235464562Sgshapiro 235538032Speter if (pid < 0) 235638032Speter { 235738032Speter /* failure */ 235838032Speter syserr("%s... openmailer(%s): cannot fork", 235990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 236038032Speter (void) close(mpvect[0]); 236138032Speter (void) close(mpvect[1]); 236264562Sgshapiro (void) close(rpvect[0]); 236364562Sgshapiro (void) close(rpvect[1]); 236438032Speter if (tTd(11, 1)) 236590792Sgshapiro sm_dprintf("openmailer: NULL\n"); 236638032Speter rcode = EX_OSERR; 236738032Speter goto give_up; 236838032Speter } 236938032Speter else if (pid == 0) 237038032Speter { 237164562Sgshapiro int save_errno; 237290792Sgshapiro int sff; 237338032Speter int new_euid = NO_UID; 237438032Speter int new_ruid = NO_UID; 237538032Speter int new_gid = NO_GID; 237690792Sgshapiro char *user = NULL; 237738032Speter struct stat stb; 237838032Speter extern int DtableSize; 237938032Speter 238090792Sgshapiro CurrentPid = getpid(); 238190792Sgshapiro 238280785Sgshapiro /* clear the events to turn off SIGALRMs */ 238390792Sgshapiro sm_clear_events(); 238480785Sgshapiro 238577349Sgshapiro /* Reset global flags */ 238677349Sgshapiro RestartRequest = NULL; 238790792Sgshapiro RestartWorkGroup = false; 238877349Sgshapiro ShutdownRequest = NULL; 238977349Sgshapiro PendingSignal = 0; 239077349Sgshapiro 239138032Speter if (e->e_lockfp != NULL) 239290792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, 239390792Sgshapiro SM_IO_WHAT_FD, 239490792Sgshapiro NULL)); 239538032Speter 239638032Speter /* child -- set up input & exec mailer */ 239790792Sgshapiro (void) sm_signal(SIGALRM, sm_signal_noop); 239890792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 239990792Sgshapiro (void) sm_signal(SIGHUP, SIG_IGN); 240090792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 240190792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 240280785Sgshapiro# ifdef SIGUSR1 240390792Sgshapiro (void) sm_signal(SIGUSR1, sm_signal_noop); 240480785Sgshapiro# endif /* SIGUSR1 */ 240538032Speter 240638032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 240738032Speter stb.st_mode = 0; 240838032Speter 240964562Sgshapiro# if HASSETUSERCONTEXT 241038032Speter /* 241138032Speter ** Set user resources. 241238032Speter */ 241338032Speter 241438032Speter if (contextaddr != NULL) 241538032Speter { 2416110560Sgshapiro int sucflags; 241738032Speter struct passwd *pwd; 241838032Speter 241938032Speter if (contextaddr->q_ruser != NULL) 242038032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 242138032Speter else 242238032Speter pwd = sm_getpwnam(contextaddr->q_user); 2423110560Sgshapiro sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2424223701Strasz#ifdef LOGIN_SETCPUMASK 2425223701Strasz sucflags |= LOGIN_SETCPUMASK; 2426223701Strasz#endif /* LOGIN_SETCPUMASK */ 2427223701Strasz#ifdef LOGIN_SETLOGINCLASS 2428223701Strasz sucflags |= LOGIN_SETLOGINCLASS; 2429223701Strasz#endif /* LOGIN_SETLOGINCLASS */ 2430110560Sgshapiro#ifdef LOGIN_SETMAC 2431110560Sgshapiro sucflags |= LOGIN_SETMAC; 2432110560Sgshapiro#endif /* LOGIN_SETMAC */ 2433102528Sgshapiro if (pwd != NULL && 2434102528Sgshapiro setusercontext(NULL, pwd, pwd->pw_uid, 2435110560Sgshapiro sucflags) == -1 && 2436102528Sgshapiro suidwarn) 2437102528Sgshapiro { 2438102528Sgshapiro syserr("openmailer: setusercontext() failed"); 2439102528Sgshapiro exit(EX_TEMPFAIL); 2440102528Sgshapiro } 244138032Speter } 244264562Sgshapiro# endif /* HASSETUSERCONTEXT */ 244338032Speter 244490792Sgshapiro#if HASNICE 244538032Speter /* tweak niceness */ 244638032Speter if (m->m_nice != 0) 244764562Sgshapiro (void) nice(m->m_nice); 244890792Sgshapiro#endif /* HASNICE */ 244938032Speter 245038032Speter /* reset group id */ 245138032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 2452132943Sgshapiro { 2453132943Sgshapiro if (m->m_gid == NO_GID) 2454132943Sgshapiro new_gid = RunAsGid; 2455132943Sgshapiro else 2456132943Sgshapiro new_gid = m->m_gid; 2457132943Sgshapiro } 245838032Speter else if (bitset(S_ISGID, stb.st_mode)) 245938032Speter new_gid = stb.st_gid; 246038032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 246138032Speter { 246238032Speter if (!DontInitGroups) 246338032Speter { 246490792Sgshapiro user = ctladdr->q_ruser; 246590792Sgshapiro if (user == NULL) 246690792Sgshapiro user = ctladdr->q_user; 246738032Speter 246890792Sgshapiro if (initgroups(user, 246990792Sgshapiro ctladdr->q_gid) == -1 247090792Sgshapiro && suidwarn) 247164562Sgshapiro { 2472285303Sgshapiro syserr("openmailer: initgroups(%s, %ld) failed", 2473285303Sgshapiro user, (long) ctladdr->q_gid); 247464562Sgshapiro exit(EX_TEMPFAIL); 247564562Sgshapiro } 247638032Speter } 247738032Speter else 247838032Speter { 247938032Speter GIDSET_T gidset[1]; 248038032Speter 248138032Speter gidset[0] = ctladdr->q_gid; 248290792Sgshapiro if (setgroups(1, gidset) == -1 248390792Sgshapiro && suidwarn) 248464562Sgshapiro { 248538032Speter syserr("openmailer: setgroups() failed"); 248664562Sgshapiro exit(EX_TEMPFAIL); 248764562Sgshapiro } 248838032Speter } 248938032Speter new_gid = ctladdr->q_gid; 249038032Speter } 249138032Speter else 249238032Speter { 249338032Speter if (!DontInitGroups) 249438032Speter { 249590792Sgshapiro user = DefUser; 249690792Sgshapiro if (initgroups(DefUser, DefGid) == -1 && 249790792Sgshapiro suidwarn) 249864562Sgshapiro { 2499285303Sgshapiro syserr("openmailer: initgroups(%s, %ld) failed", 2500285303Sgshapiro DefUser, (long) DefGid); 250164562Sgshapiro exit(EX_TEMPFAIL); 250264562Sgshapiro } 250338032Speter } 250438032Speter else 250538032Speter { 250638032Speter GIDSET_T gidset[1]; 250738032Speter 250838032Speter gidset[0] = DefGid; 250990792Sgshapiro if (setgroups(1, gidset) == -1 251090792Sgshapiro && suidwarn) 251164562Sgshapiro { 251238032Speter syserr("openmailer: setgroups() failed"); 251364562Sgshapiro exit(EX_TEMPFAIL); 251464562Sgshapiro } 251538032Speter } 2516132943Sgshapiro if (m->m_gid == NO_GID) 251738032Speter new_gid = DefGid; 251838032Speter else 251938032Speter new_gid = m->m_gid; 252038032Speter } 252164562Sgshapiro if (new_gid != NO_GID) 252264562Sgshapiro { 252364562Sgshapiro if (RunAsUid != 0 && 252464562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 252564562Sgshapiro new_gid != getgid() && 252664562Sgshapiro new_gid != getegid()) 252764562Sgshapiro { 252864562Sgshapiro /* Only root can change the gid */ 2529285303Sgshapiro syserr("openmailer: insufficient privileges to change gid, RunAsUid=%ld, new_gid=%ld, gid=%ld, egid=%ld", 2530285303Sgshapiro (long) RunAsUid, (long) new_gid, 2531285303Sgshapiro (long) getgid(), (long) getegid()); 253264562Sgshapiro exit(EX_TEMPFAIL); 253364562Sgshapiro } 253438032Speter 253564562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 253664562Sgshapiro { 253764562Sgshapiro syserr("openmailer: setgid(%ld) failed", 253864562Sgshapiro (long) new_gid); 253964562Sgshapiro exit(EX_TEMPFAIL); 254064562Sgshapiro } 254164562Sgshapiro } 254264562Sgshapiro 254364562Sgshapiro /* change root to some "safe" directory */ 254464562Sgshapiro if (m->m_rootdir != NULL) 254564562Sgshapiro { 2546168515Sgshapiro expand(m->m_rootdir, cbuf, sizeof(cbuf), e); 254764562Sgshapiro if (tTd(11, 20)) 254890792Sgshapiro sm_dprintf("openmailer: chroot %s\n", 254998121Sgshapiro cbuf); 255098121Sgshapiro if (chroot(cbuf) < 0) 255164562Sgshapiro { 255264562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 255398121Sgshapiro cbuf); 255464562Sgshapiro exit(EX_TEMPFAIL); 255564562Sgshapiro } 255664562Sgshapiro if (chdir("/") < 0) 255764562Sgshapiro { 255864562Sgshapiro syserr("openmailer: cannot chdir(/)"); 255964562Sgshapiro exit(EX_TEMPFAIL); 256064562Sgshapiro } 256164562Sgshapiro } 256264562Sgshapiro 256338032Speter /* reset user id */ 256438032Speter endpwent(); 256590792Sgshapiro sm_mbdb_terminate(); 256638032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 256780785Sgshapiro { 2568132943Sgshapiro if (m->m_uid == NO_UID) 2569132943Sgshapiro new_euid = RunAsUid; 2570132943Sgshapiro else 2571132943Sgshapiro new_euid = m->m_uid; 257280785Sgshapiro 257380785Sgshapiro /* 257480785Sgshapiro ** Undo the effects of the uid change in main 257580785Sgshapiro ** for signal handling. The real uid may 257680785Sgshapiro ** be used by mailer in adding a "From " 257780785Sgshapiro ** line. 257880785Sgshapiro */ 257980785Sgshapiro 258080785Sgshapiro if (RealUid != 0 && RealUid != getuid()) 258190792Sgshapiro { 258290792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 258390792Sgshapiro# if HASSETREUID 258490792Sgshapiro if (setreuid(RealUid, geteuid()) < 0) 258590792Sgshapiro { 258690792Sgshapiro syserr("openmailer: setreuid(%d, %d) failed", 258790792Sgshapiro (int) RealUid, (int) geteuid()); 258890792Sgshapiro exit(EX_OSERR); 258990792Sgshapiro } 259090792Sgshapiro# endif /* HASSETREUID */ 259190792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 259290792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 259380785Sgshapiro new_ruid = RealUid; 259490792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 259590792Sgshapiro } 259680785Sgshapiro } 259738032Speter else if (bitset(S_ISUID, stb.st_mode)) 259838032Speter new_ruid = stb.st_uid; 259938032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 260038032Speter new_ruid = ctladdr->q_uid; 2601132943Sgshapiro else if (m->m_uid != NO_UID) 260238032Speter new_ruid = m->m_uid; 260338032Speter else 260438032Speter new_ruid = DefUid; 260594334Sgshapiro 260694334Sgshapiro# if _FFR_USE_SETLOGIN 260794334Sgshapiro /* run disconnected from terminal and set login name */ 260894334Sgshapiro if (setsid() >= 0 && 260994334Sgshapiro ctladdr != NULL && ctladdr->q_uid != 0 && 261094334Sgshapiro new_euid == ctladdr->q_uid) 261194334Sgshapiro { 261294334Sgshapiro struct passwd *pwd; 261394334Sgshapiro 261494334Sgshapiro pwd = sm_getpwuid(ctladdr->q_uid); 261594334Sgshapiro if (pwd != NULL && suidwarn) 261694334Sgshapiro (void) setlogin(pwd->pw_name); 261794334Sgshapiro endpwent(); 261894334Sgshapiro } 261994334Sgshapiro# endif /* _FFR_USE_SETLOGIN */ 262094334Sgshapiro 262138032Speter if (new_euid != NO_UID) 262238032Speter { 262364562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 262464562Sgshapiro { 262564562Sgshapiro /* Only root can change the uid */ 2626285303Sgshapiro syserr("openmailer: insufficient privileges to change uid, new_euid=%ld, RunAsUid=%ld", 2627285303Sgshapiro (long) new_euid, (long) RunAsUid); 262864562Sgshapiro exit(EX_TEMPFAIL); 262964562Sgshapiro } 263064562Sgshapiro 263138032Speter vendor_set_uid(new_euid); 263264562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 263338032Speter if (seteuid(new_euid) < 0 && suidwarn) 263464562Sgshapiro { 263538032Speter syserr("openmailer: seteuid(%ld) failed", 263690792Sgshapiro (long) new_euid); 263764562Sgshapiro exit(EX_TEMPFAIL); 263864562Sgshapiro } 263964562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 264064562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 264138032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 264264562Sgshapiro { 264338032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 264490792Sgshapiro (long) new_ruid, (long) new_euid); 264564562Sgshapiro exit(EX_TEMPFAIL); 264664562Sgshapiro } 264764562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 264864562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID 264938032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 265064562Sgshapiro { 265138032Speter syserr("openmailer: setuid(%ld) failed", 265290792Sgshapiro (long) new_euid); 265364562Sgshapiro exit(EX_TEMPFAIL); 265464562Sgshapiro } 265564562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */ 265638032Speter } 265738032Speter else if (new_ruid != NO_UID) 265838032Speter { 265938032Speter vendor_set_uid(new_ruid); 266038032Speter if (setuid(new_ruid) < 0 && suidwarn) 266164562Sgshapiro { 266238032Speter syserr("openmailer: setuid(%ld) failed", 266390792Sgshapiro (long) new_ruid); 266464562Sgshapiro exit(EX_TEMPFAIL); 266564562Sgshapiro } 266638032Speter } 266738032Speter 266838032Speter if (tTd(11, 2)) 2669285303Sgshapiro sm_dprintf("openmailer: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n", 2670285303Sgshapiro (long) getuid(), (long) geteuid(), 2671285303Sgshapiro (long) getgid(), (long) getegid()); 267238032Speter 267338032Speter /* move into some "safe" directory */ 267438032Speter if (m->m_execdir != NULL) 267538032Speter { 267638032Speter char *q; 267738032Speter 267838032Speter for (p = m->m_execdir; p != NULL; p = q) 267938032Speter { 268038032Speter q = strchr(p, ':'); 268138032Speter if (q != NULL) 268238032Speter *q = '\0'; 2683168515Sgshapiro expand(p, cbuf, sizeof(cbuf), e); 268438032Speter if (q != NULL) 268538032Speter *q++ = ':'; 268638032Speter if (tTd(11, 20)) 268790792Sgshapiro sm_dprintf("openmailer: trydir %s\n", 268898121Sgshapiro cbuf); 268998121Sgshapiro if (cbuf[0] != '\0' && 269098121Sgshapiro chdir(cbuf) >= 0) 269138032Speter break; 269238032Speter } 269338032Speter } 269438032Speter 269590792Sgshapiro /* Check safety of program to be run */ 269690792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 269790792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, 269890792Sgshapiro DontBlameSendmail)) 269990792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 270090792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 270190792Sgshapiro DontBlameSendmail)) 270290792Sgshapiro sff |= SFF_NOPATHCHECK; 270390792Sgshapiro else 270490792Sgshapiro sff |= SFF_SAFEDIRPATH; 270590792Sgshapiro ret = safefile(m->m_mailer, getuid(), getgid(), 270690792Sgshapiro user, sff, 0, NULL); 270790792Sgshapiro if (ret != 0) 270890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 270990792Sgshapiro "Warning: program %s unsafe: %s", 271090792Sgshapiro m->m_mailer, sm_errstring(ret)); 271190792Sgshapiro 271238032Speter /* arrange to filter std & diag output of command */ 271364562Sgshapiro (void) close(rpvect[0]); 271464562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 271538032Speter { 271664562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 271764562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 271864562Sgshapiro m->m_name, rpvect[1]); 271964562Sgshapiro _exit(EX_OSERR); 272038032Speter } 272164562Sgshapiro (void) close(rpvect[1]); 272264562Sgshapiro 272338032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 272438032Speter { 272538032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 272690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 272790792Sgshapiro m->m_name); 272838032Speter _exit(EX_OSERR); 272938032Speter } 273038032Speter 273138032Speter /* arrange to get standard input */ 273238032Speter (void) close(mpvect[1]); 273338032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 273438032Speter { 273538032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 273690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 273790792Sgshapiro m->m_name, mpvect[0]); 273838032Speter _exit(EX_OSERR); 273938032Speter } 274038032Speter (void) close(mpvect[0]); 274138032Speter 274238032Speter /* arrange for all the files to be closed */ 2743132943Sgshapiro sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 274438032Speter 274594334Sgshapiro# if !_FFR_USE_SETLOGIN 274638032Speter /* run disconnected from terminal */ 274738032Speter (void) setsid(); 274894334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */ 274938032Speter 275038032Speter /* try to execute the mailer */ 275164562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 275264562Sgshapiro (ARGV_T) UserEnviron); 275364562Sgshapiro save_errno = errno; 275438032Speter syserr("Cannot exec %s", m->m_mailer); 275538032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 275664562Sgshapiro transienterror(save_errno)) 275738032Speter _exit(EX_OSERR); 275838032Speter _exit(EX_UNAVAILABLE); 275938032Speter } 276038032Speter 276138032Speter /* 276238032Speter ** Set up return value. 276338032Speter */ 276438032Speter 276538032Speter if (mci == NULL) 276638032Speter { 276790792Sgshapiro if (clever) 276890792Sgshapiro { 276990792Sgshapiro /* 277090792Sgshapiro ** Allocate from general heap, not 277190792Sgshapiro ** envelope rpool, because this mci 277290792Sgshapiro ** is going to be cached. 277390792Sgshapiro */ 277490792Sgshapiro 277590792Sgshapiro mci = mci_new(NULL); 277690792Sgshapiro } 277790792Sgshapiro else 277890792Sgshapiro { 277990792Sgshapiro /* 278090792Sgshapiro ** Prevent a storage leak by allocating 278190792Sgshapiro ** this from the envelope rpool. 278290792Sgshapiro */ 278390792Sgshapiro 278490792Sgshapiro mci = mci_new(e->e_rpool); 278590792Sgshapiro } 278638032Speter } 278738032Speter mci->mci_mailer = m; 278838032Speter if (clever) 278938032Speter { 279038032Speter mci->mci_state = MCIS_OPENING; 279138032Speter mci_cache(mci); 279238032Speter } 279338032Speter else 279438032Speter { 279538032Speter mci->mci_state = MCIS_OPEN; 279638032Speter } 279738032Speter mci->mci_pid = pid; 279838032Speter (void) close(mpvect[0]); 279990792Sgshapiro mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2800132943Sgshapiro (void *) &(mpvect[1]), SM_IO_WRONLY_B, 280190792Sgshapiro NULL); 280238032Speter if (mci->mci_out == NULL) 280338032Speter { 280438032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 280590792Sgshapiro mpvect[1]); 280638032Speter (void) close(mpvect[1]); 280764562Sgshapiro (void) close(rpvect[0]); 280864562Sgshapiro (void) close(rpvect[1]); 280938032Speter rcode = EX_OSERR; 281038032Speter goto give_up; 281138032Speter } 281264562Sgshapiro 281364562Sgshapiro (void) close(rpvect[1]); 281490792Sgshapiro mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2815132943Sgshapiro (void *) &(rpvect[0]), SM_IO_RDONLY_B, 281690792Sgshapiro NULL); 281764562Sgshapiro if (mci->mci_in == NULL) 281838032Speter { 281964562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 282064562Sgshapiro mpvect[1]); 282164562Sgshapiro (void) close(rpvect[0]); 282290792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 282364562Sgshapiro mci->mci_out = NULL; 282464562Sgshapiro rcode = EX_OSERR; 282564562Sgshapiro goto give_up; 282638032Speter } 282738032Speter } 282838032Speter 282938032Speter /* 283038032Speter ** If we are in SMTP opening state, send initial protocol. 283138032Speter */ 283238032Speter 283338032Speter if (bitnset(M_7BITS, m->m_flags) && 283438032Speter (!clever || mci->mci_state == MCIS_OPENING)) 283538032Speter mci->mci_flags |= MCIF_7BIT; 283638032Speter if (clever && mci->mci_state != MCIS_CLOSED) 283738032Speter { 283890792Sgshapiro# if STARTTLS || SASL 283990792Sgshapiro int dotpos; 284090792Sgshapiro char *srvname; 284190792Sgshapiro extern SOCKADDR CurHostAddr; 284290792Sgshapiro# endif /* STARTTLS || SASL */ 284390792Sgshapiro 284490792Sgshapiro# if SASL 284571345Sgshapiro# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 284690792Sgshapiro# endif /* SASL */ 284764562Sgshapiro# if STARTTLS 284871345Sgshapiro# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 284964562Sgshapiro# endif /* STARTTLS */ 285071345Sgshapiro# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 285171345Sgshapiro# define SET_HELO(f) f |= MCIF_ONLY_EHLO 285271345Sgshapiro# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 285338032Speter 285490792Sgshapiro# if STARTTLS || SASL 285590792Sgshapiro /* don't use CurHostName, it is changed in many places */ 285690792Sgshapiro if (mci->mci_host != NULL) 285790792Sgshapiro { 285890792Sgshapiro srvname = mci->mci_host; 285990792Sgshapiro dotpos = strlen(srvname) - 1; 286090792Sgshapiro if (dotpos >= 0) 286190792Sgshapiro { 286290792Sgshapiro if (srvname[dotpos] == '.') 286390792Sgshapiro srvname[dotpos] = '\0'; 286490792Sgshapiro else 286590792Sgshapiro dotpos = -1; 286690792Sgshapiro } 286790792Sgshapiro } 286890792Sgshapiro else if (mci->mci_mailer != NULL) 286990792Sgshapiro { 287090792Sgshapiro srvname = mci->mci_mailer->m_name; 287190792Sgshapiro dotpos = -1; 287290792Sgshapiro } 287390792Sgshapiro else 287490792Sgshapiro { 287590792Sgshapiro srvname = "local"; 287690792Sgshapiro dotpos = -1; 287790792Sgshapiro } 287871345Sgshapiro 287990792Sgshapiro /* don't set {server_name} to NULL or "": see getauth() */ 288090792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 288190792Sgshapiro srvname); 288264562Sgshapiro 288390792Sgshapiro /* CurHostAddr is set by makeconnection() and mci_get() */ 288490792Sgshapiro if (CurHostAddr.sa.sa_family != 0) 288590792Sgshapiro { 288690792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, 288790792Sgshapiro macid("{server_addr}"), 288890792Sgshapiro anynet_ntoa(&CurHostAddr)); 288990792Sgshapiro } 289090792Sgshapiro else if (mci->mci_mailer != NULL) 289190792Sgshapiro { 289290792Sgshapiro /* mailer name is unique, use it as address */ 289390792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 289490792Sgshapiro macid("{server_addr}"), 289590792Sgshapiro mci->mci_mailer->m_name); 289690792Sgshapiro } 289790792Sgshapiro else 289890792Sgshapiro { 289990792Sgshapiro /* don't set it to NULL or "": see getauth() */ 290090792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 290190792Sgshapiro macid("{server_addr}"), "0"); 290290792Sgshapiro } 290390792Sgshapiro 290490792Sgshapiro /* undo change of srvname (mci->mci_host) */ 290590792Sgshapiro if (dotpos >= 0) 290690792Sgshapiro srvname[dotpos] = '.'; 290790792Sgshapiro 290890792Sgshapiroreconnect: /* after switching to an encrypted connection */ 290990792Sgshapiro# endif /* STARTTLS || SASL */ 291090792Sgshapiro 291190792Sgshapiro /* set the current connection information */ 291290792Sgshapiro e->e_mci = mci; 291364562Sgshapiro# if SASL 291464562Sgshapiro mci->mci_saslcap = NULL; 291564562Sgshapiro# endif /* SASL */ 291671345Sgshapiro smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 291771345Sgshapiro CLR_HELO(mci->mci_flags); 291864562Sgshapiro 291990792Sgshapiro if (IS_DLVR_RETURN(e)) 292090792Sgshapiro { 292190792Sgshapiro /* 292290792Sgshapiro ** Check whether other side can deliver e-mail 292390792Sgshapiro ** fast enough 292490792Sgshapiro */ 292590792Sgshapiro 292690792Sgshapiro if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 292790792Sgshapiro { 292890792Sgshapiro e->e_status = "5.4.7"; 292990792Sgshapiro usrerrenh(e->e_status, 293090792Sgshapiro "554 Server does not support Deliver By"); 293190792Sgshapiro rcode = EX_UNAVAILABLE; 293290792Sgshapiro goto give_up; 293390792Sgshapiro } 293490792Sgshapiro if (e->e_deliver_by > 0 && 293590792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime) < 293690792Sgshapiro mci->mci_min_by) 293790792Sgshapiro { 293890792Sgshapiro e->e_status = "5.4.7"; 293990792Sgshapiro usrerrenh(e->e_status, 294090792Sgshapiro "554 Message can't be delivered in time; %ld < %ld", 294190792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime), 294290792Sgshapiro mci->mci_min_by); 294390792Sgshapiro rcode = EX_UNAVAILABLE; 294490792Sgshapiro goto give_up; 294590792Sgshapiro } 294690792Sgshapiro } 294790792Sgshapiro 294864562Sgshapiro# if STARTTLS 294964562Sgshapiro /* first TLS then AUTH to provide a security layer */ 295071345Sgshapiro if (mci->mci_state != MCIS_CLOSED && 295171345Sgshapiro !DONE_STARTTLS(mci->mci_flags)) 295264562Sgshapiro { 295364562Sgshapiro int olderrors; 295464562Sgshapiro bool usetls; 295564562Sgshapiro bool saveQuickAbort = QuickAbort; 295664562Sgshapiro bool saveSuprErrs = SuprErrs; 295771345Sgshapiro char *host = NULL; 295864562Sgshapiro 295964562Sgshapiro rcode = EX_OK; 296064562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 296190792Sgshapiro if (usetls) 296290792Sgshapiro usetls = !iscltflgset(e, D_NOTLS); 296366494Sgshapiro 2964168515Sgshapiro host = macvalue(macid("{server_name}"), e); 296564562Sgshapiro if (usetls) 296664562Sgshapiro { 296764562Sgshapiro olderrors = Errors; 296890792Sgshapiro QuickAbort = false; 296990792Sgshapiro SuprErrs = true; 2970102528Sgshapiro if (rscheck("try_tls", host, NULL, e, 2971285303Sgshapiro RSF_RMCOMM, 7, host, NOQID, NULL, 2972285303Sgshapiro NULL) != EX_OK 297364562Sgshapiro || Errors > olderrors) 2974168515Sgshapiro { 297590792Sgshapiro usetls = false; 2976168515Sgshapiro } 297764562Sgshapiro SuprErrs = saveSuprErrs; 297864562Sgshapiro QuickAbort = saveQuickAbort; 297964562Sgshapiro } 298064562Sgshapiro 298164562Sgshapiro if (usetls) 298264562Sgshapiro { 298364562Sgshapiro if ((rcode = starttls(m, mci, e)) == EX_OK) 298464562Sgshapiro { 298564562Sgshapiro /* start again without STARTTLS */ 298664562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 298764562Sgshapiro } 298864562Sgshapiro else 298964562Sgshapiro { 299064562Sgshapiro char *s; 299164562Sgshapiro 299264562Sgshapiro /* 2993203004Sgshapiro ** TLS negotiation failed, what to do? 299464562Sgshapiro ** fall back to unencrypted connection 299564562Sgshapiro ** or abort? How to decide? 299664562Sgshapiro ** set a macro and call a ruleset. 299764562Sgshapiro */ 299890792Sgshapiro 299964562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 300064562Sgshapiro switch (rcode) 300164562Sgshapiro { 300264562Sgshapiro case EX_TEMPFAIL: 300364562Sgshapiro s = "TEMP"; 300464562Sgshapiro break; 300564562Sgshapiro case EX_USAGE: 300664562Sgshapiro s = "USAGE"; 300764562Sgshapiro break; 300864562Sgshapiro case EX_PROTOCOL: 300964562Sgshapiro s = "PROTOCOL"; 301064562Sgshapiro break; 301164562Sgshapiro case EX_SOFTWARE: 301264562Sgshapiro s = "SOFTWARE"; 301364562Sgshapiro break; 3014157001Sgshapiro case EX_UNAVAILABLE: 3015157001Sgshapiro s = "NONE"; 3016157001Sgshapiro break; 301764562Sgshapiro 301864562Sgshapiro /* everything else is a failure */ 301964562Sgshapiro default: 302064562Sgshapiro s = "FAILURE"; 302164562Sgshapiro rcode = EX_TEMPFAIL; 302264562Sgshapiro } 302390792Sgshapiro macdefine(&e->e_macro, A_PERM, 302490792Sgshapiro macid("{verify}"), s); 302564562Sgshapiro } 302664562Sgshapiro } 302764562Sgshapiro else 302890792Sgshapiro macdefine(&e->e_macro, A_PERM, 302990792Sgshapiro macid("{verify}"), "NONE"); 303064562Sgshapiro olderrors = Errors; 303190792Sgshapiro QuickAbort = false; 303290792Sgshapiro SuprErrs = true; 303364562Sgshapiro 303464562Sgshapiro /* 303564562Sgshapiro ** rcode == EX_SOFTWARE is special: 3036203004Sgshapiro ** the TLS negotiation failed 303764562Sgshapiro ** we have to drop the connection no matter what 303864562Sgshapiro ** However, we call tls_server to give it the chance 303964562Sgshapiro ** to log the problem and return an appropriate 304064562Sgshapiro ** error code. 304164562Sgshapiro */ 304290792Sgshapiro 304364562Sgshapiro if (rscheck("tls_server", 304490792Sgshapiro macvalue(macid("{verify}"), e), 3045102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3046285303Sgshapiro host, NOQID, NULL, NULL) != EX_OK || 304764562Sgshapiro Errors > olderrors || 304864562Sgshapiro rcode == EX_SOFTWARE) 304964562Sgshapiro { 305064562Sgshapiro char enhsc[ENHSCLEN]; 305164562Sgshapiro extern char MsgBuf[]; 305264562Sgshapiro 305364562Sgshapiro if (ISSMTPCODE(MsgBuf) && 305464562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 305564562Sgshapiro { 305690792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 305790792Sgshapiro MsgBuf); 305864562Sgshapiro } 305964562Sgshapiro else 306064562Sgshapiro { 306164562Sgshapiro p = "403 4.7.0 server not authenticated."; 306290792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 3063168515Sgshapiro sizeof(enhsc)); 306464562Sgshapiro } 306564562Sgshapiro SuprErrs = saveSuprErrs; 306664562Sgshapiro QuickAbort = saveQuickAbort; 306764562Sgshapiro 306864562Sgshapiro if (rcode == EX_SOFTWARE) 306964562Sgshapiro { 307064562Sgshapiro /* drop the connection */ 307164562Sgshapiro mci->mci_state = MCIS_QUITING; 307264562Sgshapiro if (mci->mci_in != NULL) 307364562Sgshapiro { 307490792Sgshapiro (void) sm_io_close(mci->mci_in, 307590792Sgshapiro SM_TIME_DEFAULT); 307664562Sgshapiro mci->mci_in = NULL; 307764562Sgshapiro } 307864562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 307964562Sgshapiro (void) endmailer(mci, e, pv); 308064562Sgshapiro } 308164562Sgshapiro else 308264562Sgshapiro { 308364562Sgshapiro /* abort transfer */ 308464562Sgshapiro smtpquit(m, mci, e); 308564562Sgshapiro } 308664562Sgshapiro 308771345Sgshapiro /* avoid bogus error msg */ 308871345Sgshapiro mci->mci_errno = 0; 308971345Sgshapiro 309064562Sgshapiro /* temp or permanent failure? */ 309164562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 309264562Sgshapiro : EX_UNAVAILABLE; 309390792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 309464562Sgshapiro 309564562Sgshapiro /* 309664562Sgshapiro ** hack to get the error message into 309764562Sgshapiro ** the envelope (done in giveresponse()) 309864562Sgshapiro */ 309990792Sgshapiro 310090792Sgshapiro (void) sm_strlcpy(SmtpError, p, 3101168515Sgshapiro sizeof(SmtpError)); 310264562Sgshapiro } 3103168515Sgshapiro else if (mci->mci_state == MCIS_CLOSED) 3104168515Sgshapiro { 3105168515Sgshapiro /* connection close caused by 421 */ 3106168515Sgshapiro mci->mci_errno = 0; 3107168515Sgshapiro rcode = EX_TEMPFAIL; 3108168515Sgshapiro mci_setstat(mci, rcode, NULL, "421"); 3109168515Sgshapiro } 3110168515Sgshapiro else 3111168515Sgshapiro rcode = 0; 3112168515Sgshapiro 311364562Sgshapiro QuickAbort = saveQuickAbort; 311464562Sgshapiro SuprErrs = saveSuprErrs; 311571345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 311671345Sgshapiro mci->mci_state != MCIS_CLOSED) 311764562Sgshapiro { 311871345Sgshapiro SET_HELO(mci->mci_flags); 3119223067Sgshapiro mci_clr_extensions(mci); 312064562Sgshapiro goto reconnect; 312164562Sgshapiro } 312264562Sgshapiro } 312364562Sgshapiro# endif /* STARTTLS */ 312464562Sgshapiro# if SASL 312564562Sgshapiro /* if other server supports authentication let's authenticate */ 312664562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 312764562Sgshapiro mci->mci_saslcap != NULL && 312890792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 312964562Sgshapiro { 313090792Sgshapiro /* Should we require some minimum authentication? */ 313190792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 313264562Sgshapiro { 313364562Sgshapiro int result; 313490792Sgshapiro sasl_ssf_t *ssf = NULL; 313564562Sgshapiro 313690792Sgshapiro /* Get security strength (features) */ 313764562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 313898121Sgshapiro# if SASL >= 20000 313998121Sgshapiro (const void **) &ssf); 314098121Sgshapiro# else /* SASL >= 20000 */ 314164562Sgshapiro (void **) &ssf); 314298121Sgshapiro# endif /* SASL >= 20000 */ 314390792Sgshapiro 314490792Sgshapiro /* XXX authid? */ 314564562Sgshapiro if (LogLevel > 9) 314664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 314790792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 314864562Sgshapiro mci->mci_host, 314990792Sgshapiro macvalue(macid("{auth_type}"), e), 315090792Sgshapiro result == SASL_OK ? *ssf : 0); 315177349Sgshapiro 315264562Sgshapiro /* 315390792Sgshapiro ** Only switch to encrypted connection 315464562Sgshapiro ** if a security layer has been negotiated 315564562Sgshapiro */ 315690792Sgshapiro 315764562Sgshapiro if (result == SASL_OK && *ssf > 0) 315864562Sgshapiro { 3159159609Sgshapiro int tmo; 3160159609Sgshapiro 316164562Sgshapiro /* 316290792Sgshapiro ** Convert I/O layer to use SASL. 316390792Sgshapiro ** If the call fails, the connection 316490792Sgshapiro ** is aborted. 316564562Sgshapiro */ 316690792Sgshapiro 3167159609Sgshapiro tmo = DATA_PROGRESS_TIMEOUT * 1000; 316890792Sgshapiro if (sfdcsasl(&mci->mci_in, 316990792Sgshapiro &mci->mci_out, 3170159609Sgshapiro mci->mci_conn, tmo) == 0) 317164562Sgshapiro { 3172223067Sgshapiro mci_clr_extensions(mci); 317390792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 317490792Sgshapiro MCIF_ONLY_EHLO; 317564562Sgshapiro goto reconnect; 317664562Sgshapiro } 317790792Sgshapiro syserr("AUTH TLS switch failed in client"); 317864562Sgshapiro } 317964562Sgshapiro /* else? XXX */ 318064562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 318164562Sgshapiro 318264562Sgshapiro } 318390792Sgshapiro else if (ret == EX_TEMPFAIL) 318490792Sgshapiro { 318590792Sgshapiro if (LogLevel > 8) 318690792Sgshapiro sm_syslog(LOG_ERR, NOQID, 318790792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 318890792Sgshapiro mci->mci_host); 318990792Sgshapiro smtpquit(m, mci, e); 319090792Sgshapiro 319190792Sgshapiro /* avoid bogus error msg */ 319290792Sgshapiro mci->mci_errno = 0; 319390792Sgshapiro rcode = EX_TEMPFAIL; 3194132943Sgshapiro mci_setstat(mci, rcode, "4.3.0", p); 319590792Sgshapiro 319690792Sgshapiro /* 319790792Sgshapiro ** hack to get the error message into 319890792Sgshapiro ** the envelope (done in giveresponse()) 319990792Sgshapiro */ 320090792Sgshapiro 320190792Sgshapiro (void) sm_strlcpy(SmtpError, 320290792Sgshapiro "Temporary AUTH failure", 3203168515Sgshapiro sizeof(SmtpError)); 320490792Sgshapiro } 320564562Sgshapiro } 320664562Sgshapiro# endif /* SASL */ 320738032Speter } 320838032Speter 320964562Sgshapiro 321038032Speterdo_transfer: 321138032Speter /* clear out per-message flags from connection structure */ 321238032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 321338032Speter 321438032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 321538032Speter !bitset(EF_DONT_MIME, e->e_flags) && 321638032Speter bitnset(M_7BITS, m->m_flags)) 321738032Speter mci->mci_flags |= MCIF_CVT8TO7; 321838032Speter 321938032Speter#if MIME7TO8 322038032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 322138032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 322238032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 322390792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 322490792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 322538032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 322638032Speter { 322738032Speter /* may want to convert 7 -> 8 */ 322838032Speter /* XXX should really parse it here -- and use a class XXX */ 322990792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 323038032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 323138032Speter mci->mci_flags |= MCIF_CVT7TO8; 323238032Speter } 323364562Sgshapiro#endif /* MIME7TO8 */ 323438032Speter 323538032Speter if (tTd(11, 1)) 323638032Speter { 323790792Sgshapiro sm_dprintf("openmailer: "); 3238132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 323938032Speter } 324038032Speter 324190792Sgshapiro#if _FFR_CLIENT_SIZE 324290792Sgshapiro /* 324390792Sgshapiro ** See if we know the maximum size and 324490792Sgshapiro ** abort if the message is too big. 324590792Sgshapiro ** 324690792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 324790792Sgshapiro */ 324890792Sgshapiro 324990792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 325090792Sgshapiro mci->mci_maxsize > 0 && 325190792Sgshapiro e->e_msgsize > mci->mci_maxsize) 325290792Sgshapiro { 325390792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 325490792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 325590792Sgshapiro e->e_status = "5.2.3"; 325690792Sgshapiro else 325790792Sgshapiro e->e_status = "5.3.4"; 325890792Sgshapiro 325990792Sgshapiro usrerrenh(e->e_status, 326090792Sgshapiro "552 Message is too large; %ld bytes max", 326190792Sgshapiro mci->mci_maxsize); 326290792Sgshapiro rcode = EX_DATAERR; 326390792Sgshapiro 326490792Sgshapiro /* Need an e_message for error */ 3265168515Sgshapiro (void) sm_snprintf(SmtpError, sizeof(SmtpError), 326690792Sgshapiro "Message is too large; %ld bytes max", 326790792Sgshapiro mci->mci_maxsize); 326890792Sgshapiro goto give_up; 326990792Sgshapiro } 327090792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 327190792Sgshapiro 327238032Speter if (mci->mci_state != MCIS_OPEN) 327338032Speter { 327438032Speter /* couldn't open the mailer */ 327538032Speter rcode = mci->mci_exitstat; 327638032Speter errno = mci->mci_errno; 327773188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 327838032Speter if (rcode == EX_OK) 327938032Speter { 328038032Speter /* shouldn't happen */ 328164562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 328290792Sgshapiro (unsigned long) mci, rcode, errno, 328390792Sgshapiro mci->mci_state, firstsig); 3284132943Sgshapiro mci_dump_all(smioout, true); 328538032Speter rcode = EX_SOFTWARE; 328638032Speter } 328764562Sgshapiro else if (nummxhosts > hostnum) 328838032Speter { 328938032Speter /* try next MX site */ 329038032Speter goto tryhost; 329138032Speter } 329238032Speter } 329338032Speter else if (!clever) 329438032Speter { 3295157001Sgshapiro bool ok; 3296157001Sgshapiro 329738032Speter /* 329838032Speter ** Format and send message. 329938032Speter */ 330038032Speter 3301157001Sgshapiro rcode = EX_OK; 3302157001Sgshapiro errno = 0; 3303157001Sgshapiro ok = putfromline(mci, e); 3304157001Sgshapiro if (ok) 3305157001Sgshapiro ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 3306157001Sgshapiro if (ok) 3307157001Sgshapiro ok = (*e->e_putbody)(mci, e, NULL); 3308173340Sgshapiro if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags)) 3309173340Sgshapiro ok = putline("", mci); 331038032Speter 3311157001Sgshapiro /* 3312157001Sgshapiro ** Ignore an I/O error that was caused by EPIPE. 3313157001Sgshapiro ** Some broken mailers don't read the entire body 3314157001Sgshapiro ** but just exit() thus causing an I/O error. 3315157001Sgshapiro */ 3316157001Sgshapiro 3317157001Sgshapiro if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE)) 3318157001Sgshapiro ok = true; 3319157001Sgshapiro 3320157001Sgshapiro /* (always) get the exit status */ 332138032Speter rcode = endmailer(mci, e, pv); 3322157001Sgshapiro if (!ok) 3323157001Sgshapiro rcode = EX_TEMPFAIL; 332490792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 332573188Sgshapiro { 332673188Sgshapiro /* 332773188Sgshapiro ** Need an e_message for mailq display. 332873188Sgshapiro ** We set SmtpError as 332973188Sgshapiro */ 333073188Sgshapiro 3331168515Sgshapiro (void) sm_snprintf(SmtpError, sizeof(SmtpError), 333290792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 333390792Sgshapiro m->m_name, m->m_mailer); 333473188Sgshapiro } 333538032Speter } 333638032Speter else 333738032Speter { 333838032Speter /* 333938032Speter ** Send the MAIL FROM: protocol 334038032Speter */ 334138032Speter 334290792Sgshapiro /* XXX this isn't pipelined... */ 334338032Speter rcode = smtpmailfrom(m, mci, e); 334438032Speter if (rcode == EX_OK) 334538032Speter { 334638032Speter register int i; 334790792Sgshapiro# if PIPELINING 334890792Sgshapiro ADDRESS *volatile pchain; 334990792Sgshapiro# endif /* PIPELINING */ 335038032Speter 335138032Speter /* send the recipient list */ 335238032Speter tobuf[0] = '\0'; 335390792Sgshapiro mci->mci_retryrcpt = false; 335490792Sgshapiro mci->mci_tolist = tobuf; 335590792Sgshapiro# if PIPELINING 335690792Sgshapiro pchain = NULL; 335790792Sgshapiro mci->mci_nextaddr = NULL; 335890792Sgshapiro# endif /* PIPELINING */ 335964562Sgshapiro 336038032Speter for (to = tochain; to != NULL; to = to->q_tchain) 336138032Speter { 336290792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 336338032Speter continue; 336464562Sgshapiro 336590792Sgshapiro /* mark recipient state as "ok so far" */ 336690792Sgshapiro to->q_state = QS_OK; 336790792Sgshapiro e->e_to = to->q_paddr; 336864562Sgshapiro# if STARTTLS 336964562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3370102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3371285303Sgshapiro mci->mci_host, e->e_id, NULL, NULL); 337264562Sgshapiro if (i != EX_OK) 337338032Speter { 337490792Sgshapiro markfailure(e, to, mci, i, false); 337590792Sgshapiro giveresponse(i, to->q_status, m, mci, 337690792Sgshapiro ctladdr, xstart, e, to); 337790792Sgshapiro if (i == EX_TEMPFAIL) 337890792Sgshapiro { 337990792Sgshapiro mci->mci_retryrcpt = true; 338090792Sgshapiro to->q_state = QS_RETRY; 338190792Sgshapiro } 338264562Sgshapiro continue; 338338032Speter } 338464562Sgshapiro# endif /* STARTTLS */ 338564562Sgshapiro 338690792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 338790792Sgshapiro# if PIPELINING 338890792Sgshapiro if (i == EX_OK && 338990792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 339064562Sgshapiro { 339190792Sgshapiro /* 339290792Sgshapiro ** Add new element to list of 339390792Sgshapiro ** recipients for pipelining. 339490792Sgshapiro */ 339590792Sgshapiro 339690792Sgshapiro to->q_pchain = NULL; 339790792Sgshapiro if (mci->mci_nextaddr == NULL) 339890792Sgshapiro mci->mci_nextaddr = to; 339990792Sgshapiro if (pchain == NULL) 340090792Sgshapiro pchain = to; 340190792Sgshapiro else 340290792Sgshapiro { 340390792Sgshapiro pchain->q_pchain = to; 340490792Sgshapiro pchain = pchain->q_pchain; 340590792Sgshapiro } 340664562Sgshapiro } 340790792Sgshapiro# endif /* PIPELINING */ 340890792Sgshapiro if (i != EX_OK) 340938032Speter { 341090792Sgshapiro markfailure(e, to, mci, i, false); 341198841Sgshapiro giveresponse(i, to->q_status, m, mci, 341290792Sgshapiro ctladdr, xstart, e, to); 341390792Sgshapiro if (i == EX_TEMPFAIL) 341490792Sgshapiro to->q_state = QS_RETRY; 341538032Speter } 341638032Speter } 341738032Speter 341890792Sgshapiro /* No recipients in list and no missing responses? */ 341990792Sgshapiro if (tobuf[0] == '\0' 342090792Sgshapiro# if PIPELINING 3421173340Sgshapiro && bitset(MCIF_PIPELINED, mci->mci_flags) 342290792Sgshapiro && mci->mci_nextaddr == NULL 342390792Sgshapiro# endif /* PIPELINING */ 342490792Sgshapiro ) 342538032Speter { 342638032Speter rcode = EX_OK; 342738032Speter e->e_to = NULL; 342838032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 342938032Speter smtprset(m, mci, e); 343038032Speter } 343138032Speter else 343238032Speter { 343338032Speter e->e_to = tobuf + 1; 343490792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 343538032Speter } 343638032Speter } 343764562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 343838032Speter { 343938032Speter /* try next MX site */ 344038032Speter goto tryhost; 344138032Speter } 344238032Speter } 344338032Speter#if NAMED_BIND 344438032Speter if (ConfigLevel < 2) 344538032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 344664562Sgshapiro#endif /* NAMED_BIND */ 344738032Speter 344838032Speter if (tTd(62, 1)) 344938032Speter checkfds("after delivery"); 345038032Speter 345138032Speter /* 345238032Speter ** Do final status disposal. 345338032Speter ** We check for something in tobuf for the SMTP case. 345438032Speter ** If we got a temporary failure, arrange to queue the 345538032Speter ** addressees. 345638032Speter */ 345738032Speter 345838032Speter give_up: 345938032Speter if (bitnset(M_LMTP, m->m_flags)) 346038032Speter { 346138032Speter lmtp_rcode = rcode; 346238032Speter tobuf[0] = '\0'; 346390792Sgshapiro anyok = false; 346490792Sgshapiro strsize = 0; 346538032Speter } 346638032Speter else 346738032Speter anyok = rcode == EX_OK; 346838032Speter 346938032Speter for (to = tochain; to != NULL; to = to->q_tchain) 347038032Speter { 347138032Speter /* see if address already marked */ 347264562Sgshapiro if (!QS_IS_OK(to->q_state)) 347338032Speter continue; 347438032Speter 347538032Speter /* if running LMTP, get the status for each address */ 347638032Speter if (bitnset(M_LMTP, m->m_flags)) 347738032Speter { 347838032Speter if (lmtp_rcode == EX_OK) 347938032Speter rcode = smtpgetstat(m, mci, e); 348038032Speter if (rcode == EX_OK) 348138032Speter { 348290792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 348390792Sgshapiro to->q_paddr, 348490792Sgshapiro tobufsize - strsize); 348590792Sgshapiro SM_ASSERT(strsize < tobufsize); 348690792Sgshapiro anyok = true; 348738032Speter } 348838032Speter else 348938032Speter { 349038032Speter e->e_to = to->q_paddr; 349190792Sgshapiro markfailure(e, to, mci, rcode, true); 349264562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 349390792Sgshapiro ctladdr, xstart, e, to); 349438032Speter e->e_to = tobuf + 1; 349538032Speter continue; 349638032Speter } 349738032Speter } 349838032Speter else 349938032Speter { 350038032Speter /* mark bad addresses */ 350138032Speter if (rcode != EX_OK) 350238032Speter { 350338032Speter if (goodmxfound && rcode == EX_NOHOST) 350438032Speter rcode = EX_TEMPFAIL; 350590792Sgshapiro markfailure(e, to, mci, rcode, true); 350638032Speter continue; 350738032Speter } 350838032Speter } 350938032Speter 351038032Speter /* successful delivery */ 351164562Sgshapiro to->q_state = QS_SENT; 351238032Speter to->q_statdate = curtime(); 351338032Speter e->e_nsent++; 351464562Sgshapiro 351564562Sgshapiro /* 351664562Sgshapiro ** Checkpoint the send list every few addresses 351764562Sgshapiro */ 351864562Sgshapiro 351966494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 352064562Sgshapiro { 352190792Sgshapiro queueup(e, false, false); 352264562Sgshapiro e->e_nsent = 0; 352364562Sgshapiro } 352464562Sgshapiro 352538032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 352638032Speter bitset(QPINGONSUCCESS, to->q_flags)) 352738032Speter { 352838032Speter to->q_flags |= QDELIVERED; 352938032Speter to->q_status = "2.1.5"; 353090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 353190792Sgshapiro "%s... Successfully delivered\n", 353290792Sgshapiro to->q_paddr); 353338032Speter } 353438032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 353538032Speter bitset(QPRIMARY, to->q_flags) && 353638032Speter !bitset(MCIF_DSN, mci->mci_flags)) 353738032Speter { 353838032Speter to->q_flags |= QRELAYED; 353990792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 354090792Sgshapiro "%s... relayed; expect no further notifications\n", 354190792Sgshapiro to->q_paddr); 354238032Speter } 354390792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 354490792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 354590792Sgshapiro bitset(QPRIMARY, to->q_flags) && 354690792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 354790792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 354890792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 354990792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 355090792Sgshapiro { 355190792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 355290792Sgshapiro to->q_flags |= QBYNRELAY; 355390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 355490792Sgshapiro "%s... Deliver-by notify: relayed\n", 355590792Sgshapiro to->q_paddr); 355690792Sgshapiro } 355790792Sgshapiro else if (IS_DLVR_TRACE(e) && 355890792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 355990792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 356090792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 356190792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 356290792Sgshapiro bitset(QPRIMARY, to->q_flags)) 356390792Sgshapiro { 356490792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 356590792Sgshapiro to->q_flags |= QBYTRACE; 356690792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 356790792Sgshapiro "%s... Deliver-By trace: relayed\n", 356890792Sgshapiro to->q_paddr); 356990792Sgshapiro } 357038032Speter } 357138032Speter 357238032Speter if (bitnset(M_LMTP, m->m_flags)) 357338032Speter { 357438032Speter /* 357538032Speter ** Global information applies to the last recipient only; 357638032Speter ** clear it out to avoid bogus errors. 357738032Speter */ 357838032Speter 357938032Speter rcode = EX_OK; 358038032Speter e->e_statmsg = NULL; 358138032Speter 358238032Speter /* reset the mci state for the next transaction */ 358390792Sgshapiro if (mci != NULL && 358490792Sgshapiro (mci->mci_state == MCIS_MAIL || 358590792Sgshapiro mci->mci_state == MCIS_RCPT || 358690792Sgshapiro mci->mci_state == MCIS_DATA)) 3587125820Sgshapiro { 358838032Speter mci->mci_state = MCIS_OPEN; 3589125820Sgshapiro SmtpPhase = mci->mci_phase = "idle"; 3590125820Sgshapiro sm_setproctitle(true, e, "%s: %s", CurHostName, 3591125820Sgshapiro mci->mci_phase); 3592125820Sgshapiro } 359338032Speter } 359438032Speter 359538032Speter if (tobuf[0] != '\0') 359690792Sgshapiro { 3597285303Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, NULL); 359890792Sgshapiro#if 0 359990792Sgshapiro /* 360090792Sgshapiro ** This code is disabled for now because I am not 360190792Sgshapiro ** sure that copying status from the first recipient 360290792Sgshapiro ** to all non-status'ed recipients is a good idea. 360390792Sgshapiro */ 360490792Sgshapiro 360590792Sgshapiro if (tochain->q_message != NULL && 360690792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 360790792Sgshapiro { 360890792Sgshapiro for (to = tochain->q_tchain; to != NULL; 360990792Sgshapiro to = to->q_tchain) 361090792Sgshapiro { 361190792Sgshapiro /* see if address already marked */ 361290792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 361390792Sgshapiro to->q_message == NULL) 361490792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 361590792Sgshapiro tochain->q_message); 361690792Sgshapiro } 361790792Sgshapiro } 361890792Sgshapiro#endif /* 0 */ 361990792Sgshapiro } 362038032Speter if (anyok) 362190792Sgshapiro markstats(e, tochain, STATS_NORMAL); 362238032Speter mci_store_persistent(mci); 362338032Speter 362490792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 362590792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 362690792Sgshapiro { 362790792Sgshapiro /* try next MX site */ 362890792Sgshapiro goto tryhost; 362990792Sgshapiro } 363090792Sgshapiro 363138032Speter /* now close the connection */ 363238032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 363338032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 363438032Speter smtpquit(m, mci, e); 363538032Speter 363690792Sgshapirocleanup: ; 363790792Sgshapiro } 363890792Sgshapiro SM_FINALLY 363990792Sgshapiro { 364090792Sgshapiro /* 364190792Sgshapiro ** Restore state and return. 364290792Sgshapiro */ 364338032Speter#if XDEBUG 364438032Speter char wbuf[MAXLINE]; 364538032Speter 364638032Speter /* make absolutely certain 0, 1, and 2 are in use */ 3647168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), 364890792Sgshapiro "%s... end of deliver(%s)", 364990792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 365090792Sgshapiro : shortenstring(e->e_to, 365190792Sgshapiro MAXSHORTSTR), 365290792Sgshapiro m->m_name); 365338032Speter checkfd012(wbuf); 365464562Sgshapiro#endif /* XDEBUG */ 365538032Speter 365690792Sgshapiro errno = 0; 365790792Sgshapiro 365890792Sgshapiro /* 365990792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 366090792Sgshapiro ** because it previously pointed to an auto buffer. 366190792Sgshapiro ** We don't do this any more, so this may be unnecessary. 366290792Sgshapiro */ 366390792Sgshapiro 366490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 366590792Sgshapiro e->e_to = NULL; 366690792Sgshapiro } 366790792Sgshapiro SM_END_TRY 366864562Sgshapiro return rcode; 366938032Speter} 367064562Sgshapiro 367190792Sgshapiro/* 367238032Speter** MARKFAILURE -- mark a failure on a specific address. 367338032Speter** 367438032Speter** Parameters: 367538032Speter** e -- the envelope we are sending. 367638032Speter** q -- the address to mark. 367738032Speter** mci -- mailer connection information. 367838032Speter** rcode -- the code signifying the particular failure. 367964562Sgshapiro** ovr -- override an existing code? 368038032Speter** 368138032Speter** Returns: 368238032Speter** none. 368338032Speter** 368438032Speter** Side Effects: 368538032Speter** marks the address (and possibly the envelope) with the 368638032Speter** failure so that an error will be returned or 368738032Speter** the message will be queued, as appropriate. 368838032Speter*/ 368938032Speter 369090792Sgshapirovoid 369164562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 369238032Speter register ENVELOPE *e; 369338032Speter register ADDRESS *q; 369438032Speter register MCI *mci; 369538032Speter int rcode; 369664562Sgshapiro bool ovr; 369738032Speter{ 369890792Sgshapiro int save_errno = errno; 369964562Sgshapiro char *status = NULL; 370064562Sgshapiro char *rstatus = NULL; 370138032Speter 370238032Speter switch (rcode) 370338032Speter { 370438032Speter case EX_OK: 370538032Speter break; 370638032Speter 370738032Speter case EX_TEMPFAIL: 370838032Speter case EX_IOERR: 370938032Speter case EX_OSERR: 371064562Sgshapiro q->q_state = QS_QUEUEUP; 371138032Speter break; 371238032Speter 371338032Speter default: 371464562Sgshapiro q->q_state = QS_BADADDR; 371538032Speter break; 371638032Speter } 371738032Speter 371838032Speter /* find most specific error code possible */ 371938032Speter if (mci != NULL && mci->mci_status != NULL) 372038032Speter { 372190792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 372238032Speter if (mci->mci_rstatus != NULL) 372390792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 372490792Sgshapiro mci->mci_rstatus); 372538032Speter else 372664562Sgshapiro rstatus = NULL; 372738032Speter } 372838032Speter else if (e->e_status != NULL) 372938032Speter { 373064562Sgshapiro status = e->e_status; 373164562Sgshapiro rstatus = NULL; 373238032Speter } 373338032Speter else 373438032Speter { 373538032Speter switch (rcode) 373638032Speter { 373738032Speter case EX_USAGE: 373864562Sgshapiro status = "5.5.4"; 373938032Speter break; 374038032Speter 374138032Speter case EX_DATAERR: 374264562Sgshapiro status = "5.5.2"; 374338032Speter break; 374438032Speter 374538032Speter case EX_NOUSER: 374664562Sgshapiro status = "5.1.1"; 374738032Speter break; 374838032Speter 374938032Speter case EX_NOHOST: 375064562Sgshapiro status = "5.1.2"; 375138032Speter break; 375238032Speter 375338032Speter case EX_NOINPUT: 375438032Speter case EX_CANTCREAT: 375538032Speter case EX_NOPERM: 375664562Sgshapiro status = "5.3.0"; 375738032Speter break; 375838032Speter 375938032Speter case EX_UNAVAILABLE: 376038032Speter case EX_SOFTWARE: 376138032Speter case EX_OSFILE: 376238032Speter case EX_PROTOCOL: 376338032Speter case EX_CONFIG: 376464562Sgshapiro status = "5.5.0"; 376538032Speter break; 376638032Speter 376738032Speter case EX_OSERR: 376838032Speter case EX_IOERR: 376964562Sgshapiro status = "4.5.0"; 377038032Speter break; 377138032Speter 377238032Speter case EX_TEMPFAIL: 377364562Sgshapiro status = "4.2.0"; 377438032Speter break; 377538032Speter } 377638032Speter } 377738032Speter 377864562Sgshapiro /* new status? */ 377964562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 378064562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 378164562Sgshapiro { 378264562Sgshapiro q->q_status = status; 378364562Sgshapiro q->q_rstatus = rstatus; 378464562Sgshapiro } 378538032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 378638032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 378790792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 378838032Speter { 378964562Sgshapiro char buf[16]; 379038032Speter 3791168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%d", rcode); 379290792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 379338032Speter } 379464562Sgshapiro 379564562Sgshapiro q->q_statdate = curtime(); 379664562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 379764562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 379890792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 379990792Sgshapiro 380090792Sgshapiro /* restore errno */ 380190792Sgshapiro errno = save_errno; 380238032Speter} 380390792Sgshapiro/* 380438032Speter** ENDMAILER -- Wait for mailer to terminate. 380538032Speter** 380638032Speter** We should never get fatal errors (e.g., segmentation 380738032Speter** violation), so we report those specially. For other 380838032Speter** errors, we choose a status message (into statmsg), 380938032Speter** and if it represents an error, we print it. 381038032Speter** 381138032Speter** Parameters: 381280785Sgshapiro** mci -- the mailer connection info. 381338032Speter** e -- the current envelope. 381438032Speter** pv -- the parameter vector that invoked the mailer 381538032Speter** (for error messages). 381638032Speter** 381738032Speter** Returns: 381838032Speter** exit code of mailer. 381938032Speter** 382038032Speter** Side Effects: 382138032Speter** none. 382238032Speter*/ 382338032Speter 382464562Sgshapirostatic jmp_buf EndWaitTimeout; 382564562Sgshapiro 382664562Sgshapirostatic void 3827141858Sgshapiroendwaittimeout(ignore) 3828141858Sgshapiro int ignore; 382964562Sgshapiro{ 383077349Sgshapiro /* 383177349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 383277349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 383377349Sgshapiro ** DOING. 383477349Sgshapiro */ 383577349Sgshapiro 383664562Sgshapiro errno = ETIMEDOUT; 383764562Sgshapiro longjmp(EndWaitTimeout, 1); 383864562Sgshapiro} 383964562Sgshapiro 384038032Speterint 384138032Speterendmailer(mci, e, pv) 384238032Speter register MCI *mci; 384338032Speter register ENVELOPE *e; 384438032Speter char **pv; 384538032Speter{ 384638032Speter int st; 384764562Sgshapiro int save_errno = errno; 384864562Sgshapiro char buf[MAXLINE]; 384990792Sgshapiro SM_EVENT *ev = NULL; 385038032Speter 385164562Sgshapiro 385238032Speter mci_unlock_host(mci); 385338032Speter 385477349Sgshapiro /* close output to mailer */ 385577349Sgshapiro if (mci->mci_out != NULL) 3856141858Sgshapiro { 385790792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 3858141858Sgshapiro mci->mci_out = NULL; 3859141858Sgshapiro } 386077349Sgshapiro 386177349Sgshapiro /* copy any remaining input to transcript */ 386277349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 386377349Sgshapiro e->e_xfp != NULL) 386477349Sgshapiro { 3865168515Sgshapiro while (sfgets(buf, sizeof(buf), mci->mci_in, 386690792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 386790792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 386877349Sgshapiro } 386977349Sgshapiro 387064562Sgshapiro#if SASL 387190792Sgshapiro /* close SASL connection */ 387264562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 387364562Sgshapiro { 387464562Sgshapiro sasl_dispose(&mci->mci_conn); 387564562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 387664562Sgshapiro } 387764562Sgshapiro#endif /* SASL */ 387864562Sgshapiro 387964562Sgshapiro#if STARTTLS 388064562Sgshapiro /* shutdown TLS */ 388164562Sgshapiro (void) endtlsclt(mci); 388264562Sgshapiro#endif /* STARTTLS */ 388364562Sgshapiro 388464562Sgshapiro /* now close the input */ 388538032Speter if (mci->mci_in != NULL) 3886141858Sgshapiro { 388790792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 3888141858Sgshapiro mci->mci_in = NULL; 3889141858Sgshapiro } 389038032Speter mci->mci_state = MCIS_CLOSED; 389138032Speter 389264562Sgshapiro errno = save_errno; 389364562Sgshapiro 389438032Speter /* in the IPC case there is nothing to wait for */ 389538032Speter if (mci->mci_pid == 0) 389664562Sgshapiro return EX_OK; 389738032Speter 389864562Sgshapiro /* put a timeout around the wait */ 389964562Sgshapiro if (mci->mci_mailer->m_wait > 0) 390064562Sgshapiro { 390164562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 390290792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 390390792Sgshapiro endwaittimeout, 0); 390464562Sgshapiro else 390564562Sgshapiro { 390666494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 390764562Sgshapiro mci->mci_mailer->m_name, 390866494Sgshapiro (long) mci->mci_mailer->m_wait); 390964562Sgshapiro return EX_TEMPFAIL; 391064562Sgshapiro } 391164562Sgshapiro } 391238032Speter 391364562Sgshapiro /* wait for the mailer process, collect status */ 391438032Speter st = waitfor(mci->mci_pid); 391564562Sgshapiro save_errno = errno; 391664562Sgshapiro if (ev != NULL) 391790792Sgshapiro sm_clrevent(ev); 391864562Sgshapiro errno = save_errno; 391964562Sgshapiro 392038032Speter if (st == -1) 392138032Speter { 392238032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 392364562Sgshapiro return EX_SOFTWARE; 392438032Speter } 392538032Speter 392638032Speter if (WIFEXITED(st)) 392738032Speter { 392838032Speter /* normal death -- return status */ 392938032Speter return (WEXITSTATUS(st)); 393038032Speter } 393138032Speter 393238032Speter /* it died a horrid death */ 393364562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 393464562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 393564562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 393664562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 393738032Speter 393838032Speter /* log the arguments */ 393938032Speter if (pv != NULL && e->e_xfp != NULL) 394038032Speter { 394138032Speter register char **av; 394238032Speter 394390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 394438032Speter for (av = pv; *av != NULL; av++) 394590792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 394690792Sgshapiro *av); 394790792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 394838032Speter } 394938032Speter 395038032Speter ExitStat = EX_TEMPFAIL; 395164562Sgshapiro return EX_TEMPFAIL; 395238032Speter} 395390792Sgshapiro/* 395438032Speter** GIVERESPONSE -- Interpret an error response from a mailer 395538032Speter** 395638032Speter** Parameters: 395764562Sgshapiro** status -- the status code from the mailer (high byte 395838032Speter** only; core dumps must have been taken care of 395938032Speter** already). 396064562Sgshapiro** dsn -- the DSN associated with the address, if any. 396138032Speter** m -- the mailer info for this mailer. 396238032Speter** mci -- the mailer connection info -- can be NULL if the 396338032Speter** response is given before the connection is made. 396438032Speter** ctladdr -- the controlling address for the recipient 396538032Speter** address(es). 396638032Speter** xstart -- the transaction start time, for computing 396738032Speter** transaction delays. 396838032Speter** e -- the current envelope. 396990792Sgshapiro** to -- the current recipient (NULL if none). 397038032Speter** 397138032Speter** Returns: 397238032Speter** none. 397338032Speter** 397438032Speter** Side Effects: 397538032Speter** Errors may be incremented. 397638032Speter** ExitStat may be set. 397738032Speter*/ 397838032Speter 397938032Spetervoid 398090792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 398164562Sgshapiro int status; 398264562Sgshapiro char *dsn; 398338032Speter register MAILER *m; 398438032Speter register MCI *mci; 398538032Speter ADDRESS *ctladdr; 398638032Speter time_t xstart; 398738032Speter ENVELOPE *e; 398890792Sgshapiro ADDRESS *to; 398938032Speter{ 399038032Speter register const char *statmsg; 399164562Sgshapiro int errnum = errno; 399264562Sgshapiro int off = 4; 399390792Sgshapiro bool usestat = false; 399464562Sgshapiro char dsnbuf[ENHSCLEN]; 399538032Speter char buf[MAXLINE]; 399690792Sgshapiro char *exmsg; 399738032Speter 399838032Speter if (e == NULL) 3999159609Sgshapiro { 400038032Speter syserr("giveresponse: null envelope"); 4001159609Sgshapiro /* NOTREACHED */ 4002159609Sgshapiro SM_ASSERT(0); 4003159609Sgshapiro } 400438032Speter 400538032Speter /* 400638032Speter ** Compute status message from code. 400738032Speter */ 400838032Speter 400990792Sgshapiro exmsg = sm_sysexmsg(status); 401064562Sgshapiro if (status == 0) 401138032Speter { 401264562Sgshapiro statmsg = "250 2.0.0 Sent"; 401338032Speter if (e->e_statmsg != NULL) 401438032Speter { 4015168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", 401690792Sgshapiro statmsg, 401790792Sgshapiro shortenstring(e->e_statmsg, 403)); 401838032Speter statmsg = buf; 401938032Speter } 402038032Speter } 402190792Sgshapiro else if (exmsg == NULL) 402238032Speter { 4023168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 402490792Sgshapiro "554 5.3.0 unknown mailer error %d", 402590792Sgshapiro status); 402664562Sgshapiro status = EX_UNAVAILABLE; 402738032Speter statmsg = buf; 402890792Sgshapiro usestat = true; 402938032Speter } 403064562Sgshapiro else if (status == EX_TEMPFAIL) 403138032Speter { 403238032Speter char *bp = buf; 403338032Speter 403490792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 403538032Speter bp += strlen(bp); 403638032Speter#if NAMED_BIND 403738032Speter if (h_errno == TRY_AGAIN) 403890792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 403938032Speter else 404064562Sgshapiro#endif /* NAMED_BIND */ 404138032Speter { 404264562Sgshapiro if (errnum != 0) 404390792Sgshapiro statmsg = sm_errstring(errnum); 404438032Speter else 404538032Speter statmsg = SmtpError; 404638032Speter } 404738032Speter if (statmsg != NULL && statmsg[0] != '\0') 404864562Sgshapiro { 404964562Sgshapiro switch (errnum) 405064562Sgshapiro { 405164562Sgshapiro#ifdef ENETDOWN 405264562Sgshapiro case ENETDOWN: /* Network is down */ 405364562Sgshapiro#endif /* ENETDOWN */ 405464562Sgshapiro#ifdef ENETUNREACH 405564562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 405664562Sgshapiro#endif /* ENETUNREACH */ 405764562Sgshapiro#ifdef ENETRESET 405864562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 405964562Sgshapiro#endif /* ENETRESET */ 406064562Sgshapiro#ifdef ECONNABORTED 406164562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 406264562Sgshapiro#endif /* ECONNABORTED */ 406364562Sgshapiro#ifdef EHOSTDOWN 406464562Sgshapiro case EHOSTDOWN: /* Host is down */ 406564562Sgshapiro#endif /* EHOSTDOWN */ 406664562Sgshapiro#ifdef EHOSTUNREACH 406764562Sgshapiro case EHOSTUNREACH: /* No route to host */ 406864562Sgshapiro#endif /* EHOSTUNREACH */ 4069141858Sgshapiro if (mci != NULL && mci->mci_host != NULL) 407064562Sgshapiro { 407190792Sgshapiro (void) sm_strlcpyn(bp, 407290792Sgshapiro SPACELEFT(buf, bp), 407390792Sgshapiro 2, ": ", 407490792Sgshapiro mci->mci_host); 407564562Sgshapiro bp += strlen(bp); 407664562Sgshapiro } 407764562Sgshapiro break; 407864562Sgshapiro } 407990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 408090792Sgshapiro statmsg); 408190792Sgshapiro usestat = true; 408264562Sgshapiro } 408338032Speter statmsg = buf; 408438032Speter } 408538032Speter#if NAMED_BIND 408664562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 408738032Speter { 408890792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 4089168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1, 409090792Sgshapiro statmsg); 409138032Speter statmsg = buf; 409290792Sgshapiro usestat = true; 409338032Speter } 409464562Sgshapiro#endif /* NAMED_BIND */ 409538032Speter else 409638032Speter { 409790792Sgshapiro statmsg = exmsg; 409864562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 409938032Speter { 4100168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg, 410190792Sgshapiro sm_errstring(errnum)); 410238032Speter statmsg = buf; 410390792Sgshapiro usestat = true; 410438032Speter } 410594334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 410694334Sgshapiro { 4107168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg, 410894334Sgshapiro shortenstring(e->e_statmsg, 403)); 410994334Sgshapiro statmsg = buf; 411094334Sgshapiro usestat = true; 411194334Sgshapiro } 411238032Speter } 411338032Speter 411438032Speter /* 411538032Speter ** Print the message as appropriate 411638032Speter */ 411738032Speter 411864562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 411938032Speter { 412038032Speter extern char MsgBuf[]; 412138032Speter 412264562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 412364562Sgshapiro { 412464562Sgshapiro if (dsn == NULL) 412564562Sgshapiro { 4126168515Sgshapiro (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 412790792Sgshapiro "%.*s", off, statmsg + 4); 412864562Sgshapiro dsn = dsnbuf; 412964562Sgshapiro } 413064562Sgshapiro off += 5; 413164562Sgshapiro } 413264562Sgshapiro else 413364562Sgshapiro { 413464562Sgshapiro off = 4; 413564562Sgshapiro } 413664562Sgshapiro message("%s", statmsg + off); 413764562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 413890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 413990792Sgshapiro &MsgBuf[4]); 414038032Speter } 414138032Speter else 414238032Speter { 414364562Sgshapiro char mbuf[ENHSCLEN + 4]; 414438032Speter 414538032Speter Errors++; 414664562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 4147168515Sgshapiro off < sizeof(mbuf) - 4) 414864562Sgshapiro { 414964562Sgshapiro if (dsn == NULL) 415064562Sgshapiro { 4151168515Sgshapiro (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 415290792Sgshapiro "%.*s", off, statmsg + 4); 415364562Sgshapiro dsn = dsnbuf; 415464562Sgshapiro } 415564562Sgshapiro off += 5; 415690792Sgshapiro 415790792Sgshapiro /* copy only part of statmsg to mbuf */ 415890792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 4159168515Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof(mbuf)); 416064562Sgshapiro } 416164562Sgshapiro else 416264562Sgshapiro { 416364562Sgshapiro dsnbuf[0] = '\0'; 4164168515Sgshapiro (void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s", 416590792Sgshapiro statmsg); 416664562Sgshapiro off = 4; 416764562Sgshapiro } 416864562Sgshapiro usrerr(mbuf, &statmsg[off]); 416938032Speter } 417038032Speter 417138032Speter /* 417238032Speter ** Final cleanup. 4173285303Sgshapiro ** Log a record of the transaction. Compute the new ExitStat 4174285303Sgshapiro ** -- if we already had an error, stick with that. 417538032Speter */ 417638032Speter 417738032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 417864562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 4179285303Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e, to, status); 418038032Speter 418138032Speter if (tTd(11, 2)) 418290792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 418390792Sgshapiro status, 418490792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 418590792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 418690792Sgshapiro errnum); 418738032Speter 418864562Sgshapiro if (status != EX_TEMPFAIL) 418964562Sgshapiro setstat(status); 419064562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 419190792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 419290792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 419338032Speter { 419490792Sgshapiro if (!usestat && e->e_message != NULL) 419590792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 419690792Sgshapiro e->e_message); 419790792Sgshapiro else 419890792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 419990792Sgshapiro statmsg + off); 420038032Speter } 420138032Speter errno = 0; 420273188Sgshapiro SM_SET_H_ERRNO(0); 420338032Speter} 420490792Sgshapiro/* 420538032Speter** LOGDELIVERY -- log the delivery in the system log 420638032Speter** 420738032Speter** Care is taken to avoid logging lines that are too long, because 420838032Speter** some versions of syslog have an unfortunate proclivity for core 420938032Speter** dumping. This is a hack, to be sure, that is at best empirical. 421038032Speter** 421138032Speter** Parameters: 421238032Speter** m -- the mailer info. Can be NULL for initial queue. 421338032Speter** mci -- the mailer connection info -- can be NULL if the 421464562Sgshapiro** log is occurring when no connection is active. 421564562Sgshapiro** dsn -- the DSN attached to the status. 421664562Sgshapiro** status -- the message to print for the status. 421738032Speter** ctladdr -- the controlling address for the to list. 421838032Speter** xstart -- the transaction start time, used for 421938032Speter** computing transaction delay. 422038032Speter** e -- the current envelope. 4221285303Sgshapiro** to -- the current recipient (NULL if none). 4222285303Sgshapiro** rcode -- status code 422338032Speter** 422438032Speter** Returns: 422538032Speter** none 422638032Speter** 422738032Speter** Side Effects: 422838032Speter** none 422938032Speter*/ 423038032Speter 423138032Spetervoid 4232285303Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode) 423338032Speter MAILER *m; 423438032Speter register MCI *mci; 423564562Sgshapiro char *dsn; 423664562Sgshapiro const char *status; 423738032Speter ADDRESS *ctladdr; 423838032Speter time_t xstart; 423938032Speter register ENVELOPE *e; 4240285303Sgshapiro ADDRESS *to; 4241285303Sgshapiro int rcode; 424238032Speter{ 424338032Speter register char *bp; 424438032Speter register char *p; 424538032Speter int l; 424690792Sgshapiro time_t now = curtime(); 424738032Speter char buf[1024]; 424838032Speter 424964562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 425038032Speter /* ctladdr: max 106 bytes */ 425138032Speter bp = buf; 425238032Speter if (ctladdr != NULL) 425338032Speter { 425490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 425590792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 425638032Speter bp += strlen(bp); 425738032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 425838032Speter { 425990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 426090792Sgshapiro (int) ctladdr->q_uid, 426190792Sgshapiro (int) ctladdr->q_gid); 426238032Speter bp += strlen(bp); 426338032Speter } 426438032Speter } 426538032Speter 426638032Speter /* delay & xdelay: max 41 bytes */ 426790792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 426890792Sgshapiro pintvl(now - e->e_ctime, true)); 426938032Speter bp += strlen(bp); 427038032Speter 427138032Speter if (xstart != (time_t) 0) 427238032Speter { 427390792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 427490792Sgshapiro pintvl(now - xstart, true)); 427538032Speter bp += strlen(bp); 427638032Speter } 427738032Speter 427838032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 427938032Speter if (m != NULL) 428038032Speter { 428190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 428290792Sgshapiro m->m_name); 428338032Speter bp += strlen(bp); 428438032Speter } 428538032Speter 4286285303Sgshapiro# if _FFR_LOG_MORE2 4287285303Sgshapiro# if STARTTLS 4288285303Sgshapiro p = macvalue(macid("{verify}"), e); 4289285303Sgshapiro if (p == NULL || *p == '\0') 4290285303Sgshapiro p = "NONE"; 4291285303Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", tls_verify=%.20s", p); 4292285303Sgshapiro bp += strlen(bp); 4293285303Sgshapiro# endif /* STARTTLS */ 4294285303Sgshapiro# endif /* _FFR_LOG_MORE2 */ 4295285303Sgshapiro 429664562Sgshapiro /* pri: changes with each delivery attempt */ 429790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 4298244833Sgshapiro PRT_NONNEGL(e->e_msgpriority)); 429964562Sgshapiro bp += strlen(bp); 430064562Sgshapiro 430138032Speter /* relay: max 66 bytes for IPv4 addresses */ 430238032Speter if (mci != NULL && mci->mci_host != NULL) 430338032Speter { 430438032Speter extern SOCKADDR CurHostAddr; 430538032Speter 430690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 430790792Sgshapiro shortenstring(mci->mci_host, 40)); 430838032Speter bp += strlen(bp); 430938032Speter 431038032Speter if (CurHostAddr.sa.sa_family != 0) 431138032Speter { 431290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 431390792Sgshapiro anynet_ntoa(&CurHostAddr)); 431438032Speter } 431538032Speter } 431690792Sgshapiro else if (strcmp(status, "quarantined") == 0) 431790792Sgshapiro { 431890792Sgshapiro if (e->e_quarmsg != NULL) 431990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 432090792Sgshapiro ", quarantine=%s", 432190792Sgshapiro shortenstring(e->e_quarmsg, 40)); 432290792Sgshapiro } 432364562Sgshapiro else if (strcmp(status, "queued") != 0) 432438032Speter { 432538032Speter p = macvalue('h', e); 432638032Speter if (p != NULL && p[0] != '\0') 432738032Speter { 432890792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 432990792Sgshapiro ", relay=%s", shortenstring(p, 40)); 433038032Speter } 433138032Speter } 433238032Speter bp += strlen(bp); 433338032Speter 433464562Sgshapiro /* dsn */ 433564562Sgshapiro if (dsn != NULL && *dsn != '\0') 433664562Sgshapiro { 433790792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 433890792Sgshapiro shortenstring(dsn, ENHSCLEN)); 433964562Sgshapiro bp += strlen(bp); 434064562Sgshapiro } 434138032Speter 4342147078Sgshapiro#if _FFR_LOG_NTRIES 4343147078Sgshapiro /* ntries */ 4344147078Sgshapiro if (e->e_ntries >= 0) 4345147078Sgshapiro { 4346147078Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4347147078Sgshapiro ", ntries=%d", e->e_ntries + 1); 4348147078Sgshapiro bp += strlen(bp); 4349147078Sgshapiro } 4350147078Sgshapiro#endif /* _FFR_LOG_NTRIES */ 4351147078Sgshapiro 435264562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 435364562Sgshapiro# if (STATLEN) < 63 435464562Sgshapiro# undef STATLEN 435564562Sgshapiro# define STATLEN 63 435664562Sgshapiro# endif /* (STATLEN) < 63 */ 435764562Sgshapiro# if (STATLEN) > 203 435864562Sgshapiro# undef STATLEN 435964562Sgshapiro# define STATLEN 203 436064562Sgshapiro# endif /* (STATLEN) > 203 */ 436164562Sgshapiro 4362285303Sgshapiro#if _FFR_LOGREPLY 4363285303Sgshapiro /* 4364285303Sgshapiro ** Notes: 4365285303Sgshapiro ** per-rcpt status: to->q_rstatus 4366285303Sgshapiro ** global status: e->e_text 4367285303Sgshapiro ** 4368285303Sgshapiro ** We (re)use STATLEN here, is that a good choice? 4369285303Sgshapiro ** 4370285303Sgshapiro ** stat=Deferred: ... 4371285303Sgshapiro ** has sometimes the same text? 4372285303Sgshapiro ** 4373285303Sgshapiro ** Note: this doesn't show the stage at which the error happened. 4374285303Sgshapiro ** can/should we log that? 4375285303Sgshapiro ** XS_* in reply() basically encodes the state. 4376285303Sgshapiro */ 4377285303Sgshapiro 4378285303Sgshapiro /* only show errors */ 4379285303Sgshapiro if (rcode != EX_OK && to != NULL && to->q_rstatus != NULL && 4380285303Sgshapiro *to->q_rstatus != '\0') 4381285303Sgshapiro { 4382285303Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4383285303Sgshapiro ", reply=%s", 4384285303Sgshapiro shortenstring(to->q_rstatus, STATLEN)); 4385285303Sgshapiro bp += strlen(bp); 4386285303Sgshapiro } 4387285303Sgshapiro else if (rcode != EX_OK && e->e_text != NULL) 4388285303Sgshapiro { 4389285303Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4390285303Sgshapiro ", reply=%d %s%s%s", 4391285303Sgshapiro e->e_rcode, 4392285303Sgshapiro e->e_renhsc, 4393285303Sgshapiro (e->e_renhsc[0] != '\0') ? " " : "", 4394285303Sgshapiro shortenstring(e->e_text, STATLEN)); 4395285303Sgshapiro bp += strlen(bp); 4396285303Sgshapiro } 4397285303Sgshapiro#endif 4398285303Sgshapiro 439938032Speter /* stat: max 210 bytes */ 4400168515Sgshapiro if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20))) 440138032Speter { 440238032Speter /* desperation move -- truncate data */ 4403168515Sgshapiro bp = buf + sizeof(buf) - ((STATLEN) + 17); 440490792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 440538032Speter bp += 3; 440638032Speter } 440738032Speter 440890792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 440938032Speter bp += strlen(bp); 441038032Speter 441190792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 441290792Sgshapiro SPACELEFT(buf, bp)); 441338032Speter 441438032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 441538032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 441690792Sgshapiro if (l < 0) 441790792Sgshapiro l = 0; 441864562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 441990792Sgshapiro while (strlen(p) >= l) 442038032Speter { 442164562Sgshapiro register char *q; 442238032Speter 442364562Sgshapiro for (q = p + l; q > p; q--) 442464562Sgshapiro { 4425285303Sgshapiro /* XXX a comma in an address will break this! */ 442664562Sgshapiro if (*q == ',') 442764562Sgshapiro break; 442864562Sgshapiro } 442964562Sgshapiro if (p == q) 443064562Sgshapiro break; 443190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 443266494Sgshapiro (int) (++q - p), p, buf); 443338032Speter p = q; 443438032Speter } 443564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 443638032Speter 443764562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 443838032Speter 443938032Speter l = SYSLOG_BUFSIZE - 85; 444090792Sgshapiro if (l < 0) 444190792Sgshapiro l = 0; 444264562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 444390792Sgshapiro while (strlen(p) >= l) 444438032Speter { 444564562Sgshapiro register char *q; 444638032Speter 444764562Sgshapiro for (q = p + l; q > p; q--) 444864562Sgshapiro { 444964562Sgshapiro if (*q == ',') 445064562Sgshapiro break; 445164562Sgshapiro } 445264562Sgshapiro if (p == q) 445364562Sgshapiro break; 445464562Sgshapiro 445590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 445666494Sgshapiro (int) (++q - p), p); 445738032Speter p = q; 445838032Speter } 445964562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 446038032Speter 446138032Speter if (ctladdr != NULL) 446238032Speter { 446338032Speter bp = buf; 446490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 446590792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 446638032Speter bp += strlen(bp); 446738032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 446838032Speter { 446990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 447090792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 447138032Speter bp += strlen(bp); 447238032Speter } 447338032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 447438032Speter } 447538032Speter bp = buf; 447690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 447790792Sgshapiro pintvl(now - e->e_ctime, true)); 447838032Speter bp += strlen(bp); 447938032Speter if (xstart != (time_t) 0) 448038032Speter { 448190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 448290792Sgshapiro pintvl(now - xstart, true)); 448338032Speter bp += strlen(bp); 448438032Speter } 448538032Speter 448638032Speter if (m != NULL) 448738032Speter { 448890792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 448990792Sgshapiro m->m_name); 449038032Speter bp += strlen(bp); 449138032Speter } 449238032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 449338032Speter 449438032Speter buf[0] = '\0'; 449538032Speter bp = buf; 449638032Speter if (mci != NULL && mci->mci_host != NULL) 449738032Speter { 449838032Speter extern SOCKADDR CurHostAddr; 449938032Speter 450090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 450190792Sgshapiro mci->mci_host); 450238032Speter bp += strlen(bp); 450338032Speter 450438032Speter if (CurHostAddr.sa.sa_family != 0) 450590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 450690792Sgshapiro " [%.100s]", 450790792Sgshapiro anynet_ntoa(&CurHostAddr)); 450838032Speter } 450990792Sgshapiro else if (strcmp(status, "quarantined") == 0) 451090792Sgshapiro { 451190792Sgshapiro if (e->e_quarmsg != NULL) 451290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 451390792Sgshapiro ", quarantine=%.100s", 451490792Sgshapiro e->e_quarmsg); 451590792Sgshapiro } 451664562Sgshapiro else if (strcmp(status, "queued") != 0) 451738032Speter { 451838032Speter p = macvalue('h', e); 451938032Speter if (p != NULL && p[0] != '\0') 4520168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p); 452138032Speter } 452238032Speter if (buf[0] != '\0') 452338032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 452438032Speter 452564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 452664562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 452738032Speter} 452890792Sgshapiro/* 452938032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 453038032Speter** 453138032Speter** This can be made an arbitrary message separator by changing $l 453238032Speter** 453338032Speter** One of the ugliest hacks seen by human eyes is contained herein: 453438032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 453538032Speter** does a well-meaning programmer such as myself have to deal with 453638032Speter** this kind of antique garbage???? 453738032Speter** 453838032Speter** Parameters: 453938032Speter** mci -- the connection information. 454038032Speter** e -- the envelope. 454138032Speter** 454238032Speter** Returns: 4543157001Sgshapiro** true iff line was written successfully 454438032Speter** 454538032Speter** Side Effects: 454638032Speter** outputs some text to fp. 454738032Speter*/ 454838032Speter 4549157001Sgshapirobool 455038032Speterputfromline(mci, e) 455138032Speter register MCI *mci; 455238032Speter ENVELOPE *e; 455338032Speter{ 455438032Speter char *template = UnixFromLine; 455538032Speter char buf[MAXLINE]; 455638032Speter char xbuf[MAXLINE]; 455738032Speter 455838032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 4559157001Sgshapiro return true; 456038032Speter 456138032Speter mci->mci_flags |= MCIF_INHEADER; 456238032Speter 456338032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 456438032Speter { 456538032Speter char *bang; 456638032Speter 4567168515Sgshapiro expand("\201g", buf, sizeof(buf), e); 456838032Speter bang = strchr(buf, '!'); 456938032Speter if (bang == NULL) 457038032Speter { 457138032Speter char *at; 457238032Speter char hname[MAXNAME]; 457338032Speter 457464562Sgshapiro /* 457542575Speter ** If we can construct a UUCP path, do so 457638032Speter */ 457738032Speter 457838032Speter at = strrchr(buf, '@'); 457938032Speter if (at == NULL) 458038032Speter { 4581168515Sgshapiro expand("\201k", hname, sizeof(hname), e); 458238032Speter at = hname; 458338032Speter } 458438032Speter else 458538032Speter *at++ = '\0'; 4586168515Sgshapiro (void) sm_snprintf(xbuf, sizeof(xbuf), 458790792Sgshapiro "From %.800s \201d remote from %.100s\n", 458890792Sgshapiro buf, at); 458938032Speter } 459038032Speter else 459138032Speter { 459238032Speter *bang++ = '\0'; 4593168515Sgshapiro (void) sm_snprintf(xbuf, sizeof(xbuf), 459490792Sgshapiro "From %.800s \201d remote from %.100s\n", 459590792Sgshapiro bang, buf); 459638032Speter template = xbuf; 459738032Speter } 459838032Speter } 4599168515Sgshapiro expand(template, buf, sizeof(buf), e); 4600157001Sgshapiro return putxline(buf, strlen(buf), mci, PXLF_HEADER); 460138032Speter} 4602157001Sgshapiro 460390792Sgshapiro/* 460438032Speter** PUTBODY -- put the body of a message. 460538032Speter** 460638032Speter** Parameters: 460738032Speter** mci -- the connection information. 460838032Speter** e -- the envelope to put out. 460938032Speter** separator -- if non-NULL, a message separator that must 461038032Speter** not be permitted in the resulting message. 461138032Speter** 461238032Speter** Returns: 4613157001Sgshapiro** true iff message was written successfully 461438032Speter** 461538032Speter** Side Effects: 461638032Speter** The message is written onto fp. 461738032Speter*/ 461838032Speter 461938032Speter/* values for output state variable */ 4620157001Sgshapiro#define OSTATE_HEAD 0 /* at beginning of line */ 4621157001Sgshapiro#define OSTATE_CR 1 /* read a carriage return */ 4622157001Sgshapiro#define OSTATE_INLINE 2 /* putting rest of line */ 462338032Speter 4624157001Sgshapirobool 462538032Speterputbody(mci, e, separator) 462638032Speter register MCI *mci; 462738032Speter register ENVELOPE *e; 462838032Speter char *separator; 462938032Speter{ 463090792Sgshapiro bool dead = false; 4631157001Sgshapiro bool ioerr = false; 4632157001Sgshapiro int save_errno; 463338032Speter char buf[MAXLINE]; 463490792Sgshapiro#if MIME8TO7 463542575Speter char *boundaries[MAXMIMENESTING + 1]; 463690792Sgshapiro#endif /* MIME8TO7 */ 463738032Speter 463838032Speter /* 463938032Speter ** Output the body of the message 464038032Speter */ 464138032Speter 464238032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 464338032Speter { 464490792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 464538032Speter 464690792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4647120256Sgshapiro SM_IO_RDONLY_B, NULL); 464838032Speter if (e->e_dfp == NULL) 464964562Sgshapiro { 465064562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 465164562Sgshapiro 465264562Sgshapiro if (errno == ENOENT) 465364562Sgshapiro msg++; 465464562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 465564562Sgshapiro } 465690792Sgshapiro 465738032Speter } 465838032Speter if (e->e_dfp == NULL) 465938032Speter { 466038032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 466138032Speter { 4662157001Sgshapiro if (!putline("", mci)) 4663157001Sgshapiro goto writeerr; 466438032Speter mci->mci_flags &= ~MCIF_INHEADER; 466538032Speter } 4666157001Sgshapiro if (!putline("<<< No Message Collected >>>", mci)) 4667157001Sgshapiro goto writeerr; 466838032Speter goto endofmessage; 466938032Speter } 467064562Sgshapiro 467138032Speter if (e->e_dfino == (ino_t) 0) 467238032Speter { 467338032Speter struct stat stbuf; 467438032Speter 467590792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 467690792Sgshapiro < 0) 467738032Speter e->e_dfino = -1; 467838032Speter else 467938032Speter { 468038032Speter e->e_dfdev = stbuf.st_dev; 468138032Speter e->e_dfino = stbuf.st_ino; 468238032Speter } 468338032Speter } 468438032Speter 468590792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 468664562Sgshapiro (void) bfrewind(e->e_dfp); 468764562Sgshapiro 4688157001Sgshapiro /* simulate an I/O timeout when used as source */ 4689157001Sgshapiro if (tTd(84, 101)) 4690157001Sgshapiro sleep(319); 4691157001Sgshapiro 469238032Speter#if MIME8TO7 469338032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 469438032Speter { 469538032Speter /* 469638032Speter ** Do 8 to 7 bit MIME conversion. 469738032Speter */ 469838032Speter 469938032Speter /* make sure it looks like a MIME message */ 4700157001Sgshapiro if (hvalue("MIME-Version", e->e_header) == NULL && 4701157001Sgshapiro !putline("MIME-Version: 1.0", mci)) 4702157001Sgshapiro goto writeerr; 470338032Speter 470438032Speter if (hvalue("Content-Type", e->e_header) == NULL) 470538032Speter { 4706168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 470790792Sgshapiro "Content-Type: text/plain; charset=%s", 470890792Sgshapiro defcharset(e)); 4709157001Sgshapiro if (!putline(buf, mci)) 4710157001Sgshapiro goto writeerr; 471138032Speter } 471238032Speter 471338032Speter /* now do the hard work */ 471438032Speter boundaries[0] = NULL; 471538032Speter mci->mci_flags |= MCIF_INHEADER; 4716159609Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) == 4717157001Sgshapiro SM_IO_EOF) 4718157001Sgshapiro goto writeerr; 471938032Speter } 472038032Speter# if MIME7TO8 472138032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 472238032Speter { 4723157001Sgshapiro if (!mime7to8(mci, e->e_header, e)) 4724157001Sgshapiro goto writeerr; 472538032Speter } 472664562Sgshapiro# endif /* MIME7TO8 */ 472742575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 472842575Speter { 472964562Sgshapiro bool oldsuprerrs = SuprErrs; 473064562Sgshapiro 473142575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 473242575Speter boundaries[0] = NULL; 473342575Speter mci->mci_flags |= MCIF_INHEADER; 473464562Sgshapiro 473564562Sgshapiro /* 473664562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 473764562Sgshapiro ** and don't want to generate a new bounce message whose 473864562Sgshapiro ** body propagates the broken MIME. We can't just not call 473964562Sgshapiro ** mime8to7() as is done above since we need the security 474064562Sgshapiro ** checks. The best we can do is suppress the errors. 474164562Sgshapiro */ 474264562Sgshapiro 474364562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 474490792Sgshapiro SuprErrs = true; 474564562Sgshapiro 4746157001Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, 4747159609Sgshapiro M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF) 4748157001Sgshapiro goto writeerr; 474964562Sgshapiro 475064562Sgshapiro /* restore SuprErrs */ 475164562Sgshapiro SuprErrs = oldsuprerrs; 475242575Speter } 475338032Speter else 475464562Sgshapiro#endif /* MIME8TO7 */ 475538032Speter { 475638032Speter int ostate; 475738032Speter register char *bp; 475838032Speter register char *pbp; 475938032Speter register int c; 476038032Speter register char *xp; 476138032Speter int padc; 476238032Speter char *buflim; 476338032Speter int pos = 0; 476464562Sgshapiro char peekbuf[12]; 476538032Speter 476638032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 476738032Speter { 4768157001Sgshapiro if (!putline("", mci)) 4769157001Sgshapiro goto writeerr; 477038032Speter mci->mci_flags &= ~MCIF_INHEADER; 477138032Speter } 477238032Speter 477338032Speter /* determine end of buffer; allow for short mailer lines */ 4774168515Sgshapiro buflim = &buf[sizeof(buf) - 1]; 477538032Speter if (mci->mci_mailer->m_linelimit > 0 && 4776168515Sgshapiro mci->mci_mailer->m_linelimit < sizeof(buf) - 1) 477738032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 477838032Speter 477938032Speter /* copy temp file to output with mapping */ 4780157001Sgshapiro ostate = OSTATE_HEAD; 478138032Speter bp = buf; 478238032Speter pbp = peekbuf; 478390792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 478438032Speter { 478538032Speter if (pbp > peekbuf) 478638032Speter c = *--pbp; 478790792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 478890792Sgshapiro == SM_IO_EOF) 478938032Speter break; 479038032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 479138032Speter c &= 0x7f; 479238032Speter switch (ostate) 479338032Speter { 4794157001Sgshapiro case OSTATE_HEAD: 479538032Speter if (c == '\0' && 479690792Sgshapiro bitnset(M_NONULLS, 479790792Sgshapiro mci->mci_mailer->m_flags)) 479838032Speter break; 479938032Speter if (c != '\r' && c != '\n' && bp < buflim) 480038032Speter { 480138032Speter *bp++ = c; 480238032Speter break; 480338032Speter } 480438032Speter 480538032Speter /* check beginning of line for special cases */ 480638032Speter *bp = '\0'; 480738032Speter pos = 0; 480890792Sgshapiro padc = SM_IO_EOF; 480938032Speter if (buf[0] == 'F' && 481090792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 481190792Sgshapiro && strncmp(buf, "From ", 5) == 0) 481238032Speter { 481338032Speter padc = '>'; 481438032Speter } 481538032Speter if (buf[0] == '-' && buf[1] == '-' && 481638032Speter separator != NULL) 481738032Speter { 481838032Speter /* possible separator */ 481938032Speter int sl = strlen(separator); 482038032Speter 482190792Sgshapiro if (strncmp(&buf[2], separator, sl) 482290792Sgshapiro == 0) 482338032Speter padc = ' '; 482438032Speter } 482538032Speter if (buf[0] == '.' && 482638032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 482738032Speter { 482838032Speter padc = '.'; 482938032Speter } 483038032Speter 483138032Speter /* now copy out saved line */ 483238032Speter if (TrafficLogFile != NULL) 483338032Speter { 483490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 483590792Sgshapiro SM_TIME_DEFAULT, 483690792Sgshapiro "%05d >>> ", 483790792Sgshapiro (int) CurrentPid); 483890792Sgshapiro if (padc != SM_IO_EOF) 483990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 484090792Sgshapiro SM_TIME_DEFAULT, 484190792Sgshapiro padc); 484238032Speter for (xp = buf; xp < bp; xp++) 484390792Sgshapiro (void) sm_io_putc(TrafficLogFile, 484490792Sgshapiro SM_TIME_DEFAULT, 484590792Sgshapiro (unsigned char) *xp); 484638032Speter if (c == '\n') 484790792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 484890792Sgshapiro SM_TIME_DEFAULT, 484990792Sgshapiro mci->mci_mailer->m_eol); 485038032Speter } 485190792Sgshapiro if (padc != SM_IO_EOF) 485238032Speter { 485390792Sgshapiro if (sm_io_putc(mci->mci_out, 485490792Sgshapiro SM_TIME_DEFAULT, padc) 485590792Sgshapiro == SM_IO_EOF) 485664562Sgshapiro { 485790792Sgshapiro dead = true; 485864562Sgshapiro continue; 485964562Sgshapiro } 486038032Speter pos++; 486138032Speter } 486238032Speter for (xp = buf; xp < bp; xp++) 486338032Speter { 486490792Sgshapiro if (sm_io_putc(mci->mci_out, 486590792Sgshapiro SM_TIME_DEFAULT, 486690792Sgshapiro (unsigned char) *xp) 486790792Sgshapiro == SM_IO_EOF) 486864562Sgshapiro { 486990792Sgshapiro dead = true; 487064562Sgshapiro break; 487164562Sgshapiro } 487238032Speter } 487364562Sgshapiro if (dead) 487464562Sgshapiro continue; 487538032Speter if (c == '\n') 487638032Speter { 487790792Sgshapiro if (sm_io_fputs(mci->mci_out, 487890792Sgshapiro SM_TIME_DEFAULT, 487990792Sgshapiro mci->mci_mailer->m_eol) 488090792Sgshapiro == SM_IO_EOF) 488164562Sgshapiro break; 488238032Speter pos = 0; 488338032Speter } 488438032Speter else 488538032Speter { 488638032Speter pos += bp - buf; 488738032Speter if (c != '\r') 4888112810Sgshapiro { 4889112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4890112810Sgshapiro sizeof(peekbuf)); 489138032Speter *pbp++ = c; 4892112810Sgshapiro } 489338032Speter } 489464562Sgshapiro 489538032Speter bp = buf; 489638032Speter 489738032Speter /* determine next state */ 489838032Speter if (c == '\n') 4899157001Sgshapiro ostate = OSTATE_HEAD; 490038032Speter else if (c == '\r') 4901157001Sgshapiro ostate = OSTATE_CR; 490238032Speter else 4903157001Sgshapiro ostate = OSTATE_INLINE; 490438032Speter continue; 490538032Speter 4906157001Sgshapiro case OSTATE_CR: 490738032Speter if (c == '\n') 490838032Speter { 490938032Speter /* got CRLF */ 491090792Sgshapiro if (sm_io_fputs(mci->mci_out, 491190792Sgshapiro SM_TIME_DEFAULT, 491290792Sgshapiro mci->mci_mailer->m_eol) 491390792Sgshapiro == SM_IO_EOF) 491464562Sgshapiro continue; 491564562Sgshapiro 491638032Speter if (TrafficLogFile != NULL) 491738032Speter { 491890792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 491990792Sgshapiro SM_TIME_DEFAULT, 492090792Sgshapiro mci->mci_mailer->m_eol); 492138032Speter } 4922168515Sgshapiro pos = 0; 4923157001Sgshapiro ostate = OSTATE_HEAD; 492438032Speter continue; 492538032Speter } 492638032Speter 492738032Speter /* had a naked carriage return */ 4928112810Sgshapiro SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 492938032Speter *pbp++ = c; 493038032Speter c = '\r'; 4931157001Sgshapiro ostate = OSTATE_INLINE; 493238032Speter goto putch; 493338032Speter 4934157001Sgshapiro case OSTATE_INLINE: 493538032Speter if (c == '\r') 493638032Speter { 4937157001Sgshapiro ostate = OSTATE_CR; 493838032Speter continue; 493938032Speter } 494038032Speter if (c == '\0' && 494190792Sgshapiro bitnset(M_NONULLS, 494290792Sgshapiro mci->mci_mailer->m_flags)) 494338032Speter break; 494438032Speterputch: 494538032Speter if (mci->mci_mailer->m_linelimit > 0 && 494664562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 494738032Speter c != '\n') 494838032Speter { 494964562Sgshapiro int d; 495064562Sgshapiro 495164562Sgshapiro /* check next character for EOL */ 495264562Sgshapiro if (pbp > peekbuf) 495364562Sgshapiro d = *(pbp - 1); 495490792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 495590792Sgshapiro SM_TIME_DEFAULT)) 495690792Sgshapiro != SM_IO_EOF) 4957112810Sgshapiro { 4958112810Sgshapiro SM_ASSERT(pbp < peekbuf + 4959112810Sgshapiro sizeof(peekbuf)); 496064562Sgshapiro *pbp++ = d; 4961112810Sgshapiro } 496264562Sgshapiro 496390792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 496464562Sgshapiro { 496564562Sgshapiro if (TrafficLogFile != NULL) 496690792Sgshapiro (void) sm_io_putc(TrafficLogFile, 496790792Sgshapiro SM_TIME_DEFAULT, 496890792Sgshapiro (unsigned char) c); 496990792Sgshapiro if (sm_io_putc(mci->mci_out, 497090792Sgshapiro SM_TIME_DEFAULT, 497190792Sgshapiro (unsigned char) c) 497290792Sgshapiro == SM_IO_EOF) 497364562Sgshapiro { 497490792Sgshapiro dead = true; 497564562Sgshapiro continue; 497664562Sgshapiro } 497764562Sgshapiro pos++; 497864562Sgshapiro continue; 497964562Sgshapiro } 498064562Sgshapiro 498190792Sgshapiro if (sm_io_putc(mci->mci_out, 498290792Sgshapiro SM_TIME_DEFAULT, '!') 498390792Sgshapiro == SM_IO_EOF || 498490792Sgshapiro sm_io_fputs(mci->mci_out, 498590792Sgshapiro SM_TIME_DEFAULT, 498690792Sgshapiro mci->mci_mailer->m_eol) 498790792Sgshapiro == SM_IO_EOF) 498864562Sgshapiro { 498990792Sgshapiro dead = true; 499064562Sgshapiro continue; 499164562Sgshapiro } 499264562Sgshapiro 499338032Speter if (TrafficLogFile != NULL) 499438032Speter { 499590792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 499690792Sgshapiro SM_TIME_DEFAULT, 499790792Sgshapiro "!%s", 499890792Sgshapiro mci->mci_mailer->m_eol); 499938032Speter } 5000157001Sgshapiro ostate = OSTATE_HEAD; 5001112810Sgshapiro SM_ASSERT(pbp < peekbuf + 5002112810Sgshapiro sizeof(peekbuf)); 500338032Speter *pbp++ = c; 500438032Speter continue; 500538032Speter } 500638032Speter if (c == '\n') 500738032Speter { 500838032Speter if (TrafficLogFile != NULL) 500990792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 501090792Sgshapiro SM_TIME_DEFAULT, 501190792Sgshapiro mci->mci_mailer->m_eol); 501290792Sgshapiro if (sm_io_fputs(mci->mci_out, 501390792Sgshapiro SM_TIME_DEFAULT, 501490792Sgshapiro mci->mci_mailer->m_eol) 501590792Sgshapiro == SM_IO_EOF) 501664562Sgshapiro continue; 501738032Speter pos = 0; 5018157001Sgshapiro ostate = OSTATE_HEAD; 501938032Speter } 502038032Speter else 502138032Speter { 502238032Speter if (TrafficLogFile != NULL) 502390792Sgshapiro (void) sm_io_putc(TrafficLogFile, 502490792Sgshapiro SM_TIME_DEFAULT, 502590792Sgshapiro (unsigned char) c); 502690792Sgshapiro if (sm_io_putc(mci->mci_out, 502790792Sgshapiro SM_TIME_DEFAULT, 502890792Sgshapiro (unsigned char) c) 502990792Sgshapiro == SM_IO_EOF) 503064562Sgshapiro { 503190792Sgshapiro dead = true; 503264562Sgshapiro continue; 503364562Sgshapiro } 503438032Speter pos++; 5035157001Sgshapiro ostate = OSTATE_INLINE; 503638032Speter } 503738032Speter break; 503838032Speter } 503938032Speter } 504038032Speter 504138032Speter /* make sure we are at the beginning of a line */ 504238032Speter if (bp > buf) 504338032Speter { 504438032Speter if (TrafficLogFile != NULL) 504538032Speter { 504638032Speter for (xp = buf; xp < bp; xp++) 504790792Sgshapiro (void) sm_io_putc(TrafficLogFile, 504890792Sgshapiro SM_TIME_DEFAULT, 504990792Sgshapiro (unsigned char) *xp); 505038032Speter } 505138032Speter for (xp = buf; xp < bp; xp++) 505238032Speter { 505390792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 505490792Sgshapiro (unsigned char) *xp) 505590792Sgshapiro == SM_IO_EOF) 505664562Sgshapiro { 505790792Sgshapiro dead = true; 505864562Sgshapiro break; 505964562Sgshapiro } 506038032Speter } 506138032Speter pos += bp - buf; 506238032Speter } 506364562Sgshapiro if (!dead && pos > 0) 506438032Speter { 506538032Speter if (TrafficLogFile != NULL) 506690792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 506790792Sgshapiro SM_TIME_DEFAULT, 506890792Sgshapiro mci->mci_mailer->m_eol); 5069157001Sgshapiro if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 5070157001Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF) 5071157001Sgshapiro goto writeerr; 507238032Speter } 507338032Speter } 507438032Speter 507590792Sgshapiro if (sm_io_error(e->e_dfp)) 507638032Speter { 507790792Sgshapiro syserr("putbody: %s/%cf%s: read error", 507890792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 507990792Sgshapiro DATAFL_LETTER, e->e_id); 508038032Speter ExitStat = EX_IOERR; 5081157001Sgshapiro ioerr = true; 508238032Speter } 508338032Speter 508438032Speterendofmessage: 508564562Sgshapiro /* 508664562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 508764562Sgshapiro ** the file offset in the stdio library for the 508864562Sgshapiro ** parent process will not agree with the in-kernel 508964562Sgshapiro ** file offset since the file descriptor is shared 509064562Sgshapiro ** between the processes. Therefore, it is vital 509164562Sgshapiro ** that the file always be rewound. This forces the 509264562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 509364562Sgshapiro ** offset to match. 509464562Sgshapiro */ 509564562Sgshapiro 5096157001Sgshapiro save_errno = errno; 509764562Sgshapiro if (e->e_dfp != NULL) 509864562Sgshapiro (void) bfrewind(e->e_dfp); 509964562Sgshapiro 510038032Speter /* some mailers want extra blank line at end of message */ 510164562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 510238032Speter buf[0] != '\0' && buf[0] != '\n') 5103157001Sgshapiro { 5104157001Sgshapiro if (!putline("", mci)) 5105157001Sgshapiro goto writeerr; 5106157001Sgshapiro } 510738032Speter 5108157001Sgshapiro if (!dead && 5109157001Sgshapiro (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF || 5110157001Sgshapiro (sm_io_error(mci->mci_out) && errno != EPIPE))) 511138032Speter { 5112157001Sgshapiro save_errno = errno; 511338032Speter syserr("putbody: write error"); 511438032Speter ExitStat = EX_IOERR; 5115157001Sgshapiro ioerr = true; 511638032Speter } 511764562Sgshapiro 5118157001Sgshapiro errno = save_errno; 5119157001Sgshapiro return !dead && !ioerr; 5120157001Sgshapiro 5121157001Sgshapiro writeerr: 5122157001Sgshapiro return false; 512338032Speter} 5124157001Sgshapiro 512590792Sgshapiro/* 512638032Speter** MAILFILE -- Send a message to a file. 512738032Speter** 512890792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 512990792Sgshapiro** execute bits, sendmail will try to become the owner of that file 513038032Speter** rather than the real user. Obviously, this only works if 513138032Speter** sendmail runs as root. 513238032Speter** 513338032Speter** This could be done as a subordinate mailer, except that it 513438032Speter** is used implicitly to save messages in ~/dead.letter. We 513538032Speter** view this as being sufficiently important as to include it 513638032Speter** here. For example, if the system is dying, we shouldn't have 513738032Speter** to create another process plus some pipes to save the message. 513838032Speter** 513938032Speter** Parameters: 514038032Speter** filename -- the name of the file to send to. 514138032Speter** mailer -- mailer definition for recipient -- if NULL, 514238032Speter** use FileMailer. 514338032Speter** ctladdr -- the controlling address header -- includes 514438032Speter** the userid/groupid to be when sending. 514538032Speter** sfflags -- flags for opening. 514638032Speter** e -- the current envelope. 514738032Speter** 514838032Speter** Returns: 514938032Speter** The exit code associated with the operation. 515038032Speter** 515138032Speter** Side Effects: 515238032Speter** none. 515338032Speter*/ 515438032Speter 515590792Sgshapiro# define RETURN(st) exit(st); 515690792Sgshapiro 515738032Speterstatic jmp_buf CtxMailfileTimeout; 515838032Speter 515938032Speterint 516038032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 516138032Speter char *volatile filename; 516238032Speter MAILER *volatile mailer; 516338032Speter ADDRESS *ctladdr; 516464562Sgshapiro volatile long sfflags; 516538032Speter register ENVELOPE *e; 516638032Speter{ 516790792Sgshapiro register SM_FILE_T *f; 516838032Speter register pid_t pid = -1; 516964562Sgshapiro volatile int mode; 517064562Sgshapiro int len; 517164562Sgshapiro off_t curoff; 517238032Speter bool suidwarn = geteuid() == 0; 517338032Speter char *p; 517464562Sgshapiro char *volatile realfile; 517590792Sgshapiro SM_EVENT *ev; 517698121Sgshapiro char buf[MAXPATHLEN]; 517798121Sgshapiro char targetfile[MAXPATHLEN]; 517838032Speter 517938032Speter if (tTd(11, 1)) 518038032Speter { 518190792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 5182132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 518338032Speter } 518438032Speter 518538032Speter if (mailer == NULL) 518638032Speter mailer = FileMailer; 518738032Speter 518838032Speter if (e->e_xfp != NULL) 518990792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 519038032Speter 519138032Speter /* 519238032Speter ** Special case /dev/null. This allows us to restrict file 519338032Speter ** delivery to regular files only. 519438032Speter */ 519538032Speter 519690792Sgshapiro if (sm_path_isdevnull(filename)) 519738032Speter return EX_OK; 519838032Speter 519938032Speter /* check for 8-bit available */ 520038032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 520138032Speter bitnset(M_7BITS, mailer->m_flags) && 520238032Speter (bitset(EF_DONT_MIME, e->e_flags) || 520338032Speter !(bitset(MM_MIME8BIT, MimeMode) || 520438032Speter (bitset(EF_IS_MIME, e->e_flags) && 520538032Speter bitset(MM_CVTMIME, MimeMode))))) 520638032Speter { 520738032Speter e->e_status = "5.6.3"; 520864562Sgshapiro usrerrenh(e->e_status, 520990792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 521090792Sgshapiro errno = 0; 521164562Sgshapiro return EX_DATAERR; 521238032Speter } 521338032Speter 521464562Sgshapiro /* Find the actual file */ 521564562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 521664562Sgshapiro { 521764562Sgshapiro len = strlen(SafeFileEnv); 521864562Sgshapiro 521964562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 522064562Sgshapiro filename += len; 522164562Sgshapiro 5222168515Sgshapiro if (len + strlen(filename) + 1 >= sizeof(targetfile)) 522364562Sgshapiro { 522464562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 522564562Sgshapiro SafeFileEnv, filename); 522664562Sgshapiro return EX_CANTCREAT; 522764562Sgshapiro } 5228168515Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile)); 522964562Sgshapiro realfile = targetfile + len; 523064562Sgshapiro if (*filename == '/') 523164562Sgshapiro filename++; 523294334Sgshapiro if (*filename != '\0') 523394334Sgshapiro { 523494334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 523594334Sgshapiro if (targetfile[len - 1] != '/') 523694334Sgshapiro (void) sm_strlcat(targetfile, 5237168515Sgshapiro "/", sizeof(targetfile)); 523894334Sgshapiro (void) sm_strlcat(targetfile, filename, 5239168515Sgshapiro sizeof(targetfile)); 524094334Sgshapiro } 524164562Sgshapiro } 524264562Sgshapiro else if (mailer->m_rootdir != NULL) 524364562Sgshapiro { 5244168515Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e); 524564562Sgshapiro len = strlen(targetfile); 524664562Sgshapiro 524764562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 524864562Sgshapiro filename += len; 524964562Sgshapiro 5250168515Sgshapiro if (len + strlen(filename) + 1 >= sizeof(targetfile)) 525164562Sgshapiro { 525264562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 525364562Sgshapiro targetfile, filename); 525464562Sgshapiro return EX_CANTCREAT; 525564562Sgshapiro } 525664562Sgshapiro realfile = targetfile + len; 525764562Sgshapiro if (targetfile[len - 1] != '/') 5258168515Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof(targetfile)); 525964562Sgshapiro if (*filename == '/') 526090792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 5261168515Sgshapiro sizeof(targetfile)); 526264562Sgshapiro else 526390792Sgshapiro (void) sm_strlcat(targetfile, filename, 5264168515Sgshapiro sizeof(targetfile)); 526564562Sgshapiro } 526664562Sgshapiro else 526764562Sgshapiro { 5268168515Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >= 5269168515Sgshapiro sizeof(targetfile)) 527064562Sgshapiro { 527164562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 527264562Sgshapiro return EX_CANTCREAT; 527364562Sgshapiro } 527464562Sgshapiro realfile = targetfile; 527564562Sgshapiro } 527664562Sgshapiro 527738032Speter /* 527838032Speter ** Fork so we can change permissions here. 527938032Speter ** Note that we MUST use fork, not vfork, because of 528038032Speter ** the complications of calling subroutines, etc. 528138032Speter */ 528238032Speter 528394334Sgshapiro 528494334Sgshapiro /* 528594334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 528694334Sgshapiro ** around so that the waitfor() below will get it. 528794334Sgshapiro */ 528894334Sgshapiro 528994334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 529094334Sgshapiro 529138032Speter DOFORK(fork); 529238032Speter 529338032Speter if (pid < 0) 529464562Sgshapiro return EX_OSERR; 529538032Speter else if (pid == 0) 529638032Speter { 529738032Speter /* child -- actually write to file */ 529838032Speter struct stat stb; 529938032Speter MCI mcibuf; 530042575Speter int err; 530138032Speter volatile int oflags = O_WRONLY|O_APPEND; 530238032Speter 530377349Sgshapiro /* Reset global flags */ 530477349Sgshapiro RestartRequest = NULL; 530590792Sgshapiro RestartWorkGroup = false; 530677349Sgshapiro ShutdownRequest = NULL; 530777349Sgshapiro PendingSignal = 0; 530890792Sgshapiro CurrentPid = getpid(); 530977349Sgshapiro 531038032Speter if (e->e_lockfp != NULL) 5311159609Sgshapiro { 5312159609Sgshapiro int fd; 531338032Speter 5314159609Sgshapiro fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 5315159609Sgshapiro /* SM_ASSERT(fd >= 0); */ 5316159609Sgshapiro if (fd >= 0) 5317159609Sgshapiro (void) close(fd); 5318159609Sgshapiro } 5319159609Sgshapiro 532090792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 532190792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 532290792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 532338032Speter (void) umask(OldUmask); 532438032Speter e->e_to = filename; 532538032Speter ExitStat = EX_OK; 532638032Speter 532738032Speter if (setjmp(CtxMailfileTimeout) != 0) 532838032Speter { 532990792Sgshapiro RETURN(EX_TEMPFAIL); 533038032Speter } 533138032Speter 533238032Speter if (TimeOuts.to_fileopen > 0) 533390792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 533490792Sgshapiro 0); 533538032Speter else 533638032Speter ev = NULL; 533738032Speter 533890792Sgshapiro /* check file mode to see if set-user-ID */ 533964562Sgshapiro if (stat(targetfile, &stb) < 0) 534064562Sgshapiro mode = FileMode; 534142575Speter else 534238032Speter mode = stb.st_mode; 534338032Speter 534438032Speter /* limit the errors to those actually caused in the child */ 534538032Speter errno = 0; 534638032Speter ExitStat = EX_OK; 534738032Speter 534864562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 534964562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 535064562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 535138032Speter { 535290792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 535338032Speter mode &= ~(S_ISGID|S_ISUID); 535464562Sgshapiro if (tTd(11, 20)) 535590792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 535638032Speter } 535738032Speter 535890792Sgshapiro /* we have to open the data file BEFORE setuid() */ 535938032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 536038032Speter { 536190792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 536238032Speter 536390792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5364120256Sgshapiro SM_IO_RDONLY_B, NULL); 536538032Speter if (e->e_dfp == NULL) 536638032Speter { 536738032Speter syserr("mailfile: Cannot open %s for %s from %s", 536838032Speter df, e->e_to, e->e_from.q_paddr); 536938032Speter } 537038032Speter } 537138032Speter 537238032Speter /* select a new user to run as */ 537338032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 537438032Speter { 537538032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 537638032Speter { 537738032Speter RealUserName = NULL; 5378132943Sgshapiro if (mailer->m_uid == NO_UID) 5379132943Sgshapiro RealUid = RunAsUid; 5380132943Sgshapiro else 5381132943Sgshapiro RealUid = mailer->m_uid; 538264562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 538364562Sgshapiro { 538464562Sgshapiro /* Only root can change the uid */ 5385285303Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%ld, RealUid=%ld", 5386285303Sgshapiro (long) RunAsUid, (long) RealUid); 538790792Sgshapiro RETURN(EX_TEMPFAIL); 538864562Sgshapiro } 538938032Speter } 539038032Speter else if (bitset(S_ISUID, mode)) 539138032Speter { 539238032Speter RealUserName = NULL; 539338032Speter RealUid = stb.st_uid; 539438032Speter } 539538032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 539638032Speter { 539738032Speter if (ctladdr->q_ruser != NULL) 539838032Speter RealUserName = ctladdr->q_ruser; 539938032Speter else 540038032Speter RealUserName = ctladdr->q_user; 540138032Speter RealUid = ctladdr->q_uid; 540238032Speter } 5403132943Sgshapiro else if (mailer != NULL && mailer->m_uid != NO_UID) 540438032Speter { 540538032Speter RealUserName = DefUser; 540638032Speter RealUid = mailer->m_uid; 540738032Speter } 540838032Speter else 540938032Speter { 541038032Speter RealUserName = DefUser; 541138032Speter RealUid = DefUid; 541238032Speter } 541338032Speter 541438032Speter /* select a new group to run as */ 541538032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 541664562Sgshapiro { 5417132943Sgshapiro if (mailer->m_gid == NO_GID) 5418132943Sgshapiro RealGid = RunAsGid; 5419132943Sgshapiro else 5420132943Sgshapiro RealGid = mailer->m_gid; 542164562Sgshapiro if (RunAsUid != 0 && 542264562Sgshapiro (RealGid != getgid() || 542364562Sgshapiro RealGid != getegid())) 542464562Sgshapiro { 542564562Sgshapiro /* Only root can change the gid */ 5426285303Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%ld, RunAsUid=%ld, gid=%ld, egid=%ld", 5427285303Sgshapiro (long) RealGid, (long) RunAsUid, 5428285303Sgshapiro (long) getgid(), (long) getegid()); 542990792Sgshapiro RETURN(EX_TEMPFAIL); 543064562Sgshapiro } 543164562Sgshapiro } 543238032Speter else if (bitset(S_ISGID, mode)) 543338032Speter RealGid = stb.st_gid; 543464562Sgshapiro else if (ctladdr != NULL && 543564562Sgshapiro ctladdr->q_uid == DefUid && 543664562Sgshapiro ctladdr->q_gid == 0) 543771345Sgshapiro { 543871345Sgshapiro /* 543971345Sgshapiro ** Special case: This means it is an 544071345Sgshapiro ** alias and we should act as DefaultUser. 544171345Sgshapiro ** See alias()'s comments. 544271345Sgshapiro */ 544371345Sgshapiro 544464562Sgshapiro RealGid = DefGid; 544571345Sgshapiro RealUserName = DefUser; 544671345Sgshapiro } 544771345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 544871345Sgshapiro RealGid = ctladdr->q_gid; 5449132943Sgshapiro else if (mailer != NULL && mailer->m_gid != NO_GID) 545038032Speter RealGid = mailer->m_gid; 545138032Speter else 545238032Speter RealGid = DefGid; 545338032Speter } 545438032Speter 545538032Speter /* last ditch */ 545638032Speter if (!bitset(SFF_ROOTOK, sfflags)) 545738032Speter { 545838032Speter if (RealUid == 0) 545938032Speter RealUid = DefUid; 546038032Speter if (RealGid == 0) 546138032Speter RealGid = DefGid; 546238032Speter } 546338032Speter 546438032Speter /* set group id list (needs /etc/group access) */ 546538032Speter if (RealUserName != NULL && !DontInitGroups) 546638032Speter { 546738032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 546864562Sgshapiro { 5469285303Sgshapiro syserr("mailfile: initgroups(%s, %ld) failed", 5470285303Sgshapiro RealUserName, (long) RealGid); 547190792Sgshapiro RETURN(EX_TEMPFAIL); 547264562Sgshapiro } 547338032Speter } 547438032Speter else 547538032Speter { 547638032Speter GIDSET_T gidset[1]; 547738032Speter 547838032Speter gidset[0] = RealGid; 547938032Speter if (setgroups(1, gidset) == -1 && suidwarn) 548064562Sgshapiro { 548138032Speter syserr("mailfile: setgroups() failed"); 548290792Sgshapiro RETURN(EX_TEMPFAIL); 548364562Sgshapiro } 548438032Speter } 548538032Speter 548664562Sgshapiro /* 548764562Sgshapiro ** If you have a safe environment, go into it. 548864562Sgshapiro */ 548964562Sgshapiro 549064562Sgshapiro if (realfile != targetfile) 549138032Speter { 549294334Sgshapiro char save; 549394334Sgshapiro 549494334Sgshapiro save = *realfile; 549564562Sgshapiro *realfile = '\0'; 549664562Sgshapiro if (tTd(11, 20)) 549790792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 549864562Sgshapiro if (chroot(targetfile) < 0) 549938032Speter { 550038032Speter syserr("mailfile: Cannot chroot(%s)", 550164562Sgshapiro targetfile); 550290792Sgshapiro RETURN(EX_CANTCREAT); 550338032Speter } 550494334Sgshapiro *realfile = save; 550538032Speter } 550664562Sgshapiro 550764562Sgshapiro if (tTd(11, 40)) 550890792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 550964562Sgshapiro 551038032Speter if (chdir("/") < 0) 551164562Sgshapiro { 551238032Speter syserr("mailfile: cannot chdir(/)"); 551390792Sgshapiro RETURN(EX_CANTCREAT); 551464562Sgshapiro } 551538032Speter 551638032Speter /* now reset the group and user ids */ 551738032Speter endpwent(); 551890792Sgshapiro sm_mbdb_terminate(); 551938032Speter if (setgid(RealGid) < 0 && suidwarn) 552064562Sgshapiro { 552138032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 552290792Sgshapiro RETURN(EX_TEMPFAIL); 552364562Sgshapiro } 552438032Speter vendor_set_uid(RealUid); 552538032Speter if (setuid(RealUid) < 0 && suidwarn) 552664562Sgshapiro { 552738032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 552890792Sgshapiro RETURN(EX_TEMPFAIL); 552964562Sgshapiro } 553038032Speter 553164562Sgshapiro if (tTd(11, 2)) 5532285303Sgshapiro sm_dprintf("mailfile: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n", 5533285303Sgshapiro (long) getuid(), (long) geteuid(), 5534285303Sgshapiro (long) getgid(), (long) getegid()); 553564562Sgshapiro 553664562Sgshapiro 553738032Speter /* move into some "safe" directory */ 553838032Speter if (mailer->m_execdir != NULL) 553938032Speter { 554038032Speter char *q; 554138032Speter 554238032Speter for (p = mailer->m_execdir; p != NULL; p = q) 554338032Speter { 554438032Speter q = strchr(p, ':'); 554538032Speter if (q != NULL) 554638032Speter *q = '\0'; 5547168515Sgshapiro expand(p, buf, sizeof(buf), e); 554838032Speter if (q != NULL) 554938032Speter *q++ = ':'; 555038032Speter if (tTd(11, 20)) 555190792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 555290792Sgshapiro buf); 555338032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 555438032Speter break; 555538032Speter } 555638032Speter } 555738032Speter 555864562Sgshapiro /* 555964562Sgshapiro ** Recheck the file after we have assumed the ID of the 556064562Sgshapiro ** delivery user to make sure we can deliver to it as 556164562Sgshapiro ** that user. This is necessary if sendmail is running 556264562Sgshapiro ** as root and the file is on an NFS mount which treats 556364562Sgshapiro ** root as nobody. 556464562Sgshapiro */ 556564562Sgshapiro 556664562Sgshapiro#if HASLSTAT 556764562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 556864562Sgshapiro err = stat(realfile, &stb); 556964562Sgshapiro else 557064562Sgshapiro err = lstat(realfile, &stb); 557164562Sgshapiro#else /* HASLSTAT */ 557264562Sgshapiro err = stat(realfile, &stb); 557364562Sgshapiro#endif /* HASLSTAT */ 557464562Sgshapiro 557564562Sgshapiro if (err < 0) 557664562Sgshapiro { 557764562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 557864562Sgshapiro mode = FileMode; 557964562Sgshapiro oflags |= O_CREAT|O_EXCL; 558064562Sgshapiro } 558164562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 558264562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 558364562Sgshapiro DontBlameSendmail) && 558464562Sgshapiro stb.st_nlink != 1) || 558564562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 558664562Sgshapiro exit(EX_CANTCREAT); 558764562Sgshapiro else 558864562Sgshapiro mode = stb.st_mode; 558964562Sgshapiro 559064562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 559138032Speter sfflags |= SFF_NOSLINK; 559264562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 559338032Speter sfflags |= SFF_NOHLINK; 559438032Speter sfflags &= ~SFF_OPENASROOT; 559564562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 559638032Speter if (f == NULL) 559738032Speter { 559864562Sgshapiro if (transienterror(errno)) 559964562Sgshapiro { 560064562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 560164562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 560290792Sgshapiro sm_errstring(errno)); 560390792Sgshapiro RETURN(EX_TEMPFAIL); 560464562Sgshapiro } 560564562Sgshapiro else 560664562Sgshapiro { 560764562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 560864562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 560990792Sgshapiro sm_errstring(errno)); 561090792Sgshapiro RETURN(EX_CANTCREAT); 561164562Sgshapiro } 561238032Speter } 561390792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 561490792Sgshapiro &stb)) 561538032Speter { 561664562Sgshapiro syserr("554 5.3.0 file changed after open"); 561790792Sgshapiro RETURN(EX_CANTCREAT); 561838032Speter } 561990792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 562038032Speter { 562190792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 562290792Sgshapiro sm_errstring(errno)); 562390792Sgshapiro RETURN(EX_CANTCREAT); 562438032Speter } 562538032Speter 562664562Sgshapiro curoff = stb.st_size; 562764562Sgshapiro 562838032Speter if (ev != NULL) 562990792Sgshapiro sm_clrevent(ev); 563038032Speter 5631168515Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 563238032Speter mcibuf.mci_mailer = mailer; 563338032Speter mcibuf.mci_out = f; 563438032Speter if (bitnset(M_7BITS, mailer->m_flags)) 563538032Speter mcibuf.mci_flags |= MCIF_7BIT; 563638032Speter 563738032Speter /* clear out per-message flags from connection structure */ 563838032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 563938032Speter 564038032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 564138032Speter !bitset(EF_DONT_MIME, e->e_flags) && 564238032Speter bitnset(M_7BITS, mailer->m_flags)) 564338032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 564438032Speter 564538032Speter#if MIME7TO8 564638032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 564738032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 564838032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 564990792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 565090792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 565138032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 565238032Speter { 565338032Speter /* may want to convert 7 -> 8 */ 565438032Speter /* XXX should really parse it here -- and use a class XXX */ 565590792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 565664562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 565738032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 565838032Speter } 565964562Sgshapiro#endif /* MIME7TO8 */ 566038032Speter 5661157001Sgshapiro if (!putfromline(&mcibuf, e) || 5662157001Sgshapiro !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) || 5663157001Sgshapiro !(*e->e_putbody)(&mcibuf, e, NULL) || 5664157001Sgshapiro !putline("\n", &mcibuf) || 5665157001Sgshapiro (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 566690792Sgshapiro (SuperSafe != SAFE_NO && 566790792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 5668157001Sgshapiro sm_io_error(f))) 566938032Speter { 567038032Speter setstat(EX_IOERR); 567164562Sgshapiro#if !NOFTRUNCATE 567290792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 567390792Sgshapiro curoff); 567464562Sgshapiro#endif /* !NOFTRUNCATE */ 567538032Speter } 567638032Speter 567738032Speter /* reset ISUID & ISGID bits for paranoid systems */ 567838032Speter#if HASFCHMOD 567990792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 568090792Sgshapiro (MODE_T) mode); 568164562Sgshapiro#else /* HASFCHMOD */ 568264562Sgshapiro (void) chmod(filename, (MODE_T) mode); 568364562Sgshapiro#endif /* HASFCHMOD */ 568490792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 568564562Sgshapiro setstat(EX_IOERR); 568690792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 568764562Sgshapiro (void) setuid(RealUid); 568838032Speter exit(ExitStat); 568964562Sgshapiro /* NOTREACHED */ 569038032Speter } 569138032Speter else 569238032Speter { 569338032Speter /* parent -- wait for exit status */ 569438032Speter int st; 569538032Speter 569638032Speter st = waitfor(pid); 569738032Speter if (st == -1) 569838032Speter { 569938032Speter syserr("mailfile: %s: wait", mailer->m_name); 570064562Sgshapiro return EX_SOFTWARE; 570138032Speter } 570238032Speter if (WIFEXITED(st)) 570390792Sgshapiro { 570490792Sgshapiro errno = 0; 570538032Speter return (WEXITSTATUS(st)); 570690792Sgshapiro } 570738032Speter else 570838032Speter { 570938032Speter syserr("mailfile: %s: child died on signal %d", 571038032Speter mailer->m_name, st); 571164562Sgshapiro return EX_UNAVAILABLE; 571238032Speter } 571364562Sgshapiro /* NOTREACHED */ 571438032Speter } 571538032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 571638032Speter} 571738032Speter 571838032Speterstatic void 5719141858Sgshapiromailfiletimeout(ignore) 5720141858Sgshapiro int ignore; 572138032Speter{ 572277349Sgshapiro /* 572377349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 572477349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 572577349Sgshapiro ** DOING. 572677349Sgshapiro */ 572777349Sgshapiro 572877349Sgshapiro errno = ETIMEDOUT; 572938032Speter longjmp(CtxMailfileTimeout, 1); 573038032Speter} 573190792Sgshapiro/* 573238032Speter** HOSTSIGNATURE -- return the "signature" for a host. 573338032Speter** 573438032Speter** The signature describes how we are going to send this -- it 573538032Speter** can be just the hostname (for non-Internet hosts) or can be 573638032Speter** an ordered list of MX hosts. 573738032Speter** 573838032Speter** Parameters: 573938032Speter** m -- the mailer describing this host. 574038032Speter** host -- the host name. 574138032Speter** 574238032Speter** Returns: 574338032Speter** The signature for this host. 574438032Speter** 574538032Speter** Side Effects: 574638032Speter** Can tweak the symbol table. 574738032Speter*/ 574890792Sgshapiro 574964562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 575038032Speter 575190792Sgshapirochar * 575264562Sgshapirohostsignature(m, host) 575338032Speter register MAILER *m; 575438032Speter char *host; 575538032Speter{ 575638032Speter register char *p; 575738032Speter register STAB *s; 575890792Sgshapiro time_t now; 575964562Sgshapiro#if NAMED_BIND 576064562Sgshapiro char sep = ':'; 576164562Sgshapiro char prevsep = ':'; 576238032Speter int i; 576338032Speter int len; 576438032Speter int nmx; 576564562Sgshapiro int hl; 576638032Speter char *hp; 576738032Speter char *endp; 576838032Speter int oldoptions = _res.options; 576938032Speter char *mxhosts[MAXMXHOSTS + 1]; 577090792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 577164562Sgshapiro#endif /* NAMED_BIND */ 577238032Speter 577364562Sgshapiro if (tTd(17, 3)) 577490792Sgshapiro sm_dprintf("hostsignature(%s)\n", host); 577564562Sgshapiro 577638032Speter /* 577777349Sgshapiro ** If local delivery (and not remote), just return a constant. 577864562Sgshapiro */ 577964562Sgshapiro 578077349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 578190792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 578290792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 578364562Sgshapiro return "localhost"; 578464562Sgshapiro 5785147078Sgshapiro /* an empty host does not have MX records */ 5786147078Sgshapiro if (*host == '\0') 5787147078Sgshapiro return "_empty_"; 5788147078Sgshapiro 578964562Sgshapiro /* 579038032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 579138032Speter */ 579238032Speter 579390792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 579490792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 579538032Speter { 579690792Sgshapiro /* just an ordinary mailer or deferred mode */ 579738032Speter return host; 579838032Speter } 579964562Sgshapiro#if NETUNIX 580064562Sgshapiro else if (m->m_argv[0] != NULL && 580164562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 580264562Sgshapiro { 580364562Sgshapiro /* rendezvous in the file system, no MX records */ 580464562Sgshapiro return host; 580564562Sgshapiro } 580664562Sgshapiro#endif /* NETUNIX */ 580738032Speter 580838032Speter /* 580938032Speter ** Look it up in the symbol table. 581038032Speter */ 581138032Speter 581290792Sgshapiro now = curtime(); 581338032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 581490792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 581564562Sgshapiro { 581690792Sgshapiro if (s->s_hostsig.hs_exp >= now) 581790792Sgshapiro { 581890792Sgshapiro if (tTd(17, 3)) 581990792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 582090792Sgshapiro s->s_hostsig.hs_sig); 582190792Sgshapiro return s->s_hostsig.hs_sig; 582290792Sgshapiro } 582390792Sgshapiro 582490792Sgshapiro /* signature is expired: clear it */ 582590792Sgshapiro sm_free(s->s_hostsig.hs_sig); 582690792Sgshapiro s->s_hostsig.hs_sig = NULL; 582764562Sgshapiro } 582838032Speter 582990792Sgshapiro /* set default TTL */ 583090792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 583190792Sgshapiro 583238032Speter /* 583390792Sgshapiro ** Not already there or expired -- create a signature. 583438032Speter */ 583538032Speter 583638032Speter#if NAMED_BIND 583738032Speter if (ConfigLevel < 2) 583838032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 583938032Speter 584038032Speter for (hp = host; hp != NULL; hp = endp) 584138032Speter { 584264562Sgshapiro#if NETINET6 584364562Sgshapiro if (*hp == '[') 584464562Sgshapiro { 584564562Sgshapiro endp = strchr(hp + 1, ']'); 584664562Sgshapiro if (endp != NULL) 584764562Sgshapiro endp = strpbrk(endp + 1, ":,"); 584864562Sgshapiro } 584964562Sgshapiro else 585064562Sgshapiro endp = strpbrk(hp, ":,"); 585164562Sgshapiro#else /* NETINET6 */ 585264562Sgshapiro endp = strpbrk(hp, ":,"); 585364562Sgshapiro#endif /* NETINET6 */ 585438032Speter if (endp != NULL) 585564562Sgshapiro { 585664562Sgshapiro sep = *endp; 585738032Speter *endp = '\0'; 585864562Sgshapiro } 585938032Speter 586038032Speter if (bitnset(M_NOMX, m->m_flags)) 586138032Speter { 586238032Speter /* skip MX lookups */ 586338032Speter nmx = 1; 586438032Speter mxhosts[0] = hp; 586538032Speter } 586638032Speter else 586738032Speter { 586838032Speter auto int rcode; 586990792Sgshapiro int ttl; 587038032Speter 587190792Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 587290792Sgshapiro &ttl); 587338032Speter if (nmx <= 0) 587438032Speter { 587580785Sgshapiro int save_errno; 587638032Speter register MCI *mci; 587738032Speter 587838032Speter /* update the connection info for this host */ 587980785Sgshapiro save_errno = errno; 588038032Speter mci = mci_get(hp, m); 588180785Sgshapiro mci->mci_errno = save_errno; 588238032Speter mci->mci_herrno = h_errno; 588371345Sgshapiro mci->mci_lastuse = now; 588464562Sgshapiro if (rcode == EX_NOHOST) 588564562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 588690792Sgshapiro "550 Host unknown"); 588764562Sgshapiro else 588864562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 588938032Speter 589038032Speter /* use the original host name as signature */ 589138032Speter nmx = 1; 589238032Speter mxhosts[0] = hp; 589338032Speter } 589464562Sgshapiro if (tTd(17, 3)) 589590792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 589690792Sgshapiro nmx, mxhosts[0]); 589790792Sgshapiro 589890792Sgshapiro /* 589990792Sgshapiro ** Set new TTL: we use only one! 590090792Sgshapiro ** We could try to use the minimum instead. 590190792Sgshapiro */ 590290792Sgshapiro 590390792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 590438032Speter } 590538032Speter 590638032Speter len = 0; 590738032Speter for (i = 0; i < nmx; i++) 590838032Speter len += strlen(mxhosts[i]) + 1; 590990792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 591090792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 591190792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 591264562Sgshapiro { 591364562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 591464562Sgshapiro host, MAXHOSTSIGNATURE, len); 591564562Sgshapiro len = MAXHOSTSIGNATURE; 591664562Sgshapiro } 591790792Sgshapiro p = sm_pmalloc_x(len); 591890792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 591938032Speter { 592090792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 592190792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 592290792Sgshapiro s->s_hostsig.hs_sig = p; 592364562Sgshapiro hl = strlen(p); 592464562Sgshapiro p += hl; 592564562Sgshapiro *p++ = prevsep; 592664562Sgshapiro len -= hl + 1; 592738032Speter } 592838032Speter else 592990792Sgshapiro s->s_hostsig.hs_sig = p; 593038032Speter for (i = 0; i < nmx; i++) 593138032Speter { 593264562Sgshapiro hl = strlen(mxhosts[i]); 593364562Sgshapiro if (len - 1 < hl || len <= 1) 593464562Sgshapiro { 593564562Sgshapiro /* force to drop out of outer loop */ 593664562Sgshapiro len = -1; 593764562Sgshapiro break; 593864562Sgshapiro } 593938032Speter if (i != 0) 594064562Sgshapiro { 594164562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 594264562Sgshapiro *p++ = ','; 594364562Sgshapiro else 594464562Sgshapiro *p++ = ':'; 594564562Sgshapiro len--; 594664562Sgshapiro } 594790792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 594864562Sgshapiro p += hl; 594964562Sgshapiro len -= hl; 595038032Speter } 595164562Sgshapiro 595264562Sgshapiro /* 595364562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 595464562Sgshapiro ** because we won't have more space for further hosts 595564562Sgshapiro ** anyway (separated by : in the .cf file). 595664562Sgshapiro */ 595764562Sgshapiro 595864562Sgshapiro if (len < 0) 595964562Sgshapiro break; 596038032Speter if (endp != NULL) 596164562Sgshapiro *endp++ = sep; 596264562Sgshapiro prevsep = sep; 596338032Speter } 596490792Sgshapiro makelower(s->s_hostsig.hs_sig); 596538032Speter if (ConfigLevel < 2) 596638032Speter _res.options = oldoptions; 596764562Sgshapiro#else /* NAMED_BIND */ 596838032Speter /* not using BIND -- the signature is just the host name */ 596990792Sgshapiro /* 597090792Sgshapiro ** 'host' points to storage that will be freed after we are 597190792Sgshapiro ** done processing the current envelope, so we copy it. 597290792Sgshapiro */ 597390792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 597464562Sgshapiro#endif /* NAMED_BIND */ 597538032Speter if (tTd(17, 1)) 597690792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 597790792Sgshapiro return s->s_hostsig.hs_sig; 597838032Speter} 597990792Sgshapiro/* 598064562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 598164562Sgshapiro** 598264562Sgshapiro** The signature describes how we are going to send this -- it 598364562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 598464562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 598564562Sgshapiro** MX preference values. 598664562Sgshapiro** 598764562Sgshapiro** Parameters: 598864562Sgshapiro** sig -- the host signature. 598964562Sgshapiro** mxhosts -- array to populate. 599090792Sgshapiro** mailer -- mailer. 599164562Sgshapiro** 599264562Sgshapiro** Returns: 599364562Sgshapiro** The number of hosts inserted into mxhosts array. 599464562Sgshapiro** 599564562Sgshapiro** Side Effects: 599664562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 599764562Sgshapiro*/ 599864562Sgshapiro 599964562Sgshapirostatic int 600064562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 600164562Sgshapiro char *sig; 600264562Sgshapiro char **mxhosts; 600364562Sgshapiro MAILER *mailer; 600464562Sgshapiro{ 600590792Sgshapiro unsigned short curpref = 0; 600690792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 600764562Sgshapiro char *hp, *endp; 600890792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 600964562Sgshapiro long rndm[MAXMXHOSTS]; 601064562Sgshapiro 601164562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 601264562Sgshapiro { 601364562Sgshapiro char sep = ':'; 601464562Sgshapiro 601564562Sgshapiro#if NETINET6 601664562Sgshapiro if (*hp == '[') 601764562Sgshapiro { 601864562Sgshapiro endp = strchr(hp + 1, ']'); 601964562Sgshapiro if (endp != NULL) 602064562Sgshapiro endp = strpbrk(endp + 1, ":,"); 602164562Sgshapiro } 602264562Sgshapiro else 602364562Sgshapiro endp = strpbrk(hp, ":,"); 602464562Sgshapiro#else /* NETINET6 */ 602564562Sgshapiro endp = strpbrk(hp, ":,"); 602664562Sgshapiro#endif /* NETINET6 */ 602764562Sgshapiro if (endp != NULL) 602864562Sgshapiro { 602964562Sgshapiro sep = *endp; 603064562Sgshapiro *endp = '\0'; 603164562Sgshapiro } 603264562Sgshapiro 603364562Sgshapiro mxhosts[nmx] = hp; 603464562Sgshapiro prefer[nmx] = curpref; 603564562Sgshapiro if (mci_match(hp, mailer)) 603664562Sgshapiro rndm[nmx] = 0; 603764562Sgshapiro else 603864562Sgshapiro rndm[nmx] = get_random(); 603964562Sgshapiro 604064562Sgshapiro if (endp != NULL) 604164562Sgshapiro { 604264562Sgshapiro /* 604364562Sgshapiro ** Since we don't have the original MX prefs, 604464562Sgshapiro ** make our own. If the separator is a ':', that 604564562Sgshapiro ** means the preference for the next host will be 604664562Sgshapiro ** higher than this one, so simply increment curpref. 604764562Sgshapiro */ 604864562Sgshapiro 604964562Sgshapiro if (sep == ':') 605064562Sgshapiro curpref++; 605164562Sgshapiro 605264562Sgshapiro *endp++ = sep; 605364562Sgshapiro } 605464562Sgshapiro if (++nmx >= MAXMXHOSTS) 605564562Sgshapiro break; 605664562Sgshapiro } 605764562Sgshapiro 605864562Sgshapiro /* sort the records using the random factor for equal preferences */ 605964562Sgshapiro for (i = 0; i < nmx; i++) 606064562Sgshapiro { 606164562Sgshapiro for (j = i + 1; j < nmx; j++) 606264562Sgshapiro { 606364562Sgshapiro /* 606464562Sgshapiro ** List is already sorted by MX preference, only 606564562Sgshapiro ** need to look for equal preference MX records 606664562Sgshapiro */ 606764562Sgshapiro 606864562Sgshapiro if (prefer[i] < prefer[j]) 606964562Sgshapiro break; 607064562Sgshapiro 607164562Sgshapiro if (prefer[i] > prefer[j] || 607264562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 607364562Sgshapiro { 607490792Sgshapiro register unsigned short tempp; 607564562Sgshapiro register long tempr; 607664562Sgshapiro register char *temp1; 607764562Sgshapiro 607864562Sgshapiro tempp = prefer[i]; 607964562Sgshapiro prefer[i] = prefer[j]; 608064562Sgshapiro prefer[j] = tempp; 608164562Sgshapiro temp1 = mxhosts[i]; 608264562Sgshapiro mxhosts[i] = mxhosts[j]; 608364562Sgshapiro mxhosts[j] = temp1; 608464562Sgshapiro tempr = rndm[i]; 608564562Sgshapiro rndm[i] = rndm[j]; 608664562Sgshapiro rndm[j] = tempr; 608764562Sgshapiro } 608864562Sgshapiro } 608964562Sgshapiro } 609064562Sgshapiro return nmx; 609164562Sgshapiro} 609264562Sgshapiro 609364562Sgshapiro# if STARTTLS 609464562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 609590792Sgshapirostatic bool tls_ok_clt = true; 609664562Sgshapiro 609790792Sgshapiro/* 609890792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 609964562Sgshapiro** 610064562Sgshapiro** Parameters: 610190792Sgshapiro** tls_ok -- should tls be done? 610290792Sgshapiro** 610390792Sgshapiro** Returns: 610464562Sgshapiro** none. 610564562Sgshapiro** 610690792Sgshapiro** Side Effects: 610790792Sgshapiro** sets tls_ok_clt (static variable in this module) 610890792Sgshapiro*/ 610990792Sgshapiro 611090792Sgshapirovoid 611190792Sgshapirosetclttls(tls_ok) 611290792Sgshapiro bool tls_ok; 611390792Sgshapiro{ 611490792Sgshapiro tls_ok_clt = tls_ok; 611590792Sgshapiro return; 611690792Sgshapiro} 611790792Sgshapiro/* 611890792Sgshapiro** INITCLTTLS -- initialize client side TLS 611990792Sgshapiro** 612090792Sgshapiro** Parameters: 612190792Sgshapiro** tls_ok -- should tls initialization be done? 612290792Sgshapiro** 612364562Sgshapiro** Returns: 612464562Sgshapiro** succeeded? 612590792Sgshapiro** 612690792Sgshapiro** Side Effects: 612790792Sgshapiro** sets tls_ok_clt (static variable in this module) 612864562Sgshapiro*/ 612964562Sgshapiro 613064562Sgshapirobool 613190792Sgshapiroinitclttls(tls_ok) 613290792Sgshapiro bool tls_ok; 613364562Sgshapiro{ 613490792Sgshapiro if (!tls_ok_clt) 613590792Sgshapiro return false; 613690792Sgshapiro tls_ok_clt = tls_ok; 613790792Sgshapiro if (!tls_ok_clt) 613890792Sgshapiro return false; 613964562Sgshapiro if (clt_ctx != NULL) 614090792Sgshapiro return true; /* already done */ 6141203004Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, Clt_SSL_Options, false, 6142203004Sgshapiro CltCertFile, CltKeyFile, 6143203004Sgshapiro CACertPath, CACertFile, DHParams); 614490792Sgshapiro return tls_ok_clt; 614564562Sgshapiro} 614664562Sgshapiro 614790792Sgshapiro/* 614864562Sgshapiro** STARTTLS -- try to start secure connection (client side) 614964562Sgshapiro** 615064562Sgshapiro** Parameters: 615164562Sgshapiro** m -- the mailer. 615264562Sgshapiro** mci -- the mailer connection info. 615364562Sgshapiro** e -- the envelope. 615464562Sgshapiro** 615564562Sgshapiro** Returns: 615664562Sgshapiro** success? 615764562Sgshapiro** (maybe this should be some other code than EX_ 615864562Sgshapiro** that denotes which stage failed.) 615964562Sgshapiro*/ 616064562Sgshapiro 616164562Sgshapirostatic int 616264562Sgshapirostarttls(m, mci, e) 616364562Sgshapiro MAILER *m; 616464562Sgshapiro MCI *mci; 616564562Sgshapiro ENVELOPE *e; 616664562Sgshapiro{ 616764562Sgshapiro int smtpresult; 616866494Sgshapiro int result = 0; 616966494Sgshapiro int rfd, wfd; 617064562Sgshapiro SSL *clt_ssl = NULL; 617190792Sgshapiro time_t tlsstart; 617264562Sgshapiro 617390792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 617466494Sgshapiro return EX_TEMPFAIL; 6175203004Sgshapiro 6176203004Sgshapiro# if USE_OPENSSL_ENGINE 6177223067Sgshapiro if (!SSLEngineInitialized && !SSL_set_engine(NULL)) 6178203004Sgshapiro { 6179203004Sgshapiro sm_syslog(LOG_ERR, NOQID, 6180203004Sgshapiro "STARTTLS=client, SSL_set_engine=failed"); 6181203004Sgshapiro return EX_TEMPFAIL; 6182203004Sgshapiro } 6183223067Sgshapiro SSLEngineInitialized = true; 6184203004Sgshapiro# endif /* USE_OPENSSL_ENGINE */ 6185203004Sgshapiro 618664562Sgshapiro smtpmessage("STARTTLS", m, mci); 618764562Sgshapiro 618864562Sgshapiro /* get the reply */ 6189132943Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 6190132943Sgshapiro XS_STARTTLS); 619164562Sgshapiro 619264562Sgshapiro /* check return code from server */ 6193157001Sgshapiro if (REPLYTYPE(smtpresult) == 4) 619464562Sgshapiro return EX_TEMPFAIL; 619564562Sgshapiro if (smtpresult == 501) 619664562Sgshapiro return EX_USAGE; 619764562Sgshapiro if (smtpresult == -1) 619864562Sgshapiro return smtpresult; 6199157001Sgshapiro 6200157001Sgshapiro /* not an expected reply but we have to deal with it */ 6201157001Sgshapiro if (REPLYTYPE(smtpresult) == 5) 6202157001Sgshapiro return EX_UNAVAILABLE; 620364562Sgshapiro if (smtpresult != 220) 620464562Sgshapiro return EX_PROTOCOL; 620564562Sgshapiro 620664562Sgshapiro if (LogLevel > 13) 620790792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 620864562Sgshapiro 620964562Sgshapiro /* start connection */ 621064562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 621164562Sgshapiro { 621264562Sgshapiro if (LogLevel > 5) 621364562Sgshapiro { 621490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 621590792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 621664562Sgshapiro if (LogLevel > 9) 6217249729Sgshapiro tlslogerr(LOG_WARNING, "client"); 621864562Sgshapiro } 621964562Sgshapiro return EX_SOFTWARE; 622064562Sgshapiro } 6221285303Sgshapiro /* SSL_clear(clt_ssl); ? */ 622264562Sgshapiro 6223285303Sgshapiro if (get_tls_se_options(e, clt_ssl, false) != 0) 6224285303Sgshapiro { 6225285303Sgshapiro sm_syslog(LOG_ERR, NOQID, 6226285303Sgshapiro "STARTTLS=client, get_tls_se_options=fail"); 6227285303Sgshapiro return EX_SOFTWARE; 6228285303Sgshapiro } 6229285303Sgshapiro 623090792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 623190792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 623266494Sgshapiro 623366494Sgshapiro if (rfd < 0 || wfd < 0 || 623490792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 623590792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 623664562Sgshapiro { 623764562Sgshapiro if (LogLevel > 5) 623864562Sgshapiro { 623990792Sgshapiro sm_syslog(LOG_ERR, NOQID, 624090792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 624190792Sgshapiro result); 624264562Sgshapiro if (LogLevel > 9) 6243249729Sgshapiro tlslogerr(LOG_WARNING, "client"); 624464562Sgshapiro } 624564562Sgshapiro return EX_SOFTWARE; 624664562Sgshapiro } 624764562Sgshapiro SSL_set_connect_state(clt_ssl); 624890792Sgshapiro tlsstart = curtime(); 624990792Sgshapiro 625090792Sgshapirossl_retry: 625164562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 625264562Sgshapiro { 6253157001Sgshapiro int i, ssl_err; 6254285303Sgshapiro int save_errno = errno; 625564562Sgshapiro 6256157001Sgshapiro ssl_err = SSL_get_error(clt_ssl, result); 6257157001Sgshapiro i = tls_retry(clt_ssl, rfd, wfd, tlsstart, 6258157001Sgshapiro TimeOuts.to_starttls, ssl_err, "client"); 6259157001Sgshapiro if (i > 0) 6260157001Sgshapiro goto ssl_retry; 626190792Sgshapiro 6262157001Sgshapiro if (LogLevel > 5) 626390792Sgshapiro { 6264244833Sgshapiro unsigned long l; 6265244833Sgshapiro const char *sr; 6266244833Sgshapiro 6267244833Sgshapiro l = ERR_peek_error(); 6268244833Sgshapiro sr = ERR_reason_error_string(l); 6269157001Sgshapiro sm_syslog(LOG_WARNING, NOQID, 6270244833Sgshapiro "STARTTLS=client, error: connect failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d", 6271244833Sgshapiro result, sr == NULL ? "unknown" : sr, ssl_err, 6272285303Sgshapiro save_errno, i); 6273244833Sgshapiro if (LogLevel > 9) 6274249729Sgshapiro tlslogerr(LOG_WARNING, "client"); 6275110560Sgshapiro } 627690792Sgshapiro 627764562Sgshapiro SSL_free(clt_ssl); 627864562Sgshapiro clt_ssl = NULL; 627964562Sgshapiro return EX_SOFTWARE; 628064562Sgshapiro } 628164562Sgshapiro mci->mci_ssl = clt_ssl; 628290792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 628390792Sgshapiro &mci->mci_macro, true); 628464562Sgshapiro 628590792Sgshapiro /* switch to use TLS... */ 628664562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 628764562Sgshapiro return EX_OK; 628864562Sgshapiro 628964562Sgshapiro /* failure */ 629064562Sgshapiro SSL_free(clt_ssl); 629164562Sgshapiro clt_ssl = NULL; 629264562Sgshapiro return EX_SOFTWARE; 629364562Sgshapiro} 629490792Sgshapiro/* 629564562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 629664562Sgshapiro** 629764562Sgshapiro** Parameters: 629864562Sgshapiro** mci -- the mailer connection info. 629964562Sgshapiro** 630064562Sgshapiro** Returns: 630164562Sgshapiro** success? 630264562Sgshapiro*/ 630390792Sgshapiro 630490792Sgshapirostatic int 630564562Sgshapiroendtlsclt(mci) 630664562Sgshapiro MCI *mci; 630764562Sgshapiro{ 630864562Sgshapiro int r; 630964562Sgshapiro 631064562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 631164562Sgshapiro return EX_OK; 631264562Sgshapiro r = endtls(mci->mci_ssl, "client"); 631364562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 631464562Sgshapiro return r; 631564562Sgshapiro} 631690792Sgshapiro# endif /* STARTTLS */ 631790792Sgshapiro# if STARTTLS || SASL 631890792Sgshapiro/* 631990792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 632064562Sgshapiro** 632164562Sgshapiro** Parameters: 632290792Sgshapiro** e -- envelope. 632390792Sgshapiro** flag -- flag to check in {client_flags} 632464562Sgshapiro** 632564562Sgshapiro** Returns: 632690792Sgshapiro** true iff flag is set. 632764562Sgshapiro*/ 632864562Sgshapiro 632990792Sgshapirostatic bool 633090792Sgshapiroiscltflgset(e, flag) 633190792Sgshapiro ENVELOPE *e; 633290792Sgshapiro int flag; 633364562Sgshapiro{ 633490792Sgshapiro char *p; 633573188Sgshapiro 633690792Sgshapiro p = macvalue(macid("{client_flags}"), e); 633790792Sgshapiro if (p == NULL) 633890792Sgshapiro return false; 633990792Sgshapiro for (; *p != '\0'; p++) 634064562Sgshapiro { 634190792Sgshapiro /* look for just this one flag */ 634290792Sgshapiro if (*p == (char) flag) 634390792Sgshapiro return true; 634464562Sgshapiro } 634590792Sgshapiro return false; 634664562Sgshapiro} 634790792Sgshapiro# endif /* STARTTLS || SASL */ 6348