#include "local_scan.h"
#include "structs.h"
+#include "blob.h"
#include "globals.h"
#include "dbstuff.h"
#include "functions.h"
#include "macros.h"
#include "dbstuff.h"
#include "structs.h"
+#include "blob.h"
#include "globals.h"
#include "hash.h"
#include "functions.h"
extern void invert_address(uschar *, uschar *);
extern int ip_addr(void *, int, const uschar *, int);
extern int ip_bind(int, int, uschar *, int);
-extern int ip_connect(int, int, const uschar *, int, int, BOOL);
+extern int ip_connect(int, int, const uschar *, int, int, const blob *);
extern int ip_connectedsocket(int, const uschar *, int, int,
int, host_item *, uschar **);
extern int ip_get_address_family(int);
extern int smtp_connect(host_item *, int, uschar *, int,
transport_instance *);
extern int smtp_sock_connect(host_item *, int, int, uschar *,
- transport_instance * tb, int);
+ transport_instance * tb, int, const blob *);
extern int smtp_feof(void);
extern int smtp_ferror(void);
extern uschar *smtp_get_connection_info(void);
BOOL system_filtering = FALSE;
BOOL tcp_fastopen_ok = FALSE;
+blob tcp_fastopen_nodata = { .data = NULL, .len = 0 };
BOOL tcp_in_fastopen = FALSE;
BOOL tcp_in_fastopen_logged = FALSE;
BOOL tcp_nodelay = TRUE;
extern BOOL system_filtering; /* TRUE when running system filter */
extern BOOL tcp_fastopen_ok; /* appears to be supported by kernel */
+extern blob tcp_fastopen_nodata; /* for zero-data TFO connect requests */
extern BOOL tcp_in_fastopen; /* conn used fastopen */
extern BOOL tcp_in_fastopen_logged; /* one-time logging */
extern BOOL tcp_nodelay; /* Controls TCP_NODELAY on daemon */
#define HASH_H
#include "sha_ver.h"
-#include "blob.h"
#ifdef SHA_OPENSSL
# include <openssl/sha.h>
address the remote address, in text form
port the remote port
timeout a timeout (zero for indefinite timeout)
- fastopen TRUE iff TCP_FASTOPEN can be used
+ fastopen 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,
- BOOL fastopen)
+ const blob * fastopen)
{
struct sockaddr_in s_in4;
struct sockaddr *s_ptr;
if (fastopen)
{
- if ((rc = sendto(sock, NULL, 0, MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) < 0)
- if (errno == EINPROGRESS) /* the expected case */
- rc = 0;
+ if ((rc = sendto(sock, fastopen->data, fastopen->len,
+ MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) < 0)
+ if (errno == EINPROGRESS) /* expected for nonready peer */
+ { /* queue the data */
+ if ( (rc = send(sock, fastopen->data, fastopen->len, 0)) < 0
+ && errno == EINPROGRESS) /* expected for nonready peer */
+ rc = 0;
+ }
else if(errno == EOPNOTSUPP)
{
DEBUG(D_transport)
debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
- rc = connect(sock, s_ptr, s_len);
+ goto legacy_connect;
}
}
else
#endif
- rc = connect(sock, s_ptr, s_len);
+ {
+legacy_connect:
+ if ((rc = connect(sock, s_ptr, s_len)) >= 0)
+ if ( fastopen && fastopen->data && fastopen->len
+ && send(sock, fastopen->data, fastopen->len, 0) < 0)
+ rc = -1;
+ }
save_errno = errno;
alarm(0);
timeout a timeout
connhost if not NULL, host_item filled in with connection details
errstr pointer for allocated string on error
+XXX could add early-data support
Return:
socket fd, or -1 on failure (having allocated an error string)
host_item shost;
host_item *h;
int af = 0, fd, fd4 = -1, fd6 = -1;
-BOOL fastopen = tcp_fastopen_ok && type == SOCK_STREAM;
+blob * fastopen = tcp_fastopen_ok && type == SOCK_STREAM
+ ? &tcp_fastopen_nodata : NULL;
shost.next = NULL;
shost.address = NULL;
/* Connect to the remote host, under a timeout. In fact, timeouts can occur
here only for TCP calls; for a UDP socket, "connect" always works (the
router will timeout later on the read call). */
+/*XXX could take advantage of TFO */
if (ip_connect(query_socket, host_af, h->address,ob->port, ob->timeout,
- ob->protocol != ip_udp) < 0)
+ ob->protocol == ip_udp ? NULL : &tcp_fastopen_nodata) < 0)
{
close(query_socket);
DEBUG(D_route)
#endif
+/* Arguments as for smtp_connect(), plus
+ early_data if non-NULL, data to be sent - preferably in the TCP SYN segment
+
+Returns: connected socket number, or -1 with errno set
+*/
+
int
smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface,
- transport_instance * tb, int timeout)
+ transport_instance * tb, int timeout, const blob * early_data)
{
smtp_transport_options_block * ob =
(smtp_transport_options_block *)tb->options_block;
int dscp_option;
int sock;
int save_errno = 0;
-BOOL fastopen = FALSE;
#ifndef DISABLE_EVENT
deliver_host_address = host->address;
(void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value));
}
-#ifdef TCP_FASTOPEN
-if (verify_check_given_host (&ob->hosts_try_fastopen, host) == OK) fastopen = TRUE;
-#endif
-
/* Bind to a specific interface if requested. Caller must ensure the interface
is the same type (IPv4 or IPv6) as the outgoing address. */
}
/* Connect to the remote host, and add keepalive to the socket before returning
-it, if requested. */
+it, if requested. If the build supports TFO, request it - and if the caller
+requested some early-data then include that in the TFO request. */
-else if (ip_connect(sock, host_af, host->address, port, timeout, fastopen) < 0)
- save_errno = errno;
+else
+ {
+ const blob * fastopen = NULL;
+
+#ifdef TCP_FASTOPEN
+ if (verify_check_given_host(&ob->hosts_try_fastopen, host) == OK)
+ fastopen = early_data ? early_data : &tcp_fastopen_nodata;
+#endif
+
+ if (ip_connect(sock, host_af, host->address, port, timeout, fastopen) < 0)
+ save_errno = errno;
+ else if (early_data && !fastopen && early_data->data && early_data->len)
+ if (send(sock, early_data->data, early_data->len, 0) < 0)
+ save_errno = errno;
+ }
/* Either bind() or connect() failed */
return socks_sock_connect(host, host_af, port, interface, tb, timeout);
#endif
-return smtp_sock_connect(host, host_af, port, interface, tb, timeout);
+return smtp_sock_connect(host, host_af, port, interface, tb, timeout, NULL);
}
/* Connect never fails for a UDP socket, so don't set a timeout. */
- (void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0, FALSE);
+ (void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0, NULL);
rc = send(sock, buffer, Ustrlen(buffer) + 1, 0);
(void)close(sock);
unsigned nproxies;
socks_opts * sob;
unsigned size;
+blob early_data;
if (!timeout) timeout = 24*60*60; /* use 1 day for "indefinite" */
tmo = time(NULL) + timeout;
socks_option(sob, option);
}
+/* Set up the socks protocol method-selection message,
+for sending on connection */
+
+state = US"method select";
+buf[0] = 5; buf[1] = 1; buf[2] = sob->auth_type;
+early_data.data = buf;
+early_data.len = 3;
+
/* Try proxies until a connection succeeds */
for(;;)
proxy_af = Ustrchr(sob->proxy_host, ':') ? AF_INET6 : AF_INET;
if ((fd = smtp_sock_connect(&proxy, proxy_af, sob->port,
- interface, tb, sob->timeout)) >= 0)
+ interface, tb, sob->timeout, &early_data)) >= 0)
{
proxy_local_address = string_copy(proxy.address);
proxy_local_port = sob->port;
}
/* Do the socks protocol stuff */
-/* Send method-selection */
-state = US"method select";
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SOCKS>> 05 01 %02x\n", sob->auth_type);
-buf[0] = 5; buf[1] = 1; buf[2] = sob->auth_type;
-if (send(fd, buf, 3, 0) < 0)
- goto snd_err;
/* expect method response */
goto END_OFF;
}
+/*XXX could take advantage of TFO early-data. Hmm, what are the
+error returns; can we differentiate connect from data fails?
+Do we need to? */
if (ip_connect(sock, host_af, sender_host_address, port,
- rfc1413_query_timeout, TRUE) < 0)
+ rfc1413_query_timeout, &tcp_fastopen_nodata) < 0)
{
if (errno == ETIMEDOUT && LOGGING(ident_timeout))
log_write(0, LOG_MAIN, "ident connection to %s timed out",
int rc;
unsigned int *local_cache_bits = cache_bits;
const uschar *save_host_address = deliver_host_address;
-check_host_block cb;
-cb.host_name = host_name;
-cb.host_address = host_address;
+check_host_block cb = { .host_name = host_name, .host_address = host_address };
-if (valueptr != NULL) *valueptr = NULL;
+if (valueptr) *valueptr = NULL;
/* If the host address starts off ::ffff: it is an IPv6 address in
IPv4-compatible mode. Find the IPv4 part for checking against IPv4
addresses. */
-cb.host_ipv4 = (Ustrncmp(host_address, "::ffff:", 7) == 0)?
- host_address + 7 : host_address;
+cb.host_ipv4 = Ustrncmp(host_address, "::ffff:", 7) == 0
+ ? host_address + 7 : host_address;
/* During the running of the check, put the IP address into $host_address. In
the case of calls from the smtp transport, it will already be there. However,
begin transports
my_smtp:
- driver = smtp
- interface = HOSTIPV4
- port = PORT_S
- hide socks_proxy = 127.0.0.1 port=PORT_D OPT
- debug_print = transport_name <$transport_name>
+ driver = smtp
+ interface = HOSTIPV4
+ port = PORT_S
+ hide socks_proxy = 127.0.0.1 port=PORT_D OPT
+ hosts_try_fastopen = ${if eq {$local_part}{user_tfo} {*}}
+ debug_print = transport_name <$transport_name>
# End
--- /dev/null
+4020
\ No newline at end of file
--- /dev/null
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => user_tfo@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 C="250 accepted OK"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 => user_tfo@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 C="250 accepted OK"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
--- /dev/null
+# socks5 proxy on smtp transport, TCP Fast Open
+#
+munge loopback
+#
+#
+# TFO client, not server
+server PORT_D
+<<\x05\x01\x00
+>>\x05\x00
+<<\x05\x01\x00\x01\x7f\x00\x00\x01\x04\xc8
+>>\x05\x00\x00\x01\x7f\x00\x00\x01\xbe\xef
+220 Connected OK
+EHLO
+250-server id
+250
+MAIL FROM
+250
+RCPT TO
+250
+DATA
+354 do me
+.
+250 accepted OK
+QUIT
+250 bye
+****
+#
+#
+exim -odi -bs -DOPT=
+ehlo test.ex
+mail from:<>
+rcpt to:<user_tfo@test.ex>
+data
+Date: Fri, 17 Dec 2004 14:35:01 +0100
+Subject: message should be sent
+
+connection trying TFO
+via null-auth proxy
+.
+quit
+****
+#
+#
+#
+# TFO client and server
+server -tfo PORT_D
+<<\x05\x01\x00
+>>\x05\x00
+<<\x05\x01\x00\x01\x7f\x00\x00\x01\x04\xc8
+>>\x05\x00\x00\x01\x7f\x00\x00\x01\xbe\xef
+220 Connected OK
+EHLO
+250-server id
+250
+MAIL FROM
+250
+RCPT TO
+250
+DATA
+354 do me mate
+.
+250 accepted OK
+QUIT
+250 bye
+****
+#
+#
+exim -odi -bs -DOPT=
+ehlo test.ex
+mail from:<>
+rcpt to:<user_tfo@test.ex>
+data
+Date: Fri, 17 Dec 2004 14:35:01 +0100
+Subject: message should be sent
+
+connection using TFO
+via null-auth proxy
+.
+quit
+****
+#
+#
+# Ends
--- /dev/null
+support SOCKS
+support TCP_Fast_Open
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
+#include <netinet/tcp.h>
#ifdef HAVE_NETINET_IP_VAR_H
# include <netinet/ip_var.h>
int count;
int on = 1;
int timeout = 5;
-int initial_pause = 0;
+int initial_pause = 0, tfo = 0;
int use_ipv4 = 1;
int use_ipv6 = 1;
int debug = 0;
"\n\t-noipv6 disable ipv6"
"\n\t-oP file write PID to file"
"\n\t-t n n seconds timeout"
+ "\n\t-tfo enable TCP Fast Open"
);
exit(0);
}
while (na < argc && argv[na][0] == '-')
{
if (strcmp(argv[na], "-d") == 0) debug = 1;
+ else if (strcmp(argv[na], "-tfo") == 0) tfo = 1;
else if (strcmp(argv[na], "-t") == 0)
{
if (tmo_noerror = ((timeout = atoi(argv[++na])) < 0)) timeout = -timeout;
exit(1);
}
+ if (tfo)
+ {
+ int backlog = 5;
+ if (setsockopt(listen_socket[v6n], IPPROTO_TCP, TCP_FASTOPEN,
+ &backlog, sizeof(backlog)))
+ if (debug) printf("setsockopt TCP_FASTOPEN: %s\n", strerror(errno));
+ }
+
/* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
available. */
printf("IPv4 socket creation failed: %s\n", strerror(errno));
exit(1);
}
+ if (tfo)
+ {
+ int backlog = 5;
+ if (setsockopt(listen_socket[v4n], IPPROTO_TCP, TCP_FASTOPEN,
+ &backlog, sizeof(backlog)))
+ if (debug) printf("setsockopt TCP_FASTOPEN: %s\n", strerror(errno));
+ }
+
}
}
--- /dev/null
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaX-0005vi-00\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaY-0005vi-00\r
+221 myhost.test.ex closing connection\r
+
+******** SERVER ********
+Listening on port 1225 ...
+Connection request from [ip4.ip4.ip4.ip4]
+<<\x05\x01\x00
+>>\x05\x00
+<<\x05\x01\x00\x01\x7f\x00\x00\x01\x04\xc8
+>>\x05\x00\x00\x01\x7f\x00\x00\x01\xbe\xef
+220 Connected OK
+EHLO
+250-server id
+250
+MAIL FROM
+250
+RCPT TO
+250
+DATA
+354 do me
+R
+250 accepted OK
+QUIT
+250 bye
+End of script
+Listening on port 1225 ...
+Connection request from [ip4.ip4.ip4.ip4]
+<<\x05\x01\x00
+>>\x05\x00
+<<\x05\x01\x00\x01\x7f\x00\x00\x01\x04\xc8
+>>\x05\x00\x00\x01\x7f\x00\x00\x01\xbe\xef
+220 Connected OK
+EHLO
+250-server id
+250
+MAIL FROM
+250
+RCPT TO
+250
+DATA
+354 do me mate
+R
+250 accepted OK
+QUIT
+250 bye
+End of script