TFO: on FreeBSD avoid client TFO-mode connects unless a TCP_FASTOPEN setsocketopt...
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 8 Oct 2017 15:12:06 +0000 (16:12 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 8 Oct 2017 16:30:39 +0000 (17:30 +0100)
This fixes the FreeSBD no-kernel-support issues (it succeded the sendto for the
connect-with-data, but dod not queue the data).  Having checked dynamically, do
not claim support-for TFO either.

src/OS/os.h-FreeBSD
src/src/deliver.c
src/src/exim.c
src/src/functions.h
src/src/ip.c
src/src/malware.c
src/src/smtp_out.c

index 3a06e766ed365a75e01f8ac99305e309a57ad8d3..7cb836cbebb35360f87db2a289d8545e655c7fab 100644 (file)
@@ -48,6 +48,7 @@ extern ssize_t os_sendfile(int, int, off_t *, size_t);
 
 /* TCP_FASTOPEN support.  There does not seems to be a
 MSG_FASTOPEN defined yet... */
+#define EXIM_TFO_PROBE
 
 #include <netinet/tcp.h>        /* for TCP_FASTOPEN */
 #include <sys/socket.h>         /* for MSG_FASTOPEN */
index 648c63d691d6e977ceac9705a93dcbf619cb6d99..df51948d4035231444ae8c72c3770423305063fc 100644 (file)
@@ -8413,6 +8413,13 @@ return final_yield;
 void
 deliver_init(void)
 {
+#ifdef EXIM_TFO_PROBE
+tfo_probe();
+#else
+tcp_fastopen_ok = TRUE;
+#endif
+
+
 if (!regex_PIPELINING) regex_PIPELINING =
   regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
 
index f09b26902a3a7b77b818b741503f0501c3a870f9..f0c00cdf137d03af4f8501158828d9dd2349a9a4 100644 (file)
@@ -840,7 +840,8 @@ fprintf(f, "Support for:");
   fprintf(f, " SOCKS");
 #endif
 #ifdef TCP_FASTOPEN
-  fprintf(f, " TCP_Fast_Open");
+  deliver_init();
+  if (tcp_fastopen_ok) fprintf(f, " TCP_Fast_Open");
 #endif
 #ifdef EXPERIMENTAL_LMDB
   fprintf(f, " Experimental_LMDB");
index 9d1f6dc6e118ac7ae2c8e5b8750fa0ed43fa1979..e9f71c984f690eb6200ba62b1fe019d40965bdfe 100644 (file)
@@ -474,6 +474,9 @@ extern int     strcmpic(const uschar *, const uschar *);
 extern int     strncmpic(const uschar *, const uschar *, int);
 extern uschar *strstric(uschar *, uschar *, BOOL);
 
+#ifdef EXIM_TFO_PROBE
+extern void    tfo_probe(void);
+#endif
 extern void    timesince(struct timeval * diff, struct timeval * then);
 extern void    tls_modify_variables(tls_support *);
 extern uschar *tod_stamp(int);
index e11aef98580587f6b1a7a094e88fd401eccd12b5..ee91293fafece78926e8faba4d19a35cc3deff31 100644 (file)
@@ -160,6 +160,27 @@ 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,
+                   &connect_backlog, sizeof(smtp_connect_backlog))
+   )
+  tcp_fastopen_ok = TRUE;
+close(sock);
+# endif
+}
+#endif
+
+
 /*************************************************
 *        Connect socket to remote host           *
 *************************************************/
@@ -175,7 +196,7 @@ Arguments:
   address     the remote address, in text form
   port        the remote port
   timeout     a timeout (zero for indefinite timeout)
-  fastopen    non-null iff TCP_FASTOPEN can be used; may indicate early-data to
+  fastopen_blob    non-null iff TCP_FASTOPEN can be used; may indicate early-data to
                be sent in SYN segment
 
 Returns:      0 on success; -1 on failure, with errno set
@@ -183,7 +204,7 @@ Returns:      0 on success; -1 on failure, with errno set
 
 int
 ip_connect(int sock, int af, const uschar *address, int port, int timeout,
-  const blob * fastopen)
+  const blob * fastopen_blob)
 {
 struct sockaddr_in s_in4;
 struct sockaddr *s_ptr;
@@ -232,16 +253,17 @@ before it gets our ACK of its SYN,ACK - the latter is useful for
 the SMTP banner.  Other (than SMTP) cases of TCP connections can
 possibly use the data-on-syn, so support that too.  */
 
-if (fastopen)
+if (fastopen_blob && tcp_fastopen_ok)
   {
-  if ((rc = sendto(sock, fastopen->data, fastopen->len,
+  if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len,
                    MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) >= 0)
        /* seen for with-data, experimental TFO option, with-cookie case */
        /* seen for with-data, proper TFO opt, with-cookie case */
     {
-    DEBUG(D_transport|D_v) debug_printf("TFO mode connection attempt, %s data\n",
-      fastopen->len > 0 ? "with"  : "no");
-    tcp_out_fastopen = fastopen->len > 0 ?  2 : 1;
+    DEBUG(D_transport|D_v)
+      debug_printf("non-TFO mode connection attempt to %s, %d data\n",
+       address, fastopen_blob->len);
+    tcp_out_fastopen = fastopen_blob->len > 0 ?  2 : 1;
     }
   else if (errno == EINPROGRESS)       /* expected if we had no cookie for peer */
        /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */
@@ -251,14 +273,14 @@ if (fastopen)
        /* ? older Experimental TFO option behaviour ? */
     {                                  /* queue unsent data */
     DEBUG(D_transport|D_v) debug_printf("TFO mode sendto, %s data: EINPROGRESS\n",
-      fastopen->len > 0 ? "with"  : "no");
-    if (!fastopen->data)
+      fastopen_blob->len > 0 ? "with"  : "no");
+    if (!fastopen_blob->data)
       {
       tcp_out_fastopen = 1;            /* we tried; unknown if useful yet */
       rc = 0;
       }
     else
-      rc = send(sock, fastopen->data, fastopen->len, 0);
+      rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
     }
   else if(errno == EOPNOTSUPP)
     {
@@ -271,9 +293,12 @@ else
 #endif
   {
 legacy_connect:
+  DEBUG(D_transport|D_v) if (fastopen_blob)
+    debug_printf("non-TFO mode connection attempt to %s, %d data\n",
+      address, fastopen_blob->len);
   if ((rc = connect(sock, s_ptr, s_len)) >= 0)
-    if (  fastopen && fastopen->data && fastopen->len
-       && send(sock, fastopen->data, fastopen->len, 0) < 0)
+    if (  fastopen_blob && fastopen_blob->data && fastopen_blob->len
+       && send(sock, fastopen_blob->data, fastopen_blob->len, 0) < 0)
        rc = -1;
   }
 
@@ -320,7 +345,7 @@ Arguments:
   timeout       a timeout
   connhost     if not NULL, host_item to be filled in with connection details
   errstr        pointer for allocated string on error
-  fastopen     with SOCK_STREAM, if non-null, request TCP Fast Open.
+  fastopen_blob        with SOCK_STREAM, if non-null, request TCP Fast Open.
                Additionally, optional early-data to send
 
 Return:
@@ -328,7 +353,7 @@ Return:
 */
 int
 ip_connectedsocket(int type, const uschar * hostname, int portlo, int porthi,
-      int timeout, host_item * connhost, uschar ** errstr, const blob * fastopen)
+      int timeout, host_item * connhost, uschar ** errstr, const blob * fastopen_blob)
 {
 int namelen, port;
 host_item shost;
@@ -389,7 +414,7 @@ for (h = &shost; h; h = h->next)
     }
 
   for(port = portlo; port <= porthi; port++)
-    if (ip_connect(fd, af, h->address, port, timeout, fastopen) == 0)
+    if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0)
       {
       if (fd != fd6) close(fd6);
       if (fd != fd4) close(fd4);
index 32f2e9e492461fd4209e70570925df71a29bf4fb..e39cea8148f095adfcedb112be9f55a4639cf4e3 100644 (file)
@@ -147,10 +147,10 @@ uses the returned in_addr to get a second connection to the same system.
 */
 static inline int
 m_tcpsocket(const uschar * hostname, unsigned int port,
-       host_item * host, uschar ** errstr, const blob * fastopen)
+       host_item * host, uschar ** errstr, const blob * fastopen_blob)
 {
 return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
-                         host, errstr, fastopen);
+                         host, errstr, fastopen_blob);
 }
 
 static int
index 786f8b592802d1e05df8cf932e6de1fe92a3325f..a507ed0e8fd18bb31e7764c106c765db2d1d2bf8 100644 (file)
@@ -212,7 +212,7 @@ int dscp_level;
 int dscp_option;
 int sock;
 int save_errno = 0;
-const blob * fastopen = NULL;
+const blob * fastopen_blob = NULL;
 
 
 #ifndef DISABLE_EVENT
@@ -265,12 +265,12 @@ else
   {
 #ifdef TCP_FASTOPEN
   if (verify_check_given_host(&ob->hosts_try_fastopen, host) == OK)
-    fastopen = early_data ? early_data : &tcp_fastopen_nodata;
+    fastopen_blob = early_data ? early_data : &tcp_fastopen_nodata;
 #endif
 
-  if (ip_connect(sock, host_af, host->address, port, timeout, fastopen) < 0)
+  if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0)
     save_errno = errno;
-  else if (early_data && !fastopen && early_data->data && early_data->len)
+  else if (early_data && !fastopen_blob && early_data->data && early_data->len)
     if (send(sock, early_data->data, early_data->len, 0) < 0)
       save_errno = errno;
   }
@@ -309,7 +309,7 @@ else
     }
   if (ob->keepalive) ip_keepalive(sock, host->address, TRUE);
 #ifdef TCP_FASTOPEN
-  if (fastopen) tfo_out_check(sock);
+  if (fastopen_blob) tfo_out_check(sock);
 #endif
   return sock;
   }