From 81df60f6229e66dc8306e55ea2103e577782d984 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 21 Jan 2021 22:02:18 +0000 Subject: [PATCH] TLS: on Linux when sockopt TCP_FASTOPEN_CONNECT is available, use TFO for TLS-on-connect client connections --- src/src/smtp_out.c | 32 +++++++++++++++++++--- src/src/transports/smtp.c | 7 ++++- test/confs/1160 | 2 ++ test/log/1160 | 46 ++++++++++++++++---------------- test/scripts/1100-Basic-TLS/1160 | 27 +++++++++++++++++-- 5 files changed, 85 insertions(+), 29 deletions(-) diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 2d2fd2180..b1a25f70a 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -246,9 +246,18 @@ switch (tcp_out_fastopen) #endif -/* Arguments as for smtp_connect(), plus - early_data if non-NULL, idenmpotent data to be sent - +/* 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 */ @@ -318,8 +327,22 @@ early-data but no TFO support, send it after connecting. */ else { #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) - fastopen_blob = early_data ? early_data : &tcp_fastopen_nodata; + { + if (!early_data) + fastopen_blob = &tcp_fastopen_nodata; /* TFO, with no data */ + else if (early_data->data) + fastopen_blob = early_data; /* TFO, with data */ +# ifdef TCP_FASTOPEN_CONNECT + else + { /* expecting client data */ + debug_printf(" set up lazy-connect\n"); + setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, US &on, sizeof(on)); + /* fastopen_blob = NULL; lazy TFO, triggered by data write */ + } +# endif + } #endif if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0) @@ -409,6 +432,9 @@ host->address will always be an IPv4 address. Arguments: sc details for making connection: host, af, interface, transport early_data if non-NULL, 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 */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ee5e49e57..eb6b77416 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2098,7 +2098,12 @@ PIPE_CONNECT_RETRY: else #endif { - if ((sx->cctx.sock = smtp_connect(&sx->conn_args, NULL)) < 0) + blob lazy_conn = {.data = NULL}; + /* For TLS-connect, a TFO lazy-connect is useful since the Client Hello + can go on the TCP SYN. */ + + if ((sx->cctx.sock = smtp_connect(&sx->conn_args, + sx->smtps ? &lazy_conn : NULL)) < 0) { set_errno_nohost(sx->addrlist, errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno, diff --git a/test/confs/1160 b/test/confs/1160 index c4898310e..47bb2d5d1 100644 --- a/test/confs/1160 +++ b/test/confs/1160 @@ -20,6 +20,8 @@ tls_on_connect_ports = PORT_D tls_certificate = DIR/aux-fixed/cert1 +log_selector = +millisec + # ------ ACL ------ begin acl diff --git a/test/log/1160 b/test/log/1160 index f1c0aa8ab..f4c1efe1a 100644 --- a/test/log/1160 +++ b/test/log/1160 @@ -1,25 +1,25 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 Start queue run: pid=pppp -qf -1999-03-02 09:44:33 10HmaX-0005vi-00 => CALLER@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmaZ-0005vi-00" -1999-03-02 09:44:33 10HmaX-0005vi-00 Completed -1999-03-02 09:44:33 10HmaY-0005vi-00 => CALLER@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbA-0005vi-00" -1999-03-02 09:44:33 10HmaY-0005vi-00 -> xyz@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbA-0005vi-00" -1999-03-02 09:44:33 10HmaY-0005vi-00 => abcd@test.ex R=client T=send_to_server2 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbB-0005vi-00" -1999-03-02 09:44:33 10HmaY-0005vi-00 Completed -1999-03-02 09:44:33 End queue run: pid=pppp -qf +2017-07-30 18:51:05.712 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +2017-07-30 18:51:05.712 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +2017-07-30 18:51:05.712 Start queue run: pid=pppp -qf +2017-07-30 18:51:05.712 10HmaX-0005vi-00 => CALLER@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmaZ-0005vi-00" +2017-07-30 18:51:05.712 10HmaX-0005vi-00 Completed +2017-07-30 18:51:05.712 10HmaY-0005vi-00 => CALLER@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbA-0005vi-00" +2017-07-30 18:51:05.712 10HmaY-0005vi-00 -> xyz@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbA-0005vi-00" +2017-07-30 18:51:05.712 10HmaY-0005vi-00 => abcd@test.ex R=client T=send_to_server2 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbB-0005vi-00" +2017-07-30 18:51:05.712 10HmaY-0005vi-00 Completed +2017-07-30 18:51:05.712 End queue run: pid=pppp -qf ******** SERVER ******** -1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port PORT_D -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaY-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaY-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 Start queue run: pid=pppp -qf -1999-03-02 09:44:33 10HmaZ-0005vi-00 => CALLER R=server T=local_delivery -1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed -1999-03-02 09:44:33 10HmbA-0005vi-00 => CALLER R=server T=local_delivery -1999-03-02 09:44:33 10HmbA-0005vi-00 => xyz R=server T=local_delivery -1999-03-02 09:44:33 10HmbA-0005vi-00 Completed -1999-03-02 09:44:33 10HmbB-0005vi-00 => abcd R=server T=local_delivery -1999-03-02 09:44:33 10HmbB-0005vi-00 Completed -1999-03-02 09:44:33 End queue run: pid=pppp -qf +2017-07-30 18:51:05.712 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port PORT_D +2017-07-30 18:51:05.712 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex +2017-07-30 18:51:05.712 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaY-0005vi-00@myhost.test.ex +2017-07-30 18:51:05.712 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaY-0005vi-00@myhost.test.ex +2017-07-30 18:51:05.712 Start queue run: pid=pppp -qf +2017-07-30 18:51:05.712 10HmaZ-0005vi-00 => CALLER R=server T=local_delivery +2017-07-30 18:51:05.712 10HmaZ-0005vi-00 Completed +2017-07-30 18:51:05.712 10HmbA-0005vi-00 => CALLER R=server T=local_delivery +2017-07-30 18:51:05.712 10HmbA-0005vi-00 => xyz R=server T=local_delivery +2017-07-30 18:51:05.712 10HmbA-0005vi-00 Completed +2017-07-30 18:51:05.712 10HmbB-0005vi-00 => abcd R=server T=local_delivery +2017-07-30 18:51:05.712 10HmbB-0005vi-00 Completed +2017-07-30 18:51:05.712 End queue run: pid=pppp -qf diff --git a/test/scripts/1100-Basic-TLS/1160 b/test/scripts/1100-Basic-TLS/1160 index e57867e1c..77eef1f06 100644 --- a/test/scripts/1100-Basic-TLS/1160 +++ b/test/scripts/1100-Basic-TLS/1160 @@ -25,8 +25,31 @@ # Finished > # (otherwise the same). The extra segments are piplined and do not incur an extra roundtrip time. # -# exim -DSERVER=server -bd -oX PORT_D -sudo exim -DSERVER=server -d+tls -bd -oX PORT_D +# To see that pipelining: +# sudo tc qdisc add dev lo root netem delay 50ms / sudo tc qdisc delete dev lo root +# +# To test TFO, enable in the transport in the conf/ file +# With TFO we get the Client Hello on the SYN, and the initial Server segment pipelined with/after the SYN,ACK +# and before the 3rd-ACK. We still can't merge the 3rd-ACK with the second Client record set, +# but it does ack the initial Server data. +# +# To see the TFO((R): +# First clear any previously-obtained cookie: +#sudo perl +#open(INFO, "-|", "/usr/bin/uname -s"); +#$_ = ; +#if (/^FreeBSD/) { +#system("sysctl net.inet.tcp.fastopen.client_enable=0"); system("sysctl net.inet.tcp.fastopen.client_enable=1"); +#} else { +#system ("[ -e /proc/sys/net/ipv4/tcp_fastopen_blackhole_timeout_sec ] && echo 0 > /proc/sys/net/ipv4/tcp_fastopen_blackhole_timeout_sec"); +#system ("ip tcp_metrics delete 127.0.0.1"); +#} +# +#**** +# +# +# sudo exim -DSERVER=server -d+tls -bd -oX PORT_D +exim -DSERVER=server -bd -oX PORT_D **** exim CALLER@test.ex Test message. Contains FF: ΓΏ -- 2.30.2