X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fdns.c;h=f1619f4a464122f46445c98aaf058891f0061dbb;hp=83daf50f39a902dac92071ff80a644e7c791d5da;hb=9d4319dfec653f43b64562c8f31b87f2890365b2;hpb=bef5a11fb38375ca2934201119d5adc604ddcfc5 diff --git a/src/src/dns.c b/src/src/dns.c index 83daf50f3..f1619f4a4 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/dns.c,v 1.10 2005/09/13 15:40:07 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -12,16 +10,6 @@ #include "exim.h" -/* Function declaration needed for mutual recursion when A6 records -are supported. */ - -#if HAVE_IPV6 -#ifdef SUPPORT_A6 -static void dns_complete_a6(dns_address ***, dns_answer *, dns_record *, - int, uschar *); -#endif -#endif - /************************************************* * Fake DNS resolver * @@ -29,17 +17,17 @@ static void dns_complete_a6(dns_address ***, dns_answer *, dns_record *, /* This function is called instead of res_search() when Exim is running in its test harness. It recognizes some special domain names, and uses them to force -failure and retry responses (optionally with a delay). It also recognises the -zones test.ex and 10.in-addr.arpa, and for those it calls an external utility -that mock-up a nameserver, if it can find the utility. Otherwise, it passes its -arguments on to res_search(). +failure and retry responses (optionally with a delay). Otherwise, it calls an +external utility that mocks-up a nameserver, if it can find the utility. +If not, it passes its arguments on to res_search(). The fake nameserver may +also return a code specifying that the name should be passed on. Background: the original test suite required a real nameserver to carry the -test.ex and 10.in-addr.arpa zones, whereas the new test suit has the fake -server for portability. This code supports both. +test zones, whereas the new test suit has the fake server for portability. This +code supports both. Arguments: - name the domain name + domain the domain name type the DNS record type answerptr where to put the answer size size of the answer area @@ -48,10 +36,26 @@ Returns: length of returned data, or -1 on error (h_errno set) */ static int -fakens_search(uschar *name, int type, uschar *answerptr, int size) +fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { -int len = Ustrlen(name); -uschar *endname = name + len; +int len = Ustrlen(domain); +int asize = size; /* Locally modified */ +uschar *endname; +uschar name[256]; +uschar utilname[256]; +uschar *aptr = answerptr; /* Locally modified */ +struct stat statbuf; + +/* Remove terminating dot. */ + +if (domain[len - 1] == '.') len--; +Ustrncpy(name, domain, len); +name[len] = 0; +endname = name + len; + +/* This code, for forcing TRY_AGAIN and NO_RECOVERY, is here so that it works +for the old test suite that uses a real nameserver. When the old test suite is +eventually abandoned, this code could be moved into the fakens utility. */ if (len >= 14 && Ustrcmp(endname - 14, "test.again.dns") == 0) { @@ -75,65 +79,62 @@ if (len >= 13 && Ustrcmp(endname - 13, "test.fail.dns") == 0) return -1; } -if (Ustrcmp(name, "test.ex") == 0 || - (len > 8 && Ustrcmp(endname - 8, ".test.ex") == 0) || - (len >= 16 && Ustrcmp(endname - 16, ".10.in-addr.arpa") == 0)) +/* Look for the fakens utility, and if it exists, call it. */ + +(void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens", + spool_directory); + +if (stat(CS utilname, &statbuf) >= 0) { - uschar utilname[256]; - struct stat statbuf; + pid_t pid; + 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)); + + argv[0] = utilname; + argv[1] = spool_directory; + argv[2] = name; + argv[3] = dns_text_type(type); + argv[4] = NULL; - (void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens", - spool_directory); + pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE); + if (pid < 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s", + strerror(errno)); - if (stat(CS utilname, &statbuf) >= 0) + len = 0; + rc = -1; + while (asize > 0 && (rc = read(outfd, aptr, asize)) > 0) { - pid_t pid; - 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)); - - argv[0] = utilname; - argv[1] = spool_directory; - argv[2] = name; - argv[3] = dns_text_type(type); - argv[4] = NULL; - - pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE); - if (pid < 0) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s", - strerror(errno)); - - len = 0; - rc = -1; - while (size > 0 && (rc = read(outfd, answerptr, size)) > 0) - { - len += rc; - answerptr += rc; - size -= rc; - } + len += rc; + aptr += rc; /* Don't modify the actual arguments, because they */ + asize -= rc; /* may need to be passed on to res_search(). */ + } - if (rc < 0) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s", - strerror(errno)); + if (rc < 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s", + strerror(errno)); - switch(child_close(pid, 0)) - { - case 0: return len; - case 1: h_errno = HOST_NOT_FOUND; break; - case 2: h_errno = TRY_AGAIN; break; - default: - case 3: h_errno = NO_RECOVERY; break; - case 4: h_errno = NO_DATA; break; - } - return -1; + switch(child_close(pid, 0)) + { + case 0: return len; + case 1: h_errno = HOST_NOT_FOUND; return -1; + case 2: h_errno = TRY_AGAIN; return -1; + default: + case 3: h_errno = NO_RECOVERY; return -1; + case 4: h_errno = NO_DATA; return -1; + case 5: /* Pass on to res_search() */ + DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n"); } } -/* Not test.ex or 10.in-addr.arpa, or fakens utility not found. */ +/* fakens utility not found, or it returned "pass on" */ + +DEBUG(D_dns) debug_printf("passing %s on to res_search()\n", domain); -return res_search(CS name, C_IN, type, answerptr, size); +return res_search(CS domain, C_IN, type, answerptr, size); } @@ -148,25 +149,85 @@ the first time we have been here, and set the resolver options. Arguments: qualify_single TRUE to set the RES_DEFNAMES option search_parents TRUE to set the RES_DNSRCH option + use_dnssec TRUE to set the RES_USE_DNSSEC option Returns: nothing */ void -dns_init(BOOL qualify_single, BOOL search_parents) +dns_init(BOOL qualify_single, BOOL search_parents, BOOL use_dnssec) { -if ((_res.options & RES_INIT) == 0) +res_state resp = os_get_dns_resolver_res(); + +if ((resp->options & RES_INIT) == 0) { - DEBUG(D_resolver) _res.options |= RES_DEBUG; /* For Cygwin */ + DEBUG(D_resolver) resp->options |= RES_DEBUG; /* For Cygwin */ + os_put_dns_resolver_res(resp); res_init(); - DEBUG(D_resolver) _res.options |= RES_DEBUG; + DEBUG(D_resolver) resp->options |= RES_DEBUG; + os_put_dns_resolver_res(resp); } -_res.options &= ~(RES_DNSRCH | RES_DEFNAMES); -_res.options |= (qualify_single? RES_DEFNAMES : 0) | +resp->options &= ~(RES_DNSRCH | RES_DEFNAMES); +resp->options |= (qualify_single? RES_DEFNAMES : 0) | (search_parents? RES_DNSRCH : 0); -if (dns_retrans > 0) _res.retrans = dns_retrans; -if (dns_retry > 0) _res.retry = dns_retry; +if (dns_retrans > 0) resp->retrans = dns_retrans; +if (dns_retry > 0) resp->retry = dns_retry; + +#ifdef RES_USE_EDNS0 +if (dns_use_edns0 >= 0) + { + if (dns_use_edns0) + resp->options |= RES_USE_EDNS0; + else + resp->options &= ~RES_USE_EDNS0; + DEBUG(D_resolver) + debug_printf("Coerced resolver EDNS0 support %s.\n", + dns_use_edns0 ? "on" : "off"); + } +#else +if (dns_use_edns0 >= 0) + DEBUG(D_resolver) + debug_printf("Unable to %sset EDNS0 without resolver support.\n", + dns_use_edns0 ? "" : "un"); +#endif + +#ifndef DISABLE_DNSSEC +# ifdef RES_USE_DNSSEC +# ifndef RES_USE_EDNS0 +# error Have RES_USE_DNSSEC but not RES_USE_EDNS0? Something hinky ... +# endif +if (use_dnssec) + resp->options |= RES_USE_DNSSEC; +if (dns_dnssec_ok >= 0) + { + if (dns_use_edns0 == 0 && dns_dnssec_ok != 0) + { + DEBUG(D_resolver) + debug_printf("CONFLICT: dns_use_edns0 forced false, dns_dnssec_ok forced true, ignoring latter!\n"); + } + else + { + if (dns_dnssec_ok) + resp->options |= RES_USE_DNSSEC; + else + resp->options &= ~RES_USE_DNSSEC; + DEBUG(D_resolver) debug_printf("Coerced resolver DNSSEC support %s.\n", + dns_dnssec_ok ? "on" : "off"); + } + } +# else +if (dns_dnssec_ok >= 0) + DEBUG(D_resolver) + debug_printf("Unable to %sset DNSSEC without resolver support.\n", + dns_dnssec_ok ? "" : "un"); +if (use_dnssec) + DEBUG(D_resolver) + debug_printf("Unable to set DNSSEC without resolver support.\n"); +# endif +#endif /* DISABLE_DNSSEC */ + +os_put_dns_resolver_res(resp); } @@ -186,9 +247,9 @@ Returns: nothing */ void -dns_build_reverse(uschar *string, uschar *buffer) +dns_build_reverse(const uschar *string, uschar *buffer) { -uschar *p = string + Ustrlen(string); +const uschar *p = string + Ustrlen(string); uschar *pp = buffer; /* Handle IPv4 address */ @@ -200,7 +261,7 @@ if (Ustrchr(string, ':') == NULL) int i; for (i = 0; i < 4; i++) { - uschar *ppp = p; + const uschar *ppp = p; while (ppp > string && ppp[-1] != '.') ppp--; Ustrncpy(pp, ppp, p - ppp); pp += p - ppp; @@ -359,6 +420,41 @@ return &(dnss->srr); +/************************************************* +* Return whether AD bit set in DNS result * +*************************************************/ + +/* 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) + +Argument: pointer to dns answer block +Returns: bool indicating presence of AD bit +*/ + +BOOL +dns_is_secure(const dns_answer * dnsa) +{ +#ifdef DISABLE_DNSSEC +DEBUG(D_dns) + debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n"); +return FALSE; +#else +HEADER *h = (HEADER *)dnsa->answer; +return h->ad ? TRUE : FALSE; +#endif +} + +static void +dns_set_insecure(dns_answer * dnsa) +{ +HEADER * h = (HEADER *)dnsa->answer; +h->ad = 0; +} + + + + /************************************************* * Turn DNS type into text * *************************************************/ @@ -380,11 +476,13 @@ switch(t) case T_AAAA: return US"AAAA"; case T_A6: return US"A6"; case T_TXT: return US"TXT"; + case T_SPF: return US"SPF"; case T_PTR: return US"PTR"; case T_SOA: return US"SOA"; case T_SRV: return US"SRV"; case T_NS: return US"NS"; case T_CNAME: return US"CNAME"; + case T_TLSA: return US"TLSA"; default: return US"?"; } } @@ -409,11 +507,12 @@ Returns: the return code */ static int -dns_return(uschar *name, int type, int rc) +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), - _res.options); + resp->options); node->data.val = rc; (void)tree_insertnode(&tree_dns_fails, node); return rc; @@ -440,18 +539,20 @@ Arguments: Returns: DNS_SUCCEED successful lookup DNS_NOMATCH name not found (NXDOMAIN) or name contains illegal characters (if checking) + or name is an IP address (for IP address lookup) DNS_NODATA domain exists, but no data for this type (NODATA) DNS_AGAIN soft failure, try again later DNS_FAIL DNS failure */ int -dns_basic_lookup(dns_answer *dnsa, uschar *name, int type) +dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type) { -int rc = -1; #ifndef STAND_ALONE -uschar *save; +int rc = -1; +const uschar *save_domain; #endif +res_state resp = os_get_dns_resolver_res(); tree_node *previous; uschar node_name[290]; @@ -462,7 +563,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), - _res.options); + resp->options); previous = tree_search(tree_dns_fails, node_name); if (previous != NULL) { @@ -475,6 +576,23 @@ if (previous != NULL) return previous->data.val; } +#ifdef EXPERIMENTAL_INTERNATIONAL +/* Convert all names to a-label form before doing lookup */ + { + uschar * alabel; + uschar * errstr = NULL; + if ((alabel = string_domain_utf8_to_alabel(name, &errstr)), errstr) + { + DEBUG(D_dns) + debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s", name, + errstr); + host_find_failed_syntax = TRUE; + return DNS_NOMATCH; + } + name = alabel; + } +#endif + /* If configured, check the hygene 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 @@ -491,25 +609,23 @@ For SRV records, we omit the initial _smtp._tcp. components at the start. */ #ifndef STAND_ALONE /* Omit this for stand-alone tests */ -if (check_dns_names_pattern[0] != 0 && type != T_PTR) +if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) { - uschar *checkname = name; + const uschar *checkname = name; int ovector[3*(EXPAND_MAXN+1)]; - if (regex_check_dns_names == NULL) - regex_check_dns_names = - regex_must_compile(check_dns_names_pattern, FALSE, TRUE); + dns_pattern_init(); /* For an SRV lookup, skip over the first two components (the service and protocol names, which both start with an underscore). */ - if (type == T_SRV) + if (type == T_SRV || type == T_TLSA) { while (*checkname++ != '.'); while (*checkname++ != '.'); } - if (pcre_exec(regex_check_dns_names, NULL, CS checkname, Ustrlen(checkname), + if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname), 0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0) { DEBUG(D_dns) @@ -526,16 +642,28 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR) number of bytes the message would need, so we need to check for this case. The effect is to truncate overlong data. -If we are running in the test harness, instead of calling the normal resolver +On some systems, res_search() will recognize "A-for-A" queries and return +the IP address instead of returning -1 with h_error=HOST_NOT_FOUND. Some +nameservers are also believed to do this. It is, of course, contrary to the +specification of the DNS, so we lock it out. */ + +if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0) + return DNS_NOMATCH; + +/* If we are running in the test harness, instead of calling the normal resolver (res_search), we call fakens_search(), which recognizes certain special domains, and interfaces to a fake nameserver for certain special zones. */ -if (running_in_test_harness) - dnsa->answerlen = fakens_search(name, type, dnsa->answer, MAXPACKET); -else - dnsa->answerlen = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET); +dnsa->answerlen = running_in_test_harness + ? fakens_search(name, type, dnsa->answer, MAXPACKET) + : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET); -if (dnsa->answerlen > MAXPACKET) dnsa->answerlen = MAXPACKET; +if (dnsa->answerlen > MAXPACKET) + { + 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; + } if (dnsa->answerlen < 0) switch (h_errno) { @@ -549,12 +677,12 @@ if (dnsa->answerlen < 0) switch (h_errno) name, dns_text_type(type)); /* Cut this out for various test programs */ - #ifndef STAND_ALONE - save = deliver_domain; - deliver_domain = name; /* set $domain */ - rc = match_isinlist(name, &dns_again_means_nonexist, 0, NULL, NULL, +#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; + deliver_domain = save_domain; if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); @@ -564,9 +692,9 @@ if (dnsa->answerlen < 0) switch (h_errno) "DNS_NOMATCH\n", name); return dns_return(name, type, DNS_NOMATCH); - #else /* For stand-alone tests */ +#else /* For stand-alone tests */ return dns_return(name, type, DNS_AGAIN); - #endif +#endif case NO_RECOVERY: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" @@ -627,10 +755,12 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_lookup(dns_answer *dnsa, uschar *name, int type, uschar **fully_qualified_name) +dns_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { int i; -uschar *orig_name = name; +const uschar *orig_name = name; +BOOL secure_so_far = TRUE; /* Loop to follow CNAME chains so far, but no further... */ @@ -664,12 +794,10 @@ for (i = 0; i < 10; i++) else if (rr->type == T_CNAME) cname_rr = *rr; } - /* If a CNAME was found, take the fully qualified name from it; otherwise - from the first data record, if present. For testing, there is a magic name - that gets its casing adjusted, because my resolver doesn't seem to pass back - upper case letters in domain names. */ + /* For the first time round this loop, if a CNAME was found, take the fully + qualified name from it; otherwise from the first data record, if present. */ - if (fully_qualified_name != NULL) + if (i == 0 && fully_qualified_name != NULL) { if (cname_rr.data != NULL) { @@ -679,21 +807,20 @@ for (i = 0; i < 10; i++) } else if (type_rr.data != NULL) { - if (running_in_test_harness && - Ustrcmp(type_rr.name, "uppercase.test.ex") == 0) - *fully_qualified_name = US"UpperCase.test.ex"; - else - { - if (Ustrcmp(type_rr.name, *fully_qualified_name) != 0 && - type_rr.name[0] != '*') - *fully_qualified_name = string_copy_dnsdomain(type_rr.name); - } + if (Ustrcmp(type_rr.name, *fully_qualified_name) != 0 && + type_rr.name[0] != '*') + *fully_qualified_name = string_copy_dnsdomain(type_rr.name); } } /* If any data records of the correct type were found, we are done. */ - if (type_rr.data != NULL) return DNS_SUCCEED; + if (type_rr.data != NULL) + { + if (!secure_so_far) /* mark insecure if any element of CNAME chain was */ + dns_set_insecure(dnsa); + return DNS_SUCCEED; + } /* If there are no data records, we need to re-scan the DNS using the domain given in the CNAME record, which should exist (otherwise we should @@ -702,9 +829,14 @@ for (i = 0; i < 10; i++) if (cname_rr.data == NULL) return DNS_FAIL; datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256); + cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data)); if (datalen < 0) return DNS_FAIL; name = data; + + if (!dns_is_secure(dnsa)) + secure_so_far = FALSE; + + DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name); } /* Loop back to do another lookup */ /*Control reaches here after 10 times round the CNAME loop. Something isn't @@ -742,8 +874,8 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_special_lookup(dns_answer *dnsa, uschar *name, int type, - uschar **fully_qualified_name) +dns_special_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { if (type >= 0) return dns_lookup(dnsa, name, type, fully_qualified_name); @@ -756,7 +888,7 @@ root servers. */ if (type == T_ZNS) { - uschar *d = name; + const uschar *d = name; while (d != 0) { int rc = dns_lookup(dnsa, d, T_NS, fully_qualified_name); @@ -789,7 +921,7 @@ if (type == T_CSA) rc = dns_lookup(dnsa, srvname, T_SRV, NULL); if (rc == DNS_SUCCEED || rc == DNS_AGAIN) { - if (rc == DNS_SUCCEED) *fully_qualified_name = name; + if (rc == DNS_SUCCEED) *fully_qualified_name = string_copy(name); return rc; } @@ -897,7 +1029,7 @@ if (type == T_CSA) /* Extract the numerical SRV fields (p is incremented) */ p = rr->data; GETSHORT(priority, p); - GETSHORT(weight, p); + GETSHORT(weight, p); weight = weight; /* compiler quietening */ GETSHORT(port, p); /* Check the CSA version number */ @@ -921,164 +1053,6 @@ return DNS_FAIL; -/* Support for A6 records has been commented out since they were demoted to -experimental status at IETF 51. */ - -#if HAVE_IPV6 && defined(SUPPORT_A6) - -/************************************************* -* Search DNS block for prefix RRs * -*************************************************/ - -/* Called from dns_complete_a6() to search an additional section or a main -answer section for required prefix records to complete an IPv6 address obtained -from an A6 record. For each prefix record, a recursive call to dns_complete_a6 -is made, with a new copy of the address so far. - -Arguments: - dnsa the DNS answer block - which RESET_ADDITIONAL or RESET_ANSWERS - name name of prefix record - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - bits number of bits we have already got - bitvec the bits we have already got - -Returns: TRUE if any records were found -*/ - -static BOOL -dns_find_prefix(dns_answer *dnsa, int which, uschar *name, dns_address - ***yptrptr, int bits, uschar *bitvec) -{ -BOOL yield = FALSE; -dns_record *rr; -dns_scan dnss; - -for (rr = dns_next_rr(dnsa, &dnss, which); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - { - uschar cbitvec[16]; - if (rr->type != T_A6 || strcmpic(rr->name, name) != 0) continue; - yield = TRUE; - memcpy(cbitvec, bitvec, sizeof(cbitvec)); - dns_complete_a6(yptrptr, dnsa, rr, bits, cbitvec); - } - -return yield; -} - - - -/************************************************* -* Follow chains of A6 records * -*************************************************/ - -/* A6 records may be incomplete, with pointers to other records containing more -bits of the address. There can be a tree structure, leading to a number of -addresses originating from a single initial A6 record. - -Arguments: - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - dnsa the current DNS answer block - rr the RR we have at present - bits number of bits we have already got - bitvec the bits we have already got - -Returns: nothing -*/ - -static void -dns_complete_a6(dns_address ***yptrptr, dns_answer *dnsa, dns_record *rr, - int bits, uschar *bitvec) -{ -static uschar bitmask[] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 }; -uschar *p = (uschar *)(rr->data); -int prefix_len, suffix_len; -int i, j, k; -uschar *chainptr; -uschar chain[264]; -dns_answer cdnsa; - -/* The prefix length is the first byte. It defines the prefix which is missing -from the data in this record as a number of bits. Zero means this is the end of -a chain. The suffix is the data in this record; only sufficient bytes to hold -it are supplied. There may be zero bytes. We have to ignore trailing bits that -we have already obtained from earlier RRs in the chain. */ - -prefix_len = *p++; /* bits */ -suffix_len = (128 - prefix_len + 7)/8; /* bytes */ - -/* If the prefix in this record is greater than the prefix in the previous -record in the chain, we have to ignore the record (RFC 2874). */ - -if (prefix_len > 128 - bits) return; - -/* In this little loop, the number of bits up to and including the current byte -is held in k. If we have none of the bits in this byte, we can just or it into -the current data. If we have all of the bits in this byte, we skip it. -Otherwise, some masking has to be done. */ - -for (i = suffix_len - 1, j = 15, k = 8; i >= 0; i--) - { - int required = k - bits; - if (required >= 8) bitvec[j] |= p[i]; - else if (required > 0) bitvec[j] |= p[i] & bitmask[required]; - j--; /* I tried putting these in the "for" statement, but gcc muttered */ - k += 8; /* about computed values not being used. */ - } - -/* If the prefix_length is zero, we are at the end of a chain. Build a -dns_address item with the current data, hang it onto the end of the chain, -adjust the hanging pointer, and we are done. */ - -if (prefix_len == 0) - { - dns_address *new = store_get(sizeof(dns_address) + 50); - inet_ntop(AF_INET6, bitvec, CS new->address, 50); - new->next = NULL; - **yptrptr = new; - *yptrptr = &(new->next); - return; - } - -/* Prefix length is not zero. Reset the number of bits that we have collected -so far, and extract the chain name. */ - -bits = 128 - prefix_len; -p += suffix_len; - -chainptr = chain; -while ((i = *p++) != 0) - { - if (chainptr != chain) *chainptr++ = '.'; - memcpy(chainptr, p, i); - chainptr += i; - p += i; - } -*chainptr = 0; -chainptr = chain; - -/* Now scan the current DNS response record to see if the additional section -contains the records we want. This processing can be cut out for testing -purposes. */ - -if (dns_find_prefix(dnsa, RESET_ADDITIONAL, chainptr, yptrptr, bits, bitvec)) - return; - -/* No chain records were found in the current DNS response block. Do a new DNS -lookup to try to find these records. This opens up the possibility of DNS -failures. We ignore them at this point; if all branches of the tree fail, there -will be no addresses at the end. */ - -if (dns_lookup(&cdnsa, chainptr, T_A6, NULL) == DNS_SUCCEED) - (void)dns_find_prefix(&cdnsa, RESET_ANSWERS, chainptr, yptrptr, bits, bitvec); -} -#endif /* HAVE_IPV6 && defined(SUPPORT_A6) */ - - /************************************************* @@ -1103,12 +1077,7 @@ dns_address_from_rr(dns_answer *dnsa, dns_record *rr) { dns_address *yield = NULL; -#if HAVE_IPV6 && defined(SUPPORT_A6) -dns_address **yieldptr = &yield; -uschar bitvec[16]; -#else dnsa = dnsa; /* Stop picky compilers warning */ -#endif if (rr->type == T_A) { @@ -1120,14 +1089,6 @@ if (rr->type == T_A) #if HAVE_IPV6 -#ifdef SUPPORT_A6 -else if (rr->type == T_A6) - { - memset(bitvec, 0, sizeof(bitvec)); - dns_complete_a6(&yieldptr, dnsa, rr, 0, bitvec); - } -#endif /* SUPPORT_A6 */ - else { yield = store_get(sizeof(dns_address) + 50); @@ -1139,4 +1100,16 @@ else return yield; } + + +void +dns_pattern_init(void) +{ +if (check_dns_names_pattern[0] != 0 && !regex_check_dns_names) + regex_check_dns_names = + regex_must_compile(check_dns_names_pattern, FALSE, TRUE); +} + +/* vi: aw ai sw=2 +*/ /* End of dns.c */