X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/979c462ed43bd4f53f61a0031ec22967dea83902..e2be2df5c0760e2b6a7870c88ad486a23f5e4b01:/src/src/dns.c diff --git a/src/src/dns.c b/src/src/dns.c index 8d9ec4708..c68698786 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -60,7 +61,8 @@ if (stat(CS utilname, &statbuf) >= 0) int infd, outfd, rc; uschar *argv[5]; - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type)); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", + name, dns_text_type(type)); argv[0] = utilname; argv[1] = config_main_directory; @@ -68,7 +70,7 @@ if (stat(CS utilname, &statbuf) >= 0) argv[3] = dns_text_type(type); argv[4] = NULL; - pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE); + pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE, US"fakens-search"); if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s", strerror(errno)); @@ -221,16 +223,15 @@ a name that can be used to look up PTR records. Arguments: string the IP address as a string - buffer a suitable buffer, long enough to hold the result -Returns: nothing +Returns: an allocated string */ -void -dns_build_reverse(const uschar *string, uschar *buffer) +uschar * +dns_build_reverse(const uschar * string) { -const uschar *p = string + Ustrlen(string); -uschar *pp = buffer; +const uschar * p = string + Ustrlen(string); +gstring * g = NULL; /* Handle IPv4 address */ @@ -240,14 +241,13 @@ if (Ustrchr(string, ':') == NULL) { for (int i = 0; i < 4; i++) { - const uschar *ppp = p; + const uschar * ppp = p; while (ppp > string && ppp[-1] != '.') ppp--; - Ustrncpy(pp, ppp, p - ppp); - pp += p - ppp; - *pp++ = '.'; + g = string_catn(g, ppp, p - ppp); + g = string_catn(g, US".", 1); p = ppp - 1; } - Ustrcpy(pp, US"in-addr.arpa"); + g = string_catn(g, US"in-addr.arpa", 12); } /* Handle IPv6 address; convert to binary so as to fill out any @@ -257,6 +257,8 @@ abbreviation in the textual form. */ else { int v6[4]; + + g = string_get_tainted(32, is_tainted(string)); (void)host_aton(string, v6); /* The original specification for IPv6 reverse lookup was to invert each @@ -265,8 +267,8 @@ else for (int i = 3; i >= 0; i--) for (int j = 0; j < 32; j += 4) - pp += sprintf(CS pp, "%x.", (v6[i] >> j) & 15); - Ustrcpy(pp, US"ip6.arpa."); + g = string_fmt_append(g, "%x.", (v6[i] >> j) & 15); + g = string_catn(g, US"ip6.arpa.", 9); /* Another way of doing IPv6 reverse lookups was proposed in conjunction with A6 records. However, it fell out of favour when they did. The @@ -290,6 +292,7 @@ else } #endif +return string_from_gstring(g); } @@ -331,7 +334,6 @@ char * trace = NULL; #ifdef rr_trace # define TRACE DEBUG(D_dns) #else -trace = trace; # define TRACE if (FALSE) #endif @@ -499,6 +501,8 @@ const uschar * auth_name; const uschar * trusted; if (dnsa->answerlen < 0) return FALSE; +/* Beware that newer versions of glibc on Linux will filter out the ad bit +unless their shiny new RES_TRUSTAD bit is set for the resolver. */ if (h->ad) return TRUE; /* If the resolver we ask is authoritative for the domain in question, it may @@ -511,7 +515,7 @@ if ( !h->aa || !(trusted = expand_string(dns_trust_aa)) || !*trusted || !(auth_name = dns_extract_auth_name(dnsa)) - || OK != match_isinlist(auth_name, &trusted, 0, NULL, NULL, + || OK != match_isinlist(auth_name, &trusted, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) ) return FALSE; @@ -667,13 +671,10 @@ e = previous->data.ptr; val = e->data.val; rc = e->expiry && e->expiry <= time(NULL) ? -1 : val; -DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: %scached value %s%s\n", +DEBUG(D_dns) debug_printf("DNS lookup of %.255s (%s): %scached value %s%s\n", name, dns_text_type(type), rc == -1 ? "" : "using ", - val == DNS_NOMATCH ? "DNS_NOMATCH" : - val == DNS_NODATA ? "DNS_NODATA" : - val == DNS_AGAIN ? "DNS_AGAIN" : - val == DNS_FAIL ? "DNS_FAIL" : "??", + dns_rc_names[val], rc == -1 ? " past valid time" : ""); return rc; @@ -681,17 +682,6 @@ return rc; -/* Return the TTL suitable for an NXDOMAIN result, which is given -in the SOA. We hope that one was returned in the lookup, and do not -bother doing a separate lookup; if not found return a forever TTL. -*/ - -time_t -dns_expire_from_soa(dns_answer * dnsa) -{ -const HEADER * h = (const HEADER *)dnsa->answer; -dns_scan dnss; - /* This is really gross. The successful return value from res_search() is the packet length, which is stored in dnsa->answerlen. If we get a negative DNS reply then res_search() returns -1, which causes the bounds @@ -703,12 +693,38 @@ re-implement res_search() and res_query() so that they don't muddle their success and packet length return values.) For added safety we only reset the packet length if the packet header looks plausible. */ -if ( h->qr == 1 && h->opcode == QUERY && h->tc == 0 +static void +fake_dnsa_len_for_fail(dns_answer * dnsa, int type) +{ +const HEADER * h = (const HEADER *)dnsa->answer; + +if ( h->qr == 1 /* a response */ + && h->opcode == QUERY + && h->tc == 0 /* nmessage not truncated */ && (h->rcode == NOERROR || h->rcode == NXDOMAIN) - && (ntohs(h->qdcount) == 1 || f.running_in_test_harness) - && ntohs(h->ancount) == 0 - && ntohs(h->nscount) >= 1) - dnsa->answerlen = sizeof(dnsa->answer); + && ( ntohs(h->qdcount) == 1 /* one question record */ + || f.running_in_test_harness) + && ntohs(h->ancount) == 0 /* no answer records */ + && ntohs(h->nscount) >= 1) /* authority records */ + { + DEBUG(D_dns) debug_printf("faking res_search(%s) response length as %d\n", + dns_text_type(type), (int)sizeof(dnsa->answer)); + dnsa->answerlen = sizeof(dnsa->answer); + } +} + + +/* Return the TTL suitable for an NXDOMAIN result, which is given +in the SOA. We hope that one was returned in the lookup, and do not +bother doing a separate lookup; if not found return a forever TTL. +*/ + +time_t +dns_expire_from_soa(dns_answer * dnsa, int type) +{ +dns_scan dnss; + +fake_dnsa_len_for_fail(dnsa, type); for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) @@ -878,7 +894,7 @@ 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_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NOMATCH); + return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); case TRY_AGAIN: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", @@ -888,8 +904,8 @@ if (dnsa->answerlen < 0) switch (h_errno) #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); + rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save_domain; if (rc != OK) { @@ -898,7 +914,7 @@ if (dnsa->answerlen < 0) switch (h_errno) } DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning " "DNS_NOMATCH\n", name); - return dns_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NOMATCH); + return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); #else /* For stand-alone tests */ return dns_fail_return(name, type, 0, DNS_AGAIN); @@ -912,7 +928,7 @@ if (dnsa->answerlen < 0) switch (h_errno) 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_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NODATA); + return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NODATA); default: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n" @@ -1185,23 +1201,7 @@ switch (type) if (rc == DNS_NOMATCH) { - /* This is really gross. The successful return value from res_search() is - the packet length, which is stored in dnsa->answerlen. If we get a - negative DNS reply then res_search() returns -1, which causes the bounds - checks for name decompression to fail when it is treated as a packet - length, which in turn causes the authority search to fail. The correct - packet length has been lost inside libresolv, so we have to guess a - replacement value. (The only way to fix this properly would be to - re-implement res_search() and res_query() so that they don't muddle their - success and packet length return values.) For added safety we only reset - the packet length if the packet header looks plausible. */ - - const HEADER * h = (const HEADER *)dnsa->answer; - if (h->qr == 1 && h->opcode == QUERY && h->tc == 0 - && (h->rcode == NOERROR || h->rcode == NXDOMAIN) - && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 - && ntohs(h->nscount) >= 1) - dnsa->answerlen = sizeof(dnsa->answer); + fake_dnsa_len_for_fail(dnsa, T_CSA); for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) @@ -1245,7 +1245,7 @@ switch (type) /* Extract the numerical SRV fields (p is incremented) */ GETSHORT(priority, p); - GETSHORT(weight, p); weight = weight; /* compiler quietening */ + GETSHORT(weight, p); GETSHORT(port, p); /* Check the CSA version number */