summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2023-03-25 23:21:15 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2023-03-25 23:21:15 +0000
commit24cda181fb88542cf38db2beae5d0ddb37f59c5c (patch)
treeed6c3337099bf3846067833e25321a9deaf6ceea /src
parentdf0dc54a7666ef64b8a6681ab7b50a4836905203 (diff)
downloadexim4-24cda181fb88542cf38db2beae5d0ddb37f59c5c.tar.gz
Experimental_XCLIENT. Bug 2702
Diffstat (limited to 'src')
-rw-r--r--src/OS/Makefile-Base4
-rwxr-xr-xsrc/scripts/MakeLinks2
-rw-r--r--src/src/auths/xtextdecode.c4
-rw-r--r--src/src/config.h.defaults1
-rw-r--r--src/src/exim.c3
-rw-r--r--src/src/functions.h5
-rw-r--r--src/src/globals.c17
-rw-r--r--src/src/globals.h13
-rw-r--r--src/src/host.c4
-rw-r--r--src/src/macro_predef.c3
-rw-r--r--src/src/macros.h6
-rw-r--r--src/src/readconf.c5
-rw-r--r--src/src/smtp_in.c74
-rw-r--r--src/src/xclient.c299
14 files changed, 415 insertions, 25 deletions
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 */