X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/aa7751be078fe9a20efa2cf2a8856fadb98f4178..806c3df997132e99f4abade6d00be18930695408:/src/src/dns.c diff --git a/src/src/dns.c b/src/src/dns.c index a239bec30..17152d9a6 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -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,24 +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 += 6; /* Don't want class or 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; } @@ -467,7 +517,7 @@ if ( !h->aa || !*trusted || !(auth_name = dns_extract_auth_name(dnsa)) || OK != match_isinlist(auth_name, &trusted, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL) + MCL_DOMAIN, TRUE, NULL) ) return FALSE; @@ -482,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 } /************************************************ @@ -495,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 } @@ -557,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; @@ -606,7 +662,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), - resp->options); + (unsigned long) resp->options); previous = tree_search(tree_dns_fails, node_name); if (previous != NULL) { @@ -619,7 +675,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; @@ -812,7 +868,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; @@ -850,7 +906,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 @@ -862,7 +918,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); @@ -874,10 +930,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)) @@ -1111,8 +1171,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 @@ -1144,8 +1203,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; } }