deliver.c revision 110560
138032Speter/* 290792Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 1590792Sgshapiro#include <sys/time.h> 1638032Speter 17110560SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.940.2.10 2002/12/12 22:46:34 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 3190792Sgshapirovoid markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool)); 3264562Sgshapirostatic int deliver __P((ENVELOPE *, ADDRESS *)); 3364562Sgshapirostatic void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 3464562Sgshapirostatic void mailfiletimeout __P((void)); 3564562Sgshapirostatic int parse_hostsignature __P((char *, char **, MAILER *)); 3664562Sgshapirostatic void sendenvelope __P((ENVELOPE *, int)); 3790792Sgshapiroextern MCI *mci_new __P((SM_RPOOL_T *)); 3890792Sgshapirostatic int coloncmp __P((const char *, const char *)); 3964562Sgshapiro 4090792Sgshapiro#if STARTTLS 4164562Sgshapirostatic int starttls __P((MAILER *, MCI *, ENVELOPE *)); 4290792Sgshapirostatic int endtlsclt __P((MCI *)); 4390792Sgshapiro#endif /* STARTTLS */ 4490792Sgshapiro# if STARTTLS || SASL 4590792Sgshapirostatic bool iscltflgset __P((ENVELOPE *, int)); 4690792Sgshapiro# endif /* STARTTLS || SASL */ 4738032Speter 4838032Speter/* 4938032Speter** SENDALL -- actually send all the messages. 5038032Speter** 5138032Speter** Parameters: 5238032Speter** e -- the envelope to send. 5338032Speter** mode -- the delivery mode to use. If SM_DEFAULT, use 5438032Speter** the current e->e_sendmode. 5538032Speter** 5638032Speter** Returns: 5738032Speter** none. 5838032Speter** 5938032Speter** Side Effects: 6038032Speter** Scans the send lists and sends everything it finds. 6138032Speter** Delivers any appropriate error messages. 6238032Speter** If we are running in a non-interactive mode, takes the 6338032Speter** appropriate action. 6438032Speter*/ 6538032Speter 6638032Spetervoid 6738032Spetersendall(e, mode) 6838032Speter ENVELOPE *e; 6938032Speter int mode; 7038032Speter{ 7138032Speter register ADDRESS *q; 7238032Speter char *owner; 7338032Speter int otherowners; 7464562Sgshapiro int save_errno; 7538032Speter register ENVELOPE *ee; 7638032Speter ENVELOPE *splitenv = NULL; 7738032Speter int oldverbose = Verbose; 7890792Sgshapiro bool somedeliveries = false, expensive = false; 7938032Speter pid_t pid; 8038032Speter 8138032Speter /* 8238032Speter ** If this message is to be discarded, don't bother sending 8338032Speter ** the message at all. 8438032Speter */ 8538032Speter 8638032Speter if (bitset(EF_DISCARD, e->e_flags)) 8738032Speter { 8838032Speter if (tTd(13, 1)) 8990792Sgshapiro sm_dprintf("sendall: discarding id %s\n", e->e_id); 9038032Speter e->e_flags |= EF_CLRQUEUE; 9190792Sgshapiro if (LogLevel > 9) 9290792Sgshapiro logundelrcpts(e, "discarded", 9, true); 9390792Sgshapiro else if (LogLevel > 4) 9438032Speter sm_syslog(LOG_INFO, e->e_id, "discarded"); 9590792Sgshapiro markstats(e, NULL, STATS_REJECT); 9638032Speter return; 9738032Speter } 9838032Speter 9938032Speter /* 10038032Speter ** If we have had global, fatal errors, don't bother sending 10138032Speter ** the message at all if we are in SMTP mode. Local errors 10238032Speter ** (e.g., a single address failing) will still cause the other 10338032Speter ** addresses to be sent. 10438032Speter */ 10538032Speter 10638032Speter if (bitset(EF_FATALERRS, e->e_flags) && 10738032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 10838032Speter { 10938032Speter e->e_flags |= EF_CLRQUEUE; 11038032Speter return; 11138032Speter } 11238032Speter 11338032Speter /* determine actual delivery mode */ 11438032Speter if (mode == SM_DEFAULT) 11538032Speter { 11638032Speter mode = e->e_sendmode; 11738032Speter if (mode != SM_VERIFY && mode != SM_DEFER && 11838032Speter shouldqueue(e->e_msgpriority, e->e_ctime)) 11938032Speter mode = SM_QUEUE; 12038032Speter } 12138032Speter 12238032Speter if (tTd(13, 1)) 12338032Speter { 12490792Sgshapiro sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 12538032Speter mode, e->e_id); 12690792Sgshapiro printaddr(&e->e_from, false); 12790792Sgshapiro sm_dprintf("\te_flags = "); 12838032Speter printenvflags(e); 12990792Sgshapiro sm_dprintf("sendqueue:\n"); 13090792Sgshapiro printaddr(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 "); 18790792Sgshapiro printaddr(&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"); 21890792Sgshapiro printaddr(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 "); 23690792Sgshapiro printaddr(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 "); 24790792Sgshapiro printaddr(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 30764562Sgshapiro if (FallBackMX != NULL && 30864562Sgshapiro !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)) 31990792Sgshapiro sm_dprintf(" ... FallBackMX\n"); 32064562Sgshapiro 32190792Sgshapiro len = strlen(FallBackMX) + 1; 32290792Sgshapiro p = sm_rpool_malloc_x(e->e_rpool, len); 32390792Sgshapiro (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#if _FFR_QUARANTINE 36390792Sgshapiro else if (QueueMode != QM_QUARANTINE && 36490792Sgshapiro e->e_quarmsg != NULL) 36590792Sgshapiro { 36690792Sgshapiro if (tTd(13, 30)) 36790792Sgshapiro sm_dprintf(" ... quarantine: %s\n", 36890792Sgshapiro e->e_quarmsg); 36990792Sgshapiro q->q_state = QS_QUEUEUP; 37090792Sgshapiro expensive = true; 37190792Sgshapiro } 37290792Sgshapiro#endif /* _FFR_QUARANTINE */ 37338032Speter else 37438032Speter { 37538032Speter if (tTd(13, 30)) 37690792Sgshapiro sm_dprintf(" ... deliverable\n"); 37790792Sgshapiro somedeliveries = true; 37838032Speter } 37938032Speter } 38038032Speter 38138032Speter if (owner != NULL && otherowners > 0) 38238032Speter { 38338032Speter /* 38438032Speter ** Split this envelope into two. 38538032Speter */ 38638032Speter 38790792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 38890792Sgshapiro sizeof *ee); 38990792Sgshapiro STRUCTCOPY(*e, *ee); 39064562Sgshapiro ee->e_message = NULL; 39138032Speter ee->e_id = NULL; 39264562Sgshapiro assign_queueid(ee); 39338032Speter 39438032Speter if (tTd(13, 1)) 39590792Sgshapiro sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 39690792Sgshapiro e->e_id, ee->e_id, owner, 39790792Sgshapiro otherowners); 39838032Speter 39990792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 40090792Sgshapiro ee->e_sendqueue = copyqueue(e->e_sendqueue, 40190792Sgshapiro ee->e_rpool); 40290792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, 40390792Sgshapiro ee->e_rpool); 40438032Speter ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 40538032Speter ee->e_flags |= EF_NORECEIPT; 40690792Sgshapiro setsender(owner, ee, NULL, '\0', true); 40738032Speter if (tTd(13, 5)) 40838032Speter { 40990792Sgshapiro sm_dprintf("sendall(split): QS_SENDER "); 41090792Sgshapiro printaddr(&ee->e_from, false); 41138032Speter } 41264562Sgshapiro ee->e_from.q_state = QS_SENDER; 41338032Speter ee->e_dfp = NULL; 41464562Sgshapiro ee->e_lockfp = NULL; 41538032Speter ee->e_xfp = NULL; 41690792Sgshapiro ee->e_qgrp = e->e_qgrp; 41790792Sgshapiro ee->e_qdir = e->e_qdir; 41838032Speter ee->e_errormode = EM_MAIL; 41938032Speter ee->e_sibling = splitenv; 42064562Sgshapiro ee->e_statmsg = NULL; 42190792Sgshapiro#if _FFR_QUARANTINE 42290792Sgshapiro if (e->e_quarmsg != NULL) 42390792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 42490792Sgshapiro e->e_quarmsg); 42590792Sgshapiro#endif /* _FFR_QUARANTINE */ 42638032Speter splitenv = ee; 42738032Speter 42838032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 42938032Speter { 43038032Speter if (q->q_owner == owner) 43138032Speter { 43264562Sgshapiro q->q_state = QS_CLONED; 43338032Speter if (tTd(13, 6)) 43490792Sgshapiro sm_dprintf("\t... stripping %s from original envelope\n", 43590792Sgshapiro q->q_paddr); 43638032Speter } 43738032Speter } 43838032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 43938032Speter { 44038032Speter if (q->q_owner != owner) 44138032Speter { 44264562Sgshapiro q->q_state = QS_CLONED; 44338032Speter if (tTd(13, 6)) 44490792Sgshapiro sm_dprintf("\t... dropping %s from cloned envelope\n", 44590792Sgshapiro q->q_paddr); 44638032Speter } 44738032Speter else 44838032Speter { 44938032Speter /* clear DSN parameters */ 45038032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 45138032Speter q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 45238032Speter if (tTd(13, 6)) 45390792Sgshapiro sm_dprintf("\t... moving %s to cloned envelope\n", 45490792Sgshapiro q->q_paddr); 45538032Speter } 45638032Speter } 45738032Speter 45838032Speter if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 45990792Sgshapiro dup_queue_file(e, ee, DATAFL_LETTER); 46064562Sgshapiro 46164562Sgshapiro /* 46264562Sgshapiro ** Give the split envelope access to the parent 46364562Sgshapiro ** transcript file for errors obtained while 46464562Sgshapiro ** processing the recipients (done before the 46564562Sgshapiro ** envelope splitting). 46664562Sgshapiro */ 46764562Sgshapiro 46864562Sgshapiro if (e->e_xfp != NULL) 46990792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 47064562Sgshapiro 47164562Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 47264562Sgshapiro if (ee->e_xfp == NULL) 47364562Sgshapiro openxscript(ee); 47464562Sgshapiro 47542575Speter if (mode != SM_VERIFY && LogLevel > 4) 47690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 47790792Sgshapiro "%s: clone: owner=%s", 47890792Sgshapiro ee->e_id, owner); 47938032Speter } 48038032Speter } 48138032Speter 48238032Speter if (owner != NULL) 48338032Speter { 48490792Sgshapiro setsender(owner, e, NULL, '\0', true); 48538032Speter if (tTd(13, 5)) 48638032Speter { 48790792Sgshapiro sm_dprintf("sendall(owner): QS_SENDER "); 48890792Sgshapiro printaddr(&e->e_from, false); 48938032Speter } 49064562Sgshapiro e->e_from.q_state = QS_SENDER; 49138032Speter e->e_errormode = EM_MAIL; 49238032Speter e->e_flags |= EF_NORECEIPT; 49338032Speter e->e_flags &= ~EF_FATALERRS; 49438032Speter } 49538032Speter 49638032Speter /* if nothing to be delivered, just queue up everything */ 49790792Sgshapiro if (!somedeliveries && !WILL_BE_QUEUED(mode) && 49838032Speter mode != SM_VERIFY) 49938032Speter { 50090792Sgshapiro time_t now; 50171345Sgshapiro 50238032Speter if (tTd(13, 29)) 50390792Sgshapiro sm_dprintf("No deliveries: auto-queuing\n"); 50438032Speter mode = SM_QUEUE; 50590792Sgshapiro now = curtime(); 50638032Speter 50738032Speter /* treat this as a delivery in terms of counting tries */ 50871345Sgshapiro e->e_dtime = now; 50938032Speter if (!expensive) 51038032Speter e->e_ntries++; 51138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 51238032Speter { 51371345Sgshapiro ee->e_dtime = now; 51438032Speter if (!expensive) 51538032Speter ee->e_ntries++; 51638032Speter } 51738032Speter } 51838032Speter 51990792Sgshapiro if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 52090792Sgshapiro (mode != SM_VERIFY && SuperSafe == SAFE_REALLY)) && 52138032Speter (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 52238032Speter { 52390792Sgshapiro bool msync; 52490792Sgshapiro 52566494Sgshapiro /* 52666494Sgshapiro ** Be sure everything is instantiated in the queue. 52766494Sgshapiro ** Split envelopes first in case the machine crashes. 52866494Sgshapiro ** If the original were done first, we may lose 52966494Sgshapiro ** recipients. 53066494Sgshapiro */ 53166494Sgshapiro 53290792Sgshapiro#if !HASFLOCK 53390792Sgshapiro msync = false; 53490792Sgshapiro#else /* !HASFLOCK */ 53590792Sgshapiro msync = mode == SM_FORK; 53690792Sgshapiro#endif /* !HASFLOCK */ 53790792Sgshapiro 53838032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 53990792Sgshapiro queueup(ee, WILL_BE_QUEUED(mode), msync); 54090792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), msync); 54138032Speter } 54238032Speter 54338032Speter if (tTd(62, 10)) 54438032Speter checkfds("after envelope splitting"); 54538032Speter 54638032Speter /* 54738032Speter ** If we belong in background, fork now. 54838032Speter */ 54938032Speter 55038032Speter if (tTd(13, 20)) 55138032Speter { 55290792Sgshapiro sm_dprintf("sendall: final mode = %c\n", mode); 55338032Speter if (tTd(13, 21)) 55438032Speter { 55590792Sgshapiro sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 55690792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 55790792Sgshapiro e->e_id, e->e_from.q_paddr); 55890792Sgshapiro printaddr(e->e_sendqueue, true); 55938032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 56038032Speter { 56190792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 56290792Sgshapiro ee->e_id, ee->e_from.q_paddr); 56390792Sgshapiro printaddr(ee->e_sendqueue, true); 56438032Speter } 56590792Sgshapiro sm_dprintf("==========================================================\n\n"); 56638032Speter } 56738032Speter } 56838032Speter switch (mode) 56938032Speter { 57038032Speter case SM_VERIFY: 57138032Speter Verbose = 2; 57238032Speter break; 57338032Speter 57438032Speter case SM_QUEUE: 57538032Speter case SM_DEFER: 57664562Sgshapiro#if HASFLOCK 57738032Speter queueonly: 57864562Sgshapiro#endif /* HASFLOCK */ 57938032Speter if (e->e_nrcpts > 0) 58038032Speter e->e_flags |= EF_INQUEUE; 58190792Sgshapiro dropenvelope(e, splitenv != NULL, true); 58238032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 58338032Speter { 58438032Speter if (ee->e_nrcpts > 0) 58538032Speter ee->e_flags |= EF_INQUEUE; 58690792Sgshapiro dropenvelope(ee, false, true); 58738032Speter } 58838032Speter return; 58938032Speter 59038032Speter case SM_FORK: 59138032Speter if (e->e_xfp != NULL) 59290792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 59338032Speter 59464562Sgshapiro#if !HASFLOCK 59538032Speter /* 59638032Speter ** Since fcntl locking has the interesting semantic that 59738032Speter ** the lock is owned by a process, not by an open file 59838032Speter ** descriptor, we have to flush this to the queue, and 59938032Speter ** then restart from scratch in the child. 60038032Speter */ 60138032Speter 60238032Speter { 60338032Speter /* save id for future use */ 60438032Speter char *qid = e->e_id; 60538032Speter 60638032Speter /* now drop the envelope in the parent */ 60738032Speter e->e_flags |= EF_INQUEUE; 60890792Sgshapiro dropenvelope(e, splitenv != NULL, false); 60938032Speter 61038032Speter /* arrange to reacquire lock after fork */ 61138032Speter e->e_id = qid; 61238032Speter } 61338032Speter 61438032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 61538032Speter { 61638032Speter /* save id for future use */ 61738032Speter char *qid = ee->e_id; 61838032Speter 61938032Speter /* drop envelope in parent */ 62038032Speter ee->e_flags |= EF_INQUEUE; 62190792Sgshapiro dropenvelope(ee, false, false); 62238032Speter 62338032Speter /* and save qid for reacquisition */ 62438032Speter ee->e_id = qid; 62538032Speter } 62664562Sgshapiro#endif /* !HASFLOCK */ 62738032Speter 62864562Sgshapiro /* 62964562Sgshapiro ** Since the delivery may happen in a child and the parent 63064562Sgshapiro ** does not wait, the parent may close the maps thereby 63164562Sgshapiro ** removing any shared memory used by the map. Therefore, 63264562Sgshapiro ** close the maps now so the child will dynamically open 63364562Sgshapiro ** them if necessary. 63464562Sgshapiro */ 63564562Sgshapiro 63690792Sgshapiro closemaps(false); 63764562Sgshapiro 63838032Speter pid = fork(); 63938032Speter if (pid < 0) 64038032Speter { 64164562Sgshapiro syserr("deliver: fork 1"); 64264562Sgshapiro#if HASFLOCK 64338032Speter goto queueonly; 64464562Sgshapiro#else /* HASFLOCK */ 64538032Speter e->e_id = NULL; 64638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 64738032Speter ee->e_id = NULL; 64838032Speter return; 64964562Sgshapiro#endif /* HASFLOCK */ 65038032Speter } 65138032Speter else if (pid > 0) 65238032Speter { 65364562Sgshapiro#if HASFLOCK 65438032Speter /* be sure we leave the temp files to our child */ 65538032Speter /* close any random open files in the envelope */ 65638032Speter closexscript(e); 65738032Speter if (e->e_dfp != NULL) 65890792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 65938032Speter e->e_dfp = NULL; 66038032Speter e->e_flags &= ~EF_HAS_DF; 66138032Speter 66238032Speter /* can't call unlockqueue to avoid unlink of xfp */ 66338032Speter if (e->e_lockfp != NULL) 66490792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 66564562Sgshapiro else 66664562Sgshapiro syserr("%s: sendall: null lockfp", e->e_id); 66738032Speter e->e_lockfp = NULL; 66864562Sgshapiro#endif /* HASFLOCK */ 66938032Speter 67038032Speter /* make sure the parent doesn't own the envelope */ 67138032Speter e->e_id = NULL; 67238032Speter 67390792Sgshapiro#if USE_DOUBLE_FORK 67438032Speter /* catch intermediate zombie */ 67538032Speter (void) waitfor(pid); 67690792Sgshapiro#endif /* USE_DOUBLE_FORK */ 67738032Speter return; 67838032Speter } 67938032Speter 68077349Sgshapiro /* Reset global flags */ 68177349Sgshapiro RestartRequest = NULL; 68290792Sgshapiro RestartWorkGroup = false; 68377349Sgshapiro ShutdownRequest = NULL; 68477349Sgshapiro PendingSignal = 0; 68577349Sgshapiro 68666494Sgshapiro /* 68790792Sgshapiro ** Initialize exception stack and default exception 68890792Sgshapiro ** handler for child process. 68990792Sgshapiro */ 69090792Sgshapiro 69190792Sgshapiro sm_exc_newthread(fatal_error); 69290792Sgshapiro 69390792Sgshapiro /* 69466494Sgshapiro ** Since we have accepted responsbility for the message, 69566494Sgshapiro ** change the SIGTERM handler. intsig() (the old handler) 69666494Sgshapiro ** would remove the envelope if this was a command line 69766494Sgshapiro ** message submission. 69866494Sgshapiro */ 69966494Sgshapiro 70090792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 70166494Sgshapiro 70290792Sgshapiro#if USE_DOUBLE_FORK 70338032Speter /* double fork to avoid zombies */ 70438032Speter pid = fork(); 70538032Speter if (pid > 0) 70638032Speter exit(EX_OK); 70764562Sgshapiro save_errno = errno; 70890792Sgshapiro#endif /* USE_DOUBLE_FORK */ 70938032Speter 71090792Sgshapiro CurrentPid = getpid(); 71190792Sgshapiro 71238032Speter /* be sure we are immune from the terminal */ 71338032Speter disconnect(2, e); 71464562Sgshapiro clearstats(); 71538032Speter 71638032Speter /* prevent parent from waiting if there was an error */ 71738032Speter if (pid < 0) 71838032Speter { 71964562Sgshapiro errno = save_errno; 72064562Sgshapiro syserr("deliver: fork 2"); 72164562Sgshapiro#if HASFLOCK 72238032Speter e->e_flags |= EF_INQUEUE; 72364562Sgshapiro#else /* HASFLOCK */ 72438032Speter e->e_id = NULL; 72564562Sgshapiro#endif /* HASFLOCK */ 72690792Sgshapiro finis(true, true, ExitStat); 72738032Speter } 72838032Speter 72938032Speter /* be sure to give error messages in child */ 73090792Sgshapiro QuickAbort = false; 73138032Speter 73238032Speter /* 73338032Speter ** Close any cached connections. 73438032Speter ** 73538032Speter ** We don't send the QUIT protocol because the parent 73638032Speter ** still knows about the connection. 73738032Speter ** 73838032Speter ** This should only happen when delivering an error 73938032Speter ** message. 74038032Speter */ 74138032Speter 74290792Sgshapiro mci_flush(false, NULL); 74338032Speter 74464562Sgshapiro#if HASFLOCK 74538032Speter break; 74664562Sgshapiro#else /* HASFLOCK */ 74738032Speter 74838032Speter /* 74938032Speter ** Now reacquire and run the various queue files. 75038032Speter */ 75138032Speter 75238032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 75338032Speter { 75438032Speter ENVELOPE *sibling = ee->e_sibling; 75538032Speter 75690792Sgshapiro (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 75790792Sgshapiro false, false, ee); 75838032Speter ee->e_sibling = sibling; 75938032Speter } 76090792Sgshapiro (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 76190792Sgshapiro false, false, e); 76290792Sgshapiro finis(true, true, ExitStat); 76364562Sgshapiro#endif /* HASFLOCK */ 76438032Speter } 76538032Speter 76638032Speter sendenvelope(e, mode); 76790792Sgshapiro dropenvelope(e, true, true); 76838032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 76938032Speter { 77038032Speter CurEnv = ee; 77138032Speter if (mode != SM_VERIFY) 77238032Speter openxscript(ee); 77338032Speter sendenvelope(ee, mode); 77490792Sgshapiro dropenvelope(ee, true, true); 77538032Speter } 77638032Speter CurEnv = e; 77738032Speter 77838032Speter Verbose = oldverbose; 77938032Speter if (mode == SM_FORK) 78090792Sgshapiro finis(true, true, ExitStat); 78138032Speter} 78238032Speter 78364562Sgshapirostatic void 78438032Spetersendenvelope(e, mode) 78538032Speter register ENVELOPE *e; 78638032Speter int mode; 78738032Speter{ 78838032Speter register ADDRESS *q; 78938032Speter bool didany; 79038032Speter 79138032Speter if (tTd(13, 10)) 79290792Sgshapiro sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 79390792Sgshapiro e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 79490792Sgshapiro e->e_flags); 79538032Speter if (LogLevel > 80) 79638032Speter sm_syslog(LOG_DEBUG, e->e_id, 79764562Sgshapiro "sendenvelope, flags=0x%lx", 79864562Sgshapiro e->e_flags); 79938032Speter 80038032Speter /* 80138032Speter ** If we have had global, fatal errors, don't bother sending 80238032Speter ** the message at all if we are in SMTP mode. Local errors 80338032Speter ** (e.g., a single address failing) will still cause the other 80438032Speter ** addresses to be sent. 80538032Speter */ 80638032Speter 80738032Speter if (bitset(EF_FATALERRS, e->e_flags) && 80838032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 80938032Speter { 81038032Speter e->e_flags |= EF_CLRQUEUE; 81138032Speter return; 81238032Speter } 81338032Speter 81490792Sgshapiro /* 81590792Sgshapiro ** Don't attempt deliveries if we want to bounce now 81690792Sgshapiro ** or if deliver-by time is exceeded. 81790792Sgshapiro */ 81890792Sgshapiro 81964562Sgshapiro if (!bitset(EF_RESPONSE, e->e_flags) && 82090792Sgshapiro (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 82190792Sgshapiro (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 82290792Sgshapiro curtime() > e->e_ctime + e->e_deliver_by))) 82364562Sgshapiro return; 82464562Sgshapiro 82538032Speter /* 82638032Speter ** Run through the list and send everything. 82738032Speter ** 82838032Speter ** Set EF_GLOBALERRS so that error messages during delivery 82938032Speter ** result in returned mail. 83038032Speter */ 83138032Speter 83238032Speter e->e_nsent = 0; 83338032Speter e->e_flags |= EF_GLOBALERRS; 83464562Sgshapiro 83590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 83690792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 83790792Sgshapiro didany = false; 83838032Speter 83990792Sgshapiro if (!bitset(EF_SPLIT, e->e_flags)) 84090792Sgshapiro { 84190792Sgshapiro ENVELOPE *oldsib; 84290792Sgshapiro ENVELOPE *ee; 84390792Sgshapiro 84490792Sgshapiro /* 84590792Sgshapiro ** Save old sibling and set it to NULL to avoid 84690792Sgshapiro ** queueing up the same envelopes again. 84790792Sgshapiro ** This requires that envelopes in that list have 84890792Sgshapiro ** been take care of before (or at some other place). 84990792Sgshapiro */ 85090792Sgshapiro 85190792Sgshapiro oldsib = e->e_sibling; 85290792Sgshapiro e->e_sibling = NULL; 85390792Sgshapiro if (!split_by_recipient(e) && 85490792Sgshapiro bitset(EF_FATALERRS, e->e_flags)) 85590792Sgshapiro { 85690792Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 85790792Sgshapiro e->e_flags |= EF_CLRQUEUE; 85890792Sgshapiro return; 85990792Sgshapiro } 86090792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86190792Sgshapiro queueup(ee, false, true); 86290792Sgshapiro 86390792Sgshapiro /* clean up */ 86490792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86590792Sgshapiro { 86690792Sgshapiro /* now unlock the job */ 86790792Sgshapiro closexscript(ee); 86890792Sgshapiro unlockqueue(ee); 86990792Sgshapiro 87090792Sgshapiro /* this envelope is marked unused */ 87190792Sgshapiro if (ee->e_dfp != NULL) 87290792Sgshapiro { 87390792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 87490792Sgshapiro ee->e_dfp = NULL; 87590792Sgshapiro } 87690792Sgshapiro ee->e_id = NULL; 87790792Sgshapiro ee->e_flags &= ~EF_HAS_DF; 87890792Sgshapiro } 87990792Sgshapiro e->e_sibling = oldsib; 88090792Sgshapiro } 88190792Sgshapiro 88238032Speter /* now run through the queue */ 88338032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 88438032Speter { 88538032Speter#if XDEBUG 88638032Speter char wbuf[MAXNAME + 20]; 88738032Speter 88890792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", 88990792Sgshapiro MAXNAME, q->q_paddr); 89038032Speter checkfd012(wbuf); 89164562Sgshapiro#endif /* XDEBUG */ 89238032Speter if (mode == SM_VERIFY) 89338032Speter { 89438032Speter e->e_to = q->q_paddr; 89564562Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 89638032Speter { 89738032Speter if (q->q_host != NULL && q->q_host[0] != '\0') 89838032Speter message("deliverable: mailer %s, host %s, user %s", 89938032Speter q->q_mailer->m_name, 90038032Speter q->q_host, 90138032Speter q->q_user); 90238032Speter else 90338032Speter message("deliverable: mailer %s, user %s", 90438032Speter q->q_mailer->m_name, 90538032Speter q->q_user); 90638032Speter } 90738032Speter } 90864562Sgshapiro else if (QS_IS_OK(q->q_state)) 90938032Speter { 91038032Speter /* 91138032Speter ** Checkpoint the send list every few addresses 91238032Speter */ 91338032Speter 91466494Sgshapiro if (CheckpointInterval > 0 && 91566494Sgshapiro e->e_nsent >= CheckpointInterval) 91638032Speter { 91790792Sgshapiro queueup(e, false, false); 91838032Speter e->e_nsent = 0; 91938032Speter } 92038032Speter (void) deliver(e, q); 92190792Sgshapiro didany = true; 92238032Speter } 92338032Speter } 92438032Speter if (didany) 92538032Speter { 92638032Speter e->e_dtime = curtime(); 92738032Speter e->e_ntries++; 92838032Speter } 92938032Speter 93038032Speter#if XDEBUG 93138032Speter checkfd012("end of sendenvelope"); 93264562Sgshapiro#endif /* XDEBUG */ 93338032Speter} 93490792Sgshapiro 93590792Sgshapiro#if REQUIRES_DIR_FSYNC 93690792Sgshapiro/* 93790792Sgshapiro** SYNC_DIR -- fsync a directory based on a filename 93890792Sgshapiro** 93990792Sgshapiro** Parameters: 94090792Sgshapiro** filename -- path of file 94190792Sgshapiro** panic -- panic? 94290792Sgshapiro** 94390792Sgshapiro** Returns: 94490792Sgshapiro** none 94590792Sgshapiro*/ 94690792Sgshapiro 94790792Sgshapirovoid 94890792Sgshapirosync_dir(filename, panic) 94990792Sgshapiro char *filename; 95090792Sgshapiro bool panic; 95190792Sgshapiro{ 95290792Sgshapiro int dirfd; 95390792Sgshapiro char *dirp; 95490792Sgshapiro char dir[MAXPATHLEN]; 95590792Sgshapiro 956110560Sgshapiro#if _FFR_REQ_DIR_FSYNC_OPT 957110560Sgshapiro if (!RequiresDirfsync) 958110560Sgshapiro return; 959110560Sgshapiro#endif /* _FFR_REQ_DIR_FSYNC_OPT */ 960110560Sgshapiro 96190792Sgshapiro /* filesystems which require the directory be synced */ 96290792Sgshapiro dirp = strrchr(filename, '/'); 96390792Sgshapiro if (dirp != NULL) 96490792Sgshapiro { 96590792Sgshapiro if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir) 96690792Sgshapiro return; 96790792Sgshapiro dir[dirp - filename] = '\0'; 96890792Sgshapiro dirp = dir; 96990792Sgshapiro } 97090792Sgshapiro else 97190792Sgshapiro dirp = "."; 97290792Sgshapiro dirfd = open(dirp, O_RDONLY, 0700); 97390792Sgshapiro if (tTd(40,32)) 97490792Sgshapiro sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 97590792Sgshapiro dirp, dirfd); 97690792Sgshapiro if (dirfd >= 0) 97790792Sgshapiro { 97890792Sgshapiro if (fsync(dirfd) < 0) 97990792Sgshapiro { 98090792Sgshapiro if (panic) 98190792Sgshapiro syserr("!sync_dir: cannot fsync directory %s", 98290792Sgshapiro dirp); 98390792Sgshapiro else if (LogLevel > 1) 98490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 98590792Sgshapiro "sync_dir: cannot fsync directory %s: %s", 98690792Sgshapiro dirp, sm_errstring(errno)); 98790792Sgshapiro } 98890792Sgshapiro (void) close(dirfd); 98990792Sgshapiro } 99090792Sgshapiro} 99190792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */ 99290792Sgshapiro/* 99338032Speter** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 99438032Speter** 99538032Speter** Parameters: 99638032Speter** e -- the existing envelope 99738032Speter** ee -- the new envelope 99890792Sgshapiro** type -- the queue file type (e.g., DATAFL_LETTER) 99938032Speter** 100038032Speter** Returns: 100138032Speter** none 100238032Speter*/ 100338032Speter 100464562Sgshapirostatic void 100538032Speterdup_queue_file(e, ee, type) 100690792Sgshapiro ENVELOPE *e, *ee; 100738032Speter int type; 100838032Speter{ 100964562Sgshapiro char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 101038032Speter 101138032Speter ee->e_dfp = NULL; 101238032Speter ee->e_xfp = NULL; 101364562Sgshapiro 101464562Sgshapiro /* 101564562Sgshapiro ** Make sure both are in the same directory. 101664562Sgshapiro */ 101764562Sgshapiro 101890792Sgshapiro (void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf); 101990792Sgshapiro (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf); 1020102528Sgshapiro 1021102528Sgshapiro /* Force the df to disk if it's not there yet */ 1022102528Sgshapiro if (type == DATAFL_LETTER && e->e_dfp != NULL && 1023102528Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 1024102528Sgshapiro errno != EINVAL) 1025102528Sgshapiro { 1026102528Sgshapiro syserr("!dup_queue_file: can't commit %s", f1buf); 1027102528Sgshapiro /* NOTREACHED */ 1028102528Sgshapiro } 1029102528Sgshapiro 103038032Speter if (link(f1buf, f2buf) < 0) 103138032Speter { 103264562Sgshapiro int save_errno = errno; 103338032Speter 103438032Speter syserr("sendall: link(%s, %s)", f1buf, f2buf); 103564562Sgshapiro if (save_errno == EEXIST) 103638032Speter { 103738032Speter if (unlink(f2buf) < 0) 103838032Speter { 103938032Speter syserr("!sendall: unlink(%s): permanent", 104090792Sgshapiro f2buf); 104164562Sgshapiro /* NOTREACHED */ 104238032Speter } 104338032Speter if (link(f1buf, f2buf) < 0) 104438032Speter { 104538032Speter syserr("!sendall: link(%s, %s): permanent", 104690792Sgshapiro f1buf, f2buf); 104764562Sgshapiro /* NOTREACHED */ 104838032Speter } 104938032Speter } 105038032Speter } 105190792Sgshapiro SYNC_DIR(f2buf, true); 105238032Speter} 105390792Sgshapiro/* 105438032Speter** DOFORK -- do a fork, retrying a couple of times on failure. 105538032Speter** 105638032Speter** This MUST be a macro, since after a vfork we are running 105738032Speter** two processes on the same stack!!! 105838032Speter** 105938032Speter** Parameters: 106038032Speter** none. 106138032Speter** 106238032Speter** Returns: 106338032Speter** From a macro??? You've got to be kidding! 106438032Speter** 106538032Speter** Side Effects: 106638032Speter** Modifies the ==> LOCAL <== variable 'pid', leaving: 106738032Speter** pid of child in parent, zero in child. 106838032Speter** -1 on unrecoverable error. 106938032Speter** 107038032Speter** Notes: 107138032Speter** I'm awfully sorry this looks so awful. That's 107238032Speter** vfork for you..... 107338032Speter*/ 107438032Speter 107564562Sgshapiro#define NFORKTRIES 5 107638032Speter 107764562Sgshapiro#ifndef FORK 107838032Speter# define FORK fork 107964562Sgshapiro#endif /* ! FORK */ 108038032Speter 108164562Sgshapiro#define DOFORK(fORKfN) \ 108238032Speter{\ 108338032Speter register int i;\ 108438032Speter\ 108538032Speter for (i = NFORKTRIES; --i >= 0; )\ 108638032Speter {\ 108738032Speter pid = fORKfN();\ 108838032Speter if (pid >= 0)\ 108938032Speter break;\ 109038032Speter if (i > 0)\ 109164562Sgshapiro (void) sleep((unsigned) NFORKTRIES - i);\ 109238032Speter }\ 109338032Speter} 109490792Sgshapiro/* 109538032Speter** DOFORK -- simple fork interface to DOFORK. 109638032Speter** 109738032Speter** Parameters: 109838032Speter** none. 109938032Speter** 110038032Speter** Returns: 110138032Speter** pid of child in parent. 110238032Speter** zero in child. 110338032Speter** -1 on error. 110438032Speter** 110538032Speter** Side Effects: 110638032Speter** returns twice, once in parent and once in child. 110738032Speter*/ 110838032Speter 110977349Sgshapiropid_t 111038032Speterdofork() 111138032Speter{ 111238032Speter register pid_t pid = -1; 111338032Speter 111438032Speter DOFORK(fork); 111564562Sgshapiro return pid; 111638032Speter} 111790792Sgshapiro 111890792Sgshapiro/* 111990792Sgshapiro** COLONCMP -- compare host-signatures up to first ':' or EOS 112090792Sgshapiro** 112190792Sgshapiro** This takes two strings which happen to be host-signatures and 112290792Sgshapiro** compares them. If the lowest preference portions of the MX-RR's 112390792Sgshapiro** match (up to ':' or EOS, whichever is first), then we have 112490792Sgshapiro** match. This is used for coattail-piggybacking messages during 112590792Sgshapiro** message delivery. 112690792Sgshapiro** If the signatures are the same up to the first ':' the remainder of 112790792Sgshapiro** the signatures are then compared with a normal strcmp(). This saves 112890792Sgshapiro** re-examining the first part of the signatures. 112990792Sgshapiro** 113090792Sgshapiro** Parameters: 113190792Sgshapiro** a - first host-signature 113290792Sgshapiro** b - second host-signature 113390792Sgshapiro** 113490792Sgshapiro** Returns: 113590792Sgshapiro** HS_MATCH_NO -- no "match". 113690792Sgshapiro** HS_MATCH_FIRST -- "match" for the first MX preference 113790792Sgshapiro** (up to the first colon (':')). 113890792Sgshapiro** HS_MATCH_FULL -- match for the entire MX record. 113990792Sgshapiro** 114090792Sgshapiro** Side Effects: 114190792Sgshapiro** none. 114290792Sgshapiro*/ 114390792Sgshapiro 114490792Sgshapiro#define HS_MATCH_NO 0 114590792Sgshapiro#define HS_MATCH_FIRST 1 114690792Sgshapiro#define HS_MATCH_FULL 2 114790792Sgshapiro 114890792Sgshapirostatic int 114990792Sgshapirocoloncmp(a, b) 115090792Sgshapiro register const char *a; 115190792Sgshapiro register const char *b; 115290792Sgshapiro{ 115390792Sgshapiro int ret = HS_MATCH_NO; 115490792Sgshapiro int braclev = 0; 115590792Sgshapiro 115690792Sgshapiro while (*a == *b++) 115790792Sgshapiro { 115890792Sgshapiro /* Need to account for IPv6 bracketed addresses */ 115990792Sgshapiro if (*a == '[') 116090792Sgshapiro braclev++; 116190792Sgshapiro else if (*a == '[' && braclev > 0) 116290792Sgshapiro braclev--; 116390792Sgshapiro else if (*a == ':' && braclev <= 0) 116490792Sgshapiro { 116590792Sgshapiro ret = HS_MATCH_FIRST; 116690792Sgshapiro a++; 116790792Sgshapiro break; 116890792Sgshapiro } 116990792Sgshapiro else if (*a == '\0') 117090792Sgshapiro return HS_MATCH_FULL; /* a full match */ 117190792Sgshapiro a++; 117290792Sgshapiro } 117390792Sgshapiro if (ret == HS_MATCH_NO && 117490792Sgshapiro braclev <= 0 && 117590792Sgshapiro ((*a == '\0' && *(b - 1) == ':') || 117690792Sgshapiro (*a == ':' && *(b - 1) == '\0'))) 117790792Sgshapiro return HS_MATCH_FIRST; 117890792Sgshapiro if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 117990792Sgshapiro return HS_MATCH_FULL; 118090792Sgshapiro 118190792Sgshapiro return ret; 118290792Sgshapiro} 118390792Sgshapiro/* 118438032Speter** DELIVER -- Deliver a message to a list of addresses. 118538032Speter** 118638032Speter** This routine delivers to everyone on the same host as the 118738032Speter** user on the head of the list. It is clever about mailers 118838032Speter** that don't handle multiple users. It is NOT guaranteed 118938032Speter** that it will deliver to all these addresses however -- so 119038032Speter** deliver should be called once for each address on the 119138032Speter** list. 119290792Sgshapiro** Deliver tries to be as opportunistic as possible about piggybacking 119390792Sgshapiro** messages. Some definitions to make understanding easier follow below. 119490792Sgshapiro** Piggybacking occurs when an existing connection to a mail host can 119590792Sgshapiro** be used to send the same message to more than one recipient at the 119690792Sgshapiro** same time. So "no piggybacking" means one message for one recipient 119790792Sgshapiro** per connection. "Intentional piggybacking" happens when the 119890792Sgshapiro** recipients' host address (not the mail host address) is used to 119990792Sgshapiro** attempt piggybacking. Recipients with the same host address 120090792Sgshapiro** have the same mail host. "Coincidental piggybacking" relies on 120190792Sgshapiro** piggybacking based on all the mail host addresses in the MX-RR. This 120290792Sgshapiro** is "coincidental" in the fact it could not be predicted until the 120390792Sgshapiro** MX Resource Records for the hosts were obtained and examined. For 120490792Sgshapiro** example (preference order and equivalence is important, not values): 120590792Sgshapiro** domain1 IN MX 10 mxhost-A 120690792Sgshapiro** IN MX 20 mxhost-B 120790792Sgshapiro** domain2 IN MX 4 mxhost-A 120890792Sgshapiro** IN MX 8 mxhost-B 120990792Sgshapiro** Domain1 and domain2 can piggyback the same message to mxhost-A or 121090792Sgshapiro** mxhost-B (if mxhost-A cannot be reached). 121190792Sgshapiro** "Coattail piggybacking" relaxes the strictness of "coincidental 121290792Sgshapiro** piggybacking" in the hope that most significant (lowest value) 121390792Sgshapiro** MX preference host(s) can create more piggybacking. For example 121490792Sgshapiro** (again, preference order and equivalence is important, not values): 121590792Sgshapiro** domain3 IN MX 100 mxhost-C 121690792Sgshapiro** IN MX 100 mxhost-D 121790792Sgshapiro** IN MX 200 mxhost-E 121890792Sgshapiro** domain4 IN MX 50 mxhost-C 121990792Sgshapiro** IN MX 50 mxhost-D 122090792Sgshapiro** IN MX 80 mxhost-F 122190792Sgshapiro** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 122290792Sgshapiro** is available. Same with mxhost-D because in both RR's the preference 122390792Sgshapiro** value is the same as mxhost-C, respectively. 122490792Sgshapiro** So deliver attempts coattail piggybacking when possible. If the 122590792Sgshapiro** first MX preference level hosts cannot be used then the piggybacking 122690792Sgshapiro** reverts to coincidental piggybacking. Using the above example you 122790792Sgshapiro** cannot deliver to mxhost-F for domain3 regardless of preference value. 122890792Sgshapiro** ("Coattail" from "riding on the coattails of your predecessor" meaning 122990792Sgshapiro** gaining benefit from a predecessor effort with no or little addition 123090792Sgshapiro** effort. The predecessor here being the preceding MX RR). 123138032Speter** 123238032Speter** Parameters: 123338032Speter** e -- the envelope to deliver. 123438032Speter** firstto -- head of the address list to deliver to. 123538032Speter** 123638032Speter** Returns: 123738032Speter** zero -- successfully delivered. 123838032Speter** else -- some failure, see ExitStat for more info. 123938032Speter** 124038032Speter** Side Effects: 124138032Speter** The standard input is passed off to someone. 124238032Speter*/ 124338032Speter 124438032Speter#ifndef NO_UID 124538032Speter# define NO_UID -1 124664562Sgshapiro#endif /* ! NO_UID */ 124738032Speter#ifndef NO_GID 124838032Speter# define NO_GID -1 124964562Sgshapiro#endif /* ! NO_GID */ 125038032Speter 125164562Sgshapirostatic int 125238032Speterdeliver(e, firstto) 125338032Speter register ENVELOPE *e; 125438032Speter ADDRESS *firstto; 125538032Speter{ 125638032Speter char *host; /* host being sent to */ 125738032Speter char *user; /* user being sent to */ 125838032Speter char **pvp; 125938032Speter register char **mvp; 126038032Speter register char *p; 126138032Speter register MAILER *m; /* mailer for this recipient */ 126238032Speter ADDRESS *volatile ctladdr; 126390792Sgshapiro#if HASSETUSERCONTEXT 126438032Speter ADDRESS *volatile contextaddr = NULL; 126590792Sgshapiro#endif /* HASSETUSERCONTEXT */ 126638032Speter register MCI *volatile mci; 126790792Sgshapiro register ADDRESS *SM_NONVOLATILE to = firstto; 126890792Sgshapiro volatile bool clever = false; /* running user smtp to this mailer */ 126938032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 127038032Speter int rcode; /* response code */ 127190792Sgshapiro SM_NONVOLATILE int lmtp_rcode = EX_OK; 127290792Sgshapiro SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 127390792Sgshapiro SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 127438032Speter char *firstsig; /* signature of firstto */ 127590792Sgshapiro volatile pid_t pid = -1; 127638032Speter char *volatile curhost; 127790792Sgshapiro SM_NONVOLATILE unsigned short port = 0; 127890792Sgshapiro SM_NONVOLATILE time_t enough = 0; 127964562Sgshapiro#if NETUNIX 128090792Sgshapiro char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 128164562Sgshapiro#endif /* NETUNIX */ 128238032Speter time_t xstart; 128338032Speter bool suidwarn; 128438032Speter bool anyok; /* at least one address was OK */ 128590792Sgshapiro SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 128664562Sgshapiro bool ovr; 128790792Sgshapiro#if _FFR_QUARANTINE 128890792Sgshapiro bool quarantine; 128990792Sgshapiro#endif /* _FFR_QUARANTINE */ 129064562Sgshapiro int strsize; 129164562Sgshapiro int rcptcount; 129290792Sgshapiro int ret; 129364562Sgshapiro static int tobufsize = 0; 129464562Sgshapiro static char *tobuf = NULL; 129590792Sgshapiro char *rpath; /* translated return path */ 129638032Speter int mpvect[2]; 129738032Speter int rpvect[2]; 129864562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 129964562Sgshapiro char *pv[MAXPV + 1]; 130038032Speter char buf[MAXNAME + 1]; 130198121Sgshapiro char cbuf[MAXPATHLEN]; 130238032Speter 130338032Speter errno = 0; 130464562Sgshapiro if (!QS_IS_OK(to->q_state)) 130564562Sgshapiro return 0; 130638032Speter 130738032Speter suidwarn = geteuid() == 0; 130838032Speter 130938032Speter m = to->q_mailer; 131038032Speter host = to->q_host; 131138032Speter CurEnv = e; /* just in case */ 131238032Speter e->e_statmsg = NULL; 131338032Speter SmtpError[0] = '\0'; 131438032Speter xstart = curtime(); 131538032Speter 131638032Speter if (tTd(10, 1)) 131790792Sgshapiro sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 131838032Speter e->e_id, m->m_name, host, to->q_user); 131938032Speter if (tTd(10, 100)) 132090792Sgshapiro printopenfds(false); 132138032Speter 132238032Speter /* 132390792Sgshapiro ** Clear {client_*} macros if this is a bounce message to 132438032Speter ** prevent rejection by check_compat ruleset. 132538032Speter */ 132664562Sgshapiro 132738032Speter if (bitset(EF_RESPONSE, e->e_flags)) 132838032Speter { 132990792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 133090792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 133190792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 133290792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 133338032Speter } 133464562Sgshapiro 133590792Sgshapiro SM_TRY 133690792Sgshapiro { 133790792Sgshapiro ADDRESS *skip_back = NULL; 133890792Sgshapiro 133938032Speter /* 134038032Speter ** Do initial argv setup. 134138032Speter ** Insert the mailer name. Notice that $x expansion is 134238032Speter ** NOT done on the mailer name. Then, if the mailer has 134338032Speter ** a picky -f flag, we insert it as appropriate. This 134438032Speter ** code does not check for 'pv' overflow; this places a 134538032Speter ** manifest lower limit of 4 for MAXPV. 134638032Speter ** The from address rewrite is expected to make 134738032Speter ** the address relative to the other end. 134838032Speter */ 134938032Speter 135038032Speter /* rewrite from address, using rewriting rules */ 135138032Speter rcode = EX_OK; 135238032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 135338032Speter p = e->e_sender; 135438032Speter else 135538032Speter p = e->e_from.q_paddr; 135690792Sgshapiro rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 135790792Sgshapiro if (strlen(rpath) > MAXSHORTSTR) 135838032Speter { 135990792Sgshapiro rpath = shortenstring(rpath, MAXSHORTSTR); 136090792Sgshapiro 136190792Sgshapiro /* avoid bogus errno */ 136290792Sgshapiro errno = 0; 136390792Sgshapiro syserr("remotename: huge return path %s", rpath); 136438032Speter } 136590792Sgshapiro rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 136690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', rpath); 136790792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', host); 136838032Speter Errors = 0; 136938032Speter pvp = pv; 137038032Speter *pvp++ = m->m_argv[0]; 137138032Speter 137238032Speter /* insert -f or -r flag as appropriate */ 137364562Sgshapiro if (FromFlag && 137464562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 137564562Sgshapiro bitnset(M_ROPT, m->m_flags))) 137638032Speter { 137738032Speter if (bitnset(M_FOPT, m->m_flags)) 137838032Speter *pvp++ = "-f"; 137938032Speter else 138038032Speter *pvp++ = "-r"; 138190792Sgshapiro *pvp++ = rpath; 138238032Speter } 138338032Speter 138438032Speter /* 138538032Speter ** Append the other fixed parts of the argv. These run 138638032Speter ** up to the first entry containing "$u". There can only 138738032Speter ** be one of these, and there are only a few more slots 138838032Speter ** in the pv after it. 138938032Speter */ 139038032Speter 139138032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 139238032Speter { 139338032Speter /* can't use strchr here because of sign extension problems */ 139438032Speter while (*p != '\0') 139538032Speter { 139638032Speter if ((*p++ & 0377) == MACROEXPAND) 139738032Speter { 139838032Speter if (*p == 'u') 139938032Speter break; 140038032Speter } 140138032Speter } 140238032Speter 140338032Speter if (*p != '\0') 140438032Speter break; 140538032Speter 140638032Speter /* this entry is safe -- go ahead and process it */ 140738032Speter expand(*mvp, buf, sizeof buf, e); 140890792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 140938032Speter if (pvp >= &pv[MAXPV - 3]) 141038032Speter { 141164562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 141264562Sgshapiro pv[0]); 141390792Sgshapiro rcode = -1; 141490792Sgshapiro goto cleanup; 141538032Speter } 141638032Speter } 141738032Speter 141838032Speter /* 141938032Speter ** If we have no substitution for the user name in the argument 142038032Speter ** list, we know that we must supply the names otherwise -- and 142138032Speter ** SMTP is the answer!! 142238032Speter */ 142338032Speter 142438032Speter if (*mvp == NULL) 142538032Speter { 142673188Sgshapiro /* running LMTP or SMTP */ 142790792Sgshapiro clever = true; 142838032Speter *pvp = NULL; 142938032Speter } 143073188Sgshapiro else if (bitnset(M_LMTP, m->m_flags)) 143173188Sgshapiro { 143273188Sgshapiro /* not running LMTP */ 143373188Sgshapiro sm_syslog(LOG_ERR, NULL, 143473188Sgshapiro "Warning: mailer %s: LMTP flag (F=z) turned off", 143573188Sgshapiro m->m_name); 143673188Sgshapiro clrbitn(M_LMTP, m->m_flags); 143773188Sgshapiro } 143838032Speter 143938032Speter /* 144038032Speter ** At this point *mvp points to the argument with $u. We 144138032Speter ** run through our address list and append all the addresses 144238032Speter ** we can. If we run out of space, do not fret! We can 144338032Speter ** always send another copy later. 144438032Speter */ 144538032Speter 144664562Sgshapiro e->e_to = NULL; 144764562Sgshapiro strsize = 2; 144864562Sgshapiro rcptcount = 0; 144990792Sgshapiro ctladdr = NULL; 145090792Sgshapiro if (firstto->q_signature == NULL) 145190792Sgshapiro firstto->q_signature = hostsignature(firstto->q_mailer, 145290792Sgshapiro firstto->q_host); 145390792Sgshapiro firstsig = firstto->q_signature; 145464562Sgshapiro 145538032Speter for (; to != NULL; to = to->q_next) 145638032Speter { 145738032Speter /* avoid sending multiple recipients to dumb mailers */ 145864562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 145964562Sgshapiro break; 146038032Speter 146138032Speter /* if already sent or not for this host, don't send */ 146290792Sgshapiro if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 146338032Speter continue; 146438032Speter 146590792Sgshapiro /* 146690792Sgshapiro ** Must be same mailer to keep grouping rcpts. 146790792Sgshapiro ** If mailers don't match: continue; sendqueue is not 146890792Sgshapiro ** sorted by mailers, so don't break; 146990792Sgshapiro */ 147090792Sgshapiro 147190792Sgshapiro if (to->q_mailer != firstto->q_mailer) 147290792Sgshapiro continue; 147390792Sgshapiro 147490792Sgshapiro if (to->q_signature == NULL) /* for safety */ 147590792Sgshapiro to->q_signature = hostsignature(to->q_mailer, 147690792Sgshapiro to->q_host); 147790792Sgshapiro 147890792Sgshapiro /* 147990792Sgshapiro ** This is for coincidental and tailcoat piggybacking messages 148090792Sgshapiro ** to the same mail host. While the signatures are identical 148190792Sgshapiro ** (that's the MX-RR's are identical) we can do coincidental 148290792Sgshapiro ** piggybacking. We try hard for coattail piggybacking 148390792Sgshapiro ** with the same mail host when the next recipient has the 148490792Sgshapiro ** same host at lowest preference. It may be that this 148590792Sgshapiro ** won't work out, so 'skip_back' is maintained if a backup 148690792Sgshapiro ** to coincidental piggybacking or full signature must happen. 148790792Sgshapiro */ 148890792Sgshapiro 148990792Sgshapiro ret = firstto == to ? HS_MATCH_FULL : 149090792Sgshapiro coloncmp(to->q_signature, firstsig); 149190792Sgshapiro if (ret == HS_MATCH_FULL) 149290792Sgshapiro skip_back = to; 149390792Sgshapiro else if (ret == HS_MATCH_NO) 149464562Sgshapiro break; 149564562Sgshapiro 149690792Sgshapiro if (!clever) 149790792Sgshapiro { 149890792Sgshapiro /* avoid overflowing tobuf */ 149990792Sgshapiro strsize += strlen(to->q_paddr) + 1; 150090792Sgshapiro if (strsize > TOBUFSIZE) 150190792Sgshapiro break; 150290792Sgshapiro } 150390792Sgshapiro 150464562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 150564562Sgshapiro break; 150638032Speter 150738032Speter if (tTd(10, 1)) 150838032Speter { 150990792Sgshapiro sm_dprintf("\nsend to "); 151090792Sgshapiro printaddr(to, false); 151138032Speter } 151238032Speter 151338032Speter /* compute effective uid/gid when sending */ 151438032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 151590792Sgshapiro# if HASSETUSERCONTEXT 151638032Speter contextaddr = ctladdr = getctladdr(to); 151790792Sgshapiro# else /* HASSETUSERCONTEXT */ 151890792Sgshapiro ctladdr = getctladdr(to); 151990792Sgshapiro# endif /* HASSETUSERCONTEXT */ 152038032Speter 152138032Speter if (tTd(10, 2)) 152238032Speter { 152390792Sgshapiro sm_dprintf("ctladdr="); 152490792Sgshapiro printaddr(ctladdr, false); 152538032Speter } 152638032Speter 152738032Speter user = to->q_user; 152838032Speter e->e_to = to->q_paddr; 152938032Speter 153038032Speter /* 153138032Speter ** Check to see that these people are allowed to 153238032Speter ** talk to each other. 153366494Sgshapiro ** Check also for overflow of e_msgsize. 153438032Speter */ 153538032Speter 153666494Sgshapiro if (m->m_maxsize != 0 && 153766494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 153838032Speter { 153938032Speter e->e_flags |= EF_NO_BODY_RETN; 154038032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 154138032Speter to->q_status = "5.2.3"; 154238032Speter else 154338032Speter to->q_status = "5.3.4"; 154490792Sgshapiro 154564562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 154664562Sgshapiro usrerrenh(to->q_status, 154764562Sgshapiro "552 Message is too large; %ld bytes max", 154864562Sgshapiro m->m_maxsize); 154990792Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, false); 155064562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 155190792Sgshapiro NULL, ctladdr, xstart, e, to); 155238032Speter continue; 155338032Speter } 155473188Sgshapiro SM_SET_H_ERRNO(0); 155590792Sgshapiro ovr = true; 155638032Speter 155738032Speter /* do config file checking of compatibility */ 155890792Sgshapiro#if _FFR_QUARANTINE 155990792Sgshapiro quarantine = (e->e_quarmsg != NULL); 156090792Sgshapiro#endif /* _FFR_QUARANTINE */ 156164562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1562102528Sgshapiro e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1563102528Sgshapiro e->e_id); 156438032Speter if (rcode == EX_OK) 156538032Speter { 156642575Speter /* do in-code checking if not discarding */ 156742575Speter if (!bitset(EF_DISCARD, e->e_flags)) 156864562Sgshapiro { 156942575Speter rcode = checkcompat(to, e); 157090792Sgshapiro ovr = false; 157164562Sgshapiro } 157238032Speter } 157338032Speter if (rcode != EX_OK) 157438032Speter { 157564562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 157664562Sgshapiro giveresponse(rcode, to->q_status, m, 157790792Sgshapiro NULL, ctladdr, xstart, e, to); 157838032Speter continue; 157938032Speter } 158090792Sgshapiro#if _FFR_QUARANTINE 158190792Sgshapiro if (!quarantine && e->e_quarmsg != NULL) 158290792Sgshapiro { 158390792Sgshapiro /* 158490792Sgshapiro ** check_compat or checkcompat() has tried 158590792Sgshapiro ** to quarantine but that isn't supported. 158690792Sgshapiro ** Revert the attempt. 158790792Sgshapiro */ 158890792Sgshapiro 158990792Sgshapiro e->e_quarmsg = NULL; 159090792Sgshapiro macdefine(&e->e_macro, A_PERM, 159190792Sgshapiro macid("{quarantine}"), ""); 159290792Sgshapiro } 159390792Sgshapiro#endif /* _FFR_QUARANTINE */ 159442575Speter if (bitset(EF_DISCARD, e->e_flags)) 159542575Speter { 159642575Speter if (tTd(10, 5)) 159742575Speter { 159890792Sgshapiro sm_dprintf("deliver: discarding recipient "); 159990792Sgshapiro printaddr(to, false); 160042575Speter } 160138032Speter 160264562Sgshapiro /* pretend the message was sent */ 160364562Sgshapiro /* XXX should we log something here? */ 160464562Sgshapiro to->q_state = QS_DISCARDED; 160564562Sgshapiro 160642575Speter /* 160742575Speter ** Remove discard bit to prevent discard of 160864562Sgshapiro ** future recipients. This is safe because the 160964562Sgshapiro ** true "global discard" has been handled before 161064562Sgshapiro ** we get here. 161142575Speter */ 161264562Sgshapiro 161342575Speter e->e_flags &= ~EF_DISCARD; 161442575Speter continue; 161542575Speter } 161642575Speter 161738032Speter /* 161838032Speter ** Strip quote bits from names if the mailer is dumb 161938032Speter ** about them. 162038032Speter */ 162138032Speter 162238032Speter if (bitnset(M_STRIPQ, m->m_flags)) 162338032Speter { 162438032Speter stripquotes(user); 162538032Speter stripquotes(host); 162638032Speter } 1627110560Sgshapiro#if _FFR_STRIPBACKSL 1628110560Sgshapiro /* 1629110560Sgshapiro ** Strip one leading backslash if requesting and the 1630110560Sgshapiro ** next character is alphanumerical (the latter can 1631110560Sgshapiro ** probably relaxed a bit, see RFC2821). 1632110560Sgshapiro */ 163338032Speter 1634110560Sgshapiro if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1635110560Sgshapiro stripbackslash(user); 1636110560Sgshapiro#endif /* _FFR_STRIPBACKSL */ 1637110560Sgshapiro 163838032Speter /* hack attack -- delivermail compatibility */ 163938032Speter if (m == ProgMailer && *user == '|') 164038032Speter user++; 164138032Speter 164238032Speter /* 164338032Speter ** If an error message has already been given, don't 164438032Speter ** bother to send to this address. 164538032Speter ** 164638032Speter ** >>>>>>>>>> This clause assumes that the local mailer 164738032Speter ** >> NOTE >> cannot do any further aliasing; that 164838032Speter ** >>>>>>>>>> function is subsumed by sendmail. 164938032Speter */ 165038032Speter 165164562Sgshapiro if (!QS_IS_OK(to->q_state)) 165238032Speter continue; 165338032Speter 165438032Speter /* 165538032Speter ** See if this user name is "special". 165638032Speter ** If the user name has a slash in it, assume that this 165738032Speter ** is a file -- send it off without further ado. Note 165838032Speter ** that this type of addresses is not processed along 165938032Speter ** with the others, so we fudge on the To person. 166038032Speter */ 166138032Speter 166238032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 166338032Speter { 166490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); 166538032Speter p = to->q_home; 166638032Speter if (p == NULL && ctladdr != NULL) 166738032Speter p = ctladdr->q_home; 166890792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); 166938032Speter expand(m->m_argv[1], buf, sizeof buf, e); 167038032Speter if (strlen(buf) > 0) 167138032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 167238032Speter else 167338032Speter { 167438032Speter syserr("empty filename specification for mailer %s", 167538032Speter m->m_name); 167638032Speter rcode = EX_CONFIG; 167738032Speter } 167864562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 167990792Sgshapiro ctladdr, xstart, e, to); 168090792Sgshapiro markfailure(e, to, NULL, rcode, true); 168138032Speter e->e_nsent++; 168238032Speter if (rcode == EX_OK) 168338032Speter { 168464562Sgshapiro to->q_state = QS_SENT; 168538032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 168638032Speter bitset(QPINGONSUCCESS, to->q_flags)) 168738032Speter { 168838032Speter to->q_flags |= QDELIVERED; 168938032Speter to->q_status = "2.1.5"; 169090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, 169190792Sgshapiro SM_TIME_DEFAULT, 169290792Sgshapiro "%s... Successfully delivered\n", 169390792Sgshapiro to->q_paddr); 169438032Speter } 169538032Speter } 169638032Speter to->q_statdate = curtime(); 169790792Sgshapiro markstats(e, to, STATS_NORMAL); 169838032Speter continue; 169938032Speter } 170038032Speter 170138032Speter /* 170238032Speter ** Address is verified -- add this user to mailer 170338032Speter ** argv, and add it to the print list of recipients. 170438032Speter */ 170538032Speter 170638032Speter /* link together the chain of recipients */ 170738032Speter to->q_tchain = tochain; 170838032Speter tochain = to; 170964562Sgshapiro e->e_to = "[CHAIN]"; 171064562Sgshapiro 171190792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 171238032Speter p = to->q_home; 171338032Speter if (p == NULL && ctladdr != NULL) 171438032Speter p = ctladdr->q_home; 171590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 171638032Speter 171764562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 171864562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 171964562Sgshapiro { 172064562Sgshapiro char notify[MAXLINE]; 172164562Sgshapiro 172264562Sgshapiro notify[0] = '\0'; 172364562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 172490792Sgshapiro (void) sm_strlcat(notify, "SUCCESS,", 172590792Sgshapiro sizeof notify); 172664562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 172790792Sgshapiro (void) sm_strlcat(notify, "FAILURE,", 172890792Sgshapiro sizeof notify); 172964562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 173090792Sgshapiro (void) sm_strlcat(notify, "DELAY,", 173190792Sgshapiro sizeof notify); 173264562Sgshapiro 173364562Sgshapiro /* Set to NEVER or drop trailing comma */ 173464562Sgshapiro if (notify[0] == '\0') 173590792Sgshapiro (void) sm_strlcat(notify, "NEVER", 173690792Sgshapiro sizeof notify); 173764562Sgshapiro else 173864562Sgshapiro notify[strlen(notify) - 1] = '\0'; 173964562Sgshapiro 174090792Sgshapiro macdefine(&e->e_macro, A_TEMP, 174190792Sgshapiro macid("{dsn_notify}"), notify); 174264562Sgshapiro } 174364562Sgshapiro else 174490792Sgshapiro macdefine(&e->e_macro, A_PERM, 174590792Sgshapiro macid("{dsn_notify}"), NULL); 174664562Sgshapiro 174738032Speter /* 174838032Speter ** Expand out this user into argument list. 174938032Speter */ 175038032Speter 175138032Speter if (!clever) 175238032Speter { 175338032Speter expand(*mvp, buf, sizeof buf, e); 175490792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 175538032Speter if (pvp >= &pv[MAXPV - 2]) 175638032Speter { 175738032Speter /* allow some space for trailing parms */ 175838032Speter break; 175938032Speter } 176038032Speter } 176138032Speter } 176238032Speter 176338032Speter /* see if any addresses still exist */ 176464562Sgshapiro if (tochain == NULL) 176538032Speter { 176690792Sgshapiro rcode = 0; 176790792Sgshapiro goto cleanup; 176838032Speter } 176938032Speter 177038032Speter /* print out messages as full list */ 177190792Sgshapiro strsize = 1; 177290792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 177390792Sgshapiro strsize += strlen(to->q_paddr) + 1; 177490792Sgshapiro if (strsize < TOBUFSIZE) 177590792Sgshapiro strsize = TOBUFSIZE; 177690792Sgshapiro if (strsize > tobufsize) 177764562Sgshapiro { 177890792Sgshapiro SM_FREE_CLR(tobuf); 177990792Sgshapiro tobuf = sm_pmalloc_x(strsize); 178090792Sgshapiro tobufsize = strsize; 178164562Sgshapiro } 178290792Sgshapiro p = tobuf; 178390792Sgshapiro *p = '\0'; 178490792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 178590792Sgshapiro { 178690792Sgshapiro (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 178790792Sgshapiro ",", to->q_paddr); 178890792Sgshapiro p += strlen(p); 178990792Sgshapiro } 179038032Speter e->e_to = tobuf + 1; 179138032Speter 179238032Speter /* 179338032Speter ** Fill out any parameters after the $u parameter. 179438032Speter */ 179538032Speter 179690792Sgshapiro if (!clever) 179738032Speter { 179890792Sgshapiro while (*++mvp != NULL) 179990792Sgshapiro { 180090792Sgshapiro expand(*mvp, buf, sizeof buf, e); 180190792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 180290792Sgshapiro if (pvp >= &pv[MAXPV]) 180390792Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 180490792Sgshapiro pv[0]); 180590792Sgshapiro } 180638032Speter } 180738032Speter *pvp++ = NULL; 180838032Speter 180938032Speter /* 181038032Speter ** Call the mailer. 181138032Speter ** The argument vector gets built, pipes 181238032Speter ** are created as necessary, and we fork & exec as 181338032Speter ** appropriate. 181438032Speter ** If we are running SMTP, we just need to clean up. 181538032Speter */ 181638032Speter 181764562Sgshapiro /* XXX this seems a bit wierd */ 181838032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 181938032Speter bitset(QGOODUID, e->e_from.q_flags)) 182038032Speter ctladdr = &e->e_from; 182138032Speter 182238032Speter#if NAMED_BIND 182338032Speter if (ConfigLevel < 2) 182438032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 182564562Sgshapiro#endif /* NAMED_BIND */ 182638032Speter 182738032Speter if (tTd(11, 1)) 182838032Speter { 182990792Sgshapiro sm_dprintf("openmailer:"); 183038032Speter printav(pv); 183138032Speter } 183238032Speter errno = 0; 183373188Sgshapiro SM_SET_H_ERRNO(0); 183438032Speter CurHostName = NULL; 183538032Speter 183638032Speter /* 183738032Speter ** Deal with the special case of mail handled through an IPC 183838032Speter ** connection. 183938032Speter ** In this case we don't actually fork. We must be 184038032Speter ** running SMTP for this to work. We will return a 184138032Speter ** zero pid to indicate that we are running IPC. 184238032Speter ** We also handle a debug version that just talks to stdin/out. 184338032Speter */ 184438032Speter 184538032Speter curhost = NULL; 184638032Speter SmtpPhase = NULL; 184738032Speter mci = NULL; 184838032Speter 184938032Speter#if XDEBUG 185038032Speter { 185138032Speter char wbuf[MAXLINE]; 185238032Speter 185338032Speter /* make absolutely certain 0, 1, and 2 are in use */ 185490792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", 185590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 185690792Sgshapiro m->m_name); 185738032Speter checkfd012(wbuf); 185838032Speter } 185964562Sgshapiro#endif /* XDEBUG */ 186038032Speter 186138032Speter /* check for 8-bit available */ 186238032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 186338032Speter bitnset(M_7BITS, m->m_flags) && 186438032Speter (bitset(EF_DONT_MIME, e->e_flags) || 186538032Speter !(bitset(MM_MIME8BIT, MimeMode) || 186638032Speter (bitset(EF_IS_MIME, e->e_flags) && 186764562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 186838032Speter { 186964562Sgshapiro e->e_status = "5.6.3"; 187064562Sgshapiro usrerrenh(e->e_status, 187190792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 187238032Speter rcode = EX_DATAERR; 187338032Speter goto give_up; 187438032Speter } 187538032Speter 187638032Speter if (tTd(62, 8)) 187738032Speter checkfds("before delivery"); 187838032Speter 187938032Speter /* check for Local Person Communication -- not for mortals!!! */ 188038032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 188138032Speter { 188290792Sgshapiro#if _FFR_CACHE_LPC 188390792Sgshapiro if (clever) 188490792Sgshapiro { 188590792Sgshapiro /* flush any expired connections */ 188690792Sgshapiro (void) mci_scan(NULL); 188790792Sgshapiro 188890792Sgshapiro /* try to get a cached connection or just a slot */ 188990792Sgshapiro mci = mci_get(m->m_name, m); 189090792Sgshapiro if (mci->mci_host == NULL) 189190792Sgshapiro mci->mci_host = m->m_name; 189290792Sgshapiro CurHostName = mci->mci_host; 189390792Sgshapiro if (mci->mci_state != MCIS_CLOSED) 189490792Sgshapiro { 189590792Sgshapiro message("Using cached SMTP/LPC connection for %s...", 189690792Sgshapiro m->m_name); 189790792Sgshapiro mci->mci_deliveries++; 189890792Sgshapiro goto do_transfer; 189990792Sgshapiro } 190090792Sgshapiro } 190190792Sgshapiro else 190290792Sgshapiro { 190390792Sgshapiro mci = mci_new(e->e_rpool); 190490792Sgshapiro } 190590792Sgshapiro mci->mci_in = smioin; 190690792Sgshapiro mci->mci_out = smioout; 190790792Sgshapiro mci->mci_mailer = m; 190890792Sgshapiro mci->mci_host = m->m_name; 190990792Sgshapiro if (clever) 191090792Sgshapiro { 191190792Sgshapiro mci->mci_state = MCIS_OPENING; 191290792Sgshapiro mci_cache(mci); 191390792Sgshapiro } 191490792Sgshapiro else 191590792Sgshapiro mci->mci_state = MCIS_OPEN; 191690792Sgshapiro#else /* _FFR_CACHE_LPC */ 191790792Sgshapiro mci = mci_new(e->e_rpool); 191890792Sgshapiro mci->mci_in = smioin; 191990792Sgshapiro mci->mci_out = smioout; 192038032Speter mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; 192138032Speter mci->mci_mailer = m; 192290792Sgshapiro#endif /* _FFR_CACHE_LPC */ 192338032Speter } 192490792Sgshapiro else if (strcmp(m->m_mailer, "[IPC]") == 0) 192538032Speter { 192638032Speter register int i; 192738032Speter 192838032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 192938032Speter { 193064562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 193138032Speter rcode = EX_CONFIG; 193238032Speter goto give_up; 193338032Speter } 193438032Speter 193564562Sgshapiro# if NETUNIX 193664562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 193764562Sgshapiro { 193864562Sgshapiro curhost = CurHostName = "localhost"; 193964562Sgshapiro mux_path = pv[1]; 194064562Sgshapiro } 194164562Sgshapiro else 194264562Sgshapiro# endif /* NETUNIX */ 194364562Sgshapiro { 194464562Sgshapiro CurHostName = pv[1]; 194564562Sgshapiro curhost = hostsignature(m, pv[1]); 194664562Sgshapiro } 194738032Speter 194838032Speter if (curhost == NULL || curhost[0] == '\0') 194938032Speter { 195038032Speter syserr("null host signature for %s", pv[1]); 195138032Speter rcode = EX_CONFIG; 195238032Speter goto give_up; 195338032Speter } 195438032Speter 195538032Speter if (!clever) 195638032Speter { 195764562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 195838032Speter rcode = EX_CONFIG; 195938032Speter goto give_up; 196038032Speter } 196164562Sgshapiro if (pv[2] != NULL 196264562Sgshapiro# if NETUNIX 196364562Sgshapiro && mux_path == NULL 196464562Sgshapiro# endif /* NETUNIX */ 196564562Sgshapiro ) 196638032Speter { 196790792Sgshapiro port = htons((unsigned short) atoi(pv[2])); 196838032Speter if (port == 0) 196938032Speter { 197064562Sgshapiro# ifdef NO_GETSERVBYNAME 197164562Sgshapiro syserr("Invalid port number: %s", pv[2]); 197264562Sgshapiro# else /* NO_GETSERVBYNAME */ 197338032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 197438032Speter 197538032Speter if (sp == NULL) 197638032Speter syserr("Service %s unknown", pv[2]); 197738032Speter else 197838032Speter port = sp->s_port; 197964562Sgshapiro# endif /* NO_GETSERVBYNAME */ 198038032Speter } 198138032Speter } 198264562Sgshapiro 198364562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 198490792Sgshapiro if (TimeOuts.to_aconnect > 0) 198590792Sgshapiro enough = curtime() + TimeOuts.to_aconnect; 198638032Spetertryhost: 198764562Sgshapiro while (hostnum < nummxhosts) 198838032Speter { 198964562Sgshapiro char sep = ':'; 199064562Sgshapiro char *endp; 199138032Speter static char hostbuf[MAXNAME + 1]; 199238032Speter 199364562Sgshapiro# if NETINET6 199464562Sgshapiro if (*mxhosts[hostnum] == '[') 199538032Speter { 199664562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 199764562Sgshapiro if (endp != NULL) 199864562Sgshapiro endp = strpbrk(endp + 1, ":,"); 199964562Sgshapiro } 200064562Sgshapiro else 200164562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 200264562Sgshapiro# else /* NETINET6 */ 200364562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 200464562Sgshapiro# endif /* NETINET6 */ 200564562Sgshapiro if (endp != NULL) 200664562Sgshapiro { 200764562Sgshapiro sep = *endp; 200864562Sgshapiro *endp = '\0'; 200964562Sgshapiro } 201064562Sgshapiro 201190792Sgshapiro if (hostnum == 1 && skip_back != NULL) 201290792Sgshapiro { 201390792Sgshapiro /* 201490792Sgshapiro ** Coattail piggybacking is no longer an 201590792Sgshapiro ** option with the mail host next to be tried 201690792Sgshapiro ** no longer the lowest MX preference 201790792Sgshapiro ** (hostnum == 1 meaning we're on the second 201890792Sgshapiro ** preference). We do not try to coattail 201990792Sgshapiro ** piggyback more than the first MX preference. 202090792Sgshapiro ** Revert 'tochain' to last location for 202190792Sgshapiro ** coincidental piggybacking. This works this 202290792Sgshapiro ** easily because the q_tchain kept getting 202390792Sgshapiro ** added to the top of the linked list. 202490792Sgshapiro */ 202590792Sgshapiro 202690792Sgshapiro tochain = skip_back; 202790792Sgshapiro } 202890792Sgshapiro 202964562Sgshapiro if (*mxhosts[hostnum] == '\0') 203064562Sgshapiro { 203138032Speter syserr("deliver: null host name in signature"); 203264562Sgshapiro hostnum++; 203364562Sgshapiro if (endp != NULL) 203464562Sgshapiro *endp = sep; 203538032Speter continue; 203638032Speter } 203790792Sgshapiro (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 203890792Sgshapiro sizeof hostbuf); 203964562Sgshapiro hostnum++; 204064562Sgshapiro if (endp != NULL) 204164562Sgshapiro *endp = sep; 204238032Speter 204338032Speter /* see if we already know that this host is fried */ 204438032Speter CurHostName = hostbuf; 204538032Speter mci = mci_get(hostbuf, m); 204638032Speter if (mci->mci_state != MCIS_CLOSED) 204738032Speter { 204890792Sgshapiro char *type; 204990792Sgshapiro 205038032Speter if (tTd(11, 1)) 205138032Speter { 205290792Sgshapiro sm_dprintf("openmailer: "); 205390792Sgshapiro mci_dump(mci, false); 205438032Speter } 205538032Speter CurHostName = mci->mci_host; 205690792Sgshapiro if (bitnset(M_LMTP, m->m_flags)) 205790792Sgshapiro type = "L"; 205890792Sgshapiro else if (bitset(MCIF_ESMTP, mci->mci_flags)) 205990792Sgshapiro type = "ES"; 206090792Sgshapiro else 206190792Sgshapiro type = "S"; 206290792Sgshapiro message("Using cached %sMTP connection to %s via %s...", 206390792Sgshapiro type, hostbuf, m->m_name); 206464562Sgshapiro mci->mci_deliveries++; 206538032Speter break; 206638032Speter } 206738032Speter mci->mci_mailer = m; 206838032Speter if (mci->mci_exitstat != EX_OK) 206938032Speter { 207038032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 207190792Sgshapiro goodmxfound = true; 207238032Speter continue; 207338032Speter } 207438032Speter 207538032Speter if (mci_lock_host(mci) != EX_OK) 207638032Speter { 207738032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 207890792Sgshapiro goodmxfound = true; 207938032Speter continue; 208038032Speter } 208138032Speter 208238032Speter /* try the connection */ 208390792Sgshapiro sm_setproctitle(true, e, "%s %s: %s", 208464562Sgshapiro qid_printname(e), 208564562Sgshapiro hostbuf, "user open"); 208664562Sgshapiro# if NETUNIX 208764562Sgshapiro if (mux_path != NULL) 208864562Sgshapiro { 208938032Speter message("Connecting to %s via %s...", 209064562Sgshapiro mux_path, m->m_name); 209190792Sgshapiro i = makeconnection_ds((char *) mux_path, mci); 209264562Sgshapiro } 209338032Speter else 209464562Sgshapiro# endif /* NETUNIX */ 209564562Sgshapiro { 209664562Sgshapiro if (port == 0) 209764562Sgshapiro message("Connecting to %s via %s...", 209864562Sgshapiro hostbuf, m->m_name); 209964562Sgshapiro else 210064562Sgshapiro message("Connecting to %s port %d via %s...", 210164562Sgshapiro hostbuf, ntohs(port), 210264562Sgshapiro m->m_name); 210390792Sgshapiro i = makeconnection(hostbuf, port, mci, e, 210490792Sgshapiro enough); 210564562Sgshapiro } 210677349Sgshapiro mci->mci_errno = errno; 210738032Speter mci->mci_lastuse = curtime(); 210864562Sgshapiro mci->mci_deliveries = 0; 210938032Speter mci->mci_exitstat = i; 211064562Sgshapiro# if NAMED_BIND 211138032Speter mci->mci_herrno = h_errno; 211264562Sgshapiro# endif /* NAMED_BIND */ 211390792Sgshapiro 211490792Sgshapiro /* 211590792Sgshapiro ** Have we tried long enough to get a connection? 211690792Sgshapiro ** If yes, skip to the fallback MX hosts 211790792Sgshapiro ** (if existent). 211890792Sgshapiro */ 211990792Sgshapiro 212090792Sgshapiro if (enough > 0 && mci->mci_lastuse >= enough) 212190792Sgshapiro { 212290792Sgshapiro int h; 212390792Sgshapiro# if NAMED_BIND 212490792Sgshapiro extern int NumFallBackMXHosts; 212590792Sgshapiro# else /* NAMED_BIND */ 212690792Sgshapiro const int NumFallBackMXHosts = 0; 212790792Sgshapiro# endif /* NAMED_BIND */ 212890792Sgshapiro 212990792Sgshapiro if (hostnum < nummxhosts && LogLevel > 9) 213090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 213190792Sgshapiro "Timeout.to_aconnect occurred before exhausting all addresses"); 213290792Sgshapiro 213390792Sgshapiro /* turn off timeout if fallback available */ 213490792Sgshapiro if (NumFallBackMXHosts > 0) 213590792Sgshapiro enough = 0; 213690792Sgshapiro 213790792Sgshapiro /* skip to a fallback MX host */ 213890792Sgshapiro h = nummxhosts - NumFallBackMXHosts; 213990792Sgshapiro if (hostnum < h) 214090792Sgshapiro hostnum = h; 214190792Sgshapiro } 214238032Speter if (i == EX_OK) 214338032Speter { 214490792Sgshapiro goodmxfound = true; 214594334Sgshapiro markstats(e, firstto, STATS_CONNECT); 214638032Speter mci->mci_state = MCIS_OPENING; 214738032Speter mci_cache(mci); 214838032Speter if (TrafficLogFile != NULL) 214990792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 215090792Sgshapiro SM_TIME_DEFAULT, 215190792Sgshapiro "%05d === CONNECT %s\n", 215290792Sgshapiro (int) CurrentPid, 215390792Sgshapiro hostbuf); 215438032Speter break; 215538032Speter } 215638032Speter else 215738032Speter { 215864562Sgshapiro if (tTd(11, 1)) 215990792Sgshapiro sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 216090792Sgshapiro i, errno); 216138032Speter if (i == EX_TEMPFAIL) 216290792Sgshapiro goodmxfound = true; 216338032Speter mci_unlock_host(mci); 216438032Speter } 216538032Speter 216638032Speter /* enter status of this host */ 216738032Speter setstat(i); 216838032Speter 216938032Speter /* should print some message here for -v mode */ 217038032Speter } 217138032Speter if (mci == NULL) 217238032Speter { 217338032Speter syserr("deliver: no host name"); 217438032Speter rcode = EX_SOFTWARE; 217538032Speter goto give_up; 217638032Speter } 217738032Speter mci->mci_pid = 0; 217838032Speter } 217938032Speter else 218038032Speter { 218138032Speter /* flush any expired connections */ 218238032Speter (void) mci_scan(NULL); 218338032Speter mci = NULL; 218438032Speter 218538032Speter if (bitnset(M_LMTP, m->m_flags)) 218638032Speter { 218738032Speter /* try to get a cached connection */ 218838032Speter mci = mci_get(m->m_name, m); 218938032Speter if (mci->mci_host == NULL) 219038032Speter mci->mci_host = m->m_name; 219138032Speter CurHostName = mci->mci_host; 219238032Speter if (mci->mci_state != MCIS_CLOSED) 219338032Speter { 219438032Speter message("Using cached LMTP connection for %s...", 219538032Speter m->m_name); 219664562Sgshapiro mci->mci_deliveries++; 219738032Speter goto do_transfer; 219838032Speter } 219938032Speter } 220038032Speter 220138032Speter /* announce the connection to verbose listeners */ 220238032Speter if (host == NULL || host[0] == '\0') 220338032Speter message("Connecting to %s...", m->m_name); 220438032Speter else 220538032Speter message("Connecting to %s via %s...", host, m->m_name); 220638032Speter if (TrafficLogFile != NULL) 220738032Speter { 220838032Speter char **av; 220938032Speter 221090792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 221190792Sgshapiro "%05d === EXEC", (int) CurrentPid); 221238032Speter for (av = pv; *av != NULL; av++) 221390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 221490792Sgshapiro SM_TIME_DEFAULT, " %s", 221590792Sgshapiro *av); 221690792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 221790792Sgshapiro "\n"); 221838032Speter } 221938032Speter 222038032Speter#if XDEBUG 222138032Speter checkfd012("before creating mail pipe"); 222264562Sgshapiro#endif /* XDEBUG */ 222338032Speter 222438032Speter /* create a pipe to shove the mail through */ 222538032Speter if (pipe(mpvect) < 0) 222638032Speter { 222738032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 222890792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 222938032Speter if (tTd(11, 1)) 223090792Sgshapiro sm_dprintf("openmailer: NULL\n"); 223138032Speter rcode = EX_OSERR; 223238032Speter goto give_up; 223338032Speter } 223438032Speter 223538032Speter#if XDEBUG 223638032Speter /* make sure we didn't get one of the standard I/O files */ 223738032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 223838032Speter { 223938032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 224090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 224190792Sgshapiro mpvect[0], mpvect[1]); 224290792Sgshapiro printopenfds(true); 224338032Speter if (tTd(11, 1)) 224490792Sgshapiro sm_dprintf("openmailer: NULL\n"); 224538032Speter rcode = EX_OSERR; 224638032Speter goto give_up; 224738032Speter } 224838032Speter 224938032Speter /* make sure system call isn't dead meat */ 225038032Speter checkfdopen(mpvect[0], "mpvect[0]"); 225138032Speter checkfdopen(mpvect[1], "mpvect[1]"); 225238032Speter if (mpvect[0] == mpvect[1] || 225338032Speter (e->e_lockfp != NULL && 225490792Sgshapiro (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 225590792Sgshapiro NULL) || 225690792Sgshapiro mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 225790792Sgshapiro NULL)))) 225838032Speter { 225938032Speter if (e->e_lockfp == NULL) 226038032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 226190792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 226290792Sgshapiro m->m_name, mpvect[0], mpvect[1]); 226338032Speter else 226438032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 226590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 226690792Sgshapiro m->m_name, mpvect[0], mpvect[1], 226790792Sgshapiro sm_io_getinfo(e->e_lockfp, 226890792Sgshapiro SM_IO_WHAT_FD, NULL)); 226938032Speter } 227064562Sgshapiro#endif /* XDEBUG */ 227138032Speter 227264562Sgshapiro /* create a return pipe */ 227364562Sgshapiro if (pipe(rpvect) < 0) 227438032Speter { 227564562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 227690792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 227790792Sgshapiro m->m_name); 227864562Sgshapiro (void) close(mpvect[0]); 227964562Sgshapiro (void) close(mpvect[1]); 228064562Sgshapiro if (tTd(11, 1)) 228190792Sgshapiro sm_dprintf("openmailer: NULL\n"); 228264562Sgshapiro rcode = EX_OSERR; 228364562Sgshapiro goto give_up; 228438032Speter } 228564562Sgshapiro#if XDEBUG 228664562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 228764562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 228864562Sgshapiro#endif /* XDEBUG */ 228938032Speter 229038032Speter /* 229138032Speter ** Actually fork the mailer process. 229238032Speter ** DOFORK is clever about retrying. 229338032Speter ** 229438032Speter ** Dispose of SIGCHLD signal catchers that may be laying 229564562Sgshapiro ** around so that endmailer will get it. 229638032Speter */ 229738032Speter 229890792Sgshapiro if (e->e_xfp != NULL) /* for debugging */ 229990792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 230090792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 230190792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 230264562Sgshapiro 230364562Sgshapiro 230438032Speter DOFORK(FORK); 230538032Speter /* pid is set by DOFORK */ 230664562Sgshapiro 230738032Speter if (pid < 0) 230838032Speter { 230938032Speter /* failure */ 231038032Speter syserr("%s... openmailer(%s): cannot fork", 231190792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 231238032Speter (void) close(mpvect[0]); 231338032Speter (void) close(mpvect[1]); 231464562Sgshapiro (void) close(rpvect[0]); 231564562Sgshapiro (void) close(rpvect[1]); 231638032Speter if (tTd(11, 1)) 231790792Sgshapiro sm_dprintf("openmailer: NULL\n"); 231838032Speter rcode = EX_OSERR; 231938032Speter goto give_up; 232038032Speter } 232138032Speter else if (pid == 0) 232238032Speter { 232338032Speter int i; 232464562Sgshapiro int save_errno; 232590792Sgshapiro int sff; 232638032Speter int new_euid = NO_UID; 232738032Speter int new_ruid = NO_UID; 232838032Speter int new_gid = NO_GID; 232990792Sgshapiro char *user = NULL; 233038032Speter struct stat stb; 233138032Speter extern int DtableSize; 233238032Speter 233390792Sgshapiro CurrentPid = getpid(); 233490792Sgshapiro 233580785Sgshapiro /* clear the events to turn off SIGALRMs */ 233690792Sgshapiro sm_clear_events(); 233780785Sgshapiro 233877349Sgshapiro /* Reset global flags */ 233977349Sgshapiro RestartRequest = NULL; 234090792Sgshapiro RestartWorkGroup = false; 234177349Sgshapiro ShutdownRequest = NULL; 234277349Sgshapiro PendingSignal = 0; 234377349Sgshapiro 234438032Speter if (e->e_lockfp != NULL) 234590792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, 234690792Sgshapiro SM_IO_WHAT_FD, 234790792Sgshapiro NULL)); 234838032Speter 234938032Speter /* child -- set up input & exec mailer */ 235090792Sgshapiro (void) sm_signal(SIGALRM, sm_signal_noop); 235190792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 235290792Sgshapiro (void) sm_signal(SIGHUP, SIG_IGN); 235390792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 235490792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 235580785Sgshapiro# ifdef SIGUSR1 235690792Sgshapiro (void) sm_signal(SIGUSR1, sm_signal_noop); 235780785Sgshapiro# endif /* SIGUSR1 */ 235838032Speter 235938032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 236038032Speter stb.st_mode = 0; 236138032Speter 236264562Sgshapiro# if HASSETUSERCONTEXT 236338032Speter /* 236438032Speter ** Set user resources. 236538032Speter */ 236638032Speter 236738032Speter if (contextaddr != NULL) 236838032Speter { 2369110560Sgshapiro int sucflags; 237038032Speter struct passwd *pwd; 237138032Speter 237238032Speter if (contextaddr->q_ruser != NULL) 237338032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 237438032Speter else 237538032Speter pwd = sm_getpwnam(contextaddr->q_user); 2376110560Sgshapiro sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2377110560Sgshapiro#ifdef LOGIN_SETMAC 2378110560Sgshapiro sucflags |= LOGIN_SETMAC; 2379110560Sgshapiro#endif /* LOGIN_SETMAC */ 2380102528Sgshapiro if (pwd != NULL && 2381102528Sgshapiro setusercontext(NULL, pwd, pwd->pw_uid, 2382110560Sgshapiro sucflags) == -1 && 2383102528Sgshapiro suidwarn) 2384102528Sgshapiro { 2385102528Sgshapiro syserr("openmailer: setusercontext() failed"); 2386102528Sgshapiro exit(EX_TEMPFAIL); 2387102528Sgshapiro } 238838032Speter } 238964562Sgshapiro# endif /* HASSETUSERCONTEXT */ 239038032Speter 239190792Sgshapiro#if HASNICE 239238032Speter /* tweak niceness */ 239338032Speter if (m->m_nice != 0) 239464562Sgshapiro (void) nice(m->m_nice); 239590792Sgshapiro#endif /* HASNICE */ 239638032Speter 239738032Speter /* reset group id */ 239838032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 239938032Speter new_gid = m->m_gid; 240038032Speter else if (bitset(S_ISGID, stb.st_mode)) 240138032Speter new_gid = stb.st_gid; 240238032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 240338032Speter { 240438032Speter if (!DontInitGroups) 240538032Speter { 240690792Sgshapiro user = ctladdr->q_ruser; 240790792Sgshapiro if (user == NULL) 240890792Sgshapiro user = ctladdr->q_user; 240938032Speter 241090792Sgshapiro if (initgroups(user, 241190792Sgshapiro ctladdr->q_gid) == -1 241290792Sgshapiro && suidwarn) 241364562Sgshapiro { 241438032Speter syserr("openmailer: initgroups(%s, %d) failed", 241590792Sgshapiro user, ctladdr->q_gid); 241664562Sgshapiro exit(EX_TEMPFAIL); 241764562Sgshapiro } 241838032Speter } 241938032Speter else 242038032Speter { 242138032Speter GIDSET_T gidset[1]; 242238032Speter 242338032Speter gidset[0] = ctladdr->q_gid; 242490792Sgshapiro if (setgroups(1, gidset) == -1 242590792Sgshapiro && suidwarn) 242664562Sgshapiro { 242738032Speter syserr("openmailer: setgroups() failed"); 242864562Sgshapiro exit(EX_TEMPFAIL); 242964562Sgshapiro } 243038032Speter } 243138032Speter new_gid = ctladdr->q_gid; 243238032Speter } 243338032Speter else 243438032Speter { 243538032Speter if (!DontInitGroups) 243638032Speter { 243790792Sgshapiro user = DefUser; 243890792Sgshapiro if (initgroups(DefUser, DefGid) == -1 && 243990792Sgshapiro suidwarn) 244064562Sgshapiro { 244138032Speter syserr("openmailer: initgroups(%s, %d) failed", 244290792Sgshapiro DefUser, DefGid); 244364562Sgshapiro exit(EX_TEMPFAIL); 244464562Sgshapiro } 244538032Speter } 244638032Speter else 244738032Speter { 244838032Speter GIDSET_T gidset[1]; 244938032Speter 245038032Speter gidset[0] = DefGid; 245190792Sgshapiro if (setgroups(1, gidset) == -1 245290792Sgshapiro && suidwarn) 245364562Sgshapiro { 245438032Speter syserr("openmailer: setgroups() failed"); 245564562Sgshapiro exit(EX_TEMPFAIL); 245664562Sgshapiro } 245738032Speter } 245838032Speter if (m->m_gid == 0) 245938032Speter new_gid = DefGid; 246038032Speter else 246138032Speter new_gid = m->m_gid; 246238032Speter } 246364562Sgshapiro if (new_gid != NO_GID) 246464562Sgshapiro { 246564562Sgshapiro if (RunAsUid != 0 && 246664562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 246764562Sgshapiro new_gid != getgid() && 246864562Sgshapiro new_gid != getegid()) 246964562Sgshapiro { 247064562Sgshapiro /* Only root can change the gid */ 247190792Sgshapiro syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d", 247290792Sgshapiro (int) RunAsUid, (int) new_gid, 247390792Sgshapiro (int) getgid(), (int) getegid()); 247464562Sgshapiro exit(EX_TEMPFAIL); 247564562Sgshapiro } 247638032Speter 247764562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 247864562Sgshapiro { 247964562Sgshapiro syserr("openmailer: setgid(%ld) failed", 248064562Sgshapiro (long) new_gid); 248164562Sgshapiro exit(EX_TEMPFAIL); 248264562Sgshapiro } 248364562Sgshapiro } 248464562Sgshapiro 248564562Sgshapiro /* change root to some "safe" directory */ 248664562Sgshapiro if (m->m_rootdir != NULL) 248764562Sgshapiro { 248898121Sgshapiro expand(m->m_rootdir, cbuf, sizeof cbuf, e); 248964562Sgshapiro if (tTd(11, 20)) 249090792Sgshapiro sm_dprintf("openmailer: chroot %s\n", 249198121Sgshapiro cbuf); 249298121Sgshapiro if (chroot(cbuf) < 0) 249364562Sgshapiro { 249464562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 249598121Sgshapiro cbuf); 249664562Sgshapiro exit(EX_TEMPFAIL); 249764562Sgshapiro } 249864562Sgshapiro if (chdir("/") < 0) 249964562Sgshapiro { 250064562Sgshapiro syserr("openmailer: cannot chdir(/)"); 250164562Sgshapiro exit(EX_TEMPFAIL); 250264562Sgshapiro } 250364562Sgshapiro } 250464562Sgshapiro 250538032Speter /* reset user id */ 250638032Speter endpwent(); 250790792Sgshapiro sm_mbdb_terminate(); 250838032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 250980785Sgshapiro { 251038032Speter new_euid = m->m_uid; 251180785Sgshapiro 251280785Sgshapiro /* 251380785Sgshapiro ** Undo the effects of the uid change in main 251480785Sgshapiro ** for signal handling. The real uid may 251580785Sgshapiro ** be used by mailer in adding a "From " 251680785Sgshapiro ** line. 251780785Sgshapiro */ 251880785Sgshapiro 251980785Sgshapiro if (RealUid != 0 && RealUid != getuid()) 252090792Sgshapiro { 252190792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 252290792Sgshapiro# if HASSETREUID 252390792Sgshapiro if (setreuid(RealUid, geteuid()) < 0) 252490792Sgshapiro { 252590792Sgshapiro syserr("openmailer: setreuid(%d, %d) failed", 252690792Sgshapiro (int) RealUid, (int) geteuid()); 252790792Sgshapiro exit(EX_OSERR); 252890792Sgshapiro } 252990792Sgshapiro# endif /* HASSETREUID */ 253090792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 253190792Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 253280785Sgshapiro new_ruid = RealUid; 253390792Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 253490792Sgshapiro } 253580785Sgshapiro } 253638032Speter else if (bitset(S_ISUID, stb.st_mode)) 253738032Speter new_ruid = stb.st_uid; 253838032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 253938032Speter new_ruid = ctladdr->q_uid; 254038032Speter else if (m->m_uid != 0) 254138032Speter new_ruid = m->m_uid; 254238032Speter else 254338032Speter new_ruid = DefUid; 254494334Sgshapiro 254594334Sgshapiro# if _FFR_USE_SETLOGIN 254694334Sgshapiro /* run disconnected from terminal and set login name */ 254794334Sgshapiro if (setsid() >= 0 && 254894334Sgshapiro ctladdr != NULL && ctladdr->q_uid != 0 && 254994334Sgshapiro new_euid == ctladdr->q_uid) 255094334Sgshapiro { 255194334Sgshapiro struct passwd *pwd; 255294334Sgshapiro 255394334Sgshapiro pwd = sm_getpwuid(ctladdr->q_uid); 255494334Sgshapiro if (pwd != NULL && suidwarn) 255594334Sgshapiro (void) setlogin(pwd->pw_name); 255694334Sgshapiro endpwent(); 255794334Sgshapiro } 255894334Sgshapiro# endif /* _FFR_USE_SETLOGIN */ 255994334Sgshapiro 256038032Speter if (new_euid != NO_UID) 256138032Speter { 256264562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 256364562Sgshapiro { 256464562Sgshapiro /* Only root can change the uid */ 256590792Sgshapiro syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d", 256690792Sgshapiro (int) new_euid, (int) RunAsUid); 256764562Sgshapiro exit(EX_TEMPFAIL); 256864562Sgshapiro } 256964562Sgshapiro 257038032Speter vendor_set_uid(new_euid); 257164562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 257238032Speter if (seteuid(new_euid) < 0 && suidwarn) 257364562Sgshapiro { 257438032Speter syserr("openmailer: seteuid(%ld) failed", 257590792Sgshapiro (long) new_euid); 257664562Sgshapiro exit(EX_TEMPFAIL); 257764562Sgshapiro } 257864562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 257964562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 258038032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 258164562Sgshapiro { 258238032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 258390792Sgshapiro (long) new_ruid, (long) new_euid); 258464562Sgshapiro exit(EX_TEMPFAIL); 258564562Sgshapiro } 258664562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 258764562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETUID 258838032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 258964562Sgshapiro { 259038032Speter syserr("openmailer: setuid(%ld) failed", 259190792Sgshapiro (long) new_euid); 259264562Sgshapiro exit(EX_TEMPFAIL); 259364562Sgshapiro } 259464562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETUID */ 259538032Speter } 259638032Speter else if (new_ruid != NO_UID) 259738032Speter { 259838032Speter vendor_set_uid(new_ruid); 259938032Speter if (setuid(new_ruid) < 0 && suidwarn) 260064562Sgshapiro { 260138032Speter syserr("openmailer: setuid(%ld) failed", 260290792Sgshapiro (long) new_ruid); 260364562Sgshapiro exit(EX_TEMPFAIL); 260464562Sgshapiro } 260538032Speter } 260638032Speter 260738032Speter if (tTd(11, 2)) 260890792Sgshapiro sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 260990792Sgshapiro (int) getuid(), (int) geteuid(), 261090792Sgshapiro (int) getgid(), (int) getegid()); 261138032Speter 261238032Speter /* move into some "safe" directory */ 261338032Speter if (m->m_execdir != NULL) 261438032Speter { 261538032Speter char *q; 261638032Speter 261738032Speter for (p = m->m_execdir; p != NULL; p = q) 261838032Speter { 261938032Speter q = strchr(p, ':'); 262038032Speter if (q != NULL) 262138032Speter *q = '\0'; 262298121Sgshapiro expand(p, cbuf, sizeof cbuf, e); 262338032Speter if (q != NULL) 262438032Speter *q++ = ':'; 262538032Speter if (tTd(11, 20)) 262690792Sgshapiro sm_dprintf("openmailer: trydir %s\n", 262798121Sgshapiro cbuf); 262898121Sgshapiro if (cbuf[0] != '\0' && 262998121Sgshapiro chdir(cbuf) >= 0) 263038032Speter break; 263138032Speter } 263238032Speter } 263338032Speter 263490792Sgshapiro /* Check safety of program to be run */ 263590792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 263690792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, 263790792Sgshapiro DontBlameSendmail)) 263890792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 263990792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 264090792Sgshapiro DontBlameSendmail)) 264190792Sgshapiro sff |= SFF_NOPATHCHECK; 264290792Sgshapiro else 264390792Sgshapiro sff |= SFF_SAFEDIRPATH; 264490792Sgshapiro ret = safefile(m->m_mailer, getuid(), getgid(), 264590792Sgshapiro user, sff, 0, NULL); 264690792Sgshapiro if (ret != 0) 264790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 264890792Sgshapiro "Warning: program %s unsafe: %s", 264990792Sgshapiro m->m_mailer, sm_errstring(ret)); 265090792Sgshapiro 265138032Speter /* arrange to filter std & diag output of command */ 265264562Sgshapiro (void) close(rpvect[0]); 265364562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 265438032Speter { 265564562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 265664562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 265764562Sgshapiro m->m_name, rpvect[1]); 265864562Sgshapiro _exit(EX_OSERR); 265938032Speter } 266064562Sgshapiro (void) close(rpvect[1]); 266164562Sgshapiro 266238032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 266338032Speter { 266438032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 266590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 266690792Sgshapiro m->m_name); 266738032Speter _exit(EX_OSERR); 266838032Speter } 266938032Speter 267038032Speter /* arrange to get standard input */ 267138032Speter (void) close(mpvect[1]); 267238032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 267338032Speter { 267438032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 267590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 267690792Sgshapiro m->m_name, mpvect[0]); 267738032Speter _exit(EX_OSERR); 267838032Speter } 267938032Speter (void) close(mpvect[0]); 268038032Speter 268138032Speter /* arrange for all the files to be closed */ 268238032Speter for (i = 3; i < DtableSize; i++) 268338032Speter { 268438032Speter register int j; 268538032Speter 268638032Speter if ((j = fcntl(i, F_GETFD, 0)) != -1) 268764562Sgshapiro (void) fcntl(i, F_SETFD, 268864562Sgshapiro j | FD_CLOEXEC); 268938032Speter } 269038032Speter 269194334Sgshapiro# if !_FFR_USE_SETLOGIN 269238032Speter /* run disconnected from terminal */ 269338032Speter (void) setsid(); 269494334Sgshapiro# endif /* !_FFR_USE_SETLOGIN */ 269538032Speter 269638032Speter /* try to execute the mailer */ 269764562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 269864562Sgshapiro (ARGV_T) UserEnviron); 269964562Sgshapiro save_errno = errno; 270038032Speter syserr("Cannot exec %s", m->m_mailer); 270138032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 270264562Sgshapiro transienterror(save_errno)) 270338032Speter _exit(EX_OSERR); 270438032Speter _exit(EX_UNAVAILABLE); 270538032Speter } 270638032Speter 270738032Speter /* 270838032Speter ** Set up return value. 270938032Speter */ 271038032Speter 271138032Speter if (mci == NULL) 271238032Speter { 271390792Sgshapiro if (clever) 271490792Sgshapiro { 271590792Sgshapiro /* 271690792Sgshapiro ** Allocate from general heap, not 271790792Sgshapiro ** envelope rpool, because this mci 271890792Sgshapiro ** is going to be cached. 271990792Sgshapiro */ 272090792Sgshapiro 272190792Sgshapiro mci = mci_new(NULL); 272290792Sgshapiro } 272390792Sgshapiro else 272490792Sgshapiro { 272590792Sgshapiro /* 272690792Sgshapiro ** Prevent a storage leak by allocating 272790792Sgshapiro ** this from the envelope rpool. 272890792Sgshapiro */ 272990792Sgshapiro 273090792Sgshapiro mci = mci_new(e->e_rpool); 273190792Sgshapiro } 273238032Speter } 273338032Speter mci->mci_mailer = m; 273438032Speter if (clever) 273538032Speter { 273638032Speter mci->mci_state = MCIS_OPENING; 273738032Speter mci_cache(mci); 273838032Speter } 273938032Speter else 274038032Speter { 274138032Speter mci->mci_state = MCIS_OPEN; 274238032Speter } 274338032Speter mci->mci_pid = pid; 274438032Speter (void) close(mpvect[0]); 274590792Sgshapiro mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 274690792Sgshapiro (void *) &(mpvect[1]), SM_IO_WRONLY, 274790792Sgshapiro NULL); 274838032Speter if (mci->mci_out == NULL) 274938032Speter { 275038032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 275190792Sgshapiro mpvect[1]); 275238032Speter (void) close(mpvect[1]); 275364562Sgshapiro (void) close(rpvect[0]); 275464562Sgshapiro (void) close(rpvect[1]); 275538032Speter rcode = EX_OSERR; 275638032Speter goto give_up; 275738032Speter } 275864562Sgshapiro 275964562Sgshapiro (void) close(rpvect[1]); 276090792Sgshapiro mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 276190792Sgshapiro (void *) &(rpvect[0]), SM_IO_RDONLY, 276290792Sgshapiro NULL); 276364562Sgshapiro if (mci->mci_in == NULL) 276438032Speter { 276564562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 276664562Sgshapiro mpvect[1]); 276764562Sgshapiro (void) close(rpvect[0]); 276890792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 276964562Sgshapiro mci->mci_out = NULL; 277064562Sgshapiro rcode = EX_OSERR; 277164562Sgshapiro goto give_up; 277238032Speter } 277338032Speter } 277438032Speter 277538032Speter /* 277638032Speter ** If we are in SMTP opening state, send initial protocol. 277738032Speter */ 277838032Speter 277938032Speter if (bitnset(M_7BITS, m->m_flags) && 278038032Speter (!clever || mci->mci_state == MCIS_OPENING)) 278138032Speter mci->mci_flags |= MCIF_7BIT; 278238032Speter if (clever && mci->mci_state != MCIS_CLOSED) 278338032Speter { 278490792Sgshapiro# if STARTTLS || SASL 278590792Sgshapiro int dotpos; 278690792Sgshapiro char *srvname; 278790792Sgshapiro extern SOCKADDR CurHostAddr; 278890792Sgshapiro# endif /* STARTTLS || SASL */ 278990792Sgshapiro 279090792Sgshapiro# if SASL 279171345Sgshapiro# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 279290792Sgshapiro# endif /* SASL */ 279364562Sgshapiro# if STARTTLS 279471345Sgshapiro# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 279564562Sgshapiro# endif /* STARTTLS */ 279671345Sgshapiro# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 279771345Sgshapiro# define SET_HELO(f) f |= MCIF_ONLY_EHLO 279871345Sgshapiro# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 279938032Speter 280090792Sgshapiro# if STARTTLS || SASL 280190792Sgshapiro /* don't use CurHostName, it is changed in many places */ 280290792Sgshapiro if (mci->mci_host != NULL) 280390792Sgshapiro { 280490792Sgshapiro srvname = mci->mci_host; 280590792Sgshapiro dotpos = strlen(srvname) - 1; 280690792Sgshapiro if (dotpos >= 0) 280790792Sgshapiro { 280890792Sgshapiro if (srvname[dotpos] == '.') 280990792Sgshapiro srvname[dotpos] = '\0'; 281090792Sgshapiro else 281190792Sgshapiro dotpos = -1; 281290792Sgshapiro } 281390792Sgshapiro } 281490792Sgshapiro else if (mci->mci_mailer != NULL) 281590792Sgshapiro { 281690792Sgshapiro srvname = mci->mci_mailer->m_name; 281790792Sgshapiro dotpos = -1; 281890792Sgshapiro } 281990792Sgshapiro else 282090792Sgshapiro { 282190792Sgshapiro srvname = "local"; 282290792Sgshapiro dotpos = -1; 282390792Sgshapiro } 282471345Sgshapiro 282590792Sgshapiro /* don't set {server_name} to NULL or "": see getauth() */ 282690792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 282790792Sgshapiro srvname); 282864562Sgshapiro 282990792Sgshapiro /* CurHostAddr is set by makeconnection() and mci_get() */ 283090792Sgshapiro if (CurHostAddr.sa.sa_family != 0) 283190792Sgshapiro { 283290792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, 283390792Sgshapiro macid("{server_addr}"), 283490792Sgshapiro anynet_ntoa(&CurHostAddr)); 283590792Sgshapiro } 283690792Sgshapiro else if (mci->mci_mailer != NULL) 283790792Sgshapiro { 283890792Sgshapiro /* mailer name is unique, use it as address */ 283990792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 284090792Sgshapiro macid("{server_addr}"), 284190792Sgshapiro mci->mci_mailer->m_name); 284290792Sgshapiro } 284390792Sgshapiro else 284490792Sgshapiro { 284590792Sgshapiro /* don't set it to NULL or "": see getauth() */ 284690792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 284790792Sgshapiro macid("{server_addr}"), "0"); 284890792Sgshapiro } 284990792Sgshapiro 285090792Sgshapiro /* undo change of srvname (mci->mci_host) */ 285190792Sgshapiro if (dotpos >= 0) 285290792Sgshapiro srvname[dotpos] = '.'; 285390792Sgshapiro 285490792Sgshapiroreconnect: /* after switching to an encrypted connection */ 285590792Sgshapiro# endif /* STARTTLS || SASL */ 285690792Sgshapiro 285790792Sgshapiro /* set the current connection information */ 285890792Sgshapiro e->e_mci = mci; 285964562Sgshapiro# if SASL 286064562Sgshapiro mci->mci_saslcap = NULL; 286164562Sgshapiro# endif /* SASL */ 286271345Sgshapiro smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 286371345Sgshapiro CLR_HELO(mci->mci_flags); 286464562Sgshapiro 286590792Sgshapiro if (IS_DLVR_RETURN(e)) 286690792Sgshapiro { 286790792Sgshapiro /* 286890792Sgshapiro ** Check whether other side can deliver e-mail 286990792Sgshapiro ** fast enough 287090792Sgshapiro */ 287190792Sgshapiro 287290792Sgshapiro if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 287390792Sgshapiro { 287490792Sgshapiro e->e_status = "5.4.7"; 287590792Sgshapiro usrerrenh(e->e_status, 287690792Sgshapiro "554 Server does not support Deliver By"); 287790792Sgshapiro rcode = EX_UNAVAILABLE; 287890792Sgshapiro goto give_up; 287990792Sgshapiro } 288090792Sgshapiro if (e->e_deliver_by > 0 && 288190792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime) < 288290792Sgshapiro mci->mci_min_by) 288390792Sgshapiro { 288490792Sgshapiro e->e_status = "5.4.7"; 288590792Sgshapiro usrerrenh(e->e_status, 288690792Sgshapiro "554 Message can't be delivered in time; %ld < %ld", 288790792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime), 288890792Sgshapiro mci->mci_min_by); 288990792Sgshapiro rcode = EX_UNAVAILABLE; 289090792Sgshapiro goto give_up; 289190792Sgshapiro } 289290792Sgshapiro } 289390792Sgshapiro 289464562Sgshapiro# if STARTTLS 289564562Sgshapiro /* first TLS then AUTH to provide a security layer */ 289671345Sgshapiro if (mci->mci_state != MCIS_CLOSED && 289771345Sgshapiro !DONE_STARTTLS(mci->mci_flags)) 289864562Sgshapiro { 289964562Sgshapiro int olderrors; 290064562Sgshapiro bool usetls; 290164562Sgshapiro bool saveQuickAbort = QuickAbort; 290264562Sgshapiro bool saveSuprErrs = SuprErrs; 290371345Sgshapiro char *host = NULL; 290464562Sgshapiro 290564562Sgshapiro rcode = EX_OK; 290664562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 290790792Sgshapiro if (usetls) 290890792Sgshapiro usetls = !iscltflgset(e, D_NOTLS); 290966494Sgshapiro 291064562Sgshapiro if (usetls) 291164562Sgshapiro { 291290792Sgshapiro host = macvalue(macid("{server_name}"), e); 291364562Sgshapiro olderrors = Errors; 291490792Sgshapiro QuickAbort = false; 291590792Sgshapiro SuprErrs = true; 2916102528Sgshapiro if (rscheck("try_tls", host, NULL, e, 2917102528Sgshapiro RSF_RMCOMM, 7, host, NOQID) != EX_OK 291864562Sgshapiro || Errors > olderrors) 291990792Sgshapiro usetls = false; 292064562Sgshapiro SuprErrs = saveSuprErrs; 292164562Sgshapiro QuickAbort = saveQuickAbort; 292264562Sgshapiro } 292364562Sgshapiro 292464562Sgshapiro if (usetls) 292564562Sgshapiro { 292664562Sgshapiro if ((rcode = starttls(m, mci, e)) == EX_OK) 292764562Sgshapiro { 292864562Sgshapiro /* start again without STARTTLS */ 292964562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 293064562Sgshapiro } 293164562Sgshapiro else 293264562Sgshapiro { 293364562Sgshapiro char *s; 293464562Sgshapiro 293564562Sgshapiro /* 293664562Sgshapiro ** TLS negotation failed, what to do? 293764562Sgshapiro ** fall back to unencrypted connection 293864562Sgshapiro ** or abort? How to decide? 293964562Sgshapiro ** set a macro and call a ruleset. 294064562Sgshapiro */ 294190792Sgshapiro 294264562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 294364562Sgshapiro switch (rcode) 294464562Sgshapiro { 294564562Sgshapiro case EX_TEMPFAIL: 294664562Sgshapiro s = "TEMP"; 294764562Sgshapiro break; 294864562Sgshapiro case EX_USAGE: 294964562Sgshapiro s = "USAGE"; 295064562Sgshapiro break; 295164562Sgshapiro case EX_PROTOCOL: 295264562Sgshapiro s = "PROTOCOL"; 295364562Sgshapiro break; 295464562Sgshapiro case EX_SOFTWARE: 295564562Sgshapiro s = "SOFTWARE"; 295664562Sgshapiro break; 295764562Sgshapiro 295864562Sgshapiro /* everything else is a failure */ 295964562Sgshapiro default: 296064562Sgshapiro s = "FAILURE"; 296164562Sgshapiro rcode = EX_TEMPFAIL; 296264562Sgshapiro } 296390792Sgshapiro macdefine(&e->e_macro, A_PERM, 296490792Sgshapiro macid("{verify}"), s); 296564562Sgshapiro } 296664562Sgshapiro } 296764562Sgshapiro else 296890792Sgshapiro macdefine(&e->e_macro, A_PERM, 296990792Sgshapiro macid("{verify}"), "NONE"); 297064562Sgshapiro olderrors = Errors; 297190792Sgshapiro QuickAbort = false; 297290792Sgshapiro SuprErrs = true; 297364562Sgshapiro 297464562Sgshapiro /* 297564562Sgshapiro ** rcode == EX_SOFTWARE is special: 297664562Sgshapiro ** the TLS negotation failed 297764562Sgshapiro ** we have to drop the connection no matter what 297864562Sgshapiro ** However, we call tls_server to give it the chance 297964562Sgshapiro ** to log the problem and return an appropriate 298064562Sgshapiro ** error code. 298164562Sgshapiro */ 298290792Sgshapiro 298364562Sgshapiro if (rscheck("tls_server", 298490792Sgshapiro macvalue(macid("{verify}"), e), 2985102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 2986102528Sgshapiro host, NOQID) != EX_OK || 298764562Sgshapiro Errors > olderrors || 298864562Sgshapiro rcode == EX_SOFTWARE) 298964562Sgshapiro { 299064562Sgshapiro char enhsc[ENHSCLEN]; 299164562Sgshapiro extern char MsgBuf[]; 299264562Sgshapiro 299364562Sgshapiro if (ISSMTPCODE(MsgBuf) && 299464562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 299564562Sgshapiro { 299690792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 299790792Sgshapiro MsgBuf); 299864562Sgshapiro } 299964562Sgshapiro else 300064562Sgshapiro { 300164562Sgshapiro p = "403 4.7.0 server not authenticated."; 300290792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 300390792Sgshapiro sizeof enhsc); 300464562Sgshapiro } 300564562Sgshapiro SuprErrs = saveSuprErrs; 300664562Sgshapiro QuickAbort = saveQuickAbort; 300764562Sgshapiro 300864562Sgshapiro if (rcode == EX_SOFTWARE) 300964562Sgshapiro { 301064562Sgshapiro /* drop the connection */ 301164562Sgshapiro mci->mci_state = MCIS_QUITING; 301264562Sgshapiro if (mci->mci_in != NULL) 301364562Sgshapiro { 301490792Sgshapiro (void) sm_io_close(mci->mci_in, 301590792Sgshapiro SM_TIME_DEFAULT); 301664562Sgshapiro mci->mci_in = NULL; 301764562Sgshapiro } 301864562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 301964562Sgshapiro (void) endmailer(mci, e, pv); 302064562Sgshapiro } 302164562Sgshapiro else 302264562Sgshapiro { 302364562Sgshapiro /* abort transfer */ 302464562Sgshapiro smtpquit(m, mci, e); 302564562Sgshapiro } 302664562Sgshapiro 302771345Sgshapiro /* avoid bogus error msg */ 302871345Sgshapiro mci->mci_errno = 0; 302971345Sgshapiro 303064562Sgshapiro /* temp or permanent failure? */ 303164562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 303264562Sgshapiro : EX_UNAVAILABLE; 303390792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 303464562Sgshapiro 303564562Sgshapiro /* 303664562Sgshapiro ** hack to get the error message into 303764562Sgshapiro ** the envelope (done in giveresponse()) 303864562Sgshapiro */ 303990792Sgshapiro 304090792Sgshapiro (void) sm_strlcpy(SmtpError, p, 304190792Sgshapiro sizeof SmtpError); 304264562Sgshapiro } 304364562Sgshapiro QuickAbort = saveQuickAbort; 304464562Sgshapiro SuprErrs = saveSuprErrs; 304571345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 304671345Sgshapiro mci->mci_state != MCIS_CLOSED) 304764562Sgshapiro { 304871345Sgshapiro SET_HELO(mci->mci_flags); 304964562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 305064562Sgshapiro goto reconnect; 305164562Sgshapiro } 305264562Sgshapiro } 305364562Sgshapiro# endif /* STARTTLS */ 305464562Sgshapiro# if SASL 305564562Sgshapiro /* if other server supports authentication let's authenticate */ 305664562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 305764562Sgshapiro mci->mci_saslcap != NULL && 305890792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 305964562Sgshapiro { 306090792Sgshapiro /* Should we require some minimum authentication? */ 306190792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 306264562Sgshapiro { 306364562Sgshapiro int result; 306490792Sgshapiro sasl_ssf_t *ssf = NULL; 306564562Sgshapiro 306690792Sgshapiro /* Get security strength (features) */ 306764562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 306898121Sgshapiro# if SASL >= 20000 306998121Sgshapiro (const void **) &ssf); 307098121Sgshapiro# else /* SASL >= 20000 */ 307164562Sgshapiro (void **) &ssf); 307298121Sgshapiro# endif /* SASL >= 20000 */ 307390792Sgshapiro 307490792Sgshapiro /* XXX authid? */ 307564562Sgshapiro if (LogLevel > 9) 307664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 307790792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 307864562Sgshapiro mci->mci_host, 307990792Sgshapiro macvalue(macid("{auth_type}"), e), 308090792Sgshapiro result == SASL_OK ? *ssf : 0); 308177349Sgshapiro 308264562Sgshapiro /* 308390792Sgshapiro ** Only switch to encrypted connection 308464562Sgshapiro ** if a security layer has been negotiated 308564562Sgshapiro */ 308690792Sgshapiro 308764562Sgshapiro if (result == SASL_OK && *ssf > 0) 308864562Sgshapiro { 308964562Sgshapiro /* 309090792Sgshapiro ** Convert I/O layer to use SASL. 309190792Sgshapiro ** If the call fails, the connection 309290792Sgshapiro ** is aborted. 309364562Sgshapiro */ 309490792Sgshapiro 309590792Sgshapiro if (sfdcsasl(&mci->mci_in, 309690792Sgshapiro &mci->mci_out, 309764562Sgshapiro mci->mci_conn) == 0) 309864562Sgshapiro { 309964562Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 310090792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 310190792Sgshapiro MCIF_ONLY_EHLO; 310264562Sgshapiro goto reconnect; 310364562Sgshapiro } 310490792Sgshapiro syserr("AUTH TLS switch failed in client"); 310564562Sgshapiro } 310664562Sgshapiro /* else? XXX */ 310764562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 310864562Sgshapiro 310964562Sgshapiro } 311090792Sgshapiro else if (ret == EX_TEMPFAIL) 311190792Sgshapiro { 311290792Sgshapiro if (LogLevel > 8) 311390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 311490792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 311590792Sgshapiro mci->mci_host); 311690792Sgshapiro smtpquit(m, mci, e); 311790792Sgshapiro 311890792Sgshapiro /* avoid bogus error msg */ 311990792Sgshapiro mci->mci_errno = 0; 312090792Sgshapiro rcode = EX_TEMPFAIL; 312190792Sgshapiro mci_setstat(mci, rcode, "4.7.1", p); 312290792Sgshapiro 312390792Sgshapiro /* 312490792Sgshapiro ** hack to get the error message into 312590792Sgshapiro ** the envelope (done in giveresponse()) 312690792Sgshapiro */ 312790792Sgshapiro 312890792Sgshapiro (void) sm_strlcpy(SmtpError, 312990792Sgshapiro "Temporary AUTH failure", 313090792Sgshapiro sizeof SmtpError); 313190792Sgshapiro } 313264562Sgshapiro } 313364562Sgshapiro# endif /* SASL */ 313438032Speter } 313538032Speter 313664562Sgshapiro 313738032Speterdo_transfer: 313838032Speter /* clear out per-message flags from connection structure */ 313938032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 314038032Speter 314138032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 314238032Speter !bitset(EF_DONT_MIME, e->e_flags) && 314338032Speter bitnset(M_7BITS, m->m_flags)) 314438032Speter mci->mci_flags |= MCIF_CVT8TO7; 314538032Speter 314638032Speter#if MIME7TO8 314738032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 314838032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 314938032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 315090792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 315190792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 315238032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 315338032Speter { 315438032Speter /* may want to convert 7 -> 8 */ 315538032Speter /* XXX should really parse it here -- and use a class XXX */ 315690792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 315738032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 315838032Speter mci->mci_flags |= MCIF_CVT7TO8; 315938032Speter } 316064562Sgshapiro#endif /* MIME7TO8 */ 316138032Speter 316238032Speter if (tTd(11, 1)) 316338032Speter { 316490792Sgshapiro sm_dprintf("openmailer: "); 316590792Sgshapiro mci_dump(mci, false); 316638032Speter } 316738032Speter 316890792Sgshapiro#if _FFR_CLIENT_SIZE 316990792Sgshapiro /* 317090792Sgshapiro ** See if we know the maximum size and 317190792Sgshapiro ** abort if the message is too big. 317290792Sgshapiro ** 317390792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 317490792Sgshapiro */ 317590792Sgshapiro 317690792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 317790792Sgshapiro mci->mci_maxsize > 0 && 317890792Sgshapiro e->e_msgsize > mci->mci_maxsize) 317990792Sgshapiro { 318090792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 318190792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 318290792Sgshapiro e->e_status = "5.2.3"; 318390792Sgshapiro else 318490792Sgshapiro e->e_status = "5.3.4"; 318590792Sgshapiro 318690792Sgshapiro usrerrenh(e->e_status, 318790792Sgshapiro "552 Message is too large; %ld bytes max", 318890792Sgshapiro mci->mci_maxsize); 318990792Sgshapiro rcode = EX_DATAERR; 319090792Sgshapiro 319190792Sgshapiro /* Need an e_message for error */ 319290792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 319390792Sgshapiro "Message is too large; %ld bytes max", 319490792Sgshapiro mci->mci_maxsize); 319590792Sgshapiro goto give_up; 319690792Sgshapiro } 319790792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 319890792Sgshapiro 319938032Speter if (mci->mci_state != MCIS_OPEN) 320038032Speter { 320138032Speter /* couldn't open the mailer */ 320238032Speter rcode = mci->mci_exitstat; 320338032Speter errno = mci->mci_errno; 320473188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 320538032Speter if (rcode == EX_OK) 320638032Speter { 320738032Speter /* shouldn't happen */ 320864562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 320990792Sgshapiro (unsigned long) mci, rcode, errno, 321090792Sgshapiro mci->mci_state, firstsig); 321190792Sgshapiro mci_dump_all(true); 321238032Speter rcode = EX_SOFTWARE; 321338032Speter } 321464562Sgshapiro else if (nummxhosts > hostnum) 321538032Speter { 321638032Speter /* try next MX site */ 321738032Speter goto tryhost; 321838032Speter } 321938032Speter } 322038032Speter else if (!clever) 322138032Speter { 322238032Speter /* 322338032Speter ** Format and send message. 322438032Speter */ 322538032Speter 322638032Speter putfromline(mci, e); 322743730Speter (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 322838032Speter (*e->e_putbody)(mci, e, NULL); 322938032Speter 323038032Speter /* get the exit status */ 323138032Speter rcode = endmailer(mci, e, pv); 323290792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 323373188Sgshapiro { 323473188Sgshapiro /* 323573188Sgshapiro ** Need an e_message for mailq display. 323673188Sgshapiro ** We set SmtpError as 323773188Sgshapiro */ 323873188Sgshapiro 323990792Sgshapiro (void) sm_snprintf(SmtpError, sizeof SmtpError, 324090792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 324190792Sgshapiro m->m_name, m->m_mailer); 324273188Sgshapiro } 324338032Speter } 324438032Speter else 324538032Speter { 324638032Speter /* 324738032Speter ** Send the MAIL FROM: protocol 324838032Speter */ 324938032Speter 325090792Sgshapiro /* XXX this isn't pipelined... */ 325138032Speter rcode = smtpmailfrom(m, mci, e); 325238032Speter if (rcode == EX_OK) 325338032Speter { 325438032Speter register int i; 325590792Sgshapiro# if PIPELINING 325690792Sgshapiro ADDRESS *volatile pchain; 325790792Sgshapiro# endif /* PIPELINING */ 325838032Speter 325938032Speter /* send the recipient list */ 326038032Speter tobuf[0] = '\0'; 326190792Sgshapiro mci->mci_retryrcpt = false; 326290792Sgshapiro mci->mci_tolist = tobuf; 326390792Sgshapiro# if PIPELINING 326490792Sgshapiro pchain = NULL; 326590792Sgshapiro mci->mci_nextaddr = NULL; 326690792Sgshapiro# endif /* PIPELINING */ 326764562Sgshapiro 326838032Speter for (to = tochain; to != NULL; to = to->q_tchain) 326938032Speter { 327090792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 327138032Speter continue; 327264562Sgshapiro 327390792Sgshapiro /* mark recipient state as "ok so far" */ 327490792Sgshapiro to->q_state = QS_OK; 327590792Sgshapiro e->e_to = to->q_paddr; 327664562Sgshapiro# if STARTTLS 327764562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3278102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3279102528Sgshapiro mci->mci_host, e->e_id); 328064562Sgshapiro if (i != EX_OK) 328138032Speter { 328290792Sgshapiro markfailure(e, to, mci, i, false); 328390792Sgshapiro giveresponse(i, to->q_status, m, mci, 328490792Sgshapiro ctladdr, xstart, e, to); 328590792Sgshapiro if (i == EX_TEMPFAIL) 328690792Sgshapiro { 328790792Sgshapiro mci->mci_retryrcpt = true; 328890792Sgshapiro to->q_state = QS_RETRY; 328990792Sgshapiro } 329064562Sgshapiro continue; 329138032Speter } 329264562Sgshapiro# endif /* STARTTLS */ 329364562Sgshapiro 329490792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 329590792Sgshapiro# if PIPELINING 329690792Sgshapiro if (i == EX_OK && 329790792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 329864562Sgshapiro { 329990792Sgshapiro /* 330090792Sgshapiro ** Add new element to list of 330190792Sgshapiro ** recipients for pipelining. 330290792Sgshapiro */ 330390792Sgshapiro 330490792Sgshapiro to->q_pchain = NULL; 330590792Sgshapiro if (mci->mci_nextaddr == NULL) 330690792Sgshapiro mci->mci_nextaddr = to; 330790792Sgshapiro if (pchain == NULL) 330890792Sgshapiro pchain = to; 330990792Sgshapiro else 331090792Sgshapiro { 331190792Sgshapiro pchain->q_pchain = to; 331290792Sgshapiro pchain = pchain->q_pchain; 331390792Sgshapiro } 331464562Sgshapiro } 331590792Sgshapiro# endif /* PIPELINING */ 331690792Sgshapiro if (i != EX_OK) 331738032Speter { 331890792Sgshapiro markfailure(e, to, mci, i, false); 331998841Sgshapiro giveresponse(i, to->q_status, m, mci, 332090792Sgshapiro ctladdr, xstart, e, to); 332190792Sgshapiro if (i == EX_TEMPFAIL) 332290792Sgshapiro to->q_state = QS_RETRY; 332338032Speter } 332438032Speter } 332538032Speter 332690792Sgshapiro /* No recipients in list and no missing responses? */ 332790792Sgshapiro if (tobuf[0] == '\0' 332890792Sgshapiro# if PIPELINING 332990792Sgshapiro && mci->mci_nextaddr == NULL 333090792Sgshapiro# endif /* PIPELINING */ 333190792Sgshapiro ) 333238032Speter { 333338032Speter rcode = EX_OK; 333438032Speter e->e_to = NULL; 333538032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 333638032Speter smtprset(m, mci, e); 333738032Speter } 333838032Speter else 333938032Speter { 334038032Speter e->e_to = tobuf + 1; 334190792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 334238032Speter } 334338032Speter } 334464562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 334538032Speter { 334638032Speter /* try next MX site */ 334738032Speter goto tryhost; 334838032Speter } 334938032Speter } 335038032Speter#if NAMED_BIND 335138032Speter if (ConfigLevel < 2) 335238032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 335364562Sgshapiro#endif /* NAMED_BIND */ 335438032Speter 335538032Speter if (tTd(62, 1)) 335638032Speter checkfds("after delivery"); 335738032Speter 335838032Speter /* 335938032Speter ** Do final status disposal. 336038032Speter ** We check for something in tobuf for the SMTP case. 336138032Speter ** If we got a temporary failure, arrange to queue the 336238032Speter ** addressees. 336338032Speter */ 336438032Speter 336538032Speter give_up: 336638032Speter if (bitnset(M_LMTP, m->m_flags)) 336738032Speter { 336838032Speter lmtp_rcode = rcode; 336938032Speter tobuf[0] = '\0'; 337090792Sgshapiro anyok = false; 337190792Sgshapiro strsize = 0; 337238032Speter } 337338032Speter else 337438032Speter anyok = rcode == EX_OK; 337538032Speter 337638032Speter for (to = tochain; to != NULL; to = to->q_tchain) 337738032Speter { 337838032Speter /* see if address already marked */ 337964562Sgshapiro if (!QS_IS_OK(to->q_state)) 338038032Speter continue; 338138032Speter 338238032Speter /* if running LMTP, get the status for each address */ 338338032Speter if (bitnset(M_LMTP, m->m_flags)) 338438032Speter { 338538032Speter if (lmtp_rcode == EX_OK) 338638032Speter rcode = smtpgetstat(m, mci, e); 338738032Speter if (rcode == EX_OK) 338838032Speter { 338990792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 339090792Sgshapiro to->q_paddr, 339190792Sgshapiro tobufsize - strsize); 339290792Sgshapiro SM_ASSERT(strsize < tobufsize); 339390792Sgshapiro anyok = true; 339438032Speter } 339538032Speter else 339638032Speter { 339738032Speter e->e_to = to->q_paddr; 339890792Sgshapiro markfailure(e, to, mci, rcode, true); 339964562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 340090792Sgshapiro ctladdr, xstart, e, to); 340138032Speter e->e_to = tobuf + 1; 340238032Speter continue; 340338032Speter } 340438032Speter } 340538032Speter else 340638032Speter { 340738032Speter /* mark bad addresses */ 340838032Speter if (rcode != EX_OK) 340938032Speter { 341038032Speter if (goodmxfound && rcode == EX_NOHOST) 341138032Speter rcode = EX_TEMPFAIL; 341290792Sgshapiro markfailure(e, to, mci, rcode, true); 341338032Speter continue; 341438032Speter } 341538032Speter } 341638032Speter 341738032Speter /* successful delivery */ 341864562Sgshapiro to->q_state = QS_SENT; 341938032Speter to->q_statdate = curtime(); 342038032Speter e->e_nsent++; 342164562Sgshapiro 342264562Sgshapiro /* 342364562Sgshapiro ** Checkpoint the send list every few addresses 342464562Sgshapiro */ 342564562Sgshapiro 342666494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 342764562Sgshapiro { 342890792Sgshapiro queueup(e, false, false); 342964562Sgshapiro e->e_nsent = 0; 343064562Sgshapiro } 343164562Sgshapiro 343238032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 343338032Speter bitset(QPINGONSUCCESS, to->q_flags)) 343438032Speter { 343538032Speter to->q_flags |= QDELIVERED; 343638032Speter to->q_status = "2.1.5"; 343790792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 343890792Sgshapiro "%s... Successfully delivered\n", 343990792Sgshapiro to->q_paddr); 344038032Speter } 344138032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 344238032Speter bitset(QPRIMARY, to->q_flags) && 344338032Speter !bitset(MCIF_DSN, mci->mci_flags)) 344438032Speter { 344538032Speter to->q_flags |= QRELAYED; 344690792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 344790792Sgshapiro "%s... relayed; expect no further notifications\n", 344890792Sgshapiro to->q_paddr); 344938032Speter } 345090792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 345190792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 345290792Sgshapiro bitset(QPRIMARY, to->q_flags) && 345390792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 345490792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 345590792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 345690792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 345790792Sgshapiro { 345890792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 345990792Sgshapiro to->q_flags |= QBYNRELAY; 346090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 346190792Sgshapiro "%s... Deliver-by notify: relayed\n", 346290792Sgshapiro to->q_paddr); 346390792Sgshapiro } 346490792Sgshapiro else if (IS_DLVR_TRACE(e) && 346590792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 346690792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 346790792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 346890792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 346990792Sgshapiro bitset(QPRIMARY, to->q_flags)) 347090792Sgshapiro { 347190792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 347290792Sgshapiro to->q_flags |= QBYTRACE; 347390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 347490792Sgshapiro "%s... Deliver-By trace: relayed\n", 347590792Sgshapiro to->q_paddr); 347690792Sgshapiro } 347738032Speter } 347838032Speter 347938032Speter if (bitnset(M_LMTP, m->m_flags)) 348038032Speter { 348138032Speter /* 348238032Speter ** Global information applies to the last recipient only; 348338032Speter ** clear it out to avoid bogus errors. 348438032Speter */ 348538032Speter 348638032Speter rcode = EX_OK; 348738032Speter e->e_statmsg = NULL; 348838032Speter 348938032Speter /* reset the mci state for the next transaction */ 349090792Sgshapiro if (mci != NULL && 349190792Sgshapiro (mci->mci_state == MCIS_MAIL || 349290792Sgshapiro mci->mci_state == MCIS_RCPT || 349390792Sgshapiro mci->mci_state == MCIS_DATA)) 349438032Speter mci->mci_state = MCIS_OPEN; 349538032Speter } 349638032Speter 349738032Speter if (tobuf[0] != '\0') 349890792Sgshapiro { 349990792Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 350090792Sgshapiro#if 0 350190792Sgshapiro /* 350290792Sgshapiro ** This code is disabled for now because I am not 350390792Sgshapiro ** sure that copying status from the first recipient 350490792Sgshapiro ** to all non-status'ed recipients is a good idea. 350590792Sgshapiro */ 350690792Sgshapiro 350790792Sgshapiro if (tochain->q_message != NULL && 350890792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 350990792Sgshapiro { 351090792Sgshapiro for (to = tochain->q_tchain; to != NULL; 351190792Sgshapiro to = to->q_tchain) 351290792Sgshapiro { 351390792Sgshapiro /* see if address already marked */ 351490792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 351590792Sgshapiro to->q_message == NULL) 351690792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 351790792Sgshapiro tochain->q_message); 351890792Sgshapiro } 351990792Sgshapiro } 352090792Sgshapiro#endif /* 0 */ 352190792Sgshapiro } 352238032Speter if (anyok) 352390792Sgshapiro markstats(e, tochain, STATS_NORMAL); 352438032Speter mci_store_persistent(mci); 352538032Speter 352690792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 352790792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 352890792Sgshapiro { 352990792Sgshapiro /* try next MX site */ 353090792Sgshapiro goto tryhost; 353190792Sgshapiro } 353290792Sgshapiro 353338032Speter /* now close the connection */ 353438032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 353538032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 353638032Speter smtpquit(m, mci, e); 353738032Speter 353890792Sgshapirocleanup: ; 353990792Sgshapiro } 354090792Sgshapiro SM_FINALLY 354190792Sgshapiro { 354290792Sgshapiro /* 354390792Sgshapiro ** Restore state and return. 354490792Sgshapiro */ 354538032Speter#if XDEBUG 354638032Speter char wbuf[MAXLINE]; 354738032Speter 354838032Speter /* make absolutely certain 0, 1, and 2 are in use */ 354990792Sgshapiro (void) sm_snprintf(wbuf, sizeof wbuf, 355090792Sgshapiro "%s... end of deliver(%s)", 355190792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 355290792Sgshapiro : shortenstring(e->e_to, 355390792Sgshapiro MAXSHORTSTR), 355490792Sgshapiro m->m_name); 355538032Speter checkfd012(wbuf); 355664562Sgshapiro#endif /* XDEBUG */ 355738032Speter 355890792Sgshapiro errno = 0; 355990792Sgshapiro 356090792Sgshapiro /* 356190792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 356290792Sgshapiro ** because it previously pointed to an auto buffer. 356390792Sgshapiro ** We don't do this any more, so this may be unnecessary. 356490792Sgshapiro */ 356590792Sgshapiro 356690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 356790792Sgshapiro e->e_to = NULL; 356890792Sgshapiro } 356990792Sgshapiro SM_END_TRY 357064562Sgshapiro return rcode; 357138032Speter} 357264562Sgshapiro 357390792Sgshapiro/* 357438032Speter** MARKFAILURE -- mark a failure on a specific address. 357538032Speter** 357638032Speter** Parameters: 357738032Speter** e -- the envelope we are sending. 357838032Speter** q -- the address to mark. 357938032Speter** mci -- mailer connection information. 358038032Speter** rcode -- the code signifying the particular failure. 358164562Sgshapiro** ovr -- override an existing code? 358238032Speter** 358338032Speter** Returns: 358438032Speter** none. 358538032Speter** 358638032Speter** Side Effects: 358738032Speter** marks the address (and possibly the envelope) with the 358838032Speter** failure so that an error will be returned or 358938032Speter** the message will be queued, as appropriate. 359038032Speter*/ 359138032Speter 359290792Sgshapirovoid 359364562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 359438032Speter register ENVELOPE *e; 359538032Speter register ADDRESS *q; 359638032Speter register MCI *mci; 359738032Speter int rcode; 359864562Sgshapiro bool ovr; 359938032Speter{ 360090792Sgshapiro int save_errno = errno; 360164562Sgshapiro char *status = NULL; 360264562Sgshapiro char *rstatus = NULL; 360338032Speter 360438032Speter switch (rcode) 360538032Speter { 360638032Speter case EX_OK: 360738032Speter break; 360838032Speter 360938032Speter case EX_TEMPFAIL: 361038032Speter case EX_IOERR: 361138032Speter case EX_OSERR: 361264562Sgshapiro q->q_state = QS_QUEUEUP; 361338032Speter break; 361438032Speter 361538032Speter default: 361664562Sgshapiro q->q_state = QS_BADADDR; 361738032Speter break; 361838032Speter } 361938032Speter 362038032Speter /* find most specific error code possible */ 362138032Speter if (mci != NULL && mci->mci_status != NULL) 362238032Speter { 362390792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 362438032Speter if (mci->mci_rstatus != NULL) 362590792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 362690792Sgshapiro mci->mci_rstatus); 362738032Speter else 362864562Sgshapiro rstatus = NULL; 362938032Speter } 363038032Speter else if (e->e_status != NULL) 363138032Speter { 363264562Sgshapiro status = e->e_status; 363364562Sgshapiro rstatus = NULL; 363438032Speter } 363538032Speter else 363638032Speter { 363738032Speter switch (rcode) 363838032Speter { 363938032Speter case EX_USAGE: 364064562Sgshapiro status = "5.5.4"; 364138032Speter break; 364238032Speter 364338032Speter case EX_DATAERR: 364464562Sgshapiro status = "5.5.2"; 364538032Speter break; 364638032Speter 364738032Speter case EX_NOUSER: 364864562Sgshapiro status = "5.1.1"; 364938032Speter break; 365038032Speter 365138032Speter case EX_NOHOST: 365264562Sgshapiro status = "5.1.2"; 365338032Speter break; 365438032Speter 365538032Speter case EX_NOINPUT: 365638032Speter case EX_CANTCREAT: 365738032Speter case EX_NOPERM: 365864562Sgshapiro status = "5.3.0"; 365938032Speter break; 366038032Speter 366138032Speter case EX_UNAVAILABLE: 366238032Speter case EX_SOFTWARE: 366338032Speter case EX_OSFILE: 366438032Speter case EX_PROTOCOL: 366538032Speter case EX_CONFIG: 366664562Sgshapiro status = "5.5.0"; 366738032Speter break; 366838032Speter 366938032Speter case EX_OSERR: 367038032Speter case EX_IOERR: 367164562Sgshapiro status = "4.5.0"; 367238032Speter break; 367338032Speter 367438032Speter case EX_TEMPFAIL: 367564562Sgshapiro status = "4.2.0"; 367638032Speter break; 367738032Speter } 367838032Speter } 367938032Speter 368064562Sgshapiro /* new status? */ 368164562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 368264562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 368364562Sgshapiro { 368464562Sgshapiro q->q_status = status; 368564562Sgshapiro q->q_rstatus = rstatus; 368664562Sgshapiro } 368738032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 368838032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 368990792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 369038032Speter { 369164562Sgshapiro char buf[16]; 369238032Speter 369390792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", rcode); 369490792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 369538032Speter } 369664562Sgshapiro 369764562Sgshapiro q->q_statdate = curtime(); 369864562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 369964562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 370090792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 370190792Sgshapiro 370290792Sgshapiro /* restore errno */ 370390792Sgshapiro errno = save_errno; 370438032Speter} 370590792Sgshapiro/* 370638032Speter** ENDMAILER -- Wait for mailer to terminate. 370738032Speter** 370838032Speter** We should never get fatal errors (e.g., segmentation 370938032Speter** violation), so we report those specially. For other 371038032Speter** errors, we choose a status message (into statmsg), 371138032Speter** and if it represents an error, we print it. 371238032Speter** 371338032Speter** Parameters: 371480785Sgshapiro** mci -- the mailer connection info. 371538032Speter** e -- the current envelope. 371638032Speter** pv -- the parameter vector that invoked the mailer 371738032Speter** (for error messages). 371838032Speter** 371938032Speter** Returns: 372038032Speter** exit code of mailer. 372138032Speter** 372238032Speter** Side Effects: 372338032Speter** none. 372438032Speter*/ 372538032Speter 372664562Sgshapirostatic jmp_buf EndWaitTimeout; 372764562Sgshapiro 372864562Sgshapirostatic void 372964562Sgshapiroendwaittimeout() 373064562Sgshapiro{ 373177349Sgshapiro /* 373277349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 373377349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 373477349Sgshapiro ** DOING. 373577349Sgshapiro */ 373677349Sgshapiro 373764562Sgshapiro errno = ETIMEDOUT; 373864562Sgshapiro longjmp(EndWaitTimeout, 1); 373964562Sgshapiro} 374064562Sgshapiro 374138032Speterint 374238032Speterendmailer(mci, e, pv) 374338032Speter register MCI *mci; 374438032Speter register ENVELOPE *e; 374538032Speter char **pv; 374638032Speter{ 374738032Speter int st; 374864562Sgshapiro int save_errno = errno; 374964562Sgshapiro char buf[MAXLINE]; 375090792Sgshapiro SM_EVENT *ev = NULL; 375138032Speter 375264562Sgshapiro 375338032Speter mci_unlock_host(mci); 375438032Speter 375577349Sgshapiro /* close output to mailer */ 375677349Sgshapiro if (mci->mci_out != NULL) 375790792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 375877349Sgshapiro 375977349Sgshapiro /* copy any remaining input to transcript */ 376077349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 376177349Sgshapiro e->e_xfp != NULL) 376277349Sgshapiro { 376377349Sgshapiro while (sfgets(buf, sizeof buf, mci->mci_in, 376490792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 376590792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 376677349Sgshapiro } 376777349Sgshapiro 376864562Sgshapiro#if SASL 376990792Sgshapiro /* close SASL connection */ 377064562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 377164562Sgshapiro { 377264562Sgshapiro sasl_dispose(&mci->mci_conn); 377364562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 377464562Sgshapiro } 377564562Sgshapiro#endif /* SASL */ 377664562Sgshapiro 377764562Sgshapiro#if STARTTLS 377864562Sgshapiro /* shutdown TLS */ 377964562Sgshapiro (void) endtlsclt(mci); 378064562Sgshapiro#endif /* STARTTLS */ 378164562Sgshapiro 378264562Sgshapiro /* now close the input */ 378338032Speter if (mci->mci_in != NULL) 378490792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 378538032Speter mci->mci_in = mci->mci_out = NULL; 378638032Speter mci->mci_state = MCIS_CLOSED; 378738032Speter 378864562Sgshapiro errno = save_errno; 378964562Sgshapiro 379038032Speter /* in the IPC case there is nothing to wait for */ 379138032Speter if (mci->mci_pid == 0) 379264562Sgshapiro return EX_OK; 379338032Speter 379464562Sgshapiro /* put a timeout around the wait */ 379564562Sgshapiro if (mci->mci_mailer->m_wait > 0) 379664562Sgshapiro { 379764562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 379890792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 379990792Sgshapiro endwaittimeout, 0); 380064562Sgshapiro else 380164562Sgshapiro { 380266494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 380364562Sgshapiro mci->mci_mailer->m_name, 380466494Sgshapiro (long) mci->mci_mailer->m_wait); 380564562Sgshapiro return EX_TEMPFAIL; 380664562Sgshapiro } 380764562Sgshapiro } 380838032Speter 380964562Sgshapiro /* wait for the mailer process, collect status */ 381038032Speter st = waitfor(mci->mci_pid); 381164562Sgshapiro save_errno = errno; 381264562Sgshapiro if (ev != NULL) 381390792Sgshapiro sm_clrevent(ev); 381464562Sgshapiro errno = save_errno; 381564562Sgshapiro 381638032Speter if (st == -1) 381738032Speter { 381838032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 381964562Sgshapiro return EX_SOFTWARE; 382038032Speter } 382138032Speter 382238032Speter if (WIFEXITED(st)) 382338032Speter { 382438032Speter /* normal death -- return status */ 382538032Speter return (WEXITSTATUS(st)); 382638032Speter } 382738032Speter 382838032Speter /* it died a horrid death */ 382964562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 383064562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 383164562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 383264562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 383338032Speter 383438032Speter /* log the arguments */ 383538032Speter if (pv != NULL && e->e_xfp != NULL) 383638032Speter { 383738032Speter register char **av; 383838032Speter 383990792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 384038032Speter for (av = pv; *av != NULL; av++) 384190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 384290792Sgshapiro *av); 384390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 384438032Speter } 384538032Speter 384638032Speter ExitStat = EX_TEMPFAIL; 384764562Sgshapiro return EX_TEMPFAIL; 384838032Speter} 384990792Sgshapiro/* 385038032Speter** GIVERESPONSE -- Interpret an error response from a mailer 385138032Speter** 385238032Speter** Parameters: 385364562Sgshapiro** status -- the status code from the mailer (high byte 385438032Speter** only; core dumps must have been taken care of 385538032Speter** already). 385664562Sgshapiro** dsn -- the DSN associated with the address, if any. 385738032Speter** m -- the mailer info for this mailer. 385838032Speter** mci -- the mailer connection info -- can be NULL if the 385938032Speter** response is given before the connection is made. 386038032Speter** ctladdr -- the controlling address for the recipient 386138032Speter** address(es). 386238032Speter** xstart -- the transaction start time, for computing 386338032Speter** transaction delays. 386438032Speter** e -- the current envelope. 386590792Sgshapiro** to -- the current recipient (NULL if none). 386638032Speter** 386738032Speter** Returns: 386838032Speter** none. 386938032Speter** 387038032Speter** Side Effects: 387138032Speter** Errors may be incremented. 387238032Speter** ExitStat may be set. 387338032Speter*/ 387438032Speter 387538032Spetervoid 387690792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 387764562Sgshapiro int status; 387864562Sgshapiro char *dsn; 387938032Speter register MAILER *m; 388038032Speter register MCI *mci; 388138032Speter ADDRESS *ctladdr; 388238032Speter time_t xstart; 388338032Speter ENVELOPE *e; 388490792Sgshapiro ADDRESS *to; 388538032Speter{ 388638032Speter register const char *statmsg; 388764562Sgshapiro int errnum = errno; 388864562Sgshapiro int off = 4; 388990792Sgshapiro bool usestat = false; 389064562Sgshapiro char dsnbuf[ENHSCLEN]; 389138032Speter char buf[MAXLINE]; 389290792Sgshapiro char *exmsg; 389338032Speter 389438032Speter if (e == NULL) 389538032Speter syserr("giveresponse: null envelope"); 389638032Speter 389738032Speter /* 389838032Speter ** Compute status message from code. 389938032Speter */ 390038032Speter 390190792Sgshapiro exmsg = sm_sysexmsg(status); 390264562Sgshapiro if (status == 0) 390338032Speter { 390464562Sgshapiro statmsg = "250 2.0.0 Sent"; 390538032Speter if (e->e_statmsg != NULL) 390638032Speter { 390790792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", 390890792Sgshapiro statmsg, 390990792Sgshapiro shortenstring(e->e_statmsg, 403)); 391038032Speter statmsg = buf; 391138032Speter } 391238032Speter } 391390792Sgshapiro else if (exmsg == NULL) 391438032Speter { 391590792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 391690792Sgshapiro "554 5.3.0 unknown mailer error %d", 391790792Sgshapiro status); 391864562Sgshapiro status = EX_UNAVAILABLE; 391938032Speter statmsg = buf; 392090792Sgshapiro usestat = true; 392138032Speter } 392264562Sgshapiro else if (status == EX_TEMPFAIL) 392338032Speter { 392438032Speter char *bp = buf; 392538032Speter 392690792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 392738032Speter bp += strlen(bp); 392838032Speter#if NAMED_BIND 392938032Speter if (h_errno == TRY_AGAIN) 393090792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 393138032Speter else 393264562Sgshapiro#endif /* NAMED_BIND */ 393338032Speter { 393464562Sgshapiro if (errnum != 0) 393590792Sgshapiro statmsg = sm_errstring(errnum); 393638032Speter else 393738032Speter statmsg = SmtpError; 393838032Speter } 393938032Speter if (statmsg != NULL && statmsg[0] != '\0') 394064562Sgshapiro { 394164562Sgshapiro switch (errnum) 394264562Sgshapiro { 394364562Sgshapiro#ifdef ENETDOWN 394464562Sgshapiro case ENETDOWN: /* Network is down */ 394564562Sgshapiro#endif /* ENETDOWN */ 394664562Sgshapiro#ifdef ENETUNREACH 394764562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 394864562Sgshapiro#endif /* ENETUNREACH */ 394964562Sgshapiro#ifdef ENETRESET 395064562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 395164562Sgshapiro#endif /* ENETRESET */ 395264562Sgshapiro#ifdef ECONNABORTED 395364562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 395464562Sgshapiro#endif /* ECONNABORTED */ 395564562Sgshapiro#ifdef EHOSTDOWN 395664562Sgshapiro case EHOSTDOWN: /* Host is down */ 395764562Sgshapiro#endif /* EHOSTDOWN */ 395864562Sgshapiro#ifdef EHOSTUNREACH 395964562Sgshapiro case EHOSTUNREACH: /* No route to host */ 396064562Sgshapiro#endif /* EHOSTUNREACH */ 396164562Sgshapiro if (mci->mci_host != NULL) 396264562Sgshapiro { 396390792Sgshapiro (void) sm_strlcpyn(bp, 396490792Sgshapiro SPACELEFT(buf, bp), 396590792Sgshapiro 2, ": ", 396690792Sgshapiro mci->mci_host); 396764562Sgshapiro bp += strlen(bp); 396864562Sgshapiro } 396964562Sgshapiro break; 397064562Sgshapiro } 397190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 397290792Sgshapiro statmsg); 397390792Sgshapiro usestat = true; 397464562Sgshapiro } 397538032Speter statmsg = buf; 397638032Speter } 397738032Speter#if NAMED_BIND 397864562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 397938032Speter { 398090792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 398190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1, 398290792Sgshapiro statmsg); 398338032Speter statmsg = buf; 398490792Sgshapiro usestat = true; 398538032Speter } 398664562Sgshapiro#endif /* NAMED_BIND */ 398738032Speter else 398838032Speter { 398990792Sgshapiro statmsg = exmsg; 399064562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 399138032Speter { 399290792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg, 399390792Sgshapiro sm_errstring(errnum)); 399438032Speter statmsg = buf; 399590792Sgshapiro usestat = true; 399638032Speter } 399794334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 399894334Sgshapiro { 399994334Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg, 400094334Sgshapiro shortenstring(e->e_statmsg, 403)); 400194334Sgshapiro statmsg = buf; 400294334Sgshapiro usestat = true; 400394334Sgshapiro } 400438032Speter } 400538032Speter 400638032Speter /* 400738032Speter ** Print the message as appropriate 400838032Speter */ 400938032Speter 401064562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 401138032Speter { 401238032Speter extern char MsgBuf[]; 401338032Speter 401464562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 401564562Sgshapiro { 401664562Sgshapiro if (dsn == NULL) 401764562Sgshapiro { 401890792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 401990792Sgshapiro "%.*s", off, statmsg + 4); 402064562Sgshapiro dsn = dsnbuf; 402164562Sgshapiro } 402264562Sgshapiro off += 5; 402364562Sgshapiro } 402464562Sgshapiro else 402564562Sgshapiro { 402664562Sgshapiro off = 4; 402764562Sgshapiro } 402864562Sgshapiro message("%s", statmsg + off); 402964562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 403090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 403190792Sgshapiro &MsgBuf[4]); 403238032Speter } 403338032Speter else 403438032Speter { 403564562Sgshapiro char mbuf[ENHSCLEN + 4]; 403638032Speter 403738032Speter Errors++; 403864562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 403964562Sgshapiro off < sizeof mbuf - 4) 404064562Sgshapiro { 404164562Sgshapiro if (dsn == NULL) 404264562Sgshapiro { 404390792Sgshapiro (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 404490792Sgshapiro "%.*s", off, statmsg + 4); 404564562Sgshapiro dsn = dsnbuf; 404664562Sgshapiro } 404764562Sgshapiro off += 5; 404890792Sgshapiro 404990792Sgshapiro /* copy only part of statmsg to mbuf */ 405090792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 405190792Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof mbuf); 405264562Sgshapiro } 405364562Sgshapiro else 405464562Sgshapiro { 405564562Sgshapiro dsnbuf[0] = '\0'; 405690792Sgshapiro (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s", 405790792Sgshapiro statmsg); 405864562Sgshapiro off = 4; 405964562Sgshapiro } 406064562Sgshapiro usrerr(mbuf, &statmsg[off]); 406138032Speter } 406238032Speter 406338032Speter /* 406438032Speter ** Final cleanup. 406538032Speter ** Log a record of the transaction. Compute the new 406638032Speter ** ExitStat -- if we already had an error, stick with 406738032Speter ** that. 406838032Speter */ 406938032Speter 407038032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 407164562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 407264562Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 407338032Speter 407438032Speter if (tTd(11, 2)) 407590792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 407690792Sgshapiro status, 407790792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 407890792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 407990792Sgshapiro errnum); 408038032Speter 408164562Sgshapiro if (status != EX_TEMPFAIL) 408264562Sgshapiro setstat(status); 408364562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 408490792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 408590792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 408638032Speter { 408790792Sgshapiro if (!usestat && e->e_message != NULL) 408890792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 408990792Sgshapiro e->e_message); 409090792Sgshapiro else 409190792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 409290792Sgshapiro statmsg + off); 409338032Speter } 409438032Speter errno = 0; 409573188Sgshapiro SM_SET_H_ERRNO(0); 409638032Speter} 409790792Sgshapiro/* 409838032Speter** LOGDELIVERY -- log the delivery in the system log 409938032Speter** 410038032Speter** Care is taken to avoid logging lines that are too long, because 410138032Speter** some versions of syslog have an unfortunate proclivity for core 410238032Speter** dumping. This is a hack, to be sure, that is at best empirical. 410338032Speter** 410438032Speter** Parameters: 410538032Speter** m -- the mailer info. Can be NULL for initial queue. 410638032Speter** mci -- the mailer connection info -- can be NULL if the 410764562Sgshapiro** log is occurring when no connection is active. 410864562Sgshapiro** dsn -- the DSN attached to the status. 410964562Sgshapiro** status -- the message to print for the status. 411038032Speter** ctladdr -- the controlling address for the to list. 411138032Speter** xstart -- the transaction start time, used for 411238032Speter** computing transaction delay. 411338032Speter** e -- the current envelope. 411438032Speter** 411538032Speter** Returns: 411638032Speter** none 411738032Speter** 411838032Speter** Side Effects: 411938032Speter** none 412038032Speter*/ 412138032Speter 412238032Spetervoid 412364562Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e) 412438032Speter MAILER *m; 412538032Speter register MCI *mci; 412664562Sgshapiro char *dsn; 412764562Sgshapiro const char *status; 412838032Speter ADDRESS *ctladdr; 412938032Speter time_t xstart; 413038032Speter register ENVELOPE *e; 413138032Speter{ 413238032Speter register char *bp; 413338032Speter register char *p; 413438032Speter int l; 413590792Sgshapiro time_t now = curtime(); 413638032Speter char buf[1024]; 413738032Speter 413864562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 413938032Speter /* ctladdr: max 106 bytes */ 414038032Speter bp = buf; 414138032Speter if (ctladdr != NULL) 414238032Speter { 414390792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 414490792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 414538032Speter bp += strlen(bp); 414638032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 414738032Speter { 414890792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 414990792Sgshapiro (int) ctladdr->q_uid, 415090792Sgshapiro (int) ctladdr->q_gid); 415138032Speter bp += strlen(bp); 415238032Speter } 415338032Speter } 415438032Speter 415538032Speter /* delay & xdelay: max 41 bytes */ 415690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 415790792Sgshapiro pintvl(now - e->e_ctime, true)); 415838032Speter bp += strlen(bp); 415938032Speter 416038032Speter if (xstart != (time_t) 0) 416138032Speter { 416290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 416390792Sgshapiro pintvl(now - xstart, true)); 416438032Speter bp += strlen(bp); 416538032Speter } 416638032Speter 416738032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 416838032Speter if (m != NULL) 416938032Speter { 417090792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 417190792Sgshapiro m->m_name); 417238032Speter bp += strlen(bp); 417338032Speter } 417438032Speter 417564562Sgshapiro /* pri: changes with each delivery attempt */ 417690792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 417790792Sgshapiro e->e_msgpriority); 417864562Sgshapiro bp += strlen(bp); 417964562Sgshapiro 418038032Speter /* relay: max 66 bytes for IPv4 addresses */ 418138032Speter if (mci != NULL && mci->mci_host != NULL) 418238032Speter { 418338032Speter extern SOCKADDR CurHostAddr; 418438032Speter 418590792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 418690792Sgshapiro shortenstring(mci->mci_host, 40)); 418738032Speter bp += strlen(bp); 418838032Speter 418938032Speter if (CurHostAddr.sa.sa_family != 0) 419038032Speter { 419190792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 419290792Sgshapiro anynet_ntoa(&CurHostAddr)); 419338032Speter } 419438032Speter } 419590792Sgshapiro#if _FFR_QUARANTINE 419690792Sgshapiro else if (strcmp(status, "quarantined") == 0) 419790792Sgshapiro { 419890792Sgshapiro if (e->e_quarmsg != NULL) 419990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 420090792Sgshapiro ", quarantine=%s", 420190792Sgshapiro shortenstring(e->e_quarmsg, 40)); 420290792Sgshapiro } 420390792Sgshapiro#endif /* _FFR_QUARANTINE */ 420464562Sgshapiro else if (strcmp(status, "queued") != 0) 420538032Speter { 420638032Speter p = macvalue('h', e); 420738032Speter if (p != NULL && p[0] != '\0') 420838032Speter { 420990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 421090792Sgshapiro ", relay=%s", shortenstring(p, 40)); 421138032Speter } 421238032Speter } 421338032Speter bp += strlen(bp); 421438032Speter 421564562Sgshapiro /* dsn */ 421664562Sgshapiro if (dsn != NULL && *dsn != '\0') 421764562Sgshapiro { 421890792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 421990792Sgshapiro shortenstring(dsn, ENHSCLEN)); 422064562Sgshapiro bp += strlen(bp); 422164562Sgshapiro } 422238032Speter 422364562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 422464562Sgshapiro# if (STATLEN) < 63 422564562Sgshapiro# undef STATLEN 422664562Sgshapiro# define STATLEN 63 422764562Sgshapiro# endif /* (STATLEN) < 63 */ 422864562Sgshapiro# if (STATLEN) > 203 422964562Sgshapiro# undef STATLEN 423064562Sgshapiro# define STATLEN 203 423164562Sgshapiro# endif /* (STATLEN) > 203 */ 423264562Sgshapiro 423338032Speter /* stat: max 210 bytes */ 423438032Speter if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) 423538032Speter { 423638032Speter /* desperation move -- truncate data */ 423738032Speter bp = buf + sizeof buf - ((STATLEN) + 17); 423890792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 423938032Speter bp += 3; 424038032Speter } 424138032Speter 424290792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 424338032Speter bp += strlen(bp); 424438032Speter 424590792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 424690792Sgshapiro SPACELEFT(buf, bp)); 424738032Speter 424838032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 424938032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 425090792Sgshapiro if (l < 0) 425190792Sgshapiro l = 0; 425264562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 425390792Sgshapiro while (strlen(p) >= l) 425438032Speter { 425564562Sgshapiro register char *q; 425638032Speter 425764562Sgshapiro for (q = p + l; q > p; q--) 425864562Sgshapiro { 425964562Sgshapiro if (*q == ',') 426064562Sgshapiro break; 426164562Sgshapiro } 426264562Sgshapiro if (p == q) 426364562Sgshapiro break; 426490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 426566494Sgshapiro (int) (++q - p), p, buf); 426638032Speter p = q; 426738032Speter } 426864562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 426938032Speter 427064562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 427138032Speter 427238032Speter l = SYSLOG_BUFSIZE - 85; 427390792Sgshapiro if (l < 0) 427490792Sgshapiro l = 0; 427564562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 427690792Sgshapiro while (strlen(p) >= l) 427738032Speter { 427864562Sgshapiro register char *q; 427938032Speter 428064562Sgshapiro for (q = p + l; q > p; q--) 428164562Sgshapiro { 428264562Sgshapiro if (*q == ',') 428364562Sgshapiro break; 428464562Sgshapiro } 428564562Sgshapiro if (p == q) 428664562Sgshapiro break; 428764562Sgshapiro 428890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 428966494Sgshapiro (int) (++q - p), p); 429038032Speter p = q; 429138032Speter } 429264562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 429338032Speter 429438032Speter if (ctladdr != NULL) 429538032Speter { 429638032Speter bp = buf; 429790792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 429890792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 429938032Speter bp += strlen(bp); 430038032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 430138032Speter { 430290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 430390792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 430438032Speter bp += strlen(bp); 430538032Speter } 430638032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 430738032Speter } 430838032Speter bp = buf; 430990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 431090792Sgshapiro pintvl(now - e->e_ctime, true)); 431138032Speter bp += strlen(bp); 431238032Speter if (xstart != (time_t) 0) 431338032Speter { 431490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 431590792Sgshapiro pintvl(now - xstart, true)); 431638032Speter bp += strlen(bp); 431738032Speter } 431838032Speter 431938032Speter if (m != NULL) 432038032Speter { 432190792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 432290792Sgshapiro m->m_name); 432338032Speter bp += strlen(bp); 432438032Speter } 432538032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 432638032Speter 432738032Speter buf[0] = '\0'; 432838032Speter bp = buf; 432938032Speter if (mci != NULL && mci->mci_host != NULL) 433038032Speter { 433138032Speter extern SOCKADDR CurHostAddr; 433238032Speter 433390792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 433490792Sgshapiro mci->mci_host); 433538032Speter bp += strlen(bp); 433638032Speter 433738032Speter if (CurHostAddr.sa.sa_family != 0) 433890792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 433990792Sgshapiro " [%.100s]", 434090792Sgshapiro anynet_ntoa(&CurHostAddr)); 434138032Speter } 434290792Sgshapiro#if _FFR_QUARANTINE 434390792Sgshapiro else if (strcmp(status, "quarantined") == 0) 434490792Sgshapiro { 434590792Sgshapiro if (e->e_quarmsg != NULL) 434690792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 434790792Sgshapiro ", quarantine=%.100s", 434890792Sgshapiro e->e_quarmsg); 434990792Sgshapiro } 435090792Sgshapiro#endif /* _FFR_QUARANTINE */ 435164562Sgshapiro else if (strcmp(status, "queued") != 0) 435238032Speter { 435338032Speter p = macvalue('h', e); 435438032Speter if (p != NULL && p[0] != '\0') 435590792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p); 435638032Speter } 435738032Speter if (buf[0] != '\0') 435838032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 435938032Speter 436064562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 436164562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 436238032Speter} 436390792Sgshapiro/* 436438032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 436538032Speter** 436638032Speter** This can be made an arbitrary message separator by changing $l 436738032Speter** 436838032Speter** One of the ugliest hacks seen by human eyes is contained herein: 436938032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 437038032Speter** does a well-meaning programmer such as myself have to deal with 437138032Speter** this kind of antique garbage???? 437238032Speter** 437338032Speter** Parameters: 437438032Speter** mci -- the connection information. 437538032Speter** e -- the envelope. 437638032Speter** 437738032Speter** Returns: 437838032Speter** none 437938032Speter** 438038032Speter** Side Effects: 438138032Speter** outputs some text to fp. 438238032Speter*/ 438338032Speter 438438032Spetervoid 438538032Speterputfromline(mci, e) 438638032Speter register MCI *mci; 438738032Speter ENVELOPE *e; 438838032Speter{ 438938032Speter char *template = UnixFromLine; 439038032Speter char buf[MAXLINE]; 439138032Speter char xbuf[MAXLINE]; 439238032Speter 439338032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 439438032Speter return; 439538032Speter 439638032Speter mci->mci_flags |= MCIF_INHEADER; 439738032Speter 439838032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 439938032Speter { 440038032Speter char *bang; 440138032Speter 440238032Speter expand("\201g", buf, sizeof buf, e); 440338032Speter bang = strchr(buf, '!'); 440438032Speter if (bang == NULL) 440538032Speter { 440638032Speter char *at; 440738032Speter char hname[MAXNAME]; 440838032Speter 440964562Sgshapiro /* 441042575Speter ** If we can construct a UUCP path, do so 441138032Speter */ 441238032Speter 441338032Speter at = strrchr(buf, '@'); 441438032Speter if (at == NULL) 441538032Speter { 441664562Sgshapiro expand("\201k", hname, sizeof hname, e); 441738032Speter at = hname; 441838032Speter } 441938032Speter else 442038032Speter *at++ = '\0'; 442190792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 442290792Sgshapiro "From %.800s \201d remote from %.100s\n", 442390792Sgshapiro buf, at); 442438032Speter } 442538032Speter else 442638032Speter { 442738032Speter *bang++ = '\0'; 442890792Sgshapiro (void) sm_snprintf(xbuf, sizeof xbuf, 442990792Sgshapiro "From %.800s \201d remote from %.100s\n", 443090792Sgshapiro bang, buf); 443138032Speter template = xbuf; 443238032Speter } 443338032Speter } 443438032Speter expand(template, buf, sizeof buf, e); 443538032Speter putxline(buf, strlen(buf), mci, PXLF_HEADER); 443638032Speter} 443790792Sgshapiro/* 443838032Speter** PUTBODY -- put the body of a message. 443938032Speter** 444038032Speter** Parameters: 444138032Speter** mci -- the connection information. 444238032Speter** e -- the envelope to put out. 444338032Speter** separator -- if non-NULL, a message separator that must 444438032Speter** not be permitted in the resulting message. 444538032Speter** 444638032Speter** Returns: 444738032Speter** none. 444838032Speter** 444938032Speter** Side Effects: 445038032Speter** The message is written onto fp. 445138032Speter*/ 445238032Speter 445338032Speter/* values for output state variable */ 445438032Speter#define OS_HEAD 0 /* at beginning of line */ 445538032Speter#define OS_CR 1 /* read a carriage return */ 445638032Speter#define OS_INLINE 2 /* putting rest of line */ 445738032Speter 445838032Spetervoid 445938032Speterputbody(mci, e, separator) 446038032Speter register MCI *mci; 446138032Speter register ENVELOPE *e; 446238032Speter char *separator; 446338032Speter{ 446490792Sgshapiro bool dead = false; 446538032Speter char buf[MAXLINE]; 446690792Sgshapiro#if MIME8TO7 446742575Speter char *boundaries[MAXMIMENESTING + 1]; 446890792Sgshapiro#endif /* MIME8TO7 */ 446938032Speter 447038032Speter /* 447138032Speter ** Output the body of the message 447238032Speter */ 447338032Speter 447438032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 447538032Speter { 447690792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 447738032Speter 447890792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 447990792Sgshapiro SM_IO_RDONLY, NULL); 448038032Speter if (e->e_dfp == NULL) 448164562Sgshapiro { 448264562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 448364562Sgshapiro 448464562Sgshapiro if (errno == ENOENT) 448564562Sgshapiro msg++; 448664562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 448764562Sgshapiro } 448890792Sgshapiro 448938032Speter } 449038032Speter if (e->e_dfp == NULL) 449138032Speter { 449238032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 449338032Speter { 449438032Speter putline("", mci); 449538032Speter mci->mci_flags &= ~MCIF_INHEADER; 449638032Speter } 449738032Speter putline("<<< No Message Collected >>>", mci); 449838032Speter goto endofmessage; 449938032Speter } 450064562Sgshapiro 450138032Speter if (e->e_dfino == (ino_t) 0) 450238032Speter { 450338032Speter struct stat stbuf; 450438032Speter 450590792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 450690792Sgshapiro < 0) 450738032Speter e->e_dfino = -1; 450838032Speter else 450938032Speter { 451038032Speter e->e_dfdev = stbuf.st_dev; 451138032Speter e->e_dfino = stbuf.st_ino; 451238032Speter } 451338032Speter } 451438032Speter 451590792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 451664562Sgshapiro (void) bfrewind(e->e_dfp); 451764562Sgshapiro 451838032Speter#if MIME8TO7 451938032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 452038032Speter { 452138032Speter /* 452238032Speter ** Do 8 to 7 bit MIME conversion. 452338032Speter */ 452438032Speter 452538032Speter /* make sure it looks like a MIME message */ 452638032Speter if (hvalue("MIME-Version", e->e_header) == NULL) 452738032Speter putline("MIME-Version: 1.0", mci); 452838032Speter 452938032Speter if (hvalue("Content-Type", e->e_header) == NULL) 453038032Speter { 453190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, 453290792Sgshapiro "Content-Type: text/plain; charset=%s", 453390792Sgshapiro defcharset(e)); 453438032Speter putline(buf, mci); 453538032Speter } 453638032Speter 453738032Speter /* now do the hard work */ 453838032Speter boundaries[0] = NULL; 453938032Speter mci->mci_flags |= MCIF_INHEADER; 454064562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); 454138032Speter } 454238032Speter# if MIME7TO8 454338032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 454438032Speter { 454564562Sgshapiro (void) mime7to8(mci, e->e_header, e); 454638032Speter } 454764562Sgshapiro# endif /* MIME7TO8 */ 454842575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 454942575Speter { 455064562Sgshapiro bool oldsuprerrs = SuprErrs; 455164562Sgshapiro 455242575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 455342575Speter boundaries[0] = NULL; 455442575Speter mci->mci_flags |= MCIF_INHEADER; 455564562Sgshapiro 455664562Sgshapiro /* 455764562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 455864562Sgshapiro ** and don't want to generate a new bounce message whose 455964562Sgshapiro ** body propagates the broken MIME. We can't just not call 456064562Sgshapiro ** mime8to7() as is done above since we need the security 456164562Sgshapiro ** checks. The best we can do is suppress the errors. 456264562Sgshapiro */ 456364562Sgshapiro 456464562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 456590792Sgshapiro SuprErrs = true; 456664562Sgshapiro 456764562Sgshapiro (void) mime8to7(mci, e->e_header, e, boundaries, 456864562Sgshapiro M87F_OUTER|M87F_NO8TO7); 456964562Sgshapiro 457064562Sgshapiro /* restore SuprErrs */ 457164562Sgshapiro SuprErrs = oldsuprerrs; 457242575Speter } 457338032Speter else 457464562Sgshapiro#endif /* MIME8TO7 */ 457538032Speter { 457638032Speter int ostate; 457738032Speter register char *bp; 457838032Speter register char *pbp; 457938032Speter register int c; 458038032Speter register char *xp; 458138032Speter int padc; 458238032Speter char *buflim; 458338032Speter int pos = 0; 458464562Sgshapiro char peekbuf[12]; 458538032Speter 458638032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 458738032Speter { 458838032Speter putline("", mci); 458938032Speter mci->mci_flags &= ~MCIF_INHEADER; 459038032Speter } 459138032Speter 459238032Speter /* determine end of buffer; allow for short mailer lines */ 459338032Speter buflim = &buf[sizeof buf - 1]; 459438032Speter if (mci->mci_mailer->m_linelimit > 0 && 459538032Speter mci->mci_mailer->m_linelimit < sizeof buf - 1) 459638032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 459738032Speter 459838032Speter /* copy temp file to output with mapping */ 459938032Speter ostate = OS_HEAD; 460038032Speter bp = buf; 460138032Speter pbp = peekbuf; 460290792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 460338032Speter { 460438032Speter if (pbp > peekbuf) 460538032Speter c = *--pbp; 460690792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 460790792Sgshapiro == SM_IO_EOF) 460838032Speter break; 460938032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 461038032Speter c &= 0x7f; 461138032Speter switch (ostate) 461238032Speter { 461338032Speter case OS_HEAD: 461438032Speter if (c == '\0' && 461590792Sgshapiro bitnset(M_NONULLS, 461690792Sgshapiro mci->mci_mailer->m_flags)) 461738032Speter break; 461838032Speter if (c != '\r' && c != '\n' && bp < buflim) 461938032Speter { 462038032Speter *bp++ = c; 462138032Speter break; 462238032Speter } 462338032Speter 462438032Speter /* check beginning of line for special cases */ 462538032Speter *bp = '\0'; 462638032Speter pos = 0; 462790792Sgshapiro padc = SM_IO_EOF; 462838032Speter if (buf[0] == 'F' && 462990792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 463090792Sgshapiro && strncmp(buf, "From ", 5) == 0) 463138032Speter { 463238032Speter padc = '>'; 463338032Speter } 463438032Speter if (buf[0] == '-' && buf[1] == '-' && 463538032Speter separator != NULL) 463638032Speter { 463738032Speter /* possible separator */ 463838032Speter int sl = strlen(separator); 463938032Speter 464090792Sgshapiro if (strncmp(&buf[2], separator, sl) 464190792Sgshapiro == 0) 464238032Speter padc = ' '; 464338032Speter } 464438032Speter if (buf[0] == '.' && 464538032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 464638032Speter { 464738032Speter padc = '.'; 464838032Speter } 464938032Speter 465038032Speter /* now copy out saved line */ 465138032Speter if (TrafficLogFile != NULL) 465238032Speter { 465390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 465490792Sgshapiro SM_TIME_DEFAULT, 465590792Sgshapiro "%05d >>> ", 465690792Sgshapiro (int) CurrentPid); 465790792Sgshapiro if (padc != SM_IO_EOF) 465890792Sgshapiro (void) sm_io_putc(TrafficLogFile, 465990792Sgshapiro SM_TIME_DEFAULT, 466090792Sgshapiro padc); 466138032Speter for (xp = buf; xp < bp; xp++) 466290792Sgshapiro (void) sm_io_putc(TrafficLogFile, 466390792Sgshapiro SM_TIME_DEFAULT, 466490792Sgshapiro (unsigned char) *xp); 466538032Speter if (c == '\n') 466690792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 466790792Sgshapiro SM_TIME_DEFAULT, 466890792Sgshapiro mci->mci_mailer->m_eol); 466938032Speter } 467090792Sgshapiro if (padc != SM_IO_EOF) 467138032Speter { 467290792Sgshapiro if (sm_io_putc(mci->mci_out, 467390792Sgshapiro SM_TIME_DEFAULT, padc) 467490792Sgshapiro == SM_IO_EOF) 467564562Sgshapiro { 467690792Sgshapiro dead = true; 467764562Sgshapiro continue; 467864562Sgshapiro } 467971345Sgshapiro else 468071345Sgshapiro { 468171345Sgshapiro /* record progress for DATA timeout */ 468290792Sgshapiro DataProgress = true; 468371345Sgshapiro } 468438032Speter pos++; 468538032Speter } 468638032Speter for (xp = buf; xp < bp; xp++) 468738032Speter { 468890792Sgshapiro if (sm_io_putc(mci->mci_out, 468990792Sgshapiro SM_TIME_DEFAULT, 469090792Sgshapiro (unsigned char) *xp) 469190792Sgshapiro == SM_IO_EOF) 469264562Sgshapiro { 469390792Sgshapiro dead = true; 469464562Sgshapiro break; 469564562Sgshapiro } 469671345Sgshapiro else 469771345Sgshapiro { 469871345Sgshapiro /* record progress for DATA timeout */ 469990792Sgshapiro DataProgress = true; 470071345Sgshapiro } 470138032Speter } 470264562Sgshapiro if (dead) 470364562Sgshapiro continue; 470438032Speter if (c == '\n') 470538032Speter { 470690792Sgshapiro if (sm_io_fputs(mci->mci_out, 470790792Sgshapiro SM_TIME_DEFAULT, 470890792Sgshapiro mci->mci_mailer->m_eol) 470990792Sgshapiro == SM_IO_EOF) 471064562Sgshapiro break; 471171345Sgshapiro else 471271345Sgshapiro { 471371345Sgshapiro /* record progress for DATA timeout */ 471490792Sgshapiro DataProgress = true; 471571345Sgshapiro } 471638032Speter pos = 0; 471738032Speter } 471838032Speter else 471938032Speter { 472038032Speter pos += bp - buf; 472138032Speter if (c != '\r') 472238032Speter *pbp++ = c; 472338032Speter } 472464562Sgshapiro 472538032Speter bp = buf; 472638032Speter 472738032Speter /* determine next state */ 472838032Speter if (c == '\n') 472938032Speter ostate = OS_HEAD; 473038032Speter else if (c == '\r') 473138032Speter ostate = OS_CR; 473238032Speter else 473338032Speter ostate = OS_INLINE; 473438032Speter continue; 473538032Speter 473638032Speter case OS_CR: 473738032Speter if (c == '\n') 473838032Speter { 473938032Speter /* got CRLF */ 474090792Sgshapiro if (sm_io_fputs(mci->mci_out, 474190792Sgshapiro SM_TIME_DEFAULT, 474290792Sgshapiro mci->mci_mailer->m_eol) 474390792Sgshapiro == SM_IO_EOF) 474464562Sgshapiro continue; 474571345Sgshapiro else 474671345Sgshapiro { 474771345Sgshapiro /* record progress for DATA timeout */ 474890792Sgshapiro DataProgress = true; 474971345Sgshapiro } 475064562Sgshapiro 475138032Speter if (TrafficLogFile != NULL) 475238032Speter { 475390792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 475490792Sgshapiro SM_TIME_DEFAULT, 475590792Sgshapiro mci->mci_mailer->m_eol); 475638032Speter } 475738032Speter ostate = OS_HEAD; 475838032Speter continue; 475938032Speter } 476038032Speter 476138032Speter /* had a naked carriage return */ 476238032Speter *pbp++ = c; 476338032Speter c = '\r'; 476438032Speter ostate = OS_INLINE; 476538032Speter goto putch; 476638032Speter 476738032Speter case OS_INLINE: 476838032Speter if (c == '\r') 476938032Speter { 477038032Speter ostate = OS_CR; 477138032Speter continue; 477238032Speter } 477338032Speter if (c == '\0' && 477490792Sgshapiro bitnset(M_NONULLS, 477590792Sgshapiro mci->mci_mailer->m_flags)) 477638032Speter break; 477738032Speterputch: 477838032Speter if (mci->mci_mailer->m_linelimit > 0 && 477964562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 478038032Speter c != '\n') 478138032Speter { 478264562Sgshapiro int d; 478364562Sgshapiro 478464562Sgshapiro /* check next character for EOL */ 478564562Sgshapiro if (pbp > peekbuf) 478664562Sgshapiro d = *(pbp - 1); 478790792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 478890792Sgshapiro SM_TIME_DEFAULT)) 478990792Sgshapiro != SM_IO_EOF) 479064562Sgshapiro *pbp++ = d; 479164562Sgshapiro 479290792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 479364562Sgshapiro { 479464562Sgshapiro if (TrafficLogFile != NULL) 479590792Sgshapiro (void) sm_io_putc(TrafficLogFile, 479690792Sgshapiro SM_TIME_DEFAULT, 479790792Sgshapiro (unsigned char) c); 479890792Sgshapiro if (sm_io_putc(mci->mci_out, 479990792Sgshapiro SM_TIME_DEFAULT, 480090792Sgshapiro (unsigned char) c) 480190792Sgshapiro == SM_IO_EOF) 480264562Sgshapiro { 480390792Sgshapiro dead = true; 480464562Sgshapiro continue; 480564562Sgshapiro } 480671345Sgshapiro else 480771345Sgshapiro { 480871345Sgshapiro /* record progress for DATA timeout */ 480990792Sgshapiro DataProgress = true; 481071345Sgshapiro } 481164562Sgshapiro pos++; 481264562Sgshapiro continue; 481364562Sgshapiro } 481464562Sgshapiro 481590792Sgshapiro if (sm_io_putc(mci->mci_out, 481690792Sgshapiro SM_TIME_DEFAULT, '!') 481790792Sgshapiro == SM_IO_EOF || 481890792Sgshapiro sm_io_fputs(mci->mci_out, 481990792Sgshapiro SM_TIME_DEFAULT, 482090792Sgshapiro mci->mci_mailer->m_eol) 482190792Sgshapiro == SM_IO_EOF) 482264562Sgshapiro { 482390792Sgshapiro dead = true; 482464562Sgshapiro continue; 482564562Sgshapiro } 482671345Sgshapiro else 482771345Sgshapiro { 482871345Sgshapiro /* record progress for DATA timeout */ 482990792Sgshapiro DataProgress = true; 483071345Sgshapiro } 483164562Sgshapiro 483238032Speter if (TrafficLogFile != NULL) 483338032Speter { 483490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 483590792Sgshapiro SM_TIME_DEFAULT, 483690792Sgshapiro "!%s", 483790792Sgshapiro mci->mci_mailer->m_eol); 483838032Speter } 483938032Speter ostate = OS_HEAD; 484038032Speter *pbp++ = c; 484138032Speter continue; 484238032Speter } 484338032Speter if (c == '\n') 484438032Speter { 484538032Speter if (TrafficLogFile != NULL) 484690792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 484790792Sgshapiro SM_TIME_DEFAULT, 484890792Sgshapiro mci->mci_mailer->m_eol); 484990792Sgshapiro if (sm_io_fputs(mci->mci_out, 485090792Sgshapiro SM_TIME_DEFAULT, 485190792Sgshapiro mci->mci_mailer->m_eol) 485290792Sgshapiro == SM_IO_EOF) 485364562Sgshapiro continue; 485471345Sgshapiro else 485571345Sgshapiro { 485671345Sgshapiro /* record progress for DATA timeout */ 485790792Sgshapiro DataProgress = true; 485871345Sgshapiro } 485938032Speter pos = 0; 486038032Speter ostate = OS_HEAD; 486138032Speter } 486238032Speter else 486338032Speter { 486438032Speter if (TrafficLogFile != NULL) 486590792Sgshapiro (void) sm_io_putc(TrafficLogFile, 486690792Sgshapiro SM_TIME_DEFAULT, 486790792Sgshapiro (unsigned char) c); 486890792Sgshapiro if (sm_io_putc(mci->mci_out, 486990792Sgshapiro SM_TIME_DEFAULT, 487090792Sgshapiro (unsigned char) c) 487190792Sgshapiro == SM_IO_EOF) 487264562Sgshapiro { 487390792Sgshapiro dead = true; 487464562Sgshapiro continue; 487564562Sgshapiro } 487671345Sgshapiro else 487771345Sgshapiro { 487871345Sgshapiro /* record progress for DATA timeout */ 487990792Sgshapiro DataProgress = true; 488071345Sgshapiro } 488138032Speter pos++; 488238032Speter ostate = OS_INLINE; 488338032Speter } 488438032Speter break; 488538032Speter } 488638032Speter } 488738032Speter 488838032Speter /* make sure we are at the beginning of a line */ 488938032Speter if (bp > buf) 489038032Speter { 489138032Speter if (TrafficLogFile != NULL) 489238032Speter { 489338032Speter for (xp = buf; xp < bp; xp++) 489490792Sgshapiro (void) sm_io_putc(TrafficLogFile, 489590792Sgshapiro SM_TIME_DEFAULT, 489690792Sgshapiro (unsigned char) *xp); 489738032Speter } 489838032Speter for (xp = buf; xp < bp; xp++) 489938032Speter { 490090792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 490190792Sgshapiro (unsigned char) *xp) 490290792Sgshapiro == SM_IO_EOF) 490364562Sgshapiro { 490490792Sgshapiro dead = true; 490564562Sgshapiro break; 490664562Sgshapiro } 490771345Sgshapiro else 490871345Sgshapiro { 490971345Sgshapiro /* record progress for DATA timeout */ 491090792Sgshapiro DataProgress = true; 491171345Sgshapiro } 491238032Speter } 491338032Speter pos += bp - buf; 491438032Speter } 491564562Sgshapiro if (!dead && pos > 0) 491638032Speter { 491738032Speter if (TrafficLogFile != NULL) 491890792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 491990792Sgshapiro SM_TIME_DEFAULT, 492090792Sgshapiro mci->mci_mailer->m_eol); 492190792Sgshapiro (void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 492290792Sgshapiro mci->mci_mailer->m_eol); 492364562Sgshapiro 492464562Sgshapiro /* record progress for DATA timeout */ 492590792Sgshapiro DataProgress = true; 492638032Speter } 492738032Speter } 492838032Speter 492990792Sgshapiro if (sm_io_error(e->e_dfp)) 493038032Speter { 493190792Sgshapiro syserr("putbody: %s/%cf%s: read error", 493290792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 493390792Sgshapiro DATAFL_LETTER, e->e_id); 493438032Speter ExitStat = EX_IOERR; 493538032Speter } 493638032Speter 493738032Speterendofmessage: 493864562Sgshapiro /* 493964562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 494064562Sgshapiro ** the file offset in the stdio library for the 494164562Sgshapiro ** parent process will not agree with the in-kernel 494264562Sgshapiro ** file offset since the file descriptor is shared 494364562Sgshapiro ** between the processes. Therefore, it is vital 494464562Sgshapiro ** that the file always be rewound. This forces the 494564562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 494664562Sgshapiro ** offset to match. 494764562Sgshapiro */ 494864562Sgshapiro 494964562Sgshapiro if (e->e_dfp != NULL) 495064562Sgshapiro (void) bfrewind(e->e_dfp); 495164562Sgshapiro 495238032Speter /* some mailers want extra blank line at end of message */ 495364562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 495438032Speter buf[0] != '\0' && buf[0] != '\n') 495538032Speter putline("", mci); 495638032Speter 495790792Sgshapiro (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); 495890792Sgshapiro if (sm_io_error(mci->mci_out) && errno != EPIPE) 495938032Speter { 496038032Speter syserr("putbody: write error"); 496138032Speter ExitStat = EX_IOERR; 496238032Speter } 496364562Sgshapiro 496438032Speter errno = 0; 496538032Speter} 496690792Sgshapiro/* 496738032Speter** MAILFILE -- Send a message to a file. 496838032Speter** 496990792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 497090792Sgshapiro** execute bits, sendmail will try to become the owner of that file 497138032Speter** rather than the real user. Obviously, this only works if 497238032Speter** sendmail runs as root. 497338032Speter** 497438032Speter** This could be done as a subordinate mailer, except that it 497538032Speter** is used implicitly to save messages in ~/dead.letter. We 497638032Speter** view this as being sufficiently important as to include it 497738032Speter** here. For example, if the system is dying, we shouldn't have 497838032Speter** to create another process plus some pipes to save the message. 497938032Speter** 498038032Speter** Parameters: 498138032Speter** filename -- the name of the file to send to. 498238032Speter** mailer -- mailer definition for recipient -- if NULL, 498338032Speter** use FileMailer. 498438032Speter** ctladdr -- the controlling address header -- includes 498538032Speter** the userid/groupid to be when sending. 498638032Speter** sfflags -- flags for opening. 498738032Speter** e -- the current envelope. 498838032Speter** 498938032Speter** Returns: 499038032Speter** The exit code associated with the operation. 499138032Speter** 499238032Speter** Side Effects: 499338032Speter** none. 499438032Speter*/ 499538032Speter 499690792Sgshapiro# define RETURN(st) exit(st); 499790792Sgshapiro 499838032Speterstatic jmp_buf CtxMailfileTimeout; 499938032Speter 500038032Speterint 500138032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 500238032Speter char *volatile filename; 500338032Speter MAILER *volatile mailer; 500438032Speter ADDRESS *ctladdr; 500564562Sgshapiro volatile long sfflags; 500638032Speter register ENVELOPE *e; 500738032Speter{ 500890792Sgshapiro register SM_FILE_T *f; 500938032Speter register pid_t pid = -1; 501064562Sgshapiro volatile int mode; 501164562Sgshapiro int len; 501264562Sgshapiro off_t curoff; 501338032Speter bool suidwarn = geteuid() == 0; 501438032Speter char *p; 501564562Sgshapiro char *volatile realfile; 501690792Sgshapiro SM_EVENT *ev; 501798121Sgshapiro char buf[MAXPATHLEN]; 501898121Sgshapiro char targetfile[MAXPATHLEN]; 501938032Speter 502038032Speter if (tTd(11, 1)) 502138032Speter { 502290792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 502390792Sgshapiro printaddr(ctladdr, false); 502438032Speter } 502538032Speter 502638032Speter if (mailer == NULL) 502738032Speter mailer = FileMailer; 502838032Speter 502938032Speter if (e->e_xfp != NULL) 503090792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 503138032Speter 503238032Speter /* 503338032Speter ** Special case /dev/null. This allows us to restrict file 503438032Speter ** delivery to regular files only. 503538032Speter */ 503638032Speter 503790792Sgshapiro if (sm_path_isdevnull(filename)) 503838032Speter return EX_OK; 503938032Speter 504038032Speter /* check for 8-bit available */ 504138032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 504238032Speter bitnset(M_7BITS, mailer->m_flags) && 504338032Speter (bitset(EF_DONT_MIME, e->e_flags) || 504438032Speter !(bitset(MM_MIME8BIT, MimeMode) || 504538032Speter (bitset(EF_IS_MIME, e->e_flags) && 504638032Speter bitset(MM_CVTMIME, MimeMode))))) 504738032Speter { 504838032Speter e->e_status = "5.6.3"; 504964562Sgshapiro usrerrenh(e->e_status, 505090792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 505190792Sgshapiro errno = 0; 505264562Sgshapiro return EX_DATAERR; 505338032Speter } 505438032Speter 505564562Sgshapiro /* Find the actual file */ 505664562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 505764562Sgshapiro { 505864562Sgshapiro len = strlen(SafeFileEnv); 505964562Sgshapiro 506064562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 506164562Sgshapiro filename += len; 506264562Sgshapiro 506390792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 506464562Sgshapiro { 506564562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 506664562Sgshapiro SafeFileEnv, filename); 506764562Sgshapiro return EX_CANTCREAT; 506864562Sgshapiro } 506990792Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile); 507064562Sgshapiro realfile = targetfile + len; 507164562Sgshapiro if (*filename == '/') 507264562Sgshapiro filename++; 507394334Sgshapiro if (*filename != '\0') 507494334Sgshapiro { 507594334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 507694334Sgshapiro if (targetfile[len - 1] != '/') 507794334Sgshapiro (void) sm_strlcat(targetfile, 507894334Sgshapiro "/", sizeof targetfile); 507994334Sgshapiro (void) sm_strlcat(targetfile, filename, 508094334Sgshapiro sizeof targetfile); 508194334Sgshapiro } 508264562Sgshapiro } 508364562Sgshapiro else if (mailer->m_rootdir != NULL) 508464562Sgshapiro { 508564562Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof targetfile, e); 508664562Sgshapiro len = strlen(targetfile); 508764562Sgshapiro 508864562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 508964562Sgshapiro filename += len; 509064562Sgshapiro 509190792Sgshapiro if (len + strlen(filename) + 1 >= sizeof targetfile) 509264562Sgshapiro { 509364562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 509464562Sgshapiro targetfile, filename); 509564562Sgshapiro return EX_CANTCREAT; 509664562Sgshapiro } 509764562Sgshapiro realfile = targetfile + len; 509864562Sgshapiro if (targetfile[len - 1] != '/') 509990792Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof targetfile); 510064562Sgshapiro if (*filename == '/') 510190792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 510290792Sgshapiro sizeof targetfile); 510364562Sgshapiro else 510490792Sgshapiro (void) sm_strlcat(targetfile, filename, 510590792Sgshapiro sizeof targetfile); 510664562Sgshapiro } 510764562Sgshapiro else 510864562Sgshapiro { 510990792Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof targetfile) >= 511090792Sgshapiro sizeof targetfile) 511164562Sgshapiro { 511264562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 511364562Sgshapiro return EX_CANTCREAT; 511464562Sgshapiro } 511564562Sgshapiro realfile = targetfile; 511664562Sgshapiro } 511764562Sgshapiro 511838032Speter /* 511938032Speter ** Fork so we can change permissions here. 512038032Speter ** Note that we MUST use fork, not vfork, because of 512138032Speter ** the complications of calling subroutines, etc. 512238032Speter */ 512338032Speter 512494334Sgshapiro 512594334Sgshapiro /* 512694334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 512794334Sgshapiro ** around so that the waitfor() below will get it. 512894334Sgshapiro */ 512994334Sgshapiro 513094334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 513194334Sgshapiro 513238032Speter DOFORK(fork); 513338032Speter 513438032Speter if (pid < 0) 513564562Sgshapiro return EX_OSERR; 513638032Speter else if (pid == 0) 513738032Speter { 513838032Speter /* child -- actually write to file */ 513938032Speter struct stat stb; 514038032Speter MCI mcibuf; 514142575Speter int err; 514238032Speter volatile int oflags = O_WRONLY|O_APPEND; 514338032Speter 514477349Sgshapiro /* Reset global flags */ 514577349Sgshapiro RestartRequest = NULL; 514690792Sgshapiro RestartWorkGroup = false; 514777349Sgshapiro ShutdownRequest = NULL; 514877349Sgshapiro PendingSignal = 0; 514990792Sgshapiro CurrentPid = getpid(); 515077349Sgshapiro 515138032Speter if (e->e_lockfp != NULL) 515290792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 515390792Sgshapiro NULL)); 515438032Speter 515590792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 515690792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 515790792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 515838032Speter (void) umask(OldUmask); 515938032Speter e->e_to = filename; 516038032Speter ExitStat = EX_OK; 516138032Speter 516238032Speter if (setjmp(CtxMailfileTimeout) != 0) 516338032Speter { 516490792Sgshapiro RETURN(EX_TEMPFAIL); 516538032Speter } 516638032Speter 516738032Speter if (TimeOuts.to_fileopen > 0) 516890792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 516990792Sgshapiro 0); 517038032Speter else 517138032Speter ev = NULL; 517238032Speter 517390792Sgshapiro /* check file mode to see if set-user-ID */ 517464562Sgshapiro if (stat(targetfile, &stb) < 0) 517564562Sgshapiro mode = FileMode; 517642575Speter else 517738032Speter mode = stb.st_mode; 517838032Speter 517938032Speter /* limit the errors to those actually caused in the child */ 518038032Speter errno = 0; 518138032Speter ExitStat = EX_OK; 518238032Speter 518364562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 518464562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 518564562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 518638032Speter { 518790792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 518838032Speter mode &= ~(S_ISGID|S_ISUID); 518964562Sgshapiro if (tTd(11, 20)) 519090792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 519138032Speter } 519238032Speter 519390792Sgshapiro /* we have to open the data file BEFORE setuid() */ 519438032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 519538032Speter { 519690792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 519738032Speter 519890792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 519990792Sgshapiro SM_IO_RDONLY, NULL); 520038032Speter if (e->e_dfp == NULL) 520138032Speter { 520238032Speter syserr("mailfile: Cannot open %s for %s from %s", 520338032Speter df, e->e_to, e->e_from.q_paddr); 520438032Speter } 520538032Speter } 520638032Speter 520738032Speter /* select a new user to run as */ 520838032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 520938032Speter { 521038032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 521138032Speter { 521238032Speter RealUserName = NULL; 521338032Speter RealUid = mailer->m_uid; 521464562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 521564562Sgshapiro { 521664562Sgshapiro /* Only root can change the uid */ 521790792Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 521890792Sgshapiro (int) RunAsUid, (int) RealUid); 521990792Sgshapiro RETURN(EX_TEMPFAIL); 522064562Sgshapiro } 522138032Speter } 522238032Speter else if (bitset(S_ISUID, mode)) 522338032Speter { 522438032Speter RealUserName = NULL; 522538032Speter RealUid = stb.st_uid; 522638032Speter } 522738032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 522838032Speter { 522938032Speter if (ctladdr->q_ruser != NULL) 523038032Speter RealUserName = ctladdr->q_ruser; 523138032Speter else 523238032Speter RealUserName = ctladdr->q_user; 523338032Speter RealUid = ctladdr->q_uid; 523438032Speter } 523538032Speter else if (mailer != NULL && mailer->m_uid != 0) 523638032Speter { 523738032Speter RealUserName = DefUser; 523838032Speter RealUid = mailer->m_uid; 523938032Speter } 524038032Speter else 524138032Speter { 524238032Speter RealUserName = DefUser; 524338032Speter RealUid = DefUid; 524438032Speter } 524538032Speter 524638032Speter /* select a new group to run as */ 524738032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 524864562Sgshapiro { 524938032Speter RealGid = mailer->m_gid; 525064562Sgshapiro if (RunAsUid != 0 && 525164562Sgshapiro (RealGid != getgid() || 525264562Sgshapiro RealGid != getegid())) 525364562Sgshapiro { 525464562Sgshapiro /* Only root can change the gid */ 525590792Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 525690792Sgshapiro (int) RealGid, (int) RunAsUid, 525790792Sgshapiro (int) getgid(), (int) getegid()); 525890792Sgshapiro RETURN(EX_TEMPFAIL); 525964562Sgshapiro } 526064562Sgshapiro } 526138032Speter else if (bitset(S_ISGID, mode)) 526238032Speter RealGid = stb.st_gid; 526364562Sgshapiro else if (ctladdr != NULL && 526464562Sgshapiro ctladdr->q_uid == DefUid && 526564562Sgshapiro ctladdr->q_gid == 0) 526671345Sgshapiro { 526771345Sgshapiro /* 526871345Sgshapiro ** Special case: This means it is an 526971345Sgshapiro ** alias and we should act as DefaultUser. 527071345Sgshapiro ** See alias()'s comments. 527171345Sgshapiro */ 527271345Sgshapiro 527364562Sgshapiro RealGid = DefGid; 527471345Sgshapiro RealUserName = DefUser; 527571345Sgshapiro } 527671345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 527771345Sgshapiro RealGid = ctladdr->q_gid; 527838032Speter else if (mailer != NULL && mailer->m_gid != 0) 527938032Speter RealGid = mailer->m_gid; 528038032Speter else 528138032Speter RealGid = DefGid; 528238032Speter } 528338032Speter 528438032Speter /* last ditch */ 528538032Speter if (!bitset(SFF_ROOTOK, sfflags)) 528638032Speter { 528738032Speter if (RealUid == 0) 528838032Speter RealUid = DefUid; 528938032Speter if (RealGid == 0) 529038032Speter RealGid = DefGid; 529138032Speter } 529238032Speter 529338032Speter /* set group id list (needs /etc/group access) */ 529438032Speter if (RealUserName != NULL && !DontInitGroups) 529538032Speter { 529638032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 529764562Sgshapiro { 529838032Speter syserr("mailfile: initgroups(%s, %d) failed", 529938032Speter RealUserName, RealGid); 530090792Sgshapiro RETURN(EX_TEMPFAIL); 530164562Sgshapiro } 530238032Speter } 530338032Speter else 530438032Speter { 530538032Speter GIDSET_T gidset[1]; 530638032Speter 530738032Speter gidset[0] = RealGid; 530838032Speter if (setgroups(1, gidset) == -1 && suidwarn) 530964562Sgshapiro { 531038032Speter syserr("mailfile: setgroups() failed"); 531190792Sgshapiro RETURN(EX_TEMPFAIL); 531264562Sgshapiro } 531338032Speter } 531438032Speter 531564562Sgshapiro /* 531664562Sgshapiro ** If you have a safe environment, go into it. 531764562Sgshapiro */ 531864562Sgshapiro 531964562Sgshapiro if (realfile != targetfile) 532038032Speter { 532194334Sgshapiro char save; 532294334Sgshapiro 532394334Sgshapiro save = *realfile; 532464562Sgshapiro *realfile = '\0'; 532564562Sgshapiro if (tTd(11, 20)) 532690792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 532764562Sgshapiro if (chroot(targetfile) < 0) 532838032Speter { 532938032Speter syserr("mailfile: Cannot chroot(%s)", 533064562Sgshapiro targetfile); 533190792Sgshapiro RETURN(EX_CANTCREAT); 533238032Speter } 533394334Sgshapiro *realfile = save; 533438032Speter } 533564562Sgshapiro 533664562Sgshapiro if (tTd(11, 40)) 533790792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 533864562Sgshapiro 533938032Speter if (chdir("/") < 0) 534064562Sgshapiro { 534138032Speter syserr("mailfile: cannot chdir(/)"); 534290792Sgshapiro RETURN(EX_CANTCREAT); 534364562Sgshapiro } 534438032Speter 534538032Speter /* now reset the group and user ids */ 534638032Speter endpwent(); 534790792Sgshapiro sm_mbdb_terminate(); 534838032Speter if (setgid(RealGid) < 0 && suidwarn) 534964562Sgshapiro { 535038032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 535190792Sgshapiro RETURN(EX_TEMPFAIL); 535264562Sgshapiro } 535338032Speter vendor_set_uid(RealUid); 535438032Speter if (setuid(RealUid) < 0 && suidwarn) 535564562Sgshapiro { 535638032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 535790792Sgshapiro RETURN(EX_TEMPFAIL); 535864562Sgshapiro } 535938032Speter 536064562Sgshapiro if (tTd(11, 2)) 536190792Sgshapiro sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 536264562Sgshapiro (int) getuid(), (int) geteuid(), 536364562Sgshapiro (int) getgid(), (int) getegid()); 536464562Sgshapiro 536564562Sgshapiro 536638032Speter /* move into some "safe" directory */ 536738032Speter if (mailer->m_execdir != NULL) 536838032Speter { 536938032Speter char *q; 537038032Speter 537138032Speter for (p = mailer->m_execdir; p != NULL; p = q) 537238032Speter { 537338032Speter q = strchr(p, ':'); 537438032Speter if (q != NULL) 537538032Speter *q = '\0'; 537638032Speter expand(p, buf, sizeof buf, e); 537738032Speter if (q != NULL) 537838032Speter *q++ = ':'; 537938032Speter if (tTd(11, 20)) 538090792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 538190792Sgshapiro buf); 538238032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 538338032Speter break; 538438032Speter } 538538032Speter } 538638032Speter 538764562Sgshapiro /* 538864562Sgshapiro ** Recheck the file after we have assumed the ID of the 538964562Sgshapiro ** delivery user to make sure we can deliver to it as 539064562Sgshapiro ** that user. This is necessary if sendmail is running 539164562Sgshapiro ** as root and the file is on an NFS mount which treats 539264562Sgshapiro ** root as nobody. 539364562Sgshapiro */ 539464562Sgshapiro 539564562Sgshapiro#if HASLSTAT 539664562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 539764562Sgshapiro err = stat(realfile, &stb); 539864562Sgshapiro else 539964562Sgshapiro err = lstat(realfile, &stb); 540064562Sgshapiro#else /* HASLSTAT */ 540164562Sgshapiro err = stat(realfile, &stb); 540264562Sgshapiro#endif /* HASLSTAT */ 540364562Sgshapiro 540464562Sgshapiro if (err < 0) 540564562Sgshapiro { 540664562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 540764562Sgshapiro mode = FileMode; 540864562Sgshapiro oflags |= O_CREAT|O_EXCL; 540964562Sgshapiro } 541064562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 541164562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 541264562Sgshapiro DontBlameSendmail) && 541364562Sgshapiro stb.st_nlink != 1) || 541464562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 541564562Sgshapiro exit(EX_CANTCREAT); 541664562Sgshapiro else 541764562Sgshapiro mode = stb.st_mode; 541864562Sgshapiro 541964562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 542038032Speter sfflags |= SFF_NOSLINK; 542164562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 542238032Speter sfflags |= SFF_NOHLINK; 542338032Speter sfflags &= ~SFF_OPENASROOT; 542464562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 542538032Speter if (f == NULL) 542638032Speter { 542764562Sgshapiro if (transienterror(errno)) 542864562Sgshapiro { 542964562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 543064562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 543190792Sgshapiro sm_errstring(errno)); 543290792Sgshapiro RETURN(EX_TEMPFAIL); 543364562Sgshapiro } 543464562Sgshapiro else 543564562Sgshapiro { 543664562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 543764562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 543890792Sgshapiro sm_errstring(errno)); 543990792Sgshapiro RETURN(EX_CANTCREAT); 544064562Sgshapiro } 544138032Speter } 544290792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 544390792Sgshapiro &stb)) 544438032Speter { 544564562Sgshapiro syserr("554 5.3.0 file changed after open"); 544690792Sgshapiro RETURN(EX_CANTCREAT); 544738032Speter } 544890792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 544938032Speter { 545090792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 545190792Sgshapiro sm_errstring(errno)); 545290792Sgshapiro RETURN(EX_CANTCREAT); 545338032Speter } 545438032Speter 545564562Sgshapiro curoff = stb.st_size; 545664562Sgshapiro 545738032Speter if (ev != NULL) 545890792Sgshapiro sm_clrevent(ev); 545938032Speter 546064562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 546138032Speter mcibuf.mci_mailer = mailer; 546238032Speter mcibuf.mci_out = f; 546338032Speter if (bitnset(M_7BITS, mailer->m_flags)) 546438032Speter mcibuf.mci_flags |= MCIF_7BIT; 546538032Speter 546638032Speter /* clear out per-message flags from connection structure */ 546738032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 546838032Speter 546938032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 547038032Speter !bitset(EF_DONT_MIME, e->e_flags) && 547138032Speter bitnset(M_7BITS, mailer->m_flags)) 547238032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 547338032Speter 547438032Speter#if MIME7TO8 547538032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 547638032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 547738032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 547890792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 547990792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 548038032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 548138032Speter { 548238032Speter /* may want to convert 7 -> 8 */ 548338032Speter /* XXX should really parse it here -- and use a class XXX */ 548490792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 548564562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 548638032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 548738032Speter } 548864562Sgshapiro#endif /* MIME7TO8 */ 548938032Speter 549038032Speter putfromline(&mcibuf, e); 549143730Speter (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); 549238032Speter (*e->e_putbody)(&mcibuf, e, NULL); 549338032Speter putline("\n", &mcibuf); 549490792Sgshapiro if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 549590792Sgshapiro (SuperSafe != SAFE_NO && 549690792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 549790792Sgshapiro sm_io_error(f)) 549838032Speter { 549938032Speter setstat(EX_IOERR); 550064562Sgshapiro#if !NOFTRUNCATE 550190792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 550290792Sgshapiro curoff); 550364562Sgshapiro#endif /* !NOFTRUNCATE */ 550438032Speter } 550538032Speter 550638032Speter /* reset ISUID & ISGID bits for paranoid systems */ 550738032Speter#if HASFCHMOD 550890792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 550990792Sgshapiro (MODE_T) mode); 551064562Sgshapiro#else /* HASFCHMOD */ 551164562Sgshapiro (void) chmod(filename, (MODE_T) mode); 551264562Sgshapiro#endif /* HASFCHMOD */ 551390792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 551464562Sgshapiro setstat(EX_IOERR); 551590792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 551664562Sgshapiro (void) setuid(RealUid); 551738032Speter exit(ExitStat); 551864562Sgshapiro /* NOTREACHED */ 551938032Speter } 552038032Speter else 552138032Speter { 552238032Speter /* parent -- wait for exit status */ 552338032Speter int st; 552438032Speter 552538032Speter st = waitfor(pid); 552638032Speter if (st == -1) 552738032Speter { 552838032Speter syserr("mailfile: %s: wait", mailer->m_name); 552964562Sgshapiro return EX_SOFTWARE; 553038032Speter } 553138032Speter if (WIFEXITED(st)) 553290792Sgshapiro { 553390792Sgshapiro errno = 0; 553438032Speter return (WEXITSTATUS(st)); 553590792Sgshapiro } 553638032Speter else 553738032Speter { 553838032Speter syserr("mailfile: %s: child died on signal %d", 553938032Speter mailer->m_name, st); 554064562Sgshapiro return EX_UNAVAILABLE; 554138032Speter } 554264562Sgshapiro /* NOTREACHED */ 554338032Speter } 554438032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 554538032Speter} 554638032Speter 554738032Speterstatic void 554838032Spetermailfiletimeout() 554938032Speter{ 555077349Sgshapiro /* 555177349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 555277349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 555377349Sgshapiro ** DOING. 555477349Sgshapiro */ 555577349Sgshapiro 555677349Sgshapiro errno = ETIMEDOUT; 555738032Speter longjmp(CtxMailfileTimeout, 1); 555838032Speter} 555990792Sgshapiro/* 556038032Speter** HOSTSIGNATURE -- return the "signature" for a host. 556138032Speter** 556238032Speter** The signature describes how we are going to send this -- it 556338032Speter** can be just the hostname (for non-Internet hosts) or can be 556438032Speter** an ordered list of MX hosts. 556538032Speter** 556638032Speter** Parameters: 556738032Speter** m -- the mailer describing this host. 556838032Speter** host -- the host name. 556938032Speter** 557038032Speter** Returns: 557138032Speter** The signature for this host. 557238032Speter** 557338032Speter** Side Effects: 557438032Speter** Can tweak the symbol table. 557538032Speter*/ 557690792Sgshapiro 557764562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 557838032Speter 557990792Sgshapirochar * 558064562Sgshapirohostsignature(m, host) 558138032Speter register MAILER *m; 558238032Speter char *host; 558338032Speter{ 558438032Speter register char *p; 558538032Speter register STAB *s; 558690792Sgshapiro time_t now; 558764562Sgshapiro#if NAMED_BIND 558864562Sgshapiro char sep = ':'; 558964562Sgshapiro char prevsep = ':'; 559038032Speter int i; 559138032Speter int len; 559238032Speter int nmx; 559364562Sgshapiro int hl; 559438032Speter char *hp; 559538032Speter char *endp; 559638032Speter int oldoptions = _res.options; 559738032Speter char *mxhosts[MAXMXHOSTS + 1]; 559890792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 559964562Sgshapiro#endif /* NAMED_BIND */ 560038032Speter 560164562Sgshapiro if (tTd(17, 3)) 560290792Sgshapiro sm_dprintf("hostsignature(%s)\n", host); 560364562Sgshapiro 560438032Speter /* 560577349Sgshapiro ** If local delivery (and not remote), just return a constant. 560664562Sgshapiro */ 560764562Sgshapiro 560877349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 560990792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 561090792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 561164562Sgshapiro return "localhost"; 561264562Sgshapiro 561364562Sgshapiro /* 561438032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 561538032Speter */ 561638032Speter 561790792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 561890792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 561938032Speter { 562090792Sgshapiro /* just an ordinary mailer or deferred mode */ 562138032Speter return host; 562238032Speter } 562364562Sgshapiro#if NETUNIX 562464562Sgshapiro else if (m->m_argv[0] != NULL && 562564562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 562664562Sgshapiro { 562764562Sgshapiro /* rendezvous in the file system, no MX records */ 562864562Sgshapiro return host; 562964562Sgshapiro } 563064562Sgshapiro#endif /* NETUNIX */ 563138032Speter 563238032Speter /* 563338032Speter ** Look it up in the symbol table. 563438032Speter */ 563538032Speter 563690792Sgshapiro now = curtime(); 563738032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 563890792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 563964562Sgshapiro { 564090792Sgshapiro if (s->s_hostsig.hs_exp >= now) 564190792Sgshapiro { 564290792Sgshapiro if (tTd(17, 3)) 564390792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 564490792Sgshapiro s->s_hostsig.hs_sig); 564590792Sgshapiro return s->s_hostsig.hs_sig; 564690792Sgshapiro } 564790792Sgshapiro 564890792Sgshapiro /* signature is expired: clear it */ 564990792Sgshapiro sm_free(s->s_hostsig.hs_sig); 565090792Sgshapiro s->s_hostsig.hs_sig = NULL; 565164562Sgshapiro } 565238032Speter 565390792Sgshapiro /* set default TTL */ 565490792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 565590792Sgshapiro 565638032Speter /* 565790792Sgshapiro ** Not already there or expired -- create a signature. 565838032Speter */ 565938032Speter 566038032Speter#if NAMED_BIND 566138032Speter if (ConfigLevel < 2) 566238032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 566338032Speter 566438032Speter for (hp = host; hp != NULL; hp = endp) 566538032Speter { 566664562Sgshapiro#if NETINET6 566764562Sgshapiro if (*hp == '[') 566864562Sgshapiro { 566964562Sgshapiro endp = strchr(hp + 1, ']'); 567064562Sgshapiro if (endp != NULL) 567164562Sgshapiro endp = strpbrk(endp + 1, ":,"); 567264562Sgshapiro } 567364562Sgshapiro else 567464562Sgshapiro endp = strpbrk(hp, ":,"); 567564562Sgshapiro#else /* NETINET6 */ 567664562Sgshapiro endp = strpbrk(hp, ":,"); 567764562Sgshapiro#endif /* NETINET6 */ 567838032Speter if (endp != NULL) 567964562Sgshapiro { 568064562Sgshapiro sep = *endp; 568138032Speter *endp = '\0'; 568264562Sgshapiro } 568338032Speter 568438032Speter if (bitnset(M_NOMX, m->m_flags)) 568538032Speter { 568638032Speter /* skip MX lookups */ 568738032Speter nmx = 1; 568838032Speter mxhosts[0] = hp; 568938032Speter } 569038032Speter else 569138032Speter { 569238032Speter auto int rcode; 569390792Sgshapiro int ttl; 569438032Speter 569590792Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 569690792Sgshapiro &ttl); 569738032Speter if (nmx <= 0) 569838032Speter { 569980785Sgshapiro int save_errno; 570038032Speter register MCI *mci; 570138032Speter 570238032Speter /* update the connection info for this host */ 570380785Sgshapiro save_errno = errno; 570438032Speter mci = mci_get(hp, m); 570580785Sgshapiro mci->mci_errno = save_errno; 570638032Speter mci->mci_herrno = h_errno; 570771345Sgshapiro mci->mci_lastuse = now; 570864562Sgshapiro if (rcode == EX_NOHOST) 570964562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 571090792Sgshapiro "550 Host unknown"); 571164562Sgshapiro else 571264562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 571338032Speter 571438032Speter /* use the original host name as signature */ 571538032Speter nmx = 1; 571638032Speter mxhosts[0] = hp; 571738032Speter } 571864562Sgshapiro if (tTd(17, 3)) 571990792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 572090792Sgshapiro nmx, mxhosts[0]); 572190792Sgshapiro 572290792Sgshapiro /* 572390792Sgshapiro ** Set new TTL: we use only one! 572490792Sgshapiro ** We could try to use the minimum instead. 572590792Sgshapiro */ 572690792Sgshapiro 572790792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 572838032Speter } 572938032Speter 573038032Speter len = 0; 573138032Speter for (i = 0; i < nmx; i++) 573238032Speter len += strlen(mxhosts[i]) + 1; 573390792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 573490792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 573590792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 573664562Sgshapiro { 573764562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 573864562Sgshapiro host, MAXHOSTSIGNATURE, len); 573964562Sgshapiro len = MAXHOSTSIGNATURE; 574064562Sgshapiro } 574190792Sgshapiro p = sm_pmalloc_x(len); 574290792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 574338032Speter { 574490792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 574590792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 574690792Sgshapiro s->s_hostsig.hs_sig = p; 574764562Sgshapiro hl = strlen(p); 574864562Sgshapiro p += hl; 574964562Sgshapiro *p++ = prevsep; 575064562Sgshapiro len -= hl + 1; 575138032Speter } 575238032Speter else 575390792Sgshapiro s->s_hostsig.hs_sig = p; 575438032Speter for (i = 0; i < nmx; i++) 575538032Speter { 575664562Sgshapiro hl = strlen(mxhosts[i]); 575764562Sgshapiro if (len - 1 < hl || len <= 1) 575864562Sgshapiro { 575964562Sgshapiro /* force to drop out of outer loop */ 576064562Sgshapiro len = -1; 576164562Sgshapiro break; 576264562Sgshapiro } 576338032Speter if (i != 0) 576464562Sgshapiro { 576564562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 576664562Sgshapiro *p++ = ','; 576764562Sgshapiro else 576864562Sgshapiro *p++ = ':'; 576964562Sgshapiro len--; 577064562Sgshapiro } 577190792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 577264562Sgshapiro p += hl; 577364562Sgshapiro len -= hl; 577438032Speter } 577564562Sgshapiro 577664562Sgshapiro /* 577764562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 577864562Sgshapiro ** because we won't have more space for further hosts 577964562Sgshapiro ** anyway (separated by : in the .cf file). 578064562Sgshapiro */ 578164562Sgshapiro 578264562Sgshapiro if (len < 0) 578364562Sgshapiro break; 578438032Speter if (endp != NULL) 578564562Sgshapiro *endp++ = sep; 578664562Sgshapiro prevsep = sep; 578738032Speter } 578890792Sgshapiro makelower(s->s_hostsig.hs_sig); 578938032Speter if (ConfigLevel < 2) 579038032Speter _res.options = oldoptions; 579164562Sgshapiro#else /* NAMED_BIND */ 579238032Speter /* not using BIND -- the signature is just the host name */ 579390792Sgshapiro /* 579490792Sgshapiro ** 'host' points to storage that will be freed after we are 579590792Sgshapiro ** done processing the current envelope, so we copy it. 579690792Sgshapiro */ 579790792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 579864562Sgshapiro#endif /* NAMED_BIND */ 579938032Speter if (tTd(17, 1)) 580090792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 580190792Sgshapiro return s->s_hostsig.hs_sig; 580238032Speter} 580390792Sgshapiro/* 580464562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 580564562Sgshapiro** 580664562Sgshapiro** The signature describes how we are going to send this -- it 580764562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 580864562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 580964562Sgshapiro** MX preference values. 581064562Sgshapiro** 581164562Sgshapiro** Parameters: 581264562Sgshapiro** sig -- the host signature. 581364562Sgshapiro** mxhosts -- array to populate. 581490792Sgshapiro** mailer -- mailer. 581564562Sgshapiro** 581664562Sgshapiro** Returns: 581764562Sgshapiro** The number of hosts inserted into mxhosts array. 581864562Sgshapiro** 581964562Sgshapiro** Side Effects: 582064562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 582164562Sgshapiro*/ 582264562Sgshapiro 582364562Sgshapirostatic int 582464562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 582564562Sgshapiro char *sig; 582664562Sgshapiro char **mxhosts; 582764562Sgshapiro MAILER *mailer; 582864562Sgshapiro{ 582990792Sgshapiro unsigned short curpref = 0; 583090792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 583164562Sgshapiro char *hp, *endp; 583290792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 583364562Sgshapiro long rndm[MAXMXHOSTS]; 583464562Sgshapiro 583564562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 583664562Sgshapiro { 583764562Sgshapiro char sep = ':'; 583864562Sgshapiro 583964562Sgshapiro#if NETINET6 584064562Sgshapiro if (*hp == '[') 584164562Sgshapiro { 584264562Sgshapiro endp = strchr(hp + 1, ']'); 584364562Sgshapiro if (endp != NULL) 584464562Sgshapiro endp = strpbrk(endp + 1, ":,"); 584564562Sgshapiro } 584664562Sgshapiro else 584764562Sgshapiro endp = strpbrk(hp, ":,"); 584864562Sgshapiro#else /* NETINET6 */ 584964562Sgshapiro endp = strpbrk(hp, ":,"); 585064562Sgshapiro#endif /* NETINET6 */ 585164562Sgshapiro if (endp != NULL) 585264562Sgshapiro { 585364562Sgshapiro sep = *endp; 585464562Sgshapiro *endp = '\0'; 585564562Sgshapiro } 585664562Sgshapiro 585764562Sgshapiro mxhosts[nmx] = hp; 585864562Sgshapiro prefer[nmx] = curpref; 585964562Sgshapiro if (mci_match(hp, mailer)) 586064562Sgshapiro rndm[nmx] = 0; 586164562Sgshapiro else 586264562Sgshapiro rndm[nmx] = get_random(); 586364562Sgshapiro 586464562Sgshapiro if (endp != NULL) 586564562Sgshapiro { 586664562Sgshapiro /* 586764562Sgshapiro ** Since we don't have the original MX prefs, 586864562Sgshapiro ** make our own. If the separator is a ':', that 586964562Sgshapiro ** means the preference for the next host will be 587064562Sgshapiro ** higher than this one, so simply increment curpref. 587164562Sgshapiro */ 587264562Sgshapiro 587364562Sgshapiro if (sep == ':') 587464562Sgshapiro curpref++; 587564562Sgshapiro 587664562Sgshapiro *endp++ = sep; 587764562Sgshapiro } 587864562Sgshapiro if (++nmx >= MAXMXHOSTS) 587964562Sgshapiro break; 588064562Sgshapiro } 588164562Sgshapiro 588264562Sgshapiro /* sort the records using the random factor for equal preferences */ 588364562Sgshapiro for (i = 0; i < nmx; i++) 588464562Sgshapiro { 588564562Sgshapiro for (j = i + 1; j < nmx; j++) 588664562Sgshapiro { 588764562Sgshapiro /* 588864562Sgshapiro ** List is already sorted by MX preference, only 588964562Sgshapiro ** need to look for equal preference MX records 589064562Sgshapiro */ 589164562Sgshapiro 589264562Sgshapiro if (prefer[i] < prefer[j]) 589364562Sgshapiro break; 589464562Sgshapiro 589564562Sgshapiro if (prefer[i] > prefer[j] || 589664562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 589764562Sgshapiro { 589890792Sgshapiro register unsigned short tempp; 589964562Sgshapiro register long tempr; 590064562Sgshapiro register char *temp1; 590164562Sgshapiro 590264562Sgshapiro tempp = prefer[i]; 590364562Sgshapiro prefer[i] = prefer[j]; 590464562Sgshapiro prefer[j] = tempp; 590564562Sgshapiro temp1 = mxhosts[i]; 590664562Sgshapiro mxhosts[i] = mxhosts[j]; 590764562Sgshapiro mxhosts[j] = temp1; 590864562Sgshapiro tempr = rndm[i]; 590964562Sgshapiro rndm[i] = rndm[j]; 591064562Sgshapiro rndm[j] = tempr; 591164562Sgshapiro } 591264562Sgshapiro } 591364562Sgshapiro } 591464562Sgshapiro return nmx; 591564562Sgshapiro} 591664562Sgshapiro 591764562Sgshapiro# if STARTTLS 591864562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 591990792Sgshapirostatic bool tls_ok_clt = true; 592064562Sgshapiro 592190792Sgshapiro/* 592290792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 592364562Sgshapiro** 592464562Sgshapiro** Parameters: 592590792Sgshapiro** tls_ok -- should tls be done? 592690792Sgshapiro** 592790792Sgshapiro** Returns: 592864562Sgshapiro** none. 592964562Sgshapiro** 593090792Sgshapiro** Side Effects: 593190792Sgshapiro** sets tls_ok_clt (static variable in this module) 593290792Sgshapiro*/ 593390792Sgshapiro 593490792Sgshapirovoid 593590792Sgshapirosetclttls(tls_ok) 593690792Sgshapiro bool tls_ok; 593790792Sgshapiro{ 593890792Sgshapiro tls_ok_clt = tls_ok; 593990792Sgshapiro return; 594090792Sgshapiro} 594190792Sgshapiro/* 594290792Sgshapiro** INITCLTTLS -- initialize client side TLS 594390792Sgshapiro** 594490792Sgshapiro** Parameters: 594590792Sgshapiro** tls_ok -- should tls initialization be done? 594690792Sgshapiro** 594764562Sgshapiro** Returns: 594864562Sgshapiro** succeeded? 594990792Sgshapiro** 595090792Sgshapiro** Side Effects: 595190792Sgshapiro** sets tls_ok_clt (static variable in this module) 595264562Sgshapiro*/ 595364562Sgshapiro 595464562Sgshapirobool 595590792Sgshapiroinitclttls(tls_ok) 595690792Sgshapiro bool tls_ok; 595764562Sgshapiro{ 595890792Sgshapiro if (!tls_ok_clt) 595990792Sgshapiro return false; 596090792Sgshapiro tls_ok_clt = tls_ok; 596190792Sgshapiro if (!tls_ok_clt) 596290792Sgshapiro return false; 596364562Sgshapiro if (clt_ctx != NULL) 596490792Sgshapiro return true; /* already done */ 5965110560Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile, 5966110560Sgshapiro CltKeyFile, CACertPath, CACertFile, DHParams); 596790792Sgshapiro return tls_ok_clt; 596864562Sgshapiro} 596964562Sgshapiro 597090792Sgshapiro/* 597164562Sgshapiro** STARTTLS -- try to start secure connection (client side) 597264562Sgshapiro** 597364562Sgshapiro** Parameters: 597464562Sgshapiro** m -- the mailer. 597564562Sgshapiro** mci -- the mailer connection info. 597664562Sgshapiro** e -- the envelope. 597764562Sgshapiro** 597864562Sgshapiro** Returns: 597964562Sgshapiro** success? 598064562Sgshapiro** (maybe this should be some other code than EX_ 598164562Sgshapiro** that denotes which stage failed.) 598264562Sgshapiro*/ 598364562Sgshapiro 598464562Sgshapirostatic int 598564562Sgshapirostarttls(m, mci, e) 598664562Sgshapiro MAILER *m; 598764562Sgshapiro MCI *mci; 598864562Sgshapiro ENVELOPE *e; 598964562Sgshapiro{ 599064562Sgshapiro int smtpresult; 599166494Sgshapiro int result = 0; 599266494Sgshapiro int rfd, wfd; 599364562Sgshapiro SSL *clt_ssl = NULL; 599490792Sgshapiro time_t tlsstart; 599564562Sgshapiro 599690792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 599766494Sgshapiro return EX_TEMPFAIL; 599864562Sgshapiro smtpmessage("STARTTLS", m, mci); 599964562Sgshapiro 600064562Sgshapiro /* get the reply */ 600190792Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL); 600264562Sgshapiro 600364562Sgshapiro /* check return code from server */ 600464562Sgshapiro if (smtpresult == 454) 600564562Sgshapiro return EX_TEMPFAIL; 600664562Sgshapiro if (smtpresult == 501) 600764562Sgshapiro return EX_USAGE; 600864562Sgshapiro if (smtpresult == -1) 600964562Sgshapiro return smtpresult; 601064562Sgshapiro if (smtpresult != 220) 601164562Sgshapiro return EX_PROTOCOL; 601264562Sgshapiro 601364562Sgshapiro if (LogLevel > 13) 601490792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 601564562Sgshapiro 601664562Sgshapiro /* start connection */ 601764562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 601864562Sgshapiro { 601964562Sgshapiro if (LogLevel > 5) 602064562Sgshapiro { 602190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 602290792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 602364562Sgshapiro if (LogLevel > 9) 602490792Sgshapiro tlslogerr("client"); 602564562Sgshapiro } 602664562Sgshapiro return EX_SOFTWARE; 602764562Sgshapiro } 602864562Sgshapiro 602990792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 603090792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 603166494Sgshapiro 603264562Sgshapiro /* SSL_clear(clt_ssl); ? */ 603366494Sgshapiro if (rfd < 0 || wfd < 0 || 603490792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 603590792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 603664562Sgshapiro { 603764562Sgshapiro if (LogLevel > 5) 603864562Sgshapiro { 603990792Sgshapiro sm_syslog(LOG_ERR, NOQID, 604090792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 604190792Sgshapiro result); 604264562Sgshapiro if (LogLevel > 9) 604390792Sgshapiro tlslogerr("client"); 604464562Sgshapiro } 604564562Sgshapiro return EX_SOFTWARE; 604664562Sgshapiro } 604764562Sgshapiro SSL_set_connect_state(clt_ssl); 604890792Sgshapiro tlsstart = curtime(); 604990792Sgshapiro 605090792Sgshapirossl_retry: 605164562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 605264562Sgshapiro { 605364562Sgshapiro int i; 605490792Sgshapiro bool timedout; 605590792Sgshapiro time_t left; 605690792Sgshapiro time_t now = curtime(); 605790792Sgshapiro struct timeval tv; 605864562Sgshapiro 605964562Sgshapiro /* what to do in this case? */ 606064562Sgshapiro i = SSL_get_error(clt_ssl, result); 606190792Sgshapiro 606290792Sgshapiro /* 606390792Sgshapiro ** For SSL_ERROR_WANT_{READ,WRITE}: 606490792Sgshapiro ** There is not a complete SSL record available yet 606590792Sgshapiro ** or there is only a partial SSL record removed from 606690792Sgshapiro ** the network (socket) buffer into the SSL buffer. 606790792Sgshapiro ** The SSL_connect will only succeed when a full 606890792Sgshapiro ** SSL record is available (assuming a "real" error 606990792Sgshapiro ** doesn't happen). To handle when a "real" error 607090792Sgshapiro ** does happen the select is set for exceptions too. 607190792Sgshapiro ** The connection may be re-negotiated during this time 607290792Sgshapiro ** so both read and write "want errors" need to be handled. 607390792Sgshapiro ** A select() exception loops back so that a proper SSL 607490792Sgshapiro ** error message can be gotten. 607590792Sgshapiro */ 607690792Sgshapiro 607790792Sgshapiro left = TimeOuts.to_starttls - (now - tlsstart); 607890792Sgshapiro timedout = left <= 0; 607990792Sgshapiro if (!timedout) 608090792Sgshapiro { 608190792Sgshapiro tv.tv_sec = left; 608290792Sgshapiro tv.tv_usec = 0; 608390792Sgshapiro } 608490792Sgshapiro 6085110560Sgshapiro if (!timedout && FD_SETSIZE > 0 && 6086110560Sgshapiro (rfd >= FD_SETSIZE || 6087110560Sgshapiro (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE))) 6088110560Sgshapiro { 6089110560Sgshapiro if (LogLevel > 5) 6090110560Sgshapiro { 6091110560Sgshapiro sm_syslog(LOG_ERR, e->e_id, 6092110560Sgshapiro "STARTTLS=client, error: fd %d/%d too large", 6093110560Sgshapiro rfd, wfd); 6094110560Sgshapiro if (LogLevel > 8) 6095110560Sgshapiro tlslogerr("client"); 6096110560Sgshapiro } 6097110560Sgshapiro errno = EINVAL; 6098110560Sgshapiro goto tlsfail; 6099110560Sgshapiro } 610090792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_READ) 610190792Sgshapiro { 610290792Sgshapiro fd_set ssl_maskr, ssl_maskx; 610390792Sgshapiro 610490792Sgshapiro FD_ZERO(&ssl_maskr); 610590792Sgshapiro FD_SET(rfd, &ssl_maskr); 610690792Sgshapiro FD_ZERO(&ssl_maskx); 610790792Sgshapiro FD_SET(rfd, &ssl_maskx); 610890792Sgshapiro if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv) 610990792Sgshapiro > 0) 611090792Sgshapiro goto ssl_retry; 611190792Sgshapiro } 611290792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_WRITE) 611390792Sgshapiro { 611490792Sgshapiro fd_set ssl_maskw, ssl_maskx; 611590792Sgshapiro 611690792Sgshapiro FD_ZERO(&ssl_maskw); 611790792Sgshapiro FD_SET(wfd, &ssl_maskw); 611890792Sgshapiro FD_ZERO(&ssl_maskx); 611990792Sgshapiro FD_SET(rfd, &ssl_maskx); 612090792Sgshapiro if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv) 612190792Sgshapiro > 0) 612290792Sgshapiro goto ssl_retry; 612390792Sgshapiro } 612464562Sgshapiro if (LogLevel > 5) 612564562Sgshapiro { 612664562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 612790792Sgshapiro "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d", 612890792Sgshapiro result, i, (int) timedout); 612990792Sgshapiro if (LogLevel > 8) 613090792Sgshapiro tlslogerr("client"); 613164562Sgshapiro } 6132110560Sgshapirotlsfail: 613364562Sgshapiro SSL_free(clt_ssl); 613464562Sgshapiro clt_ssl = NULL; 613564562Sgshapiro return EX_SOFTWARE; 613664562Sgshapiro } 613764562Sgshapiro mci->mci_ssl = clt_ssl; 613890792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 613990792Sgshapiro &mci->mci_macro, true); 614064562Sgshapiro 614190792Sgshapiro /* switch to use TLS... */ 614264562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 614364562Sgshapiro return EX_OK; 614464562Sgshapiro 614564562Sgshapiro /* failure */ 614664562Sgshapiro SSL_free(clt_ssl); 614764562Sgshapiro clt_ssl = NULL; 614864562Sgshapiro return EX_SOFTWARE; 614964562Sgshapiro} 615090792Sgshapiro/* 615164562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 615264562Sgshapiro** 615364562Sgshapiro** Parameters: 615464562Sgshapiro** mci -- the mailer connection info. 615564562Sgshapiro** 615664562Sgshapiro** Returns: 615764562Sgshapiro** success? 615864562Sgshapiro*/ 615990792Sgshapiro 616090792Sgshapirostatic int 616164562Sgshapiroendtlsclt(mci) 616264562Sgshapiro MCI *mci; 616364562Sgshapiro{ 616464562Sgshapiro int r; 616564562Sgshapiro 616664562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 616764562Sgshapiro return EX_OK; 616864562Sgshapiro r = endtls(mci->mci_ssl, "client"); 616964562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 617064562Sgshapiro return r; 617164562Sgshapiro} 617290792Sgshapiro# endif /* STARTTLS */ 617390792Sgshapiro# if STARTTLS || SASL 617490792Sgshapiro/* 617590792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 617664562Sgshapiro** 617764562Sgshapiro** Parameters: 617890792Sgshapiro** e -- envelope. 617990792Sgshapiro** flag -- flag to check in {client_flags} 618064562Sgshapiro** 618164562Sgshapiro** Returns: 618290792Sgshapiro** true iff flag is set. 618364562Sgshapiro*/ 618464562Sgshapiro 618590792Sgshapirostatic bool 618690792Sgshapiroiscltflgset(e, flag) 618790792Sgshapiro ENVELOPE *e; 618890792Sgshapiro int flag; 618964562Sgshapiro{ 619090792Sgshapiro char *p; 619173188Sgshapiro 619290792Sgshapiro p = macvalue(macid("{client_flags}"), e); 619390792Sgshapiro if (p == NULL) 619490792Sgshapiro return false; 619590792Sgshapiro for (; *p != '\0'; p++) 619664562Sgshapiro { 619790792Sgshapiro /* look for just this one flag */ 619890792Sgshapiro if (*p == (char) flag) 619990792Sgshapiro return true; 620064562Sgshapiro } 620190792Sgshapiro return false; 620264562Sgshapiro} 620390792Sgshapiro# endif /* STARTTLS || SASL */ 6204