Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  smtp_connect.c

  Sprache: C
 

/*++
/* NAME
/* smtp_connect 3
/* SUMMARY
/* connect to SMTP/LMTP server and deliver
/* SYNOPSIS
/* #include "smtp.h"
/*
/* int smtp_connect(state)
/* SMTP_STATE *state;
/* DESCRIPTION
/* This module implements SMTP/LMTP connection management and controls
/* mail delivery.
/*
/* smtp_connect() attempts to establish an SMTP/LMTP session with a host
/* that represents the destination domain, or with an optional fallback
/* relay when {the destination cannot be found, or when all the
/* destination servers are unavailable}. It skips over IP addresses
/* that fail to complete the SMTP/LMTP handshake and tries to find
/* an alternate server when an SMTP/LMTP session fails to deliver.
/*
/* This layer also controls what connections are retrieved from
/* the connection cache, and what connections are saved to the cache.
/*
/* The destination is either a host (or domain) name or a numeric
/* address. Symbolic or numeric service port information may be
/* appended, separated by a colon (":"). In the case of LMTP,
/* destinations may be specified as "unix:pathname", "inet:host"
/* or "inet:host:port".
/*
/* With SMTP, or with SRV record lookup enabled, the Internet
/* domain name service is queried for mail
/* exchanger hosts. Quote the domain name with `[' and `]' to
/* suppress mail exchanger lookups.
/*
/* Numerical address information should always be quoted with `[]'.
/* DIAGNOSTICS
/* The delivery status is the result value.
/* SEE ALSO
/* smtp_proto(3) SMTP client protocol
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Connection caching in cooperation with:
/* Victor Duchovni
/* Morgan Stanley
/*--*/


/* System library. */

#include <sys_defs.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>

#ifndef IPPORT_SMTP
#define IPPORT_SMTP 25
#endif

/* Utility library. */

#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <split_at.h>
#include <mymalloc.h>
#include <inet_addr_list.h>
#include <iostuff.h>
#include <timed_connect.h>
#include <stringops.h>
#include <host_port.h>
#include <sane_connect.h>
#include <myaddrinfo.h>
#include <sock_addr.h>
#include <inet_proto.h>
#include <known_tcp_ports.h>

/* Global library. */

#include <mail_params.h>
#include <own_inet_addr.h>
#include <deliver_pass.h>
#include <mail_error.h>
#include <dsn_buf.h>
#include <mail_addr.h>
#include <valid_hostname.h>
#include <sendopts.h>

/* DNS library. */

#include <dns.h>

/* Application-specific. */

#include <smtp.h>
#include <smtp_addr.h>
#include <smtp_reuse.h>

 /*
  * Forward declaration.
  */

static SMTP_SESSION *smtp_connect_sock(intstruct sockaddr *, int,
                   SMTP_ITERATOR *, DSN_BUF *,
                   int);

/* smtp_connect_unix - connect to UNIX-domain address */

static SMTP_SESSION *smtp_connect_unix(SMTP_ITERATOR *iter, DSN_BUF *why,
                   int sess_flags)
{
    const char *myname = "smtp_connect_unix";
    struct sockaddr_un sock_un;
    const char *addr = STR(iter->addr);
    int     len = strlen(addr);
    int     sock;

    dsb_reset(why);    /* Paranoia */

    /*
     * Sanity checks.
     */

    if (len >= (intsizeof(sock_un.sun_path)) {
 msg_warn("unix-domain name too long: %s", addr);
 dsb_simple(why, "4.3.5""Server configuration error");
 return (0);
    }

    /*
     * Initialize.
     */

    memset((void *) &sock_un, 0sizeof(sock_un));
    sock_un.sun_family = AF_UNIX;
#ifdef HAS_SUN_LEN
    sock_un.sun_len = len + 1;
#endif
    memcpy(sock_un.sun_path, addr, len + 1);

    /*
     * Create a client socket.
     */

    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
 msg_fatal("%s: socket: %m", myname);

    /*
     * Connect to the server.
     */

    if (msg_verbose)
 msg_info("%s: trying: %s...", myname, addr);

    return (smtp_connect_sock(sock, (struct sockaddr *) &sock_un,
         sizeof(sock_un), iter, why, sess_flags));
}

/* smtp_connect_addr - connect to explicit address */

static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why,
                   int sess_flags)
{
    const char *myname = "smtp_connect_addr";
    struct sockaddr_storage ss;  /* remote */
    struct sockaddr *sa = (struct sockaddr *) &ss;
    SOCKADDR_SIZE salen = sizeof(ss);
    MAI_HOSTADDR_STR hostaddr;
    DNS_RR *addr = iter->rr;
    unsigned port = iter->port;
    int     sock;
    char   *bind_addr;
    char   *bind_var;
    char   *saved_bind_addr = 0;
    char   *tail;

    dsb_reset(why);    /* Paranoia */

    /*
     * Sanity checks.
     */

    if (dns_rr_to_sa(addr, port, sa, &salen) != 0) {
 msg_warn("%s: skip address type %s: %m",
   myname, dns_strtype(addr->type));
 dsb_simple(why, "4.4.0""network address conversion failed: %m");
 return (0);
    }

    /*
     * Initialize.
     */

    if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
 msg_fatal("%s: socket: %m", myname);

#define RETURN_EARLY() do { \
 if (saved_bind_addr) \
     myfree(saved_bind_addr); \
 (void) close(sock); \
 return (0); \
    } while (0)

    if (inet_windowsize > 0)
 set_inet_windowsize(sock, inet_windowsize);

    /*
     * Allow the sysadmin to specify the source address, for example, as "-o
     * smtp_bind_address=x.x.x.x" in the master.cf file.
     */

#ifdef HAS_IPV6
    if (sa->sa_family == AF_INET6) {
 bind_addr = var_smtp_bind_addr6;
 bind_var = VAR_LMTP_SMTP(BIND_ADDR6);
    } else
#endif
    if (sa->sa_family == AF_INET) {
 bind_addr = var_smtp_bind_addr;
 bind_var = VAR_LMTP_SMTP(BIND_ADDR);
    } else
 bind_var = bind_addr = "";
    if (*bind_addr) {
 int     aierr;
 struct addrinfo *res0;

 if (*bind_addr == '[') {
     saved_bind_addr = mystrdup(bind_addr + 1);
     if ((tail = split_at(saved_bind_addr, ']')) == 0 || *tail)
  msg_fatal("%s: malformed %s parameter: %s",
     myname, bind_var, bind_addr);
     bind_addr = saved_bind_addr;
 }
 if ((aierr = hostaddr_to_sockaddr(bind_addr, (char *) 00, &res0)) != 0)
     msg_fatal("%s: bad %s parameter: %s: %s",
        myname, bind_var, bind_addr, MAI_STRERROR(aierr));
 if (bind(sock, res0->ai_addr, res0->ai_addrlen) < 0) {
     msg_warn("%s: bind %s: %m", myname, bind_addr);
     if (var_smtp_bind_addr_enforce) {
  freeaddrinfo(res0);
  dsb_simple(why, "4.4.0""server configuration error");
  RETURN_EARLY();
     }
 } else if (msg_verbose)
     msg_info("%s: bind %s", myname, bind_addr);
 if (saved_bind_addr)
     myfree(saved_bind_addr);
 freeaddrinfo(res0);
    }

    /*
     * When running as a virtual host, bind to the virtual interface so that
     * the mail appears to come from the "right" machine address.
     * 
     * XXX The IPv6 patch expands the null host (as client endpoint) and uses
     * the result as the loopback address list.
     */

    else {
 int     count = 0;
 struct sockaddr *own_addr = 0;
 INET_ADDR_LIST *addr_list = own_inet_addr_list();
 struct sockaddr_storage *s;

 for (s = addr_list->addrs; s < addr_list->addrs + addr_list->used; s++) {
     if (SOCK_ADDR_FAMILY(s) == sa->sa_family) {
  if (count++ > 0)
      break;
  own_addr = SOCK_ADDR_PTR(s);
     }
 }
 if (count == 1 && !sock_addr_in_loopback(own_addr)) {
     if (bind(sock, own_addr, SOCK_ADDR_LEN(own_addr)) < 0) {
  SOCKADDR_TO_HOSTADDR(own_addr, SOCK_ADDR_LEN(own_addr),
         &hostaddr, (MAI_SERVPORT_STR *) 00);
  msg_warn("%s: bind %s: %m", myname, hostaddr.buf);
     } else if (msg_verbose) {
  SOCKADDR_TO_HOSTADDR(own_addr, SOCK_ADDR_LEN(own_addr),
         &hostaddr, (MAI_SERVPORT_STR *) 00);
  msg_info("%s: bind %s", myname, hostaddr.buf);
     }
 }
    }

    /*
     * Connect to the server.
     */

    if (msg_verbose)
 msg_info("%s: trying: %s[%s] port %d...",
   myname, STR(iter->host), STR(iter->addr), ntohs(port));

    return (smtp_connect_sock(sock, sa, salen, iter, why, sess_flags));
}

/* smtp_connect_sock - connect a socket over some transport */

static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr *sa,
                   int salen,
                   SMTP_ITERATOR *iter,
                   DSN_BUF *why,
                   int sess_flags)
{
    int     conn_stat;
    int     saved_errno;
    VSTREAM *stream;
    time_t  start_time;
    const char *name = STR(iter->host);
    const char *addr = STR(iter->addr);
    unsigned port = iter->port;

    start_time = time((time_t *) 0);
    if (var_smtp_conn_tmout > 0) {
 non_blocking(sock, NON_BLOCKING);
 conn_stat = timed_connect(sock, sa, salen, var_smtp_conn_tmout);
 saved_errno = errno;
 non_blocking(sock, BLOCKING);
 errno = saved_errno;
    } else {
 conn_stat = sane_connect(sock, sa, salen);
    }
    if (conn_stat < 0) {
 if (port)
     dsb_simple(why, "4.4.1""connect to %s[%s]:%d: %m",
         name, addr, ntohs(port));
 else
     dsb_simple(why, "4.4.1""connect to %s[%s]: %m", name, addr);
 close(sock);
 return (0);
    }
    stream = vstream_fdopen(sock, O_RDWR);

    /*
     * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
     */

    if (sa->sa_family == AF_INET
#ifdef AF_INET6
 || sa->sa_family == AF_INET6
#endif
 )
 vstream_tweak_tcp(stream);

    /*
     * Bundle up what we have into a nice SMTP_SESSION object.
     */

    return (smtp_session_alloc(stream, iter, start_time, sess_flags));
}

/* smtp_parse_destination - parse host/port destination */

static char *smtp_parse_destination(char *destination, char *def_service,
                char **hostp, char **servicep,
                unsigned *portp)
{
    char   *buf = mystrdup(destination);
    char   *service;
    struct servent *sp;
    char   *protocol = "tcp";  /* XXX configurable? */
    unsigned port;
    const char *err;

    if (msg_verbose)
 msg_info("smtp_parse_destination: %s %s", destination, def_service);

    /*
     * Parse the host/port information. We're working with a copy of the
     * destination argument so the parsing can be destructive.
     */

    if ((err = host_port(buf, hostp, (char *) 0, servicep, def_service)) != 0)
 msg_fatal("%s in server description: %s", err, destination);

    /*
     * Convert service to port number, network byte order.
     */

    service = (char *) filter_known_tcp_port(*servicep);
    if (alldig(service)) {
 if ((port = atoi(service)) >= 65536 || port == 0)
     msg_fatal("bad network port: %s for destination: %s",
        service, destination);
 *portp = htons(port);
    } else {
 if ((sp = getservbyname(service, protocol)) == 0)
     msg_fatal("unknown service: %s/%s", service, protocol);
 *portp = sp->s_port;
    }
    return (buf);
}

/* smtp_cleanup_session - clean up after using a session */

static void smtp_cleanup_session(SMTP_STATE *state)
{
    DELIVER_REQUEST *request = state->request;
    SMTP_SESSION *session = state->session;
    int     throttled;

    /*
     * Inform the postmaster of trouble.
     * 
     * XXX Don't send notifications about errors while sending notifications.
     */

#define POSSIBLE_NOTIFICATION(sender) \
 (*sender == 0 || strcmp(sender, mail_addr_double_bounce()) == 0)

    if (session->history != 0
 && (session->error_mask & name_mask(VAR_NOTIFY_CLASSES,
         mail_error_masks,
         var_notify_classes)) != 0
 && POSSIBLE_NOTIFICATION(request->sender) == 0)
 smtp_chat_notify(session);

    /*
     * When session caching is enabled, cache the first good session for this
     * delivery request under the next-hop destination, and cache all good
     * sessions under their server network address (destroying the session in
     * the process).
     * 
     * Caching under the next-hop destination name (rather than the fall-back
     * destination) allows us to skip over non-responding primary or backup
     * hosts. In fact, this is the only benefit of caching logical to
     * physical bindings; caching a session under its own hostname provides
     * no performance benefit, given the way smtp_connect() works.
     */

    throttled = THIS_SESSION_IS_THROTTLED; /* smtp_quit() may fail */
    if (THIS_SESSION_IS_EXPIRED)
 smtp_quit(state);   /* also disables caching */
    if (THIS_SESSION_IS_CACHED
    /* Redundant tests for safety... */
 && vstream_ferror(session->stream) == 0
 && vstream_feof(session->stream) == 0) {
 smtp_save_session(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL,
     SMTP_KEY_MASK_SCACHE_ENDP_LABEL);
    } else {
 smtp_session_free(session);
    }
    state->session = 0;

    /*
     * If this session was good, reset the scache next-hop destination, so
     * that we won't cache connections to less-preferred servers under the
     * same next-hop destination. Otherwise we could end up skipping over the
     * available and more-preferred servers.
     */

    if (HAVE_SCACHE_REQUEST_NEXTHOP(state) && !throttled)
 CLEAR_SCACHE_REQUEST_NEXTHOP(state);

    /*
     * Clean up the lists with todo and dropped recipients.
     */

    smtp_rcpt_cleanup(state);

    /*
     * Reset profiling info.
     * 
     * XXX When one delivery request results in multiple sessions, the set-up
     * and transmission latencies of the earlier sessions will count as
     * connection set-up time for the later sessions.
     * 
     * XXX On the other hand, when we first try to connect to one or more dead
     * hosts before we reach a good host, then all that time must be counted
     * as connection set-up time for the session with the good host.
     * 
     * XXX So this set-up attribution problem exists only when we actually
     * engage in a session, spend a lot of time delivering a message, find
     * that it fails, and then connect to an alternate host.
     */

    memset((void *) &request->msg_stats.conn_setup_done, 0,
    sizeof(request->msg_stats.conn_setup_done));
    memset((void *) &request->msg_stats.deliver_done, 0,
    sizeof(request->msg_stats.deliver_done));
    request->msg_stats.reuse_count = 0;
}

static void smtp_cache_policy(SMTP_STATE *state, const char *dest)
{
    DELIVER_REQUEST *request = state->request;

    state->misc_flags &= ~SMTP_MISC_FLAG_CONN_CACHE_MASK;

    if (smtp_cache_dest && string_list_match(smtp_cache_dest, dest)) {
 state->misc_flags |= SMTP_MISC_FLAG_CONN_CACHE_MASK;
    } else if (var_smtp_cache_demand) {
 if (request->flags & DEL_REQ_FLAG_CONN_LOAD)
     state->misc_flags |= SMTP_MISC_FLAG_CONN_LOAD;
 if (request->flags & DEL_REQ_FLAG_CONN_STORE)
     state->misc_flags |= SMTP_MISC_FLAG_CONN_STORE;
    }
}

#ifdef USE_TLS

/* smtp_get_effective_tls_level - get the effective TLS security level */

static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state)
{
    SMTP_ITERATOR *iter = state->iterator;
    SMTP_TLS_POLICY *tls = state->tls;

    /*
     * Prepare TLS feature status logging.
     */

    if (state->tls_stats) {
 pol_stats_revert(state->tls_stats);
 if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE)
     smtp_tls_stat_activate_reqtls(state->tls_stats,
       SMTP_TLS_STAT_NAME_REQTLS);
    }

    /*
     * Determine the TLS level for this destination.
     */

    if (!smtp_tls_policy_cache_query(why, tls, iter)) {
 if (state->tls_stats)
     smtp_tls_stat_activate_sec_unknown(state->tls_stats);
 return (0);
    }
    if (state->tls_stats)
 smtp_tls_stat_activate_sec_level(state->tls_stats,
      state->tls->level);

    /*
     * Skip this destination if its TLS policy cannot satisfy the REQUIRETLS
     * policy for this destination (REQUIRETLS Failure).
     * 
     * Otherwise, log what would fail if REQUIRETLS was fully enforced
     * (REQUIRETLS Debug).
     * 
     * Finally, skip this destination if its REQUIRETLS policy is bad.
     */

    switch (state->reqtls_level) {
    case SMTP_REQTLS_POLICY_ACT_ENFORCE:
 if (TLS_MUST_MATCH(tls->level) == 0) {
     if (state->tls_stats)
  smtp_tls_stat_decide_reqtls(state->tls_stats,
         SMTP_TLS_STAT_NAME_NOCMATCH,
         POL_STAT_VIOLATION);
     dsb_simple(why, "5.7.10""Sender requested REQUIRETLS, "
         "but my configured TLS security level '%s' "
         "disables certificate matching. The last "
         "attempted server was %s", str_tls_level(tls->level),
         STR(iter->host));
     return (0);
 }
  break;
    case SMTP_REQTLS_POLICY_ACT_OPP_TLS:
 if (tls->level == TLS_LEV_NONE) {
     if (state->tls_stats)
  smtp_tls_stat_decide_reqtls(state->tls_stats,
         SMTP_TLS_STAT_NAME_NOTLS,
         POL_STAT_VIOLATION);
     dsb_simple(why, "5.7.10""Sender requested REQUIRETLS, "
         "but my configured TLS security level '%s' "
         "disables encryption. The last attempted "
         "server was %s", str_tls_level(tls->level),
         STR(iter->host));
     return (0);
 } else if (TLS_MUST_MATCH(tls->level) == 0) {
     msg_info("%s: Sender requested REQUIRETLS, but my "
       "configured TLS security level '%s' disables "
       "certificate matching. The last attempted server "
       "was %s", state->request->queue_id,
       str_tls_level(tls->level), STR(iter->host));
 }
 break;
    case SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC:
    case SMTP_REQTLS_POLICY_ACT_DISABLE:
 break;
    default:
 dsb_simple(why, "4.7.10""REQUIRETLS policy configuration "
     "error. The last attempted server was %s",
     STR(iter->host));
 return (0);
    }

    /*
     * Success.
     */

    return (1);
}

#endif

/* smtp_connect_local - connect to local server */

static void smtp_connect_local(SMTP_STATE *state, const char *path)
{
    const char *myname = "smtp_connect_local";
    SMTP_ITERATOR *iter = state->iterator;
    SMTP_SESSION *session;
    DSN_BUF *why = state->why;

    /*
     * Do not silently ignore an unused setting.
     */

    if (*var_fallback_relay)
 msg_warn("ignoring \"%s = %s\" setting for non-TCP connections",
   VAR_LMTP_FALLBACK, var_fallback_relay);

    /*
     * It's too painful to weave this code into the SMTP connection
     * management routine.
     * 
     * Connection cache management is based on the UNIX-domain pathname, without
     * the "unix:" prefix.
     */

    smtp_cache_policy(state, path);
    if (state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE_MASK)
 SET_SCACHE_REQUEST_NEXTHOP(state, path);

    /*
     * REQUIRETLS policy selection is based on the same TLS net-hop name as
     * with certificate matching. When var_reqtls_enable != 0,
     * smtp_reqtls_policy must also be != 0.
     */

#ifdef USE_TLS
    if (STATE_REQTLS_IS_REQUESTED(var_reqtls_enable, state))
 state->reqtls_level =
     smtp_reqtls_policy_eval(smtp_reqtls_policy, var_myhostname);
    else
 state->reqtls_level = SMTP_REQTLS_POLICY_ACT_DISABLE;
#endif

    /*
     * Here we ensure that the iter->addr member refers to a copy of the
     * UNIX-domain pathname, so that smtp_save_session() will cache the
     * connection using the pathname as the physical endpoint name.
     * 
     * We set dest=path for backwards compatibility.
     */

#define NO_PORT 0

    SMTP_ITER_INIT(iter, path, var_myhostname, path, NO_PORT, state);

    /*
     * Opportunistic TLS for unix domain sockets does not make much sense,
     * since the channel is private, mere encryption without authentication
     * is just wasted cycles and opportunity for breakage. Since we are not
     * willing to retry after TLS handshake failures here, we downgrade "may"
     * no "none". Nothing is lost, and much waste is avoided.
     * 
     * If a client cert is available, "encrypt" may be a sensible policy.
     * Without client cert, "encrypt" and "may" over UNIX-domain sockets are
     * not useful.
     * 
     * We use smtp_reuse_nexthop() instead of smtp_reuse_addr(), so that we can
     * reuse a SASL-authenticated connection (however unlikely this scenario
     * may be). The smtp_reuse_addr() interface currently supports only reuse
     * of SASL-unauthenticated connections.
     */

#ifdef USE_TLS
    if (!smtp_get_effective_tls_level(why, state)) {
 msg_warn("TLS policy lookup error for %s/%s: %s",
   STR(iter->host), STR(iter->addr), STR(why->reason));
 return;
    }
#endif
    if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0
 || (session = smtp_reuse_nexthop(state,
         SMTP_KEY_MASK_SCACHE_DEST_LABEL)) == 0)
 session = smtp_connect_unix(iter, why, state->misc_flags);
    if ((state->session = session) != 0) {
 session->state = state;
#ifdef USE_TLS
 session->tls_nexthop = var_myhostname; /* for TLS_LEV_SECURE */
 if (state->tls->level == TLS_LEV_MAY) {
     msg_warn("%s: opportunistic TLS encryption is not appropriate "
       "for unix-domain destinations.", myname);
     state->tls->level = TLS_LEV_NONE;
 }
#endif
 /* All delivery errors bounce or defer. */
 state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;

 /*
  * When a TLS handshake fails, the stream is marked "dead" to avoid
  * further I/O over a broken channel.
 */

 if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0
     && smtp_helo(state) != 0) {
     if (!THIS_SESSION_IS_FORBIDDEN
  && vstream_ferror(session->stream) == 0
  && vstream_feof(session->stream) == 0)
  smtp_quit(state);
 } else {
     smtp_xfer(state);
 }

 /*
  * With opportunistic TLS disabled we don't expect to be asked to
  * retry connections without TLS, and so we expect the final server
  * flag to stay on.
 */

 if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0)
     msg_panic("%s: unix-domain destination not final!", myname);
 smtp_cleanup_session(state);
    }

    /*
     * Cleanup.
     */

    if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
 CLEAR_SCACHE_REQUEST_NEXTHOP(state);
}

/* smtp_scrub_address_list - delete all cached addresses from list */

static void smtp_scrub_addr_list(HTABLE *cached_addr, DNS_RR **addr_list)
{
    MAI_HOSTADDR_STR hostaddr;
    DNS_RR *addr;
    DNS_RR *next;

    /*
     * XXX Extend the DNS_RR structure with fields for the printable address
     * and/or binary sockaddr representations, so that we can avoid repeated
     * binary->string transformations for the same address.
     */

    for (addr = *addr_list; addr; addr = next) {
 next = addr->next;
 if (dns_rr_to_pa(addr, &hostaddr) == 0) {
     msg_warn("cannot convert type %s record to printable address",
       dns_strtype(addr->type));
     continue;
 }
 if (htable_locate(cached_addr, hostaddr.buf))
     *addr_list = dns_rr_remove(*addr_list, addr);
    }
}

/* smtp_update_addr_list - common address list update */

static void smtp_update_addr_list(DNS_RR **addr_list, const char *server_addr,
              int session_count)
{
    DNS_RR *addr;
    DNS_RR *next;
    int     aierr;
    struct addrinfo *res0;

    if (*addr_list == 0)
 return;

    /*
     * Truncate the address list if we are not going to use it anyway.
     */

    if (session_count == var_smtp_mxsess_limit
 || session_count == var_smtp_mxaddr_limit) {
 dns_rr_free(*addr_list);
 *addr_list = 0;
 return;
    }

    /*
     * Convert server address to internal form, and look it up in the address
     * list.
     * 
     * XXX smtp_reuse_session() breaks if we remove two or more adjacent list
     * elements but do not truncate the list to zero length.
     * 
     * XXX Extend the SMTP_SESSION structure with sockaddr information so that
     * we can avoid repeated string->binary transformations for the same
     * address.
     * 
     * XXX SRV support: this should match the port, too, otherwise we may
     * eliminate too many list entries.
     */

    if ((aierr = hostaddr_to_sockaddr(server_addr, (char *) 00, &res0)) != 0) {
 msg_warn("hostaddr_to_sockaddr %s: %s",
   server_addr, MAI_STRERROR(aierr));
    } else {
 for (addr = *addr_list; addr; addr = next) {
     next = addr->next;
     if (DNS_RR_EQ_SA(addr, (struct sockaddr *) res0->ai_addr)) {
  *addr_list = dns_rr_remove(*addr_list, addr);
  break;
     }
 }
 freeaddrinfo(res0);
    }
}

/* smtp_reuse_session - try to use existing connection, return session count */

static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
                 int domain_best_pref)
{
    int     session_count = 0;
    DNS_RR *addr;
    DNS_RR *next;
    MAI_HOSTADDR_STR hostaddr;
    SMTP_SESSION *session;
    SMTP_ITERATOR *iter = state->iterator;
    DSN_BUF *why = state->why;

    /*
     * This code is called after server address/port lookup, before
     * iter->host, iter->addr, iter->rr and iter->mx are assigned concrete
     * values, and while iter->port still corresponds to the nexthop service,
     * or the default service configured with smtp_tcp_port or lmtp_tcp_port.
     * 
     * When a connection is reused by nexthop/service or by server address/port,
     * iter->host, iter->addr and iter->port are updated with actual values
     * from the cached session. Additionally, when a connection is searched
     * by nexthop/service, iter->rr remains null, and when a connection is
     * searched by server address/port, iter->rr is updated with an actual
     * server address/port before the search is made.
     * 
     * First, search the cache by delivery request nexthop. We truncate the
     * server address list when all the sessions for this destination are
     * used up, to reduce the number of variables that need to be checked
     * later.
     * 
     * Note: connection reuse by delivery request nexthop restores the "best MX"
     * bit.
     * 
     * smtp_reuse_nexthop() clobbers the iterators's "dest" attribute. We save
     * and restore it here, so that subsequent connections will use the
     * proper nexthop information.
     * 
     * We don't use TLS level info for nexthop-based connection cache storage
     * keys. The combination of (service, nexthop, etc.) should be stable
     * over the time range of interest, and the policy is still enforced on
     * an individual connection to an MX host, before that connection is
     * stored under a nexthop- or host-based storage key.
     */

#ifdef USE_TLS
    smtp_tls_policy_dummy(state->tls);
#endif
    SMTP_ITER_SAVE_DEST(state->iterator);
    if (*addr_list && SMTP_RCPT_LEFT(state) > 0
 && HAVE_SCACHE_REQUEST_NEXTHOP(state)
 && (session = smtp_reuse_nexthop(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL)) != 0) {
 session_count = 1;
 smtp_update_addr_list(addr_list, STR(iter->addr), session_count);
 if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
     && *addr_list == 0)
     state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
 smtp_xfer(state);
 smtp_cleanup_session(state);
    }
    SMTP_ITER_RESTORE_DEST(state->iterator);

    /*
     * Second, search the cache by primary MX address. Again, we use address
     * list truncation so that we have to check fewer variables later.
     * 
     * XXX This loop is safe because smtp_update_addr_list() either truncates
     * the list to zero length, or removes at most one list element.
     * 
     * Currently, we use smtp_reuse_addr() only for SASL-unauthenticated
     * connections. Furthermore, we rely on smtp_reuse_addr() to look up an
     * existing SASL-unauthenticated connection only when a new connection
     * would be guaranteed not to require SASL authentication.
     * 
     * In addition, we rely on smtp_reuse_addr() to look up an existing
     * plaintext connection only when a new connection would be guaranteed
     * not to use TLS.
     * 
     * For more precise control over reuse, the iterator should look up SASL and
     * TLS policy as it evaluates mail exchangers in order, instead of
     * relying on duplicate lookup request code in smtp_reuse(3) and
     * smtp_session(3).
     */

    for (addr = *addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) {
 if (addr->pref != domain_best_pref)
     break;
 next = addr->next;
 if (dns_rr_to_pa(addr, &hostaddr) == 0) {
     msg_warn("cannot convert type %s record to printable address",
       dns_strtype(addr->type));
     /* XXX Assume there is no code at the end of this loop. */
     continue;
 }
 SMTP_ITER_UPDATE_HOST(iter, SMTP_HNAME(addr), hostaddr.buf, addr);
#ifdef USE_TLS
 if (!smtp_get_effective_tls_level(why, state)) {
     msg_warn("TLS policy lookup error for %s/%s: %s",
       STR(iter->dest), STR(iter->host), STR(why->reason));
     continue;
     /* XXX Assume there is no code at the end of this loop. */
 }
#endif
 if ((session = smtp_reuse_addr(state,
       SMTP_KEY_MASK_SCACHE_ENDP_LABEL)) != 0) {
     session->features |= SMTP_FEATURE_BEST_MX;
     session_count += 1;
     smtp_update_addr_list(addr_list, STR(iter->addr), session_count);
     if (*addr_list == 0)
  next = 0;
     if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
  && next == 0)
  state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
     smtp_xfer(state);
     smtp_cleanup_session(state);
 }
    }
    return (session_count);
}

/* smtp_connect_inet - establish network connection */

static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
                 char *def_service)
{
    DELIVER_REQUEST *request = state->request;
    SMTP_ITERATOR *iter = state->iterator;
    ARGV   *sites;
    char   *dest;
    char  **cpp;
    int     non_fallback_sites;
    int     retry_plain = 0;
    DSN_BUF *why = state->why;

    /*
     * For sanity, require that at least one of INET or INET6 is enabled.
     * Otherwise, we can't look up interface information, and we can't
     * convert names or addresses.
     */

    if (inet_proto_info()->ai_family_list[0] == 0) {
 dsb_simple(why, "4.4.4""all network protocols are disabled");
 return;
    }

    /*
     * Do a null destination sanity check in case the primary destination is
     * a list that consists of only separators.
     */

    sites = argv_split(nexthop, CHARS_COMMA_SP);
    if (sites->argc == 0)
 msg_panic("null destination: \"%s\"", nexthop);
    non_fallback_sites = sites->argc;
    argv_split_append(sites, var_fallback_relay, CHARS_COMMA_SP);

    /*
     * Don't give up after a hard host lookup error until we have tried the
     * fallback relay servers.
     * 
     * Don't bounce mail after a host lookup problem with a relayhost or with a
     * fallback relay.
     * 
     * Don't give up after a qualifying soft error until we have tried all
     * qualifying backup mail servers.
     * 
     * All this means that error handling and error reporting depends on whether
     * the error qualifies for trying to deliver to a backup mail server, or
     * whether we're looking up a relayhost or fallback relay. The challenge
     * then is to build this into the pre-existing SMTP client without
     * getting lost in the complexity.
     */

#define IS_FALLBACK_RELAY(cpp, sites, non_fallback_sites) \
     (*(cpp) && (cpp) >= (sites)->argv + (non_fallback_sites))

    for (cpp = sites->argv, (state->misc_flags |= SMTP_MISC_FLAG_FIRST_NEXTHOP);
  SMTP_RCPT_LEFT(state) > 0 && (dest = *cpp) != 0;
  cpp++, (state->misc_flags &= ~SMTP_MISC_FLAG_FIRST_NEXTHOP)) {
 char   *dest_buf;
 char   *domain;
 unsigned port;
 char   *service;
 DNS_RR *addr_list;
 DNS_RR *addr;
 DNS_RR *next;
 int     addr_count;
 int     sess_count;
 SMTP_SESSION *session;
 int     lookup_mx;
 int     non_dns_or_literal;
 int     i_am_mx;
 unsigned domain_best_pref;
 MAI_HOSTADDR_STR hostaddr;

 if (cpp[1] == 0)
     state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP;

 /*
  * Parse the destination. If no TCP port is specified, use the port
  * that is reserved for the protocol (SMTP or LMTP).
  * 
  * The 'service' variable corresponds to the remote service specified
  * with the nexthop, or the default service configured with
  * smtp_tcp_port or lmtp_tcp_port. The 'port' variable and
  * SMTP_ITERATOR.port initially correspond to that service. This
  * determines what loop prevention will be in effect.
  * 
  * The SMTP_ITERATOR.port will be overwritten after SRV record lookup.
  * This guarantees that the connection cache key contains the correct
  * port value when caching and retrieving a connection by its server
  * address (and port).
  * 
  * By design, the connection cache key contains NO port information when
  * caching or retrieving a connection by its nexthop destination.
  * Instead, the cache key contains the master.cf service name (a
  * proxy for all the parameter settings including the default service
  * from smtp_tcp_port or lmtp_tcp_port), together with the nexthop
  * destination and sender-dependent info. This should be sufficient
  * to avoid cross talk between mail streams that should be separated.
 */

 dest_buf = smtp_parse_destination(dest, def_service, &domain,
       &service, &port);
 if (var_helpful_warnings && var_smtp_tls_wrappermode == 0
     && ntohs(port) == 465) {
     msg_info("SMTPS wrappermode (TCP port 465) requires setting "
       "\"%s = yes\", and \"%s = encrypt\" (or stronger)",
       VAR_LMTP_SMTP(TLS_WRAPPER), VAR_LMTP_SMTP(TLS_LEVEL));
 }
#define NO_HOST ""    /* safety */
#define NO_ADDR ""    /* safety */

 SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state);

 /*
  * TODO(wietse) If the domain publishes a TLSRPT policy, they expect
  * that clients use SMTP over TLS. Should we upgrade a TLS security
  * level of "may" to "encrypt"? This would disable falling back to
  * plaintext, and could break interoperability with receivers that
  * crank up security up to 11.
  * 
  * With "TLS-Required: no" in effect, the SMTP client ignores the
  * recipient-side policy mechanism TLSRPT, in addition to the already
  * ignored DANE and MTA-STS mechanisms. This prevents TLSRPT
  * notifications for all SMTP deliveries that do not require TLS.
 */

#ifdef USE_TLSRPT
 if (smtp_mode && var_smtp_tlsrpt_enable
     && STATE_TLS_NOT_REQUIRED(state) == 0
     && tls_level_lookup(var_smtp_tls_level) > TLS_LEV_NONE
     && !valid_hostaddr(domain, DONT_GRIPE))
     smtp_tlsrpt_create_wrapper(state, domain);
 else
     state->tlsrpt = 0;
#endif      /* USE_TLSRPT */

 /*
  * REQUIRETLS policy selection is based on the same TLS net-hop name
  * as with certificate matching. When var_reqtls_enable != 0,
  * smtp_reqtls_policy must also be != 0.
 */

#ifdef USE_TLS
 if (STATE_REQTLS_IS_REQUESTED(var_reqtls_enable, state))
     state->reqtls_level =
  smtp_reqtls_policy_eval(smtp_reqtls_policy, domain);
 else
     state->reqtls_level = SMTP_REQTLS_POLICY_ACT_DISABLE;
#endif

 /*
  * Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a
  * quoted domain is specified or when DNS lookups are disabled.
 */

 if (msg_verbose)
     msg_info("connecting to %s service %s", domain, service);
 non_dns_or_literal = (smtp_dns_support == SMTP_DNS_DISABLED
         || *dest == '[');
 if (smtp_mode) {
     if (ntohs(port) == IPPORT_SMTP)
  state->misc_flags |= SMTP_MISC_FLAG_LOOP_DETECT;
     else
  state->misc_flags &= ~SMTP_MISC_FLAG_LOOP_DETECT;
     lookup_mx = !non_dns_or_literal;
 } else
     lookup_mx = 0;

 /*
  * Look up SRV and address records and fall back to non-SRV lookups
  * if permitted by configuration settings, or look up MX and address
  * records, or look up address records only.
 */

 i_am_mx = 0;
 addr_list = 0;
 if (!non_dns_or_literal && smtp_use_srv_lookup
     && string_list_match(smtp_use_srv_lookup, service)) {
     if (lookup_mx)
  state->misc_flags |= SMTP_MISC_FLAG_FALLBACK_SRV_TO_MX;
     else
  state->misc_flags &= ~SMTP_MISC_FLAG_FALLBACK_SRV_TO_MX;
     addr_list = smtp_service_addr(domain, service, &iter->mx,
       state->misc_flags, why, &i_am_mx);
 } else if (!lookup_mx) {
     /* Non-DNS, literal, or non-SMTP service */
     addr_list = smtp_host_addr(domain, state->misc_flags, why);
     /* XXX We could be an MX host for this destination... */
 } else {
     addr_list = smtp_domain_addr(domain, &iter->mx, state->misc_flags,
      why, &i_am_mx);
 }
 /* If we're MX host, don't connect to non-MX backups. */
 if (i_am_mx)
     state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP;

 /*
  * Don't try fall-back hosts if mail loops to myself. That would just
  * make the problem worse.
 */

 if (addr_list == 0 && SMTP_HAS_LOOP_DSN(why))
     state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP;

 /*
  * No early loop exit or we have a memory leak with dest_buf.
 */

 if (addr_list)
     domain_best_pref = addr_list->pref;

 /*
  * When connection caching is enabled, store the first good
  * connection for this delivery request under the delivery request
  * next-hop name. Good connections will also be stored under their
  * specific server IP address.
  * 
  * XXX smtp_session_cache_destinations specifies domain names without
  * :port, because : is already used for maptype:mapname. Because of
  * this limitation we use the bare domain without the optional [] or
  * non-default TCP port.
  * 
  * Opportunistic (a.k.a. on-demand) session caching on request by the
  * queue manager. This is turned temporarily when a destination has a
  * high volume of mail in the active queue. When the surge reaches
  * its end, the queue manager requests that connections be retrieved
  * but not stored.
 */

 if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_FIRST_NEXTHOP)) {
     smtp_cache_policy(state, domain);
     if (state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE_MASK)
  SET_SCACHE_REQUEST_NEXTHOP(state, dest);
 }

 /*
  * Delete visited cached hosts from the address list.
  * 
  * Optionally search the connection cache by domain name or by primary
  * MX address before we try to create new connections.
  * 
  * Enforce the MX session and MX address counts per next-hop or
  * fall-back destination. smtp_reuse_session() will truncate the
  * address list when either limit is reached.
 */

 if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD)) {
     if (state->cache_used->used > 0)
  smtp_scrub_addr_list(state->cache_used, &addr_list);
     sess_count = addr_count =
  smtp_reuse_session(state, &addr_list, domain_best_pref);
 } else
     sess_count = addr_count = 0;

 /*
  * Connect to an SMTP server: create primary MX connections, and
  * reuse or create backup MX connections.
  * 
  * At the start of an SMTP session, all recipients are unmarked. In the
  * course of an SMTP session, recipients are marked as KEEP (deliver
  * to alternate mail server) or DROP (remove from recipient list). At
  * the end of an SMTP session, weed out the recipient list. Unmark
  * any left-over recipients and try to deliver them to a backup mail
  * server.
  * 
  * Cache the first good session under the next-hop destination name.
  * Cache all good sessions under their physical endpoint.
  * 
  * Don't query the session cache for primary MX hosts. We already did
  * that in smtp_reuse_session(), and if any were found in the cache,
  * they were already deleted from the address list.
  * 
  * Currently, we use smtp_reuse_addr() only for SASL-unauthenticated
  * connections. Furthermore, we rely on smtp_reuse_addr() to look up
  * an existing SASL-unauthenticated connection only when a new
  * connection would be guaranteed not to require SASL authentication.
  * 
  * In addition, we rely on smtp_reuse_addr() to look up an existing
  * plaintext connection only when a new connection would be
  * guaranteed not to use TLS.
 */

 for (addr = addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) {
     next = addr->next;
     if (++addr_count == var_smtp_mxaddr_limit)
  next = 0;
     if (dns_rr_to_pa(addr, &hostaddr) == 0) {
  msg_warn("cannot convert type %s record to printable address",
    dns_strtype(addr->type));
  /* XXX Assume there is no code at the end of this loop. */
  continue;
     }
     SMTP_ITER_UPDATE_HOST(iter, SMTP_HNAME(addr), hostaddr.buf, addr);
#ifdef USE_TLS
     if (!smtp_get_effective_tls_level(why, state)) {
  msg_warn("TLS policy lookup for %s/%s: %s",
    STR(iter->dest), STR(iter->host), STR(why->reason));
  continue;
  /* XXX Assume there is no code at the end of this loop. */
     }
     if (var_smtp_tls_wrappermode
  && state->tls->level < TLS_LEV_ENCRYPT) {
  msg_warn("%s requires \"%s = encrypt\" (or stronger)",
        VAR_LMTP_SMTP(TLS_WRAPPER), VAR_LMTP_SMTP(TLS_LEVEL));
  continue;
  /* XXX Assume there is no code at the end of this loop. */
     }
     /* Skip MX hosts that lack authorization. */
     if (!smtp_tls_authorize_mx_hostname(state->tls, SMTP_HNAME(addr))) {
  continue;
  /* XXX Assume there is no code at the end of this loop. */
     }

     /*
      * Disable TLS when retrying after a handshake failure. This must
      * never happen when TLS is required. See PLAINTEXT_FALLBACK_OK
      * macros.
      * 
      * By dropping the TLS level after smtp_get_effective_tls_level()
      * and smtp_tls_stat_activate_*(), we will properly record the
      * fallback for the TLS level etc. in TLS status logging.
     */

     if (retry_plain) {
  state->tls->level = TLS_LEV_NONE;
  retry_plain = 0;
     }
#endif
     if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0
  || addr->pref == domain_best_pref
  || !(session = smtp_reuse_addr(state,
       SMTP_KEY_MASK_SCACHE_ENDP_LABEL)))
  session = smtp_connect_addr(iter, why, state->misc_flags);
     if ((state->session = session) != 0) {
  session->state = state;
#ifdef USE_TLS
  session->tls_nexthop = domain;

  /*
   * Update TLSRPT state even if this is a reused SMTP
   * connection. If for some unlikely reason we must report a
   * problem, then we must report correct information.
 */

#ifdef USE_TLSRPT
  if (state->tlsrpt) {
      smtp_tlsrpt_set_tls_policy(state);
      smtp_tlsrpt_set_tcp_connection(state);
  }
#endif      /* USE_TLSRPT */
#endif
  if (addr->pref == domain_best_pref)
      session->features |= SMTP_FEATURE_BEST_MX;
  /* Don't count handshake errors towards the session limit. */
  if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
      && next == 0)
      state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
  if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0
      && smtp_helo(state) != 0) {
#ifdef USE_TLS

      /*
       * When an opportunistic TLS handshake fails, try the
       * same address again, with TLS disabled. See also the
       * RETRY_AS_PLAINTEXT macro.
     */

      if ((retry_plain = session->tls_retry_plain) != 0) {
   --addr_count;
   next = addr;
      }
#endif

      /*
       * When a TLS handshake fails, the stream is marked
       * "dead" to avoid further I/O over a broken channel.
     */

      if (!THIS_SESSION_IS_FORBIDDEN
   && vstream_ferror(session->stream) == 0
   && vstream_feof(session->stream) == 0)
   smtp_quit(state);
  } else {
      /* Do count delivery errors towards the session limit. */
      if (++sess_count == var_smtp_mxsess_limit)
   next = 0;
      if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
   && next == 0)
   state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
      smtp_xfer(state);
#ifdef USE_TLS

      /*
       * When opportunistic TLS fails after the STARTTLS
       * handshake, try the same address again, with TLS
       * disabled. See also the RETRY_AS_PLAINTEXT macro.
     */

      if ((retry_plain = session->tls_retry_plain) != 0) {
   --sess_count;
   --addr_count;
   next = addr;
      }
#endif
  }
  smtp_cleanup_session(state);
     } else {
  /* The reason already includes the IP address and TCP port. */
  msg_info("%s", STR(why->reason));
     }
     /* XXX Code above assumes there is no code at this loop ending. */
 }
 dns_rr_free(addr_list);
 if (iter->mx) {
     dns_rr_free(iter->mx);
     iter->mx = 0;   /* Just in case */
 }
 myfree(dest_buf);
 if (state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
     break;
    }

    /*
     * We still need to deliver, bounce or defer some left-over recipients:
     * either mail loops or some backup mail server was unavailable.
     */

    if (SMTP_RCPT_LEFT(state) > 0) {

 /*
  * In case of a "no error" indication we make up an excuse: we did
  * find the host address, but we did not attempt to connect to it.
  * This can happen when the fall-back relay was already tried via a
  * cached connection, so that the address list scrubber left behind
  * an empty list.
 */

 if (!SMTP_HAS_DSN(why)) {
     dsb_simple(why, "4.3.0",
         "server unavailable or unable to receive mail");
 }

 /*
  * Pay attention to what could be configuration problems, and pretend
  * that these are recoverable rather than bouncing the mail.
 */

 else if (!SMTP_HAS_SOFT_DSN(why)) {

     /*
      * The fall-back destination did not resolve as expected, or it
      * is refusing to talk to us, or mail for it loops back to us.
     */

     if (IS_FALLBACK_RELAY(cpp, sites, non_fallback_sites)) {
  msg_warn("%s configuration problem", VAR_SMTP_FALLBACK);
  vstring_strcpy(why->status, "4.3.5");
  /* XXX Keep the diagnostic code and MTA. */
     }

     /*
      * The next-hop relayhost did not resolve as expected, or it is
      * refusing to talk to us, or mail for it loops back to us.
      * 
      * XXX There is no equivalent safety net for mis-configured
      * sender-dependent relay hosts. The trivial-rewrite resolver
      * would have to flag the result, and the queue manager would
      * have to provide that information to delivery agents.
     */

     else if (smtp_mode && strcmp(sites->argv[0], var_relayhost) == 0) {
  msg_warn("%s configuration problem", VAR_RELAYHOST);
  vstring_strcpy(why->status, "4.3.5");
  /* XXX Keep the diagnostic code and MTA. */
     }

     /*
      * Mail for the next-hop destination loops back to myself. Pass
      * the mail to the best_mx_transport or bounce it.
     */

     else if (smtp_mode && SMTP_HAS_LOOP_DSN(why) && *var_bestmx_transp) {
  dsb_reset(why);   /* XXX */
  state->status = deliver_pass_all(MAIL_CLASS_PRIVATE,
       var_bestmx_transp,
       request);
  SMTP_RCPT_LEFT(state) = 0/* XXX */
     }
 }
    }

    /*
     * Cleanup.
     */

    if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
 CLEAR_SCACHE_REQUEST_NEXTHOP(state);
    argv_free(sites);
}

/* smtp_connect - establish SMTP connection */

int     smtp_connect(SMTP_STATE *state)
{
    DELIVER_REQUEST *request = state->request;
    char   *destination = request->nexthop;

    /*
     * All deliveries proceed along the same lines, whether they are over TCP
     * or UNIX-domain sockets, and whether they use SMTP or LMTP: get a
     * connection from the cache or create a new connection; deliver mail;
     * update the connection cache or disconnect.
     * 
     * The major differences appear at a higher level: the expansion from
     * destination to address list, and whether to stop before we reach the
     * end of that list.
     */


    /*
     * With LMTP we have direct-to-host delivery only. The destination may
     * have multiple IP addresses.
     */

    if (!smtp_mode) {
 if (strncmp(destination, "unix:"5) == 0) {
     smtp_connect_local(state, destination + 5);
 } else {
     if (strncmp(destination, "inet:"5) == 0)
  destination += 5;
     smtp_connect_inet(state, destination, var_smtp_tcp_port);
 }
    }

    /*
     * XXX We don't add support for "unix:" or "inet:" prefixes in SMTP
     * destinations, because that would break compatibility with existing
     * Postfix configurations that have a host with such a name.
     */

    else {
 smtp_connect_inet(state, destination, var_smtp_tcp_port);
    }

    /*
     * We still need to bounce or defer some left-over recipients: either
     * (SMTP) mail loops or some server was unavailable.
     * 
     * We could avoid this (and the "final server" complexity) by keeping one
     * DSN structure per recipient in memory, by updating those in-memory
     * structures with each delivery attempt, and by always flushing all
     * deferred recipients at the end. We'd probably still want to bounce
     * recipients immediately, so we'd end up with another chunk of code for
     * defer logging only.
     */

    if (SMTP_RCPT_LEFT(state) > 0) {
 state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER; /* XXX */
 smtp_sess_fail(state);

 /*
  * Sanity check. Don't silently lose recipients.
 */

 smtp_rcpt_cleanup(state);
 if (SMTP_RCPT_LEFT(state) > 0)
     msg_panic("smtp_connect: left-over recipients");
    }
    return (state->status);
}

Messung V0.5 in Prozent
C=76 H=94 G=85

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.29Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-08) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002