X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/f3ebb786e451da973560f1c9d8cdb151d25108b5..dbbc1c20b59dc10368e31a7c81f110eb40b36494:/src/src/ip.c diff --git a/src/src/ip.c b/src/src/ip.c index 19be51a03..96a6c29f2 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for doing things with sockets. With the advent of IPv6 this has @@ -14,6 +15,12 @@ different places in the code where sockets are used. */ #include "exim.h" +#if defined(TCP_FASTOPEN) +# if defined(MSG_FASTOPEN) || defined(EXIM_TFO_CONNECTX) || defined(EXIM_TFO_FREEBSD) +# define EXIM_SUPPORT_TFO +# endif +#endif + /************************************************* * Create a socket * *************************************************/ @@ -120,8 +127,6 @@ if (af == AF_INET6) return sizeof(sin->v6); } else -#else /* HAVE_IPv6 */ -af = af; /* Avoid compiler warning */ #endif /* HAVE_IPV6 */ /* Setup code when using IPv4 socket. The wildcard address is "". */ @@ -160,26 +165,6 @@ return bind(sock, (struct sockaddr *)&sin, s_len); -/************************************************* -*************************************************/ - -#ifdef EXIM_TFO_PROBE -void -tfo_probe(void) -{ -# ifdef TCP_FASTOPEN -int sock, backlog = 5; - -if ( (sock = socket(SOCK_STREAM, AF_INET, 0)) < 0 - && setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog)) - ) - f.tcp_fastopen_ok = TRUE; -close(sock); -# endif -} -#endif - - /************************************************* * Connect socket to remote host * *************************************************/ @@ -222,8 +207,6 @@ if (af == AF_INET6) s_len = sizeof(s_in6); } else -#else /* HAVE_IPV6 */ -af = af; /* Avoid compiler warning */ #endif /* HAVE_IPV6 */ /* For an IPv4 address, use an IPv4 sockaddr structure, even on a system with @@ -245,7 +228,7 @@ callout_address = string_sprintf("[%s]:%d", address, port); sigalrm_seen = FALSE; if (timeout > 0) ALARM(timeout); -#ifdef TCP_FASTOPEN +#ifdef EXIM_SUPPORT_TFO /* TCP Fast Open, if the system has a cookie from a previous call to this peer, can send data in the SYN packet. The peer can send data before it gets our ACK of its SYN,ACK - the latter is useful for @@ -255,8 +238,7 @@ possibly use the data-on-syn, so support that too. */ if (fastopen_blob && f.tcp_fastopen_ok) { # ifdef MSG_FASTOPEN - /* This is a Linux implementation. It might be useable on FreeBSD; I have - not checked. */ + /* This is a Linux implementation. */ if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len, MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) >= 0) @@ -269,31 +251,55 @@ if (fastopen_blob && f.tcp_fastopen_ok) /*XXX also seen on successful TFO, sigh */ tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA; } - else if (errno == EINPROGRESS) /* expected if we had no cookie for peer */ + else switch (errno) + { + case EINPROGRESS: /* expected if we had no cookie for peer */ /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */ /* apparently no visibility of the diffference at this point */ /* seen for with-data, proper TFO opt, cookie-req */ /* with netwk delay, post-conn tcp_info sees unacked 1 for R, 2 for C; code in smtp_out.c */ /* ? older Experimental TFO option behaviour ? */ - { /* queue unsent data */ - DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", - fastopen_blob->len > 0 ? "with" : "no"); - if (!fastopen_blob->data) - { - tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ - rc = 0; - } - else - rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); + DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", + fastopen_blob->len > 0 ? "with" : "no"); + if (!fastopen_blob->data) + { + tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ + rc = 0; + } + else /* queue unsent data */ + rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); + break; + + case EOPNOTSUPP: + DEBUG(D_transport) + debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); + goto legacy_connect; + + case EPIPE: + DEBUG(D_transport) + debug_printf("Tried TCP Fast Open but kernel too old to support it\n"); + goto legacy_connect; } - else if(errno == EOPNOTSUPP) + +# elif defined(EXIM_TFO_FREEBSD) + /* Re: https://people.freebsd.org/~pkelsey/tfo-tools/tfo-client.c */ + + if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &on, sizeof(on)) < 0) { DEBUG(D_transport) debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); goto legacy_connect; } -# endif -# ifdef EXIM_TFO_CONNECTX + if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len, 0, + s_ptr, s_len)) >= 0) + { + DEBUG(D_transport|D_v) + debug_printf(" TFO mode connection attempt to %s, %lu data\n", + address, (unsigned long)fastopen_blob->len); + tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA; + } + +# elif defined(EXIM_TFO_CONNECTX) /* MacOS */ sa_endpoints_t ends = { .sae_srcif = 0, .sae_srcaddr = NULL, .sae_srcaddrlen = 0, @@ -329,9 +335,9 @@ if (fastopen_blob && f.tcp_fastopen_ok) # endif } else -#endif /*TCP_FASTOPEN*/ +#endif /*EXIM_SUPPORT_TFO*/ { -#if defined(TCP_FASTOPEN) && defined(MSG_FASTOPEN) +#if defined(EXIM_SUPPORT_TFO) && !defined(EXIM_TFO_CONNECTX) legacy_connect: #endif @@ -457,8 +463,8 @@ for (host_item * h = &shost; h; h = h->next) for (int port = portlo; port <= porthi; port++) if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0) { - if (fd != fd6) close(fd6); - if (fd != fd4) close(fd4); + if (fd6 >= 0 && fd != fd6) close(fd6); + if (fd4 >= 0 && fd != fd4) close(fd4); if (connhost) { h->port = port; @@ -479,7 +485,8 @@ bad: /*XXX TFO? */ int -ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo) +ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo, + host_item * connhost) { int scan; uschar hostname[256]; @@ -498,7 +505,7 @@ if (scan != 3) } return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh, - tmo, NULL, errstr, NULL); + tmo, connhost, errstr, NULL); } int @@ -528,11 +535,18 @@ if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) return sock; } +/* spec is either an absolute path (with a leading /), or +a host (name or IP) and port (whitespace-separated). +The port can be a range, dash-separated, or a single number. + +For a TCP socket, optionally fill in a host_item. +*/ int -ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo) +ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo, + host_item * connhost) { return *spec == '/' - ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo); + ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo, connhost); } /*************************************************