Add control=suppress_local_fixups to complete the quartet.
[exim.git] / src / src / host.c
index 32e24e2803793ae91702ad287e20860f5ff3f0ce..58e18a757e034d477fc809f80e8f68fac60f8923 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/host.c,v 1.10 2005/06/22 15:44:38 ph10 Exp $ */
+/* $Cambridge: exim/src/src/host.c,v 1.13 2005/08/22 15:28:20 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -40,6 +40,9 @@ with these comments:
   code by Stuart Levy
   as seen in comp.sys.sgi.admin
 
+August 2005: Apparently this is also needed for AIX systems; USE_INET_NTOA_FIX
+should now be set for them as well.
+
 Arguments:  sa  an in_addr structure
 Returns:        pointer to static text string
 */
@@ -234,7 +237,7 @@ Returns:     0 if there is no port, else the port number. If there's a syntax
 */
 
 int
-host_extract_port(uschar *address)
+host_address_extract_port(uschar *address)
 {
 int port = 0;
 uschar *endptr;
@@ -278,6 +281,59 @@ return port;
 }
 
 
+/*************************************************
+*         Get port from a host item's name       *
+*************************************************/
+
+/* This function is called when finding the IP address for a host that is in a
+list of hosts explicitly configured, such as in the manualroute router, or in a
+fallback hosts list. We see if there is a port specification at the end of the
+host name, and if so, remove it. A minimum length of 3 is required for the
+original name; nothing shorter is recognized as having a port.
+
+We test for a name ending with a sequence of digits; if preceded by colon we
+have a port if the character before the colon is ] and the name starts with [
+or if there are no other colons in the name (i.e. it's not an IPv6 address).
+
+Arguments:  pointer to the host item
+Returns:    a port number or PORT_NONE
+*/
+
+int
+host_item_get_port(host_item *h)
+{
+uschar *p;
+int port, x;
+int len = Ustrlen(h->name);
+
+if (len < 3 || (p = h->name + len - 1, !isdigit(*p))) return PORT_NONE;
+
+/* Extract potential port number */
+
+port = *p-- - '0';
+x = 10;
+
+while (p > h->name + 1 && isdigit(*p))
+  {
+  port += (*p-- - '0') * x;
+  x *= 10;
+  }
+
+/* The smallest value of p at this point is h->name + 1. */
+
+if (*p != ':') return PORT_NONE;
+
+if (p[-1] == ']' && h->name[0] == '[')
+  h->name = string_copyn(h->name + 1, p - h->name - 2);
+else if (Ustrchr(h->name, ':') == p)
+  h->name = string_copyn(h->name, p - h->name);
+else return PORT_NONE;
+
+DEBUG(D_route|D_host_lookup) debug_printf("host=%s port=%d\n", h->name, port);
+return port;
+}
+
+
 
 #ifndef STAND_ALONE    /* Omit when standalone testing */
 
@@ -379,11 +435,41 @@ if (sender_host_name == NULL)
 else
   {
   int len;
+  BOOL no_helo = FALSE;
+
+  /* Comparing a HELO name to a host name is easy */
+
   if (sender_helo_name == NULL ||
-      strcmpic(sender_host_name, sender_helo_name) == 0 ||
-        (sender_helo_name[0] == '[' &&
-         sender_helo_name[(len=Ustrlen(sender_helo_name))-1] == ']' &&
-         strncmpic(sender_helo_name+1, sender_host_address, len - 2) == 0))
+      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)?
@@ -492,7 +578,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 */
+  int port = host_address_extract_port(s);            /* Leaves just the IP address */
   if (string_is_ip_address(s, NULL) == 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Malformed IP address \"%s\" in %s",
       s, name);