X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/94431adbd61d7706fe6df3a19bcae043fec950bf..ec8b777a224687c7ea440b78b0bb0fec95acff87:/src/src/dns.c diff --git a/src/src/dns.c b/src/src/dns.c index 27ab2d2a2..575b81560 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2015 */ +/* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -305,6 +305,15 @@ else +/* Increment the aptr in dnss, checking against dnsa length. +Return: TRUE for a bad result +*/ +static BOOL +dnss_inc(dns_answer * dnsa, dns_scan * dnss, unsigned delta) +{ +return (dnss->aptr += delta) >= dnsa->answer + dnsa->answerlen; +} + /************************************************* * Get next DNS record from answer block * *************************************************/ @@ -328,10 +337,19 @@ dns_next_rr(dns_answer *dnsa, dns_scan *dnss, int reset) HEADER *h = (HEADER *)dnsa->answer; int namelen; +char * trace = NULL; +#ifdef rr_trace +# define TRACE DEBUG(D_dns) +#else +trace = trace; +# define TRACE if (FALSE) +#endif + /* Reset the saved data when requested to, and skip to the first required RR */ if (reset != RESET_NEXT) { + TRACE debug_printf("%s: reset\n", __FUNCTION__); dnss->rrcount = ntohs(h->qdcount); dnss->aptr = dnsa->answer + sizeof(HEADER); @@ -339,10 +357,13 @@ if (reset != RESET_NEXT) while (dnss->rrcount-- > 0) { + TRACE trace = "Q-namelen"; namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - dnss->aptr, (DN_EXPAND_ARG4_TYPE) &(dnss->srr.name), DNS_MAXNAME); - if (namelen < 0) { dnss->rrcount = 0; return NULL; } - dnss->aptr += namelen + 4; /* skip name & type & class */ + dnss->aptr, (DN_EXPAND_ARG4_TYPE) &dnss->srr.name, DNS_MAXNAME); + if (namelen < 0) goto null_return; + /* skip name & type & class */ + TRACE trace = "Q-skip"; + if (dnss_inc(dnsa, dnss, namelen+4)) goto null_return; } /* Get the number of answer records. */ @@ -353,23 +374,37 @@ if (reset != RESET_NEXT) the NS records (i.e. authority section) if wanting to look at the additional records. */ - if (reset == RESET_ADDITIONAL) dnss->rrcount += ntohs(h->nscount); + if (reset == RESET_ADDITIONAL) + { + TRACE debug_printf("%s: additional\n", __FUNCTION__); + dnss->rrcount += ntohs(h->nscount); + } if (reset == RESET_AUTHORITY || reset == RESET_ADDITIONAL) { + TRACE if (reset == RESET_AUTHORITY) + debug_printf("%s: authority\n", __FUNCTION__); while (dnss->rrcount-- > 0) { + TRACE trace = "A-namelen"; namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - dnss->aptr, (DN_EXPAND_ARG4_TYPE) &(dnss->srr.name), DNS_MAXNAME); - if (namelen < 0) { dnss->rrcount = 0; return NULL; } - dnss->aptr += namelen + 8; /* skip name, type, class & TTL */ + dnss->aptr, (DN_EXPAND_ARG4_TYPE) &dnss->srr.name, DNS_MAXNAME); + if (namelen < 0) goto null_return; + /* skip name, type, class & TTL */ + TRACE trace = "A-hdr"; + if (dnss_inc(dnsa, dnss, namelen+8)) goto null_return; GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */ - dnss->aptr += dnss->srr.size; /* skip over it */ + /* skip over it */ + TRACE trace = "A-skip"; + if (dnss_inc(dnsa, dnss, dnss->srr.size)) goto null_return; } - dnss->rrcount = (reset == RESET_AUTHORITY) + dnss->rrcount = reset == RESET_AUTHORITY ? ntohs(h->nscount) : ntohs(h->arcount); } + TRACE debug_printf("%s: %d RRs to read\n", __FUNCTION__, dnss->rrcount); } +else + TRACE debug_printf("%s: next (%d left)\n", __FUNCTION__, dnss->rrcount); /* The variable dnss->aptr is now pointing at the next RR, and dnss->rrcount contains the number of RR records left. */ @@ -379,25 +414,39 @@ if (dnss->rrcount-- <= 0) return NULL; /* If expanding the RR domain name fails, behave as if no more records (something safe). */ +TRACE trace = "R-namelen"; namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, dnss->aptr, - (DN_EXPAND_ARG4_TYPE) &(dnss->srr.name), DNS_MAXNAME); -if (namelen < 0) { dnss->rrcount = 0; return NULL; } + (DN_EXPAND_ARG4_TYPE) &dnss->srr.name, DNS_MAXNAME); +if (namelen < 0) goto null_return; /* Move the pointer past the name and fill in the rest of the data structure from the following bytes. */ -dnss->aptr += namelen; -GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */ -dnss->aptr += 2; /* Don't want class */ -GETLONG(dnss->srr.ttl, dnss->aptr); /* TTL */ -GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */ -dnss->srr.data = dnss->aptr; /* The record's data follows */ -dnss->aptr += dnss->srr.size; /* Advance to next RR */ +TRACE trace = "R-name"; +if (dnss_inc(dnsa, dnss, namelen)) goto null_return; + +GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */ +TRACE trace = "R-class"; +if (dnss_inc(dnsa, dnss, 2)) goto null_return; /* Don't want class */ +GETLONG(dnss->srr.ttl, dnss->aptr); /* TTL */ +GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */ +dnss->srr.data = dnss->aptr; /* The record's data follows */ + +/* Unchecked increment ok here since no further access on this iteration; +will be checked on next at "R-name". */ + +dnss->aptr += dnss->srr.size; /* Advance to next RR */ /* Return a pointer to the dns_record structure within the dns_answer. This is for convenience so that the scans can use nice-looking for loops. */ -return &(dnss->srr); +return &dnss->srr; + +null_return: + TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s\n", + __FUNCTION__, dnss->rrcount, trace); + dnss->rrcount = 0; + return NULL; } @@ -483,8 +532,10 @@ return TRUE; static void dns_set_insecure(dns_answer * dnsa) { +#ifndef DISABLE_DNSSEC HEADER * h = (HEADER *)dnsa->answer; h->ad = 0; +#endif } /************************************************ @@ -496,7 +547,11 @@ h->ad = 0; BOOL dns_is_aa(const dns_answer *dnsa) { +#ifdef DISABLE_DNSSEC +return FALSE; +#else return ((HEADER*)dnsa->answer)->aa; +#endif } @@ -558,7 +613,7 @@ dns_return(const 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), - resp->options); + (unsigned long) resp->options); node->data.val = rc; (void)tree_insertnode(&tree_dns_fails, node); return rc; @@ -607,9 +662,8 @@ 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), - resp->options); -previous = tree_search(tree_dns_fails, node_name); -if (previous != NULL) + (unsigned long) resp->options); +if ((previous = tree_search(tree_dns_fails, node_name))) { DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: using cached value %s\n", name, dns_text_type(type), @@ -620,7 +674,7 @@ if (previous != NULL) return previous->data.val; } -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N /* Convert all names to a-label form before doing lookup */ { uschar * alabel; @@ -714,48 +768,48 @@ if (dnsa->answerlen > MAXPACKET) if (dnsa->answerlen < 0) switch (h_errno) { case HOST_NOT_FOUND: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n" - "returning DNS_NOMATCH\n", name, dns_text_type(type)); - return dns_return(name, type, DNS_NOMATCH); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n" + "returning DNS_NOMATCH\n", name, dns_text_type(type)); + return dns_return(name, type, DNS_NOMATCH); case TRY_AGAIN: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", - name, dns_text_type(type)); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", + name, dns_text_type(type)); - /* Cut this out for various test programs */ + /* Cut this out for various test programs */ #ifndef STAND_ALONE - save_domain = deliver_domain; - deliver_domain = string_copy(name); /* set $domain */ - rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL); - deliver_domain = save_domain; - if (rc != OK) - { - DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); - return dns_return(name, type, DNS_AGAIN); - } - DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning " - "DNS_NOMATCH\n", name); - return dns_return(name, type, DNS_NOMATCH); + save_domain = deliver_domain; + deliver_domain = string_copy(name); /* set $domain */ + rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL); + deliver_domain = save_domain; + if (rc != OK) + { + DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); + return dns_return(name, type, DNS_AGAIN); + } + DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning " + "DNS_NOMATCH\n", name); + return dns_return(name, type, DNS_NOMATCH); #else /* For stand-alone tests */ - return dns_return(name, type, DNS_AGAIN); + return dns_return(name, type, DNS_AGAIN); #endif case NO_RECOVERY: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" - "returning DNS_FAIL\n", name, dns_text_type(type)); - return dns_return(name, type, DNS_FAIL); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" + "returning DNS_FAIL\n", name, dns_text_type(type)); + return dns_return(name, type, DNS_FAIL); case NO_DATA: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n" - "returning DNS_NODATA\n", name, dns_text_type(type)); - return dns_return(name, type, DNS_NODATA); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n" + "returning DNS_NODATA\n", name, dns_text_type(type)); + return dns_return(name, type, DNS_NODATA); default: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n" - "returning DNS_FAIL\n", name, dns_text_type(type), h_errno); - return dns_return(name, type, DNS_FAIL); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n" + "returning DNS_FAIL\n", name, dns_text_type(type), h_errno); + return dns_return(name, type, DNS_FAIL); } DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) succeeded\n", @@ -813,7 +867,7 @@ BOOL secure_so_far = TRUE; for (i = 0; i < 10; i++) { - uschar data[256]; + uschar * data; dns_record *rr, cname_rr, type_rr; dns_scan dnss; int datalen, rc; @@ -851,7 +905,7 @@ for (i = 0; i < 10; i++) if ( rr_name && Ustrcmp(rr_name, *fully_qualified_name) != 0 && rr_name[0] != '*' -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N && ( !string_is_utf8(*fully_qualified_name) || Ustrcmp(rr_name, string_domain_utf8_to_alabel(*fully_qualified_name, NULL)) != 0 @@ -863,7 +917,7 @@ for (i = 0; i < 10; i++) /* If any data records of the correct type were found, we are done. */ - if (type_rr.data != NULL) + if (type_rr.data) { if (!secure_so_far) /* mark insecure if any element of CNAME chain was */ dns_set_insecure(dnsa); @@ -875,10 +929,14 @@ for (i = 0; i < 10; i++) have had a failure from dns_lookup). However code against the possibility of its not existing. */ - if (cname_rr.data == NULL) return DNS_FAIL; + if (!cname_rr.data) + return DNS_FAIL; + + data = store_get(256); datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data)); - if (datalen < 0) return DNS_FAIL; + cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256); + if (datalen < 0) + return DNS_FAIL; name = data; if (!dns_is_secure(dnsa)) @@ -1112,8 +1170,7 @@ return DNS_FAIL; * Get address(es) from DNS record * *************************************************/ -/* The record type is either T_A for an IPv4 address or T_AAAA (or T_A6 when -supported) for an IPv6 address. +/* The record type is either T_A for an IPv4 address or T_AAAA for an IPv6 address. Argument: dnsa the DNS answer block @@ -1145,8 +1202,11 @@ else { if (rr->data + 16 <= dnsa_lim) { + struct in6_addr in6; + int i; + for (i = 0; i < 16; i++) in6.s6_addr[i] = rr->data[i]; yield = store_get(sizeof(dns_address) + 50); - inet_ntop(AF_INET6, US rr->data, CS yield->address, 50); + inet_ntop(AF_INET6, &in6, CS yield->address, 50); yield->next = NULL; } }