SOCKS: as a client, talk SMTP via a socks5 proxy. Bug 1590
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 15 Mar 2015 12:32:11 +0000 (12:32 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 15 Mar 2015 12:34:33 +0000 (12:34 +0000)
37 files changed:
doc/doc-txt/NewStuff
doc/doc-txt/experimental-spec.txt
src/scripts/MakeLinks
src/src/EDITME
src/src/config.h.defaults
src/src/exim.c
src/src/functions.h
src/src/ip.c
src/src/smtp_out.c
src/src/transports/Makefile
src/src/transports/smtp.c
src/src/transports/smtp.h
src/src/transports/smtp_socks.c [new file with mode: 0644]
src/src/verify.c
test/README
test/confs/4020 [new file with mode: 0644]
test/confs/4028 [new file with mode: 0644]
test/confs/4029 [new file with mode: 0644]
test/log/4020 [new file with mode: 0644]
test/log/4028 [new file with mode: 0644]
test/log/4029 [new file with mode: 0644]
test/scripts/2000-GnuTLS/2000
test/scripts/2000-GnuTLS/2018
test/scripts/4000-scanning/4006
test/scripts/4020-socks/4020 [new file with mode: 0644]
test/scripts/4020-socks/REQUIRES [new file with mode: 0644]
test/scripts/4028-GnuTLS-socks/4028 [new file with mode: 0644]
test/scripts/4028-GnuTLS-socks/REQUIRES [new file with mode: 0644]
test/scripts/4029-OpenSSL-socks/4029 [new file with mode: 0644]
test/scripts/4029-OpenSSL-socks/REQUIRES [new file with mode: 0644]
test/src/server.c
test/stdout/0259
test/stdout/4003
test/stdout/4006
test/stdout/4020 [new file with mode: 0644]
test/stdout/4028 [new file with mode: 0644]
test/stdout/4029 [new file with mode: 0644]

index 33d23f72a88517de4215df5498a65e87396fed44..e4bc586a5ce33116d64c29e022a9f0032695fc05 100644 (file)
@@ -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
 ------------
 
index 4f763658bafbdf1b258bf678e4e7140cf0831918..e6e066c04c42551d788de4e4858ad47a9914f2a9 100644 (file)
@@ -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
index a50f75b369ba4e99c2d707c95af4c5f99e876554..f68fd6f4bcbbea0e4988578f69bd634aa06ef8f1 100755 (executable)
@@ -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
index 353dde3ee6ea9514b55acbfd3ef1e19c02c141de..d48f268b90daff41a3f479135ec57a19ee30745c 100644 (file)
@@ -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                  #
 ###############################################################################
index e339bdf9e253361a1fea683dd0cee01336ba68d1..ac4994a3e1ca129a846286feb1ef46f409f4b2ab 100644 (file)
@@ -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
index c94bdc1dbd6c6c7b32f9670f15c3320b9738be5a..54725ef37d27bfa9724fd7d873a9b6d0b68f6a1e 100644 (file)
@@ -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):");
index 49bb5a9525ea9e7e1d6e558d3ebe03ef33240dd9..df40d645628d20f15f3e6a3649311de3eae80492 100644 (file)
@@ -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);
index f6072c2e80019e4df87e34f3d490f956bb8c39b9..83c8d167bb57fef8a4c542f96360923f12b88d40 100644 (file)
@@ -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);
 }
 
index 8147b61d09e9d7dbe49b5b15003a30154149974d..724339556e4f4928aa8d50c70e916c7eabf44ebf 100644 (file)
@@ -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
+*/
index 02cf0c8d768086e635a7dcaefc6d28334b54a460..25973d5df1913cad024973609a123159a92d8568 100644 (file)
@@ -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
 
index 5662ffbf20c9d5b0c3aeae779c687c661fe9fc25..11d7fdd12b6ec58832b2e9847000f156dba2740c 100644 (file)
@@ -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)
     {
index 1b51c133d6253c3980582dbace47c579cd1fb756..84fb9f50c5082314a96246adee0e56af62282c3b 100644 (file)
@@ -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 (file)
index 0000000..cf9f73b
--- /dev/null
@@ -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
+*/
index c1fb17767659a6be24de8c68d5578758bf953c9f..d85ef3b4f16d12e4d70cd9ee5119f9e63524b901 100644 (file)
@@ -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",
index e544857889535c0c54a6f5e6bd793742263d4e9e..653cf951f31489b19ca24189a10a85ed65d8126a 100644 (file)
@@ -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 (file)
index 0000000..8a3a91f
--- /dev/null
@@ -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 (file)
index 0000000..3174e75
--- /dev/null
@@ -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 (file)
index 0000000..ae4e718
--- /dev/null
@@ -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 (file)
index 0000000..f289bef
--- /dev/null
@@ -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 (file)
index 0000000..373bdf0
--- /dev/null
@@ -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 (file)
index 0000000..a2ef850
--- /dev/null
@@ -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
index c8dcb6a8484379fb61d4cc73b83b736c6dfff66a..a1299e57425e5564a743b13cab73b139a4d08f14 100644 (file)
@@ -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
index ac6049fd85fda98efffd081fccdc8578ec27a692..3f06e59e8357daf5b7e901f2d1aabf3a2489db6c 100644 (file)
@@ -1,4 +1,4 @@
-# TLS: ACL encryption test
+# TLS ACL encryption test
 gnutls
 exim -DSERVER=server -bd -oX PORT_D
 ****
index 1adf8b136684ba9699ff0f26aefb50a80cc1ebc5..a58188c952eded18d7ce6e77ee79e37045bc5f64 100644 (file)
@@ -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 (file)
index 0000000..49d97c0
--- /dev/null
@@ -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 (file)
index 0000000..135603c
--- /dev/null
@@ -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 (file)
index 0000000..1692bea
--- /dev/null
@@ -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 (file)
index 0000000..0b41941
--- /dev/null
@@ -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 (file)
index 0000000..ac87b37
--- /dev/null
@@ -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 (file)
index 0000000..b24bbd9
--- /dev/null
@@ -0,0 +1,4 @@
+support Experimental_SOCKS
+support OpenSSL
+running IPv4
+running socks
index f4173ecd86976f476ea51089cc104e46cd363619..e425880a8b2181812c1c813059f5b0ea734610d1 100644 (file)
@@ -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;
+         }
+       }
       }
     }
 
index c4f4ea0839eb494f2749ff3d0cdc3d28058c4396..b65d88452f17376a03599e56b6ca3cfe5f60bab1 100644 (file)
@@ -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\rcd
+>999 , 25 : USERID : UNIX :ab\x0dcd
 End of script
 Listening on port 1413 ... 
 Connection request from [127.0.0.1]
index f27d192385028d2603b6d7328d559f082d7a2849..e705096f6b02bdceef4e8d42ab74c1423b640c6e 100644 (file)
@@ -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
index bd18c95625f3d5970ef2b5445457eeba2a519a6d..19e0f305b1c027c411a5b530040157ca75dba281 100644 (file)
@@ -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 (file)
index 0000000..720c954
--- /dev/null
@@ -0,0 +1,68 @@
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaX-0005vi-00\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaY-0005vi-00\r
+221 myhost.test.ex closing connection\r
+
+******** SERVER ********
+Listening on port 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 (file)
index 0000000..9c94d76
--- /dev/null
@@ -0,0 +1,12 @@
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250-STARTTLS\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaX-0005vi-00\r
+221 myhost.test.ex closing connection\r
diff --git a/test/stdout/4029 b/test/stdout/4029
new file mode 100644 (file)
index 0000000..9c94d76
--- /dev/null
@@ -0,0 +1,12 @@
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250-STARTTLS\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaX-0005vi-00\r
+221 myhost.test.ex closing connection\r