From 7cd1141be4e551e80514c38662ec6e8209608205 Mon Sep 17 00:00:00 2001 From: Philip Hazel Date: Tue, 9 Aug 2005 13:31:52 +0000 Subject: [PATCH] Allow port setting on lists of hosts in manualroute, queryprogram, fallback_hosts, and "hosts" in smtp. --- doc/doc-misc/WishList | 8 +--- doc/doc-txt/ChangeLog | 8 +++- doc/doc-txt/NewStuff | 28 ++++++++++++- src/exim_monitor/em_main.c | 4 +- src/src/exim.c | 4 +- src/src/functions.h | 5 ++- src/src/host.c | 59 ++++++++++++++++++++++++++-- src/src/routers/rf_lookup_hostlist.c | 25 ++++++++++-- src/src/spool_in.c | 6 +-- src/src/transports/smtp.c | 52 +++++++++++++++--------- 10 files changed, 156 insertions(+), 43 deletions(-) diff --git a/doc/doc-misc/WishList b/doc/doc-misc/WishList index 220892e99..0b7338f97 100644 --- a/doc/doc-misc/WishList +++ b/doc/doc-misc/WishList @@ -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. diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 62da9c879..30c391366 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -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 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 0a23d2d1b..575d269d7 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -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 = * "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); diff --git a/src/src/routers/rf_lookup_hostlist.c b/src/src/routers/rf_lookup_hostlist.c index 5646315cb..3e7eee137 100644 --- a/src/src/routers/rf_lookup_hostlist.c +++ b/src/src/routers/rf_lookup_hostlist.c @@ -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. */ diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 6efe5aa27..8e56677bd 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -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); } diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c16e620b3..a503d8f13 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -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 -- 2.30.2