X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fdns.c;h=f322fafca1679efc8502973db3a9d63a06b651d0;hb=6d06cf480f74aec15035066c615e68deeacca8b9;hp=9e66396009adff308e7f8e448a13e1993354957e;hpb=c988f1f4faa9f679f79beddf3c14676c5dcb8e28;p=exim.git diff --git a/src/src/dns.c b/src/src/dns.c index 9e6639600..f322fafca 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/dns.c,v 1.4 2005/01/04 10:00:42 ph10 Exp $ */ +/* $Cambridge: exim/src/src/dns.c,v 1.8 2005/06/10 13:38:06 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -153,9 +153,9 @@ else *************************************************/ /* Call this with reset == RESET_ANSWERS to scan the answer block, reset == -RESET_ADDITIONAL to scan the additional records, and reset == RESET_NEXT to -get the next record. The result is in static storage which must be copied if -it is to be preserved. +RESET_AUTHORITY to scan the authority records, reset == RESET_ADDITIONAL to +scan the additional records, and reset == RESET_NEXT to get the next record. +The result is in static storage which must be copied if it is to be preserved. Arguments: dnsa pointer to dns answer block @@ -192,12 +192,14 @@ if (reset != RESET_NEXT) dnss->rrcount = ntohs(h->ancount); - /* Skip over answers and NS records if wanting to look at the additional + /* 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 records. */ - if (reset == RESET_ADDITIONAL) + if (reset == RESET_ADDITIONAL) dnss->rrcount += ntohs(h->nscount); + + if (reset == RESET_AUTHORITY || reset == RESET_ADDITIONAL) { - dnss->rrcount += ntohs(h->nscount); while (dnss->rrcount-- > 0) { namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, @@ -207,11 +209,11 @@ if (reset != RESET_NEXT) GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */ dnss->aptr += dnss->srr.size; /* skip over it */ } - dnss->rrcount = ntohs(h->arcount); + dnss->rrcount = (reset == RESET_AUTHORITY) + ? ntohs(h->nscount) : ntohs(h->arcount); } } - /* The variable dnss->aptr is now pointing at the next RR, and dnss->rrcount contains the number of RR records left. */ @@ -266,7 +268,7 @@ switch(t) case T_PTR: return US"PTR"; case T_SRV: return US"SRV"; case T_NS: return US"NS"; - case T_CNAME: return US"CNAME"; + case T_CNAME: return US"CNAME"; default: return US"?"; } } @@ -626,8 +628,8 @@ return DNS_FAIL; * Do a DNS lookup and handle virtual types * ************************************************/ -/* This function handles some invented "lookup types" that synthesize feature -not available in the basic types. The special types all have negative values. +/* This function handles some invented "lookup types" that synthesize feature +not available in the basic types. The special types all have negative values. Positive type values are passed straight on to dns_lookup(). Arguments: @@ -645,7 +647,7 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_special_lookup(dns_answer *dnsa, uschar *name, int type, +dns_special_lookup(dns_answer *dnsa, uschar *name, int type, uschar **fully_qualified_name) { if (type >= 0) return dns_lookup(dnsa, name, type, fully_qualified_name); @@ -654,7 +656,7 @@ if (type >= 0) return dns_lookup(dnsa, name, type, fully_qualified_name); if (type == T_MXH) return dns_lookup(dnsa, name, T_MX, fully_qualified_name); -/* Find nameservers for the domain or the nearest enclosing zone, excluding the +/* Find nameservers for the domain or the nearest enclosing zone, excluding the root servers. */ if (type == T_ZNS) @@ -665,10 +667,157 @@ if (type == T_ZNS) int rc = dns_lookup(dnsa, d, T_NS, fully_qualified_name); if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; while (*d != 0 && *d != '.') d++; - if (*d++ == 0) break; + if (*d++ == 0) break; + } + return DNS_NOMATCH; + } + +/* Try to look up the Client SMTP Authorization SRV record for the name. If +there isn't one, search from the top downwards for a CSA record in a parent +domain, which might be making assertions about subdomains. If we find a record +we set fully_qualified_name to whichever lookup succeeded, so that the caller +can tell whether to look at the explicit authorization field or the subdomain +assertion field. */ + +if (type == T_CSA) + { + uschar *srvname, *namesuff, *tld, *p; + int priority, weight, port; + int limit, rc, i; + BOOL ipv6; + dns_record *rr; + dns_scan dnss; + + DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); + + srvname = string_sprintf("_client._smtp.%s", name); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_SUCCEED || rc == DNS_AGAIN) + { + if (rc == DNS_SUCCEED) *fully_qualified_name = name; + return rc; } - return DNS_NOMATCH; - } + + /* Search for CSA subdomain assertion SRV records from the top downwards, + starting with the 2nd level domain. This order maximizes cache-friendliness. + We skip the top level domains to avoid loading their nameservers and because + we know they'll never have CSA SRV records. */ + + namesuff = Ustrrchr(name, '.'); + if (namesuff == NULL) return DNS_NOMATCH; + tld = namesuff + 1; + ipv6 = FALSE; + limit = dns_csa_search_limit; + + /* Use more appropriate search parameters if we are in the reverse DNS. */ + + if (strcmpic(namesuff, US".arpa") == 0) + { + if (namesuff - 8 > name && strcmpic(namesuff - 8, US".in-addr.arpa") == 0) + { + namesuff -= 8; + tld = namesuff + 1; + limit = 3; + } + else if (namesuff - 4 > name && strcmpic(namesuff - 4, US".ip6.arpa") == 0) + { + namesuff -= 4; + tld = namesuff + 1; + ipv6 = TRUE; + limit = 3; + } + } + + DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); + + /* Do not perform the search if the top level or 2nd level domains do not + exist. This is quite common, and when it occurs all the search queries would + go to the root or TLD name servers, which is not friendly. So we check the + AUTHORITY section; if it contains the root's SOA record or the TLD's SOA then + the TLD or the 2LD (respectively) doesn't exist and we can skip the search. + If the TLD and the 2LD exist but the explicit CSA record lookup failed, then + the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */ + + 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. */ + + HEADER *h = (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 = MAXPACKET; + + for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); + rr != NULL; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + if (rr->type != T_SOA) continue; + else if (strcmpic(rr->name, US"") == 0 || + strcmpic(rr->name, tld) == 0) return DNS_NOMATCH; + else break; + } + + for (i = 0; i < limit; i++) + { + if (ipv6) + { + /* Scan through the IPv6 reverse DNS in chunks of 16 bits worth of IP + address, i.e. 4 hex chars and 4 dots, i.e. 8 chars. */ + namesuff -= 8; + if (namesuff <= name) return DNS_NOMATCH; + } + else + /* Find the start of the preceding domain name label. */ + do + if (--namesuff <= name) return DNS_NOMATCH; + while (*namesuff != '.'); + + DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); + + srvname = string_sprintf("_client._smtp.%s", namesuff + 1); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_AGAIN) return rc; + if (rc != DNS_SUCCEED) continue; + + /* Check that the SRV record we have found is worth returning. We don't + just return the first one we find, because some lower level SRV record + might make stricter assertions than its parent domain. */ + + for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); + rr != NULL; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + { + if (rr->type != T_SRV) continue; + + /* Extract the numerical SRV fields (p is incremented) */ + p = rr->data; + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + + /* Check the CSA version number */ + if (priority != 1) continue; + + /* If it's making an interesting assertion, return this response. */ + if (port & 1) + { + *fully_qualified_name = namesuff + 1; + return DNS_SUCCEED; + } + } + } + return DNS_NOMATCH; + } /* Control should never reach here */