diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/scripts/MakeLinks | 1 | ||||
-rw-r--r-- | src/src/EDITME | 3 | ||||
-rw-r--r-- | src/src/config.h.defaults | 3 | ||||
-rw-r--r-- | src/src/exim.c | 3 | ||||
-rw-r--r-- | src/src/functions.h | 10 | ||||
-rw-r--r-- | src/src/ip.c | 64 | ||||
-rw-r--r-- | src/src/smtp_out.c | 142 | ||||
-rw-r--r-- | src/src/transports/Makefile | 3 | ||||
-rw-r--r-- | src/src/transports/smtp.c | 14 | ||||
-rw-r--r-- | src/src/transports/smtp.h | 8 | ||||
-rw-r--r-- | src/src/transports/smtp_socks.c | 310 | ||||
-rw-r--r-- | src/src/verify.c | 9 |
12 files changed, 455 insertions, 115 deletions
diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index a50f75b36..f68fd6f4b 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -105,6 +105,7 @@ ln -s ../../src/transports/pipe.h pipe.h ln -s ../../src/transports/pipe.c pipe.c ln -s ../../src/transports/smtp.h smtp.h ln -s ../../src/transports/smtp.c smtp.c +ln -s ../../src/transports/smtp_socks.c smtp_socks.c ln -s ../../src/transports/tf_maildir.c tf_maildir.c ln -s ../../src/transports/tf_maildir.h tf_maildir.h diff --git a/src/src/EDITME b/src/src/EDITME index 353dde3ee..d48f268b9 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -494,6 +494,9 @@ EXIM_MONITOR=eximon.bin # Uncomment the following line to add DANE support # EXPERIMENTAL_DANE=yes +# Uncomment the following line to add SOCKS support +# EXPERIMENTAL_SOCKS=yes + ############################################################################### # THESE ARE THINGS YOU MIGHT WANT TO SPECIFY # ############################################################################### diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index e339bdf9e..ac4994a3e 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -170,11 +170,12 @@ it's a default value. */ #define EXPERIMENTAL_DANE #define EXPERIMENTAL_DCC #define EXPERIMENTAL_DMARC +#define EXPERIMENTAL_EVENT #define EXPERIMENTAL_PROXY #define EXPERIMENTAL_REDIS +#define EXPERIMENTAL_SOCKS #define EXPERIMENTAL_SPF #define EXPERIMENTAL_SRS -#define EXPERIMENTAL_EVENT /* For developers */ #define WANT_DEEPER_PRINTF_CHECKS diff --git a/src/src/exim.c b/src/src/exim.c index c94bdc1db..54725ef37 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -853,6 +853,9 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_REDIS fprintf(f, " Experimental_Redis"); #endif +#ifdef EXPERIMENTAL_SOCKS + fprintf(f, " Experimental_SOCKS"); +#endif fprintf(f, "\n"); fprintf(f, "Lookups (built-in):"); diff --git a/src/src/functions.h b/src/src/functions.h index 49bb5a952..df40d6456 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -208,6 +208,7 @@ extern uschar *host_ntoa(int, const void *, uschar *, int *); extern int host_scan_for_local_hosts(host_item *, host_item **, BOOL *); 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); extern int ip_connectedsocket(int, const uschar *, int, int, @@ -358,11 +359,10 @@ extern int sieve_interpret(uschar *, int, uschar *, uschar *, uschar *, extern void sigalrm_handler(int); extern BOOL smtp_buffered(void); extern void smtp_closedown(uschar *); -extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar * -#ifdef EXPERIMENTAL_EVENT - , uschar * -#endif - ); +extern int smtp_connect(host_item *, int, int, uschar *, int, + transport_instance *); +extern int smtp_sock_connect(host_item *, int, int, uschar *, + transport_instance * tb, int); extern int smtp_feof(void); extern int smtp_ferror(void); extern uschar *smtp_get_connection_info(void); diff --git a/src/src/ip.c b/src/src/ip.c index f6072c2e8..83c8d167b 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -97,24 +97,11 @@ ip_addrinfo(const uschar *address, struct sockaddr_in6 *saddr) * Bind socket to interface and port * *************************************************/ -/* This function binds a socket to a local interface address and port. For a -wildcard IPv6 bind, the address is ":". - -Arguments: - sock the socket - af AF_INET or AF_INET6 - the socket type - address the IP address, in text form - port the IP port (host order) - -Returns: the result of bind() -*/ - int -ip_bind(int sock, int af, uschar *address, int port) +ip_addr(void * sin_, int af, const uschar * address, int port) { -int s_len; -union sockaddr_46 sin; -memset(&sin, 0, sizeof(sin)); +union sockaddr_46 * sin = sin_; +memset(sin, 0, sizeof(sin)); /* Setup code when using an IPv6 socket. The wildcard address is ":", to ensure an IPv6 socket is used. */ @@ -124,15 +111,13 @@ if (af == AF_INET6) { if (address[0] == ':' && address[1] == 0) { - sin.v6.sin6_family = AF_INET6; - sin.v6.sin6_addr = in6addr_any; + sin->v6.sin6_family = AF_INET6; + sin->v6.sin6_addr = in6addr_any; } else - { - ip_addrinfo(address, &sin.v6); /* Panic-dies on error */ - } - sin.v6.sin6_port = htons(port); - s_len = sizeof(sin.v6); + ip_addrinfo(address, &sin->v6); /* Panic-dies on error */ + sin->v6.sin6_port = htons(port); + return sizeof(sin->v6); } else #else /* HAVE_IPv6 */ @@ -142,17 +127,34 @@ af = af; /* Avoid compiler warning */ /* Setup code when using IPv4 socket. The wildcard address is "". */ { - sin.v4.sin_family = AF_INET; - sin.v4.sin_port = htons(port); - s_len = sizeof(sin.v4); - if (address[0] == 0) - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY; - else - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CS address); + sin->v4.sin_family = AF_INET; + sin->v4.sin_port = htons(port); + sin->v4.sin_addr.s_addr = address[0] == 0 + ? (S_ADDR_TYPE)INADDR_ANY + : (S_ADDR_TYPE)inet_addr(CS address); + return sizeof(sin->v4); } +} -/* Now we can call the bind() function */ + +/* This function binds a socket to a local interface address and port. For a +wildcard IPv6 bind, the address is ":". + +Arguments: + sock the socket + af AF_INET or AF_INET6 - the socket type + address the IP address, in text form + port the IP port (host order) + +Returns: the result of bind() +*/ + +int +ip_bind(int sock, int af, uschar *address, int port) +{ +union sockaddr_46 sin; +int s_len = ip_addr(&sin, af, address, port); return bind(sock, (struct sockaddr *)&sin, s_len); } diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 8147b61d0..724339556 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -2,13 +2,14 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* A number of functions for driving outgoing SMTP calls. */ #include "exim.h" +#include "transports/smtp.h" @@ -143,75 +144,26 @@ return TRUE; -/************************************************* -* Connect to remote host * -*************************************************/ - -/* Create a socket, and connect it to a remote host. IPv6 addresses are -detected by checking for a colon in the address. AF_INET6 is defined even on -non-IPv6 systems, to enable the code to be less messy. However, on such systems -host->address will always be an IPv4 address. - -The port field in the host item is used if it is set (usually router from SRV -records or elsewhere). In other cases, the default passed as an argument is -used, and the host item is updated with its value. - -Arguments: - host host item containing name and address (and sometimes port) - host_af AF_INET or AF_INET6 - port default remote port to connect to, in host byte order, for those - hosts whose port setting is PORT_NONE - interface outgoing interface address or NULL - timeout timeout value or 0 - keepalive TRUE to use keepalive - dscp DSCP value to assign to socket - event event expansion - -Returns: connected socket number, or -1 with errno set -*/ - int -smtp_connect(host_item *host, int host_af, int port, uschar *interface, - int timeout, BOOL keepalive, const uschar *dscp -#ifdef EXPERIMENTAL_EVENT - , uschar * event -#endif - ) +smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface, + transport_instance * tb, int timeout) { -int on = 1; -int save_errno = 0; +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; +const uschar * dscp = ob->dscp; int dscp_value; int dscp_level; int dscp_option; int sock; - -if (host->port != PORT_NONE) - { - HDEBUG(D_transport|D_acl|D_v) - debug_printf("Transport port=%d replaced by host-specific port=%d\n", port, - host->port); - port = host->port; - } -else host->port = port; /* Set the port actually used */ - -HDEBUG(D_transport|D_acl|D_v) - { - if (interface == NULL) - debug_printf("Connecting to %s [%s]:%d ... ",host->name,host->address,port); - else - debug_printf("Connecting to %s [%s]:%d from %s ... ", host->name, - host->address, port, interface); - } +int on = 1; +int save_errno = 0; #ifdef EXPERIMENTAL_EVENT - deliver_host_address = host->address; - deliver_host_port = port; - if (event_raise(event, US"tcp:connect", NULL)) return -1; - /* Logging? Debug? */ +deliver_host_address = host->address; +deliver_host_port = port; +if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1; #endif -/* Create the socket */ - if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1; /* Set TCP_NODELAY; Exim does its own buffering. */ @@ -232,15 +184,13 @@ if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value)) option for both; ignore failures here */ if (host_af == AF_INET6 && dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value)) - { (void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)); - } } /* Bind to a specific interface if requested. Caller must ensure the interface is the same type (IPv4 or IPv6) as the outgoing address. */ -if (interface != NULL && ip_bind(sock, host_af, interface, 0) < 0) +if (interface && ip_bind(sock, host_af, interface, 0) < 0) { save_errno = errno; HDEBUG(D_transport|D_acl|D_v) @@ -286,11 +236,73 @@ else close(sock); return -1; } - if (keepalive) ip_keepalive(sock, host->address, TRUE); + if (ob->keepalive) ip_keepalive(sock, host->address, TRUE); return sock; } } +/************************************************* +* Connect to remote host * +*************************************************/ + +/* Create a socket, and connect it to a remote host. IPv6 addresses are +detected by checking for a colon in the address. AF_INET6 is defined even on +non-IPv6 systems, to enable the code to be less messy. However, on such systems +host->address will always be an IPv4 address. + +The port field in the host item is used if it is set (usually router from SRV +records or elsewhere). In other cases, the default passed as an argument is +used, and the host item is updated with its value. + +Arguments: + host host item containing name and address (and sometimes port) + host_af AF_INET or AF_INET6 + port default remote port to connect to, in host byte order, for those + hosts whose port setting is PORT_NONE + interface outgoing interface address or NULL + timeout timeout value or 0 + tb transport + +Returns: connected socket number, or -1 with errno set +*/ + +int +smtp_connect(host_item *host, int host_af, int port, uschar *interface, + int timeout, transport_instance * tb) +{ +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; + +if (host->port != PORT_NONE) + { + HDEBUG(D_transport|D_acl|D_v) + debug_printf("Transport port=%d replaced by host-specific port=%d\n", port, + host->port); + port = host->port; + } +else host->port = port; /* Set the port actually used */ + +HDEBUG(D_transport|D_acl|D_v) + { + uschar * s = US" "; + if (interface) s = string_sprintf(" from %s ", interface); +#ifdef EXPERIMENTAL_SOCKS + if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s); +#endif + debug_printf("Connecting to %s [%s]:%d%s... ", + host->name, host->address, port, s); + } + +/* Create and connect the socket */ + +#ifdef EXPERIMENTAL_SOCKS +if (ob->socks_proxy) + return socks_sock_connect(host, host_af, port, interface, tb, timeout); +#endif + +return smtp_sock_connect(host, host_af, port, interface, tb, timeout); +} + /************************************************* * Flush outgoing command buffer * @@ -581,3 +593,5 @@ return buffer[0] == okdigit; } /* End of smtp_out.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/transports/Makefile b/src/src/transports/Makefile index 02cf0c8d7..25973d5df 100644 --- a/src/src/transports/Makefile +++ b/src/src/transports/Makefile @@ -2,7 +2,7 @@ # calling it transports.a. This is called from the main make file, after cd'ing # to the transports subdirectory. -OBJ = appendfile.o autoreply.o lmtp.o pipe.o smtp.o tf_maildir.o +OBJ = appendfile.o autoreply.o lmtp.o pipe.o smtp.o smtp_socks.o tf_maildir.o transports.a: $(OBJ) @$(RM_COMMAND) -f transports.a @@ -19,6 +19,7 @@ autoreply.o: $(HDRS) autoreply.c autoreply.h lmtp.o: $(HDRS) lmtp.c lmtp.h pipe.o: $(HDRS) pipe.c pipe.h smtp.o: $(HDRS) smtp.c smtp.h +smtp_socks.o: $(HDRS) smtp_socks.c smtp.h tf_maildir.o: $(HDRS) tf_maildir.c tf_maildir.h appendfile.h diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 5662ffbf2..11d7fdd12 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -159,6 +159,10 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, serialize_hosts) }, { "size_addition", opt_int, (void *)offsetof(smtp_transport_options_block, size_addition) } +#ifdef EXPERIMENTAL_SOCKS + ,{ "socks_proxy", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, socks_proxy) } +#endif #ifdef SUPPORT_TLS ,{ "tls_certificate", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_certificate) }, @@ -246,6 +250,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { FALSE, /* lmtp_ignore_quota */ NULL, /* expand_retry_include_ip_address */ TRUE /* retry_include_ip_address */ +#ifdef EXPERIMENTAL_SOCKS +#endif + ,NULL /* socks_proxy */ #ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ NULL, /* tls_crl */ @@ -1350,12 +1357,7 @@ if (continue_hostname == NULL) { /* This puts port into host->port */ inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, ob->connect_timeout, - ob->keepalive, ob->dscp -#ifdef EXPERIMENTAL_EVENT - , tblock->event_action -#endif - ); + smtp_connect(host, host_af, port, interface, ob->connect_timeout, tblock); if (inblock.sock < 0) { diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 1b51c133d..84fb9f50c 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -60,6 +60,9 @@ typedef struct { BOOL lmtp_ignore_quota; uschar *expand_retry_include_ip_address; BOOL retry_include_ip_address; +#ifdef EXPERIMENTAL_SOCKS + uschar *socks_proxy; +#endif #ifdef SUPPORT_TLS uschar *tls_certificate; uschar *tls_crl; @@ -109,4 +112,9 @@ extern int smtp_auth(uschar *, unsigned, address_item *, host_item *, extern BOOL smtp_mail_auth_str(uschar *, unsigned, address_item *, smtp_transport_options_block *); +#ifdef EXPERMENTAL_SOCKS +extern int socks_sock_connect(host_item, int, int, uschar *, + transport_instance *, int); +#endif + /* End of transports/smtp.h */ diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c new file mode 100644 index 000000000..cf9f73ba3 --- /dev/null +++ b/src/src/transports/smtp_socks.c @@ -0,0 +1,310 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* SOCKS version 5 proxy, client-mode */ + +#include "../exim.h" +#include "smtp.h" + +#ifdef EXPERIMENTAL_SOCKS /* entire file */ + +#ifndef nelem +# define nelem(arr) (sizeof(arr)/sizeof(*arr)) +#endif + + +/* Defaults */ +#define SOCKS_PORT 1080 +#define SOCKS_TIMEOUT 5 + +#define AUTH_NONE 0 +#define AUTH_NAME 2 /* user/password per RFC 1929 */ +#define AUTH_NAME_VER 1 + +struct socks_err + { + uschar * reason; + int errcode; + } socks_errs[] = + { + {NULL, 0}, + {US"general SOCKS server failure", EIO}, + {US"connection not allowed by ruleset", EACCES}, + {US"Network unreachable", ENETUNREACH}, + {US"Host unreachable", EHOSTUNREACH}, + {US"Connection refused", ECONNREFUSED}, + {US"TTL expired", ECANCELED}, + {US"Command not supported", EOPNOTSUPP}, + {US"Address type not supported", EAFNOSUPPORT} + }; + +typedef struct + { + uschar auth_type; /* RFC 1928 encoding */ + const uschar * auth_name; + const uschar * auth_pwd; + short port; + unsigned timeout; + } socks_opts; + +static void +socks_option_defaults(socks_opts * sob) +{ +sob->auth_type = AUTH_NONE; +sob->auth_name = US""; +sob->auth_pwd = US""; +sob->port = SOCKS_PORT; +sob->timeout = SOCKS_TIMEOUT; +} + +static void +socks_option(socks_opts * sob, const uschar * opt) +{ +const uschar * s; + +if (Ustrncmp(opt, "auth=", 5) == 0) + { + opt += 5; + if (Ustrcmp(opt, "none") == 0) sob->auth_type = AUTH_NONE; + else if (Ustrcmp(opt, "name") == 0) sob->auth_type = AUTH_NAME; + } +else if (Ustrncmp(opt, "name=", 5) == 0) + sob->auth_name = opt + 5; +else if (Ustrncmp(opt, "pass=", 5) == 0) + sob->auth_pwd = opt + 5; +else if (Ustrncmp(opt, "port=", 5) == 0) + sob->port = atoi(opt + 5); +else if (Ustrncmp(opt, "tmo=", 4) == 0) + sob->timeout = atoi(opt + 4); +return; +} + +static int +socks_auth(int fd, int method, socks_opts * sob, time_t tmo) +{ +uschar * s; +int len, i, j; + +switch(method) + { + default: + log_write(0, LOG_MAIN|LOG_PANIC, + "Unrecognised socks auth method %d", method); + return FAIL; + case AUTH_NONE: + return OK; + case AUTH_NAME: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" socks auth NAME '%s' '%s'\n", + sob->auth_name, sob->auth_pwd); + i = Ustrlen(sob->auth_name); + j = Ustrlen(sob->auth_pwd); + s = string_sprintf("%c%c%.255s%c%.255s", AUTH_NAME_VER, + i, sob->auth_name, j, sob->auth_pwd); + len = i + j + 3; + HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; i<len; i++) debug_printf(" %02x", s[i]); + debug_printf("\n"); + } + if ( send(fd, s, len, 0) < 0 + || !fd_ready(fd, tmo-time(NULL)) + || read(fd, s, 2) != 2 + ) + return FAIL; + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" SOCKS<< %02x %02x\n", s[0], s[1]); + if (s[0] == AUTH_NAME_VER && s[1] == 0) + { + HDEBUG(D_transport|D_acl|D_v) debug_printf(" socks auth OK\n"); + return OK; + } + + log_write(0, LOG_MAIN|LOG_PANIC, "socks auth failed"); + errno = EPROTO; + return FAIL; + } +} + + + +/* Make a connection via a socks proxy + +Arguments: + host smtp target host + host_af address family + port remote tcp port number + interface local interface + tb transport + timeout connection timeout (zero for indefinite) + +Return value: + 0 on success; -1 on failure, with errno set +*/ + +int +socks_sock_connect(host_item * host, int host_af, int port, uschar * interface, + transport_instance * tb, int timeout) + +{ +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; +const uschar * proxy_list; +const uschar * proxy_spec; +int sep = 0; +int fd; +time_t tmo; +const uschar * state; +uschar buf[24]; + +if (!timeout) timeout = 24*60*60; /* use 1 day for "indefinite" */ +tmo = time(NULL) + timeout; + +if (!(proxy_list = expand_string(ob->socks_proxy))) + { + log_write(0, LOG_MAIN|LOG_PANIC, "Bad expansion for socks_proxy in %s", + tb->name); + return -1; + } + +/* Loop over proxy list, trying in order until one works */ +while ((proxy_spec = string_nextinlist(&proxy_list, &sep, NULL, 0))) + { + const uschar * proxy_host; + int subsep = -' '; + host_item proxy; + int proxy_af; + union sockaddr_46 sin; + unsigned size; + socks_opts sob; + const uschar * option; + + if (!(proxy_host = string_nextinlist(&proxy_spec, &subsep, NULL, 0))) + { + /* paniclog config error */ + return -1; + } + + /*XXX consider global options eg. "hide socks_password = wibble" on the tpt */ + socks_option_defaults(&sob); + + /* extract any further per-proxy options */ + while ((option = string_nextinlist(&proxy_spec, &subsep, NULL, 0))) + socks_option(&sob, option); + + /* bodge up a host struct for the proxy */ + proxy.address = proxy_host; + proxy_af = Ustrchr(proxy_host, ':') ? AF_INET6 : AF_INET; + + if ((fd = smtp_sock_connect(&proxy, proxy_af, sob.port, + interface, tb, sob.timeout)) < 0) + continue; + + /* Do the socks protocol stuff */ + /* Send method-selection */ + state = US"method select"; + HDEBUG(D_transport|D_acl|D_v) debug_printf(" 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 */ + if ( !fd_ready(fd, tmo-time(NULL)) + || read(fd, buf, 2) != 2 + ) + goto rcv_err; + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" SOCKS<< %02x %02x\n", buf[0], buf[1]); + if ( buf[0] != 5 + || socks_auth(fd, buf[1], &sob, tmo) != OK + ) + goto proxy_err; + + (void) ip_addr(&sin, host_af, host->address, port); + + /* send connect (ipver, ipaddr, port) */ + buf[0] = 5; buf[1] = 1; buf[2] = 0; buf[3] = host_af == AF_INET6 ? 4 : 1; + if (host_af == AF_INET6) + { + memcpy(buf+4, &sin.v6.sin6_addr, sizeof(sin.v6.sin6_addr)); + memcpy(buf+4+sizeof(sin.v6.sin6_addr), + &sin.v6.sin6_port, sizeof(sin.v6.sin6_port)); + size = 4+sizeof(sin.v6.sin6_addr)+sizeof(sin.v6.sin6_port); + } + else + { + memcpy(buf+4, &sin.v4.sin_addr.s_addr, sizeof(sin.v4.sin_addr.s_addr)); + memcpy(buf+4+sizeof(sin.v4.sin_addr.s_addr), + &sin.v4.sin_port, sizeof(sin.v4.sin_port)); + size = 4+sizeof(sin.v4.sin_addr.s_addr)+sizeof(sin.v4.sin_port); + } + + state = US"connect"; + HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; i<size; i++) debug_printf(" %02x", buf[i]); + debug_printf("\n"); + } + if (send(fd, buf, size, 0) < 0) + goto snd_err; + + /* expect conn-reply (success, local(ipver, addr, port)) + of same length as conn-request, or non-success fail code */ + if ( !fd_ready(fd, tmo-time(NULL)) + || (size = read(fd, buf, size)) < 2 + ) + goto rcv_err; + HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; i<size; i++) debug_printf(" %02x", buf[i]); + debug_printf("\n"); + } + if ( buf[0] != 5 + || buf[1] != 0 + ) + goto proxy_err; + + /*XXX log proxy outbound addr/port? */ + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" proxy farside local: [%s]:%d\n", + host_ntoa(buf[3] == 4 ? AF_INET6 : AF_INET, buf+4, NULL, NULL), + ntohs(*((uint16_t *)(buf + (buf[3] == 4 ? 20 : 8))))); + + return fd; + } + +HDEBUG(D_transport|D_acl|D_v) debug_printf(" no proxies left\n"); +return -1; + +snd_err: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" proxy snd_err %s: %s\n", state, strerror(errno)); + return -1; + +proxy_err: + { + struct socks_err * se = + buf[1] > nelem(socks_errs) ? NULL : socks_errs + buf[1]; + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" proxy %s: %s\n", state, se ? se->reason : US"unknown error code received"); + errno = se ? se->errcode : EPROTO; + } + +rcv_err: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" proxy rcv_err %s: %s\n", state, strerror(errno)); + if (!errno) errno = EPROTO; + else if (errno == ENOENT) errno = ECONNABORTED; + return -1; +} + +#endif /* entire file */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/verify.c b/src/src/verify.c index c1fb17767..d85ef3b4f 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -641,13 +641,8 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount. tls_out.cipher = tls_out.peerdn = tls_out.peercert = NULL; inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL -#ifdef EXPERIMENTAL_EVENT - /*XXX event action? NULL for now. */ - , NULL -#endif - ); - /* reconsider DSCP here */ + smtp_connect(host, host_af, port, interface, callout_connect, + addr->transport); if (inblock.sock < 0) { addr->message = string_sprintf("could not connect to %s [%s]: %s", |