* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for doing things with sockets. With the advent of IPv6 this has
+/*************************************************
+*************************************************/
+
+#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 *
*************************************************/
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
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;
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)
+if (fastopen_blob && f.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("TCP_FASTOPEN mode connection, with data\n");
- tcp_out_fastopen = TRUE;
+ debug_printf("non-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;
}
- else if (errno == EINPROGRESS) /* expected for nonready peer */
- {
- if (!fastopen->data)
+ 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 */
+ /* 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)
{
- DEBUG(D_transport|D_v)
- debug_printf("TCP_FASTOPEN mode connection, no data\n");
- tcp_out_fastopen = TRUE;
+ tcp_out_fastopen = 1; /* we tried; unknown if useful yet */
rc = 0;
}
- else if ( (rc = send(sock, fastopen->data, fastopen->len, 0)) < 0
- && errno == EINPROGRESS) /* expected for nonready peer */
- rc = 0;
+ else
+ rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
}
else if(errno == EOPNOTSUPP)
{
#endif
{
legacy_connect:
+ DEBUG(D_transport|D_v) if (fastopen_blob)
+ debug_printf("non-TFO mode connection attempt to %s, %lu data\n",
+ address, (unsigned long)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;
}
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;
Arguments:
type SOCK_DGRAM or SOCK_STREAM
af AF_INET6 or AF_INET for the socket type
- address the remote address, in text form
+ hostname host name, or ip address (as text)
portlo,porthi the remote port range
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:
*/
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;
}
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);
result but no ready descriptor. Is this in fact possible?
Arguments:
- sock the socket
+ cctx the connection context (socket fd, possibly TLS context)
buffer to read into
bufsize the buffer size
timeout the timeout
*/
int
-ip_recv(int sock, uschar *buffer, int buffsize, int timeout)
+ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, int timeout)
{
int rc;
-if (!fd_ready(sock, timeout))
+if (!fd_ready(cctx->sock, timeout))
return -1;
/* The socket is ready, read from it (via TLS if it's active). On EOF (i.e.
close down of the connection), set errno to zero; otherwise leave it alone. */
#ifdef SUPPORT_TLS
-if (tls_out.active == sock)
- rc = tls_read(FALSE, buffer, buffsize);
-else if (tls_in.active == sock)
- rc = tls_read(TRUE, buffer, buffsize);
+if (cctx->tls_ctx) /* client TLS */
+ rc = tls_read(cctx->tls_ctx, buffer, buffsize);
+else if (tls_in.active.sock == cctx->sock) /* server TLS */
+ rc = tls_read(NULL, buffer, buffsize);
else
#endif
- rc = recv(sock, buffer, buffsize, 0);
+ rc = recv(cctx->sock, buffer, buffsize, 0);
if (rc > 0) return rc;
if (rc == 0) errno = 0;