-/* $Cambridge: exim/src/src/host.c,v 1.28 2007/01/08 10:50:18 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2012 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or
very good for the uses to which it is put. When running the regression tests,
start with a fixed seed.
+If you need better, see vaguely_random_number() which is potentially stronger,
+if a crypto library is available, but might end up just calling this instead.
+
Arguments:
limit: one more than the largest number required
int
random_number(int limit)
{
+if (limit < 1)
+ return 0;
if (random_seed == 0)
{
if (running_in_test_harness) random_seed = 42; else
{
int sep = 0;
uschar buffer[32];
-uschar *list = tls_on_connect_ports;
+uschar *list = tls_in.on_connect_ports;
uschar *s;
-if (tls_on_connect) return TRUE;
+if (tls_in.on_connect) return TRUE;
while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
{
treat this as non-existent. In some operating systems, this is returned as an
empty string; in others as a single dot. */
-if (hosts->h_name[0] == 0 || hosts->h_name[0] == '.')
+if (hosts->h_name == NULL || hosts->h_name[0] == 0 || hosts->h_name[0] == '.')
{
HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an empty name: "
"treated as non-existent host name\n");
dns_answer dnsa;
dns_scan dnss;
-host_lookup_deferred = host_lookup_failed = FALSE;
+sender_host_dnssec = host_lookup_deferred = host_lookup_failed = FALSE;
HDEBUG(D_host_lookup)
debug_printf("looking up host name for %s\n", sender_host_address);
{
if (strcmpic(ordername, US"bydns") == 0)
{
- dns_init(FALSE, FALSE);
+ dns_init(FALSE, FALSE, FALSE); /*XXX dnssec? */
dns_build_reverse(sender_host_address, buffer);
rc = dns_lookup(&dnsa, buffer, T_PTR, NULL);
int count = 0;
int old_pool = store_pool;
+ /* Ideally we'd check DNSSEC both forward and reverse, but we use the
+ gethost* routines for forward, so can't do that unless/until we rewrite. */
+ sender_host_dnssec = dns_is_secure(&dnsa);
+ DEBUG(D_dns)
+ debug_printf("Reverse DNS security status: %s\n",
+ sender_host_dnssec ? "DNSSEC verified (AD)" : "unverified");
+
store_pool = POOL_PERM; /* Save names in permanent storage */
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
some circumstances when the get..byname() function actually calls the DNS. */
dns_init((flags & HOST_FIND_QUALIFY_SINGLE) != 0,
- (flags & HOST_FIND_SEARCH_PARENTS) != 0);
+ (flags & HOST_FIND_SEARCH_PARENTS) != 0,
+ FALSE); /*XXX dnssec? */
/* In an IPv6 world, unless IPv6 has been disabled, we need to scan for both
kinds of address, so go round the loop twice. Note that we have ensured that
fully_qualified_name if not NULL, return fully qualified name here if
the contents are different (i.e. it must be preset
to something)
+ dnnssec_require if TRUE check the DNS result AD bit
Returns: HOST_FIND_FAILED couldn't find A record
HOST_FIND_AGAIN try again later
static int
set_address_from_dns(host_item *host, host_item **lastptr,
- uschar *ignore_target_hosts, BOOL allow_ip, uschar **fully_qualified_name)
+ uschar *ignore_target_hosts, BOOL allow_ip, uschar **fully_qualified_name,
+ BOOL dnssec_require)
{
dns_record *rr;
host_item *thishostlast = NULL; /* Indicates not yet filled in anything */
if (rc != DNS_NOMATCH && rc != DNS_NODATA) v6_find_again = TRUE;
continue;
}
+ if (dnssec_require && !dns_is_secure(&dnsa))
+ {
+ log_write(L_host_lookup_failed, LOG_MAIN, "dnssec fail on %s for %.256s",
+ i>1 ? "A6" : i>0 ? "AAAA" : "A", host->name);
+ continue;
+ }
/* Lookup succeeded: fill in the given host item with the first non-ignored
address found; create additional items for any others. A single A6 record
srv_service when SRV used, the service name
srv_fail_domains DNS errors for these domains => assume nonexist
mx_fail_domains DNS errors for these domains => assume nonexist
+ dnssec_request_domains => make dnssec request
+ dnssec_require_domains => ditto and nonexist failures
fully_qualified_name if not NULL, return fully-qualified name
removed set TRUE if local host was removed from the list
int
host_find_bydns(host_item *host, uschar *ignore_target_hosts, int whichrrs,
uschar *srv_service, uschar *srv_fail_domains, uschar *mx_fail_domains,
+ uschar *dnssec_request_domains, uschar *dnssec_require_domains,
uschar **fully_qualified_name, BOOL *removed)
{
host_item *h, *last;
int yield;
dns_answer dnsa;
dns_scan dnss;
+BOOL dnssec_request = match_isinlist(host->name, &dnssec_request_domains,
+ 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
+BOOL dnssec_require = match_isinlist(host->name, &dnssec_require_domains,
+ 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
/* Set the default fully qualified name to the incoming name, initialize the
resolver if necessary, set up the relevant options, and initialize the flag
if (fully_qualified_name != NULL) *fully_qualified_name = host->name;
dns_init((whichrrs & HOST_FIND_QUALIFY_SINGLE) != 0,
- (whichrrs & HOST_FIND_SEARCH_PARENTS) != 0);
+ (whichrrs & HOST_FIND_SEARCH_PARENTS) != 0,
+ dnssec_request || dnssec_require
+ );
host_find_failed_syntax = FALSE;
/* First, if requested, look for SRV records. The service name is given; we
/* On DNS failures, we give the "try again" error unless the domain is
listed as one for which we continue. */
+ if (rc == DNS_SUCCEED && dnssec_require && !dns_is_secure(&dnsa))
+ {
+ log_write(L_host_lookup_failed, LOG_MAIN,
+ "dnssec fail on SRV for %.256s", host->name);
+ rc = DNS_FAIL;
+ }
if (rc == DNS_FAIL || rc == DNS_AGAIN)
{
#ifndef STAND_ALONE
if (match_isinlist(host->name, &srv_fail_domains, 0, NULL, NULL, MCL_DOMAIN,
TRUE, NULL) != OK)
#endif
- return HOST_FIND_AGAIN;
+ { yield = HOST_FIND_AGAIN; goto out; }
DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA "
"(domain in srv_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN");
}
{
ind_type = T_MX;
rc = dns_lookup(&dnsa, host->name, ind_type, fully_qualified_name);
- if (rc == DNS_NOMATCH) return HOST_FIND_FAILED;
- if (rc == DNS_FAIL || rc == DNS_AGAIN)
- {
- #ifndef STAND_ALONE
- if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN,
- TRUE, NULL) != OK)
- #endif
- return HOST_FIND_AGAIN;
- DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA "
- "(domain in mx_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN");
+ switch (rc)
+ {
+ case DNS_NOMATCH:
+ yield = HOST_FIND_FAILED; goto out;
+
+ case DNS_SUCCEED:
+ if (!dnssec_require || dns_is_secure(&dnsa))
+ break;
+ log_write(L_host_lookup_failed, LOG_MAIN,
+ "dnssec fail on MX for %.256s", host->name);
+ rc = DNS_FAIL;
+ /*FALLTRHOUGH*/
+
+ case DNS_FAIL:
+ case DNS_AGAIN:
+ #ifndef STAND_ALONE
+ if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN,
+ TRUE, NULL) != OK)
+ #endif
+ { yield = HOST_FIND_AGAIN; goto out; }
+ DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA "
+ "(domain in mx_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN");
+ break;
}
}
if ((whichrrs & HOST_FIND_BY_A) == 0)
{
DEBUG(D_host_lookup) debug_printf("Address records are not being sought\n");
- return HOST_FIND_FAILED;
+ yield = HOST_FIND_FAILED;
+ goto out;
}
last = host; /* End of local chainlet */
host->mx = MX_NONE;
host->port = PORT_NONE;
rc = set_address_from_dns(host, &last, ignore_target_hosts, FALSE,
- fully_qualified_name);
+ fully_qualified_name, dnssec_require);
/* If one or more address records have been found, check that none of them
are local. Since we know the host items all have their IP addresses
}
}
- return rc;
+ yield = rc;
+ goto out;
}
/* We have found one or more MX or SRV records. Sort them according to
if (host == last && host->name[0] == 0)
{
DEBUG(D_host_lookup) debug_printf("the single SRV record is \".\"\n");
- return HOST_FIND_FAILED;
+ yield = HOST_FIND_FAILED;
+ goto out;
}
DEBUG(D_host_lookup)
if they happen to match something local. */
yield = HOST_FIND_FAILED; /* Default yield */
-dns_init(FALSE, FALSE); /* Disable qualify_single and search_parents */
+dns_init(FALSE, FALSE, /* Disable qualify_single and search_parents */
+ dnssec_request || dnssec_require);
for (h = host; h != last->next; h = h->next)
{
if (h->address != NULL) continue; /* Inserted by a multihomed host */
- rc = set_address_from_dns(h, &last, ignore_target_hosts, allow_mx_to_ip, NULL);
+ rc = set_address_from_dns(h, &last, ignore_target_hosts, allow_mx_to_ip,
+ NULL, dnssec_require);
if (rc != HOST_FOUND)
{
h->status = hstatus_unusable;
}
}
+out:
+
+dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */
return yield;
}
BOOL byname = FALSE;
BOOL qualify_single = TRUE;
BOOL search_parents = FALSE;
+BOOL request_dnssec = FALSE;
+BOOL require_dnssec = FALSE;
uschar **argv = USS cargv;
uschar buffer[256];
/* So that debug level changes can be done first */
-dns_init(qualify_single, search_parents);
+dns_init(qualify_single, search_parents, FALSE);
printf("Testing host lookup\n");
printf("> ");
whichrrs = HOST_FIND_BY_SRV | HOST_FIND_BY_MX;
else if (Ustrcmp(buffer, "srv+mx+a") == 0)
whichrrs = HOST_FIND_BY_SRV | HOST_FIND_BY_MX | HOST_FIND_BY_A;
- else if (Ustrcmp(buffer, "qualify_single") == 0) qualify_single = TRUE;
+ else if (Ustrcmp(buffer, "qualify_single") == 0) qualify_single = TRUE;
else if (Ustrcmp(buffer, "no_qualify_single") == 0) qualify_single = FALSE;
- else if (Ustrcmp(buffer, "search_parents") == 0) search_parents = TRUE;
+ else if (Ustrcmp(buffer, "search_parents") == 0) search_parents = TRUE;
else if (Ustrcmp(buffer, "no_search_parents") == 0) search_parents = FALSE;
+ else if (Ustrcmp(buffer, "request_dnssec") == 0) request_dnssec = TRUE;
+ else if (Ustrcmp(buffer, "no_request_dnssec") == 0) request_dnssec = FALSE;
+ else if (Ustrcmp(buffer, "require_dnssec") == 0) require_dnssec = TRUE;
+ else if (Ustrcmp(buffer, "no_reqiret_dnssec") == 0) require_dnssec = FALSE;
else if (Ustrcmp(buffer, "test_harness") == 0)
running_in_test_harness = !running_in_test_harness;
else if (Ustrcmp(buffer, "ipv6") == 0) disable_ipv6 = !disable_ipv6;
if (qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
if (search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
- rc = byname?
- host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE)
- :
- host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL,
- &fully_qualified_name, NULL);
+ rc = byname
+ ? host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE)
+ : host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL,
+ request_dnssec ? &h.name : NULL,
+ require_dnssec ? &h.name : NULL,
+ &fully_qualified_name, NULL);
if (rc == HOST_FIND_FAILED) printf("Failed\n");
else if (rc == HOST_FIND_AGAIN) printf("Again\n");
}
#endif /* STAND_ALONE */
+/* vi: aw ai sw=2
+*/
/* End of host.c */