summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/doc-docbook/spec.xfpt5
-rw-r--r--doc/doc-txt/ChangeLog5
-rw-r--r--src/src/functions.h4
-rw-r--r--src/src/smtp_out.c149
-rw-r--r--src/src/structs.h1
-rw-r--r--src/src/transports/smtp.c12
-rw-r--r--src/src/transports/smtp_socks.c13
-rw-r--r--src/src/verify.c1
-rw-r--r--test/confs/40503
-rw-r--r--test/log/405110
-rw-r--r--test/scripts/4050-pipe-conn/40519
11 files changed, 140 insertions, 72 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 0ac98d668..7f96768f7 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -25582,7 +25582,10 @@ so combines well with TCP Fast Open.
See also the &%pipelining_connect_advertise_hosts%& main option.
Note:
-When the facility is used, the transport &%helo_data%& option
+.new
+When the facility is used, if the transport &%interface%& option is unset
+the &%helo_data%& option
+.wen
will be expanded before the &$sending_ip_address$& variable
is filled in.
A check is made for the use of that variable, without the
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 93eac06a1..3b1aa2664 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -116,6 +116,11 @@ JH/26 Fix CHUNKING on a continued-transport. Previously the usabliility of
the the facility was not passed across execs, and only the first message
passed over a connection could use BDAT; any further ones using DATA.
+JH/27 Support the PIPECONNECT facility in the smtp transport when the helo_data
+ uses $sending_ip_address and an interface is specified.
+ Previously any use of the local address in the EHLO name disabled
+ PIPECONNECT, the common case being to use the rDNS of it.
+
Exim version 4.95
-----------------
diff --git a/src/src/functions.h b/src/src/functions.h
index e231ce204..8badd90a4 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -485,6 +485,7 @@ extern int sieve_interpret(const uschar *, int, const uschar *,
const uschar *, const uschar *, const uschar *,
address_item **, uschar **);
extern void sigalrm_handler(int);
+extern int smtp_boundsock(smtp_connect_args *);
extern void smtp_closedown(uschar *);
extern void smtp_command_timeout_exit(void) NORETURN;
extern void smtp_command_sigterm_exit(void) NORETURN;
@@ -493,8 +494,6 @@ extern void smtp_data_sigint_exit(void) NORETURN;
extern void smtp_deliver_init(void);
extern uschar *smtp_cmd_hist(void);
extern int smtp_connect(smtp_connect_args *, const blob *);
-extern int smtp_sock_connect(host_item *, int, int, uschar *,
- transport_instance * tb, int, const blob *);
extern int smtp_feof(void);
extern int smtp_ferror(void);
extern uschar *smtp_get_connection_info(void);
@@ -516,6 +515,7 @@ extern void smtp_notquit_exit(uschar *, uschar *, uschar *, ...);
extern void smtp_port_for_connect(host_item *, int);
extern void smtp_send_prohibition_message(int, uschar *);
extern int smtp_setup_msg(void);
+extern int smtp_sock_connect(smtp_connect_args *, int, const blob *);
extern BOOL smtp_start_session(void);
extern int smtp_ungetc(int);
extern BOOL smtp_verify_helo(void);
diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c
index 7b8212477..610e4a068 100644
--- a/src/src/smtp_out.c
+++ b/src/src/smtp_out.c
@@ -252,44 +252,21 @@ switch (tcp_out_fastopen)
#endif
-/* Arguments:
- host host item containing name and address and port
- host_af AF_INET or AF_INET6
- port TCP port number
- interface outgoing interface address or NULL
- tb transport
- timeout timeout value or 0
- early_data if non-NULL, idempotent data to be sent -
- preferably in the TCP SYN segment
- Special case: non-NULL but with NULL blob.data - caller is
- client-data-first (eg. TLS-on-connect) and a lazy-TCP-connect is
- acceptable.
-
-Returns: connected socket number, or -1 with errno set
+/* Create and bind a socket, given the connect-args.
+Update those with the state. Return the fd, or -1 with errno set.
*/
int
-smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface,
- transport_instance * tb, int timeout, const blob * early_data)
+smtp_boundsock(smtp_connect_args * sc)
{
+transport_instance * tb = sc->tblock;
smtp_transport_options_block * ob =
(smtp_transport_options_block *)tb->options_block;
const uschar * dscp = ob->dscp;
-int dscp_value;
-int dscp_level;
-int dscp_option;
-int sock;
-int save_errno = 0;
-const blob * fastopen_blob = NULL;
+int sock, dscp_value, dscp_level, dscp_option;
-
-#ifndef DISABLE_EVENT
-deliver_host_address = host->address;
-deliver_host_port = port;
-if (event_raise(tb->event_action, US"tcp:connect", NULL, &errno)) return -1;
-#endif
-
-if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
+if ((sock = ip_socket(SOCK_STREAM, sc->host_af)) < 0)
+ return -1;
/* Set TCP_NODELAY; Exim does its own buffering. */
@@ -300,7 +277,7 @@ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, US &on, sizeof(on)))
/* Set DSCP value, if we can. For now, if we fail to set the value, we don't
bomb out, just log it and continue in default traffic class. */
-if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value))
+if (dscp && dscp_lookup(dscp, sc->host_af, &dscp_level, &dscp_option, &dscp_value))
{
HDEBUG(D_transport|D_acl|D_v)
debug_printf_indent("DSCP \"%s\"=%x ", dscp, dscp_value);
@@ -309,7 +286,7 @@ if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value))
debug_printf_indent("failed to set DSCP: %s ", strerror(errno));
/* If the kernel supports IPv4 and IPv6 on an IPv6 socket, we need to set the
option for both; ignore failures here */
- if (host_af == AF_INET6 &&
+ if (sc->host_af == AF_INET6 &&
dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value))
(void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value));
}
@@ -317,24 +294,76 @@ if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value))
/* Bind to a specific interface if requested. Caller must ensure the interface
is the same type (IPv4 or IPv6) as the outgoing address. */
-if (interface && ip_bind(sock, host_af, interface, 0) < 0)
+if (sc->interface)
{
- save_errno = errno;
- HDEBUG(D_transport|D_acl|D_v)
- debug_printf_indent("unable to bind outgoing SMTP call to %s: %s", interface,
- strerror(errno));
+ union sockaddr_46 interface_sock;
+ EXIM_SOCKLEN_T size = sizeof(interface_sock);
+
+ if ( ip_bind(sock, sc->host_af, sc->interface, 0) < 0
+ || getsockname(sock, (struct sockaddr *) &interface_sock, &size) < 0
+ )
+ {
+ HDEBUG(D_transport|D_acl|D_v)
+ debug_printf_indent("unable to bind outgoing SMTP call to %s: %s", sc->interface,
+ strerror(errno));
+ close(sock);
+ return -1;
+ }
+ sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
}
+sc->sock = sock;
+return sock;
+}
+
+
+/* Arguments:
+ host host item containing name and address and port
+ host_af AF_INET or AF_INET6
+ port TCP port number
+ interface outgoing interface address or NULL
+ tb transport
+ timeout timeout value or 0
+ early_data if non-NULL, idempotent data to be sent -
+ preferably in the TCP SYN segment
+ Special case: non-NULL but with NULL blob.data - caller is
+ client-data-first (eg. TLS-on-connect) and a lazy-TCP-connect is
+ acceptable.
+
+Returns: connected socket number, or -1 with errno set
+*/
+
+int
+smtp_sock_connect(smtp_connect_args * sc, int timeout, const blob * early_data)
+{
+smtp_transport_options_block * ob =
+ (smtp_transport_options_block *)sc->tblock->options_block;
+int sock;
+int save_errno = 0;
+const blob * fastopen_blob = NULL;
+
+
+#ifndef DISABLE_EVENT
+deliver_host_address = sc->host->address;
+deliver_host_port = sc->host->port;
+if (event_raise(sc->tblock->event_action, US"tcp:connect", NULL, &errno)) return -1;
+#endif
+
+if ( (sock = sc->sock) < 0
+ && (sock = smtp_boundsock(sc)) < 0)
+ save_errno = errno;
+sc->sock = -1;
+
/* Connect to the remote host, and add keepalive to the socket before returning
it, if requested. If the build supports TFO, request it - and if the caller
requested some early-data then include that in the TFO request. If there is
early-data but no TFO support, send it after connecting. */
-else
+if (!save_errno)
{
#ifdef TCP_FASTOPEN
/* See if TCP Fast Open usable. Default is a traditional 3WHS connect */
- if (verify_check_given_host(CUSS &ob->hosts_try_fastopen, host) == OK)
+ if (verify_check_given_host(CUSS &ob->hosts_try_fastopen, sc->host) == OK)
{
if (!early_data)
fastopen_blob = &tcp_fastopen_nodata; /* TFO, with no data */
@@ -351,7 +380,7 @@ else
}
#endif
- if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0)
+ if (ip_connect(sock, sc->host_af, sc->host->address, sc->host->port, timeout, fastopen_blob) < 0)
save_errno = errno;
else if (early_data && !fastopen_blob && early_data->data && early_data->len)
{
@@ -374,29 +403,13 @@ else
#endif
}
-/* Either bind() or connect() failed */
-
-if (save_errno != 0)
- {
- HDEBUG(D_transport|D_acl|D_v)
- {
- debug_printf_indent(" failed: %s", CUstrerror(save_errno));
- if (save_errno == ETIMEDOUT)
- debug_printf(" (timeout=%s)", readconf_printtime(timeout));
- debug_printf("\n");
- }
- (void)close(sock);
- errno = save_errno;
- return -1;
- }
-
-/* Both bind() and connect() succeeded, and any early-data */
-
-else
+if (!save_errno)
{
union sockaddr_46 interface_sock;
EXIM_SOCKLEN_T size = sizeof(interface_sock);
+ /* Both bind() and connect() succeeded, and any early-data */
+
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" connected\n");
if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0)
sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
@@ -408,12 +421,25 @@ else
return -1;
}
- if (ob->keepalive) ip_keepalive(sock, host->address, TRUE);
+ if (ob->keepalive) ip_keepalive(sock, sc->host->address, TRUE);
#ifdef TCP_FASTOPEN
tfo_out_check(sock);
#endif
return sock;
}
+
+/* Either bind() or connect() failed */
+
+HDEBUG(D_transport|D_acl|D_v)
+ {
+ debug_printf_indent(" failed: %s", CUstrerror(save_errno));
+ if (save_errno == ETIMEDOUT)
+ debug_printf(" (timeout=%s)", readconf_printtime(timeout));
+ debug_printf("\n");
+ }
+(void)close(sock);
+errno = save_errno;
+return -1;
}
@@ -501,8 +527,7 @@ if (ob->socks_proxy)
}
#endif
-return smtp_sock_connect(sc->host, sc->host_af, port, sc->interface,
- sc->tblock, ob->connect_timeout, early_data);
+return smtp_sock_connect(sc, ob->connect_timeout, early_data);
}
diff --git a/src/src/structs.h b/src/src/structs.h
index 8c103caa8..46cc99ff6 100644
--- a/src/src/structs.h
+++ b/src/src/structs.h
@@ -830,6 +830,7 @@ typedef struct {
host_item * host;
int host_af;
uschar * interface;
+ int sock; /* used for a bound but not connected socket */
#ifdef SUPPORT_DANE
BOOL dane:1; /* connection must do dane */
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index d897c691e..4d6135be7 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -2251,6 +2251,7 @@ if (!continue_hostname)
the helo string might use it avoid doing early-pipelining. */
if ( !sx->helo_data
+ || sx->conn_args.interface
|| !Ustrstr(sx->helo_data, "$sending_ip_address")
|| Ustrstr(sx->helo_data, "def:sending_ip_address")
)
@@ -2270,7 +2271,10 @@ if (!continue_hostname)
PIPE_CONNECT_RETRY:
if (sx->early_pipe_active)
+ {
sx->outblock.conn_args = &sx->conn_args;
+ (void) smtp_boundsock(&sx->conn_args);
+ }
else
#endif
{
@@ -2295,9 +2299,10 @@ PIPE_CONNECT_RETRY:
}
/* Expand the greeting message while waiting for the initial response. (Makes
sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
- delayed till here so that $sending_interface and $sending_port are set. */
-/*XXX early-pipe: they still will not be. Is there any way to find out what they
-will be? Somehow I doubt it. */
+ delayed till here so that $sending_ip_address and $sending_port are set.
+ Those will be known even for a TFO lazy-connect, having been set by the bind().
+ For early-pipe, we are ok if binding to a local interface; otherwise (if
+ $sending_ip_address is seen in helo_data) we disabled early-pipe above. */
if (sx->helo_data)
if (!(sx->helo_data = expand_string(sx->helo_data)))
@@ -3716,6 +3721,7 @@ sx->port = defport;
sx->conn_args.interface = interface;
sx->helo_data = NULL;
sx->conn_args.tblock = tblock;
+sx->conn_args.sock = -1;
gettimeofday(&sx->delivery_start, NULL);
sx->sync_addr = sx->first_addr = addrlist;
diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c
index 5aff62c15..4dd03c1f1 100644
--- a/src/src/transports/smtp_socks.c
+++ b/src/src/transports/smtp_socks.c
@@ -274,7 +274,7 @@ for(;;)
{
int idx;
host_item proxy;
- int proxy_af;
+ smtp_connect_args sc = {.sock = -1};
if ((idx = socks_get_proxy(proxies, nproxies)) < 0)
{
@@ -286,11 +286,16 @@ for(;;)
/* bodge up a host struct for the proxy */
proxy.address = proxy.name = sob->proxy_host;
- proxy_af = Ustrchr(sob->proxy_host, ':') ? AF_INET6 : AF_INET;
+ proxy.port = sob->port;
+
+ sc.tblock = tb;
+ sc.ob = ob;
+ sc.host = &proxy;
+ sc.host_af = Ustrchr(sob->proxy_host, ':') ? AF_INET6 : AF_INET;
+ sc.interface = interface;
/*XXX we trust that the method-select command is idempotent */
- if ((fd = smtp_sock_connect(&proxy, proxy_af, sob->port,
- interface, tb, sob->timeout, &early_data)) >= 0)
+ if ((fd = smtp_sock_connect(&sc, sob->timeout, &early_data)) >= 0)
{
proxy_local_address = string_copy(proxy.address);
proxy_local_port = sob->port;
diff --git a/src/src/verify.c b/src/src/verify.c
index d1c4af275..ed0898c9b 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -661,6 +661,7 @@ coding means skipping this whole loop and doing the append separately. */
sx->conn_args.interface = interface;
sx->helo_data = tf->helo_data;
sx->conn_args.tblock = addr->transport;
+ sx->conn_args.sock = -1;
sx->verify = TRUE;
tls_retry_connection:
diff --git a/test/confs/4050 b/test/confs/4050
index fd3d7db54..2523328f7 100644
--- a/test/confs/4050
+++ b/test/confs/4050
@@ -60,6 +60,9 @@ begin transports
smtp:
driver = smtp
+.ifdef BB
+ interface = BB
+.endif
hosts_try_fastopen = :
hosts_pipe_connect = CONTROL
.ifdef HELO_MSG
diff --git a/test/log/4051 b/test/log/4051
index c332087d7..d56d4061a 100644
--- a/test/log/4051
+++ b/test/log/4051
@@ -23,6 +23,11 @@
1999-03-02 09:44:33 10HmbF-0005vi-00 => helo_data@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmbG-0005vi-00"
1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for helo_data_bound@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbH-0005vi-00 => helo_data_bound@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 OK id=10HmbI-0005vi-00"
+1999-03-02 09:44:33 10HmbH-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
******** SERVER ********
1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
@@ -52,3 +57,8 @@
1999-03-02 09:44:33 10HmbG-0005vi-00 => :blackhole: <helo_data@test.ex> R=server
1999-03-02 09:44:33 10HmbG-0005vi-00 Completed
1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbI-0005vi-00 <= CALLER@the.local.host.name H=(127.0.0.1) [127.0.0.1] P=esmtp L* S=sss id=E10HmbH-0005vi-00@the.local.host.name for helo_data_bound@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbI-0005vi-00 => :blackhole: <helo_data_bound@test.ex> R=server
+1999-03-02 09:44:33 10HmbI-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/scripts/4050-pipe-conn/4051 b/test/scripts/4050-pipe-conn/4051
index dd5f4c7a8..e06bf5c7e 100644
--- a/test/scripts/4050-pipe-conn/4051
+++ b/test/scripts/4050-pipe-conn/4051
@@ -64,6 +64,15 @@ exim -DCONTROL=127.0.0.1 -DHELO_MSG='$sending_ip_address' -q
exim -DNOTDAEMON -DSERVER=server -q
****
#
+# ... unless the connection is bound to a specified interface
+exim helo_data_bound@test.ex
+Subject test 6
+****
+exim -DCONTROL=127.0.0.1 -DHELO_MSG='$sending_ip_address' -DBB=127.0.0.1 -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
#
killdaemon
no_msglog_check