SOCKS: support option expansion result for no-proxy. Bug 3118
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 8 Oct 2024 12:56:25 +0000 (13:56 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 8 Oct 2024 12:56:25 +0000 (13:56 +0100)
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/smtp_out.c
src/src/transports/smtp.h
src/src/transports/smtp_socks.c
test/confs/4020
test/log/4020
test/runtest
test/scripts/4020-socks/4020
test/stdout/4020

index dc41146b32696a51be25d6e3db15b52e5e167f42..76bdd1396028f6d325a392162517c84a8ca1dd9e 100644 (file)
@@ -43122,13 +43122,16 @@ Local/Makefile.
 
 Use of a proxy is enabled by setting the &%socks_proxy%& option
 on an smtp transport.
 
 Use of a proxy is enabled by setting the &%socks_proxy%& option
 on an smtp transport.
-The option value is expanded and should then be a list
+.new
+If unset (or empty after expansion) then proxying is not done.
+.wen
+Otherwise, expansion should result in 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.
 
 (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>.
+Each option is a string of form <name>=<value>.
 The list of options is in the following table:
 .itable none 0 0 2 10* left 90* left
 .irow &'auth'& "authentication method"
 The list of options is in the following table:
 .itable none 0 0 2 10* left 90* left
 .irow &'auth'& "authentication method"
index 0a83c294a6ba03b886de679cc7d987b4696f9795..86c5dad83e2c27d2fcc5641d1368e3d5109212c4 100644 (file)
@@ -21,6 +21,9 @@ Version 4.98
     and all the transport drivers except smtp, can now be built as loadable
     modules
 
     and all the transport drivers except smtp, can now be built as loadable
     modules
 
+ 6. A transport "socks_proxy" may expand to an empty string, specifying no
+    proxying.
+
 Version 4.98
 ------------
  1. The dkim_status ACL condition may now be used in data ACLs
 Version 4.98
 ------------
  1. The dkim_status ACL condition may now be used in data ACLs
index 2ea29a7e442e6d0e0f386188b35e57e74776b4f8..4d31ff39e6caad3608ea50e71ae0ec06ea61c873 100644 (file)
@@ -319,12 +319,8 @@ return sock;
 
 
 /* Arguments:
 
 
 /* Arguments:
-  host        host item containing name and address and port
-  host_af     AF_INET or AF_INET6
-  port       TCP port number
-  interface   outgoing interface address or NULL
-  tb          transport
-  timeout     timeout value or 0
+  sc           details for making connection: host, af, interface, transport
+  timeout      timeout value or 0
   early_data   if non-NULL, idempotent data to be sent -
                preferably in the TCP SYN segment
              Special case: non-NULL but with NULL blob.data - caller is
   early_data   if non-NULL, idempotent data to be sent -
                preferably in the TCP SYN segment
              Special case: non-NULL but with NULL blob.data - caller is
@@ -484,48 +480,36 @@ Returns:      connected socket number, or -1 with errno set
 int
 smtp_connect(smtp_connect_args * sc, const blob * early_data)
 {
 int
 smtp_connect(smtp_connect_args * sc, const blob * early_data)
 {
-int port = sc->host->port;
 smtp_transport_options_block * ob = sc->ob;
 
 smtp_transport_options_block * ob = sc->ob;
 
-callout_address = string_sprintf("[%s]:%d", sc->host->address, port);
+callout_address = string_sprintf("[%s]:%d", sc->host->address, sc->host->port);
 
 HDEBUG(D_transport|D_acl|D_v)
   {
 
 HDEBUG(D_transport|D_acl|D_v)
   {
-  uschar * s = US" ";
-  if (sc->interface) s = string_sprintf(" from %s ", sc->interface);
+  gstring * g = sc->interface
+    ? string_fmt_append(NULL, " from %s", sc->interface)
+    : string_get(10);
 #ifdef SUPPORT_SOCKS
 #ifdef SUPPORT_SOCKS
-  if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s);
+  if (ob->socks_proxy) g = string_catn(g, US"via proxy", 9);
 #endif
 #endif
-  debug_printf_indent("Connecting to %s %s%s...\n", sc->host->name, callout_address, s);
+  debug_printf_indent("Connecting to %s %s%Y ...\n",
+                     sc->host->name, callout_address, g);
   }
 
 /* Create and connect the socket */
 
 #ifdef SUPPORT_SOCKS
   }
 
 /* Create and connect the socket */
 
 #ifdef SUPPORT_SOCKS
+GET_OPTION("socks_proxy");
 if (ob->socks_proxy)
   {
 if (ob->socks_proxy)
   {
-  int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface,
-                               sc->tblock, ob->connect_timeout);
-  
-  if (sock >= 0)
+  if (!(ob->socks_proxy = expand_string(ob->socks_proxy)))
     {
     {
-    if (early_data && early_data->data && early_data->len)
-      if (send(sock, early_data->data, early_data->len, 0) < 0)
-       {
-       int save_errno = errno;
-       HDEBUG(D_transport|D_acl|D_v)
-         {
-         debug_printf_indent("failed: %s", CUstrerror(save_errno));
-         if (save_errno == ETIMEDOUT)
-           debug_printf(" (timeout=%s)", readconf_printtime(ob->connect_timeout));
-         debug_printf("\n");
-         }
-       (void)close(sock);
-       sock = -1;
-       errno = save_errno;
-       }
+    log_write(0, LOG_MAIN|LOG_PANIC, "Bad expansion for socks_proxy in %s",
+      sc->tblock->drinst.name);
+    return -1;
     }
     }
-  return sock;
+  if (*ob->socks_proxy)
+    return socks_sock_connect(sc, early_data);
   }
 #endif
 
   }
 #endif
 
index 9fc95444bd5f8cbaed0321df4bb3815cfc24bf6b..fe4b1c17a8e72346b4de262c53566e7d57380f0d 100644 (file)
@@ -247,8 +247,7 @@ extern void smtp_transport_closedown(transport_instance *);
 
 
 #ifdef SUPPORT_SOCKS
 
 
 #ifdef SUPPORT_SOCKS
-extern int     socks_sock_connect(host_item *, int, int, uschar *,
-                transport_instance *, int);
+extern int     socks_sock_connect(smtp_connect_args *, const blob *);
 #endif
 
 /* End of transports/smtp.h */
 #endif
 
 /* End of transports/smtp.h */
index a626a00d37f1e120b79f4b6c26b821a7a1c9819a..8d4beb39ae1d4801c41d75b2ac619d495448eb3c 100644 (file)
@@ -196,23 +196,21 @@ return -1;
 /* Make a connection via a socks proxy
 
 Arguments:
 /* 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)
+ sc            details for making connection: host, af, interface, transport
+ early_data    data to send down the smtp channel (once proxied)
 
 Return value:
  0 on success; -1 on failure, with errno set
 */
 
 int
 
 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)
+socks_sock_connect(smtp_connect_args * sc, const blob * early_data)
 {
 {
+transport_instance * tb = sc->tblock;
 smtp_transport_options_block * ob = tb->drinst.options_block;
 smtp_transport_options_block * ob = tb->drinst.options_block;
-const uschar * proxy_list, * proxy_spec;
+int timeout = ob->connect_timeout;
+const uschar * proxy_list = ob->socks_proxy,   /* already expanded */
+             * proxy_spec;
 int sep = 0;
 int fd;
 time_t tmo;
 int sep = 0;
 int fd;
 time_t tmo;
@@ -222,7 +220,7 @@ socks_opts proxies[32];                     /* max #proxies handled */
 unsigned nproxies;
 socks_opts * sob = NULL;
 unsigned size;
 unsigned nproxies;
 socks_opts * sob = NULL;
 unsigned size;
-blob early_data;
+blob proxy_early_data;
 
 if (!timeout) timeout = 24*60*60;      /* use 1 day for "indefinite" */
 tmo = time(NULL) + timeout;
 
 if (!timeout) timeout = 24*60*60;      /* use 1 day for "indefinite" */
 tmo = time(NULL) + timeout;
@@ -265,8 +263,8 @@ for sending on connection */
 
 state = US"method select";
 buf[0] = 5; buf[1] = 1; buf[2] = sob->auth_type;
 
 state = US"method select";
 buf[0] = 5; buf[1] = 1; buf[2] = sob->auth_type;
-early_data.data = buf;
-early_data.len = 3;
+proxy_early_data.data = buf;
+proxy_early_data.len = 3;
 
 /* Try proxies until a connection succeeds */
 
 
 /* Try proxies until a connection succeeds */
 
@@ -274,7 +272,7 @@ for(;;)
   {
   int idx;
   host_item proxy;
   {
   int idx;
   host_item proxy;
-  smtp_connect_args sc = {.sock = -1};
+  smtp_connect_args proxy_sc = {.sock = -1};
 
   if ((idx = socks_get_proxy(proxies, nproxies)) < 0)
     {
 
   if ((idx = socks_get_proxy(proxies, nproxies)) < 0)
     {
@@ -288,14 +286,14 @@ for(;;)
   proxy.address = proxy.name = sob->proxy_host;
   proxy.port = sob->port;
 
   proxy.address = proxy.name = sob->proxy_host;
   proxy.port = sob->port;
 
-  sc.tblock = tb;
-  sc.ob = ob;
-  sc.host = &proxy;
-  sc.host_af = Ustrchr(sob->proxy_host, ':') ? AF_INET6 : AF_INET;
-  sc.interface = interface;
+  proxy_sc.tblock = tb;
+  proxy_sc.ob = ob;
+  proxy_sc.host = &proxy;
+  proxy_sc.host_af = Ustrchr(sob->proxy_host, ':') ? AF_INET6 : AF_INET;
+  proxy_sc.interface = sc->interface;
 
   /*XXX we trust that the method-select command is idempotent */
 
   /*XXX we trust that the method-select command is idempotent */
-  if ((fd = smtp_sock_connect(&sc, sob->timeout, &early_data)) >= 0)
+  if ((fd = smtp_sock_connect(&proxy_sc, sob->timeout, &proxy_early_data)) >= 0)
     {
     proxy_local_address = string_copy(proxy.address);
     proxy_local_port = sob->port;
     {
     proxy_local_address = string_copy(proxy.address);
     proxy_local_port = sob->port;
@@ -308,7 +306,8 @@ for(;;)
 
 /* Do the socks protocol stuff */
 
 
 /* Do the socks protocol stuff */
 
-HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SOCKS>> 05 01 %02x\n", sob->auth_type);
+HDEBUG(D_transport|D_acl|D_v)
+  debug_printf_indent("  SOCKS>> 05 01 %02x\n", sob->auth_type);
 
 /* expect method response */
 
 
 /* expect method response */
 
@@ -328,8 +327,10 @@ if (  buf[0] != 5
   goto proxy_err;
 
  {
   goto proxy_err;
 
  {
+  int host_af = sc->host_af;
+  host_item * host = sc->host;
   union sockaddr_46 sin;
   union sockaddr_46 sin;
-  (void) ip_addr(&sin, host_af, host->address, port);
+  (void) ip_addr(&sin, host_af, host->address, host->port);
 
   /* send connect (ipver, ipaddr, port) */
 
 
   /* send connect (ipver, ipaddr, port) */
 
@@ -388,6 +389,22 @@ proxy_session = TRUE;
 HDEBUG(D_transport|D_acl|D_v)
   debug_printf_indent("  proxy farside: [%s]:%d\n", proxy_external_address, proxy_external_port);
 
 HDEBUG(D_transport|D_acl|D_v)
   debug_printf_indent("  proxy farside: [%s]:%d\n", proxy_external_address, proxy_external_port);
 
+if (early_data && early_data->data && early_data->len)
+  if (send(fd, early_data->data, early_data->len, 0) < 0)
+    {
+    int save_errno = errno;
+    HDEBUG(D_transport|D_acl|D_v)
+      {
+      debug_printf_indent("failed: %s", CUstrerror(save_errno));
+      if (save_errno == ETIMEDOUT)
+       debug_printf(" (timeout=%s)", readconf_printtime(ob->connect_timeout));
+      debug_printf("\n");
+      }
+    (void)close(fd);
+    fd= -1;
+    errno = save_errno;
+    }
+
 return fd;
 
 snd_err:
 return fd;
 
 snd_err:
index 6702dcd6895e8a41f1ac02a62faa12c9e5b9fd43..763acc15a381f3b199d1263870696cd072411274 100644 (file)
@@ -43,7 +43,11 @@ my_smtp:
   driver =             smtp
   interface =          HOSTIPV4
   port =               PORT_S
   driver =             smtp
   interface =          HOSTIPV4
   port =               PORT_S
+.ifdef FALLBACK
+  socks_proxy =                ${if eq {1}{1} {}}
+.else
   hide socks_proxy =   HOSTIPV4 port=PORT_D OPT
   hide socks_proxy =   HOSTIPV4 port=PORT_D OPT
+.endif
   hosts_try_fastopen = ${if eq {$local_part}{user_tfo} {*}}
   debug_print =                transport_name <$transport_name>
 .ifdef _HAVE_EVENT
   hosts_try_fastopen = ${if eq {$local_part}{user_tfo} {*}}
   debug_print =                transport_name <$transport_name>
 .ifdef _HAVE_EVENT
index d56b922f20005c636597da33d202a1b0a82716f7..d547e961137e6bef9568797ada1a706f4bc33854 100644 (file)
@@ -6,3 +6,7 @@
 1999-03-02 09:44:33 10HmaY-000000005vi-0000 pla ip4.ip4.ip4.ip4 plp 1225 pea 127.0.0.1 pep 48879
 1999-03-02 09:44:33 10HmaY-000000005vi-0000 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:PORT_S PRX=[ip4.ip4.ip4.ip4]:PORT_D C="250 accepted OK"
 1999-03-02 09:44:33 10HmaY-000000005vi-0000 Completed
 1999-03-02 09:44:33 10HmaY-000000005vi-0000 pla ip4.ip4.ip4.ip4 plp 1225 pea 127.0.0.1 pep 48879
 1999-03-02 09:44:33 10HmaY-000000005vi-0000 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:PORT_S PRX=[ip4.ip4.ip4.ip4]:PORT_D C="250 accepted OK"
 1999-03-02 09:44:33 10HmaY-000000005vi-0000 Completed
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 pla  plp 0 pea  pep 0
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:PORT_S C="250 accepted OK"
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 Completed
index bb9b1ceeed1a897372ea3afb91be57de166adb35..a178659f9052817f65905ec2d9f5f7dd235fc8c0 100755 (executable)
@@ -1560,6 +1560,9 @@ RESET_AFTER_EXTRA_LINE_READ:
     # Not all platforms build with DKIM enabled
     next if /^DKIM >> Body data for hash, canonicalized/;
 
     # Not all platforms build with DKIM enabled
     next if /^DKIM >> Body data for hash, canonicalized/;
 
+    # Not all platforms build with SOCKS enabled
+    next if /^try option socks_proxy$/;
+
     # Not all platforms build with SPF enabled
     next if /(^$time_pid?spf_conn_init|spf_compile\.c)/;
     next if /try option spf_smtp_comment_template$/;
     # Not all platforms build with SPF enabled
     next if /(^$time_pid?spf_conn_init|spf_compile\.c)/;
     next if /try option spf_smtp_comment_template$/;
index 4a0ac0893c4c84a1f58a17a6d169952e80c7fb76..2ba57ba525e15685c9282f78fc7480e9770c8edb 100644 (file)
@@ -80,5 +80,35 @@ via name/pwd-auth proxy
 quit
 ****
 #
 quit
 ****
 #
+# sock_proxy option set but expands to empty string
+server PORT_S
+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 -DFALLBACK=yes
+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
+
+direct, not via proxy
+.
+quit
+****
 #
 # Ends
 #
 # Ends
index 47dc6e92b2902e2e743e11a60210484944636a44..b5bbaf34d13cbd7c6d1b3b3355cd6064dd351b1c 100644 (file)
 354 Enter message, ending with "." on a line by itself\r
 250 OK id=10HmaY-000000005vi-0000\r
 221 myhost.test.ex closing connection\r
 354 Enter message, ending with "." on a line by itself\r
 250 OK id=10HmaY-000000005vi-0000\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-LIMITS MAILMAX=1000 RCPTMAX=50000\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=10HmaZ-000000005vi-0000\r
+221 myhost.test.ex closing connection\r
 
 ******** SERVER ********
 Listening on port 1225 ... 
 
 ******** SERVER ********
 Listening on port 1225 ... 
@@ -68,3 +80,32 @@ R
 QUIT
 250 bye
 End of script
 QUIT
 250 bye
 End of script
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 Connected OK
+EHLO myhost.test.ex
+250-server id
+250
+MAIL FROM:<CALLER@myhost.test.ex>
+250
+RCPT TO:<userx@test.ex>
+250
+DATA
+354 hit me
+Received: from CALLER (helo=test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmaZ-000000005vi-0000
+       for userx@test.ex;
+       Tue, 2 Mar 1999 09:44:33 +0000
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message should be sent
+Message-Id: <E10HmaZ-000000005vi-0000@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+
+direct, not via proxy
+.
+250 accepted OK
+QUIT
+250 bye
+End of script