(1) Last-minute sieve patch (updates to latest spec).
[exim.git] / src / src / host.c
index 0acafd8a83f97c262f9bfefc84bedc9237979a0d..c2a44a4313c9790f6b3bc5654667f6473941c2ee 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/host.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/src/src/host.c,v 1.9 2005/02/17 11:58:26 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2005 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or
@@ -90,6 +90,48 @@ return (unsigned int)(random_seed >> 16) % limit;
 
 
 
+/*************************************************
+*         Sort addresses when testing            *
+*************************************************/
+
+/* This function is called only when running in the test harness. It sorts a
+number of multihomed host IP addresses into the order, so as to get
+repeatability. This doesn't have to be efficient. But don't interchange IPv4
+and IPv6 addresses!
+
+Arguments:
+  host        -> the first host item
+  last        -> the last host item
+
+Returns:      nothing
+*/
+
+static void
+sort_addresses(host_item *host, host_item *last)
+{
+BOOL done = FALSE;
+while (!done)
+  {
+  host_item *h;
+  done = TRUE;
+  for (h = host; h != last; h = h->next)
+    {
+    if ((Ustrchr(h->address, ':') == NULL) !=
+        (Ustrchr(h->next->address, ':') == NULL))
+      continue;
+    if (Ustrcmp(h->address, h->next->address) > 0)
+      {
+      uschar *temp = h->address;
+      h->address = h->next->address;
+      h->next->address = temp;
+      done = FALSE;
+      }
+    }
+  }
+}
+
+
+
 /*************************************************
 *       Build chain of host items from list      *
 *************************************************/
@@ -451,7 +493,7 @@ ip_address_item *next;
 while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
   {
   int port = host_extract_port(s);            /* Leaves just the IP address */
-  if (!string_is_ip_address(s, NULL))
+  if (string_is_ip_address(s, NULL) == 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Malformed IP address \"%s\" in %s",
       s, name);
 
@@ -693,9 +735,10 @@ host_aton(uschar *address, int *bin)
 int x[4];
 int v4offset = 0;
 
-/* Handle IPv6 address, which may end with an IPv4 address. This code is NOT
-enclosed in #if HAVE_IPV6 in order that IPv6 addresses are recognized even if
-IPv6 is not supported. */
+/* Handle IPv6 address, which may end with an IPv4 address. It may also end
+with a "scope", introduced by a percent sign. This code is NOT enclosed in #if
+HAVE_IPV6 in order that IPv6 addresses are recognized even if IPv6 is not
+supported. */
 
 if (Ustrchr(address, ':') != NULL)
   {
@@ -712,12 +755,18 @@ if (Ustrchr(address, ':') != NULL)
 
   if (*p == ':') p++;
 
-  /* Split the address into components separated by colons. */
+  /* Split the address into components separated by colons. The input address
+  is supposed to be checked for syntax. There was a case where this was
+  overlooked; to guard against that happening again, check here and crash if
+  there are too many components. */
 
-  while (*p != 0)
+  while (*p != 0 && *p != '%')
     {
-    int len = Ustrcspn(p, ":");
+    int len = Ustrcspn(p, ":%");
     if (len == 0) nulloffset = ci;
+    if (ci > 7) log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+      "Internal error: invalid IPv6 address \"%s\" passed to host_aton()",
+      address);
     component[ci++] = p;
     p += len;
     if (*p == ':') p++;
@@ -814,22 +863,24 @@ for (i = 0; i < count; i++)
 /* We can't use host_ntoa() because it assumes the binary values are in network
 byte order, and these are the result of host_aton(), which puts them in ints in
 host byte order. Also, we really want IPv6 addresses to be in a canonical
-format, so we output them with no abbreviation. However, we can't use the
-normal colon separator in them because it terminates keys in lsearch files, so
-use dot instead.
+format, so we output them with no abbreviation. In a number of cases we can't
+use the normal colon separator in them because it terminates keys in lsearch
+files, so we want to use dot instead. There's an argument that specifies what
+to use for IPv6 addresses.
 
 Arguments:
   count       1 or 4 (number of ints)
   binary      points to the ints
   mask        mask value; if < 0 don't add to result
   buffer      big enough to hold the result
+  sep         component separator character for IPv6 addresses
 
 Returns:      the number of characters placed in buffer, not counting
               the final nul.
 */
 
 int
-host_nmtoa(int count, int *binary, int mask, uschar *buffer)
+host_nmtoa(int count, int *binary, int mask, uschar *buffer, int sep)
 {
 int i, j;
 uschar *tt = buffer;
@@ -848,12 +899,12 @@ else
   for (i = 0; i < 4; i++)
     {
     j = binary[i];
-    sprintf(CS tt, "%04x.%04x.", (j >> 16) & 0xffff, j & 0xffff);
+    sprintf(CS tt, "%04x%c%04x%c", (j >> 16) & 0xffff, sep, j & 0xffff, sep);
     while (*tt) tt++;
     }
   }
 
-tt--;   /* lose final . */
+tt--;   /* lose final separator */
 
 if (mask < 0)
   *tt = 0;
@@ -1285,9 +1336,11 @@ Returns:      OK on success, the answer being placed in the global variable
 
 The variable host_lookup_msg is set to an empty string on sucess, or to a
 reason for the failure otherwise, in a form suitable for tagging onto an error
-message, and also host_lookup_failed is set TRUE if the lookup failed. Any
-dynamically constructed string for host_lookup_msg must be in permanent store,
-because it might be used for several incoming messages on the same SMTP
+message, and also host_lookup_failed is set TRUE if the lookup failed. If there
+was a defer, host_lookup_deferred is set TRUE.
+
+Any dynamically constructed string for host_lookup_msg must be in permanent
+store, because it might be used for several incoming messages on the same SMTP
 connection. */
 
 int
@@ -1304,6 +1357,8 @@ dns_record *rr;
 dns_answer dnsa;
 dns_scan dnss;
 
+host_lookup_deferred = host_lookup_failed = FALSE;
+
 HDEBUG(D_host_lookup)
   debug_printf("looking up host name for %s\n", sender_host_address);
 
@@ -1315,6 +1370,7 @@ if (running_in_test_harness &&
   {
   HDEBUG(D_host_lookup)
     debug_printf("Test harness: host name lookup returns DEFER\n");
+  host_lookup_deferred = TRUE;
   return DEFER;
   }
 
@@ -1404,6 +1460,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
       {
       HDEBUG(D_host_lookup)
         debug_printf("IP address PTR lookup gave temporary error\n");
+      host_lookup_deferred = TRUE;
       return DEFER;
       }
     }
@@ -1414,9 +1471,12 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
     {
     HDEBUG(D_host_lookup)
       debug_printf("IP address lookup using gethostbyaddr()\n");
-
     rc = host_name_lookup_byaddr();
-    if (rc == DEFER) return rc;        /* Can't carry on */
+    if (rc == DEFER)
+      {
+      host_lookup_deferred = TRUE;
+      return rc;                       /* Can't carry on */
+      }
     if (rc == OK) break;               /* Found a name */
     }
   }      /* Loop for bydns/byaddr scanning */
@@ -1430,8 +1490,7 @@ if (sender_host_name == NULL)
     log_write(L_host_lookup_failed, LOG_MAIN, "no host name found for IP "
       "address %s", sender_host_address);
   host_lookup_msg = US" (failed to find host name from IP address)";
-
-host_lookup_failed = TRUE;
+  host_lookup_failed = TRUE;
   return FAIL;
   }
 
@@ -1519,6 +1578,7 @@ for (hname = sender_host_name; hname != NULL; hname = *aliases++)
   else if (rc == HOST_FIND_AGAIN)
     {
     HDEBUG(D_host_lookup) debug_printf("temporary error for host name lookup\n");
+    host_lookup_deferred = TRUE;
     return DEFER;
     }
   else
@@ -1563,7 +1623,6 @@ store_pool = POOL_PERM;
 host_lookup_msg = string_sprintf(" (%s does not match any IP address for %s)",
   sender_host_address, save_hostname);
 store_pool = old_pool;
-
 host_lookup_failed = TRUE;
 return FAIL;
 }
@@ -1613,6 +1672,19 @@ int i, yield, times;
 uschar **addrlist;
 host_item *last = NULL;
 BOOL temp_error = FALSE;
+#if HAVE_IPV6
+int af;
+#endif
+
+/* If we are in the test harness, a name ending in .test.again.dns always
+forces a temporary error response. */
+
+if (running_in_test_harness)
+  {
+  uschar *endname = host->name + Ustrlen(host->name);
+  if (Ustrcmp(endname - 14, "test.again.dns") == 0)
+    return HOST_FIND_AGAIN;
+  }
 
 /* In an IPv6 world, we need to scan for both kinds of address, so go round the
 loop twice. Note that we have ensured that AF_INET6 is defined even in an IPv4
@@ -1621,8 +1693,6 @@ matches the domain, we also just do IPv4 lookups here (except when testing
 standalone). */
 
 #if HAVE_IPV6
-  int af;
-
   #ifndef STAND_ALONE
   if (dns_ipv4_lookup != NULL &&
         match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN,
@@ -1791,31 +1861,9 @@ yield = local_host_check?
   host_scan_for_local_hosts(host, &last, NULL) : HOST_FOUND;
 
 /* When running in the test harness, sort into the order of addresses so as to
-get repeatability. This doesn't have to be efficient. But don't interchange
-IPv4 and IPv6 addresses! */
+get repeatability. */
 
-if (running_in_test_harness)
-  {
-  BOOL done = FALSE;
-  while (!done)
-    {
-    host_item *h;
-    done = TRUE;
-    for (h = host; h != last; h = h->next)
-      {
-      if ((Ustrchr(h->address, ':') == NULL) !=
-          (Ustrchr(h->next->address, ':') == NULL))
-        continue;
-      if (Ustrcmp(h->address, h->next->address) > 0)
-        {
-        uschar *temp = h->address;
-        h->address = h->next->address;
-        h->next->address = temp;
-        done = FALSE;
-        }
-      }
-    }
-  }
+if (running_in_test_harness) sort_addresses(host, last);
 
 HDEBUG(D_host_lookup)
   {
@@ -2242,6 +2290,11 @@ if (rc != DNS_SUCCEED)
   else
     if (rc == HOST_IGNORED) rc = HOST_FIND_FAILED;  /* No special action */
 
+  /* When running in the test harness, sort into the order of addresses so as
+  to get repeatability. */
+
+  if (running_in_test_harness) sort_addresses(host, last);
+
   DEBUG(D_host_lookup)
     {
     host_item *h;