X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/99c1bb4ed9d99c7b0f615750c37884d7a7f9aa0d..a85c067ba6c6940512cf57ec213277a370d87e70:/src/src/routers/rf_lookup_hostlist.c diff --git a/src/src/routers/rf_lookup_hostlist.c b/src/src/routers/rf_lookup_hostlist.c index 7ff7f45e1..affd70b6e 100644 --- a/src/src/routers/rf_lookup_hostlist.c +++ b/src/src/routers/rf_lookup_hostlist.c @@ -2,8 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" @@ -35,7 +37,8 @@ Arguments: rblock the router block addr the address being routed ignore_target_hosts list of hosts to ignore - lookup_type lk_default or lk_byname or lk_bydns + lookup_type LK_DEFAULT or LK_BYNAME or LK_BYDNS, + plus LK_IPV4_{ONLY,PREFER} hff_code what to do for host find failed addr_new passed to rf_self_action for self=reroute @@ -52,7 +55,6 @@ rf_lookup_hostlist(router_instance *rblock, address_item *addr, address_item **addr_new) { BOOL self_send = FALSE; -host_item *h, *next_h, *prev; /* Look up each host address. A lookup may add additional items into the chain if there are multiple addresses. Hence the use of next_h to start each cycle of @@ -61,14 +63,13 @@ host, omit it and any subsequent hosts - i.e. treat the list like an ordered list of MX hosts. If the first host is the local host, act according to the "self" option in the configuration. */ -prev = NULL; -for (h = addr->host_list; h != NULL; h = next_h) +for (host_item * prev = NULL, * h = addr->host_list, *next_h; h; h = next_h) { const uschar *canonical_name; - int rc, len, port; + int rc, len, port, mx, sort_key; next_h = h->next; - if (h->address != NULL) { prev = h; continue; } + if (h->address) { prev = h; continue; } DEBUG(D_route|D_host_lookup) debug_printf("finding IP address for %s\n", h->name); @@ -78,32 +79,45 @@ for (h = addr->host_list; h != NULL; h = next_h) port = host_item_get_port(h); + /* Store the previous mx and sort_key values, which were assigned in + host_build_hostlist and will be overwritten by host_find_bydns. */ + + mx = h->mx; + sort_key = h->sort_key; + /* If the name ends with "/MX", we interpret it to mean "the list of hosts - pointed to by MX records with this name". */ + pointed to by MX records with this name", and the MX record values override + the ordering from host_build_hostlist. */ len = Ustrlen(h->name); if (len > 3 && strcmpic(h->name + len - 3, US"/mx") == 0) { + int whichrrs = lookup_type & LK_IPV4_ONLY + ? HOST_FIND_BY_MX | HOST_FIND_IPV4_ONLY + : lookup_type & LK_IPV4_PREFER + ? HOST_FIND_BY_MX | HOST_FIND_IPV4_FIRST + : HOST_FIND_BY_MX; + DEBUG(D_route|D_host_lookup) debug_printf("doing DNS MX lookup for %s\n", h->name); + mx = MX_NONE; h->name = string_copyn(h->name, len - 3); rc = host_find_bydns(h, ignore_target_hosts, - HOST_FIND_BY_MX, /* look only for MX records */ - NULL, /* SRV service not relevant */ - NULL, /* failing srv domains not relevant */ - NULL, /* no special mx failing domains */ - rblock->dnssec_request_domains, /* no dnssec request XXX ? */ - rblock->dnssec_require_domains, /* no dnssec require XXX ? */ - NULL, /* fully_qualified_name */ - NULL); /* indicate local host removed */ + whichrrs, /* look only for MX records */ + NULL, /* SRV service not relevant */ + NULL, /* failing srv domains not relevant */ + NULL, /* no special mx failing domains */ + &rblock->dnssec, /* dnssec request/require */ + NULL, /* fully_qualified_name */ + NULL); /* indicate local host removed */ } /* If explicitly configured to look up by name, or if the "host name" is actually an IP address, do a byname lookup. */ - else if (lookup_type == lk_byname || string_is_ip_address(h->name, NULL) != 0) + else if (lookup_type & LK_BYNAME || string_is_ip_address(h->name, NULL) != 0) { DEBUG(D_route|D_host_lookup) debug_printf("calling host_find_byname\n"); rc = host_find_byname(h, ignore_target_hosts, HOST_FIND_QUALIFY_SINGLE, @@ -117,30 +131,43 @@ for (h = addr->host_list; h != NULL; h = next_h) else { BOOL removed; + int whichrrs = lookup_type & LK_IPV4_ONLY + ? HOST_FIND_BY_A + : lookup_type & LK_IPV4_PREFER + ? HOST_FIND_BY_A | HOST_FIND_BY_AAAA | HOST_FIND_IPV4_FIRST + : HOST_FIND_BY_A | HOST_FIND_BY_AAAA; + DEBUG(D_route|D_host_lookup) debug_printf("doing DNS lookup\n"); - rc = host_find_bydns(h, ignore_target_hosts, HOST_FIND_BY_A, NULL, NULL, - NULL, - rblock->dnssec_request_domains, /* no dnssec request XXX ? */ - rblock->dnssec_require_domains, /* no dnssec require XXX ? */ - &canonical_name, &removed); - if (rc == HOST_FOUND) + switch (rc = host_find_bydns(h, ignore_target_hosts, whichrrs, NULL, + NULL, NULL, + &rblock->dnssec, /* domains for request/require */ + &canonical_name, &removed)) { - if (removed) setflag(addr, af_local_host_removed); - } - else if (rc == HOST_FIND_FAILED) - { - if (lookup_type == lk_default) - { - DEBUG(D_route|D_host_lookup) - debug_printf("DNS lookup failed: trying getipnodebyname\n"); - rc = host_find_byname(h, ignore_target_hosts, HOST_FIND_QUALIFY_SINGLE, - &canonical_name, TRUE); - } + case HOST_FOUND: + if (removed) setflag(addr, af_local_host_removed); + break; + case HOST_FIND_FAILED: + if (lookup_type & LK_DEFAULT) + { + DEBUG(D_route|D_host_lookup) + debug_printf("DNS lookup failed: trying %s\n", + f.running_in_test_harness + ? "host_fake_gethostbyname" : "getipnodebyname"); + rc = host_find_byname(h, ignore_target_hosts, HOST_FIND_QUALIFY_SINGLE, + &canonical_name, TRUE); + } + break; } } /* Temporary failure defers, unless pass_on_timeout is set */ + if (rc == HOST_FIND_SECURITY) + { + addr->message = string_sprintf("host lookup for %s done insecurely" , h->name); + addr->basic_errno = ERRNO_DNSDEFER; + return DEFER; + } if (rc == HOST_FIND_AGAIN) { if (rblock->pass_on_timeout) @@ -173,7 +200,7 @@ for (h = addr->host_list; h != NULL; h = next_h) addr->message = string_sprintf("lookup of host \"%s\" failed in %s router%s", h->name, rblock->name, - host_find_failed_syntax? ": syntax error in name" : ""); + f.host_find_failed_syntax? ": syntax error in name" : ""); if (hff_code == hff_defer) return DEFER; if (hff_code == hff_fail) return FAIL; @@ -182,13 +209,19 @@ for (h = addr->host_list; h != NULL; h = next_h) return DEFER; } - /* Deal with a port setting */ + /* Deal with the settings that were previously cleared: + port, mx and sort_key. */ if (port != PORT_NONE) - { - host_item *hh; - for (hh = h; hh != next_h; hh = hh->next) hh->port = port; - } + for (host_item * hh = h; hh != next_h; hh = hh->next) + hh->port = port; + + if (mx != MX_NONE) + for (host_item * hh = h; hh != next_h; hh = hh->next) + { + hh->mx = mx; + hh->sort_key = sort_key; + } /* A local host gets chopped, with its successors, if there are previous hosts. Otherwise the self option is used. If it is set to "send", any @@ -196,12 +229,12 @@ for (h = addr->host_list; h != NULL; h = next_h) if (rc == HOST_FOUND_LOCAL && !self_send) { - if (prev != NULL) + if (prev) { DEBUG(D_route) { debug_printf("Removed from host list:\n"); - for (; h != NULL; h = h->next) debug_printf(" %s\n", h->name); + for (; h; h = h->next) debug_printf(" %s\n", h->name); } prev->next = NULL; setflag(addr, af_local_host_removed);