Beispiel #1
0
DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
{
    DNS_RR *addr_list;

    dsb_reset(why);				/* Paranoia */

    /*
     * If the host is specified by numerical address, just convert the
     * address to internal form. Otherwise, the host is specified by name.
     */
#define PREF0	0
    addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why);
    if (addr_list
            && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
            && smtp_find_self(addr_list) != 0) {
        dns_rr_free(addr_list);
        dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host);
        return (0);
    }
    if (addr_list && addr_list->next) {
        if (var_smtp_rand_addr)
            addr_list = dns_rr_shuffle(addr_list);
        /* The following changes the order of equal-preference hosts. */
        if (inet_proto_info()->ai_family_list[1] != 0)
            addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags));
    }
    if (msg_verbose)
        smtp_print_addr(host, addr_list);
    return (addr_list);
}
Beispiel #2
0
DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
{
    DNS_RR *addr_list;
    int     res_opt = 0;
    const char *ahost;

    dsb_reset(why);				/* Paranoia */

    if (smtp_dns_support == SMTP_DNS_DNSSEC)
	res_opt |= RES_USE_DNSSEC;

    /*
     * IDNA support.
     */
#ifndef NO_EAI
    if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) {
	if (msg_verbose)
	    msg_info("%s asciified to %s", host, ahost);
    } else
#endif
	ahost = host;

    /*
     * If the host is specified by numerical address, just convert the
     * address to internal form. Otherwise, the host is specified by name.
     */
#define PREF0	0
    addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, why);
    if (addr_list
	&& (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
	&& smtp_find_self(addr_list) != 0) {
	dns_rr_free(addr_list);
	dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host);
	return (0);
    }
    if (addr_list && addr_list->next) {
	if (var_smtp_rand_addr)
	    addr_list = dns_rr_shuffle(addr_list);
	/* The following changes the order of equal-preference hosts. */
	if (inet_proto_info()->ai_family_list[1] != 0)
	    addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags));
    }
    if (msg_verbose)
	smtp_print_addr(host, addr_list);
    return (addr_list);
}
Beispiel #3
0
static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref)
{
    DNS_RR *addr;
    DNS_RR *last;

    for (last = 0, addr = addr_list; addr; last = addr, addr = addr->next) {
        if (pref == addr->pref) {
            if (msg_verbose)
                smtp_print_addr("truncated", addr);
            dns_rr_free(addr);
            if (last == 0) {
                addr_list = 0;
            } else {
                last->next = 0;
            }
            break;
        }
    }
    return (addr_list);
}
Beispiel #4
0
DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why,
                         int *found_myself)
{
    DNS_RR *mx_names;
    DNS_RR *addr_list = 0;
    DNS_RR *self = 0;
    unsigned best_pref;
    unsigned best_found;

    dsb_reset(why);				/* Paranoia */

    /*
     * Preferences from DNS use 0..32767, fall-backs use 32768+.
     */
#define IMPOSSIBLE_PREFERENCE	(~0)

    /*
     * Sanity check.
     */
    if (var_disable_dns)
        msg_panic("smtp_domain_addr: DNS lookup is disabled");

    /*
     * Look up the mail exchanger hosts listed for this name. Sort the
     * results by preference. Look up the corresponding host addresses, and
     * truncate the list so that it contains only hosts that are more
     * preferred than myself. When no MX resource records exist, look up the
     * addresses listed for this name.
     *
     * According to RFC 974: "It is possible that the list of MXs in the
     * response to the query will be empty.  This is a special case.  If the
     * list is empty, mailers should treat it as if it contained one RR, an
     * MX RR with a preference value of 0, and a host name of REMOTE.  (I.e.,
     * REMOTE is its only MX).  In addition, the mailer should do no further
     * processing on the list, but should attempt to deliver the message to
     * REMOTE."
     *
     * Normally it is OK if an MX host cannot be found in the DNS; we'll just
     * use a backup one, and silently ignore the better MX host. However, if
     * the best backup that we can find in the DNS is the local machine, then
     * we must remember that the local machine is not the primary MX host, or
     * else we will claim that mail loops back.
     *
     * XXX Optionally do A lookups even when the MX lookup didn't complete.
     * Unfortunately with some DNS servers this is not a transient problem.
     *
     * XXX Ideally we would perform A lookups only as far as needed. But as long
     * as we're looking up all the hosts, it would be better to look up the
     * least preferred host first, so that DNS lookup error messages make
     * more sense.
     *
     * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX
     * hosts, whereas multiple A records per hostname must be used in the
     * order as received. They make the bogus assumption that a hostname with
     * multiple A records corresponds to one machine with multiple network
     * interfaces.
     *
     * XXX 2821: Postfix recognizes the local machine by looking for its own IP
     * address in the list of mail exchangers. RFC 2821 says one has to look
     * at the mail exchanger hostname as well, making the bogus assumption
     * that an IP address is listed only under one hostname. However, looking
     * at hostnames provides a partial solution for MX hosts behind a NAT
     * gateway.
     */
    switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why->reason)) {
    default:
        dsb_status(why, "4.4.3");
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, misc_flags, why);
        break;
    case DNS_INVAL:
        dsb_status(why, "5.4.4");
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, misc_flags, why);
        break;
    case DNS_FAIL:
        dsb_status(why, "5.4.3");
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, misc_flags, why);
        break;
    case DNS_OK:
        mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any);
        best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE);
        addr_list = smtp_addr_list(mx_names, why);
        dns_rr_free(mx_names);
        if (addr_list == 0) {
            /* Text does not change. */
            if (var_smtp_defer_mxaddr) {
                /* Don't clobber the null terminator. */
                if (SMTP_HAS_HARD_DSN(why))
                    SMTP_SET_SOFT_DSN(why);	/* XXX */
                /* Require some error status. */
                else if (!SMTP_HAS_SOFT_DSN(why))
                    msg_panic("smtp_domain_addr: bad status");
            }
            msg_warn("no MX host for %s has a valid address record", name);
            break;
        }
        best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE);
        if (msg_verbose)
            smtp_print_addr(name, addr_list);
        if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
                && (self = smtp_find_self(addr_list)) != 0) {
            addr_list = smtp_truncate_self(addr_list, self->pref);
            if (addr_list == 0) {
                if (best_pref != best_found) {
                    dsb_simple(why, "4.4.4",
                               "unable to find primary relay for %s", name);
                } else {
                    dsb_simple(why, "5.4.6", "mail for %s loops back to myself",
                               name);
                }
            }
        }
#define SMTP_COMPARE_ADDR(flags) \
	(((flags) & SMTP_MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \
	 ((flags) & SMTP_MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \
	 dns_rr_compare_pref_any)

        if (addr_list && addr_list->next && var_smtp_rand_addr) {
            addr_list = dns_rr_shuffle(addr_list);
            addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags));
        }
        break;
    case DNS_NOTFOUND:
        addr_list = smtp_host_addr(name, misc_flags, why);
        break;
    }

    /*
     * Clean up.
     */
    *found_myself |= (self != 0);
    return (addr_list);
}