if ( (sock = socket(SOCK_STREAM, AF_INET, 0)) < 0
&& setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog))
)
- tcp_fastopen_ok = TRUE;
+ f.tcp_fastopen_ok = TRUE;
close(sock);
# endif
}
port the remote port
timeout a timeout (zero for indefinite timeout)
fastopen_blob non-null iff TCP_FASTOPEN can be used; may indicate early-data to
- be sent in SYN segment
+ be sent in SYN segment. Any such data must be idempotent.
Returns: 0 on success; -1 on failure, with errno set
*/
callout_address = string_sprintf("[%s]:%d", address, port);
sigalrm_seen = FALSE;
-if (timeout > 0) alarm(timeout);
+if (timeout > 0) ALARM(timeout);
-#if defined(TCP_FASTOPEN) && defined(MSG_FASTOPEN)
+#ifdef TCP_FASTOPEN
/* 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
the SMTP banner. Other (than SMTP) cases of TCP connections can
-possibly use the data-on-syn, so support that too.
+possibly use the data-on-syn, so support that too. */
-This is a Linux implementation. It might be useable on FreeBSD; I have
-not checked. I think MacOS has a "connectx" call for this purpose,
-rather than using "sendto" ? */
-
-if (fastopen_blob && tcp_fastopen_ok)
+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. */
+
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("non-TFO mode connection attempt to %s, %lu data\n",
+ debug_printf("TFO mode connection attempt to %s, %lu data\n",
address, (unsigned long)fastopen_blob->len);
/*XXX also seen on successful TFO, sigh */
- tcp_out_fastopen = fastopen_blob->len > 0 ? 2 : 1;
+ 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 */
/* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */
fastopen_blob->len > 0 ? "with" : "no");
if (!fastopen_blob->data)
{
- tcp_out_fastopen = 1; /* we tried; unknown if useful yet */
+ tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */
rc = 0;
}
else
debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
goto legacy_connect;
}
+# endif
+# ifdef EXIM_TFO_CONNECTX
+ /* MacOS */
+ sa_endpoints_t ends = {
+ .sae_srcif = 0, .sae_srcaddr = NULL, .sae_srcaddrlen = 0,
+ .sae_dstaddr = s_ptr, .sae_dstaddrlen = s_len };
+ struct iovec iov = {
+ .iov_base = fastopen_blob->data, .iov_len = fastopen_blob->len };
+ size_t len;
+
+ if ((rc = connectx(sock, &ends, SAE_ASSOCID_ANY,
+ CONNECT_DATA_IDEMPOTENT, &iov, 1, &len, NULL)) == 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;
+
+ if (len != fastopen_blob->len)
+ DEBUG(D_transport|D_v)
+ debug_printf(" only queued %lu data!\n", (unsigned long)len);
+ }
+ else if (errno == EINPROGRESS)
+ {
+ 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 /* assume that no data was queued; block in send */
+ rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
+ }
+# endif
}
else
-#endif
+#endif /*TCP_FASTOPEN*/
{
legacy_connect:
DEBUG(D_transport|D_v) if (fastopen_blob)
}
save_errno = errno;
-alarm(0);
+ALARM_CLR(0);
/* There is a testing facility for simulating a connection timeout, as I
can't think of any other way of doing this. It converts a connection refused
into a timeout if the timeout is set to 999999. */
-if (running_in_test_harness && save_errno == ECONNREFUSED && timeout == 999999)
+if (f.running_in_test_harness && save_errno == ECONNREFUSED && timeout == 999999)
{
rc = -1;
save_errno = EINTR;
connhost if not NULL, host_item to be filled in with connection details
errstr pointer for allocated string on error
fastopen_blob with SOCK_STREAM, if non-null, request TCP Fast Open.
- Additionally, optional early-data to send
+ Additionally, optional idempotent early-data to send
Return:
socket fd, or -1 on failure (having allocated an error string)
ip_connectedsocket(int type, const uschar * hostname, int portlo, int porthi,
int timeout, host_item * connhost, uschar ** errstr, const blob * fastopen_blob)
{
-int namelen, port;
+int namelen;
host_item shost;
-host_item *h;
int af = 0, fd, fd4 = -1, fd6 = -1;
shost.next = NULL;
/* Try to connect to the server - test each IP till one works */
-for (h = &shost; h; h = h->next)
+for (host_item * h = &shost; h; h = h->next)
{
fd = Ustrchr(h->address, ':') != 0
? fd6 < 0 ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6
goto bad;
}
- for(port = portlo; port <= porthi; port++)
+ for (int port = portlo; port <= porthi; port++)
if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0)
{
if (fd != fd6) close(fd6);
void
dscp_list_to_stream(FILE *stream)
{
-int i;
-for (i=0; i < dscp_table_size; ++i)
+for (int i = 0; i < dscp_table_size; ++i)
fprintf(stream, "%s\n", dscp_table[i].name);
}