X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fdns.c;h=575b815605dba78d7a2f72193e04c1e3179d2f33;hb=9960d1e59f08254a951283981d4a32c73ca7d3ad;hp=e6e4fb6b3e7708b4bc4b1f3fe39293e28e644923;hpb=f794c12b6991a528c7a950b05a6d4578f1041be8;p=exim.git diff --git a/src/src/dns.c b/src/src/dns.c index e6e4fb6b3..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; } @@ -614,8 +663,7 @@ caching for successful lookups. */ sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type), (unsigned long) resp->options); -previous = tree_search(tree_dns_fails, node_name); -if (previous != NULL) +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), @@ -720,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", @@ -819,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; @@ -869,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); @@ -881,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))