Hopefully the final lot of test files.
[exim.git] / src / src / host.c
index ee4461bc8d9b00b3857b688a59173c5f49c59e1e..3807ad45861fe1119f8d5f54d78cbbea79efeb49 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/host.c,v 1.14 2005/09/16 14:44:11 ph10 Exp $ */
+/* $Cambridge: exim/src/src/host.c,v 1.19 2005/12/06 10:25:59 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -147,8 +147,11 @@ while (!done)
 *************************************************/
 
 /* This function is called instead of gethostbyname(), gethostbyname2(), or
-getipnodebyname() when running in the test harness. It uses only the DNS to
-look up the host name. In the new test harness, this means it will access only
+getipnodebyname() when running in the test harness. It recognizes the name
+"manyhome.test.ex" and generates a humungous number of IP addresses. It also
+recognizes an unqualified "localhost" and forces it to the appropriate loopback
+address. IP addresses are treated as literals. For other names, it uses the DNS
+to find the host name. In the new test harness, this means it will access only
 the fake DNS resolver. In the old harness it will call the real resolver and
 access the test zone.
 
@@ -164,8 +167,13 @@ Returns:        a hostent structure or NULL for an error
 static struct hostent *
 host_fake_gethostbyname(uschar *name, int af, int *error_num)
 {
-int ipa;
+#if HAVE_IPV6
 int alen = (af == AF_INET)? sizeof(struct in_addr):sizeof(struct in6_addr);
+#else
+int alen = sizeof(struct in_addr);
+#endif
+
+int ipa;
 uschar *lname = name;
 uschar *adds;
 uschar **alist;
@@ -178,6 +186,36 @@ DEBUG(D_host_lookup)
   debug_printf("using host_fake_gethostbyname for %s (%s)\n", name,
     (af == AF_INET)? "IPv4" : "IPv6");
 
+/* Handle the name that needs a vast number of IP addresses */
+
+if (Ustrcmp(name, "manyhome.test.ex") == 0 && af == AF_INET)
+  {
+  int i, j;
+  yield = store_get(sizeof(struct hostent));
+  alist = store_get(2049 * sizeof(char *));
+  adds  = store_get(2048 * alen);
+  yield->h_name = CS name;
+  yield->h_aliases = NULL;
+  yield->h_addrtype = af;
+  yield->h_length = alen;
+  yield->h_addr_list = CSS alist;
+  for (i = 104; i <= 111; i++)
+    {
+    for (j = 0; j <= 255; j++)
+      {
+      *alist++ = adds;
+      *adds++ = 10;
+      *adds++ = 250;
+      *adds++ = i;
+      *adds++ = j;
+      }
+    }
+  *alist = NULL;
+  return yield;
+  }
+
+/* Handle unqualified "localhost" */
+
 if (Ustrcmp(name, "localhost") == 0)
   lname = (af == AF_INET)? US"127.0.0.1" : US"::1";
 
@@ -497,8 +535,9 @@ as follows:
 
 (a) No sender_host_name or sender_helo_name: "[ip address]"
 (b) Just sender_host_name: "host_name [ip address]"
-(c) Just sender_helo_name: "(helo_name) [ip address]"
-(d) The two are identical: "host_name [ip address]"
+(c) Just sender_helo_name: "(helo_name) [ip address]" unless helo is IP
+            in which case: "[ip address}"
+(d) The two are identical: "host_name [ip address]" includes helo = IP
 (e) The two are different: "host_name (helo_name) [ip address]"
 
 If log_incoming_port is set, the sending host's port number is added to the IP
@@ -519,7 +558,9 @@ Returns:    nothing
 void
 host_build_sender_fullhost(void)
 {
+BOOL show_helo = TRUE;
 uschar *address;
+int len;
 int old_pool = store_pool;
 
 if (sender_host_address == NULL) return;
@@ -535,6 +576,43 @@ address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port);
 if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0)
   *(Ustrrchr(address, ':')) = 0;
 
+/* If there's no EHLO/HELO data, we can't show it. */
+
+if (sender_helo_name == NULL) show_helo = FALSE;
+
+/* If HELO/EHLO was followed by an IP literal, it's messy because of two
+features of IPv6. Firstly, there's the "IPv6:" prefix (Exim is liberal and
+doesn't require this, for historical reasons). Secondly, IPv6 addresses may not
+be given in canonical form, so we have to canonicize them before comparing. As
+it happens, the code works for both IPv4 and IPv6. */
+
+else if (sender_helo_name[0] == '[' &&
+         sender_helo_name[(len=Ustrlen(sender_helo_name))-1] == ']')
+  {
+  int offset = 1;
+  uschar *helo_ip;
+
+  if (strncmpic(sender_helo_name + 1, US"IPv6:", 5) == 0) offset += 5;
+  if (strncmpic(sender_helo_name + 1, US"IPv4:", 5) == 0) offset += 5;
+
+  helo_ip = string_copyn(sender_helo_name + offset, len - offset - 1);
+
+  if (string_is_ip_address(helo_ip, NULL) != 0)
+    {
+    int x[4], y[4];
+    int sizex, sizey;
+    uschar ipx[48], ipy[48];    /* large enough for full IPv6 */
+
+    sizex = host_aton(helo_ip, x);
+    sizey = host_aton(sender_host_address, y);
+
+    (void)host_nmtoa(sizex, x, -1, ipx, ':');
+    (void)host_nmtoa(sizey, y, -1, ipy, ':');
+
+    if (strcmpic(ipx, ipy) == 0) show_helo = FALSE;
+    }
+  }
+
 /* Host name is not verified */
 
 if (sender_host_name == NULL)
@@ -550,7 +628,7 @@ if (sender_host_name == NULL)
 
   sender_rcvhost = string_cat(NULL, &size, &ptr, address, adlen);
 
-  if (sender_ident != NULL || sender_helo_name != NULL || portptr != NULL)
+  if (sender_ident != NULL || show_helo || portptr != NULL)
     {
     int firstptr;
     sender_rcvhost = string_cat(sender_rcvhost, &size, &ptr, US" (", 2);
@@ -560,7 +638,7 @@ if (sender_host_name == NULL)
       sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2, US"port=",
         portptr + 1);
 
-    if (sender_helo_name != NULL)
+    if (show_helo)
       sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2,
         (firstptr == ptr)? US"helo=" : US" helo=", sender_helo_name);
 
@@ -579,54 +657,15 @@ if (sender_host_name == NULL)
   store_reset(sender_rcvhost + ptr + 1);
   }
 
-/* Host name is known and verified. */
+/* Host name is known and verified. Unless we've already found that the HELO
+data matches the IP address, compare it with the name. */
 
 else
   {
-  int len;
-  BOOL no_helo = FALSE;
-
-  /* Comparing a HELO name to a host name is easy */
+  if (show_helo && strcmpic(sender_host_name, sender_helo_name) == 0)
+    show_helo = FALSE;
 
-  if (sender_helo_name == NULL ||
-      strcmpic(sender_host_name, sender_helo_name) == 0)
-    no_helo = TRUE;
-
-  /* If HELO/EHLO was followed by an IP literal, it's much more messy because
-  of two features of IPv6. Firstly, there's the "IPv6:" prefix (Exim is liberal
-  and doesn't require this, for historical reasons). Secondly, an IPv6 address
-  may not be given in canonical form, so we have to canonicize it before
-  comparing. As it happens, the code works for both IPv4 and IPv6. */
-
-  else if (sender_helo_name[0] == '[' &&
-           sender_helo_name[(len=Ustrlen(sender_helo_name))-1] == ']')
-    {
-    uschar *helo_ip;
-    int offset = 1;
-
-    if (strncmpic(sender_helo_name+1, US"IPv6:",5) == 0) offset += 5;
-    helo_ip = string_copyn(sender_helo_name + offset, len - offset - 1);
-
-    if (string_is_ip_address(helo_ip, NULL) != 0)
-      {
-      int x[4];
-      int size;
-      size = host_aton(helo_ip, x);
-      helo_ip = store_get(48);  /* large enough for full IPv6 */
-      (void)host_nmtoa(size, x, -1, helo_ip, ':');
-      if (strcmpic(helo_ip, sender_host_address) == 0) no_helo = TRUE;
-      }
-    }
-
-  if (no_helo)
-    {
-    sender_fullhost = string_sprintf("%s %s", sender_host_name, address);
-    sender_rcvhost = (sender_ident == NULL)?
-      string_sprintf("%s (%s)", sender_host_name, address) :
-      string_sprintf("%s (%s ident=%s)", sender_host_name, address,
-        sender_ident);
-    }
-  else
+  if (show_helo)
     {
     sender_fullhost = string_sprintf("%s (%s) %s", sender_host_name,
       sender_helo_name, address);
@@ -636,6 +675,14 @@ else
       string_sprintf("%s\n\t(%s helo=%s ident=%s)", sender_host_name,
         address, sender_helo_name, sender_ident);
     }
+  else
+    {
+    sender_fullhost = string_sprintf("%s %s", sender_host_name, address);
+    sender_rcvhost = (sender_ident == NULL)?
+      string_sprintf("%s (%s)", sender_host_name, address) :
+      string_sprintf("%s (%s ident=%s)", sender_host_name, address,
+        sender_ident);
+    }
   }
 
 store_pool = old_pool;
@@ -727,11 +774,16 @@ ip_address_item *next;
 
 while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
   {
+  int ipv;
   int port = host_address_extract_port(s);            /* Leaves just the IP address */
-  if (string_is_ip_address(s, NULL) == 0)
+  if ((ipv = string_is_ip_address(s, NULL)) == 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Malformed IP address \"%s\" in %s",
       s, name);
 
+  /* Skip IPv6 addresses if IPv6 is disabled. */
+
+  if (disable_ipv6 && ipv == 6) continue;
+
   /* This use of strcpy() is OK because we have checked that s is a valid IP
   address above. The field in the ip_address_item is large enough to hold an
   IPv6 address. */
@@ -1789,13 +1841,10 @@ for (hname = sender_host_name; hname != NULL; hname = *aliases++)
   if ((rc = host_find_byname(&h, NULL, NULL, FALSE)) == HOST_FOUND)
     {
     host_item *hh;
-    uschar *address_ipv4 = (Ustrncmp(sender_host_address, "::ffff:", 7) == 0)?
-      sender_host_address + 7 : sender_host_address;
     HDEBUG(D_host_lookup) debug_printf("checking addresses for %s\n", hname);
     for (hh = &h; hh != NULL; hh = hh->next)
       {
-      if ((Ustrcmp(hh->address, (Ustrchr(hh->address, ':') == NULL)?
-          address_ipv4 : sender_host_address)) == 0)
+      if (host_is_in_net(hh->address, sender_host_address, 0))
         {
         HDEBUG(D_host_lookup) debug_printf("  %s OK\n", hh->address);
         ok = TRUE;
@@ -1921,17 +1970,17 @@ if (running_in_test_harness)
     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
-world, which makes for slightly tidier code. However, if dns_ipv4_lookup
-matches the domain, we also just do IPv4 lookups here (except when testing
-standalone). */
+/* In an IPv6 world, unless IPv6 has been disabled, 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 world, which makes for slightly tidier
+code. However, if dns_ipv4_lookup matches the domain, we also just do IPv4
+lookups here (except when testing standalone). */
 
 #if HAVE_IPV6
   #ifndef STAND_ALONE
-  if (dns_ipv4_lookup != NULL &&
+  if (disable_ipv6 || (dns_ipv4_lookup != NULL &&
         match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN,
-          TRUE, NULL) == OK)
+          TRUE, NULL) == OK))
     { af = AF_INET; times = 1; }
   else
   #endif  /* STAND_ALONE */
@@ -2205,18 +2254,18 @@ if (allow_ip && string_is_ip_address(host->name, NULL) != 0)
   return HOST_FOUND;
   }
 
-/* On an IPv6 system, go round the loop up to three times, looking for A6 and
-AAAA records the first two times. However, unless doing standalone testing, we
-force an IPv4 lookup if the domain matches dns_ipv4_lookup is set. Since A6
-records look like being abandoned, support them only if explicitly configured
-to do so. On an IPv4 system, go round the loop once only, looking only for A
-records. */
+/* On an IPv6 system, unless IPv6 is disabled, go round the loop up to three
+times, looking for A6 and AAAA records the first two times. However, unless
+doing standalone testing, we force an IPv4 lookup if the domain matches
+dns_ipv4_lookup is set. Since A6 records look like being abandoned, support
+them only if explicitly configured to do so. On an IPv4 system, go round the
+loop once only, looking only for A records. */
 
 #if HAVE_IPV6
   #ifndef STAND_ALONE
-    if (dns_ipv4_lookup != NULL &&
+    if (disable_ipv6 || (dns_ipv4_lookup != NULL &&
         match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN,
-        TRUE, NULL) == OK)
+        TRUE, NULL) == OK))
       i = 0;    /* look up A records only */
     else
   #endif        /* STAND_ALONE */
@@ -2849,10 +2898,14 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ADDITIONAL);
 
   if (rr->type != T_A
   #if HAVE_IPV6
-    && rr->type != T_AAAA
-    #ifdef SUPPORT_A6
-    && rr->type != T_A6
-    #endif
+    && ( disable_ipv6 ||
+         (
+         rr->type != T_AAAA
+         #ifdef SUPPORT_A6
+         && rr->type != T_A6
+         #endif
+         )
+       )
   #endif
     ) continue;