Allow port setting on lists of hosts in manualroute, queryprogram,
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 9 Aug 2005 13:31:52 +0000 (13:31 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 9 Aug 2005 13:31:52 +0000 (13:31 +0000)
fallback_hosts, and "hosts" in smtp.

doc/doc-misc/WishList
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/exim_monitor/em_main.c
src/src/exim.c
src/src/functions.h
src/src/host.c
src/src/routers/rf_lookup_hostlist.c
src/src/spool_in.c
src/src/transports/smtp.c

index 220892e9900fc9819b1f058e9d1a90fbefbd1c61..0b7338f976d9e2acd562f2b1a2b818373cde2659 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-misc/WishList,v 1.46 2005/08/08 13:52:19 ph10 Exp $
+$Cambridge: exim/doc/doc-misc/WishList,v 1.47 2005/08/09 13:31:52 ph10 Exp $
 
 EXIM 4 WISH LIST
 ----------------
@@ -373,10 +373,6 @@ This is for looking up things like subject contents. Probably need an option to
 exim_dbmbuild to make them into DBM files.
 ------------------------------------------------------------------------------
 
-(174)  19-Sep-2000 S  A way of using a different port for fallback hosts.
-Dean Brooks
-------------------------------------------------------------------------------
-
 (181)  10-Nov-2000 S  Compile-time options for ignoring Sendmail options
 
 So that new ones could be accommodated easily.
@@ -507,8 +503,6 @@ these entries.
 . Options and/or a utility to enable non-privileged users to view the queue
 (e.g. -bpp), manipulate their own messages, etc.
 
-. Specify a port along with a host in a route_list.
-
 . A generalized "From" escaping scheme that also escapes >From so that the
 whole thing can be reversed.
 
index 62da9c8795ec773964f5c1b02ffe6c0857e7740a..30c391366e9d02381a04d91ff56a0afa73a24384 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.203 2005/08/08 15:02:48 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.204 2005/08/09 13:31:52 ph10 Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -77,6 +77,12 @@ PH/20 If a delivery was routed to a non-standard port by means of an SRV
       record, the port was not correctly logged when the outgoing_port log
       selector was set (it logged the transort's default port).
 
+PH/21 Added support for host-specific ports to manualroute, queryprogram,
+      fallback_hosts, and "hosts" in the smtp transport.
+
+PH/22 If the log selector "outgoing_port" is set, the port is now also given on
+      host errors such as "Connection refused".
+
 
 Exim version 4.52
 -----------------
index 0a23d2d1b670121de925e965c56943ee52929102..575d269d76b1f9cffc09d2b2bc04d1f4dc11c9a2 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.62 2005/08/08 10:48:26 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.63 2005/08/09 13:31:52 ph10 Exp $
 
 New Features in Exim
 --------------------
@@ -76,6 +76,32 @@ PH/05 Previously, if "verify = helo" was set in an ACL, the condition was true
       tested the remembered result. Now, if a previous verification attempt has
       not happened, "verify = helo" does it there and then.
 
+PH/06 It is now possible to specify a port number along with a host name or
+      IP address in the list of hosts defined in the manualroute or
+      queryprogram routers, fallback_hosts, or the "hosts" option of the smtp
+      transport. These all override any port specification on the transport.
+      The relatively standard syntax of using a colon separator has been
+      adopted, but there are some gotchas that need attention:
+
+      * In all these lists of hosts, colon is the default separator, so either
+        the colon that specifies a port must be doubled, or the separator must
+        be changed. The following two examples have the same effect:
+
+          fallback_hosts = host1.tld::1225 : host2.tld::1226
+          fallback_hosts = <; host1.tld:1225 ; host2.tld:1226
+
+      * When IPv6 addresses are involved, it gets worse, because they contain
+        colons of their own. To make this case easier, it is permitted to
+        enclose an IP address (either v4 or v6) in square brackets if a port
+        number follows. Here's an example from a manualroute router:
+
+           route_list = * "</ [10.1.1.1]:1225 / [::1]:1226"
+
+      If the "/MX" feature is to be used as well as a port specifier, the port
+      must come last. For example:
+
+           route_list = *  dom1.tld/mx::1225
+
 
 Exim version 4.52
 -----------------
index cee1fd09a6b5686d328bd68a0df574c52f594a3b..b3b27a24f2873dd113093c1ebb2d336910607f63 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/exim_monitor/em_main.c,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/src/exim_monitor/em_main.c,v 1.3 2005/08/09 13:31:52 ph10 Exp $ */
 
 /*************************************************
 *                  Exim Monitor                  *
@@ -200,7 +200,7 @@ Returns:     0 if there is no port, else the port number.
 */
 
 int
-host_extract_port(uschar *address)
+host_address_extract_port(uschar *address)
 {
 int skip = -3;                     /* Skip 3 dots in IPv4 addresses */
 address--;
index 98b01511d01c8da4c1a3ffb72c7fd2a962fd600d..06e01399a5755e5d37b09dd11243224a8fe42fae 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.23 2005/08/01 13:20:28 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.24 2005/08/09 13:31:52 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -599,7 +599,7 @@ Returns:    the port, or zero if there isn't one
 static int
 check_port(uschar *address)
 {
-int port = host_extract_port(address);
+int port = host_address_extract_port(address);
 if (string_is_ip_address(address, NULL) == 0)
   {
   fprintf(stderr, "exim abandoned: \"%s\" is not an IP address\n", address);
index 7e61c92f611330d4c87ce32ca32f072fb36736af..6d1f5cd8c9a7d492b5af3ce24daba1c9d114a05a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.18 2005/08/02 15:19:20 ph10 Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.19 2005/08/09 13:31:52 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -107,19 +107,20 @@ extern BOOL    filter_system_interpret(address_item **, uschar **);
 extern void    header_add(int, char *, ...);
 extern int     header_checkname(header_line *, BOOL);
 extern BOOL    header_match(uschar *, BOOL, BOOL, string_item *, int, ...);
+extern int     host_address_extract_port(uschar *);
 extern uschar *host_and_ident(BOOL);
 extern int     host_aton(uschar *, int *);
 extern void    host_build_hostlist(host_item **, uschar *, BOOL);
 extern ip_address_item *host_build_ifacelist(uschar *, uschar *);
 extern void    host_build_log_info(void);
 extern void    host_build_sender_fullhost(void);
-extern int     host_extract_port(uschar *);
 extern BOOL    host_find_byname(host_item *, uschar *, uschar **, BOOL);
 extern int     host_find_bydns(host_item *, uschar *, int, uschar *, uschar *,
                  uschar *,uschar **, BOOL *);
 extern ip_address_item *host_find_interfaces(void);
 extern BOOL    host_is_in_net(uschar *, uschar *, int);
 extern BOOL    host_is_tls_on_connect_port(int);
+extern int     host_item_get_port(host_item *);
 extern void    host_mask(int, int *, int);
 extern int     host_name_lookup(void);
 extern int     host_nmtoa(int, int *, int, uschar *, int);
index d8f5607884c21138a0fdb39a994d1ab6f0a91f5d..9f3a068707fa758953e95829d54daecf04b8c335 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/host.c,v 1.11 2005/08/02 09:01:44 ph10 Exp $ */
+/* $Cambridge: exim/src/src/host.c,v 1.12 2005/08/09 13:31:52 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -237,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;
@@ -281,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 */
 
@@ -495,7 +548,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);
index 5646315cbb8608ad2adb950de848d40991915d4a..3e7eee137ac3969cafcfe3f6b0661a43ba9fc265 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/routers/rf_lookup_hostlist.c,v 1.3 2005/01/11 15:51:03 ph10 Exp $ */
+/* $Cambridge: exim/src/src/routers/rf_lookup_hostlist.c,v 1.4 2005/08/09 13:31:53 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -27,6 +27,12 @@ case, MX records are looked up for the name, and the list of hosts obtained
 replaces the incoming "host". In other words, "x/MX" is shorthand for "those
 hosts pointed to by x's MX records".
 
+It is also possible for a port to be specified along with the host name or IP
+address. The syntax is to add ":port" on to the end. This doesn't work with
+IPv6 addresses, so we allow IP addresses to be enclosed in [] in order to make
+this work. The specification of the port must come last, that is, after "/MX"
+if that is present.
+
 Arguments:
   rblock               the router block
   addr                 the address being routed
@@ -61,7 +67,7 @@ prev = NULL;
 for (h = addr->host_list; h != NULL; prev = h, h = next_h)
   {
   uschar *canonical_name;
-  int rc, len;
+  int rc, len, port;
 
   next_h = h->next;
   if (h->address != NULL) continue;
@@ -69,6 +75,11 @@ for (h = addr->host_list; h != NULL; prev = h, h = next_h)
   DEBUG(D_route|D_host_lookup)
     debug_printf("finding IP address for %s\n", h->name);
 
+  /* Handle any port setting that may be on the name; it will be removed
+  from the end of the name. */
+
+  port = host_item_get_port(h);
+
   /* If the name ends with "/MX", we interpret it to mean "the list of hosts
   pointed to by MX records with this name". */
 
@@ -78,7 +89,7 @@ for (h = addr->host_list; h != NULL; prev = h, h = next_h)
     DEBUG(D_route|D_host_lookup)
       debug_printf("doing DNS MX lookup for %s\n", h->name);
 
-    h->name[len-3] = 0;
+    h->name = string_copyn(h->name, len - 3);
     rc = host_find_bydns(h,
         ignore_target_hosts,
         HOST_FIND_BY_MX,                /* look only for MX records */
@@ -159,6 +170,14 @@ for (h = addr->host_list; h != NULL; prev = h, h = next_h)
     return DEFER;
     }
 
+  /* Deal with a port setting */
+
+  if (port != PORT_NONE)
+    {
+    host_item *hh;
+    for (hh = h; hh != next_h; hh = hh->next) hh->port = port;
+    }
+
   /* A local host gets chopped, with its successors, if there are previous
   hosts. Otherwise the self option is used. If it is set to "send", any
   subsequent hosts that are also the local host do NOT get chopped. */
index 6efe5aa27db682e973eabaf6843c7beab2aaf70e..8e56677bd7e1a2d84e5691acac941b717935e8de 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/spool_in.c,v 1.12 2005/06/27 14:29:44 ph10 Exp $ */
+/* $Cambridge: exim/src/src/spool_in.c,v 1.13 2005/08/09 13:31:53 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -448,13 +448,13 @@ for (;;)
 
   else if (Ustrncmp(big_buffer, "-host_address", 13) == 0)
     {
-    sender_host_port = host_extract_port(big_buffer + 14);
+    sender_host_port = host_address_extract_port(big_buffer + 14);
     sender_host_address = string_copy(big_buffer + 14);
     }
 
   else if (Ustrncmp(big_buffer, "-interface_address", 18) == 0)
     {
-    interface_port = host_extract_port(big_buffer + 19);
+    interface_port = host_address_extract_port(big_buffer + 19);
     interface_address = string_copy(big_buffer + 19);
     }
 
index c16e620b31e44e7e84afea32cf258e7b6f95f9df..a503d8f139635241c0d2391d3ad6d4df1080fe75 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.16 2005/08/08 15:02:48 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.17 2005/08/09 13:31:53 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -329,8 +329,8 @@ this particular type of timeout.
 Returns:       nothing
 */
 
-static
-void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
+static void
+set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
   BOOL pass_message)
 {
 address_item *addr;
@@ -505,14 +505,14 @@ if (addr->message != NULL)
   }
 else
   {
-  log_write(0, LOG_MAIN, "%s [%s]: %s",
-    host->name,
-    host->address,
-    strerror(addr->basic_errno));
-  deliver_msglog("%s %s [%s]: %s\n",
-    tod_stamp(tod_log),
-    host->name,
-    host->address,
+  uschar *msg =
+    ((log_extra_selector & LX_outgoing_port) != 0)?
+    string_sprintf("%s [%s]:%d", host->name, host->address,
+      (host->port == PORT_NONE)? 25 : host->port)
+    :
+    string_sprintf("%s [%s]", host->name, host->address);
+  log_write(0, LOG_MAIN, "%s %s", msg, strerror(addr->basic_errno));
+  deliver_msglog("%s %s %s\n", tod_stamp(tod_log), msg,
     strerror(addr->basic_errno));
   }
 }
@@ -2206,14 +2206,6 @@ for (cutoff_retry = 0; expired &&
     uschar *retry_message_key = NULL;
     uschar *serialize_key = NULL;
 
-    /* Set up a string for adding to the retry key if the port number is not
-    the standard SMTP port. A host may have its own port setting that overrides
-    the default. */
-
-    pistring = string_sprintf(":%d", (host->port == PORT_NONE)?
-      port : host->port);
-    if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
-
     /* Default next host is next host. :-) But this can vary if the
     hosts_max_try limit is hit (see below). It may also be reset if a host
     address is looked up here (in case the host was multihomed). */
@@ -2243,6 +2235,8 @@ for (cutoff_retry = 0; expired &&
 
     if (host->address == NULL)
       {
+      int new_port;
+      host_item *hh;
       uschar *canonical_name;
 
       if (host->status >= hstatus_unusable)
@@ -2254,6 +2248,13 @@ for (cutoff_retry = 0; expired &&
 
       DEBUG(D_transport) debug_printf("getting address for %s\n", host->name);
 
+      /* The host name is permitted to have an attached port. Find it, and
+      strip it from the name. Just remember it for now. */
+
+      new_port = host_item_get_port(host);
+
+      /* Count hosts looked up */
+
       hosts_looked_up++;
 
       /* Find by name if so configured, or if it's an IP address. We don't
@@ -2270,6 +2271,11 @@ for (cutoff_retry = 0; expired &&
           &canonical_name, NULL);
         }
 
+      /* Update the host (and any additional blocks, resulting from
+      multihoming) with a host-specific port, if any. */
+
+      for (hh = host; hh != nexthost; hh = hh->next) hh->port = new_port;
+
       /* Failure to find the host at this time (usually DNS temporary failure)
       is really a kind of routing failure rather than a transport failure.
       Therefore we add a retry item of the routing kind, not to stop us trying
@@ -2363,6 +2369,14 @@ for (cutoff_retry = 0; expired &&
     deliver_host = host->name;
     deliver_host_address = host->address;
 
+    /* Set up a string for adding to the retry key if the port number is not
+    the standard SMTP port. A host may have its own port setting that overrides
+    the default. */
+
+    pistring = string_sprintf(":%d", (host->port == PORT_NONE)?
+      port : host->port);
+    if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
+
     /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface
     string changes upon expansion, we must add it to the key that is used for
     retries, because connections to the same host from a different interface