summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src/deliver.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/src/deliver.c')
-rw-r--r--contrib/sendmail/src/deliver.c6258
1 files changed, 0 insertions, 6258 deletions
diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c
deleted file mode 100644
index ed60e47..0000000
--- a/contrib/sendmail/src/deliver.c
+++ /dev/null
@@ -1,6258 +0,0 @@
-/*
- * Copyright (c) 1998-2007 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-#include <sm/time.h>
-
-SM_RCSID("@(#)$Id: deliver.c,v 8.1015 2007/10/17 21:35:30 ca Exp $")
-
-#if HASSETUSERCONTEXT
-# include <login_cap.h>
-#endif /* HASSETUSERCONTEXT */
-
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
-
-#if STARTTLS || SASL
-# include "sfsasl.h"
-#endif /* STARTTLS || SASL */
-
-static int deliver __P((ENVELOPE *, ADDRESS *));
-static void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
-static void mailfiletimeout __P((int));
-static void endwaittimeout __P((int));
-static int parse_hostsignature __P((char *, char **, MAILER *));
-static void sendenvelope __P((ENVELOPE *, int));
-static int coloncmp __P((const char *, const char *));
-
-#if STARTTLS
-static int starttls __P((MAILER *, MCI *, ENVELOPE *));
-static int endtlsclt __P((MCI *));
-#endif /* STARTTLS */
-# if STARTTLS || SASL
-static bool iscltflgset __P((ENVELOPE *, int));
-# endif /* STARTTLS || SASL */
-
-/*
-** SENDALL -- actually send all the messages.
-**
-** Parameters:
-** e -- the envelope to send.
-** mode -- the delivery mode to use. If SM_DEFAULT, use
-** the current e->e_sendmode.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Scans the send lists and sends everything it finds.
-** Delivers any appropriate error messages.
-** If we are running in a non-interactive mode, takes the
-** appropriate action.
-*/
-
-void
-sendall(e, mode)
- ENVELOPE *e;
- int mode;
-{
- register ADDRESS *q;
- char *owner;
- int otherowners;
- int save_errno;
- register ENVELOPE *ee;
- ENVELOPE *splitenv = NULL;
- int oldverbose = Verbose;
- bool somedeliveries = false, expensive = false;
- pid_t pid;
-
- /*
- ** If this message is to be discarded, don't bother sending
- ** the message at all.
- */
-
- if (bitset(EF_DISCARD, e->e_flags))
- {
- if (tTd(13, 1))
- sm_dprintf("sendall: discarding id %s\n", e->e_id);
- e->e_flags |= EF_CLRQUEUE;
- if (LogLevel > 9)
- logundelrcpts(e, "discarded", 9, true);
- else if (LogLevel > 4)
- sm_syslog(LOG_INFO, e->e_id, "discarded");
- markstats(e, NULL, STATS_REJECT);
- return;
- }
-
- /*
- ** If we have had global, fatal errors, don't bother sending
- ** the message at all if we are in SMTP mode. Local errors
- ** (e.g., a single address failing) will still cause the other
- ** addresses to be sent.
- */
-
- if (bitset(EF_FATALERRS, e->e_flags) &&
- (OpMode == MD_SMTP || OpMode == MD_DAEMON))
- {
- e->e_flags |= EF_CLRQUEUE;
- return;
- }
-
- /* determine actual delivery mode */
- if (mode == SM_DEFAULT)
- {
- mode = e->e_sendmode;
- if (mode != SM_VERIFY && mode != SM_DEFER &&
- shouldqueue(e->e_msgpriority, e->e_ctime))
- mode = SM_QUEUE;
- }
-
- if (tTd(13, 1))
- {
- sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
- mode, e->e_id);
- printaddr(sm_debug_file(), &e->e_from, false);
- sm_dprintf("\te_flags = ");
- printenvflags(e);
- sm_dprintf("sendqueue:\n");
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- }
-
- /*
- ** Do any preprocessing necessary for the mode we are running.
- ** Check to make sure the hop count is reasonable.
- ** Delete sends to the sender in mailing lists.
- */
-
- CurEnv = e;
- if (tTd(62, 1))
- checkfds(NULL);
-
- if (e->e_hopcount > MaxHopCount)
- {
- char *recip;
-
- if (e->e_sendqueue != NULL &&
- e->e_sendqueue->q_paddr != NULL)
- recip = e->e_sendqueue->q_paddr;
- else
- recip = "(nobody)";
-
- errno = 0;
- queueup(e, WILL_BE_QUEUED(mode), false);
- e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
- ExitStat = EX_UNAVAILABLE;
- syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
- e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
- RealHostName == NULL ? "localhost" : RealHostName,
- recip);
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_DEAD(q->q_state))
- continue;
- q->q_state = QS_BADADDR;
- q->q_status = "5.4.6";
- q->q_rstatus = "554 5.4.6 Too many hops";
- }
- return;
- }
-
- /*
- ** Do sender deletion.
- **
- ** If the sender should be queued up, skip this.
- ** This can happen if the name server is hosed when you
- ** are trying to send mail. The result is that the sender
- ** is instantiated in the queue as a recipient.
- */
-
- if (!bitset(EF_METOO, e->e_flags) &&
- !QS_IS_QUEUEUP(e->e_from.q_state))
- {
- if (tTd(13, 5))
- {
- sm_dprintf("sendall: QS_SENDER ");
- printaddr(sm_debug_file(), &e->e_from, false);
- }
- e->e_from.q_state = QS_SENDER;
- (void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
- }
-
- /*
- ** Handle alias owners.
- **
- ** We scan up the q_alias chain looking for owners.
- ** We discard owners that are the same as the return path.
- */
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- register struct address *a;
-
- for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
- continue;
- if (a != NULL)
- q->q_owner = a->q_owner;
-
- if (q->q_owner != NULL &&
- !QS_IS_DEAD(q->q_state) &&
- strcmp(q->q_owner, e->e_from.q_paddr) == 0)
- q->q_owner = NULL;
- }
-
- if (tTd(13, 25))
- {
- sm_dprintf("\nAfter first owner pass, sendq =\n");
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- }
-
- owner = "";
- otherowners = 1;
- while (owner != NULL && otherowners > 0)
- {
- if (tTd(13, 28))
- sm_dprintf("owner = \"%s\", otherowners = %d\n",
- owner, otherowners);
- owner = NULL;
- otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (tTd(13, 30))
- {
- sm_dprintf("Checking ");
- printaddr(sm_debug_file(), q, false);
- }
- if (QS_IS_DEAD(q->q_state))
- {
- if (tTd(13, 30))
- sm_dprintf(" ... QS_IS_DEAD\n");
- continue;
- }
- if (tTd(13, 29) && !tTd(13, 30))
- {
- sm_dprintf("Checking ");
- printaddr(sm_debug_file(), q, false);
- }
-
- if (q->q_owner != NULL)
- {
- if (owner == NULL)
- {
- if (tTd(13, 40))
- sm_dprintf(" ... First owner = \"%s\"\n",
- q->q_owner);
- owner = q->q_owner;
- }
- else if (owner != q->q_owner)
- {
- if (strcmp(owner, q->q_owner) == 0)
- {
- if (tTd(13, 40))
- sm_dprintf(" ... Same owner = \"%s\"\n",
- owner);
-
- /* make future comparisons cheap */
- q->q_owner = owner;
- }
- else
- {
- if (tTd(13, 40))
- sm_dprintf(" ... Another owner \"%s\"\n",
- q->q_owner);
- otherowners++;
- }
- owner = q->q_owner;
- }
- else if (tTd(13, 40))
- sm_dprintf(" ... Same owner = \"%s\"\n",
- owner);
- }
- else
- {
- if (tTd(13, 40))
- sm_dprintf(" ... Null owner\n");
- otherowners++;
- }
-
- if (QS_IS_BADADDR(q->q_state))
- {
- if (tTd(13, 30))
- sm_dprintf(" ... QS_IS_BADADDR\n");
- continue;
- }
-
- if (QS_IS_QUEUEUP(q->q_state))
- {
- MAILER *m = q->q_mailer;
-
- /*
- ** If we have temporary address failures
- ** (e.g., dns failure) and a fallback MX is
- ** set, send directly to the fallback MX host.
- */
-
- if (FallbackMX != NULL &&
- !wordinclass(FallbackMX, 'w') &&
- mode != SM_VERIFY &&
- !bitnset(M_NOMX, m->m_flags) &&
- strcmp(m->m_mailer, "[IPC]") == 0 &&
- m->m_argv[0] != NULL &&
- strcmp(m->m_argv[0], "TCP") == 0)
- {
- int len;
- char *p;
-
- if (tTd(13, 30))
- sm_dprintf(" ... FallbackMX\n");
-
- len = strlen(FallbackMX) + 1;
- p = sm_rpool_malloc_x(e->e_rpool, len);
- (void) sm_strlcpy(p, FallbackMX, len);
- q->q_state = QS_OK;
- q->q_host = p;
- }
- else
- {
- if (tTd(13, 30))
- sm_dprintf(" ... QS_IS_QUEUEUP\n");
- continue;
- }
- }
-
- /*
- ** If this mailer is expensive, and if we don't
- ** want to make connections now, just mark these
- ** addresses and return. This is useful if we
- ** want to batch connections to reduce load. This
- ** will cause the messages to be queued up, and a
- ** daemon will come along to send the messages later.
- */
-
- if (NoConnect && !Verbose &&
- bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
- {
- if (tTd(13, 30))
- sm_dprintf(" ... expensive\n");
- q->q_state = QS_QUEUEUP;
- expensive = true;
- }
- else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
- QueueLimitId == NULL &&
- QueueLimitSender == NULL &&
- QueueLimitRecipient == NULL)
- {
- if (tTd(13, 30))
- sm_dprintf(" ... hold\n");
- q->q_state = QS_QUEUEUP;
- expensive = true;
- }
- else if (QueueMode != QM_QUARANTINE &&
- e->e_quarmsg != NULL)
- {
- if (tTd(13, 30))
- sm_dprintf(" ... quarantine: %s\n",
- e->e_quarmsg);
- q->q_state = QS_QUEUEUP;
- expensive = true;
- }
- else
- {
- if (tTd(13, 30))
- sm_dprintf(" ... deliverable\n");
- somedeliveries = true;
- }
- }
-
- if (owner != NULL && otherowners > 0)
- {
- /*
- ** Split this envelope into two.
- */
-
- ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
- sizeof(*ee));
- STRUCTCOPY(*e, *ee);
- ee->e_message = NULL;
- ee->e_id = NULL;
- assign_queueid(ee);
-
- if (tTd(13, 1))
- sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
- e->e_id, ee->e_id, owner,
- otherowners);
-
- ee->e_header = copyheader(e->e_header, ee->e_rpool);
- ee->e_sendqueue = copyqueue(e->e_sendqueue,
- ee->e_rpool);
- ee->e_errorqueue = copyqueue(e->e_errorqueue,
- ee->e_rpool);
- ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
- ee->e_flags |= EF_NORECEIPT;
- setsender(owner, ee, NULL, '\0', true);
- if (tTd(13, 5))
- {
- sm_dprintf("sendall(split): QS_SENDER ");
- printaddr(sm_debug_file(), &ee->e_from, false);
- }
- ee->e_from.q_state = QS_SENDER;
- ee->e_dfp = NULL;
- ee->e_lockfp = NULL;
- ee->e_xfp = NULL;
- ee->e_qgrp = e->e_qgrp;
- ee->e_qdir = e->e_qdir;
- ee->e_errormode = EM_MAIL;
- ee->e_sibling = splitenv;
- ee->e_statmsg = NULL;
- if (e->e_quarmsg != NULL)
- ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
- e->e_quarmsg);
- splitenv = ee;
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (q->q_owner == owner)
- {
- q->q_state = QS_CLONED;
- if (tTd(13, 6))
- sm_dprintf("\t... stripping %s from original envelope\n",
- q->q_paddr);
- }
- }
- for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (q->q_owner != owner)
- {
- q->q_state = QS_CLONED;
- if (tTd(13, 6))
- sm_dprintf("\t... dropping %s from cloned envelope\n",
- q->q_paddr);
- }
- else
- {
- /* clear DSN parameters */
- q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
- q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
- if (tTd(13, 6))
- sm_dprintf("\t... moving %s to cloned envelope\n",
- q->q_paddr);
- }
- }
-
- if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
- dup_queue_file(e, ee, DATAFL_LETTER);
-
- /*
- ** Give the split envelope access to the parent
- ** transcript file for errors obtained while
- ** processing the recipients (done before the
- ** envelope splitting).
- */
-
- if (e->e_xfp != NULL)
- ee->e_xfp = sm_io_dup(e->e_xfp);
-
- /* failed to dup e->e_xfp, start a new transcript */
- if (ee->e_xfp == NULL)
- openxscript(ee);
-
- if (mode != SM_VERIFY && LogLevel > 4)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: clone: owner=%s",
- ee->e_id, owner);
- }
- }
-
- if (owner != NULL)
- {
- setsender(owner, e, NULL, '\0', true);
- if (tTd(13, 5))
- {
- sm_dprintf("sendall(owner): QS_SENDER ");
- printaddr(sm_debug_file(), &e->e_from, false);
- }
- e->e_from.q_state = QS_SENDER;
- e->e_errormode = EM_MAIL;
- e->e_flags |= EF_NORECEIPT;
- e->e_flags &= ~EF_FATALERRS;
- }
-
- /* if nothing to be delivered, just queue up everything */
- if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
- mode != SM_VERIFY)
- {
- time_t now;
-
- if (tTd(13, 29))
- sm_dprintf("No deliveries: auto-queueing\n");
- mode = SM_QUEUE;
- now = curtime();
-
- /* treat this as a delivery in terms of counting tries */
- e->e_dtime = now;
- if (!expensive)
- e->e_ntries++;
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- ee->e_dtime = now;
- if (!expensive)
- ee->e_ntries++;
- }
- }
-
- if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
- (mode != SM_VERIFY &&
- (SuperSafe == SAFE_REALLY ||
- SuperSafe == SAFE_REALLY_POSTMILTER))) &&
- (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
- {
- bool msync;
-
- /*
- ** Be sure everything is instantiated in the queue.
- ** Split envelopes first in case the machine crashes.
- ** If the original were done first, we may lose
- ** recipients.
- */
-
-#if !HASFLOCK
- msync = false;
-#else /* !HASFLOCK */
- msync = mode == SM_FORK;
-#endif /* !HASFLOCK */
-
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- queueup(ee, WILL_BE_QUEUED(mode), msync);
- queueup(e, WILL_BE_QUEUED(mode), msync);
- }
-
- if (tTd(62, 10))
- checkfds("after envelope splitting");
-
- /*
- ** If we belong in background, fork now.
- */
-
- if (tTd(13, 20))
- {
- sm_dprintf("sendall: final mode = %c\n", mode);
- if (tTd(13, 21))
- {
- sm_dprintf("\n================ Final Send Queue(s) =====================\n");
- sm_dprintf("\n *** Envelope %s, e_from=%s ***\n",
- e->e_id, e->e_from.q_paddr);
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- sm_dprintf("\n *** Envelope %s, e_from=%s ***\n",
- ee->e_id, ee->e_from.q_paddr);
- printaddr(sm_debug_file(), ee->e_sendqueue, true);
- }
- sm_dprintf("==========================================================\n\n");
- }
- }
- switch (mode)
- {
- case SM_VERIFY:
- Verbose = 2;
- break;
-
- case SM_QUEUE:
- case SM_DEFER:
-#if HASFLOCK
- queueonly:
-#endif /* HASFLOCK */
- if (e->e_nrcpts > 0)
- e->e_flags |= EF_INQUEUE;
- dropenvelope(e, splitenv != NULL, true);
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- if (ee->e_nrcpts > 0)
- ee->e_flags |= EF_INQUEUE;
- dropenvelope(ee, false, true);
- }
- return;
-
- case SM_FORK:
- if (e->e_xfp != NULL)
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
-
-#if !HASFLOCK
- /*
- ** Since fcntl locking has the interesting semantic that
- ** the lock is owned by a process, not by an open file
- ** descriptor, we have to flush this to the queue, and
- ** then restart from scratch in the child.
- */
-
- {
- /* save id for future use */
- char *qid = e->e_id;
-
- /* now drop the envelope in the parent */
- e->e_flags |= EF_INQUEUE;
- dropenvelope(e, splitenv != NULL, false);
-
- /* arrange to reacquire lock after fork */
- e->e_id = qid;
- }
-
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- /* save id for future use */
- char *qid = ee->e_id;
-
- /* drop envelope in parent */
- ee->e_flags |= EF_INQUEUE;
- dropenvelope(ee, false, false);
-
- /* and save qid for reacquisition */
- ee->e_id = qid;
- }
-
-#endif /* !HASFLOCK */
-
- /*
- ** Since the delivery may happen in a child and the parent
- ** does not wait, the parent may close the maps thereby
- ** removing any shared memory used by the map. Therefore,
- ** close the maps now so the child will dynamically open
- ** them if necessary.
- */
-
- closemaps(false);
-
- pid = fork();
- if (pid < 0)
- {
- syserr("deliver: fork 1");
-#if HASFLOCK
- goto queueonly;
-#else /* HASFLOCK */
- e->e_id = NULL;
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- ee->e_id = NULL;
- return;
-#endif /* HASFLOCK */
- }
- else if (pid > 0)
- {
-#if HASFLOCK
- /* be sure we leave the temp files to our child */
- /* close any random open files in the envelope */
- closexscript(e);
- if (e->e_dfp != NULL)
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- e->e_dfp = NULL;
- e->e_flags &= ~EF_HAS_DF;
-
- /* can't call unlockqueue to avoid unlink of xfp */
- if (e->e_lockfp != NULL)
- (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
- else
- syserr("%s: sendall: null lockfp", e->e_id);
- e->e_lockfp = NULL;
-#endif /* HASFLOCK */
-
- /* make sure the parent doesn't own the envelope */
- e->e_id = NULL;
-
-#if USE_DOUBLE_FORK
- /* catch intermediate zombie */
- (void) waitfor(pid);
-#endif /* USE_DOUBLE_FORK */
- return;
- }
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- sm_exc_newthread(fatal_error);
-
- /*
- ** Since we have accepted responsbility for the message,
- ** change the SIGTERM handler. intsig() (the old handler)
- ** would remove the envelope if this was a command line
- ** message submission.
- */
-
- (void) sm_signal(SIGTERM, SIG_DFL);
-
-#if USE_DOUBLE_FORK
- /* double fork to avoid zombies */
- pid = fork();
- if (pid > 0)
- exit(EX_OK);
- save_errno = errno;
-#endif /* USE_DOUBLE_FORK */
-
- CurrentPid = getpid();
-
- /* be sure we are immune from the terminal */
- disconnect(2, e);
- clearstats();
-
- /* prevent parent from waiting if there was an error */
- if (pid < 0)
- {
- errno = save_errno;
- syserr("deliver: fork 2");
-#if HASFLOCK
- e->e_flags |= EF_INQUEUE;
-#else /* HASFLOCK */
- e->e_id = NULL;
-#endif /* HASFLOCK */
- finis(true, true, ExitStat);
- }
-
- /* be sure to give error messages in child */
- QuickAbort = false;
-
- /*
- ** Close any cached connections.
- **
- ** We don't send the QUIT protocol because the parent
- ** still knows about the connection.
- **
- ** This should only happen when delivering an error
- ** message.
- */
-
- mci_flush(false, NULL);
-
-#if HASFLOCK
- break;
-#else /* HASFLOCK */
-
- /*
- ** Now reacquire and run the various queue files.
- */
-
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- ENVELOPE *sibling = ee->e_sibling;
-
- (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
- false, false, ee);
- ee->e_sibling = sibling;
- }
- (void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
- false, false, e);
- finis(true, true, ExitStat);
-#endif /* HASFLOCK */
- }
-
- sendenvelope(e, mode);
- dropenvelope(e, true, true);
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- CurEnv = ee;
- if (mode != SM_VERIFY)
- openxscript(ee);
- sendenvelope(ee, mode);
- dropenvelope(ee, true, true);
- }
- CurEnv = e;
-
- Verbose = oldverbose;
- if (mode == SM_FORK)
- finis(true, true, ExitStat);
-}
-
-static void
-sendenvelope(e, mode)
- register ENVELOPE *e;
- int mode;
-{
- register ADDRESS *q;
- bool didany;
-
- if (tTd(13, 10))
- sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
- e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
- e->e_flags);
- if (LogLevel > 80)
- sm_syslog(LOG_DEBUG, e->e_id,
- "sendenvelope, flags=0x%lx",
- e->e_flags);
-
- /*
- ** If we have had global, fatal errors, don't bother sending
- ** the message at all if we are in SMTP mode. Local errors
- ** (e.g., a single address failing) will still cause the other
- ** addresses to be sent.
- */
-
- if (bitset(EF_FATALERRS, e->e_flags) &&
- (OpMode == MD_SMTP || OpMode == MD_DAEMON))
- {
- e->e_flags |= EF_CLRQUEUE;
- return;
- }
-
- /*
- ** Don't attempt deliveries if we want to bounce now
- ** or if deliver-by time is exceeded.
- */
-
- if (!bitset(EF_RESPONSE, e->e_flags) &&
- (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
- (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
- curtime() > e->e_ctime + e->e_deliver_by)))
- return;
-
- /*
- ** Run through the list and send everything.
- **
- ** Set EF_GLOBALERRS so that error messages during delivery
- ** result in returned mail.
- */
-
- e->e_nsent = 0;
- e->e_flags |= EF_GLOBALERRS;
-
- macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
- macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
- didany = false;
-
- if (!bitset(EF_SPLIT, e->e_flags))
- {
- ENVELOPE *oldsib;
- ENVELOPE *ee;
-
- /*
- ** Save old sibling and set it to NULL to avoid
- ** queueing up the same envelopes again.
- ** This requires that envelopes in that list have
- ** been take care of before (or at some other place).
- */
-
- oldsib = e->e_sibling;
- e->e_sibling = NULL;
- if (!split_by_recipient(e) &&
- bitset(EF_FATALERRS, e->e_flags))
- {
- if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
- e->e_flags |= EF_CLRQUEUE;
- return;
- }
- for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
- queueup(ee, false, true);
-
- /* clean up */
- for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
- {
- /* now unlock the job */
- closexscript(ee);
- unlockqueue(ee);
-
- /* this envelope is marked unused */
- if (ee->e_dfp != NULL)
- {
- (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
- ee->e_dfp = NULL;
- }
- ee->e_id = NULL;
- ee->e_flags &= ~EF_HAS_DF;
- }
- e->e_sibling = oldsib;
- }
-
- /* now run through the queue */
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
-#if XDEBUG
- char wbuf[MAXNAME + 20];
-
- (void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)",
- MAXNAME, q->q_paddr);
- checkfd012(wbuf);
-#endif /* XDEBUG */
- if (mode == SM_VERIFY)
- {
- e->e_to = q->q_paddr;
- if (QS_IS_SENDABLE(q->q_state))
- {
- if (q->q_host != NULL && q->q_host[0] != '\0')
- message("deliverable: mailer %s, host %s, user %s",
- q->q_mailer->m_name,
- q->q_host,
- q->q_user);
- else
- message("deliverable: mailer %s, user %s",
- q->q_mailer->m_name,
- q->q_user);
- }
- }
- else if (QS_IS_OK(q->q_state))
- {
- /*
- ** Checkpoint the send list every few addresses
- */
-
- if (CheckpointInterval > 0 &&
- e->e_nsent >= CheckpointInterval)
- {
- queueup(e, false, false);
- e->e_nsent = 0;
- }
- (void) deliver(e, q);
- didany = true;
- }
- }
- if (didany)
- {
- e->e_dtime = curtime();
- e->e_ntries++;
- }
-
-#if XDEBUG
- checkfd012("end of sendenvelope");
-#endif /* XDEBUG */
-}
-
-#if REQUIRES_DIR_FSYNC
-/*
-** SYNC_DIR -- fsync a directory based on a filename
-**
-** Parameters:
-** filename -- path of file
-** panic -- panic?
-**
-** Returns:
-** none
-*/
-
-void
-sync_dir(filename, panic)
- char *filename;
- bool panic;
-{
- int dirfd;
- char *dirp;
- char dir[MAXPATHLEN];
-
- if (!RequiresDirfsync)
- return;
-
- /* filesystems which require the directory be synced */
- dirp = strrchr(filename, '/');
- if (dirp != NULL)
- {
- if (sm_strlcpy(dir, filename, sizeof(dir)) >= sizeof(dir))
- return;
- dir[dirp - filename] = '\0';
- dirp = dir;
- }
- else
- dirp = ".";
- dirfd = open(dirp, O_RDONLY, 0700);
- if (tTd(40,32))
- sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
- dirp, dirfd);
- if (dirfd >= 0)
- {
- if (fsync(dirfd) < 0)
- {
- if (panic)
- syserr("!sync_dir: cannot fsync directory %s",
- dirp);
- else if (LogLevel > 1)
- sm_syslog(LOG_ERR, NOQID,
- "sync_dir: cannot fsync directory %s: %s",
- dirp, sm_errstring(errno));
- }
- (void) close(dirfd);
- }
-}
-#endif /* REQUIRES_DIR_FSYNC */
-/*
-** DUP_QUEUE_FILE -- duplicate a queue file into a split queue
-**
-** Parameters:
-** e -- the existing envelope
-** ee -- the new envelope
-** type -- the queue file type (e.g., DATAFL_LETTER)
-**
-** Returns:
-** none
-*/
-
-static void
-dup_queue_file(e, ee, type)
- ENVELOPE *e, *ee;
- int type;
-{
- char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
-
- ee->e_dfp = NULL;
- ee->e_xfp = NULL;
-
- /*
- ** Make sure both are in the same directory.
- */
-
- (void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf));
- (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof(f2buf));
-
- /* Force the df to disk if it's not there yet */
- if (type == DATAFL_LETTER && e->e_dfp != NULL &&
- sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
- errno != EINVAL)
- {
- syserr("!dup_queue_file: can't commit %s", f1buf);
- /* NOTREACHED */
- }
-
- if (link(f1buf, f2buf) < 0)
- {
- int save_errno = errno;
-
- syserr("sendall: link(%s, %s)", f1buf, f2buf);
- if (save_errno == EEXIST)
- {
- if (unlink(f2buf) < 0)
- {
- syserr("!sendall: unlink(%s): permanent",
- f2buf);
- /* NOTREACHED */
- }
- if (link(f1buf, f2buf) < 0)
- {
- syserr("!sendall: link(%s, %s): permanent",
- f1buf, f2buf);
- /* NOTREACHED */
- }
- }
- }
- SYNC_DIR(f2buf, true);
-}
-/*
-** DOFORK -- do a fork, retrying a couple of times on failure.
-**
-** This MUST be a macro, since after a vfork we are running
-** two processes on the same stack!!!
-**
-** Parameters:
-** none.
-**
-** Returns:
-** From a macro??? You've got to be kidding!
-**
-** Side Effects:
-** Modifies the ==> LOCAL <== variable 'pid', leaving:
-** pid of child in parent, zero in child.
-** -1 on unrecoverable error.
-**
-** Notes:
-** I'm awfully sorry this looks so awful. That's
-** vfork for you.....
-*/
-
-#define NFORKTRIES 5
-
-#ifndef FORK
-# define FORK fork
-#endif /* ! FORK */
-
-#define DOFORK(fORKfN) \
-{\
- register int i;\
-\
- for (i = NFORKTRIES; --i >= 0; )\
- {\
- pid = fORKfN();\
- if (pid >= 0)\
- break;\
- if (i > 0)\
- (void) sleep((unsigned) NFORKTRIES - i);\
- }\
-}
-/*
-** DOFORK -- simple fork interface to DOFORK.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** pid of child in parent.
-** zero in child.
-** -1 on error.
-**
-** Side Effects:
-** returns twice, once in parent and once in child.
-*/
-
-pid_t
-dofork()
-{
- register pid_t pid = -1;
-
- DOFORK(fork);
- return pid;
-}
-
-/*
-** COLONCMP -- compare host-signatures up to first ':' or EOS
-**
-** This takes two strings which happen to be host-signatures and
-** compares them. If the lowest preference portions of the MX-RR's
-** match (up to ':' or EOS, whichever is first), then we have
-** match. This is used for coattail-piggybacking messages during
-** message delivery.
-** If the signatures are the same up to the first ':' the remainder of
-** the signatures are then compared with a normal strcmp(). This saves
-** re-examining the first part of the signatures.
-**
-** Parameters:
-** a - first host-signature
-** b - second host-signature
-**
-** Returns:
-** HS_MATCH_NO -- no "match".
-** HS_MATCH_FIRST -- "match" for the first MX preference
-** (up to the first colon (':')).
-** HS_MATCH_FULL -- match for the entire MX record.
-**
-** Side Effects:
-** none.
-*/
-
-#define HS_MATCH_NO 0
-#define HS_MATCH_FIRST 1
-#define HS_MATCH_FULL 2
-
-static int
-coloncmp(a, b)
- register const char *a;
- register const char *b;
-{
- int ret = HS_MATCH_NO;
- int braclev = 0;
-
- while (*a == *b++)
- {
- /* Need to account for IPv6 bracketed addresses */
- if (*a == '[')
- braclev++;
- else if (*a == ']' && braclev > 0)
- braclev--;
- else if (*a == ':' && braclev <= 0)
- {
- ret = HS_MATCH_FIRST;
- a++;
- break;
- }
- else if (*a == '\0')
- return HS_MATCH_FULL; /* a full match */
- a++;
- }
- if (ret == HS_MATCH_NO &&
- braclev <= 0 &&
- ((*a == '\0' && *(b - 1) == ':') ||
- (*a == ':' && *(b - 1) == '\0')))
- return HS_MATCH_FIRST;
- if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
- return HS_MATCH_FULL;
-
- return ret;
-}
-
-/*
-** SHOULD_TRY_FBSH -- Should try FallbackSmartHost?
-**
-** Parameters:
-** e -- envelope
-** tried_fallbacksmarthost -- has been tried already? (in/out)
-** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out)
-** hbsz -- size of hostbuf
-** status -- current delivery status
-**
-** Returns:
-** true iff FallbackSmartHost should be tried.
-*/
-
-static bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int));
-
-static bool
-should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
- ENVELOPE *e;
- bool *tried_fallbacksmarthost;
- char *hostbuf;
- size_t hbsz;
- int status;
-{
- /*
- ** If the host was not found or a temporary failure occurred
- ** and a FallbackSmartHost is defined (and we have not yet
- ** tried it), then make one last try with it as the host.
- */
-
- if ((status == EX_NOHOST || status == EX_TEMPFAIL) &&
- FallbackSmartHost != NULL && !*tried_fallbacksmarthost)
- {
- *tried_fallbacksmarthost = true;
- expand(FallbackSmartHost, hostbuf, hbsz, e);
- if (!wordinclass(hostbuf, 'w'))
- {
- if (tTd(11, 1))
- sm_dprintf("one last try with FallbackSmartHost %s\n",
- hostbuf);
- return true;
- }
- }
- return false;
-}
-/*
-** DELIVER -- Deliver a message to a list of addresses.
-**
-** This routine delivers to everyone on the same host as the
-** user on the head of the list. It is clever about mailers
-** that don't handle multiple users. It is NOT guaranteed
-** that it will deliver to all these addresses however -- so
-** deliver should be called once for each address on the
-** list.
-** Deliver tries to be as opportunistic as possible about piggybacking
-** messages. Some definitions to make understanding easier follow below.
-** Piggybacking occurs when an existing connection to a mail host can
-** be used to send the same message to more than one recipient at the
-** same time. So "no piggybacking" means one message for one recipient
-** per connection. "Intentional piggybacking" happens when the
-** recipients' host address (not the mail host address) is used to
-** attempt piggybacking. Recipients with the same host address
-** have the same mail host. "Coincidental piggybacking" relies on
-** piggybacking based on all the mail host addresses in the MX-RR. This
-** is "coincidental" in the fact it could not be predicted until the
-** MX Resource Records for the hosts were obtained and examined. For
-** example (preference order and equivalence is important, not values):
-** domain1 IN MX 10 mxhost-A
-** IN MX 20 mxhost-B
-** domain2 IN MX 4 mxhost-A
-** IN MX 8 mxhost-B
-** Domain1 and domain2 can piggyback the same message to mxhost-A or
-** mxhost-B (if mxhost-A cannot be reached).
-** "Coattail piggybacking" relaxes the strictness of "coincidental
-** piggybacking" in the hope that most significant (lowest value)
-** MX preference host(s) can create more piggybacking. For example
-** (again, preference order and equivalence is important, not values):
-** domain3 IN MX 100 mxhost-C
-** IN MX 100 mxhost-D
-** IN MX 200 mxhost-E
-** domain4 IN MX 50 mxhost-C
-** IN MX 50 mxhost-D
-** IN MX 80 mxhost-F
-** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
-** is available. Same with mxhost-D because in both RR's the preference
-** value is the same as mxhost-C, respectively.
-** So deliver attempts coattail piggybacking when possible. If the
-** first MX preference level hosts cannot be used then the piggybacking
-** reverts to coincidental piggybacking. Using the above example you
-** cannot deliver to mxhost-F for domain3 regardless of preference value.
-** ("Coattail" from "riding on the coattails of your predecessor" meaning
-** gaining benefit from a predecessor effort with no or little addition
-** effort. The predecessor here being the preceding MX RR).
-**
-** Parameters:
-** e -- the envelope to deliver.
-** firstto -- head of the address list to deliver to.
-**
-** Returns:
-** zero -- successfully delivered.
-** else -- some failure, see ExitStat for more info.
-**
-** Side Effects:
-** The standard input is passed off to someone.
-*/
-
-static int
-deliver(e, firstto)
- register ENVELOPE *e;
- ADDRESS *firstto;
-{
- char *host; /* host being sent to */
- char *user; /* user being sent to */
- char **pvp;
- register char **mvp;
- register char *p;
- register MAILER *m; /* mailer for this recipient */
- ADDRESS *volatile ctladdr;
-#if HASSETUSERCONTEXT
- ADDRESS *volatile contextaddr = NULL;
-#endif /* HASSETUSERCONTEXT */
- register MCI *volatile mci;
- register ADDRESS *SM_NONVOLATILE to = firstto;
- volatile bool clever = false; /* running user smtp to this mailer */
- ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
- int rcode; /* response code */
- SM_NONVOLATILE int lmtp_rcode = EX_OK;
- SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
- SM_NONVOLATILE int hostnum = 0; /* current MX host index */
- char *firstsig; /* signature of firstto */
- volatile pid_t pid = -1;
- char *volatile curhost;
- SM_NONVOLATILE unsigned short port = 0;
- SM_NONVOLATILE time_t enough = 0;
-#if NETUNIX
- char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */
-#endif /* NETUNIX */
- time_t xstart;
- bool suidwarn;
- bool anyok; /* at least one address was OK */
- SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
- bool ovr;
- bool quarantine;
- int strsize;
- int rcptcount;
- int ret;
- static int tobufsize = 0;
- static char *tobuf = NULL;
- char *rpath; /* translated return path */
- int mpvect[2];
- int rpvect[2];
- char *mxhosts[MAXMXHOSTS + 1];
- char *pv[MAXPV + 1];
- char buf[MAXNAME + 1];
- char cbuf[MAXPATHLEN];
-
- errno = 0;
- SM_REQUIRE(firstto != NULL); /* same as to */
- if (!QS_IS_OK(to->q_state))
- return 0;
-
- suidwarn = geteuid() == 0;
-
- SM_REQUIRE(e != NULL);
- m = to->q_mailer;
- host = to->q_host;
- CurEnv = e; /* just in case */
- e->e_statmsg = NULL;
- SmtpError[0] = '\0';
- xstart = curtime();
-
- if (tTd(10, 1))
- sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
- e->e_id, m->m_name, host, to->q_user);
- if (tTd(10, 100))
- printopenfds(false);
-
- /*
- ** Clear {client_*} macros if this is a bounce message to
- ** prevent rejection by check_compat ruleset.
- */
-
- if (bitset(EF_RESPONSE, e->e_flags))
- {
- macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
- }
-
- SM_TRY
- {
- ADDRESS *skip_back = NULL;
-
- /*
- ** Do initial argv setup.
- ** Insert the mailer name. Notice that $x expansion is
- ** NOT done on the mailer name. Then, if the mailer has
- ** a picky -f flag, we insert it as appropriate. This
- ** code does not check for 'pv' overflow; this places a
- ** manifest lower limit of 4 for MAXPV.
- ** The from address rewrite is expected to make
- ** the address relative to the other end.
- */
-
- /* rewrite from address, using rewriting rules */
- rcode = EX_OK;
- SM_ASSERT(e->e_from.q_mailer != NULL);
- if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
- p = e->e_sender;
- else
- p = e->e_from.q_paddr;
- rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
- if (strlen(rpath) > MAXSHORTSTR)
- {
- rpath = shortenstring(rpath, MAXSHORTSTR);
-
- /* avoid bogus errno */
- errno = 0;
- syserr("remotename: huge return path %s", rpath);
- }
- rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
- macdefine(&e->e_macro, A_PERM, 'g', rpath);
- macdefine(&e->e_macro, A_PERM, 'h', host);
- Errors = 0;
- pvp = pv;
- *pvp++ = m->m_argv[0];
-
- /* ignore long term host status information if mailer flag W is set */
- if (bitnset(M_NOHOSTSTAT, m->m_flags))
- IgnoreHostStatus = true;
-
- /* insert -f or -r flag as appropriate */
- if (FromFlag &&
- (bitnset(M_FOPT, m->m_flags) ||
- bitnset(M_ROPT, m->m_flags)))
- {
- if (bitnset(M_FOPT, m->m_flags))
- *pvp++ = "-f";
- else
- *pvp++ = "-r";
- *pvp++ = rpath;
- }
-
- /*
- ** Append the other fixed parts of the argv. These run
- ** up to the first entry containing "$u". There can only
- ** be one of these, and there are only a few more slots
- ** in the pv after it.
- */
-
- for (mvp = m->m_argv; (p = *++mvp) != NULL; )
- {
- /* can't use strchr here because of sign extension problems */
- while (*p != '\0')
- {
- if ((*p++ & 0377) == MACROEXPAND)
- {
- if (*p == 'u')
- break;
- }
- }
-
- if (*p != '\0')
- break;
-
- /* this entry is safe -- go ahead and process it */
- expand(*mvp, buf, sizeof(buf), e);
- *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
- if (pvp >= &pv[MAXPV - 3])
- {
- syserr("554 5.3.5 Too many parameters to %s before $u",
- pv[0]);
- rcode = -1;
- goto cleanup;
- }
- }
-
- /*
- ** If we have no substitution for the user name in the argument
- ** list, we know that we must supply the names otherwise -- and
- ** SMTP is the answer!!
- */
-
- if (*mvp == NULL)
- {
- /* running LMTP or SMTP */
- clever = true;
- *pvp = NULL;
- }
- else if (bitnset(M_LMTP, m->m_flags))
- {
- /* not running LMTP */
- sm_syslog(LOG_ERR, NULL,
- "Warning: mailer %s: LMTP flag (F=z) turned off",
- m->m_name);
- clrbitn(M_LMTP, m->m_flags);
- }
-
- /*
- ** At this point *mvp points to the argument with $u. We
- ** run through our address list and append all the addresses
- ** we can. If we run out of space, do not fret! We can
- ** always send another copy later.
- */
-
- e->e_to = NULL;
- strsize = 2;
- rcptcount = 0;
- ctladdr = NULL;
- if (firstto->q_signature == NULL)
- firstto->q_signature = hostsignature(firstto->q_mailer,
- firstto->q_host);
- firstsig = firstto->q_signature;
-
- for (; to != NULL; to = to->q_next)
- {
- /* avoid sending multiple recipients to dumb mailers */
- if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
- break;
-
- /* if already sent or not for this host, don't send */
- if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
- continue;
-
- /*
- ** Must be same mailer to keep grouping rcpts.
- ** If mailers don't match: continue; sendqueue is not
- ** sorted by mailers, so don't break;
- */
-
- if (to->q_mailer != firstto->q_mailer)
- continue;
-
- if (to->q_signature == NULL) /* for safety */
- to->q_signature = hostsignature(to->q_mailer,
- to->q_host);
-
- /*
- ** This is for coincidental and tailcoat piggybacking messages
- ** to the same mail host. While the signatures are identical
- ** (that's the MX-RR's are identical) we can do coincidental
- ** piggybacking. We try hard for coattail piggybacking
- ** with the same mail host when the next recipient has the
- ** same host at lowest preference. It may be that this
- ** won't work out, so 'skip_back' is maintained if a backup
- ** to coincidental piggybacking or full signature must happen.
- */
-
- ret = firstto == to ? HS_MATCH_FULL :
- coloncmp(to->q_signature, firstsig);
- if (ret == HS_MATCH_FULL)
- skip_back = to;
- else if (ret == HS_MATCH_NO)
- break;
-
- if (!clever)
- {
- /* avoid overflowing tobuf */
- strsize += strlen(to->q_paddr) + 1;
- if (strsize > TOBUFSIZE)
- break;
- }
-
- if (++rcptcount > to->q_mailer->m_maxrcpt)
- break;
-
- if (tTd(10, 1))
- {
- sm_dprintf("\nsend to ");
- printaddr(sm_debug_file(), to, false);
- }
-
- /* compute effective uid/gid when sending */
- if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
-# if HASSETUSERCONTEXT
- contextaddr = ctladdr = getctladdr(to);
-# else /* HASSETUSERCONTEXT */
- ctladdr = getctladdr(to);
-# endif /* HASSETUSERCONTEXT */
-
- if (tTd(10, 2))
- {
- sm_dprintf("ctladdr=");
- printaddr(sm_debug_file(), ctladdr, false);
- }
-
- user = to->q_user;
- e->e_to = to->q_paddr;
-
- /*
- ** Check to see that these people are allowed to
- ** talk to each other.
- ** Check also for overflow of e_msgsize.
- */
-
- if (m->m_maxsize != 0 &&
- (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
- {
- e->e_flags |= EF_NO_BODY_RETN;
- if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
- to->q_status = "5.2.3";
- else
- to->q_status = "5.3.4";
-
- /* set to->q_rstatus = NULL; or to the following? */
- usrerrenh(to->q_status,
- "552 Message is too large; %ld bytes max",
- m->m_maxsize);
- markfailure(e, to, NULL, EX_UNAVAILABLE, false);
- giveresponse(EX_UNAVAILABLE, to->q_status, m,
- NULL, ctladdr, xstart, e, to);
- continue;
- }
- SM_SET_H_ERRNO(0);
- ovr = true;
-
- /* do config file checking of compatibility */
- quarantine = (e->e_quarmsg != NULL);
- rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
- e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
- e->e_id, NULL);
- if (rcode == EX_OK)
- {
- /* do in-code checking if not discarding */
- if (!bitset(EF_DISCARD, e->e_flags))
- {
- rcode = checkcompat(to, e);
- ovr = false;
- }
- }
- if (rcode != EX_OK)
- {
- markfailure(e, to, NULL, rcode, ovr);
- giveresponse(rcode, to->q_status, m,
- NULL, ctladdr, xstart, e, to);
- continue;
- }
- if (!quarantine && e->e_quarmsg != NULL)
- {
- /*
- ** check_compat or checkcompat() has tried
- ** to quarantine but that isn't supported.
- ** Revert the attempt.
- */
-
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), "");
- }
- if (bitset(EF_DISCARD, e->e_flags))
- {
- if (tTd(10, 5))
- {
- sm_dprintf("deliver: discarding recipient ");
- printaddr(sm_debug_file(), to, false);
- }
-
- /* pretend the message was sent */
- /* XXX should we log something here? */
- to->q_state = QS_DISCARDED;
-
- /*
- ** Remove discard bit to prevent discard of
- ** future recipients. This is safe because the
- ** true "global discard" has been handled before
- ** we get here.
- */
-
- e->e_flags &= ~EF_DISCARD;
- continue;
- }
-
- /*
- ** Strip quote bits from names if the mailer is dumb
- ** about them.
- */
-
- if (bitnset(M_STRIPQ, m->m_flags))
- {
- stripquotes(user);
- stripquotes(host);
- }
-
- /*
- ** Strip all leading backslashes if requested and the
- ** next character is alphanumerical (the latter can
- ** probably relaxed a bit, see RFC2821).
- */
-
- if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
- stripbackslash(user);
-
- /* hack attack -- delivermail compatibility */
- if (m == ProgMailer && *user == '|')
- user++;
-
- /*
- ** If an error message has already been given, don't
- ** bother to send to this address.
- **
- ** >>>>>>>>>> This clause assumes that the local mailer
- ** >> NOTE >> cannot do any further aliasing; that
- ** >>>>>>>>>> function is subsumed by sendmail.
- */
-
- if (!QS_IS_OK(to->q_state))
- continue;
-
- /*
- ** See if this user name is "special".
- ** If the user name has a slash in it, assume that this
- ** is a file -- send it off without further ado. Note
- ** that this type of addresses is not processed along
- ** with the others, so we fudge on the To person.
- */
-
- if (strcmp(m->m_mailer, "[FILE]") == 0)
- {
- macdefine(&e->e_macro, A_PERM, 'u', user);
- p = to->q_home;
- if (p == NULL && ctladdr != NULL)
- p = ctladdr->q_home;
- macdefine(&e->e_macro, A_PERM, 'z', p);
- expand(m->m_argv[1], buf, sizeof(buf), e);
- if (strlen(buf) > 0)
- rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
- else
- {
- syserr("empty filename specification for mailer %s",
- m->m_name);
- rcode = EX_CONFIG;
- }
- giveresponse(rcode, to->q_status, m, NULL,
- ctladdr, xstart, e, to);
- markfailure(e, to, NULL, rcode, true);
- e->e_nsent++;
- if (rcode == EX_OK)
- {
- to->q_state = QS_SENT;
- if (bitnset(M_LOCALMAILER, m->m_flags) &&
- bitset(QPINGONSUCCESS, to->q_flags))
- {
- to->q_flags |= QDELIVERED;
- to->q_status = "2.1.5";
- (void) sm_io_fprintf(e->e_xfp,
- SM_TIME_DEFAULT,
- "%s... Successfully delivered\n",
- to->q_paddr);
- }
- }
- to->q_statdate = curtime();
- markstats(e, to, STATS_NORMAL);
- continue;
- }
-
- /*
- ** Address is verified -- add this user to mailer
- ** argv, and add it to the print list of recipients.
- */
-
- /* link together the chain of recipients */
- to->q_tchain = tochain;
- tochain = to;
- e->e_to = "[CHAIN]";
-
- macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */
- p = to->q_home;
- if (p == NULL && ctladdr != NULL)
- p = ctladdr->q_home;
- macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */
-
- /* set the ${dsn_notify} macro if applicable */
- if (bitset(QHASNOTIFY, to->q_flags))
- {
- char notify[MAXLINE];
-
- notify[0] = '\0';
- if (bitset(QPINGONSUCCESS, to->q_flags))
- (void) sm_strlcat(notify, "SUCCESS,",
- sizeof(notify));
- if (bitset(QPINGONFAILURE, to->q_flags))
- (void) sm_strlcat(notify, "FAILURE,",
- sizeof(notify));
- if (bitset(QPINGONDELAY, to->q_flags))
- (void) sm_strlcat(notify, "DELAY,",
- sizeof(notify));
-
- /* Set to NEVER or drop trailing comma */
- if (notify[0] == '\0')
- (void) sm_strlcat(notify, "NEVER",
- sizeof(notify));
- else
- notify[strlen(notify) - 1] = '\0';
-
- macdefine(&e->e_macro, A_TEMP,
- macid("{dsn_notify}"), notify);
- }
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_notify}"), NULL);
-
- /*
- ** Expand out this user into argument list.
- */
-
- if (!clever)
- {
- expand(*mvp, buf, sizeof(buf), e);
- *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
- if (pvp >= &pv[MAXPV - 2])
- {
- /* allow some space for trailing parms */
- break;
- }
- }
- }
-
- /* see if any addresses still exist */
- if (tochain == NULL)
- {
- rcode = 0;
- goto cleanup;
- }
-
- /* print out messages as full list */
- strsize = 1;
- for (to = tochain; to != NULL; to = to->q_tchain)
- strsize += strlen(to->q_paddr) + 1;
- if (strsize < TOBUFSIZE)
- strsize = TOBUFSIZE;
- if (strsize > tobufsize)
- {
- SM_FREE_CLR(tobuf);
- tobuf = sm_pmalloc_x(strsize);
- tobufsize = strsize;
- }
- p = tobuf;
- *p = '\0';
- for (to = tochain; to != NULL; to = to->q_tchain)
- {
- (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
- ",", to->q_paddr);
- p += strlen(p);
- }
- e->e_to = tobuf + 1;
-
- /*
- ** Fill out any parameters after the $u parameter.
- */
-
- if (!clever)
- {
- while (*++mvp != NULL)
- {
- expand(*mvp, buf, sizeof(buf), e);
- *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
- if (pvp >= &pv[MAXPV])
- syserr("554 5.3.0 deliver: pv overflow after $u for %s",
- pv[0]);
- }
- }
- *pvp++ = NULL;
-
- /*
- ** Call the mailer.
- ** The argument vector gets built, pipes
- ** are created as necessary, and we fork & exec as
- ** appropriate.
- ** If we are running SMTP, we just need to clean up.
- */
-
- /* XXX this seems a bit wierd */
- if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
- bitset(QGOODUID, e->e_from.q_flags))
- ctladdr = &e->e_from;
-
-#if NAMED_BIND
- if (ConfigLevel < 2)
- _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
-#endif /* NAMED_BIND */
-
- if (tTd(11, 1))
- {
- sm_dprintf("openmailer:");
- printav(sm_debug_file(), pv);
- }
- errno = 0;
- SM_SET_H_ERRNO(0);
- CurHostName = NULL;
-
- /*
- ** Deal with the special case of mail handled through an IPC
- ** connection.
- ** In this case we don't actually fork. We must be
- ** running SMTP for this to work. We will return a
- ** zero pid to indicate that we are running IPC.
- ** We also handle a debug version that just talks to stdin/out.
- */
-
- curhost = NULL;
- SmtpPhase = NULL;
- mci = NULL;
-
-#if XDEBUG
- {
- char wbuf[MAXLINE];
-
- /* make absolutely certain 0, 1, and 2 are in use */
- (void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
- checkfd012(wbuf);
- }
-#endif /* XDEBUG */
-
- /* check for 8-bit available */
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- bitnset(M_7BITS, m->m_flags) &&
- (bitset(EF_DONT_MIME, e->e_flags) ||
- !(bitset(MM_MIME8BIT, MimeMode) ||
- (bitset(EF_IS_MIME, e->e_flags) &&
- bitset(MM_CVTMIME, MimeMode)))))
- {
- e->e_status = "5.6.3";
- usrerrenh(e->e_status,
- "554 Cannot send 8-bit data to 7-bit destination");
- rcode = EX_DATAERR;
- goto give_up;
- }
-
- if (tTd(62, 8))
- checkfds("before delivery");
-
- /* check for Local Person Communication -- not for mortals!!! */
- if (strcmp(m->m_mailer, "[LPC]") == 0)
- {
- if (clever)
- {
- /* flush any expired connections */
- (void) mci_scan(NULL);
-
- /* try to get a cached connection or just a slot */
- mci = mci_get(m->m_name, m);
- if (mci->mci_host == NULL)
- mci->mci_host = m->m_name;
- CurHostName = mci->mci_host;
- if (mci->mci_state != MCIS_CLOSED)
- {
- message("Using cached SMTP/LPC connection for %s...",
- m->m_name);
- mci->mci_deliveries++;
- goto do_transfer;
- }
- }
- else
- {
- mci = mci_new(e->e_rpool);
- }
- mci->mci_in = smioin;
- mci->mci_out = smioout;
- mci->mci_mailer = m;
- mci->mci_host = m->m_name;
- if (clever)
- {
- mci->mci_state = MCIS_OPENING;
- mci_cache(mci);
- }
- else
- mci->mci_state = MCIS_OPEN;
- }
- else if (strcmp(m->m_mailer, "[IPC]") == 0)
- {
- register int i;
-
- if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
- {
- syserr("null destination for %s mailer", m->m_mailer);
- rcode = EX_CONFIG;
- goto give_up;
- }
-
-# if NETUNIX
- if (strcmp(pv[0], "FILE") == 0)
- {
- curhost = CurHostName = "localhost";
- mux_path = pv[1];
- }
- else
-# endif /* NETUNIX */
- {
- CurHostName = pv[1];
- curhost = hostsignature(m, pv[1]);
- }
-
- if (curhost == NULL || curhost[0] == '\0')
- {
- syserr("null host signature for %s", pv[1]);
- rcode = EX_CONFIG;
- goto give_up;
- }
-
- if (!clever)
- {
- syserr("554 5.3.5 non-clever IPC");
- rcode = EX_CONFIG;
- goto give_up;
- }
- if (pv[2] != NULL
-# if NETUNIX
- && mux_path == NULL
-# endif /* NETUNIX */
- )
- {
- port = htons((unsigned short) atoi(pv[2]));
- if (port == 0)
- {
-# ifdef NO_GETSERVBYNAME
- syserr("Invalid port number: %s", pv[2]);
-# else /* NO_GETSERVBYNAME */
- struct servent *sp = getservbyname(pv[2], "tcp");
-
- if (sp == NULL)
- syserr("Service %s unknown", pv[2]);
- else
- port = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
- }
- }
-
- nummxhosts = parse_hostsignature(curhost, mxhosts, m);
- if (TimeOuts.to_aconnect > 0)
- enough = curtime() + TimeOuts.to_aconnect;
-tryhost:
- while (hostnum < nummxhosts)
- {
- char sep = ':';
- char *endp;
- static char hostbuf[MAXNAME + 1];
- bool tried_fallbacksmarthost = false;
-
-# if NETINET6
- if (*mxhosts[hostnum] == '[')
- {
- endp = strchr(mxhosts[hostnum] + 1, ']');
- if (endp != NULL)
- endp = strpbrk(endp + 1, ":,");
- }
- else
- endp = strpbrk(mxhosts[hostnum], ":,");
-# else /* NETINET6 */
- endp = strpbrk(mxhosts[hostnum], ":,");
-# endif /* NETINET6 */
- if (endp != NULL)
- {
- sep = *endp;
- *endp = '\0';
- }
-
- if (hostnum == 1 && skip_back != NULL)
- {
- /*
- ** Coattail piggybacking is no longer an
- ** option with the mail host next to be tried
- ** no longer the lowest MX preference
- ** (hostnum == 1 meaning we're on the second
- ** preference). We do not try to coattail
- ** piggyback more than the first MX preference.
- ** Revert 'tochain' to last location for
- ** coincidental piggybacking. This works this
- ** easily because the q_tchain kept getting
- ** added to the top of the linked list.
- */
-
- tochain = skip_back;
- }
-
- if (*mxhosts[hostnum] == '\0')
- {
- syserr("deliver: null host name in signature");
- hostnum++;
- if (endp != NULL)
- *endp = sep;
- continue;
- }
- (void) sm_strlcpy(hostbuf, mxhosts[hostnum],
- sizeof(hostbuf));
- hostnum++;
- if (endp != NULL)
- *endp = sep;
-
- one_last_try:
- /* see if we already know that this host is fried */
- CurHostName = hostbuf;
- mci = mci_get(hostbuf, m);
- if (mci->mci_state != MCIS_CLOSED)
- {
- char *type;
-
- if (tTd(11, 1))
- {
- sm_dprintf("openmailer: ");
- mci_dump(sm_debug_file(), mci, false);
- }
- CurHostName = mci->mci_host;
- if (bitnset(M_LMTP, m->m_flags))
- type = "L";
- else if (bitset(MCIF_ESMTP, mci->mci_flags))
- type = "ES";
- else
- type = "S";
- message("Using cached %sMTP connection to %s via %s...",
- type, hostbuf, m->m_name);
- mci->mci_deliveries++;
- break;
- }
- mci->mci_mailer = m;
- if (mci->mci_exitstat != EX_OK)
- {
- if (mci->mci_exitstat == EX_TEMPFAIL)
- goodmxfound = true;
-
- /* Try FallbackSmartHost? */
- if (should_try_fbsh(e, &tried_fallbacksmarthost,
- hostbuf, sizeof(hostbuf),
- mci->mci_exitstat))
- goto one_last_try;
-
- continue;
- }
-
- if (mci_lock_host(mci) != EX_OK)
- {
- mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
- goodmxfound = true;
- continue;
- }
-
- /* try the connection */
- sm_setproctitle(true, e, "%s %s: %s",
- qid_printname(e),
- hostbuf, "user open");
-# if NETUNIX
- if (mux_path != NULL)
- {
- message("Connecting to %s via %s...",
- mux_path, m->m_name);
- i = makeconnection_ds((char *) mux_path, mci);
- }
- else
-# endif /* NETUNIX */
- {
- if (port == 0)
- message("Connecting to %s via %s...",
- hostbuf, m->m_name);
- else
- message("Connecting to %s port %d via %s...",
- hostbuf, ntohs(port),
- m->m_name);
- i = makeconnection(hostbuf, port, mci, e,
- enough);
- }
- mci->mci_errno = errno;
- mci->mci_lastuse = curtime();
- mci->mci_deliveries = 0;
- mci->mci_exitstat = i;
-# if NAMED_BIND
- mci->mci_herrno = h_errno;
-# endif /* NAMED_BIND */
-
- /*
- ** Have we tried long enough to get a connection?
- ** If yes, skip to the fallback MX hosts
- ** (if existent).
- */
-
- if (enough > 0 && mci->mci_lastuse >= enough)
- {
- int h;
-# if NAMED_BIND
- extern int NumFallbackMXHosts;
-# else /* NAMED_BIND */
- const int NumFallbackMXHosts = 0;
-# endif /* NAMED_BIND */
-
- if (hostnum < nummxhosts && LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "Timeout.to_aconnect occurred before exhausting all addresses");
-
- /* turn off timeout if fallback available */
- if (NumFallbackMXHosts > 0)
- enough = 0;
-
- /* skip to a fallback MX host */
- h = nummxhosts - NumFallbackMXHosts;
- if (hostnum < h)
- hostnum = h;
- }
- if (i == EX_OK)
- {
- goodmxfound = true;
- markstats(e, firstto, STATS_CONNECT);
- mci->mci_state = MCIS_OPENING;
- mci_cache(mci);
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "%05d === CONNECT %s\n",
- (int) CurrentPid,
- hostbuf);
- break;
- }
- else
- {
- /* Try FallbackSmartHost? */
- if (should_try_fbsh(e, &tried_fallbacksmarthost,
- hostbuf, sizeof(hostbuf), i))
- goto one_last_try;
-
- if (tTd(11, 1))
- sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
- i, errno);
- if (i == EX_TEMPFAIL)
- goodmxfound = true;
- mci_unlock_host(mci);
- }
-
- /* enter status of this host */
- setstat(i);
-
- /* should print some message here for -v mode */
- }
- if (mci == NULL)
- {
- syserr("deliver: no host name");
- rcode = EX_SOFTWARE;
- goto give_up;
- }
- mci->mci_pid = 0;
- }
- else
- {
- /* flush any expired connections */
- (void) mci_scan(NULL);
- mci = NULL;
-
- if (bitnset(M_LMTP, m->m_flags))
- {
- /* try to get a cached connection */
- mci = mci_get(m->m_name, m);
- if (mci->mci_host == NULL)
- mci->mci_host = m->m_name;
- CurHostName = mci->mci_host;
- if (mci->mci_state != MCIS_CLOSED)
- {
- message("Using cached LMTP connection for %s...",
- m->m_name);
- mci->mci_deliveries++;
- goto do_transfer;
- }
- }
-
- /* announce the connection to verbose listeners */
- if (host == NULL || host[0] == '\0')
- message("Connecting to %s...", m->m_name);
- else
- message("Connecting to %s via %s...", host, m->m_name);
- if (TrafficLogFile != NULL)
- {
- char **av;
-
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d === EXEC", (int) CurrentPid);
- for (av = pv; *av != NULL; av++)
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT, " %s",
- *av);
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "\n");
- }
-
-#if XDEBUG
- checkfd012("before creating mail pipe");
-#endif /* XDEBUG */
-
- /* create a pipe to shove the mail through */
- if (pipe(mpvect) < 0)
- {
- syserr("%s... openmailer(%s): pipe (to mailer)",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
- if (tTd(11, 1))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
-
-#if XDEBUG
- /* make sure we didn't get one of the standard I/O files */
- if (mpvect[0] < 3 || mpvect[1] < 3)
- {
- syserr("%s... openmailer(%s): bogus mpvect %d %d",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
- mpvect[0], mpvect[1]);
- printopenfds(true);
- if (tTd(11, 1))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
-
- /* make sure system call isn't dead meat */
- checkfdopen(mpvect[0], "mpvect[0]");
- checkfdopen(mpvect[1], "mpvect[1]");
- if (mpvect[0] == mpvect[1] ||
- (e->e_lockfp != NULL &&
- (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
- NULL) ||
- mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
- NULL))))
- {
- if (e->e_lockfp == NULL)
- syserr("%s... openmailer(%s): overlapping mpvect %d %d",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0], mpvect[1]);
- else
- syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0], mpvect[1],
- sm_io_getinfo(e->e_lockfp,
- SM_IO_WHAT_FD, NULL));
- }
-#endif /* XDEBUG */
-
- /* create a return pipe */
- if (pipe(rpvect) < 0)
- {
- syserr("%s... openmailer(%s): pipe (from mailer)",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
- (void) close(mpvect[0]);
- (void) close(mpvect[1]);
- if (tTd(11, 1))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
-#if XDEBUG
- checkfdopen(rpvect[0], "rpvect[0]");
- checkfdopen(rpvect[1], "rpvect[1]");
-#endif /* XDEBUG */
-
- /*
- ** Actually fork the mailer process.
- ** DOFORK is clever about retrying.
- **
- ** Dispose of SIGCHLD signal catchers that may be laying
- ** around so that endmailer will get it.
- */
-
- if (e->e_xfp != NULL) /* for debugging */
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- (void) sm_signal(SIGCHLD, SIG_DFL);
-
-
- DOFORK(FORK);
- /* pid is set by DOFORK */
-
- if (pid < 0)
- {
- /* failure */
- syserr("%s... openmailer(%s): cannot fork",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
- (void) close(mpvect[0]);
- (void) close(mpvect[1]);
- (void) close(rpvect[0]);
- (void) close(rpvect[1]);
- if (tTd(11, 1))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
- else if (pid == 0)
- {
- int save_errno;
- int sff;
- int new_euid = NO_UID;
- int new_ruid = NO_UID;
- int new_gid = NO_GID;
- char *user = NULL;
- struct stat stb;
- extern int DtableSize;
-
- CurrentPid = getpid();
-
- /* clear the events to turn off SIGALRMs */
- sm_clear_events();
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- if (e->e_lockfp != NULL)
- (void) close(sm_io_getinfo(e->e_lockfp,
- SM_IO_WHAT_FD,
- NULL));
-
- /* child -- set up input & exec mailer */
- (void) sm_signal(SIGALRM, sm_signal_noop);
- (void) sm_signal(SIGCHLD, SIG_DFL);
- (void) sm_signal(SIGHUP, SIG_IGN);
- (void) sm_signal(SIGINT, SIG_IGN);
- (void) sm_signal(SIGTERM, SIG_DFL);
-# ifdef SIGUSR1
- (void) sm_signal(SIGUSR1, sm_signal_noop);
-# endif /* SIGUSR1 */
-
- if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
- stb.st_mode = 0;
-
-# if HASSETUSERCONTEXT
- /*
- ** Set user resources.
- */
-
- if (contextaddr != NULL)
- {
- int sucflags;
- struct passwd *pwd;
-
- if (contextaddr->q_ruser != NULL)
- pwd = sm_getpwnam(contextaddr->q_ruser);
- else
- pwd = sm_getpwnam(contextaddr->q_user);
- sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
-#ifdef LOGIN_SETMAC
- sucflags |= LOGIN_SETMAC;
-#endif /* LOGIN_SETMAC */
- if (pwd != NULL &&
- setusercontext(NULL, pwd, pwd->pw_uid,
- sucflags) == -1 &&
- suidwarn)
- {
- syserr("openmailer: setusercontext() failed");
- exit(EX_TEMPFAIL);
- }
- }
-# endif /* HASSETUSERCONTEXT */
-
-#if HASNICE
- /* tweak niceness */
- if (m->m_nice != 0)
- (void) nice(m->m_nice);
-#endif /* HASNICE */
-
- /* reset group id */
- if (bitnset(M_SPECIFIC_UID, m->m_flags))
- {
- if (m->m_gid == NO_GID)
- new_gid = RunAsGid;
- else
- new_gid = m->m_gid;
- }
- else if (bitset(S_ISGID, stb.st_mode))
- new_gid = stb.st_gid;
- else if (ctladdr != NULL && ctladdr->q_gid != 0)
- {
- if (!DontInitGroups)
- {
- user = ctladdr->q_ruser;
- if (user == NULL)
- user = ctladdr->q_user;
-
- if (initgroups(user,
- ctladdr->q_gid) == -1
- && suidwarn)
- {
- syserr("openmailer: initgroups(%s, %d) failed",
- user, ctladdr->q_gid);
- exit(EX_TEMPFAIL);
- }
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = ctladdr->q_gid;
- if (setgroups(1, gidset) == -1
- && suidwarn)
- {
- syserr("openmailer: setgroups() failed");
- exit(EX_TEMPFAIL);
- }
- }
- new_gid = ctladdr->q_gid;
- }
- else
- {
- if (!DontInitGroups)
- {
- user = DefUser;
- if (initgroups(DefUser, DefGid) == -1 &&
- suidwarn)
- {
- syserr("openmailer: initgroups(%s, %d) failed",
- DefUser, DefGid);
- exit(EX_TEMPFAIL);
- }
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = DefGid;
- if (setgroups(1, gidset) == -1
- && suidwarn)
- {
- syserr("openmailer: setgroups() failed");
- exit(EX_TEMPFAIL);
- }
- }
- if (m->m_gid == NO_GID)
- new_gid = DefGid;
- else
- new_gid = m->m_gid;
- }
- if (new_gid != NO_GID)
- {
- if (RunAsUid != 0 &&
- bitnset(M_SPECIFIC_UID, m->m_flags) &&
- new_gid != getgid() &&
- new_gid != getegid())
- {
- /* Only root can change the gid */
- syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d",
- (int) RunAsUid, (int) new_gid,
- (int) getgid(), (int) getegid());
- exit(EX_TEMPFAIL);
- }
-
- if (setgid(new_gid) < 0 && suidwarn)
- {
- syserr("openmailer: setgid(%ld) failed",
- (long) new_gid);
- exit(EX_TEMPFAIL);
- }
- }
-
- /* change root to some "safe" directory */
- if (m->m_rootdir != NULL)
- {
- expand(m->m_rootdir, cbuf, sizeof(cbuf), e);
- if (tTd(11, 20))
- sm_dprintf("openmailer: chroot %s\n",
- cbuf);
- if (chroot(cbuf) < 0)
- {
- syserr("openmailer: Cannot chroot(%s)",
- cbuf);
- exit(EX_TEMPFAIL);
- }
- if (chdir("/") < 0)
- {
- syserr("openmailer: cannot chdir(/)");
- exit(EX_TEMPFAIL);
- }
- }
-
- /* reset user id */
- endpwent();
- sm_mbdb_terminate();
- if (bitnset(M_SPECIFIC_UID, m->m_flags))
- {
- if (m->m_uid == NO_UID)
- new_euid = RunAsUid;
- else
- new_euid = m->m_uid;
-
- /*
- ** Undo the effects of the uid change in main
- ** for signal handling. The real uid may
- ** be used by mailer in adding a "From "
- ** line.
- */
-
- if (RealUid != 0 && RealUid != getuid())
- {
-# if MAILER_SETUID_METHOD == USE_SETEUID
-# if HASSETREUID
- if (setreuid(RealUid, geteuid()) < 0)
- {
- syserr("openmailer: setreuid(%d, %d) failed",
- (int) RealUid, (int) geteuid());
- exit(EX_OSERR);
- }
-# endif /* HASSETREUID */
-# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
-# if MAILER_SETUID_METHOD == USE_SETREUID
- new_ruid = RealUid;
-# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
- }
- }
- else if (bitset(S_ISUID, stb.st_mode))
- new_ruid = stb.st_uid;
- else if (ctladdr != NULL && ctladdr->q_uid != 0)
- new_ruid = ctladdr->q_uid;
- else if (m->m_uid != NO_UID)
- new_ruid = m->m_uid;
- else
- new_ruid = DefUid;
-
-# if _FFR_USE_SETLOGIN
- /* run disconnected from terminal and set login name */
- if (setsid() >= 0 &&
- ctladdr != NULL && ctladdr->q_uid != 0 &&
- new_euid == ctladdr->q_uid)
- {
- struct passwd *pwd;
-
- pwd = sm_getpwuid(ctladdr->q_uid);
- if (pwd != NULL && suidwarn)
- (void) setlogin(pwd->pw_name);
- endpwent();
- }
-# endif /* _FFR_USE_SETLOGIN */
-
- if (new_euid != NO_UID)
- {
- if (RunAsUid != 0 && new_euid != RunAsUid)
- {
- /* Only root can change the uid */
- syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d",
- (int) new_euid, (int) RunAsUid);
- exit(EX_TEMPFAIL);
- }
-
- vendor_set_uid(new_euid);
-# if MAILER_SETUID_METHOD == USE_SETEUID
- if (seteuid(new_euid) < 0 && suidwarn)
- {
- syserr("openmailer: seteuid(%ld) failed",
- (long) new_euid);
- exit(EX_TEMPFAIL);
- }
-# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
-# if MAILER_SETUID_METHOD == USE_SETREUID
- if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
- {
- syserr("openmailer: setreuid(%ld, %ld) failed",
- (long) new_ruid, (long) new_euid);
- exit(EX_TEMPFAIL);
- }
-# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
-# if MAILER_SETUID_METHOD == USE_SETUID
- if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
- {
- syserr("openmailer: setuid(%ld) failed",
- (long) new_euid);
- exit(EX_TEMPFAIL);
- }
-# endif /* MAILER_SETUID_METHOD == USE_SETUID */
- }
- else if (new_ruid != NO_UID)
- {
- vendor_set_uid(new_ruid);
- if (setuid(new_ruid) < 0 && suidwarn)
- {
- syserr("openmailer: setuid(%ld) failed",
- (long) new_ruid);
- exit(EX_TEMPFAIL);
- }
- }
-
- if (tTd(11, 2))
- sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
- (int) getuid(), (int) geteuid(),
- (int) getgid(), (int) getegid());
-
- /* move into some "safe" directory */
- if (m->m_execdir != NULL)
- {
- char *q;
-
- for (p = m->m_execdir; p != NULL; p = q)
- {
- q = strchr(p, ':');
- if (q != NULL)
- *q = '\0';
- expand(p, cbuf, sizeof(cbuf), e);
- if (q != NULL)
- *q++ = ':';
- if (tTd(11, 20))
- sm_dprintf("openmailer: trydir %s\n",
- cbuf);
- if (cbuf[0] != '\0' &&
- chdir(cbuf) >= 0)
- break;
- }
- }
-
- /* Check safety of program to be run */
- sff = SFF_ROOTOK|SFF_EXECOK;
- if (!bitnset(DBS_RUNWRITABLEPROGRAM,
- DontBlameSendmail))
- sff |= SFF_NOGWFILES|SFF_NOWWFILES;
- if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
- DontBlameSendmail))
- sff |= SFF_NOPATHCHECK;
- else
- sff |= SFF_SAFEDIRPATH;
- ret = safefile(m->m_mailer, getuid(), getgid(),
- user, sff, 0, NULL);
- if (ret != 0)
- sm_syslog(LOG_INFO, e->e_id,
- "Warning: program %s unsafe: %s",
- m->m_mailer, sm_errstring(ret));
-
- /* arrange to filter std & diag output of command */
- (void) close(rpvect[0]);
- if (dup2(rpvect[1], STDOUT_FILENO) < 0)
- {
- syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, rpvect[1]);
- _exit(EX_OSERR);
- }
- (void) close(rpvect[1]);
-
- if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
- {
- syserr("%s... openmailer(%s): cannot dup stdout for stderr",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
- _exit(EX_OSERR);
- }
-
- /* arrange to get standard input */
- (void) close(mpvect[1]);
- if (dup2(mpvect[0], STDIN_FILENO) < 0)
- {
- syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0]);
- _exit(EX_OSERR);
- }
- (void) close(mpvect[0]);
-
- /* arrange for all the files to be closed */
- sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
-
-# if !_FFR_USE_SETLOGIN
- /* run disconnected from terminal */
- (void) setsid();
-# endif /* !_FFR_USE_SETLOGIN */
-
- /* try to execute the mailer */
- (void) execve(m->m_mailer, (ARGV_T) pv,
- (ARGV_T) UserEnviron);
- save_errno = errno;
- syserr("Cannot exec %s", m->m_mailer);
- if (bitnset(M_LOCALMAILER, m->m_flags) ||
- transienterror(save_errno))
- _exit(EX_OSERR);
- _exit(EX_UNAVAILABLE);
- }
-
- /*
- ** Set up return value.
- */
-
- if (mci == NULL)
- {
- if (clever)
- {
- /*
- ** Allocate from general heap, not
- ** envelope rpool, because this mci
- ** is going to be cached.
- */
-
- mci = mci_new(NULL);
- }
- else
- {
- /*
- ** Prevent a storage leak by allocating
- ** this from the envelope rpool.
- */
-
- mci = mci_new(e->e_rpool);
- }
- }
- mci->mci_mailer = m;
- if (clever)
- {
- mci->mci_state = MCIS_OPENING;
- mci_cache(mci);
- }
- else
- {
- mci->mci_state = MCIS_OPEN;
- }
- mci->mci_pid = pid;
- (void) close(mpvect[0]);
- mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &(mpvect[1]), SM_IO_WRONLY_B,
- NULL);
- if (mci->mci_out == NULL)
- {
- syserr("deliver: cannot create mailer output channel, fd=%d",
- mpvect[1]);
- (void) close(mpvect[1]);
- (void) close(rpvect[0]);
- (void) close(rpvect[1]);
- rcode = EX_OSERR;
- goto give_up;
- }
-
- (void) close(rpvect[1]);
- mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &(rpvect[0]), SM_IO_RDONLY_B,
- NULL);
- if (mci->mci_in == NULL)
- {
- syserr("deliver: cannot create mailer input channel, fd=%d",
- mpvect[1]);
- (void) close(rpvect[0]);
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- mci->mci_out = NULL;
- rcode = EX_OSERR;
- goto give_up;
- }
- }
-
- /*
- ** If we are in SMTP opening state, send initial protocol.
- */
-
- if (bitnset(M_7BITS, m->m_flags) &&
- (!clever || mci->mci_state == MCIS_OPENING))
- mci->mci_flags |= MCIF_7BIT;
- if (clever && mci->mci_state != MCIS_CLOSED)
- {
-# if STARTTLS || SASL
- int dotpos;
- char *srvname;
- extern SOCKADDR CurHostAddr;
-# endif /* STARTTLS || SASL */
-
-# if SASL
-# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f)
-# endif /* SASL */
-# if STARTTLS
-# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f)
-# endif /* STARTTLS */
-# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f)
-# define SET_HELO(f) f |= MCIF_ONLY_EHLO
-# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO
-
-# if STARTTLS || SASL
- /* don't use CurHostName, it is changed in many places */
- if (mci->mci_host != NULL)
- {
- srvname = mci->mci_host;
- dotpos = strlen(srvname) - 1;
- if (dotpos >= 0)
- {
- if (srvname[dotpos] == '.')
- srvname[dotpos] = '\0';
- else
- dotpos = -1;
- }
- }
- else if (mci->mci_mailer != NULL)
- {
- srvname = mci->mci_mailer->m_name;
- dotpos = -1;
- }
- else
- {
- srvname = "local";
- dotpos = -1;
- }
-
- /* don't set {server_name} to NULL or "": see getauth() */
- macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
- srvname);
-
- /* CurHostAddr is set by makeconnection() and mci_get() */
- if (CurHostAddr.sa.sa_family != 0)
- {
- macdefine(&mci->mci_macro, A_TEMP,
- macid("{server_addr}"),
- anynet_ntoa(&CurHostAddr));
- }
- else if (mci->mci_mailer != NULL)
- {
- /* mailer name is unique, use it as address */
- macdefine(&mci->mci_macro, A_PERM,
- macid("{server_addr}"),
- mci->mci_mailer->m_name);
- }
- else
- {
- /* don't set it to NULL or "": see getauth() */
- macdefine(&mci->mci_macro, A_PERM,
- macid("{server_addr}"), "0");
- }
-
- /* undo change of srvname (mci->mci_host) */
- if (dotpos >= 0)
- srvname[dotpos] = '.';
-
-reconnect: /* after switching to an encrypted connection */
-# endif /* STARTTLS || SASL */
-
- /* set the current connection information */
- e->e_mci = mci;
-# if SASL
- mci->mci_saslcap = NULL;
-# endif /* SASL */
- smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
- CLR_HELO(mci->mci_flags);
-
- if (IS_DLVR_RETURN(e))
- {
- /*
- ** Check whether other side can deliver e-mail
- ** fast enough
- */
-
- if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
- {
- e->e_status = "5.4.7";
- usrerrenh(e->e_status,
- "554 Server does not support Deliver By");
- rcode = EX_UNAVAILABLE;
- goto give_up;
- }
- if (e->e_deliver_by > 0 &&
- e->e_deliver_by - (curtime() - e->e_ctime) <
- mci->mci_min_by)
- {
- e->e_status = "5.4.7";
- usrerrenh(e->e_status,
- "554 Message can't be delivered in time; %ld < %ld",
- e->e_deliver_by - (curtime() - e->e_ctime),
- mci->mci_min_by);
- rcode = EX_UNAVAILABLE;
- goto give_up;
- }
- }
-
-# if STARTTLS
- /* first TLS then AUTH to provide a security layer */
- if (mci->mci_state != MCIS_CLOSED &&
- !DONE_STARTTLS(mci->mci_flags))
- {
- int olderrors;
- bool usetls;
- bool saveQuickAbort = QuickAbort;
- bool saveSuprErrs = SuprErrs;
- char *host = NULL;
-
- rcode = EX_OK;
- usetls = bitset(MCIF_TLS, mci->mci_flags);
- if (usetls)
- usetls = !iscltflgset(e, D_NOTLS);
-
- host = macvalue(macid("{server_name}"), e);
- if (usetls)
- {
- olderrors = Errors;
- QuickAbort = false;
- SuprErrs = true;
- if (rscheck("try_tls", host, NULL, e,
- RSF_RMCOMM, 7, host, NOQID, NULL)
- != EX_OK
- || Errors > olderrors)
- {
- usetls = false;
- }
- SuprErrs = saveSuprErrs;
- QuickAbort = saveQuickAbort;
- }
-
- if (usetls)
- {
- if ((rcode = starttls(m, mci, e)) == EX_OK)
- {
- /* start again without STARTTLS */
- mci->mci_flags |= MCIF_TLSACT;
- }
- else
- {
- char *s;
-
- /*
- ** TLS negotation failed, what to do?
- ** fall back to unencrypted connection
- ** or abort? How to decide?
- ** set a macro and call a ruleset.
- */
-
- mci->mci_flags &= ~MCIF_TLS;
- switch (rcode)
- {
- case EX_TEMPFAIL:
- s = "TEMP";
- break;
- case EX_USAGE:
- s = "USAGE";
- break;
- case EX_PROTOCOL:
- s = "PROTOCOL";
- break;
- case EX_SOFTWARE:
- s = "SOFTWARE";
- break;
- case EX_UNAVAILABLE:
- s = "NONE";
- break;
-
- /* everything else is a failure */
- default:
- s = "FAILURE";
- rcode = EX_TEMPFAIL;
- }
- macdefine(&e->e_macro, A_PERM,
- macid("{verify}"), s);
- }
- }
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{verify}"), "NONE");
- olderrors = Errors;
- QuickAbort = false;
- SuprErrs = true;
-
- /*
- ** rcode == EX_SOFTWARE is special:
- ** the TLS negotation failed
- ** we have to drop the connection no matter what
- ** However, we call tls_server to give it the chance
- ** to log the problem and return an appropriate
- ** error code.
- */
-
- if (rscheck("tls_server",
- macvalue(macid("{verify}"), e),
- NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
- host, NOQID, NULL) != EX_OK ||
- Errors > olderrors ||
- rcode == EX_SOFTWARE)
- {
- char enhsc[ENHSCLEN];
- extern char MsgBuf[];
-
- if (ISSMTPCODE(MsgBuf) &&
- extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
- {
- p = sm_rpool_strdup_x(e->e_rpool,
- MsgBuf);
- }
- else
- {
- p = "403 4.7.0 server not authenticated.";
- (void) sm_strlcpy(enhsc, "4.7.0",
- sizeof(enhsc));
- }
- SuprErrs = saveSuprErrs;
- QuickAbort = saveQuickAbort;
-
- if (rcode == EX_SOFTWARE)
- {
- /* drop the connection */
- mci->mci_state = MCIS_QUITING;
- if (mci->mci_in != NULL)
- {
- (void) sm_io_close(mci->mci_in,
- SM_TIME_DEFAULT);
- mci->mci_in = NULL;
- }
- mci->mci_flags &= ~MCIF_TLSACT;
- (void) endmailer(mci, e, pv);
- }
- else
- {
- /* abort transfer */
- smtpquit(m, mci, e);
- }
-
- /* avoid bogus error msg */
- mci->mci_errno = 0;
-
- /* temp or permanent failure? */
- rcode = (*p == '4') ? EX_TEMPFAIL
- : EX_UNAVAILABLE;
- mci_setstat(mci, rcode, enhsc, p);
-
- /*
- ** hack to get the error message into
- ** the envelope (done in giveresponse())
- */
-
- (void) sm_strlcpy(SmtpError, p,
- sizeof(SmtpError));
- }
- else if (mci->mci_state == MCIS_CLOSED)
- {
- /* connection close caused by 421 */
- mci->mci_errno = 0;
- rcode = EX_TEMPFAIL;
- mci_setstat(mci, rcode, NULL, "421");
- }
- else
- rcode = 0;
-
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
- if (DONE_STARTTLS(mci->mci_flags) &&
- mci->mci_state != MCIS_CLOSED)
- {
- SET_HELO(mci->mci_flags);
- mci->mci_flags &= ~MCIF_EXTENS;
- goto reconnect;
- }
- }
-# endif /* STARTTLS */
-# if SASL
- /* if other server supports authentication let's authenticate */
- if (mci->mci_state != MCIS_CLOSED &&
- mci->mci_saslcap != NULL &&
- !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
- {
- /* Should we require some minimum authentication? */
- if ((ret = smtpauth(m, mci, e)) == EX_OK)
- {
- int result;
- sasl_ssf_t *ssf = NULL;
-
- /* Get security strength (features) */
- result = sasl_getprop(mci->mci_conn, SASL_SSF,
-# if SASL >= 20000
- (const void **) &ssf);
-# else /* SASL >= 20000 */
- (void **) &ssf);
-# endif /* SASL >= 20000 */
-
- /* XXX authid? */
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
- mci->mci_host,
- macvalue(macid("{auth_type}"), e),
- result == SASL_OK ? *ssf : 0);
-
- /*
- ** Only switch to encrypted connection
- ** if a security layer has been negotiated
- */
-
- if (result == SASL_OK && *ssf > 0)
- {
- int tmo;
-
- /*
- ** Convert I/O layer to use SASL.
- ** If the call fails, the connection
- ** is aborted.
- */
-
- tmo = DATA_PROGRESS_TIMEOUT * 1000;
- if (sfdcsasl(&mci->mci_in,
- &mci->mci_out,
- mci->mci_conn, tmo) == 0)
- {
- mci->mci_flags &= ~MCIF_EXTENS;
- mci->mci_flags |= MCIF_AUTHACT|
- MCIF_ONLY_EHLO;
- goto reconnect;
- }
- syserr("AUTH TLS switch failed in client");
- }
- /* else? XXX */
- mci->mci_flags |= MCIF_AUTHACT;
-
- }
- else if (ret == EX_TEMPFAIL)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_ERR, NOQID,
- "AUTH=client, relay=%.100s, temporary failure, connection abort",
- mci->mci_host);
- smtpquit(m, mci, e);
-
- /* avoid bogus error msg */
- mci->mci_errno = 0;
- rcode = EX_TEMPFAIL;
- mci_setstat(mci, rcode, "4.3.0", p);
-
- /*
- ** hack to get the error message into
- ** the envelope (done in giveresponse())
- */
-
- (void) sm_strlcpy(SmtpError,
- "Temporary AUTH failure",
- sizeof(SmtpError));
- }
- }
-# endif /* SASL */
- }
-
-
-do_transfer:
- /* clear out per-message flags from connection structure */
- mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
-
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- !bitset(EF_DONT_MIME, e->e_flags) &&
- bitnset(M_7BITS, m->m_flags))
- mci->mci_flags |= MCIF_CVT8TO7;
-
-#if MIME7TO8
- if (bitnset(M_MAKE8BIT, m->m_flags) &&
- !bitset(MCIF_7BIT, mci->mci_flags) &&
- (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
- (sm_strcasecmp(p, "quoted-printable") == 0 ||
- sm_strcasecmp(p, "base64") == 0) &&
- (p = hvalue("Content-Type", e->e_header)) != NULL)
- {
- /* may want to convert 7 -> 8 */
- /* XXX should really parse it here -- and use a class XXX */
- if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
- (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
- mci->mci_flags |= MCIF_CVT7TO8;
- }
-#endif /* MIME7TO8 */
-
- if (tTd(11, 1))
- {
- sm_dprintf("openmailer: ");
- mci_dump(sm_debug_file(), mci, false);
- }
-
-#if _FFR_CLIENT_SIZE
- /*
- ** See if we know the maximum size and
- ** abort if the message is too big.
- **
- ** NOTE: _FFR_CLIENT_SIZE is untested.
- */
-
- if (bitset(MCIF_SIZE, mci->mci_flags) &&
- mci->mci_maxsize > 0 &&
- e->e_msgsize > mci->mci_maxsize)
- {
- e->e_flags |= EF_NO_BODY_RETN;
- if (bitnset(M_LOCALMAILER, m->m_flags))
- e->e_status = "5.2.3";
- else
- e->e_status = "5.3.4";
-
- usrerrenh(e->e_status,
- "552 Message is too large; %ld bytes max",
- mci->mci_maxsize);
- rcode = EX_DATAERR;
-
- /* Need an e_message for error */
- (void) sm_snprintf(SmtpError, sizeof(SmtpError),
- "Message is too large; %ld bytes max",
- mci->mci_maxsize);
- goto give_up;
- }
-#endif /* _FFR_CLIENT_SIZE */
-
- if (mci->mci_state != MCIS_OPEN)
- {
- /* couldn't open the mailer */
- rcode = mci->mci_exitstat;
- errno = mci->mci_errno;
- SM_SET_H_ERRNO(mci->mci_herrno);
- if (rcode == EX_OK)
- {
- /* shouldn't happen */
- syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
- (unsigned long) mci, rcode, errno,
- mci->mci_state, firstsig);
- mci_dump_all(smioout, true);
- rcode = EX_SOFTWARE;
- }
- else if (nummxhosts > hostnum)
- {
- /* try next MX site */
- goto tryhost;
- }
- }
- else if (!clever)
- {
- bool ok;
-
- /*
- ** Format and send message.
- */
-
- rcode = EX_OK;
- errno = 0;
- ok = putfromline(mci, e);
- if (ok)
- ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
- if (ok)
- ok = (*e->e_putbody)(mci, e, NULL);
- if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags))
- ok = putline("", mci);
-
- /*
- ** Ignore an I/O error that was caused by EPIPE.
- ** Some broken mailers don't read the entire body
- ** but just exit() thus causing an I/O error.
- */
-
- if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE))
- ok = true;
-
- /* (always) get the exit status */
- rcode = endmailer(mci, e, pv);
- if (!ok)
- rcode = EX_TEMPFAIL;
- if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
- {
- /*
- ** Need an e_message for mailq display.
- ** We set SmtpError as
- */
-
- (void) sm_snprintf(SmtpError, sizeof(SmtpError),
- "%s mailer (%s) exited with EX_TEMPFAIL",
- m->m_name, m->m_mailer);
- }
- }
- else
- {
- /*
- ** Send the MAIL FROM: protocol
- */
-
- /* XXX this isn't pipelined... */
- rcode = smtpmailfrom(m, mci, e);
- if (rcode == EX_OK)
- {
- register int i;
-# if PIPELINING
- ADDRESS *volatile pchain;
-# endif /* PIPELINING */
-
- /* send the recipient list */
- tobuf[0] = '\0';
- mci->mci_retryrcpt = false;
- mci->mci_tolist = tobuf;
-# if PIPELINING
- pchain = NULL;
- mci->mci_nextaddr = NULL;
-# endif /* PIPELINING */
-
- for (to = tochain; to != NULL; to = to->q_tchain)
- {
- if (!QS_IS_UNMARKED(to->q_state))
- continue;
-
- /* mark recipient state as "ok so far" */
- to->q_state = QS_OK;
- e->e_to = to->q_paddr;
-# if STARTTLS
- i = rscheck("tls_rcpt", to->q_user, NULL, e,
- RSF_RMCOMM|RSF_COUNT, 3,
- mci->mci_host, e->e_id, NULL);
- if (i != EX_OK)
- {
- markfailure(e, to, mci, i, false);
- giveresponse(i, to->q_status, m, mci,
- ctladdr, xstart, e, to);
- if (i == EX_TEMPFAIL)
- {
- mci->mci_retryrcpt = true;
- to->q_state = QS_RETRY;
- }
- continue;
- }
-# endif /* STARTTLS */
-
- i = smtprcpt(to, m, mci, e, ctladdr, xstart);
-# if PIPELINING
- if (i == EX_OK &&
- bitset(MCIF_PIPELINED, mci->mci_flags))
- {
- /*
- ** Add new element to list of
- ** recipients for pipelining.
- */
-
- to->q_pchain = NULL;
- if (mci->mci_nextaddr == NULL)
- mci->mci_nextaddr = to;
- if (pchain == NULL)
- pchain = to;
- else
- {
- pchain->q_pchain = to;
- pchain = pchain->q_pchain;
- }
- }
-# endif /* PIPELINING */
- if (i != EX_OK)
- {
- markfailure(e, to, mci, i, false);
- giveresponse(i, to->q_status, m, mci,
- ctladdr, xstart, e, to);
- if (i == EX_TEMPFAIL)
- to->q_state = QS_RETRY;
- }
- }
-
- /* No recipients in list and no missing responses? */
- if (tobuf[0] == '\0'
-# if PIPELINING
- && bitset(MCIF_PIPELINED, mci->mci_flags)
- && mci->mci_nextaddr == NULL
-# endif /* PIPELINING */
- )
- {
- rcode = EX_OK;
- e->e_to = NULL;
- if (bitset(MCIF_CACHED, mci->mci_flags))
- smtprset(m, mci, e);
- }
- else
- {
- e->e_to = tobuf + 1;
- rcode = smtpdata(m, mci, e, ctladdr, xstart);
- }
- }
- if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
- {
- /* try next MX site */
- goto tryhost;
- }
- }
-#if NAMED_BIND
- if (ConfigLevel < 2)
- _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */
-#endif /* NAMED_BIND */
-
- if (tTd(62, 1))
- checkfds("after delivery");
-
- /*
- ** Do final status disposal.
- ** We check for something in tobuf for the SMTP case.
- ** If we got a temporary failure, arrange to queue the
- ** addressees.
- */
-
- give_up:
- if (bitnset(M_LMTP, m->m_flags))
- {
- lmtp_rcode = rcode;
- tobuf[0] = '\0';
- anyok = false;
- strsize = 0;
- }
- else
- anyok = rcode == EX_OK;
-
- for (to = tochain; to != NULL; to = to->q_tchain)
- {
- /* see if address already marked */
- if (!QS_IS_OK(to->q_state))
- continue;
-
- /* if running LMTP, get the status for each address */
- if (bitnset(M_LMTP, m->m_flags))
- {
- if (lmtp_rcode == EX_OK)
- rcode = smtpgetstat(m, mci, e);
- if (rcode == EX_OK)
- {
- strsize += sm_strlcat2(tobuf + strsize, ",",
- to->q_paddr,
- tobufsize - strsize);
- SM_ASSERT(strsize < tobufsize);
- anyok = true;
- }
- else
- {
- e->e_to = to->q_paddr;
- markfailure(e, to, mci, rcode, true);
- giveresponse(rcode, to->q_status, m, mci,
- ctladdr, xstart, e, to);
- e->e_to = tobuf + 1;
- continue;
- }
- }
- else
- {
- /* mark bad addresses */
- if (rcode != EX_OK)
- {
- if (goodmxfound && rcode == EX_NOHOST)
- rcode = EX_TEMPFAIL;
- markfailure(e, to, mci, rcode, true);
- continue;
- }
- }
-
- /* successful delivery */
- to->q_state = QS_SENT;
- to->q_statdate = curtime();
- e->e_nsent++;
-
- /*
- ** Checkpoint the send list every few addresses
- */
-
- if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
- {
- queueup(e, false, false);
- e->e_nsent = 0;
- }
-
- if (bitnset(M_LOCALMAILER, m->m_flags) &&
- bitset(QPINGONSUCCESS, to->q_flags))
- {
- to->q_flags |= QDELIVERED;
- to->q_status = "2.1.5";
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... Successfully delivered\n",
- to->q_paddr);
- }
- else if (bitset(QPINGONSUCCESS, to->q_flags) &&
- bitset(QPRIMARY, to->q_flags) &&
- !bitset(MCIF_DSN, mci->mci_flags))
- {
- to->q_flags |= QRELAYED;
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... relayed; expect no further notifications\n",
- to->q_paddr);
- }
- else if (IS_DLVR_NOTIFY(e) &&
- !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
- bitset(QPRIMARY, to->q_flags) &&
- (!bitset(QHASNOTIFY, to->q_flags) ||
- bitset(QPINGONSUCCESS, to->q_flags) ||
- bitset(QPINGONFAILURE, to->q_flags) ||
- bitset(QPINGONDELAY, to->q_flags)))
- {
- /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
- to->q_flags |= QBYNRELAY;
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... Deliver-by notify: relayed\n",
- to->q_paddr);
- }
- else if (IS_DLVR_TRACE(e) &&
- (!bitset(QHASNOTIFY, to->q_flags) ||
- bitset(QPINGONSUCCESS, to->q_flags) ||
- bitset(QPINGONFAILURE, to->q_flags) ||
- bitset(QPINGONDELAY, to->q_flags)) &&
- bitset(QPRIMARY, to->q_flags))
- {
- /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
- to->q_flags |= QBYTRACE;
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... Deliver-By trace: relayed\n",
- to->q_paddr);
- }
- }
-
- if (bitnset(M_LMTP, m->m_flags))
- {
- /*
- ** Global information applies to the last recipient only;
- ** clear it out to avoid bogus errors.
- */
-
- rcode = EX_OK;
- e->e_statmsg = NULL;
-
- /* reset the mci state for the next transaction */
- if (mci != NULL &&
- (mci->mci_state == MCIS_MAIL ||
- mci->mci_state == MCIS_RCPT ||
- mci->mci_state == MCIS_DATA))
- {
- mci->mci_state = MCIS_OPEN;
- SmtpPhase = mci->mci_phase = "idle";
- sm_setproctitle(true, e, "%s: %s", CurHostName,
- mci->mci_phase);
- }
- }
-
- if (tobuf[0] != '\0')
- {
- giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain);
-#if 0
- /*
- ** This code is disabled for now because I am not
- ** sure that copying status from the first recipient
- ** to all non-status'ed recipients is a good idea.
- */
-
- if (tochain->q_message != NULL &&
- !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
- {
- for (to = tochain->q_tchain; to != NULL;
- to = to->q_tchain)
- {
- /* see if address already marked */
- if (QS_IS_QUEUEUP(to->q_state) &&
- to->q_message == NULL)
- to->q_message = sm_rpool_strdup_x(e->e_rpool,
- tochain->q_message);
- }
- }
-#endif /* 0 */
- }
- if (anyok)
- markstats(e, tochain, STATS_NORMAL);
- mci_store_persistent(mci);
-
- /* Some recipients were tempfailed, try them on the next host */
- if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
- {
- /* try next MX site */
- goto tryhost;
- }
-
- /* now close the connection */
- if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
- !bitset(MCIF_CACHED, mci->mci_flags))
- smtpquit(m, mci, e);
-
-cleanup: ;
- }
- SM_FINALLY
- {
- /*
- ** Restore state and return.
- */
-#if XDEBUG
- char wbuf[MAXLINE];
-
- /* make absolutely certain 0, 1, and 2 are in use */
- (void) sm_snprintf(wbuf, sizeof(wbuf),
- "%s... end of deliver(%s)",
- e->e_to == NULL ? "NO-TO-LIST"
- : shortenstring(e->e_to,
- MAXSHORTSTR),
- m->m_name);
- checkfd012(wbuf);
-#endif /* XDEBUG */
-
- errno = 0;
-
- /*
- ** It was originally necessary to set macro 'g' to NULL
- ** because it previously pointed to an auto buffer.
- ** We don't do this any more, so this may be unnecessary.
- */
-
- macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
- e->e_to = NULL;
- }
- SM_END_TRY
- return rcode;
-}
-
-/*
-** MARKFAILURE -- mark a failure on a specific address.
-**
-** Parameters:
-** e -- the envelope we are sending.
-** q -- the address to mark.
-** mci -- mailer connection information.
-** rcode -- the code signifying the particular failure.
-** ovr -- override an existing code?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** marks the address (and possibly the envelope) with the
-** failure so that an error will be returned or
-** the message will be queued, as appropriate.
-*/
-
-void
-markfailure(e, q, mci, rcode, ovr)
- register ENVELOPE *e;
- register ADDRESS *q;
- register MCI *mci;
- int rcode;
- bool ovr;
-{
- int save_errno = errno;
- char *status = NULL;
- char *rstatus = NULL;
-
- switch (rcode)
- {
- case EX_OK:
- break;
-
- case EX_TEMPFAIL:
- case EX_IOERR:
- case EX_OSERR:
- q->q_state = QS_QUEUEUP;
- break;
-
- default:
- q->q_state = QS_BADADDR;
- break;
- }
-
- /* find most specific error code possible */
- if (mci != NULL && mci->mci_status != NULL)
- {
- status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
- if (mci->mci_rstatus != NULL)
- rstatus = sm_rpool_strdup_x(e->e_rpool,
- mci->mci_rstatus);
- else
- rstatus = NULL;
- }
- else if (e->e_status != NULL)
- {
- status = e->e_status;
- rstatus = NULL;
- }
- else
- {
- switch (rcode)
- {
- case EX_USAGE:
- status = "5.5.4";
- break;
-
- case EX_DATAERR:
- status = "5.5.2";
- break;
-
- case EX_NOUSER:
- status = "5.1.1";
- break;
-
- case EX_NOHOST:
- status = "5.1.2";
- break;
-
- case EX_NOINPUT:
- case EX_CANTCREAT:
- case EX_NOPERM:
- status = "5.3.0";
- break;
-
- case EX_UNAVAILABLE:
- case EX_SOFTWARE:
- case EX_OSFILE:
- case EX_PROTOCOL:
- case EX_CONFIG:
- status = "5.5.0";
- break;
-
- case EX_OSERR:
- case EX_IOERR:
- status = "4.5.0";
- break;
-
- case EX_TEMPFAIL:
- status = "4.2.0";
- break;
- }
- }
-
- /* new status? */
- if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
- *q->q_status == '\0' || *q->q_status < *status))
- {
- q->q_status = status;
- q->q_rstatus = rstatus;
- }
- if (rcode != EX_OK && q->q_rstatus == NULL &&
- q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
- sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
- {
- char buf[16];
-
- (void) sm_snprintf(buf, sizeof(buf), "%d", rcode);
- q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
- }
-
- q->q_statdate = curtime();
- if (CurHostName != NULL && CurHostName[0] != '\0' &&
- mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
- q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
-
- /* restore errno */
- errno = save_errno;
-}
-/*
-** ENDMAILER -- Wait for mailer to terminate.
-**
-** We should never get fatal errors (e.g., segmentation
-** violation), so we report those specially. For other
-** errors, we choose a status message (into statmsg),
-** and if it represents an error, we print it.
-**
-** Parameters:
-** mci -- the mailer connection info.
-** e -- the current envelope.
-** pv -- the parameter vector that invoked the mailer
-** (for error messages).
-**
-** Returns:
-** exit code of mailer.
-**
-** Side Effects:
-** none.
-*/
-
-static jmp_buf EndWaitTimeout;
-
-static void
-endwaittimeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(EndWaitTimeout, 1);
-}
-
-int
-endmailer(mci, e, pv)
- register MCI *mci;
- register ENVELOPE *e;
- char **pv;
-{
- int st;
- int save_errno = errno;
- char buf[MAXLINE];
- SM_EVENT *ev = NULL;
-
-
- mci_unlock_host(mci);
-
- /* close output to mailer */
- if (mci->mci_out != NULL)
- {
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- mci->mci_out = NULL;
- }
-
- /* copy any remaining input to transcript */
- if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
- e->e_xfp != NULL)
- {
- while (sfgets(buf, sizeof(buf), mci->mci_in,
- TimeOuts.to_quit, "Draining Input") != NULL)
- (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
- }
-
-#if SASL
- /* close SASL connection */
- if (bitset(MCIF_AUTHACT, mci->mci_flags))
- {
- sasl_dispose(&mci->mci_conn);
- mci->mci_flags &= ~MCIF_AUTHACT;
- }
-#endif /* SASL */
-
-#if STARTTLS
- /* shutdown TLS */
- (void) endtlsclt(mci);
-#endif /* STARTTLS */
-
- /* now close the input */
- if (mci->mci_in != NULL)
- {
- (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
- mci->mci_in = NULL;
- }
- mci->mci_state = MCIS_CLOSED;
-
- errno = save_errno;
-
- /* in the IPC case there is nothing to wait for */
- if (mci->mci_pid == 0)
- return EX_OK;
-
- /* put a timeout around the wait */
- if (mci->mci_mailer->m_wait > 0)
- {
- if (setjmp(EndWaitTimeout) == 0)
- ev = sm_setevent(mci->mci_mailer->m_wait,
- endwaittimeout, 0);
- else
- {
- syserr("endmailer %s: wait timeout (%ld)",
- mci->mci_mailer->m_name,
- (long) mci->mci_mailer->m_wait);
- return EX_TEMPFAIL;
- }
- }
-
- /* wait for the mailer process, collect status */
- st = waitfor(mci->mci_pid);
- save_errno = errno;
- if (ev != NULL)
- sm_clrevent(ev);
- errno = save_errno;
-
- if (st == -1)
- {
- syserr("endmailer %s: wait", mci->mci_mailer->m_name);
- return EX_SOFTWARE;
- }
-
- if (WIFEXITED(st))
- {
- /* normal death -- return status */
- return (WEXITSTATUS(st));
- }
-
- /* it died a horrid death */
- syserr("451 4.3.0 mailer %s died with signal %d%s",
- mci->mci_mailer->m_name, WTERMSIG(st),
- WCOREDUMP(st) ? " (core dumped)" :
- (WIFSTOPPED(st) ? " (stopped)" : ""));
-
- /* log the arguments */
- if (pv != NULL && e->e_xfp != NULL)
- {
- register char **av;
-
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
- for (av = pv; *av != NULL; av++)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
- *av);
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
- }
-
- ExitStat = EX_TEMPFAIL;
- return EX_TEMPFAIL;
-}
-/*
-** GIVERESPONSE -- Interpret an error response from a mailer
-**
-** Parameters:
-** status -- the status code from the mailer (high byte
-** only; core dumps must have been taken care of
-** already).
-** dsn -- the DSN associated with the address, if any.
-** m -- the mailer info for this mailer.
-** mci -- the mailer connection info -- can be NULL if the
-** response is given before the connection is made.
-** ctladdr -- the controlling address for the recipient
-** address(es).
-** xstart -- the transaction start time, for computing
-** transaction delays.
-** e -- the current envelope.
-** to -- the current recipient (NULL if none).
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Errors may be incremented.
-** ExitStat may be set.
-*/
-
-void
-giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
- int status;
- char *dsn;
- register MAILER *m;
- register MCI *mci;
- ADDRESS *ctladdr;
- time_t xstart;
- ENVELOPE *e;
- ADDRESS *to;
-{
- register const char *statmsg;
- int errnum = errno;
- int off = 4;
- bool usestat = false;
- char dsnbuf[ENHSCLEN];
- char buf[MAXLINE];
- char *exmsg;
-
- if (e == NULL)
- {
- syserr("giveresponse: null envelope");
- /* NOTREACHED */
- SM_ASSERT(0);
- }
-
- /*
- ** Compute status message from code.
- */
-
- exmsg = sm_sysexmsg(status);
- if (status == 0)
- {
- statmsg = "250 2.0.0 Sent";
- if (e->e_statmsg != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf), "%s (%s)",
- statmsg,
- shortenstring(e->e_statmsg, 403));
- statmsg = buf;
- }
- }
- else if (exmsg == NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "554 5.3.0 unknown mailer error %d",
- status);
- status = EX_UNAVAILABLE;
- statmsg = buf;
- usestat = true;
- }
- else if (status == EX_TEMPFAIL)
- {
- char *bp = buf;
-
- (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
- bp += strlen(bp);
-#if NAMED_BIND
- if (h_errno == TRY_AGAIN)
- statmsg = sm_errstring(h_errno + E_DNSBASE);
- else
-#endif /* NAMED_BIND */
- {
- if (errnum != 0)
- statmsg = sm_errstring(errnum);
- else
- statmsg = SmtpError;
- }
- if (statmsg != NULL && statmsg[0] != '\0')
- {
- switch (errnum)
- {
-#ifdef ENETDOWN
- case ENETDOWN: /* Network is down */
-#endif /* ENETDOWN */
-#ifdef ENETUNREACH
- case ENETUNREACH: /* Network is unreachable */
-#endif /* ENETUNREACH */
-#ifdef ENETRESET
- case ENETRESET: /* Network dropped connection on reset */
-#endif /* ENETRESET */
-#ifdef ECONNABORTED
- case ECONNABORTED: /* Software caused connection abort */
-#endif /* ECONNABORTED */
-#ifdef EHOSTDOWN
- case EHOSTDOWN: /* Host is down */
-#endif /* EHOSTDOWN */
-#ifdef EHOSTUNREACH
- case EHOSTUNREACH: /* No route to host */
-#endif /* EHOSTUNREACH */
- if (mci != NULL && mci->mci_host != NULL)
- {
- (void) sm_strlcpyn(bp,
- SPACELEFT(buf, bp),
- 2, ": ",
- mci->mci_host);
- bp += strlen(bp);
- }
- break;
- }
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
- statmsg);
- usestat = true;
- }
- statmsg = buf;
- }
-#if NAMED_BIND
- else if (status == EX_NOHOST && h_errno != 0)
- {
- statmsg = sm_errstring(h_errno + E_DNSBASE);
- (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1,
- statmsg);
- statmsg = buf;
- usestat = true;
- }
-#endif /* NAMED_BIND */
- else
- {
- statmsg = exmsg;
- if (*statmsg++ == ':' && errnum != 0)
- {
- (void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg,
- sm_errstring(errnum));
- statmsg = buf;
- usestat = true;
- }
- else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg,
- shortenstring(e->e_statmsg, 403));
- statmsg = buf;
- usestat = true;
- }
- }
-
- /*
- ** Print the message as appropriate
- */
-
- if (status == EX_OK || status == EX_TEMPFAIL)
- {
- extern char MsgBuf[];
-
- if ((off = isenhsc(statmsg + 4, ' ')) > 0)
- {
- if (dsn == NULL)
- {
- (void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
- "%.*s", off, statmsg + 4);
- dsn = dsnbuf;
- }
- off += 5;
- }
- else
- {
- off = 4;
- }
- message("%s", statmsg + off);
- if (status == EX_TEMPFAIL && e->e_xfp != NULL)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
- &MsgBuf[4]);
- }
- else
- {
- char mbuf[ENHSCLEN + 4];
-
- Errors++;
- if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
- off < sizeof(mbuf) - 4)
- {
- if (dsn == NULL)
- {
- (void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
- "%.*s", off, statmsg + 4);
- dsn = dsnbuf;
- }
- off += 5;
-
- /* copy only part of statmsg to mbuf */
- (void) sm_strlcpy(mbuf, statmsg, off);
- (void) sm_strlcat(mbuf, " %s", sizeof(mbuf));
- }
- else
- {
- dsnbuf[0] = '\0';
- (void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s",
- statmsg);
- off = 4;
- }
- usrerr(mbuf, &statmsg[off]);
- }
-
- /*
- ** Final cleanup.
- ** Log a record of the transaction. Compute the new
- ** ExitStat -- if we already had an error, stick with
- ** that.
- */
-
- if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
- LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
- logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
-
- if (tTd(11, 2))
- sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
- status,
- dsn == NULL ? "<NULL>" : dsn,
- e->e_message == NULL ? "<NULL>" : e->e_message,
- errnum);
-
- if (status != EX_TEMPFAIL)
- setstat(status);
- if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
- e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
- if (status != EX_OK && to != NULL && to->q_message == NULL)
- {
- if (!usestat && e->e_message != NULL)
- to->q_message = sm_rpool_strdup_x(e->e_rpool,
- e->e_message);
- else
- to->q_message = sm_rpool_strdup_x(e->e_rpool,
- statmsg + off);
- }
- errno = 0;
- SM_SET_H_ERRNO(0);
-}
-/*
-** LOGDELIVERY -- log the delivery in the system log
-**
-** Care is taken to avoid logging lines that are too long, because
-** some versions of syslog have an unfortunate proclivity for core
-** dumping. This is a hack, to be sure, that is at best empirical.
-**
-** Parameters:
-** m -- the mailer info. Can be NULL for initial queue.
-** mci -- the mailer connection info -- can be NULL if the
-** log is occurring when no connection is active.
-** dsn -- the DSN attached to the status.
-** status -- the message to print for the status.
-** ctladdr -- the controlling address for the to list.
-** xstart -- the transaction start time, used for
-** computing transaction delay.
-** e -- the current envelope.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** none
-*/
-
-void
-logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
- MAILER *m;
- register MCI *mci;
- char *dsn;
- const char *status;
- ADDRESS *ctladdr;
- time_t xstart;
- register ENVELOPE *e;
-{
- register char *bp;
- register char *p;
- int l;
- time_t now = curtime();
- char buf[1024];
-
-#if (SYSLOG_BUFSIZE) >= 256
- /* ctladdr: max 106 bytes */
- bp = buf;
- if (ctladdr != NULL)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
- shortenstring(ctladdr->q_paddr, 83));
- bp += strlen(bp);
- if (bitset(QGOODUID, ctladdr->q_flags))
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
- (int) ctladdr->q_uid,
- (int) ctladdr->q_gid);
- bp += strlen(bp);
- }
- }
-
- /* delay & xdelay: max 41 bytes */
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
- pintvl(now - e->e_ctime, true));
- bp += strlen(bp);
-
- if (xstart != (time_t) 0)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
- pintvl(now - xstart, true));
- bp += strlen(bp);
- }
-
- /* mailer: assume about 19 bytes (max 10 byte mailer name) */
- if (m != NULL)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
- m->m_name);
- bp += strlen(bp);
- }
-
- /* pri: changes with each delivery attempt */
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
- e->e_msgpriority);
- bp += strlen(bp);
-
- /* relay: max 66 bytes for IPv4 addresses */
- if (mci != NULL && mci->mci_host != NULL)
- {
- extern SOCKADDR CurHostAddr;
-
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
- shortenstring(mci->mci_host, 40));
- bp += strlen(bp);
-
- if (CurHostAddr.sa.sa_family != 0)
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
- anynet_ntoa(&CurHostAddr));
- }
- }
- else if (strcmp(status, "quarantined") == 0)
- {
- if (e->e_quarmsg != NULL)
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", quarantine=%s",
- shortenstring(e->e_quarmsg, 40));
- }
- else if (strcmp(status, "queued") != 0)
- {
- p = macvalue('h', e);
- if (p != NULL && p[0] != '\0')
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", relay=%s", shortenstring(p, 40));
- }
- }
- bp += strlen(bp);
-
- /* dsn */
- if (dsn != NULL && *dsn != '\0')
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
- shortenstring(dsn, ENHSCLEN));
- bp += strlen(bp);
- }
-
-#if _FFR_LOG_NTRIES
- /* ntries */
- if (e->e_ntries >= 0)
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", ntries=%d", e->e_ntries + 1);
- bp += strlen(bp);
- }
-#endif /* _FFR_LOG_NTRIES */
-
-# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4)
-# if (STATLEN) < 63
-# undef STATLEN
-# define STATLEN 63
-# endif /* (STATLEN) < 63 */
-# if (STATLEN) > 203
-# undef STATLEN
-# define STATLEN 203
-# endif /* (STATLEN) > 203 */
-
- /* stat: max 210 bytes */
- if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20)))
- {
- /* desperation move -- truncate data */
- bp = buf + sizeof(buf) - ((STATLEN) + 17);
- (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
- bp += 3;
- }
-
- (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
- bp += strlen(bp);
-
- (void) sm_strlcpy(bp, shortenstring(status, STATLEN),
- SPACELEFT(buf, bp));
-
- /* id, to: max 13 + TOBUFSIZE bytes */
- l = SYSLOG_BUFSIZE - 100 - strlen(buf);
- if (l < 0)
- l = 0;
- p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
- while (strlen(p) >= l)
- {
- register char *q;
-
- for (q = p + l; q > p; q--)
- {
- if (*q == ',')
- break;
- }
- if (p == q)
- break;
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
- (int) (++q - p), p, buf);
- p = q;
- }
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
-
-#else /* (SYSLOG_BUFSIZE) >= 256 */
-
- l = SYSLOG_BUFSIZE - 85;
- if (l < 0)
- l = 0;
- p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
- while (strlen(p) >= l)
- {
- register char *q;
-
- for (q = p + l; q > p; q--)
- {
- if (*q == ',')
- break;
- }
- if (p == q)
- break;
-
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
- (int) (++q - p), p);
- p = q;
- }
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
-
- if (ctladdr != NULL)
- {
- bp = buf;
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
- shortenstring(ctladdr->q_paddr, 83));
- bp += strlen(bp);
- if (bitset(QGOODUID, ctladdr->q_flags))
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
- ctladdr->q_uid, ctladdr->q_gid);
- bp += strlen(bp);
- }
- sm_syslog(LOG_INFO, e->e_id, "%s", buf);
- }
- bp = buf;
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
- pintvl(now - e->e_ctime, true));
- bp += strlen(bp);
- if (xstart != (time_t) 0)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
- pintvl(now - xstart, true));
- bp += strlen(bp);
- }
-
- if (m != NULL)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
- m->m_name);
- bp += strlen(bp);
- }
- sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
-
- buf[0] = '\0';
- bp = buf;
- if (mci != NULL && mci->mci_host != NULL)
- {
- extern SOCKADDR CurHostAddr;
-
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
- mci->mci_host);
- bp += strlen(bp);
-
- if (CurHostAddr.sa.sa_family != 0)
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- " [%.100s]",
- anynet_ntoa(&CurHostAddr));
- }
- else if (strcmp(status, "quarantined") == 0)
- {
- if (e->e_quarmsg != NULL)
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", quarantine=%.100s",
- e->e_quarmsg);
- }
- else if (strcmp(status, "queued") != 0)
- {
- p = macvalue('h', e);
- if (p != NULL && p[0] != '\0')
- (void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p);
- }
- if (buf[0] != '\0')
- sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
-
- sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
-#endif /* (SYSLOG_BUFSIZE) >= 256 */
-}
-/*
-** PUTFROMLINE -- output a UNIX-style from line (or whatever)
-**
-** This can be made an arbitrary message separator by changing $l
-**
-** One of the ugliest hacks seen by human eyes is contained herein:
-** UUCP wants those stupid "remote from <host>" lines. Why oh why
-** does a well-meaning programmer such as myself have to deal with
-** this kind of antique garbage????
-**
-** Parameters:
-** mci -- the connection information.
-** e -- the envelope.
-**
-** Returns:
-** true iff line was written successfully
-**
-** Side Effects:
-** outputs some text to fp.
-*/
-
-bool
-putfromline(mci, e)
- register MCI *mci;
- ENVELOPE *e;
-{
- char *template = UnixFromLine;
- char buf[MAXLINE];
- char xbuf[MAXLINE];
-
- if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
- return true;
-
- mci->mci_flags |= MCIF_INHEADER;
-
- if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
- {
- char *bang;
-
- expand("\201g", buf, sizeof(buf), e);
- bang = strchr(buf, '!');
- if (bang == NULL)
- {
- char *at;
- char hname[MAXNAME];
-
- /*
- ** If we can construct a UUCP path, do so
- */
-
- at = strrchr(buf, '@');
- if (at == NULL)
- {
- expand("\201k", hname, sizeof(hname), e);
- at = hname;
- }
- else
- *at++ = '\0';
- (void) sm_snprintf(xbuf, sizeof(xbuf),
- "From %.800s \201d remote from %.100s\n",
- buf, at);
- }
- else
- {
- *bang++ = '\0';
- (void) sm_snprintf(xbuf, sizeof(xbuf),
- "From %.800s \201d remote from %.100s\n",
- bang, buf);
- template = xbuf;
- }
- }
- expand(template, buf, sizeof(buf), e);
- return putxline(buf, strlen(buf), mci, PXLF_HEADER);
-}
-
-/*
-** PUTBODY -- put the body of a message.
-**
-** Parameters:
-** mci -- the connection information.
-** e -- the envelope to put out.
-** separator -- if non-NULL, a message separator that must
-** not be permitted in the resulting message.
-**
-** Returns:
-** true iff message was written successfully
-**
-** Side Effects:
-** The message is written onto fp.
-*/
-
-/* values for output state variable */
-#define OSTATE_HEAD 0 /* at beginning of line */
-#define OSTATE_CR 1 /* read a carriage return */
-#define OSTATE_INLINE 2 /* putting rest of line */
-
-bool
-putbody(mci, e, separator)
- register MCI *mci;
- register ENVELOPE *e;
- char *separator;
-{
- bool dead = false;
- bool ioerr = false;
- int save_errno;
- char buf[MAXLINE];
-#if MIME8TO7
- char *boundaries[MAXMIMENESTING + 1];
-#endif /* MIME8TO7 */
-
- /*
- ** Output the body of the message
- */
-
- if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
- {
- char *df = queuename(e, DATAFL_LETTER);
-
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
- SM_IO_RDONLY_B, NULL);
- if (e->e_dfp == NULL)
- {
- char *msg = "!putbody: Cannot open %s for %s from %s";
-
- if (errno == ENOENT)
- msg++;
- syserr(msg, df, e->e_to, e->e_from.q_paddr);
- }
-
- }
- if (e->e_dfp == NULL)
- {
- if (bitset(MCIF_INHEADER, mci->mci_flags))
- {
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- }
- if (!putline("<<< No Message Collected >>>", mci))
- goto writeerr;
- goto endofmessage;
- }
-
- if (e->e_dfino == (ino_t) 0)
- {
- struct stat stbuf;
-
- if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
- < 0)
- e->e_dfino = -1;
- else
- {
- e->e_dfdev = stbuf.st_dev;
- e->e_dfino = stbuf.st_ino;
- }
- }
-
- /* paranoia: the data file should always be in a rewound state */
- (void) bfrewind(e->e_dfp);
-
- /* simulate an I/O timeout when used as source */
- if (tTd(84, 101))
- sleep(319);
-
-#if MIME8TO7
- if (bitset(MCIF_CVT8TO7, mci->mci_flags))
- {
- /*
- ** Do 8 to 7 bit MIME conversion.
- */
-
- /* make sure it looks like a MIME message */
- if (hvalue("MIME-Version", e->e_header) == NULL &&
- !putline("MIME-Version: 1.0", mci))
- goto writeerr;
-
- if (hvalue("Content-Type", e->e_header) == NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Content-Type: text/plain; charset=%s",
- defcharset(e));
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* now do the hard work */
- boundaries[0] = NULL;
- mci->mci_flags |= MCIF_INHEADER;
- if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) ==
- SM_IO_EOF)
- goto writeerr;
- }
-# if MIME7TO8
- else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
- {
- if (!mime7to8(mci, e->e_header, e))
- goto writeerr;
- }
-# endif /* MIME7TO8 */
- else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
- {
- bool oldsuprerrs = SuprErrs;
-
- /* Use mime8to7 to check multipart for MIME header overflows */
- boundaries[0] = NULL;
- mci->mci_flags |= MCIF_INHEADER;
-
- /*
- ** If EF_DONT_MIME is set, we have a broken MIME message
- ** and don't want to generate a new bounce message whose
- ** body propagates the broken MIME. We can't just not call
- ** mime8to7() as is done above since we need the security
- ** checks. The best we can do is suppress the errors.
- */
-
- if (bitset(EF_DONT_MIME, e->e_flags))
- SuprErrs = true;
-
- if (mime8to7(mci, e->e_header, e, boundaries,
- M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF)
- goto writeerr;
-
- /* restore SuprErrs */
- SuprErrs = oldsuprerrs;
- }
- else
-#endif /* MIME8TO7 */
- {
- int ostate;
- register char *bp;
- register char *pbp;
- register int c;
- register char *xp;
- int padc;
- char *buflim;
- int pos = 0;
- char peekbuf[12];
-
- if (bitset(MCIF_INHEADER, mci->mci_flags))
- {
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- }
-
- /* determine end of buffer; allow for short mailer lines */
- buflim = &buf[sizeof(buf) - 1];
- if (mci->mci_mailer->m_linelimit > 0 &&
- mci->mci_mailer->m_linelimit < sizeof(buf) - 1)
- buflim = &buf[mci->mci_mailer->m_linelimit - 1];
-
- /* copy temp file to output with mapping */
- ostate = OSTATE_HEAD;
- bp = buf;
- pbp = peekbuf;
- while (!sm_io_error(mci->mci_out) && !dead)
- {
- if (pbp > peekbuf)
- c = *--pbp;
- else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
- == SM_IO_EOF)
- break;
- if (bitset(MCIF_7BIT, mci->mci_flags))
- c &= 0x7f;
- switch (ostate)
- {
- case OSTATE_HEAD:
- if (c == '\0' &&
- bitnset(M_NONULLS,
- mci->mci_mailer->m_flags))
- break;
- if (c != '\r' && c != '\n' && bp < buflim)
- {
- *bp++ = c;
- break;
- }
-
- /* check beginning of line for special cases */
- *bp = '\0';
- pos = 0;
- padc = SM_IO_EOF;
- if (buf[0] == 'F' &&
- bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
- && strncmp(buf, "From ", 5) == 0)
- {
- padc = '>';
- }
- if (buf[0] == '-' && buf[1] == '-' &&
- separator != NULL)
- {
- /* possible separator */
- int sl = strlen(separator);
-
- if (strncmp(&buf[2], separator, sl)
- == 0)
- padc = ' ';
- }
- if (buf[0] == '.' &&
- bitnset(M_XDOT, mci->mci_mailer->m_flags))
- {
- padc = '.';
- }
-
- /* now copy out saved line */
- if (TrafficLogFile != NULL)
- {
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "%05d >>> ",
- (int) CurrentPid);
- if (padc != SM_IO_EOF)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- padc);
- for (xp = buf; xp < bp; xp++)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) *xp);
- if (c == '\n')
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- }
- if (padc != SM_IO_EOF)
- {
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT, padc)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
- pos++;
- }
- for (xp = buf; xp < bp; xp++)
- {
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT,
- (unsigned char) *xp)
- == SM_IO_EOF)
- {
- dead = true;
- break;
- }
- }
- if (dead)
- continue;
- if (c == '\n')
- {
- if (sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- break;
- pos = 0;
- }
- else
- {
- pos += bp - buf;
- if (c != '\r')
- {
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = c;
- }
- }
-
- bp = buf;
-
- /* determine next state */
- if (c == '\n')
- ostate = OSTATE_HEAD;
- else if (c == '\r')
- ostate = OSTATE_CR;
- else
- ostate = OSTATE_INLINE;
- continue;
-
- case OSTATE_CR:
- if (c == '\n')
- {
- /* got CRLF */
- if (sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- continue;
-
- if (TrafficLogFile != NULL)
- {
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- }
- pos = 0;
- ostate = OSTATE_HEAD;
- continue;
- }
-
- /* had a naked carriage return */
- SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
- *pbp++ = c;
- c = '\r';
- ostate = OSTATE_INLINE;
- goto putch;
-
- case OSTATE_INLINE:
- if (c == '\r')
- {
- ostate = OSTATE_CR;
- continue;
- }
- if (c == '\0' &&
- bitnset(M_NONULLS,
- mci->mci_mailer->m_flags))
- break;
-putch:
- if (mci->mci_mailer->m_linelimit > 0 &&
- pos >= mci->mci_mailer->m_linelimit - 1 &&
- c != '\n')
- {
- int d;
-
- /* check next character for EOL */
- if (pbp > peekbuf)
- d = *(pbp - 1);
- else if ((d = sm_io_getc(e->e_dfp,
- SM_TIME_DEFAULT))
- != SM_IO_EOF)
- {
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = d;
- }
-
- if (d == '\n' || d == SM_IO_EOF)
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) c);
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT,
- (unsigned char) c)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
- pos++;
- continue;
- }
-
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT, '!')
- == SM_IO_EOF ||
- sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
-
- if (TrafficLogFile != NULL)
- {
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "!%s",
- mci->mci_mailer->m_eol);
- }
- ostate = OSTATE_HEAD;
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = c;
- continue;
- }
- if (c == '\n')
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- if (sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- continue;
- pos = 0;
- ostate = OSTATE_HEAD;
- }
- else
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) c);
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT,
- (unsigned char) c)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
- pos++;
- ostate = OSTATE_INLINE;
- }
- break;
- }
- }
-
- /* make sure we are at the beginning of a line */
- if (bp > buf)
- {
- if (TrafficLogFile != NULL)
- {
- for (xp = buf; xp < bp; xp++)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) *xp);
- }
- for (xp = buf; xp < bp; xp++)
- {
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
- (unsigned char) *xp)
- == SM_IO_EOF)
- {
- dead = true;
- break;
- }
- }
- pos += bp - buf;
- }
- if (!dead && pos > 0)
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol) == SM_IO_EOF)
- goto writeerr;
- }
- }
-
- if (sm_io_error(e->e_dfp))
- {
- syserr("putbody: %s/%cf%s: read error",
- qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
- DATAFL_LETTER, e->e_id);
- ExitStat = EX_IOERR;
- ioerr = true;
- }
-
-endofmessage:
- /*
- ** Since mailfile() uses e_dfp in a child process,
- ** the file offset in the stdio library for the
- ** parent process will not agree with the in-kernel
- ** file offset since the file descriptor is shared
- ** between the processes. Therefore, it is vital
- ** that the file always be rewound. This forces the
- ** kernel offset (lseek) and stdio library (ftell)
- ** offset to match.
- */
-
- save_errno = errno;
- if (e->e_dfp != NULL)
- (void) bfrewind(e->e_dfp);
-
- /* some mailers want extra blank line at end of message */
- if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
- buf[0] != '\0' && buf[0] != '\n')
- {
- if (!putline("", mci))
- goto writeerr;
- }
-
- if (!dead &&
- (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF ||
- (sm_io_error(mci->mci_out) && errno != EPIPE)))
- {
- save_errno = errno;
- syserr("putbody: write error");
- ExitStat = EX_IOERR;
- ioerr = true;
- }
-
- errno = save_errno;
- return !dead && !ioerr;
-
- writeerr:
- return false;
-}
-
-/*
-** MAILFILE -- Send a message to a file.
-**
-** If the file has the set-user-ID/set-group-ID bits set, but NO
-** execute bits, sendmail will try to become the owner of that file
-** rather than the real user. Obviously, this only works if
-** sendmail runs as root.
-**
-** This could be done as a subordinate mailer, except that it
-** is used implicitly to save messages in ~/dead.letter. We
-** view this as being sufficiently important as to include it
-** here. For example, if the system is dying, we shouldn't have
-** to create another process plus some pipes to save the message.
-**
-** Parameters:
-** filename -- the name of the file to send to.
-** mailer -- mailer definition for recipient -- if NULL,
-** use FileMailer.
-** ctladdr -- the controlling address header -- includes
-** the userid/groupid to be when sending.
-** sfflags -- flags for opening.
-** e -- the current envelope.
-**
-** Returns:
-** The exit code associated with the operation.
-**
-** Side Effects:
-** none.
-*/
-
-# define RETURN(st) exit(st);
-
-static jmp_buf CtxMailfileTimeout;
-
-int
-mailfile(filename, mailer, ctladdr, sfflags, e)
- char *volatile filename;
- MAILER *volatile mailer;
- ADDRESS *ctladdr;
- volatile long sfflags;
- register ENVELOPE *e;
-{
- register SM_FILE_T *f;
- register pid_t pid = -1;
- volatile int mode;
- int len;
- off_t curoff;
- bool suidwarn = geteuid() == 0;
- char *p;
- char *volatile realfile;
- SM_EVENT *ev;
- char buf[MAXPATHLEN];
- char targetfile[MAXPATHLEN];
-
- if (tTd(11, 1))
- {
- sm_dprintf("mailfile %s\n ctladdr=", filename);
- printaddr(sm_debug_file(), ctladdr, false);
- }
-
- if (mailer == NULL)
- mailer = FileMailer;
-
- if (e->e_xfp != NULL)
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
-
- /*
- ** Special case /dev/null. This allows us to restrict file
- ** delivery to regular files only.
- */
-
- if (sm_path_isdevnull(filename))
- return EX_OK;
-
- /* check for 8-bit available */
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- bitnset(M_7BITS, mailer->m_flags) &&
- (bitset(EF_DONT_MIME, e->e_flags) ||
- !(bitset(MM_MIME8BIT, MimeMode) ||
- (bitset(EF_IS_MIME, e->e_flags) &&
- bitset(MM_CVTMIME, MimeMode)))))
- {
- e->e_status = "5.6.3";
- usrerrenh(e->e_status,
- "554 Cannot send 8-bit data to 7-bit destination");
- errno = 0;
- return EX_DATAERR;
- }
-
- /* Find the actual file */
- if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
- {
- len = strlen(SafeFileEnv);
-
- if (strncmp(SafeFileEnv, filename, len) == 0)
- filename += len;
-
- if (len + strlen(filename) + 1 >= sizeof(targetfile))
- {
- syserr("mailfile: filename too long (%s/%s)",
- SafeFileEnv, filename);
- return EX_CANTCREAT;
- }
- (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile));
- realfile = targetfile + len;
- if (*filename == '/')
- filename++;
- if (*filename != '\0')
- {
- /* paranoia: trailing / should be removed in readcf */
- if (targetfile[len - 1] != '/')
- (void) sm_strlcat(targetfile,
- "/", sizeof(targetfile));
- (void) sm_strlcat(targetfile, filename,
- sizeof(targetfile));
- }
- }
- else if (mailer->m_rootdir != NULL)
- {
- expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e);
- len = strlen(targetfile);
-
- if (strncmp(targetfile, filename, len) == 0)
- filename += len;
-
- if (len + strlen(filename) + 1 >= sizeof(targetfile))
- {
- syserr("mailfile: filename too long (%s/%s)",
- targetfile, filename);
- return EX_CANTCREAT;
- }
- realfile = targetfile + len;
- if (targetfile[len - 1] != '/')
- (void) sm_strlcat(targetfile, "/", sizeof(targetfile));
- if (*filename == '/')
- (void) sm_strlcat(targetfile, filename + 1,
- sizeof(targetfile));
- else
- (void) sm_strlcat(targetfile, filename,
- sizeof(targetfile));
- }
- else
- {
- if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >=
- sizeof(targetfile))
- {
- syserr("mailfile: filename too long (%s)", filename);
- return EX_CANTCREAT;
- }
- realfile = targetfile;
- }
-
- /*
- ** Fork so we can change permissions here.
- ** Note that we MUST use fork, not vfork, because of
- ** the complications of calling subroutines, etc.
- */
-
-
- /*
- ** Dispose of SIGCHLD signal catchers that may be laying
- ** around so that the waitfor() below will get it.
- */
-
- (void) sm_signal(SIGCHLD, SIG_DFL);
-
- DOFORK(fork);
-
- if (pid < 0)
- return EX_OSERR;
- else if (pid == 0)
- {
- /* child -- actually write to file */
- struct stat stb;
- MCI mcibuf;
- int err;
- volatile int oflags = O_WRONLY|O_APPEND;
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
-
- if (e->e_lockfp != NULL)
- {
- int fd;
-
- fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
- /* SM_ASSERT(fd >= 0); */
- if (fd >= 0)
- (void) close(fd);
- }
-
- (void) sm_signal(SIGINT, SIG_DFL);
- (void) sm_signal(SIGHUP, SIG_DFL);
- (void) sm_signal(SIGTERM, SIG_DFL);
- (void) umask(OldUmask);
- e->e_to = filename;
- ExitStat = EX_OK;
-
- if (setjmp(CtxMailfileTimeout) != 0)
- {
- RETURN(EX_TEMPFAIL);
- }
-
- if (TimeOuts.to_fileopen > 0)
- ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
- 0);
- else
- ev = NULL;
-
- /* check file mode to see if set-user-ID */
- if (stat(targetfile, &stb) < 0)
- mode = FileMode;
- else
- mode = stb.st_mode;
-
- /* limit the errors to those actually caused in the child */
- errno = 0;
- ExitStat = EX_OK;
-
- /* Allow alias expansions to use the S_IS{U,G}ID bits */
- if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
- bitset(SFF_RUNASREALUID, sfflags))
- {
- /* ignore set-user-ID and set-group-ID bits */
- mode &= ~(S_ISGID|S_ISUID);
- if (tTd(11, 20))
- sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
- }
-
- /* we have to open the data file BEFORE setuid() */
- if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
- {
- char *df = queuename(e, DATAFL_LETTER);
-
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
- SM_IO_RDONLY_B, NULL);
- if (e->e_dfp == NULL)
- {
- syserr("mailfile: Cannot open %s for %s from %s",
- df, e->e_to, e->e_from.q_paddr);
- }
- }
-
- /* select a new user to run as */
- if (!bitset(SFF_RUNASREALUID, sfflags))
- {
- if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
- {
- RealUserName = NULL;
- if (mailer->m_uid == NO_UID)
- RealUid = RunAsUid;
- else
- RealUid = mailer->m_uid;
- if (RunAsUid != 0 && RealUid != RunAsUid)
- {
- /* Only root can change the uid */
- syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d",
- (int) RunAsUid, (int) RealUid);
- RETURN(EX_TEMPFAIL);
- }
- }
- else if (bitset(S_ISUID, mode))
- {
- RealUserName = NULL;
- RealUid = stb.st_uid;
- }
- else if (ctladdr != NULL && ctladdr->q_uid != 0)
- {
- if (ctladdr->q_ruser != NULL)
- RealUserName = ctladdr->q_ruser;
- else
- RealUserName = ctladdr->q_user;
- RealUid = ctladdr->q_uid;
- }
- else if (mailer != NULL && mailer->m_uid != NO_UID)
- {
- RealUserName = DefUser;
- RealUid = mailer->m_uid;
- }
- else
- {
- RealUserName = DefUser;
- RealUid = DefUid;
- }
-
- /* select a new group to run as */
- if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
- {
- if (mailer->m_gid == NO_GID)
- RealGid = RunAsGid;
- else
- RealGid = mailer->m_gid;
- if (RunAsUid != 0 &&
- (RealGid != getgid() ||
- RealGid != getegid()))
- {
- /* Only root can change the gid */
- syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d",
- (int) RealGid, (int) RunAsUid,
- (int) getgid(), (int) getegid());
- RETURN(EX_TEMPFAIL);
- }
- }
- else if (bitset(S_ISGID, mode))
- RealGid = stb.st_gid;
- else if (ctladdr != NULL &&
- ctladdr->q_uid == DefUid &&
- ctladdr->q_gid == 0)
- {
- /*
- ** Special case: This means it is an
- ** alias and we should act as DefaultUser.
- ** See alias()'s comments.
- */
-
- RealGid = DefGid;
- RealUserName = DefUser;
- }
- else if (ctladdr != NULL && ctladdr->q_uid != 0)
- RealGid = ctladdr->q_gid;
- else if (mailer != NULL && mailer->m_gid != NO_GID)
- RealGid = mailer->m_gid;
- else
- RealGid = DefGid;
- }
-
- /* last ditch */
- if (!bitset(SFF_ROOTOK, sfflags))
- {
- if (RealUid == 0)
- RealUid = DefUid;
- if (RealGid == 0)
- RealGid = DefGid;
- }
-
- /* set group id list (needs /etc/group access) */
- if (RealUserName != NULL && !DontInitGroups)
- {
- if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
- {
- syserr("mailfile: initgroups(%s, %d) failed",
- RealUserName, RealGid);
- RETURN(EX_TEMPFAIL);
- }
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = RealGid;
- if (setgroups(1, gidset) == -1 && suidwarn)
- {
- syserr("mailfile: setgroups() failed");
- RETURN(EX_TEMPFAIL);
- }
- }
-
- /*
- ** If you have a safe environment, go into it.
- */
-
- if (realfile != targetfile)
- {
- char save;
-
- save = *realfile;
- *realfile = '\0';
- if (tTd(11, 20))
- sm_dprintf("mailfile: chroot %s\n", targetfile);
- if (chroot(targetfile) < 0)
- {
- syserr("mailfile: Cannot chroot(%s)",
- targetfile);
- RETURN(EX_CANTCREAT);
- }
- *realfile = save;
- }
-
- if (tTd(11, 40))
- sm_dprintf("mailfile: deliver to %s\n", realfile);
-
- if (chdir("/") < 0)
- {
- syserr("mailfile: cannot chdir(/)");
- RETURN(EX_CANTCREAT);
- }
-
- /* now reset the group and user ids */
- endpwent();
- sm_mbdb_terminate();
- if (setgid(RealGid) < 0 && suidwarn)
- {
- syserr("mailfile: setgid(%ld) failed", (long) RealGid);
- RETURN(EX_TEMPFAIL);
- }
- vendor_set_uid(RealUid);
- if (setuid(RealUid) < 0 && suidwarn)
- {
- syserr("mailfile: setuid(%ld) failed", (long) RealUid);
- RETURN(EX_TEMPFAIL);
- }
-
- if (tTd(11, 2))
- sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
- (int) getuid(), (int) geteuid(),
- (int) getgid(), (int) getegid());
-
-
- /* move into some "safe" directory */
- if (mailer->m_execdir != NULL)
- {
- char *q;
-
- for (p = mailer->m_execdir; p != NULL; p = q)
- {
- q = strchr(p, ':');
- if (q != NULL)
- *q = '\0';
- expand(p, buf, sizeof(buf), e);
- if (q != NULL)
- *q++ = ':';
- if (tTd(11, 20))
- sm_dprintf("mailfile: trydir %s\n",
- buf);
- if (buf[0] != '\0' && chdir(buf) >= 0)
- break;
- }
- }
-
- /*
- ** Recheck the file after we have assumed the ID of the
- ** delivery user to make sure we can deliver to it as
- ** that user. This is necessary if sendmail is running
- ** as root and the file is on an NFS mount which treats
- ** root as nobody.
- */
-
-#if HASLSTAT
- if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
- err = stat(realfile, &stb);
- else
- err = lstat(realfile, &stb);
-#else /* HASLSTAT */
- err = stat(realfile, &stb);
-#endif /* HASLSTAT */
-
- if (err < 0)
- {
- stb.st_mode = ST_MODE_NOFILE;
- mode = FileMode;
- oflags |= O_CREAT|O_EXCL;
- }
- else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
- (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
- DontBlameSendmail) &&
- stb.st_nlink != 1) ||
- (realfile != targetfile && !S_ISREG(mode)))
- exit(EX_CANTCREAT);
- else
- mode = stb.st_mode;
-
- if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
- sfflags |= SFF_NOSLINK;
- if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
- sfflags |= SFF_NOHLINK;
- sfflags &= ~SFF_OPENASROOT;
- f = safefopen(realfile, oflags, mode, sfflags);
- if (f == NULL)
- {
- if (transienterror(errno))
- {
- usrerr("454 4.3.0 cannot open %s: %s",
- shortenstring(realfile, MAXSHORTSTR),
- sm_errstring(errno));
- RETURN(EX_TEMPFAIL);
- }
- else
- {
- usrerr("554 5.3.0 cannot open %s: %s",
- shortenstring(realfile, MAXSHORTSTR),
- sm_errstring(errno));
- RETURN(EX_CANTCREAT);
- }
- }
- if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
- &stb))
- {
- syserr("554 5.3.0 file changed after open");
- RETURN(EX_CANTCREAT);
- }
- if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
- {
- syserr("554 5.3.0 cannot fstat %s",
- sm_errstring(errno));
- RETURN(EX_CANTCREAT);
- }
-
- curoff = stb.st_size;
-
- if (ev != NULL)
- sm_clrevent(ev);
-
- memset(&mcibuf, '\0', sizeof(mcibuf));
- mcibuf.mci_mailer = mailer;
- mcibuf.mci_out = f;
- if (bitnset(M_7BITS, mailer->m_flags))
- mcibuf.mci_flags |= MCIF_7BIT;
-
- /* clear out per-message flags from connection structure */
- mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
-
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- !bitset(EF_DONT_MIME, e->e_flags) &&
- bitnset(M_7BITS, mailer->m_flags))
- mcibuf.mci_flags |= MCIF_CVT8TO7;
-
-#if MIME7TO8
- if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
- !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
- (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
- (sm_strcasecmp(p, "quoted-printable") == 0 ||
- sm_strcasecmp(p, "base64") == 0) &&
- (p = hvalue("Content-Type", e->e_header)) != NULL)
- {
- /* may want to convert 7 -> 8 */
- /* XXX should really parse it here -- and use a class XXX */
- if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
- (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
- mcibuf.mci_flags |= MCIF_CVT7TO8;
- }
-#endif /* MIME7TO8 */
-
- if (!putfromline(&mcibuf, e) ||
- !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) ||
- !(*e->e_putbody)(&mcibuf, e, NULL) ||
- !putline("\n", &mcibuf) ||
- (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
- (SuperSafe != SAFE_NO &&
- fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
- sm_io_error(f)))
- {
- setstat(EX_IOERR);
-#if !NOFTRUNCATE
- (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
- curoff);
-#endif /* !NOFTRUNCATE */
- }
-
- /* reset ISUID & ISGID bits for paranoid systems */
-#if HASFCHMOD
- (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
- (MODE_T) mode);
-#else /* HASFCHMOD */
- (void) chmod(filename, (MODE_T) mode);
-#endif /* HASFCHMOD */
- if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
- setstat(EX_IOERR);
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- (void) setuid(RealUid);
- exit(ExitStat);
- /* NOTREACHED */
- }
- else
- {
- /* parent -- wait for exit status */
- int st;
-
- st = waitfor(pid);
- if (st == -1)
- {
- syserr("mailfile: %s: wait", mailer->m_name);
- return EX_SOFTWARE;
- }
- if (WIFEXITED(st))
- {
- errno = 0;
- return (WEXITSTATUS(st));
- }
- else
- {
- syserr("mailfile: %s: child died on signal %d",
- mailer->m_name, st);
- return EX_UNAVAILABLE;
- }
- /* NOTREACHED */
- }
- return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */
-}
-
-static void
-mailfiletimeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(CtxMailfileTimeout, 1);
-}
-/*
-** HOSTSIGNATURE -- return the "signature" for a host.
-**
-** The signature describes how we are going to send this -- it
-** can be just the hostname (for non-Internet hosts) or can be
-** an ordered list of MX hosts.
-**
-** Parameters:
-** m -- the mailer describing this host.
-** host -- the host name.
-**
-** Returns:
-** The signature for this host.
-**
-** Side Effects:
-** Can tweak the symbol table.
-*/
-
-#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */
-
-char *
-hostsignature(m, host)
- register MAILER *m;
- char *host;
-{
- register char *p;
- register STAB *s;
- time_t now;
-#if NAMED_BIND
- char sep = ':';
- char prevsep = ':';
- int i;
- int len;
- int nmx;
- int hl;
- char *hp;
- char *endp;
- int oldoptions = _res.options;
- char *mxhosts[MAXMXHOSTS + 1];
- unsigned short mxprefs[MAXMXHOSTS + 1];
-#endif /* NAMED_BIND */
-
- if (tTd(17, 3))
- sm_dprintf("hostsignature(%s)\n", host);
-
- /*
- ** If local delivery (and not remote), just return a constant.
- */
-
- if (bitnset(M_LOCALMAILER, m->m_flags) &&
- strcmp(m->m_mailer, "[IPC]") != 0 &&
- !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
- return "localhost";
-
- /* an empty host does not have MX records */
- if (*host == '\0')
- return "_empty_";
-
- /*
- ** Check to see if this uses IPC -- if not, it can't have MX records.
- */
-
- if (strcmp(m->m_mailer, "[IPC]") != 0 ||
- CurEnv->e_sendmode == SM_DEFER)
- {
- /* just an ordinary mailer or deferred mode */
- return host;
- }
-#if NETUNIX
- else if (m->m_argv[0] != NULL &&
- strcmp(m->m_argv[0], "FILE") == 0)
- {
- /* rendezvous in the file system, no MX records */
- return host;
- }
-#endif /* NETUNIX */
-
- /*
- ** Look it up in the symbol table.
- */
-
- now = curtime();
- s = stab(host, ST_HOSTSIG, ST_ENTER);
- if (s->s_hostsig.hs_sig != NULL)
- {
- if (s->s_hostsig.hs_exp >= now)
- {
- if (tTd(17, 3))
- sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
- s->s_hostsig.hs_sig);
- return s->s_hostsig.hs_sig;
- }
-
- /* signature is expired: clear it */
- sm_free(s->s_hostsig.hs_sig);
- s->s_hostsig.hs_sig = NULL;
- }
-
- /* set default TTL */
- s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
-
- /*
- ** Not already there or expired -- create a signature.
- */
-
-#if NAMED_BIND
- if (ConfigLevel < 2)
- _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
-
- for (hp = host; hp != NULL; hp = endp)
- {
-#if NETINET6
- if (*hp == '[')
- {
- endp = strchr(hp + 1, ']');
- if (endp != NULL)
- endp = strpbrk(endp + 1, ":,");
- }
- else
- endp = strpbrk(hp, ":,");
-#else /* NETINET6 */
- endp = strpbrk(hp, ":,");
-#endif /* NETINET6 */
- if (endp != NULL)
- {
- sep = *endp;
- *endp = '\0';
- }
-
- if (bitnset(M_NOMX, m->m_flags))
- {
- /* skip MX lookups */
- nmx = 1;
- mxhosts[0] = hp;
- }
- else
- {
- auto int rcode;
- int ttl;
-
- nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
- &ttl);
- if (nmx <= 0)
- {
- int save_errno;
- register MCI *mci;
-
- /* update the connection info for this host */
- save_errno = errno;
- mci = mci_get(hp, m);
- mci->mci_errno = save_errno;
- mci->mci_herrno = h_errno;
- mci->mci_lastuse = now;
- if (rcode == EX_NOHOST)
- mci_setstat(mci, rcode, "5.1.2",
- "550 Host unknown");
- else
- mci_setstat(mci, rcode, NULL, NULL);
-
- /* use the original host name as signature */
- nmx = 1;
- mxhosts[0] = hp;
- }
- if (tTd(17, 3))
- sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
- nmx, mxhosts[0]);
-
- /*
- ** Set new TTL: we use only one!
- ** We could try to use the minimum instead.
- */
-
- s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
- }
-
- len = 0;
- for (i = 0; i < nmx; i++)
- len += strlen(mxhosts[i]) + 1;
- if (s->s_hostsig.hs_sig != NULL)
- len += strlen(s->s_hostsig.hs_sig) + 1;
- if (len < 0 || len >= MAXHOSTSIGNATURE)
- {
- sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
- host, MAXHOSTSIGNATURE, len);
- len = MAXHOSTSIGNATURE;
- }
- p = sm_pmalloc_x(len);
- if (s->s_hostsig.hs_sig != NULL)
- {
- (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
- sm_free(s->s_hostsig.hs_sig); /* XXX */
- s->s_hostsig.hs_sig = p;
- hl = strlen(p);
- p += hl;
- *p++ = prevsep;
- len -= hl + 1;
- }
- else
- s->s_hostsig.hs_sig = p;
- for (i = 0; i < nmx; i++)
- {
- hl = strlen(mxhosts[i]);
- if (len - 1 < hl || len <= 1)
- {
- /* force to drop out of outer loop */
- len = -1;
- break;
- }
- if (i != 0)
- {
- if (mxprefs[i] == mxprefs[i - 1])
- *p++ = ',';
- else
- *p++ = ':';
- len--;
- }
- (void) sm_strlcpy(p, mxhosts[i], len);
- p += hl;
- len -= hl;
- }
-
- /*
- ** break out of loop if len exceeded MAXHOSTSIGNATURE
- ** because we won't have more space for further hosts
- ** anyway (separated by : in the .cf file).
- */
-
- if (len < 0)
- break;
- if (endp != NULL)
- *endp++ = sep;
- prevsep = sep;
- }
- makelower(s->s_hostsig.hs_sig);
- if (ConfigLevel < 2)
- _res.options = oldoptions;
-#else /* NAMED_BIND */
- /* not using BIND -- the signature is just the host name */
- /*
- ** 'host' points to storage that will be freed after we are
- ** done processing the current envelope, so we copy it.
- */
- s->s_hostsig.hs_sig = sm_pstrdup_x(host);
-#endif /* NAMED_BIND */
- if (tTd(17, 1))
- sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
- return s->s_hostsig.hs_sig;
-}
-/*
-** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
-**
-** The signature describes how we are going to send this -- it
-** can be just the hostname (for non-Internet hosts) or can be
-** an ordered list of MX hosts which must be randomized for equal
-** MX preference values.
-**
-** Parameters:
-** sig -- the host signature.
-** mxhosts -- array to populate.
-** mailer -- mailer.
-**
-** Returns:
-** The number of hosts inserted into mxhosts array.
-**
-** Side Effects:
-** Randomizes equal MX preference hosts in mxhosts.
-*/
-
-static int
-parse_hostsignature(sig, mxhosts, mailer)
- char *sig;
- char **mxhosts;
- MAILER *mailer;
-{
- unsigned short curpref = 0;
- int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */
- char *hp, *endp;
- unsigned short prefer[MAXMXHOSTS];
- long rndm[MAXMXHOSTS];
-
- for (hp = sig; hp != NULL; hp = endp)
- {
- char sep = ':';
-
-#if NETINET6
- if (*hp == '[')
- {
- endp = strchr(hp + 1, ']');
- if (endp != NULL)
- endp = strpbrk(endp + 1, ":,");
- }
- else
- endp = strpbrk(hp, ":,");
-#else /* NETINET6 */
- endp = strpbrk(hp, ":,");
-#endif /* NETINET6 */
- if (endp != NULL)
- {
- sep = *endp;
- *endp = '\0';
- }
-
- mxhosts[nmx] = hp;
- prefer[nmx] = curpref;
- if (mci_match(hp, mailer))
- rndm[nmx] = 0;
- else
- rndm[nmx] = get_random();
-
- if (endp != NULL)
- {
- /*
- ** Since we don't have the original MX prefs,
- ** make our own. If the separator is a ':', that
- ** means the preference for the next host will be
- ** higher than this one, so simply increment curpref.
- */
-
- if (sep == ':')
- curpref++;
-
- *endp++ = sep;
- }
- if (++nmx >= MAXMXHOSTS)
- break;
- }
-
- /* sort the records using the random factor for equal preferences */
- for (i = 0; i < nmx; i++)
- {
- for (j = i + 1; j < nmx; j++)
- {
- /*
- ** List is already sorted by MX preference, only
- ** need to look for equal preference MX records
- */
-
- if (prefer[i] < prefer[j])
- break;
-
- if (prefer[i] > prefer[j] ||
- (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
- {
- register unsigned short tempp;
- register long tempr;
- register char *temp1;
-
- tempp = prefer[i];
- prefer[i] = prefer[j];
- prefer[j] = tempp;
- temp1 = mxhosts[i];
- mxhosts[i] = mxhosts[j];
- mxhosts[j] = temp1;
- tempr = rndm[i];
- rndm[i] = rndm[j];
- rndm[j] = tempr;
- }
- }
- }
- return nmx;
-}
-
-# if STARTTLS
-static SSL_CTX *clt_ctx = NULL;
-static bool tls_ok_clt = true;
-
-/*
-** SETCLTTLS -- client side TLS: allow/disallow.
-**
-** Parameters:
-** tls_ok -- should tls be done?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** sets tls_ok_clt (static variable in this module)
-*/
-
-void
-setclttls(tls_ok)
- bool tls_ok;
-{
- tls_ok_clt = tls_ok;
- return;
-}
-/*
-** INITCLTTLS -- initialize client side TLS
-**
-** Parameters:
-** tls_ok -- should tls initialization be done?
-**
-** Returns:
-** succeeded?
-**
-** Side Effects:
-** sets tls_ok_clt (static variable in this module)
-*/
-
-bool
-initclttls(tls_ok)
- bool tls_ok;
-{
- if (!tls_ok_clt)
- return false;
- tls_ok_clt = tls_ok;
- if (!tls_ok_clt)
- return false;
- if (clt_ctx != NULL)
- return true; /* already done */
- tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile,
- CltKeyFile, CACertPath, CACertFile, DHParams);
- return tls_ok_clt;
-}
-
-/*
-** STARTTLS -- try to start secure connection (client side)
-**
-** Parameters:
-** m -- the mailer.
-** mci -- the mailer connection info.
-** e -- the envelope.
-**
-** Returns:
-** success?
-** (maybe this should be some other code than EX_
-** that denotes which stage failed.)
-*/
-
-static int
-starttls(m, mci, e)
- MAILER *m;
- MCI *mci;
- ENVELOPE *e;
-{
- int smtpresult;
- int result = 0;
- int rfd, wfd;
- SSL *clt_ssl = NULL;
- time_t tlsstart;
-
- if (clt_ctx == NULL && !initclttls(true))
- return EX_TEMPFAIL;
- smtpmessage("STARTTLS", m, mci);
-
- /* get the reply */
- smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
- XS_STARTTLS);
-
- /* check return code from server */
- if (REPLYTYPE(smtpresult) == 4)
- return EX_TEMPFAIL;
- if (smtpresult == 501)
- return EX_USAGE;
- if (smtpresult == -1)
- return smtpresult;
-
- /* not an expected reply but we have to deal with it */
- if (REPLYTYPE(smtpresult) == 5)
- return EX_UNAVAILABLE;
- if (smtpresult != 220)
- return EX_PROTOCOL;
-
- if (LogLevel > 13)
- sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
-
- /* start connection */
- if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=client, error: SSL_new failed");
- if (LogLevel > 9)
- tlslogerr("client");
- }
- return EX_SOFTWARE;
- }
-
- rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
- wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
-
- /* SSL_clear(clt_ssl); ? */
- if (rfd < 0 || wfd < 0 ||
- (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
- (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=client, error: SSL_set_xfd failed=%d",
- result);
- if (LogLevel > 9)
- tlslogerr("client");
- }
- return EX_SOFTWARE;
- }
- SSL_set_connect_state(clt_ssl);
- tlsstart = curtime();
-
-ssl_retry:
- if ((result = SSL_connect(clt_ssl)) <= 0)
- {
- int i, ssl_err;
-
- ssl_err = SSL_get_error(clt_ssl, result);
- i = tls_retry(clt_ssl, rfd, wfd, tlsstart,
- TimeOuts.to_starttls, ssl_err, "client");
- if (i > 0)
- goto ssl_retry;
-
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=client, error: connect failed=%d, SSL_error=%d, errno=%d, retry=%d",
- result, ssl_err, errno, i);
- if (LogLevel > 8)
- tlslogerr("client");
- }
-
- SSL_free(clt_ssl);
- clt_ssl = NULL;
- return EX_SOFTWARE;
- }
- mci->mci_ssl = clt_ssl;
- result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
- &mci->mci_macro, true);
-
- /* switch to use TLS... */
- if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
- return EX_OK;
-
- /* failure */
- SSL_free(clt_ssl);
- clt_ssl = NULL;
- return EX_SOFTWARE;
-}
-/*
-** ENDTLSCLT -- shutdown secure connection (client side)
-**
-** Parameters:
-** mci -- the mailer connection info.
-**
-** Returns:
-** success?
-*/
-
-static int
-endtlsclt(mci)
- MCI *mci;
-{
- int r;
-
- if (!bitset(MCIF_TLSACT, mci->mci_flags))
- return EX_OK;
- r = endtls(mci->mci_ssl, "client");
- mci->mci_flags &= ~MCIF_TLSACT;
- return r;
-}
-# endif /* STARTTLS */
-# if STARTTLS || SASL
-/*
-** ISCLTFLGSET -- check whether client flag is set.
-**
-** Parameters:
-** e -- envelope.
-** flag -- flag to check in {client_flags}
-**
-** Returns:
-** true iff flag is set.
-*/
-
-static bool
-iscltflgset(e, flag)
- ENVELOPE *e;
- int flag;
-{
- char *p;
-
- p = macvalue(macid("{client_flags}"), e);
- if (p == NULL)
- return false;
- for (; *p != '\0'; p++)
- {
- /* look for just this one flag */
- if (*p == (char) flag)
- return true;
- }
- return false;
-}
-# endif /* STARTTLS || SASL */
OpenPOWER on IntegriCloud