X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/bef5a11fb38375ca2934201119d5adc604ddcfc5..0a49a7a4f1090b6f1ce1d0f9d969804c9226b53e:/src/src/dns.c diff --git a/src/src/dns.c b/src/src/dns.c index 83daf50f3..dcafdb84a 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/dns.c,v 1.10 2005/09/13 15:40:07 ph10 Exp $ */ +/* $Cambridge: exim/src/src/dns.c,v 1.21 2009/11/16 19:50:36 nm4 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -29,17 +29,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 +48,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(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 +91,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); } @@ -440,6 +453,7 @@ 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 @@ -448,8 +462,8 @@ Returns: DNS_SUCCEED successful lookup int dns_basic_lookup(dns_answer *dnsa, uschar *name, int type) { -int rc = -1; #ifndef STAND_ALONE +int rc = -1; uschar *save; #endif @@ -491,7 +505,7 @@ 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; int ovector[3*(EXPAND_MAXN+1)]; @@ -526,7 +540,20 @@ 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 (( + #ifdef SUPPORT_A6 + type == T_A6 || + #endif + 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. */ @@ -535,7 +562,12 @@ if (running_in_test_harness) else dnsa->answerlen = res_search(CS 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) { @@ -664,12 +696,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,15 +709,9 @@ 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); } } @@ -705,6 +729,8 @@ for (i = 0; i < 10; i++) cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256); if (datalen < 0) return DNS_FAIL; name = data; + + 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