Minor comment tweaks from Bryan Henderson for Configure-Makefile and
[exim.git] / src / src / dns.c
index f322fafca1679efc8502973db3a9d63a06b651d0..7395854ff9f17db3ac8be1bff26b4a413f2cfbb1 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/dns.c,v 1.8 2005/06/10 13:38:06 tom Exp $ */
+/* $Cambridge: exim/src/src/dns.c,v 1.11 2005/09/16 14:44:11 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -23,6 +23,129 @@ static void dns_complete_a6(dns_address ***, dns_answer *, dns_record *,
 #endif
 
 
+/*************************************************
+*               Fake DNS resolver                *
+*************************************************/
+
+/* 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().
+
+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
+code supports both.
+
+Arguments:
+  domain      the domain name
+  type        the DNS record type
+  answerptr   where to put the answer
+  size        size of the answer area
+
+Returns:      length of returned data, or -1 on error (h_errno set)
+*/
+
+static int
+fakens_search(uschar *domain, int type, uschar *answerptr, int size)
+{
+int len = Ustrlen(domain);
+uschar *endname;
+uschar name[256];
+
+if (domain[len - 1] == '.') len--;
+Ustrncpy(name, domain, len);
+name[len] = 0;
+endname = name + len;
+
+if (len >= 14 && Ustrcmp(endname - 14, "test.again.dns") == 0)
+  {
+  int delay = Uatoi(name);  /* digits at the start of the name */
+  DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n",
+    name, dns_text_type(type));
+  if (delay > 0)
+    {
+    DEBUG(D_dns) debug_printf("delaying %d seconds\n", delay);
+    sleep(delay);
+    }
+  h_errno = TRY_AGAIN;
+  return -1;
+  }
+
+if (len >= 13 && Ustrcmp(endname - 13, "test.fail.dns") == 0)
+  {
+  DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n",
+    name, dns_text_type(type));
+  h_errno = NO_RECOVERY;
+  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))
+  {
+  uschar utilname[256];
+  struct stat statbuf;
+
+  (void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens",
+    spool_directory);
+
+  if (stat(CS utilname, &statbuf) >= 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;
+      }
+
+    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;
+    }
+  }
+
+/* Not test.ex or 10.in-addr.arpa, or fakens utility not found. */
+
+DEBUG(D_dns) debug_printf("passing %s on to res_search\n", domain);
+
+return res_search(CS domain, C_IN, type, answerptr, size);
+}
+
+
 
 /*************************************************
 *        Initialize and configure resolver       *
@@ -249,7 +372,8 @@ return &(dnss->srr);
 *            Turn DNS type into text             *
 *************************************************/
 
-/* Turn the coded record type into a string for printing.
+/* Turn the coded record type into a string for printing. All those that Exim
+uses should be included here.
 
 Argument:   record type
 Returns:    pointer to string
@@ -266,6 +390,7 @@ switch(t)
   case T_A6:    return US"A6";
   case T_TXT:   return US"TXT";
   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";
@@ -332,8 +457,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;
 uschar *save;
 #endif
 
@@ -359,35 +484,6 @@ if (previous != NULL)
   return previous->data.val;
   }
 
-/* If we are running in the test harness, recognize a couple of special
-names that always give error returns. This makes it straightforward to
-test the handling of DNS errors. */
-
-if (running_in_test_harness)
-  {
-  uschar *endname = name + Ustrlen(name);
-  if (Ustrcmp(endname - 14, "test.again.dns") == 0)
-    {
-    int delay = Uatoi(name);  /* digits at the start of the name */
-    DEBUG(D_dns) debug_printf("Real DNS lookup of %s (%s) bypassed for testing\n",
-      name, dns_text_type(type));
-    if (delay > 0)
-      {
-      DEBUG(D_dns) debug_printf("delaying %d seconds\n", delay);
-      sleep(delay);
-      }
-    DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n");
-    return dns_return(name, type, DNS_AGAIN);
-    }
-  if (Ustrcmp(endname - 13, "test.fail.dns") == 0)
-    {
-    DEBUG(D_dns) debug_printf("Real DNS lookup of %s (%s) bypassed for testing\n",
-      name, dns_text_type(type));
-    DEBUG(D_dns) debug_printf("returning DNS_FAIL\n");
-    return dns_return(name, type, DNS_FAIL);
-    }
-  }
-
 /* 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
@@ -436,10 +532,18 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR)
 #endif /* STAND_ALONE */
 
 /* Call the resolver; for an overlong response, res_search() will return the
-number of bytes the message would need, so we need to check for this case.
-The effect is to truncate overlong data. */
+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
+(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 = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET);
 if (dnsa->answerlen > MAXPACKET) dnsa->answerlen = MAXPACKET;
 
 if (dnsa->answerlen < 0) switch (h_errno)