Gitweb:
https://git.exim.org/exim.git/commitdiff/24cda181fb88542cf38db2beae5d0ddb37f59c5c
Commit: 24cda181fb88542cf38db2beae5d0ddb37f59c5c
Parent: df0dc54a7666ef64b8a6681ab7b50a4836905203
Author: Jeremy Harris <jgh146exb@???>
AuthorDate: Sat Mar 25 23:21:15 2023 +0000
Committer: Jeremy Harris <jgh146exb@???>
CommitDate: Sat Mar 25 23:21:15 2023 +0000
Experimental_XCLIENT. Bug 2702
---
doc/doc-txt/experimental-spec.txt | 31 ++++
src/OS/Makefile-Base | 4 +-
src/scripts/MakeLinks | 2 +-
src/src/auths/xtextdecode.c | 4 +-
src/src/config.h.defaults | 1 +
src/src/exim.c | 3 +
src/src/functions.h | 5 +
src/src/globals.c | 17 +-
src/src/globals.h | 13 +-
src/src/host.c | 4 +-
src/src/macro_predef.c | 3 +
src/src/macros.h | 6 +-
src/src/readconf.c | 5 +-
src/src/smtp_in.c | 74 +++++++-
src/src/xclient.c | 299 +++++++++++++++++++++++++++++++++
test/confs/4032 | 41 +++++
test/confs/4033 | 1 +
test/confs/4034 | 1 +
test/log/4032 | 27 +++
test/log/4034 | 29 ++++
test/rejectlog/4032 | 5 +
test/rejectlog/4034 | 5 +
test/runtest | 3 +
test/scripts/4032-xclient/4032 | 140 +++++++++++++++
test/scripts/4032-xclient/4033 | 62 +++++++
test/scripts/4032-xclient/REQUIRES | 1 +
test/scripts/4034-xclient-tls/4034 | 179 ++++++++++++++++++++
test/scripts/4034-xclient-tls/REQUIRES | 2 +
test/stdout/4032 | 199 ++++++++++++++++++++++
test/stdout/4033 | 108 ++++++++++++
30 files changed, 1249 insertions(+), 25 deletions(-)
diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt
index aac8ca77d..5bf00a7f1 100644
--- a/doc/doc-txt/experimental-spec.txt
+++ b/doc/doc-txt/experimental-spec.txt
@@ -662,6 +662,37 @@ Values advertised are only noted for TLS connections and ones for which
the server does not advertise TLS support.
+
+XCLIENT proxy support
+---------------------------------------------------------------
+Per https://www.postfix.org/XCLIENT_README.html
+
+XCLIENT is an ESMTP extension supporting an inbound proxy.
+The only client immplementation known is in Nginx
+(https://nginx.org/en/docs/mail/ngx_mail_proxy_module.html)
+
+If compiled with EXPERIMENTAL_XCLIENT=yes :-
+
+As a server, Exim will advertise XCLIENT support (conditional on a new option
+"hosts_xclient") and service XCLIENT commands with parameters
+ ADDR
+ NAME
+ PORT
+ LOGIN
+ DESTADDR
+ DESTPORT
+A fresh HELO/EHLO is required after a succesful XCLIENT, and the usual
+values are derived from that (making the HELO and PROTO paramemters redundant).
+
+An XCLIENT command must give both ADDR and PORT parameters if no previous
+XCLIENT has succeeded in the SMTP session.
+
+After a success:
+ $proxy_session variable becomes "yes"
+ $proxy_local_address, $proxy_local_port have the proxy "inside" values
+ $proxy_external_address, $proxy_external_port have the proxy "outside" values
+ $sender_host_address, $sender_host_port have the remot client values
+
--------------------------------------------------------------
End of file
--------------------------------------------------------------
diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base
index d00ab9404..71aee4d93 100644
--- a/src/OS/Makefile-Base
+++ b/src/OS/Makefile-Base
@@ -497,7 +497,8 @@ OBJ_EXPERIMENTAL = arc.o \
dmarc.o \
imap_utf7.o \
spf.o \
- utf8.o
+ utf8.o \
+ xclient.o
# Targets for final binaries; the main one has a build number which is
# updated each time. We don't bother with that for the auxiliaries.
@@ -873,6 +874,7 @@ dmarc.o: $(HDRS) pdkim/pdkim.h dmarc.h dmarc.c
imap_utf7.o: $(HDRS) imap_utf7.c
spf.o: $(HDRS) spf.h spf.c
utf8.o: $(HDRS) utf8.c
+xclient.o: $(HDRS) xclient.c
# The module containing tables of available lookups, routers, auths, and
# transports must be rebuilt if any of them are. However, because the makefiles
diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks
index af6138063..0694af4c0 100755
--- a/src/scripts/MakeLinks
+++ b/src/scripts/MakeLinks
@@ -125,7 +125,7 @@ done
# EXPERIMENTAL_*
for f in arc.c bmi_spam.c bmi_spam.h dcc.c dcc.h dane.c dane-openssl.c \
- danessl.h imap_utf7.c spf.c spf.h srs.c srs.h utf8.c
+ danessl.h imap_utf7.c spf.c spf.h srs.c srs.h utf8.c xclient.c
do
ln -s ../src/$f $f
done
diff --git a/src/src/auths/xtextdecode.c b/src/src/auths/xtextdecode.c
index b6a927194..edd2282d0 100644
--- a/src/src/auths/xtextdecode.c
+++ b/src/src/auths/xtextdecode.c
@@ -32,9 +32,9 @@ Returns: the number of bytes in the result, excluding the final zero;
*/
int
-auth_xtextdecode(uschar *code, uschar **ptr)
+auth_xtextdecode(uschar * code, uschar ** ptr)
{
-register int x;
+int x;
uschar * result = store_get(Ustrlen(code) + 1, code);
*ptr = result;
diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
index 221705224..fb5fe3603 100644
--- a/src/src/config.h.defaults
+++ b/src/src/config.h.defaults
@@ -211,6 +211,7 @@ Do not put spaces between # and the 'define'.
#define EXPERIMENTAL_DSN_INFO
#define EXPERIMENTAL_ESMTP_LIMITS
#define EXPERIMENTAL_QUEUEFILE
+#define EXPERIMENTAL_XCLIENT
/* For developers */
diff --git a/src/src/exim.c b/src/src/exim.c
index c16beb1af..06863347d 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -1132,6 +1132,9 @@ g = string_cat(g, US"Support for:");
#ifdef EXPERIMENTAL_QUEUEFILE
g = string_cat(g, US" Experimental_QUEUEFILE");
#endif
+#ifdef EXPERIMENTAL_XCLIENT
+ g = string_cat(g, US" Experimental_XCLIENT");
+#endif
g = string_cat(g, US"\n");
g = string_cat(g, US"Lookups (built-in):");
diff --git a/src/src/functions.h b/src/src/functions.h
index 76392f304..aa5057a83 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -686,6 +686,11 @@ extern BOOL write_chunk(transport_ctx *, uschar *, int);
extern ssize_t write_to_fd_buf(int, const uschar *, size_t);
extern uschar *wrap_header(const uschar *, unsigned, unsigned, const uschar *, unsigned);
+#ifdef EXPERIMENTAL_XCLIENT
+extern uschar * xclient_smtp_command(uschar *, int *, BOOL *);
+extern gstring * xclient_smtp_advertise_str(gstring *);
+#endif
+
/******************************************************************************/
/* Predicate: if an address is in a tainted pool.
diff --git a/src/src/globals.c b/src/src/globals.c
index 539bae00e..9f4053937 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -995,11 +995,18 @@ uschar *host_lookup_msg = US"";
int host_number = 0;
uschar *host_number_string = NULL;
uschar *host_reject_connection = NULL;
-tree_node *hostlist_anchor = NULL;
-int hostlist_count = 0;
+uschar *hosts_connection_nolog = NULL;
+#ifdef SUPPORT_PROXY
+uschar *hosts_proxy = NULL;
+#endif
uschar *hosts_treat_as_local = NULL;
uschar *hosts_require_helo = US"*";
-uschar *hosts_connection_nolog = NULL;
+#ifdef EXPERIMENTAL_XCLIENT
+uschar *hosts_xclient = NULL;
+#endif
+tree_node *hostlist_anchor = NULL;
+int hostlist_count = 0;
+
int ignore_bounce_errors_after = 10*7*24*60*60; /* 10 weeks */
uschar *ignore_fromline_hosts = NULL;
@@ -1232,8 +1239,7 @@ int process_info_len = 0;
uschar *process_log_path = NULL;
const uschar *process_purpose = US"fresh-exec";
-#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS)
-uschar *hosts_proxy = NULL;
+#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMENTAL_XCLIENT)
uschar *proxy_external_address = NULL;
int proxy_external_port = 0;
uschar *proxy_local_address = NULL;
@@ -1660,5 +1666,4 @@ int warning_count = 0;
const uschar *warnmsg_delay = NULL;
const uschar *warnmsg_recipients = NULL;
-
/* End of globals.c */
diff --git a/src/src/globals.h b/src/src/globals.h
index e216b9208..3a5513382 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -661,12 +661,16 @@ extern uschar *host_lookup_order; /* Order of host lookup types */
extern uschar *host_lookup_msg; /* Text for why it failed */
extern int host_number; /* For sharing spools */
extern uschar *host_number_string; /* For expanding */
-extern uschar *hosts_require_helo; /* check for HELO/EHLO before MAIL */
extern uschar *host_reject_connection; /* Reject these hosts */
-extern tree_node *hostlist_anchor; /* Tree of defined host lists */
-extern int hostlist_count; /* Number defined */
extern uschar *hosts_connection_nolog; /* Limits the logging option */
+extern uschar *hosts_require_helo; /* check for HELO/EHLO before MAIL */
extern uschar *hosts_treat_as_local; /* For routing */
+#ifdef EXPERIMENTAL_XCLIENT
+extern uschar *hosts_xclient; /* Allow XCLIENT command for specified hosts */
+#endif
+extern tree_node *hostlist_anchor; /* Tree of defined host lists */
+extern int hostlist_count; /* Number defined */
+
extern int ignore_bounce_errors_after; /* Keep them for this time. */
extern BOOL ignore_fromline_local; /* Local SMTP ignore fromline */
@@ -828,7 +832,8 @@ extern int proxy_external_port; /* Port on remote interface of proxy */
extern uschar *proxy_local_address; /* IP of local interface of proxy */
extern int proxy_local_port; /* Port on local interface of proxy */
extern int proxy_protocol_timeout; /* Timeout for proxy negotiation */
-extern BOOL proxy_session; /* TRUE if receiving mail from valid proxy */
+extern BOOL proxy_session; /* TRUE if receiving mail from valid proxy
+ or sending via one */
#endif
extern uschar *prvscheck_address; /* Set during prvscheck expansion item */
diff --git a/src/src/host.c b/src/src/host.c
index 8d53eb3de..136ee8953 100644
--- a/src/src/host.c
+++ b/src/src/host.c
@@ -824,9 +824,9 @@ Returns: pointer to character string
*/
uschar *
-host_ntoa(int type, const void *arg, uschar *buffer, int *portptr)
+host_ntoa(int type, const void * arg, uschar * buffer, int * portptr)
{
-uschar *yield;
+uschar * yield;
/* The new world. It is annoying that we have to fish out the address from
different places in the block, depending on what kind of address it is. It
diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c
index 0053cb245..8fade68ca 100644
--- a/src/src/macro_predef.c
+++ b/src/src/macro_predef.c
@@ -205,6 +205,9 @@ due to conflicts with other common macros. */
#ifndef DISABLE_TLS_RESUME
builtin_macro_create(US"_HAVE_TLS_RESUME");
#endif
+#ifdef EXPERIMENTAL_XCLIENT
+ builtin_macro_create(US"_HAVE_XCLIENT");
+#endif
#ifdef LOOKUP_LSEARCH
builtin_macro_create(US"_HAVE_LOOKUP_LSEARCH");
diff --git a/src/src/macros.h b/src/src/macros.h
index 36ed185ed..c55276332 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -822,7 +822,11 @@ most recent SMTP commands. SCH_NONE is "empty". */
enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_BDAT,
SCH_EHLO, SCH_ETRN, SCH_EXPN, SCH_HELO,
SCH_HELP, SCH_MAIL, SCH_NOOP, SCH_QUIT, SCH_RCPT, SCH_RSET, SCH_STARTTLS,
- SCH_VRFY };
+ SCH_VRFY,
+#ifdef EXPERIMENTAL_XCLIENT
+ SCH_XCLIENT,
+#endif
+ };
/* Returns from host_find_by{name,dns}() */
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 3b26e87d5..7d48f085d 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -187,6 +187,9 @@ static optionlist optionlist_config[] = {
#endif
{ "hosts_require_helo", opt_stringptr, {&hosts_require_helo} },
{ "hosts_treat_as_local", opt_stringptr, {&hosts_treat_as_local} },
+#ifdef EXPERIMENTAL_XCLIENT
+ { "hosts_xclient", opt_stringptr, {&hosts_xclient} },
+#endif
#ifdef LOOKUP_IBASE
{ "ibase_servers", opt_stringptr, {&ibase_servers} },
#endif
@@ -399,7 +402,7 @@ static optionlist optionlist_config[] = {
{ "uucp_from_pattern", opt_stringptr, {&uucp_from_pattern} },
{ "uucp_from_sender", opt_stringptr, {&uucp_from_sender} },
{ "warn_message_file", opt_stringptr, {&warn_message_file} },
- { "write_rejectlog", opt_bool, {&write_rejectlog} }
+ { "write_rejectlog", opt_bool, {&write_rejectlog} },
};
#ifndef MACRO_PREDEF
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 7a45772ce..6f4ad9495 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -75,6 +75,9 @@ enum {
ETRN_CMD, /* This by analogy with TURN from the RFC */
STARTTLS_CMD, /* Required by the STARTTLS RFC */
TLS_AUTH_CMD, /* auto-command at start of SSL */
+#ifdef EXPERIMENTAL_XCLIENT
+ XCLIENT_CMD, /* per xlexkiro implementation */
+#endif
/* This is a dummy to identify the non-sync commands when pipelining */
@@ -189,14 +192,22 @@ count of non-mail commands and possibly provoke an error.
tls_auth is a pseudo-command, never expected in input. It is activated
on TLS startup and looks for a tls authenticator. */
-enum { CL_RSET, CL_HELO, CL_EHLO, CL_AUTH,
+enum {
+ CL_RSET = 0,
+ CL_HELO,
+ CL_EHLO,
+ CL_AUTH,
#ifndef DISABLE_TLS
- CL_STLS, CL_TLAU,
+ CL_STLS,
+ CL_TLAU,
+#endif
+#ifdef EXPERIMENTAL_XCLIENT
+ CL_XCLI,
#endif
};
static smtp_cmd_list cmd_list[] = {
- /* name len cmd has_arg is_mail_cmd */
+ /* name len cmd has_arg is_mail_cmd */
[CL_RSET] = { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */
[CL_HELO] = { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE },
@@ -206,8 +217,9 @@ static smtp_cmd_list cmd_list[] = {
[CL_STLS] = { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE },
[CL_TLAU] = { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE },
#endif
-
-/* If you change anything above here, also fix the definitions below. */
+#ifdef EXPERIMENTAL_XCLIENT
+ [CL_XCLI] = { "xclient", sizeof("xclient")-1, XCLIENT_CMD, TRUE, FALSE },
+#endif
{ "mail from:", sizeof("mail from:")-1, MAIL_CMD, TRUE, TRUE },
{ "rcpt to:", sizeof("rcpt to:")-1, RCPT_CMD, TRUE, TRUE },
@@ -241,6 +253,9 @@ uschar * smtp_names[] =
[SCH_RSET] = US"RSET",
[SCH_STARTTLS] = US"STARTTLS",
[SCH_VRFY] = US"VRFY",
+#ifdef EXPERIMENTAL_XCLIENT
+ [SCH_XCLIENT] = US"XCLIENT",
+#endif
};
static uschar *protocols_local[] = {
@@ -1260,6 +1275,7 @@ return OTHER_CMD;
+
/*************************************************
* Forced closedown of call *
*************************************************/
@@ -1774,7 +1790,6 @@ while (done <= 0)
bsmtp_transaction_linecount = receive_linecount;
break;
-
/* The MAIL FROM command requires an address as an operand. All we
do here is to parse it for syntactic correctness. The form "<>" is
a special case which converts into an empty string. The start/end
@@ -4178,7 +4193,13 @@ while (done <= 0)
fl.tls_advertised = TRUE;
}
#endif
-
+#ifdef EXPERIMENTAL_XCLIENT
+ if (proxy_session || verify_check_host(&hosts_xclient) != FAIL)
+ {
+ g = string_catn(g, smtp_code, 3);
+ g = xclient_smtp_advertise_str(g);
+ }
+#endif
#ifndef DISABLE_PRDR
/* Per Recipient Data Response, draft by Eric A. Hall extending RFC */
if (prdr_enable)
@@ -4244,6 +4265,41 @@ while (done <= 0)
toomany = FALSE;
break; /* HELO/EHLO */
+#ifdef EXPERIMENTAL_XCLIENT
+ case XCLIENT_CMD:
+ {
+ BOOL fatal = fl.helo_seen;
+ uschar * errmsg;
+ int resp;
+
+ HAD(SCH_XCLIENT);
+ smtp_mailcmd_count++;
+
+ if ((errmsg = xclient_smtp_command(smtp_cmd_data, &resp, &fatal)))
+ if (fatal)
+ done = synprot_error(L_smtp_syntax_error, resp, NULL, errmsg);
+ else
+ {
+ smtp_printf("%d %s\r\n", FALSE, resp, errmsg);
+ log_write(0, LOG_MAIN|LOG_REJECT, "rejected XCLIENT from %s: %s",
+ host_and_ident(FALSE), errmsg);
+ }
+ else
+ {
+ fl.helo_seen = FALSE; /* Require another EHLO */
+ smtp_code = string_sprintf("%d", resp);
+
+ /*XXX unclear in spec. if this needs to be an ESMTP banner,
+ nor whether we get the original client's HELO after (or a proxy fake).
+ We require that we do; the following HELO/EHLO handling will set
+ sender_helo_name as normal. */
+
+ smtp_printf("%s XCLIENT success\r\n", FALSE, smtp_code);
+ }
+ break; /* XCLIENT */
+ }
+#endif
+
/* The MAIL command requires an address as an operand. All we do
here is to parse it for syntactic correctness. The form "<>" is
@@ -5353,6 +5409,10 @@ while (done <= 0)
if (acl_smtp_etrn) smtp_printf(" ETRN", TRUE);
if (acl_smtp_expn) smtp_printf(" EXPN", TRUE);
if (acl_smtp_vrfy) smtp_printf(" VRFY", TRUE);
+#ifdef EXPERIMENTAL_XCLIENT
+ if (proxy_session || verify_check_host(&hosts_xclient) != FAIL)
+ smtp_printf(" XCLIENT", TRUE);
+#endif
smtp_printf("\r\n", FALSE);
break;
diff --git a/src/src/xclient.c b/src/src/xclient.c
new file mode 100644
index 000000000..2a8be9b0e
--- /dev/null
+++ b/src/src/xclient.c
@@ -0,0 +1,299 @@
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* Copyright (c) The Exim Maintainers 2023 */
+/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "exim.h"
+
+#ifdef EXPERIMENTAL_XCLIENT
+
+/* From https://www.postfix.org/XCLIENT_README.html I infer two generations of
+protocol. The more recent one obviates the utility of the HELO attribute, since
+it mandates the proxy always sending a HELO/EHLO smtp command following (a
+successful) XCLIENT command, and that will carry a NELO name (which we assume,
+though it isn't specified, will be the actual one presented to the proxy by the
+possibly-new client). The same applies to the PROTO attribute. */
+
+# define XCLIENT_V2
+
+enum xclient_cmd_e {
+ XCLIENT_CMD_UNKNOWN,
+ XCLIENT_CMD_ADDR,
+ XCLIENT_CMD_NAME,
+ XCLIENT_CMD_PORT,
+ XCLIENT_CMD_LOGIN,
+ XCLIENT_CMD_DESTADDR,
+ XCLIENT_CMD_DESTPORT,
+# ifdef XCLIENT_V1
+ XCLIENT_CMD_HELO,
+ XCLIENT_CMD_PROTO,
+# endif
+};
+
+struct xclient_cmd {
+ const uschar * str;
+ unsigned len;
+} xclient_cmds[] = {
+ [XCLIENT_CMD_UNKNOWN] = { NULL },
+ [XCLIENT_CMD_ADDR] = { US"ADDR", 4 },
+ [XCLIENT_CMD_NAME] = { US"NAME", 4 },
+ [XCLIENT_CMD_PORT] = { US"PORT", 4 },
+ [XCLIENT_CMD_LOGIN] = { US"LOGIN", 5 },
+ [XCLIENT_CMD_DESTADDR] = { US"DESTADDR", 8 },
+ [XCLIENT_CMD_DESTPORT] = { US"DESTPORT", 8 },
+# ifdef XCLIENT_V1
+ [XCLIENT_CMD_HELO] = { US"HELO", 4 },
+ [XCLIENT_CMD_PROTO] = { US"PROTO", 5 },
+# endif
+};
+
+/*************************************************
+* XCLIENT proxy implementation *
+*************************************************/
+
+/* Arguments:
+ code points to the coded string
+ end points to the end of coded string
+ ptr where to put the pointer to the result, which is in
+ dynamic store
+Returns: the number of bytes in the result, excluding the final zero;
+ -1 if the input is malformed
+*/
+
+static int
+xclient_xtextdecode(uschar * code, uschar * end, uschar ** ptr)
+{
+return auth_xtextdecode(string_copyn(code, end-code), ptr);
+}
+
+/*************************************************
+* Check XCLIENT line and set sender_address *
+*************************************************/
+
+
+/* Check the format of a XCLIENT line.
+Arguments:
+ s the data portion of the line (already past any white space)
+ resp result: smtp respose code
+ flag input: helo seen output: fail is fatal
+
+Return: NULL on success, or error message
+*/
+
+# define XCLIENT_UNAVAIL US"[UNAVAILABLE]"
+# define XCLIENT_TEMPUNAVAIL US"[TEMPUNAVAIL]"
+
+uschar *
+xclient_smtp_command(uschar * s, int * resp, BOOL * flag)
+{
+uschar * word = s;
+enum {
+ XCLIENT_READ_COMMAND = 0,
+ XCLIENT_READ_VALUE,
+ XCLIENT_SKIP_SPACES
+} state = XCLIENT_SKIP_SPACES;
+enum xclient_cmd_e cmd;
+
+if ( !flag
+ && verify_check_host(&hosts_require_helo) == OK)
+ {
+ *resp = 503;
+ *flag = FALSE;
+ return US"no HELO/EHLO given";
+ }
+
+/* If already in a proxy session, do not re-check permission.
+Strictly we should avoid doing this for a Proxy-Protocol
+session to avoid mixups. */
+
+if(!proxy_session && verify_check_host(&hosts_xclient) == FAIL)
+ {
+ *resp = 550;
+ *flag = TRUE;
+ return US"XCLIENT command used when not advertised";
+ }
+
+if (sender_address)
+ {
+ *resp = 503;
+ *flag = FALSE;
+ return US"mail transaction in progress";
+ }
+
+if (!*word)
+ {
+ s = US"XCLIENT must have at least one operand";
+ goto fatal_501;
+ }
+
+for (state = XCLIENT_SKIP_SPACES; *s; )
+ switch (state)
+ {
+ case XCLIENT_READ_COMMAND:
+ {
+ int len;
+
+ word = s;
+ while (*s && *s != '=') s++;
+ len = s - word;
+ if (!*s)
+ {
+ s = string_sprintf("XCLIENT: missing value for parameter '%.*s'",
+ len, word);
+ goto fatal_501;
+ }
+
+ DEBUG(D_transport) debug_printf(" XCLIENT: cmd %.*s\n", len, word);
+ cmd = XCLIENT_CMD_UNKNOWN;
+ for (struct xclient_cmd * x = xclient_cmds + 1;
+ x < xclient_cmds + nelem(xclient_cmds); x++)
+ if (len == x->len && strncmpic(word, x->str, len) == 0)
+ {
+ cmd = x - xclient_cmds;
+ break;
+ }
+ if (cmd == XCLIENT_CMD_UNKNOWN)
+ {
+ s = string_sprintf("XCLIENT: unrecognised parameter '%.*s'",
+ len, word);
+ goto fatal_501;
+ }
+ state = XCLIENT_READ_VALUE;
+ }
+ break;
+
+ case XCLIENT_READ_VALUE:
+ {
+ int old_pool = store_pool;
+ int len;
+ uschar * val;
+
+ word = ++s; /* skip the = */
+ while (*s && !isspace(*s)) s++;
+ len = s - word;
+
+ DEBUG(D_transport) debug_printf(" XCLIENT: \tvalue %.*s\n", len, word);
+ if (len == 0)
+ { s = US"XCLIENT: zero-length value for param"; goto fatal_501; }
+
+ if ( len == 13
+ && ( strncmpic(word, XCLIENT_UNAVAIL, 13) == 0
+ || strncmpic(word, XCLIENT_TEMPUNAVAIL, 13) == 0
+ ) )
+ val = NULL;
+
+ else if ((len = xclient_xtextdecode(word, s, &val)) == -1)
+ {
+ s = string_sprintf("failed xtext decode for XCLIENT: '%.*s'", len, word);
+ goto fatal_501;
+ }
+
+ store_pool = POOL_PERM;
+ switch (cmd)
+ {
+ case XCLIENT_CMD_ADDR:
+ proxy_local_address = sender_host_address;
+ sender_host_address = val ? string_copyn(val, len) : NULL;
+ break;
+ case XCLIENT_CMD_NAME:
+ sender_host_name = val ? string_copyn(val, len) : NULL;
+ break;
+ case XCLIENT_CMD_PORT:
+ proxy_local_port = sender_host_port;
+ sender_host_port = val ? Uatoi(val) : 0;
+ break;
+ case XCLIENT_CMD_DESTADDR:
+ proxy_external_address = val ? string_copyn(val, len) : NULL;
+ break;
+ case XCLIENT_CMD_DESTPORT:
+ proxy_external_port = val ? Uatoi(val) : 0;
+ break;
+
+ case XCLIENT_CMD_LOGIN:
+ if (val)
+ {
+ authenticated_id = string_copyn(val, len);
+ sender_host_authenticated = US"xclient";
+ authentication_failed = FALSE;
+ }
+ else
+ {
+ authenticated_id = NULL;
+ sender_host_authenticated = NULL;
+ }
+ break;
+
+# ifdef XCLIENT_V1
+ case XCLIENT_CMD_HELO:
+ sender_helo_name = val ? string_copyn(val, len) : NULL;
+ break;
+ case XCLIENT_CMD_PROTO:
+ if (!val)
+ { store_pool = old_pool; s = US"missing proto for XCLIENT"; goto fatal_501; }
+ else if (len == 4 && strncmpic(val, US"SMTP", 4) == 0)
+ *esmtpflag = FALSE; /* function arg */
+ else if (len == 5 && strncmpic(val, US"ESMTP", 5) == 0)
+ *esmtpflag = TRUE;
+ else
+ { store_pool = old_pool; s = US"bad proto for XCLIENT"; goto fatal_501; }
+ break;
+# endif
+ }
+ store_pool = old_pool;
+ state = XCLIENT_SKIP_SPACES;
+ break;
+ }
+
+ case XCLIENT_SKIP_SPACES:
+ while (*s && isspace (*s)) s++;
+ state = XCLIENT_READ_COMMAND;
+ break;
+
+ default:
+ s = US"unhandled XCLIENT parameter type";
+ goto fatal_501;
+ }
+
+if (!proxy_local_address)
+ { s = US"missing ADDR for XCLIENT"; goto fatal_501; }
+if (!proxy_local_port)
+ { s = US"missing PORT for XCLIENT"; goto fatal_501; }
+if (state != XCLIENT_SKIP_SPACES)
+ { s = US"bad state parsing XCLIENT parameters"; goto fatal_501; }
+
+host_build_sender_fullhost();
+proxy_session = TRUE;
+*resp = 220;
+return NULL;
+
+fatal_501:
+ *flag = TRUE;
+ *resp = 501;
+ return s;
+}
+
+# undef XCLIENT_UNAVAIL
+# undef XCLIENT_TEMPUNAVAIL
+
+
+gstring *
+xclient_smtp_advertise_str(gstring * g)
+{
+g = string_catn(g, US"-XCLIENT ", 8);
+for (int i = 1; i < nelem(xclient_cmds); i++)
+ {
+ g = string_catn(g, US" ", 1);
+ g = string_cat(g, xclient_cmds[i].str);
+ }
+return string_catn(g, US"\r\n", 2);
+}
+
+
+#endif /*EXPERIMENTAL_XCLIENT*/
+
+/* vi: aw ai sw=2
+*/
+/* End of xclient.c */
diff --git a/test/confs/4032 b/test/confs/4032
new file mode 100644
index 000000000..9dbe36b9c
--- /dev/null
+++ b/test/confs/4032
@@ -0,0 +1,41 @@
+# Exim test configuration 4032
+# XCLIENT proxy
+
+.ifdef OPTION
+.include DIR/aux-var/tls_conf_prefix
+.else
+.include DIR/aux-var/std_conf_prefix
+.endif
+
+primary_hostname = myhost.test.ex
+hosts_xclient = HOSTIPV4
+queue_only
+
+# ----- Main settings -----
+
+log_selector = +proxy +incoming_port
+
+acl_smtp_rcpt = r_acl
+
+
+begin acl
+
+r_acl:
+ accept
+ logwrite = tls session: ${if def:tls_in_cipher {yes}{no}}
+ logwrite = proxy session: $proxy_session
+ logwrite = local [$received_ip_address]:$received_port
+ logwrite = proxy internal [$proxy_local_address]:$proxy_local_port
+ logwrite = proxy external [$proxy_external_address]:$proxy_external_port
+ logwrite = remote [$sender_host_address]:$sender_host_port
+
+
+# ----- Routers -----
+
+begin routers
+
+dump:
+ driver = redirect
+ data = :blackhole:
+
+# End
diff --git a/test/confs/4033 b/test/confs/4033
new file mode 120000
index 000000000..06b9789ad
--- /dev/null
+++ b/test/confs/4033
@@ -0,0 +1 @@
+4032
\ No newline at end of file
diff --git a/test/confs/4034 b/test/confs/4034
new file mode 120000
index 000000000..06b9789ad
--- /dev/null
+++ b/test/confs/4034
@@ -0,0 +1 @@
+4032
\ No newline at end of file
diff --git a/test/log/4032 b/test/log/4032
new file mode 100644
index 000000000..b02c0c263
--- /dev/null
+++ b/test/log/4032
@@ -0,0 +1,27 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 tls session: no
+1999-03-02 09:44:33 proxy session: no
+1999-03-02 09:44:33 local [127.0.0.1]:1113
+1999-03-02 09:44:33 proxy internal []:0
+1999-03-02 09:44:33 proxy external []:0
+1999-03-02 09:44:33 remote [127.0.0.1]:1114
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= a@??? H=(plainclient) [127.0.0.1]:1114 P=esmtp S=sss
+1999-03-02 09:44:33 tls session: no
+1999-03-02 09:44:33 proxy session: yes
+1999-03-02 09:44:33 local [ip4.ip4.ip4.ip4]:1113
+1999-03-02 09:44:33 proxy internal [ip4.ip4.ip4.ip4]:1115
+1999-03-02 09:44:33 proxy external [10.42.42.42]:1116
+1999-03-02 09:44:33 remote [127.0.0.2]:1117
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= c@??? H=proxylookedupname.net (clienthelo) [127.0.0.2]:1117 P=esmtpa A=xclient:hisloginname PRX=ip4.ip4.ip4.ip4 S=sss
+1999-03-02 09:44:33 tls session: no
+1999-03-02 09:44:33 proxy session: yes
+1999-03-02 09:44:33 local [ip4.ip4.ip4.ip4]:1113
+1999-03-02 09:44:33 proxy internal [127.0.0.2]:1117
+1999-03-02 09:44:33 proxy external [10.42.42.42]:1116
+1999-03-02 09:44:33 remote [127.0.0.3]:1111
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= c2@??? H=(anotherhelo) [127.0.0.3]:1111 P=esmtp PRX=127.0.0.2 S=sss
+1999-03-02 09:44:33 rejected XCLIENT from (anotherhelo) [127.0.0.3]:1111: mail transaction in progress
+1999-03-02 09:44:33 rejected MAIL from miss.ehlo.after.xclient (anotherhelo) [127.0.0.3]:1111: no HELO/EHLO given
+1999-03-02 09:44:33 SMTP call from (xclientproxy) [ip4.ip4.ip4.ip4]:1112 dropped: too many syntax or protocol errors (last command was "XCLIENT SIXSIX=", C=EHLO,XCLIENT,XCLIENT,XCLIENT,XCLIENT)
diff --git a/test/log/4034 b/test/log/4034
new file mode 100644
index 000000000..9ec307501
--- /dev/null
+++ b/test/log/4034
@@ -0,0 +1,29 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 Warning: No server certificate defined; will use a selfsigned one.
+ Suggested action: either install a certificate or change tls_advertise_hosts option
+1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 tls session: yes
+1999-03-02 09:44:33 proxy session: no
+1999-03-02 09:44:33 local [127.0.0.1]:1113
+1999-03-02 09:44:33 proxy internal []:0
+1999-03-02 09:44:33 proxy external []:0
+1999-03-02 09:44:33 remote [127.0.0.1]:1114
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= a@??? H=(plainclient) [127.0.0.1]:1114 P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss
+1999-03-02 09:44:33 tls session: yes
+1999-03-02 09:44:33 proxy session: yes
+1999-03-02 09:44:33 local [ip4.ip4.ip4.ip4]:1113
+1999-03-02 09:44:33 proxy internal [ip4.ip4.ip4.ip4]:1115
+1999-03-02 09:44:33 proxy external [10.42.42.42]:1116
+1999-03-02 09:44:33 remote [127.0.0.2]:1117
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= c@??? H=proxylookedupname.net (clienthelo) [127.0.0.2]:1117 P=esmtpsa X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no A=xclient:hisloginname PRX=ip4.ip4.ip4.ip4 S=sss
+1999-03-02 09:44:33 tls session: yes
+1999-03-02 09:44:33 proxy session: yes
+1999-03-02 09:44:33 local [ip4.ip4.ip4.ip4]:1113
+1999-03-02 09:44:33 proxy internal [127.0.0.2]:1117
+1999-03-02 09:44:33 proxy external [10.42.42.42]:1116
+1999-03-02 09:44:33 remote [127.0.0.3]:1111
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= c2@??? H=(anotherhelo) [127.0.0.3]:1111 P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no PRX=127.0.0.2 S=sss
+1999-03-02 09:44:33 rejected XCLIENT from (anotherhelo) [127.0.0.3]:1111: mail transaction in progress
+1999-03-02 09:44:33 rejected MAIL from miss.ehlo.after.xclient (anotherhelo) [127.0.0.3]:1111: no HELO/EHLO given
+1999-03-02 09:44:33 SMTP call from (xclientproxy) [ip4.ip4.ip4.ip4]:1112 dropped: too many syntax or protocol errors (last command was "XCLIENT SIXSIX=", C=EHLO,STARTTLS,EHLO,XCLIENT,XCLIENT,XCLIENT,XCLIENT)
diff --git a/test/rejectlog/4032 b/test/rejectlog/4032
new file mode 100644
index 000000000..96c50b75e
--- /dev/null
+++ b/test/rejectlog/4032
@@ -0,0 +1,5 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 rejected XCLIENT from (anotherhelo) [127.0.0.3]:1111: mail transaction in progress
+1999-03-02 09:44:33 rejected MAIL from miss.ehlo.after.xclient (anotherhelo) [127.0.0.3]:1111: no HELO/EHLO given
+1999-03-02 09:44:33 SMTP call from (xclientproxy) [ip4.ip4.ip4.ip4]:1112 dropped: too many syntax or protocol errors (last command was "XCLIENT SIXSIX=", C=EHLO,XCLIENT,XCLIENT,XCLIENT,XCLIENT)
diff --git a/test/rejectlog/4034 b/test/rejectlog/4034
new file mode 100644
index 000000000..de477a533
--- /dev/null
+++ b/test/rejectlog/4034
@@ -0,0 +1,5 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 rejected XCLIENT from (anotherhelo) [127.0.0.3]:1111: mail transaction in progress
+1999-03-02 09:44:33 rejected MAIL from miss.ehlo.after.xclient (anotherhelo) [127.0.0.3]:1111: no HELO/EHLO given
+1999-03-02 09:44:33 SMTP call from (xclientproxy) [ip4.ip4.ip4.ip4]:1112 dropped: too many syntax or protocol errors (last command was "XCLIENT SIXSIX=", C=EHLO,STARTTLS,EHLO,XCLIENT,XCLIENT,XCLIENT,XCLIENT)
diff --git a/test/runtest b/test/runtest
index 8d96e13bd..53e12d412 100755
--- a/test/runtest
+++ b/test/runtest
@@ -1387,6 +1387,9 @@ RESET_AFTER_EXTRA_LINE_READ:
}
next if / in limits_advertise_hosts?\? no \(matched "!\*"\)/;
+ # Experimental_XCLIENT
+ next if / in hosts_xclient?\? no \(option unset\)/;
+
# TCP Fast Open
next if /^(ppppp )?setsockopt FASTOPEN: Network Error/;
diff --git a/test/scripts/4032-xclient/4032 b/test/scripts/4032-xclient/4032
new file mode 100644
index 000000000..fa0d0b8c3
--- /dev/null
+++ b/test/scripts/4032-xclient/4032
@@ -0,0 +1,140 @@
+# XCLIENT proxy on inbound smtp
+#
+munge loopback
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+### (1) non-prox plain receive (not advertised) (2) XCLIENT refules when not advertised
+client 127.0.0.1 PORT_D
+??? 220
+EHLO plainclient
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250 HELP
+MAIL FROM:<a@???>
+??? 250
+RCPT TO:<b@???>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242
+??? 550
+QUIT
+??? 221
+****
+#
+### receive, (1) fully loaded (2) new conn (3) bad: transaction in progress (4) bad: missing EHLO after XCLIENT
+client HOSTIPV4 PORT_D
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242 DESTADDR=10.42.42.42 DESTPORT=25 LOGIN=hisloginname
+??? 220
+EHLO clienthelo
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+MAIL FROM:<c@???>
+??? 250
+RCPT TO:<d@???>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+XCLIENT NAME=[TEMPUNAVAIL] ADDR=127.0.0.3 PORT=4243 LOGIN=[UNAVAILABLE]
+??? 220
+EHLO anotherhelo
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+MAIL FROM:<c2@???>
+??? 250
+RCPT TO:<d2@???>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+MAIL FROM:<c2@???>
+??? 250
+XCLIENT NAME=bad.time.for.xclient
+??? 503
+RSET
+??? 250
+XCLIENT NAME=miss.ehlo.after.xclient
+??? 220
+MAIL FROM:<bad@???>
+??? 503
+QUIT
+??? 221
+****
+#
+### (5) no operands to XCLIENT (6,7) unrecognised operands
+client HOSTIPV4 PORT_D
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+XCLIENT
+??? 501
+XCLIENT NONO=
+??? 501
+XCLIENT NAMEfoobar=
+??? 501
+XCLIENT SIXSIX=
+??? 501-
+??? 501 Too many
+???*
+****
+#
+### (7) operand with zero-len value (8) operand with no value
+client HOSTIPV4 PORT_D
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+XCLIENT NAME=
+??? 501
+XCLIENT NAME
+??? 501
+****
+#
+#
+killdaemon
+no_msglog_check
+no_stdout_check
+no_stderr_check
diff --git a/test/scripts/4032-xclient/4033 b/test/scripts/4032-xclient/4033
new file mode 100644
index 000000000..f3a4ecdeb
--- /dev/null
+++ b/test/scripts/4032-xclient/4033
@@ -0,0 +1,62 @@
+# XCLIENT proxy on inbound -bh
+#
+### (1) non-prox plain receive (not advertised) (2) XCLIENT refules when not advertised
+exim -bh 127.0.0.1.4241 -oMi 127.0.0.1
+EHLO plainclient
+MAIL FROM:<a@???>
+RCPT TO:<b@???>
+DATA
+Subject: test
+
+body
+.
+XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242
+QUIT
+****
+#
+### receive, (1) fully loaded (2) new conn (3) bad: transaction in progress
+exim -bh HOSTIPV4.4241 -oMi HOSTIPV4
+EHLO xclientproxy
+XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242 DESTADDR=10.42.42.42 DESTPORT=25 LOGIN=hisloginname
+EHLO clienthelo
+MAIL FROM:<c@???>
+RCPT TO:<d@???>
+DATA
+Subject: test
+
+body
+.
+XCLIENT NAME=[TEMPUNAVAIL] ADDR=127.0.0.3 PORT=4243 LOGIN=[UNAVAILABLE]
+EHLO anotherhelo
+MAIL FROM:<c2@???>
+RCPT TO:<d2@???>
+DATA
+Subject: test
+
+body
+.
+MAIL FROM:<c2@???>
+XCLIENT NAME=bad.time.for.xclient
+RSET
+XCLIENT NAME=miss.ehlo.after.xclient
+MAIL FROM:<bad@???>
+QUIT
+****
+#
+### (4) no operands to XCLIENT (5,6) unrecognised operands
+exim -bh HOSTIPV4.4241 -oMi HOSTIPV4
+EHLO xclientproxy
+XCLIENT
+XCLIENT NONO=
+XCLIENT NAMEfoobar=
+XCLIENT SIXSIX=
+****
+#
+### (7) operand with zero-len value (8) operand with no value
+exim -bh HOSTIPV4.4241 -oMi HOSTIPV4
+EHLO xclientproxy
+XCLIENT NAME=
+XCLIENT NAME
+****
+#
+no_stderr_check
diff --git a/test/scripts/4032-xclient/REQUIRES b/test/scripts/4032-xclient/REQUIRES
new file mode 100644
index 000000000..5f4d76eed
--- /dev/null
+++ b/test/scripts/4032-xclient/REQUIRES
@@ -0,0 +1 @@
+support Experimental_XCLIENT
diff --git a/test/scripts/4034-xclient-tls/4034 b/test/scripts/4034-xclient-tls/4034
new file mode 100644
index 000000000..c8a4f10c4
--- /dev/null
+++ b/test/scripts/4034-xclient-tls/4034
@@ -0,0 +1,179 @@
+# XCLIENT under TLS
+#
+munge loopback
+#
+exim -bd -DSERVER=server -DOPTION -oX PORT_D
+****
+#
+### (1) non-prox plain receive (not advertised) (2) XCLIENT refusal when not advertised
+client-anytls 127.0.0.1 PORT_D
+??? 220
+EHLO plainclient
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-STARTTLS
+??? 250 HELP
+STARTTLS
+??? 220
+EHLO plainclient
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250 HELP
+MAIL FROM:<a@???>
+??? 250
+RCPT TO:<b@???>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242
+??? 550
+QUIT
+??? 221
+****
+#
+### receive, (1) fully loaded (2) new conn (3) bad: transaction in progress (4) bad: missing EHLO after XCLIENT
+client-anytls HOSTIPV4 PORT_D
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-STARTTLS
+??? 250-XCLIENT
+??? 250 HELP
+STARTTLS
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242 DESTADDR=10.42.42.42 DESTPORT=25 LOGIN=hisloginname
+??? 220
+EHLO clienthelo
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+MAIL FROM:<c@???>
+??? 250
+RCPT TO:<d@???>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+XCLIENT NAME=[TEMPUNAVAIL] ADDR=127.0.0.3 PORT=4243 LOGIN=[UNAVAILABLE]
+??? 220
+EHLO anotherhelo
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+MAIL FROM:<c2@???>
+??? 250
+RCPT TO:<d2@???>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+MAIL FROM:<c2@???>
+??? 250
+XCLIENT NAME=bad.time.for.xclient
+??? 503
+RSET
+??? 250
+XCLIENT NAME=miss.ehlo.after.xclient
+??? 220
+MAIL FROM:<bad@???>
+??? 503
+QUIT
+??? 221
+****
+#
+### (5) no operands to XCLIENT (6,7) unrecognised operands
+client-anytls HOSTIPV4 PORT_D
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-STARTTLS
+??? 250-XCLIENT
+??? 250 HELP
+STARTTLS
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+XCLIENT
+??? 501
+XCLIENT NONO=
+??? 501
+XCLIENT NAMEfoobar=
+??? 501
+XCLIENT SIXSIX=
+??? 501-
+??? 501 Too many
+???*
+****
+#
+### (7) operand with zero-len value (8) operand with no value
+client-anytls HOSTIPV4 PORT_D
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-STARTTLS
+??? 250-XCLIENT
+??? 250 HELP
+STARTTLS
+??? 220
+EHLO xclientproxy
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-XCLIENT
+??? 250 HELP
+XCLIENT NAME=
+??? 501
+XCLIENT NAME
+??? 501
+****
+#
+#
+killdaemon
+no_msglog_check
+no_stdout_check
+no_stderr_check
diff --git a/test/scripts/4034-xclient-tls/REQUIRES b/test/scripts/4034-xclient-tls/REQUIRES
new file mode 100644
index 000000000..4361afb13
--- /dev/null
+++ b/test/scripts/4034-xclient-tls/REQUIRES
@@ -0,0 +1,2 @@
+support Experimental_XCLIENT
+feature _HAVE_TLS
diff --git a/test/stdout/4032 b/test/stdout/4032
new file mode 100644
index 000000000..41c916c0e
--- /dev/null
+++ b/test/stdout/4032
@@ -0,0 +1,199 @@
+### (1) non-prox plain receive (not advertised) (2) XCLIENT refules when not advertised
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO plainclient
+??? 250-
+<<< 250-myhost.test.ex Hello plainclient [IP_LOOPBACK_ADDR]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250 HELP
+<<< 250 HELP
+>>> MAIL FROM:<a@???>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<b@???>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: test
+>>>
+>>> body
+>>> .
+??? 250
+<<< 250 OK id=10HmaX-0005vi-00
+>>> XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242
+??? 550
+<<< 550 XCLIENT command used when not advertised
+>>> QUIT
+??? 221
+<<< 221 myhost.test.ex closing connection
+End of script
+### receive, (1) fully loaded (2) new conn (3) bad: transaction in progress (4) bad: missing EHLO after XCLIENT
+Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO xclientproxy
+??? 250-
+<<< 250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-XCLIENT
+<<< 250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+??? 250 HELP
+<<< 250 HELP
+>>> XCLIENT NAME=proxylookedupname.net ADDR=127.0.0.2 PORT=4242 DESTADDR=10.42.42.42 DESTPORT=25 LOGIN=hisloginname
+??? 220
+<<< 220 XCLIENT success
+>>> EHLO clienthelo
+??? 250-
+<<< 250-myhost.test.ex Hello proxylookedupname.net [127.0.0.2]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-XCLIENT
+<<< 250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+??? 250 HELP
+<<< 250 HELP
+>>> MAIL FROM:<c@???>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<d@???>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: test
+>>>
+>>> body
+>>> .
+??? 250
+<<< 250 OK id=10HmaY-0005vi-00
+>>> XCLIENT NAME=[TEMPUNAVAIL] ADDR=127.0.0.3 PORT=4243 LOGIN=[UNAVAILABLE]
+??? 220
+<<< 220 XCLIENT success
+>>> EHLO anotherhelo
+??? 250-
+<<< 250-myhost.test.ex Hello anotherhelo [127.0.0.3]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-XCLIENT
+<<< 250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+??? 250 HELP
+<<< 250 HELP
+>>> MAIL FROM:<c2@???>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<d2@???>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: test
+>>>
+>>> body
+>>> .
+??? 250
+<<< 250 OK id=10HmaZ-0005vi-00
+>>> MAIL FROM:<c2@???>
+??? 250
+<<< 250 OK
+>>> XCLIENT NAME=bad.time.for.xclient
+??? 503
+<<< 503 mail transaction in progress
+>>> RSET
+??? 250
+<<< 250 Reset OK
+>>> XCLIENT NAME=miss.ehlo.after.xclient
+??? 220
+<<< 220 XCLIENT success
+>>> MAIL FROM:<bad@???>
+??? 503
+<<< 503 HELO or EHLO required
+>>> QUIT
+??? 221
+<<< 221 myhost.test.ex closing connection
+End of script
+### (5) no operands to XCLIENT (6,7) unrecognised operands
+Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO xclientproxy
+??? 250-
+<<< 250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-XCLIENT
+<<< 250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+??? 250 HELP
+<<< 250 HELP
+>>> XCLIENT
+??? 501
+<<< 501 XCLIENT must have at least one operand
+>>> XCLIENT NONO=
+??? 501
+<<< 501 XCLIENT: unrecognised parameter 'NONO'
+>>> XCLIENT NAMEfoobar=
+??? 501
+<<< 501 XCLIENT: unrecognised parameter 'NAMEfoobar'
+>>> XCLIENT SIXSIX=
+??? 501-
+<<< 501-XCLIENT: unrecognised parameter 'SIXSIX'
+??? 501 Too many
+<<< 501 Too many syntax or protocol errors
+???*
+Expected EOF read
+End of script
+### (7) operand with zero-len value (8) operand with no value
+Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO xclientproxy
+??? 250-
+<<< 250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-XCLIENT
+<<< 250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+??? 250 HELP
+<<< 250 HELP
+>>> XCLIENT NAME=
+??? 501
+<<< 501 XCLIENT: zero-length value for param
+>>> XCLIENT NAME
+??? 501
+<<< 501 XCLIENT: missing value for parameter 'NAME'
+End of script
+
+******** SERVER ********
+### (1) non-prox plain receive (not advertised) (2) XCLIENT refules when not advertised
+### receive, (1) fully loaded (2) new conn (3) bad: transaction in progress (4) bad: missing EHLO after XCLIENT
+### (5) no operands to XCLIENT (6,7) unrecognised operands
+### (7) operand with zero-len value (8) operand with no value
diff --git a/test/stdout/4033 b/test/stdout/4033
new file mode 100644
index 000000000..546ca8b1a
--- /dev/null
+++ b/test/stdout/4033
@@ -0,0 +1,108 @@
+### (1) non-prox plain receive (not advertised) (2) XCLIENT refules when not advertised
+
+**** SMTP testing session as if from host 127.0.0.1
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello plainclient [127.0.0.1]
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaX-0005vi-00
+
+**** SMTP testing: that is not a real message id!
+
+550 XCLIENT command used when not advertised
+221 myhost.test.ex closing connection
+### receive, (1) fully loaded (2) new conn (3) bad: transaction in progress
+
+**** SMTP testing session as if from host ip4.ip4.ip4.ip4
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+250 HELP
+220 XCLIENT success
+250-myhost.test.ex Hello proxylookedupname.net [127.0.0.2]
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaY-0005vi-00
+
+**** SMTP testing: that is not a real message id!
+
+220 XCLIENT success
+250-myhost.test.ex Hello anotherhelo [127.0.0.3]
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaZ-0005vi-00
+
+**** SMTP testing: that is not a real message id!
+
+250 OK
+503 mail transaction in progress
+250 Reset OK
+220 XCLIENT success
+503 HELO or EHLO required
+221 myhost.test.ex closing connection
+### (4) no operands to XCLIENT (5,6) unrecognised operands
+
+**** SMTP testing session as if from host ip4.ip4.ip4.ip4
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+250 HELP
+501 XCLIENT must have at least one operand
+501 XCLIENT: unrecognised parameter 'NONO'
+501 XCLIENT: unrecognised parameter 'NAMEfoobar'
+501-XCLIENT: unrecognised parameter 'SIXSIX'
+501 Too many syntax or protocol errors
+### (7) operand with zero-len value (8) operand with no value
+
+**** SMTP testing session as if from host ip4.ip4.ip4.ip4
+**** but without any ident (RFC 1413) callback.
+**** This is not for real!
+
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT
+250 HELP
+501 XCLIENT: zero-length value for param
+501 XCLIENT: missing value for parameter 'NAME'
+421 myhost.test.ex lost input connection
+
+******** SERVER ********
+### (1) non-prox plain receive (not advertised) (2) XCLIENT refules when not advertised
+### receive, (1) fully loaded (2) new conn (3) bad: transaction in progress
+### (4) no operands to XCLIENT (5,6) unrecognised operands
+### (7) operand with zero-len value (8) operand with no value