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.
 
 
  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
 ------------
 
 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
 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/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
 
 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 DANE support
 # EXPERIMENTAL_DANE=yes
 
+# Uncomment the following line to add SOCKS support
+# EXPERIMENTAL_SOCKS=yes
+
 ###############################################################################
 #                 THESE ARE THINGS YOU MIGHT WANT TO SPECIFY                  #
 ###############################################################################
 ###############################################################################
 #                 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_DANE
 #define EXPERIMENTAL_DCC
 #define EXPERIMENTAL_DMARC
+#define EXPERIMENTAL_EVENT
 #define EXPERIMENTAL_PROXY
 #define EXPERIMENTAL_REDIS
 #define EXPERIMENTAL_PROXY
 #define EXPERIMENTAL_REDIS
+#define EXPERIMENTAL_SOCKS
 #define EXPERIMENTAL_SPF
 #define EXPERIMENTAL_SRS
 #define EXPERIMENTAL_SPF
 #define EXPERIMENTAL_SRS
-#define EXPERIMENTAL_EVENT
 
 /* For developers */
 #define WANT_DEEPER_PRINTF_CHECKS
 
 /* 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_REDIS
   fprintf(f, " Experimental_Redis");
 #endif
+#ifdef EXPERIMENTAL_SOCKS
+  fprintf(f, " Experimental_SOCKS");
+#endif
 fprintf(f, "\n");
 
 fprintf(f, "Lookups (built-in):");
 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     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,
 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 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);
 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      *
 *************************************************/
 
 *         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
 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. */
 
 /* 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)
     {
   {
   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
     }
   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 */
   }
 else
 #else     /* HAVE_IPv6 */
@@ -142,17 +127,34 @@ af = af;  /* Avoid compiler warning */
 /* Setup code when using IPv4 socket. The wildcard address is "". */
 
   {
 /* 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);
 }
 
 return bind(sock, (struct sockaddr *)&sin, s_len);
 }
 
index 8147b61d09e9d7dbe49b5b15003a30154149974d..724339556e4f4928aa8d50c70e916c7eabf44ebf 100644 (file)
@@ -2,13 +2,14 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     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"
 /* 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
 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;
 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
 
 #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
 
 #endif
 
-/* Create the socket */
-
 if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
 
 /* Set TCP_NODELAY; Exim does its own buffering. */
 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))
   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));
     (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. */
 
   }
 
 /* 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)
   {
   save_errno = errno;
   HDEBUG(D_transport|D_acl|D_v)
@@ -286,11 +236,73 @@ else
     close(sock);
     return -1;
     }
     close(sock);
     return -1;
     }
-  if (keepalive) ip_keepalive(sock, host->address, TRUE);
+  if (ob->keepalive) ip_keepalive(sock, host->address, TRUE);
   return sock;
   }
 }
 
   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           *
 
 /*************************************************
 *        Flush outgoing command buffer           *
@@ -581,3 +593,5 @@ return buffer[0] == okdigit;
 }
 
 /* End of smtp_out.c */
 }
 
 /* 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.
 
 # 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
 
 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
 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
 
 
 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) }
       (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) },
 #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 */
   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 */
 #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 =
   {
   /* 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)
     {
 
   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;
   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;
 #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 *);
 
 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 */
 /* 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 =
     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",
     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.
 
     (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.
 
 (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
 (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:
 
 
 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
 # 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
 #
 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
 ****
 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>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
 >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;
 
 typedef struct line {
   struct line *next;
+  unsigned len;
   char line[1];
 } line;
 
   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                   *
 *************************************************/
 /*************************************************
 *                 Main Program                   *
 *************************************************/
@@ -152,6 +172,7 @@ line *script = NULL;
 line *last = NULL;
 line *s;
 FILE *in, *out;
 line *last = NULL;
 line *s;
 FILE *in, *out;
+int linebuf = 1;
 
 char *sockname = NULL;
 unsigned char buffer[10240];
 
 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
 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;
 
 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
   {
   line *next;
+  char * d;
   int n = (int)strlen(CS buffer);
   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;
   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;
   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";
     if (ss[0] == '>')
       {
       char *end = "\r\n";
-      printf("%s\n", ss++);
+      unsigned len = s->len;
+      printit(ss++, len--);
 
       if (strncmp(ss, "*eof", 4) == 0)
         {
 
       if (strncmp(ss, "*eof", 4) == 0)
         {
@@ -538,13 +581,14 @@ for (count = 0; count < connection_count; count++)
         }
 
       if (*ss == '>')
         }
 
       if (*ss == '>')
-        { end = ""; ss++; }
+        { end = ""; ss++; len--; }
       else if (strncmp(ss, "CR>", 3) == 0)
       else if (strncmp(ss, "CR>", 3) == 0)
-        { end = "\r"; ss += 3; }
+        { end = "\r"; ss += 3; len -= 3; }
       else if (strncmp(ss, "LF>", 3) == 0)
       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]))
       }
 
     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
     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;
 
 
     else
       {
       int offset;
       int data = strcmp(ss, ".") == 0;
 
-      if (ss[0] == '<')
+      if (ss[0] != '<')
+       offset = 0;
+      else
         {
         buffer[0] = '<';
         {
         buffer[0] = '<';
-        offset = 1;
+       if (ss[1] != '<')
+         offset = 1;
+       else
+         {
+         buffer[1] = '<';
+         offset = 2;
+         }
         }
         }
-      else offset = 0;
 
       fflush(out);
 
 
       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
 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]
 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
 <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 ... 
 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
 <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 ... 
 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
 <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
 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>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
 >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>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 ... 
 >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>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 ... 
 >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