summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/doc-txt/NewStuff4
-rw-r--r--doc/doc-txt/experimental-spec.txt33
-rwxr-xr-xsrc/scripts/MakeLinks1
-rw-r--r--src/src/EDITME3
-rw-r--r--src/src/config.h.defaults3
-rw-r--r--src/src/exim.c3
-rw-r--r--src/src/functions.h10
-rw-r--r--src/src/ip.c64
-rw-r--r--src/src/smtp_out.c142
-rw-r--r--src/src/transports/Makefile3
-rw-r--r--src/src/transports/smtp.c14
-rw-r--r--src/src/transports/smtp.h8
-rw-r--r--src/src/transports/smtp_socks.c310
-rw-r--r--src/src/verify.c9
-rw-r--r--test/README10
-rw-r--r--test/confs/402044
-rw-r--r--test/confs/402863
-rw-r--r--test/confs/402964
-rw-r--r--test/log/40206
-rw-r--r--test/log/40289
-rw-r--r--test/log/402911
-rw-r--r--test/scripts/2000-GnuTLS/20002
-rw-r--r--test/scripts/2000-GnuTLS/20182
-rw-r--r--test/scripts/4000-scanning/40062
-rw-r--r--test/scripts/4020-socks/402085
-rw-r--r--test/scripts/4020-socks/REQUIRES1
-rw-r--r--test/scripts/4028-GnuTLS-socks/402830
-rw-r--r--test/scripts/4028-GnuTLS-socks/REQUIRES4
-rw-r--r--test/scripts/4029-OpenSSL-socks/402930
-rw-r--r--test/scripts/4029-OpenSSL-socks/REQUIRES4
-rw-r--r--test/src/server.c162
-rw-r--r--test/stdout/02592
-rw-r--r--test/stdout/400310
-rw-r--r--test/stdout/40066
-rw-r--r--test/stdout/402068
-rw-r--r--test/stdout/402812
-rw-r--r--test/stdout/402912
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