diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2015-03-15 12:32:11 +0000 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2015-03-15 12:34:33 +0000 |
commit | 7eb6c37c5084760c1d1469bd4be652b479a8df55 (patch) | |
tree | 9d82e23bffa7a8970a8ba21c72a635414c69fa9b | |
parent | 7c9f0469cb39055fff4096d1cb17ed72429c6b6c (diff) |
SOCKS: as a client, talk SMTP via a socks5 proxy. Bug 1590
37 files changed, 1081 insertions, 165 deletions
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 33d23f72a..e4bc586a5 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -22,6 +22,10 @@ Version 4.86 6. A commandline option to write a comment into the logfile. + 7. If built with EXPERIMENTAL_SOCKS feature enabled, the smtp transport can + be configured to make connections via socks5 proxies + + Version 4.85 ------------ diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt index 4f763658b..e6e066c04 100644 --- a/doc/doc-txt/experimental-spec.txt +++ b/doc/doc-txt/experimental-spec.txt @@ -1086,6 +1086,39 @@ QUIT +SOCKS +------------------------------------------------------------ +Support for proxying outbound SMTP via a Socks 5 proxy +(RFC 1928) is included if Exim is compiled with +EXPERIMENTAL_SOCKS defined. + +If an smtp transport has a nonempty socks_proxy option +defined, this is active. The option is expanded and +should be a list (colon-separated by default) of +proxy specifiers. Each proxy specifier is a list +(space-separated by default) where the initial element +is an IP address and any subsequent elements are options. + +Options are a string <name>=<value>. +These options are currently defined: +- "auth", with possible values "none" and "name". + Using "name" selects username/password authentication + per RFC 1929. Default is "none". +- "name" sets the authentication username. Default is empty. +- "pass" sets the authentication password. Default is empty. +- "port" sets the tcp port number for the proxy. Default is 1080. +- "tmo" sets a connection timeout in seconds for this proxy. Default is 5. + +Proxies from the list are tried in order until +one responds. The timeout for the overall connection +applies to the set of proxied attempts. + +If events are used, the remote IP/port during a +tcp:connect event will be that of the proxy. + + + + DANE ------------------------------------------------------------ DNS-based Authentication of Named Entities, as applied 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", diff --git a/test/README b/test/README index e54485788..653cf951f 100644 --- a/test/README +++ b/test/README @@ -1021,7 +1021,10 @@ are of the following kinds: (d) If the line starts with ">*eof", nothing is sent and the connection is closed. - The data that is sent starts after the initial '>' sequence. + The data that is sent starts after the initial '>' sequence. Within + each line the sequence '\x' followed by two hex digits can be used + to specify an arbitrary byte value. The sequence '\\' specifies a + single backslash. (2) A line that starts with "*sleep" specifies a number of seconds to wait before proceeding. @@ -1035,7 +1038,10 @@ are of the following kinds: (5) Otherwise, the line defines the start of an input line that the client is expected to send. To allow for lines that start with digits, the line may start with '<', which is not taken as part of the input data. If the - input does not match, the server bombs out with an error message. + lines starts with '<<' then only the characters are expected; no return- + linefeed terminator. If the input does not match, the server bombs out + with an error message. Backslash-escape sequences may be used in the + line content as for output lines. Here is a simple example of server use in a test script: diff --git a/test/confs/4020 b/test/confs/4020 new file mode 100644 index 000000000..8a3a91fcc --- /dev/null +++ b/test/confs/4020 @@ -0,0 +1,44 @@ +# Exim test configuration 4020 + +OPT = + +exim_path = EXIM_PATH +host_lookup_order = bydns +primary_hostname = myhost.test.ex +spool_directory = DIR/spool +log_file_path = DIR/spool/log/%slog +gecos_pattern = "" +gecos_name = CALLER_NAME + +# ----- Main settings ----- + +domainlist local_domains = test.ex : *.test.ex +acl_smtp_rcpt = accept + + +# ----- Routers ----- + +begin routers + +my_main_router: + driver = manualroute + route_list = * 127.0.0.1 + self = send + transport = my_smtp + debug_print = router_name <$router_name> + no_more + + +# ----- Transports ----- + +begin transports + +my_smtp: + driver = smtp + interface = HOSTIPV4 + port = PORT_S + socks_proxy = 127.0.0.1 port=PORT_S OPT + debug_print = transport_name <$transport_name> + + +# End diff --git a/test/confs/4028 b/test/confs/4028 new file mode 100644 index 000000000..3174e7543 --- /dev/null +++ b/test/confs/4028 @@ -0,0 +1,63 @@ +# Exim test configuration 4028 +# starttls over socks + +OPT = +SERVER= + +exim_path = EXIM_PATH +host_lookup_order = bydns +primary_hostname = myhost.test.ex +spool_directory = DIR/spool +log_file_path = DIR/spool/log/SERVER%slog +gecos_pattern = "" +gecos_name = CALLER_NAME + +# ----- Main settings ----- + +log_selector = +tls_peerdn +domainlist local_domains = test.ex : *.test.ex +acl_smtp_rcpt = accept + +tls_advertise_hosts = * + +# Set certificate only if server + +tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail} +tls_privatekey = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail} + +tls_verify_hosts = * +tls_verify_certificates = ${if eq {SERVER}{server}{DIR/aux-fixed/cert2}fail} + +# ----- Routers ----- + +begin routers + +client: + driver = manualroute + condition = ${if eq {SERVER}{server}{no}{yes}} + route_list = * 127.0.0.1 + self = send + transport = my_smtp + no_more + +server: + driver = redirect + data = :blackhole: + + +# ----- Transports ----- + +begin transports + +my_smtp: + driver = smtp + port = PORT_D + socks_proxy = 127.0.0.1 port=1080 OPT + tls_certificate = DIR/aux-fixed/cert2 + tls_privatekey = DIR/aux-fixed/cert2 + tls_verify_certificates = DIR/aux-fixed/cert2 + tls_try_verify_hosts = * + + + +# End diff --git a/test/confs/4029 b/test/confs/4029 new file mode 100644 index 000000000..ae4e71850 --- /dev/null +++ b/test/confs/4029 @@ -0,0 +1,64 @@ +# Exim test configuration 4029 +# starttls over socks + +OPT = +SERVER= + +exim_path = EXIM_PATH +host_lookup_order = bydns +primary_hostname = myhost.test.ex +spool_directory = DIR/spool +log_file_path = DIR/spool/log/SERVER%slog +gecos_pattern = "" +gecos_name = CALLER_NAME + +# ----- Main settings ----- + +log_selector = +tls_peerdn +domainlist local_domains = test.ex : *.test.ex +acl_smtp_rcpt = accept + +tls_advertise_hosts = * + +# Set certificate only if server + +tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail} +tls_privatekey = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail} + +tls_verify_hosts = * +tls_verify_certificates = ${if eq {SERVER}{server}{DIR/aux-fixed/cert2}fail} + +# ----- Routers ----- + +begin routers + +client: + driver = manualroute + condition = ${if eq {SERVER}{server}{no}{yes}} + route_list = * 127.0.0.1 + self = send + transport = my_smtp + no_more + +server: + driver = redirect + data = :blackhole: + + +# ----- Transports ----- + +begin transports + +my_smtp: + driver = smtp + port = PORT_D + socks_proxy = 127.0.0.1 port=1080 OPT + tls_certificate = DIR/aux-fixed/cert2 + tls_privatekey = DIR/aux-fixed/cert2 + tls_verify_certificates = DIR/aux-fixed/cert2 + tls_try_verify_hosts = * + + + +# End + diff --git a/test/log/4020 b/test/log/4020 new file mode 100644 index 000000000..f289beffd --- /dev/null +++ b/test/log/4020 @@ -0,0 +1,6 @@ +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 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1] 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 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1] C="250 accepted OK" +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed diff --git a/test/log/4028 b/test/log/4028 new file mode 100644 index 000000000..373bdf0c8 --- /dev/null +++ b/test/log/4028 @@ -0,0 +1,9 @@ +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 => userx@test.ex R=client T=my_smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" C="250 OK id=10HmaY-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed + +******** SERVER ******** +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=yes DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <userx@test.ex> R=server +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed diff --git a/test/log/4029 b/test/log/4029 new file mode 100644 index 000000000..a2ef850f1 --- /dev/null +++ b/test/log/4029 @@ -0,0 +1,11 @@ +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 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmaX-0005vi-00 [127.0.0.1] SSL verify error: certificate name mismatch: "/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" +1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=client T=my_smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:AES256-SHA:256 CV=no DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" C="250 OK id=10HmaY-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed + +******** SERVER ******** +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLSv1:AES256-SHA:256 CV=yes DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <userx@test.ex> R=server +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed diff --git a/test/scripts/2000-GnuTLS/2000 b/test/scripts/2000-GnuTLS/2000 index c8dcb6a84..a1299e574 100644 --- a/test/scripts/2000-GnuTLS/2000 +++ b/test/scripts/2000-GnuTLS/2000 @@ -1,7 +1,7 @@ # TLS client: TLS setup fails - retry in clear # # For this first GnuTLS test, we do not obey "gnutls", so that Exim has to -# create the GnuTLS paramter data for itself. +# create the GnuTLS parameter data for itself. # echo ==> Creating GnuTLS parameter data ... exim -DSERVER=server -bd -oX PORT_D diff --git a/test/scripts/2000-GnuTLS/2018 b/test/scripts/2000-GnuTLS/2018 index ac6049fd8..3f06e59e8 100644 --- a/test/scripts/2000-GnuTLS/2018 +++ b/test/scripts/2000-GnuTLS/2018 @@ -1,4 +1,4 @@ -# TLS: ACL encryption test +# TLS ACL encryption test gnutls exim -DSERVER=server -bd -oX PORT_D **** diff --git a/test/scripts/4000-scanning/4006 b/test/scripts/4000-scanning/4006 index 1adf8b136..a58188c95 100644 --- a/test/scripts/4000-scanning/4006 +++ b/test/scripts/4000-scanning/4006 @@ -59,7 +59,7 @@ server DIR/eximdir/avast_sock >LF>220 ready <SCAN >LF>210 SCAN DATA ->LF>b\ l\ a\ h [L]9.9 9 VNAME +>LF>b\\ l\\ a\\ h [L]9.9 9 VNAME >LF>200 SCAN OK <QUIT <*eof diff --git a/test/scripts/4020-socks/4020 b/test/scripts/4020-socks/4020 new file mode 100644 index 000000000..49d97c05f --- /dev/null +++ b/test/scripts/4020-socks/4020 @@ -0,0 +1,85 @@ +# socks5 proxy on smtp transport +# +munge loopback +# +# auth: null +server PORT_S +<<\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 hit me +. +250 accepted OK +QUIT +250 bye +**** +# +# +# +exim -odi -bs -DOPT= +ehlo test.ex +mail from:<> +rcpt to:<userx@test.ex> +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be sent + +via null-auth proxy +. +quit +**** +# +# +# +# auth: username/password +server PORT_S +<<\x05\x01\x02 +>>\x05\x02 +<<\x01\x04fred\x05fubar +>>\x01\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 hit me +. +250 accepted OK +QUIT +250 bye +**** +# +# +# +exim -odi -bs -DOPT="auth=name name=fred pass=fubar" +ehlo test.ex +mail from:<> +rcpt to:<userx@test.ex> +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be sent + +via name/pwd-auth proxy +. +quit +**** +# +# +# + diff --git a/test/scripts/4020-socks/REQUIRES b/test/scripts/4020-socks/REQUIRES new file mode 100644 index 000000000..135603c74 --- /dev/null +++ b/test/scripts/4020-socks/REQUIRES @@ -0,0 +1 @@ +support Experimental_SOCKS diff --git a/test/scripts/4028-GnuTLS-socks/4028 b/test/scripts/4028-GnuTLS-socks/4028 new file mode 100644 index 000000000..1692beaa8 --- /dev/null +++ b/test/scripts/4028-GnuTLS-socks/4028 @@ -0,0 +1,30 @@ +# socks5 proxy on smtp/starttls transport +# +munge loopback +gnutls +# +# a TLS-capable server to receive the mail +exim -DSERVER=server -bd -oX PORT_D +**** +# +# +# THIS TEST ASSUMES we have a socks proxy +# running and listening on 1080 +# +# a mail sender +exim -odi -bs -DOPT= +ehlo test.ex +mail from:<> +rcpt to:<userx@test.ex> +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be sent + +via null-auth proxy +. +quit +**** +# +# +killdaemon +no_msglog_check diff --git a/test/scripts/4028-GnuTLS-socks/REQUIRES b/test/scripts/4028-GnuTLS-socks/REQUIRES new file mode 100644 index 000000000..0b41941ac --- /dev/null +++ b/test/scripts/4028-GnuTLS-socks/REQUIRES @@ -0,0 +1,4 @@ +support Experimental_SOCKS +support GnuTLS +running IPv4 +running socks diff --git a/test/scripts/4029-OpenSSL-socks/4029 b/test/scripts/4029-OpenSSL-socks/4029 new file mode 100644 index 000000000..ac87b376f --- /dev/null +++ b/test/scripts/4029-OpenSSL-socks/4029 @@ -0,0 +1,30 @@ +# socks5 proxy on smtp/starttls transport +# +munge loopback +# +# +# a TLS-capable server to receive the mail +exim -DSERVER=server -bd -oX PORT_D +**** +# +# +# THIS TEST ASSUMES we have a socks proxy +# running and listening on 1080 +# +# a mail sender +exim -odi -bs -DOPT= +ehlo test.ex +mail from:<> +rcpt to:<userx@test.ex> +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be sent + +via null-auth proxy +. +quit +**** +# +# +killdaemon +no_msglog_check diff --git a/test/scripts/4029-OpenSSL-socks/REQUIRES b/test/scripts/4029-OpenSSL-socks/REQUIRES new file mode 100644 index 000000000..b24bbd9a0 --- /dev/null +++ b/test/scripts/4029-OpenSSL-socks/REQUIRES @@ -0,0 +1,4 @@ +support Experimental_SOCKS +support OpenSSL +running IPv4 +running socks diff --git a/test/src/server.c b/test/src/server.c index f4173ecd8..e425880a8 100644 --- a/test/src/server.c +++ b/test/src/server.c @@ -57,6 +57,7 @@ on all interfaces, unless the option -noipv6 is given. */ typedef struct line { struct line *next; + unsigned len; char line[1]; } line; @@ -123,6 +124,25 @@ return buffer; } + +static void +printit(char * s, int n) +{ +while(n--) + { + unsigned char c = *s++; + if (c == '\\') + printf("\\\\"); + else if (c >= ' ' && c <= '~') /* assumes ascii */ + putchar(c); + else + printf("\\x%02x", c); + } +putchar('\n'); +} + + + /************************************************* * Main Program * *************************************************/ @@ -152,6 +172,7 @@ line *script = NULL; line *last = NULL; line *s; FILE *in, *out; +int linebuf = 1; char *sockname = NULL; unsigned char buffer[10240]; @@ -394,18 +415,39 @@ script of things to do. A line containing "++++" is treated as end of file. This is so that the Perl driving script doesn't have to close the pipe - because that would cause it to wait for this process, which it doesn't yet want to do. The driving script adds the "++++" automatically - it doesn't actually -appear in the test script. */ +appear in the test script. Within lines we interpret \xNN and \\ groups */ while (fgets(CS buffer, sizeof(buffer), stdin) != NULL) { line *next; + char * d; int n = (int)strlen(CS buffer); + + if (n > 1 && buffer[0] == '>' && buffer[1] == '>') + linebuf = 0; while (n > 0 && isspace(buffer[n-1])) n--; buffer[n] = 0; if (strcmp(CS buffer, "++++") == 0) break; next = malloc(sizeof(line) + n); next->next = NULL; - strcpy(next->line, CS buffer); + d = next->line; + { + char * s = CS buffer; + do + { + char ch; + char cl = *s; + if (cl == '\\' && (cl = *++s) == 'x') + { + if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A'; + if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A'; + cl |= ch << 4; + } + *d++ = cl; + } + while (*s++); + } + next->len = d - next->line - 1; if (last == NULL) script = last = next; else last->next = next; last = next; @@ -529,7 +571,8 @@ for (count = 0; count < connection_count; count++) if (ss[0] == '>') { char *end = "\r\n"; - printf("%s\n", ss++); + unsigned len = s->len; + printit(ss++, len--); if (strncmp(ss, "*eof", 4) == 0) { @@ -538,13 +581,14 @@ for (count = 0; count < connection_count; count++) } if (*ss == '>') - { end = ""; ss++; } + { end = ""; ss++; len--; } else if (strncmp(ss, "CR>", 3) == 0) - { end = "\r"; ss += 3; } + { end = "\r"; ss += 3; len -= 3; } else if (strncmp(ss, "LF>", 3) == 0) - { end = "\n"; ss += 3; } + { end = "\n"; ss += 3; len -= 3; } - fprintf(out, "%s%s", ss, end); + fwrite(ss, 1, len, out); + if (*end) fprintf(out, end); } else if (isdigit((unsigned char)ss[0])) @@ -569,47 +613,93 @@ for (count = 0; count < connection_count; count++) connection. Read command line or data lines; the latter are indicated by the expected line being just ".". If the line starts with '<', that doesn't form part of the expected input. (This allows for incoming data - starting with a digit.) */ + starting with a digit.) If the line starts with '<<' we operate in + unbuffered rather than line mode and assume that a single read gets the + entire message. */ else { int offset; int data = strcmp(ss, ".") == 0; - if (ss[0] == '<') + if (ss[0] != '<') + offset = 0; + else { buffer[0] = '<'; - offset = 1; + if (ss[1] != '<') + offset = 1; + else + { + buffer[1] = '<'; + offset = 2; + } } - else offset = 0; fflush(out); - for (;;) - { - int n; - alarm(timeout); - if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL) - { - printf("%sxpected EOF read from client\n", - (strncmp(ss, "*eof", 4) == 0)? "E" : "Une"); - s = s->next; - goto END_OFF; - } - alarm(0); - n = (int)strlen(CS buffer); - while (n > 0 && isspace(buffer[n-1])) n--; - buffer[n] = 0; - printf("%s\n", buffer); - if (!data || strcmp(CS buffer, ".") == 0) break; - } - - if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0) - { - printf("Comparison failed - bailing out\n"); - printf("Expected: %s\n", ss); - break; - } + if (!linebuf) + { + int n; + char c; + + alarm(timeout); + n = read(dup_accept_socket, CS buffer+offset, s->len - offset); + if (n == 0) + { + printf("%sxpected EOF read from client\n", + (strncmp(ss, "*eof", 4) == 0)? "E" : "Une"); + s = s->next; + goto END_OFF; + } + if (offset != 2) + while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ; + alarm(0); + n += offset; + + printit(buffer, n); + + if (data) do + { + n = (read(dup_accept_socket, &c, 1) == 1 && c == '.'); + while (c != '\n' && read(dup_accept_socket, &c, 1) == 1) + ; + } while (!n); + else if (memcmp(ss, buffer, n) != 0) + { + printf("Comparison failed - bailing out\nExpected: "); + printit(ss, n); + break; + } + } + else + { + for (;;) + { + int n; + alarm(timeout); + if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL) + { + printf("%sxpected EOF read from client\n", + (strncmp(ss, "*eof", 4) == 0)? "E" : "Une"); + s = s->next; + goto END_OFF; + } + alarm(0); + n = (int)strlen(CS buffer); + while (n > 0 && isspace(buffer[n-1])) n--; + buffer[n] = 0; + printf("%s\n", buffer); + if (!data || strcmp(CS buffer, ".") == 0) break; + } + + if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0) + { + printf("Comparison failed - bailing out\n"); + printf("Expected: %s\n", ss); + break; + } + } } } diff --git a/test/stdout/0259 b/test/stdout/0259 index c4f4ea083..b65d88452 100644 --- a/test/stdout/0259 +++ b/test/stdout/0259 @@ -69,7 +69,7 @@ End of script Listening on port 1413 ... Connection request from [127.0.0.1] <999 , 25 ->999 , 25 : USERID : UNIX :ab
cd +>999 , 25 : USERID : UNIX :ab\x0dcd End of script Listening on port 1413 ... Connection request from [127.0.0.1] diff --git a/test/stdout/4003 b/test/stdout/4003 index f27d19238..e705096f6 100644 --- a/test/stdout/4003 +++ b/test/stdout/4003 @@ -68,7 +68,7 @@ Connection request <SCAN TESTSUITE/spool/scan/10HmbB-0005vi-00/10HmbB-0005vi-00.eml >LF>random ignored line >LF>random ignored line 2 ->LF>OK Scan ok. +>LF>OK\x09Scan ok. Expected EOF read from client End of script Listening on TESTSUITE/eximdir/fsec_sock ... @@ -82,8 +82,8 @@ Connection request <CONFIGURE MIME 1 >ignored_response <SCAN TESTSUITE/spool/scan/10HmaZ-0005vi-00/10HmaZ-0005vi-00.eml ->LF>xxxINFECTED blah VNAME blah ->LF>OK Scan ok. +>LF>xxxINFECTED\x09blah\x09VNAME\x09blah +>LF>OK\x09Scan ok. Expected EOF read from client End of script Listening on TESTSUITE/eximdir/fsec_sock ... @@ -105,7 +105,7 @@ Connection request <CONFIGURE MIME 1 >ignored_response <SCAN TESTSUITE/spool/scan/10HmbA-0005vi-00/10HmbA-0005vi-00.eml ->LF>xxxINFECTED blah VNAME blah ->LF>OK Scan ok. +>LF>xxxINFECTED\x09blah\x09VNAME\x09blah +>LF>OK\x09Scan ok. Expected EOF read from client End of script diff --git a/test/stdout/4006 b/test/stdout/4006 index bd18c9562..19e0f305b 100644 --- a/test/stdout/4006 +++ b/test/stdout/4006 @@ -63,7 +63,7 @@ Connection request >LF>200 FLAGS OK <SCAN TESTSUITE/spool/scan/10HmbB-0005vi-00 >LF>210 SCAN DATA ->LF>blah [+] +>LF>blah\x09[+] >LF>200 SCAN OK <QUIT Unexpected EOF read from client @@ -73,7 +73,7 @@ Connection request >LF>220 ready <SCAN TESTSUITE/spool/scan/10HmaX-0005vi-00 >LF>210 SCAN DATA ->LF>blah [E] +>LF>blah\x09[E] >LF>200 SCAN OK Unexpected EOF read from client Listening on TESTSUITE/eximdir/avast_sock ... @@ -81,7 +81,7 @@ Connection request >LF>220 ready <SCAN TESTSUITE/spool/scan/10HmbA-0005vi-00 >LF>210 SCAN DATA ->LF>b\ l\ a\ h [L]9.9 9 VNAME +>LF>b\\ l\\ a\\ h\x09[L]9.9\x099 VNAME >LF>200 SCAN OK Unexpected EOF read from client Listening on TESTSUITE/eximdir/avast_sock ... diff --git a/test/stdout/4020 b/test/stdout/4020 new file mode 100644 index 000000000..720c954fd --- /dev/null +++ b/test/stdout/4020 @@ -0,0 +1,68 @@ +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaX-0005vi-00
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaY-0005vi-00
+221 myhost.test.ex closing connection
+ +******** SERVER ******** +Listening on port 1224 ... +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 hit me +R +250 accepted OK +QUIT +250 bye +End of script +Listening on port 1224 ... +Connection request from [ip4.ip4.ip4.ip4] +<<\x05\x01\x02 +>>\x05\x02 +<<\x01\x04fred\x05fubar +>>\x01\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 hit me +R +250 accepted OK +QUIT +250 bye +End of script diff --git a/test/stdout/4028 b/test/stdout/4028 new file mode 100644 index 000000000..9c94d76e6 --- /dev/null +++ b/test/stdout/4028 @@ -0,0 +1,12 @@ +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250-STARTTLS
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaX-0005vi-00
+221 myhost.test.ex closing connection
diff --git a/test/stdout/4029 b/test/stdout/4029 new file mode 100644 index 000000000..9c94d76e6 --- /dev/null +++ b/test/stdout/4029 @@ -0,0 +1,12 @@ +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250-STARTTLS
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaX-0005vi-00
+221 myhost.test.ex closing connection
|