X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/8688d177525f5b318ca05f61a2ac02b7e2761096..97229a2119f27f735ba4f02c131aac116ee0d5d5:/src/src/dns.c diff --git a/src/src/dns.c b/src/src/dns.c index fc0ffb2ba..b7978c521 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 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -271,10 +271,7 @@ else { int j; for (j = 0; j < 32; j += 4) - { - sprintf(CS pp, "%x.", (v6[i] >> j) & 15); - pp += 2; - } + pp += sprintf(CS pp, "%x.", (v6[i] >> j) & 15); } Ustrcpy(pp, "ip6.arpa."); @@ -326,7 +323,7 @@ The result is in static storage which must be copied if it is to be preserved. Arguments: dnsa pointer to dns answer block dnss pointer to dns scan block - reset option specifing what portion to scan, as described above + reset option specifying what portion to scan, as described above Returns: next dns record, or NULL when no more */ @@ -349,8 +346,8 @@ trace = trace; if (reset != RESET_NEXT) { - TRACE debug_printf("%s: reset\n", __FUNCTION__); dnss->rrcount = ntohs(h->qdcount); + TRACE debug_printf("%s: reset (Q rrcount %d)\n", __FUNCTION__, dnss->rrcount); dnss->aptr = dnsa->answer + sizeof(HEADER); /* Skip over questions; failure to expand the name just gives up */ @@ -369,6 +366,7 @@ if (reset != RESET_NEXT) /* Get the number of answer records. */ dnss->rrcount = ntohs(h->ancount); + TRACE debug_printf("%s: reset (A rrcount %d)\n", __FUNCTION__, dnss->rrcount); /* Skip over answers if we want to look at the authority section. Also skip the NS records (i.e. authority section) if wanting to look at the additional @@ -378,6 +376,7 @@ if (reset != RESET_NEXT) { TRACE debug_printf("%s: additional\n", __FUNCTION__); dnss->rrcount += ntohs(h->nscount); + TRACE debug_printf("%s: reset (NS rrcount %d)\n", __FUNCTION__, dnss->rrcount); } if (reset == RESET_AUTHORITY || reset == RESET_ADDITIONAL) @@ -400,6 +399,8 @@ if (reset != RESET_NEXT) } dnss->rrcount = reset == RESET_AUTHORITY ? ntohs(h->nscount) : ntohs(h->arcount); + TRACE debug_printf("%s: reset (%s rrcount %d)\n", __FUNCTION__, + reset == RESET_AUTHORITY ? "NS" : "AR", dnss->rrcount); } TRACE debug_printf("%s: %d RRs to read\n", __FUNCTION__, dnss->rrcount); } @@ -443,17 +444,17 @@ for convenience so that the scans can use nice-looking for loops. */ return &dnss->srr; null_return: - TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s\n", - __FUNCTION__, dnss->rrcount, trace); + TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s; errno %d %s\n", + __FUNCTION__, dnss->rrcount, trace, errno, strerror(errno)); dnss->rrcount = 0; return NULL; } /* Extract the AUTHORITY information from the answer. If the answer isn't -authoritive (AA not set), we do not extract anything. +authoritative (AA not set), we do not extract anything. -The AUTHORITIVE section contains NS records if the name in question was found, +The AUTHORITY section contains NS records if the name in question was found, it contains a SOA record otherwise. (This is just from experience and some tests, is there some spec?) @@ -486,7 +487,7 @@ return NULL; /* 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, AA = Authoritive Answer) +(AD = Authentic Data, AA = Authoritative Answer) Argument: pointer to dns answer block Returns: bool indicating presence of AD bit @@ -506,7 +507,7 @@ const uschar * trusted; if (h->ad) return TRUE; -/* If the resolver we ask is authoritive for the domain in question, it +/* If the resolver we ask is authoritative for the domain in question, it * may not set the AD but the AA bit. If we explicitly trust * the resolver for that domain (via a domainlist in dns_trust_aa), * we return TRUE to indicate a secure answer. @@ -542,7 +543,7 @@ h->aa = h->ad = 0; /************************************************ * Check whether the AA bit is set * * We need this to warn if we requested AD * - * from an authoritive server * + * from an authoritative server * ************************************************/ BOOL @@ -595,6 +596,15 @@ switch(t) * Cache a failed DNS lookup result * *************************************************/ +static void +dns_fail_tag(uschar * buf, const uschar * name, int dns_type) +{ +res_state resp = os_get_dns_resolver_res(); +sprintf(CS buf, "%.255s-%s-%lx", name, dns_text_type(dns_type), + (unsigned long) resp->options); +} + + /* We cache failed lookup results so as not to experience timeouts many times for the same domain. We need to retain the resolver options because they may change. For successful lookups, we rely on resolver and/or name server @@ -611,10 +621,8 @@ Returns: the return code static int 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), - (unsigned long) resp->options); +dns_fail_tag(node->name, name, type); node->data.val = rc; (void)tree_insertnode(&tree_dns_fails, node); return rc; @@ -626,11 +634,15 @@ return rc; /* Call the resolver to look up the given domain name, using the given type, and check the result. The error code TRY_AGAIN is documented as meaning "non- -Authoritive Host not found, or SERVERFAIL". Sometimes there are badly set +Authoritative Host not found, or SERVERFAIL". Sometimes there are badly set up nameservers that produce this error continually, so there is the option of providing a list of domains for which this is treated as a non-existent host. +The dns_answer structure is pretty big; enough to hold a max-sized DNS message +- so best allocated from fast-release memory. As of writing, all our callers +use a stack-auto variable. + Arguments: dnsa pointer to dns_answer structure name name to look up @@ -652,7 +664,6 @@ dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type) int rc = -1; const uschar *save_domain; #endif -res_state resp = os_get_dns_resolver_res(); tree_node *previous; uschar node_name[290]; @@ -662,16 +673,15 @@ a timeout on one domain doesn't happen time and time again for messages that 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), - (unsigned long) resp->options); +dns_fail_tag(node_name, name, type); 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), - (previous->data.val == DNS_NOMATCH)? "DNS_NOMATCH" : - (previous->data.val == DNS_NODATA)? "DNS_NODATA" : - (previous->data.val == DNS_AGAIN)? "DNS_AGAIN" : - (previous->data.val == DNS_FAIL)? "DNS_FAIL" : "??"); + previous->data.val == DNS_NOMATCH ? "DNS_NOMATCH" : + previous->data.val == DNS_NODATA ? "DNS_NODATA" : + previous->data.val == DNS_AGAIN ? "DNS_AGAIN" : + previous->data.val == DNS_FAIL ? "DNS_FAIL" : "??"); return previous->data.val; } @@ -687,14 +697,14 @@ if ((previous = tree_search(tree_dns_fails, node_name))) DEBUG(D_dns) debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s\n", name, errstr); - host_find_failed_syntax = TRUE; + f.host_find_failed_syntax = TRUE; return DNS_NOMATCH; } name = alabel; } #endif -/* If configured, check the hygene of the name passed to lookup. Otherwise, +/* If configured, check the hygiene of the name passed to lookup. Otherwise, although DNS lookups may give REFUSED at the lower level, some resolvers turn this into TRY_AGAIN, which is silly. Give a NOMATCH return, since such domains cannot be in the DNS. The check is now done by a regular expression; @@ -706,7 +716,11 @@ lookup, which constructs the names itself, so they should be OK. Besides, bitstring labels don't conform to normal name syntax. (But the aren't used any more.) -For SRV records, we omit the initial _smtp._tcp. components at the start. */ +For SRV records, we omit the initial _smtp._tcp. components at the start. +The check has been seen to bite on the destination of a SRV lookup that +initiall hit a CNAME, for which the next name had only two components. +RFC2782 makes no mention of the possibiility of CNAMES, but the Wikipedia +article on SRV says they are not a valid configuration. */ #ifndef STAND_ALONE /* Omit this for stand-alone tests */ @@ -722,17 +736,17 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) if (type == T_SRV || type == T_TLSA) { - while (*checkname++ != '.'); - while (*checkname++ != '.'); + while (*checkname && *checkname++ != '.') ; + while (*checkname && *checkname++ != '.') ; } if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname), - 0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0) + 0, PCRE_EOPT, ovector, nelem(ovector)) < 0) { DEBUG(D_dns) debug_printf("DNS name syntax check failed: %s (%s)\n", name, dns_text_type(type)); - host_find_failed_syntax = TRUE; + f.host_find_failed_syntax = TRUE; return DNS_NOMATCH; } } @@ -755,15 +769,16 @@ if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0) (res_search), we call fakens_search(), which recognizes certain special domains, and interfaces to a fake nameserver for certain special zones. */ -dnsa->answerlen = running_in_test_harness - ? fakens_search(name, type, dnsa->answer, MAXPACKET) - : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET); +dnsa->answerlen = f.running_in_test_harness + ? fakens_search(name, type, dnsa->answer, sizeof(dnsa->answer)) + : res_search(CCS name, C_IN, type, dnsa->answer, sizeof(dnsa->answer)); -if (dnsa->answerlen > MAXPACKET) +if (dnsa->answerlen > (int) sizeof(dnsa->answer)) { - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n", - name, dns_text_type(type), dnsa->answerlen, MAXPACKET); - dnsa->answerlen = MAXPACKET; + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet" + " (size %d), truncating to %u.\n", + name, dns_text_type(type), dnsa->answerlen, (unsigned int) sizeof(dnsa->answer)); + dnsa->answerlen = sizeof(dnsa->answer); } if (dnsa->answerlen < 0) switch (h_errno) @@ -829,6 +844,8 @@ return DNS_SUCCEED; /* Look up the given domain name, using the given type. Follow CNAMEs if necessary, but only so many times. There aren't supposed to be CNAME chains in the DNS, but you are supposed to cope with them if you find them. +By default, follow one CNAME since a resolver has been seen, faced with +an MX request and a CNAME (to an A) but no MX present, returning the CNAME. The assumption is made that if the resolver gives back records of the requested type *and* a CNAME, we don't need to make another call to look up @@ -864,14 +881,19 @@ int i; const uschar *orig_name = name; BOOL secure_so_far = TRUE; -/* Loop to follow CNAME chains so far, but no further... */ +/* By default, assume the resolver follows CNAME chains (and returns NODATA for +an unterminated one). If it also does that for a CNAME loop, fine; if it returns +a CNAME (maybe the last?) whine about it. However, retain the coding for dumb +resolvers hiding behind a config variable. Loop to follow CNAME chains so far, +but no further... The testsuite tests the latter case, mostly assuming that the +former will work. */ -for (i = 0; i < 10; i++) +for (i = 0; i <= dns_cname_loops; i++) { uschar * data; dns_record *rr, cname_rr, type_rr; dns_scan dnss; - int datalen, rc; + int rc; /* DNS lookup failures get passed straight back. */ @@ -933,8 +955,8 @@ for (i = 0; i < 10; i++) return DNS_FAIL; data = store_get(256); - if ((datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256)) < 0) + if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, + cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0) return DNS_FAIL; name = data; @@ -1014,7 +1036,7 @@ switch (type) assertion field. */ case T_CSA: { - uschar *srvname, *namesuff, *tld, *p; + uschar *srvname, *namesuff, *tld; int priority, weight, port; int limit, rc, i; BOOL ipv6; @@ -1087,7 +1109,7 @@ switch (type) && (h->rcode == NOERROR || h->rcode == NXDOMAIN) && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 && ntohs(h->nscount) >= 1) - dnsa->answerlen = MAXPACKET; + dnsa->answerlen = sizeof(dnsa->answer); for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)