From 7b505d28898cadb14eebded54b255d22541caaa6 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Tue, 8 Oct 2024 13:56:25 +0100 Subject: [PATCH] SOCKS: support option expansion result for no-proxy. Bug 3118 --- doc/doc-docbook/spec.xfpt | 7 ++-- doc/doc-txt/NewStuff | 3 ++ src/src/smtp_out.c | 48 +++++++++------------------ src/src/transports/smtp.h | 3 +- src/src/transports/smtp_socks.c | 59 +++++++++++++++++++++------------ test/confs/4020 | 4 +++ test/log/4020 | 4 +++ test/runtest | 3 ++ test/scripts/4020-socks/4020 | 30 +++++++++++++++++ test/stdout/4020 | 41 +++++++++++++++++++++++ 10 files changed, 145 insertions(+), 57 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index dc41146b3..76bdd1396 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -43122,13 +43122,16 @@ Local/Makefile. 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. -Options are a string =. +Each option is a string of form =. The list of options is in the following table: .itable none 0 0 2 10* left 90* left .irow &'auth'& "authentication method" diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 0a83c294a..86c5dad83 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -21,6 +21,9 @@ Version 4.98 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 diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 2ea29a7e4..4d31ff39e 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -319,12 +319,8 @@ return sock; /* 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 @@ -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 port = sc->host->port; 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) { - 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 - if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s); + if (ob->socks_proxy) g = string_catn(g, US"via proxy", 9); #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 +GET_OPTION("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 diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 9fc95444b..fe4b1c17a 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -247,8 +247,7 @@ extern void smtp_transport_closedown(transport_instance *); #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 */ diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c index a626a00d3..8d4beb39a 100644 --- a/src/src/transports/smtp_socks.c +++ b/src/src/transports/smtp_socks.c @@ -196,23 +196,21 @@ return -1; /* 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 -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; -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; @@ -222,7 +220,7 @@ socks_opts proxies[32]; /* max #proxies handled */ 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; @@ -265,8 +263,8 @@ for sending on connection */ 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 */ @@ -274,7 +272,7 @@ for(;;) { 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) { @@ -288,14 +286,14 @@ for(;;) 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 */ - 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; @@ -308,7 +306,8 @@ for(;;) /* 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 */ @@ -328,8 +327,10 @@ if ( buf[0] != 5 goto proxy_err; { + int host_af = sc->host_af; + host_item * host = sc->host; 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) */ @@ -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); +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: diff --git a/test/confs/4020 b/test/confs/4020 index 6702dcd68..763acc15a 100644 --- a/test/confs/4020 +++ b/test/confs/4020 @@ -43,7 +43,11 @@ my_smtp: driver = smtp interface = HOSTIPV4 port = PORT_S +.ifdef FALLBACK + socks_proxy = ${if eq {1}{1} {}} +.else 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 diff --git a/test/log/4020 b/test/log/4020 index d56b922f2..d547e9611 100644 --- a/test/log/4020 +++ b/test/log/4020 @@ -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 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 diff --git a/test/runtest b/test/runtest index bb9b1ceee..a178659f9 100755 --- a/test/runtest +++ b/test/runtest @@ -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 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$/; diff --git a/test/scripts/4020-socks/4020 b/test/scripts/4020-socks/4020 index 4a0ac0893..2ba57ba52 100644 --- a/test/scripts/4020-socks/4020 +++ b/test/scripts/4020-socks/4020 @@ -80,5 +80,35 @@ via name/pwd-auth proxy 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: +data +Date: Fri, 17 Dec 2004 14:35:01 +0100 +Subject: message should be sent + +direct, not via proxy +. +quit +**** # # Ends diff --git a/test/stdout/4020 b/test/stdout/4020 index 47dc6e92b..b5bbaf34d 100644 --- a/test/stdout/4020 +++ b/test/stdout/4020 @@ -22,6 +22,18 @@ 354 Enter message, ending with "." on a line by itself 250 OK id=10HmaY-000000005vi-0000 221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-LIMITS MAILMAX=1000 RCPTMAX=50000 +250-8BITMIME +250-PIPELINING +250 HELP +250 OK +250 Accepted +354 Enter message, ending with "." on a line by itself +250 OK id=10HmaZ-000000005vi-0000 +221 myhost.test.ex closing connection ******** SERVER ******** Listening on port 1225 ... @@ -68,3 +80,32 @@ R 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: +250 +RCPT TO: +250 +DATA +354 hit me +Received: from CALLER (helo=test.ex) + by myhost.test.ex with local-esmtp (Exim x.yz) + (envelope-from ) + 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: +From: CALLER_NAME + +direct, not via proxy +. +250 accepted OK +QUIT +250 bye +End of script -- 2.30.2