Improved the '($parent) =' pattern match (Daniel Tiefnig)
[exim.git] / src / src / dns.c
index 7395854ff9f17db3ac8be1bff26b4a413f2cfbb1..0b1d59d2006f709988fc064201657369e2874a16 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/dns.c,v 1.11 2005/09/16 14:44:11 ph10 Exp $ */
+/* $Cambridge: exim/src/src/dns.c,v 1.17 2007/01/08 10:50:18 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for interfacing with the DNS. */
@@ -29,10 +29,10 @@ 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, 10.in-addr.arpa, and 0.8.e.f.ip6.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 zones, whereas the new test suit has the fake server for portability. This
@@ -51,14 +51,24 @@ static int
 fakens_search(uschar *domain, int type, uschar *answerptr, int size)
 {
 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)
   {
   int delay = Uatoi(name);  /* digits at the start of the name */
@@ -81,66 +91,60 @@ 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) ||
-    (len >= 17 && Ustrcmp(endname - 17, ".0.8.e.f.ip6.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];
 
-  (void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens",
-    spool_directory);
+  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n",
+    name, dns_text_type(type));
 
-  if (stat(CS utilname, &statbuf) >= 0)
+  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 (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);
+DEBUG(D_dns) debug_printf("passing %s on to res_search()\n", domain);
 
 return res_search(CS domain, C_IN, type, answerptr, size);
 }
@@ -449,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
@@ -535,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. */
 
@@ -673,12 +691,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)
       {
@@ -688,15 +704,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);
       }
     }
 
@@ -714,6 +724,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