X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/482d1455ebb7a291d9ee39a337e5837d8bc3feba..734076a52c94c290826224e524f49be07d4476e1:/src/src/dns.c diff --git a/src/src/dns.c b/src/src/dns.c index d05669c30..542354db3 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/dns.c,v 1.19 2009/10/19 14:20:58 tom Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2007 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -161,25 +159,85 @@ the first time we have been here, and set the resolver options. Arguments: qualify_single TRUE to set the RES_DEFNAMES option search_parents TRUE to set the RES_DNSRCH option + use_dnssec TRUE to set the RES_USE_DNSSEC option Returns: nothing */ void -dns_init(BOOL qualify_single, BOOL search_parents) +dns_init(BOOL qualify_single, BOOL search_parents, BOOL use_dnssec) { -if ((_res.options & RES_INIT) == 0) +res_state resp = os_get_dns_resolver_res(); + +if ((resp->options & RES_INIT) == 0) { - DEBUG(D_resolver) _res.options |= RES_DEBUG; /* For Cygwin */ + DEBUG(D_resolver) resp->options |= RES_DEBUG; /* For Cygwin */ + os_put_dns_resolver_res(resp); res_init(); - DEBUG(D_resolver) _res.options |= RES_DEBUG; + DEBUG(D_resolver) resp->options |= RES_DEBUG; + os_put_dns_resolver_res(resp); } -_res.options &= ~(RES_DNSRCH | RES_DEFNAMES); -_res.options |= (qualify_single? RES_DEFNAMES : 0) | +resp->options &= ~(RES_DNSRCH | RES_DEFNAMES); +resp->options |= (qualify_single? RES_DEFNAMES : 0) | (search_parents? RES_DNSRCH : 0); -if (dns_retrans > 0) _res.retrans = dns_retrans; -if (dns_retry > 0) _res.retry = dns_retry; +if (dns_retrans > 0) resp->retrans = dns_retrans; +if (dns_retry > 0) resp->retry = dns_retry; + +#ifdef RES_USE_EDNS0 +if (dns_use_edns0 >= 0) + { + if (dns_use_edns0) + resp->options |= RES_USE_EDNS0; + else + resp->options &= ~RES_USE_EDNS0; + DEBUG(D_resolver) + debug_printf("Coerced resolver EDNS0 support %s.\n", + dns_use_edns0 ? "on" : "off"); + } +#else +if (dns_use_edns0 >= 0) + DEBUG(D_resolver) + debug_printf("Unable to %sset EDNS0 without resolver support.\n", + dns_use_edns0 ? "" : "un"); +#endif + +#ifndef DISABLE_DNSSEC +# ifdef RES_USE_DNSSEC +# ifndef RES_USE_EDNS0 +# error Have RES_USE_DNSSEC but not RES_USE_EDNS0? Something hinky ... +# endif +if (use_dnssec) + resp->options |= RES_USE_DNSSEC; +if (dns_dnssec_ok >= 0) + { + if (dns_use_edns0 == 0 && dns_dnssec_ok != 0) + { + DEBUG(D_resolver) + debug_printf("CONFLICT: dns_use_edns0 forced false, dns_dnssec_ok forced true, ignoring latter!\n"); + } + else + { + if (dns_dnssec_ok) + resp->options |= RES_USE_DNSSEC; + else + resp->options &= ~RES_USE_DNSSEC; + DEBUG(D_resolver) debug_printf("Coerced resolver DNSSEC support %s.\n", + dns_dnssec_ok ? "on" : "off"); + } + } +# else +if (dns_dnssec_ok >= 0) + DEBUG(D_resolver) + debug_printf("Unable to %sset DNSSEC without resolver support.\n", + dns_dnssec_ok ? "" : "un"); +if (use_dnssec) + DEBUG(D_resolver) + debug_printf("Unable to set DNSSEC without resolver support.\n"); +# endif +#endif /* DISABLE_DNSSEC */ + +os_put_dns_resolver_res(resp); } @@ -372,6 +430,42 @@ return &(dnss->srr); +/************************************************* +* Return whether AD bit set in DNS result * +*************************************************/ + +/* We do not perform DNSSEC work ourselves; if the administrator has installed +a verifying resolver which sets AD as appropriate, though, we'll use that. +(AD = Authentic Data) + +Argument: pointer to dns answer block +Returns: bool indicating presence of AD bit +*/ + +BOOL +dns_is_secure(dns_answer *dnsa) +{ +#ifdef DISABLE_DNSSEC +DEBUG(D_dns) + debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n"); +return FALSE; +#else +HEADER *h = (HEADER *)dnsa->answer; +return h->ad ? TRUE : FALSE; +#endif +} + +static void +dns_set_insecure(dns_answer * dnsa) +{ +HEADER * h = (HEADER *)dnsa->answer; +h->ad = 0; +} + + + + + /************************************************* * Turn DNS type into text * *************************************************/ @@ -393,11 +487,13 @@ switch(t) case T_AAAA: return US"AAAA"; case T_A6: return US"A6"; case T_TXT: return US"TXT"; + case T_SPF: return US"SPF"; case T_PTR: return US"PTR"; case T_SOA: return US"SOA"; case T_SRV: return US"SRV"; case T_NS: return US"NS"; case T_CNAME: return US"CNAME"; + case T_TLSA: return US"TLSA"; default: return US"?"; } } @@ -424,9 +520,10 @@ Returns: the return code static int dns_return(uschar *name, int type, int rc) { +res_state resp = os_get_dns_resolver_res(); tree_node *node = store_get_perm(sizeof(tree_node) + 290); sprintf(CS node->name, "%.255s-%s-%lx", name, dns_text_type(type), - _res.options); + resp->options); node->data.val = rc; (void)tree_insertnode(&tree_dns_fails, node); return rc; @@ -462,10 +559,11 @@ Returns: DNS_SUCCEED successful lookup int dns_basic_lookup(dns_answer *dnsa, uschar *name, int type) { -int rc = -1; #ifndef STAND_ALONE +int rc = -1; uschar *save; #endif +res_state resp = os_get_dns_resolver_res(); tree_node *previous; uschar node_name[290]; @@ -476,7 +574,7 @@ have many addresses in the same domain. We rely on the resolver and name server caching for successful lookups. */ sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type), - _res.options); + resp->options); previous = tree_search(tree_dns_fails, node_name); if (previous != NULL) { @@ -517,7 +615,7 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) /* For an SRV lookup, skip over the first two components (the service and protocol names, which both start with an underscore). */ - if (type == T_SRV) + if (type == T_SRV || type == T_TLSA) { while (*checkname++ != '.'); while (*checkname++ != '.'); @@ -662,7 +760,8 @@ int dns_lookup(dns_answer *dnsa, uschar *name, int type, uschar **fully_qualified_name) { int i; -uschar *orig_name = name; +const uschar *orig_name = name; +BOOL secure_so_far = TRUE; /* Loop to follow CNAME chains so far, but no further... */ @@ -717,7 +816,12 @@ for (i = 0; i < 10; i++) /* If any data records of the correct type were found, we are done. */ - if (type_rr.data != NULL) return DNS_SUCCEED; + if (type_rr.data != NULL) + { + if (!secure_so_far) /* mark insecure if any element of CNAME chain was */ + dns_set_insecure(dnsa); + return DNS_SUCCEED; + } /* If there are no data records, we need to re-scan the DNS using the domain given in the CNAME record, which should exist (otherwise we should @@ -730,6 +834,9 @@ for (i = 0; i < 10; i++) if (datalen < 0) return DNS_FAIL; name = data; + if (!dns_is_secure(dnsa)) + secure_so_far = FALSE; + DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name); } /* Loop back to do another lookup */ @@ -1165,4 +1272,6 @@ else return yield; } +/* vi: aw ai sw=2 +*/ /* End of dns.c */