Experimental_XCLIENT. Bug 2702
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 25 Mar 2023 23:21:15 +0000 (23:21 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 25 Mar 2023 23:21:15 +0000 (23:21 +0000)
30 files changed:
doc/doc-txt/experimental-spec.txt
src/OS/Makefile-Base
src/scripts/MakeLinks
src/src/auths/xtextdecode.c
src/src/config.h.defaults
src/src/exim.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/host.c
src/src/macro_predef.c
src/src/macros.h
src/src/readconf.c
src/src/smtp_in.c
src/src/xclient.c [new file with mode: 0644]
test/confs/4032 [new file with mode: 0644]
test/confs/4033 [new symlink]
test/confs/4034 [new symlink]
test/log/4032 [new file with mode: 0644]
test/log/4034 [new file with mode: 0644]
test/rejectlog/4032 [new file with mode: 0644]
test/rejectlog/4034 [new file with mode: 0644]
test/runtest
test/scripts/4032-xclient/4032 [new file with mode: 0644]
test/scripts/4032-xclient/4033 [new file with mode: 0644]
test/scripts/4032-xclient/REQUIRES [new file with mode: 0644]
test/scripts/4034-xclient-tls/4034 [new file with mode: 0644]
test/scripts/4034-xclient-tls/REQUIRES [new file with mode: 0644]
test/stdout/4032 [new file with mode: 0644]
test/stdout/4033 [new file with mode: 0644]

index aac8ca77d622f976d509230bac666723c745ef79..5bf00a7f1deeb9b7cc4cb92a5c74de175417427c 100644 (file)
@@ -662,6 +662,37 @@ Values advertised are only noted for TLS connections and ones for which
 the server does not advertise TLS support.
 
 
 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
 --------------------------------------------------------------
 --------------------------------------------------------------
 End of file
 --------------------------------------------------------------
index d00ab94043ea606625db18ccd14769b6f8399ed0..71aee4d93592c53d5ab8cedd1d153db2bd751b3c 100644 (file)
@@ -497,7 +497,8 @@ OBJ_EXPERIMENTAL =  arc.o \
                        dmarc.o \
                        imap_utf7.o \
                        spf.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.
 
 # 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
 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
 
 # The module containing tables of available lookups, routers, auths, and
 # transports must be rebuilt if any of them are. However, because the makefiles
index af613806326f154137364a477759aeaab9393db4..0694af4c087145413e6d7f43f97f80cd9929f738 100755 (executable)
@@ -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 \
 
 # 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
 do
   ln -s ../src/$f $f
 done
index b6a9271947978dcfa90eae0763de618ab20e3995..edd2282d0a1acf14d338d21e021458654f0e271e 100644 (file)
@@ -32,9 +32,9 @@ Returns:      the number of bytes in the result, excluding the final zero;
 */
 
 int
 */
 
 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;
 
 uschar * result = store_get(Ustrlen(code) + 1, code);
 *ptr = result;
 
index 22170522467b52c590761ea71a319eddb00cbe19..fb5fe3603bcb268420c77252b763071953204e9c 100644 (file)
@@ -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_DSN_INFO
 #define EXPERIMENTAL_ESMTP_LIMITS
 #define EXPERIMENTAL_QUEUEFILE
+#define EXPERIMENTAL_XCLIENT
 
 
 /* For developers */
 
 
 /* For developers */
index c16beb1afaf5441523892ec43f97cbe118856d5a..06863347d81b7069c63334d390a0b761fee9d81a 100644 (file)
@@ -1132,6 +1132,9 @@ g = string_cat(g, US"Support for:");
 #ifdef EXPERIMENTAL_QUEUEFILE
   g = string_cat(g, US" Experimental_QUEUEFILE");
 #endif
 #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):");
 g = string_cat(g, US"\n");
 
 g = string_cat(g, US"Lookups (built-in):");
index 76392f3043dbb8b4f595b210bec2db67a95d2a93..aa5057a83c19adb5fdbfb3b382acafe3241fdf15 100644 (file)
@@ -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);
 
 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.
 
 /******************************************************************************/
 /* Predicate: if an address is in a tainted pool.
index 539bae00e6d99f97ccc42131bb7a06d667d02b46..9f4053937e0db3f16b58fea7c8715b43b219356d 100644 (file)
@@ -995,11 +995,18 @@ uschar *host_lookup_msg        = US"";
 int     host_number            = 0;
 uschar *host_number_string     = NULL;
 uschar *host_reject_connection = NULL;
 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_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;
 
 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";
 
 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;
 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;
 
 const uschar *warnmsg_delay    = NULL;
 const uschar *warnmsg_recipients = NULL;
 
-
 /*  End of globals.c */
 /*  End of globals.c */
index e216b92087b4f9b3ab4969508750ad0bb9f7a21a..3a5513382c6e574f2a679e0ba7455cef608aa023 100644 (file)
@@ -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 *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 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_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 */
 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 */
 
 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 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 */
 #endif
 
 extern uschar *prvscheck_address;      /* Set during prvscheck expansion item */
index 8d53eb3ded1575fbcbbecc3ffb79e717351f9000..136ee89537801420c904b4ad963d563d37ef2f2c 100644 (file)
@@ -824,9 +824,9 @@ Returns:     pointer to character string
 */
 
 uschar *
 */
 
 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
 
 /* 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
index 0053cb2453b4699135a15863646360ace0fcd111..8fade68ca5f7f6e1688a7f3875a7a53207ec1ac8 100644 (file)
@@ -205,6 +205,9 @@ due to conflicts with other common macros. */
 #ifndef DISABLE_TLS_RESUME
   builtin_macro_create(US"_HAVE_TLS_RESUME");
 #endif
 #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");
 
 #ifdef LOOKUP_LSEARCH
   builtin_macro_create(US"_HAVE_LOOKUP_LSEARCH");
index 36ed185ed7442173a4930e95bbb781d990af70f6..c55276332f09c6ba44cd27c0490f94ad94498a81 100644 (file)
@@ -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,
 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}() */
 
 
 /* Returns from host_find_by{name,dns}() */
 
index 3b26e87d5a506b04d086139aba1edea606d1e4a5..7d48f085df33084759b94e1f192b00df8c1ec6a8 100644 (file)
@@ -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} },
 #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
 #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} },
   { "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
 };
 
 #ifndef MACRO_PREDEF
index 7a45772cef98c3674fcd28e8f7b6ac4a9cfc0560..6f4ad94956c8ae707f6279161d7dfe4e1bf0003a 100644 (file)
@@ -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 */
   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 */
 
 
   /* 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. */
 
 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
 #ifndef DISABLE_TLS
-       CL_STLS, CL_TLAU,
+       CL_STLS,
+       CL_TLAU,
+#endif
+#ifdef EXPERIMENTAL_XCLIENT
+       CL_XCLI,
 #endif
 };
 
 static smtp_cmd_list cmd_list[] = {
 #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 },
 
   [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
   [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  },
 
   { "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",
   [SCH_RSET] = US"RSET",
   [SCH_STARTTLS] = US"STARTTLS",
   [SCH_VRFY] = US"VRFY",
+#ifdef EXPERIMENTAL_XCLIENT
+  [SCH_XCLIENT] = US"XCLIENT",
+#endif
   };
 
 static uschar *protocols_local[] = {
   };
 
 static uschar *protocols_local[] = {
@@ -1260,6 +1275,7 @@ return OTHER_CMD;
 
 
 
 
 
 
+
 /*************************************************
 *          Forced closedown of call              *
 *************************************************/
 /*************************************************
 *          Forced closedown of call              *
 *************************************************/
@@ -1774,7 +1790,6 @@ while (done <= 0)
       bsmtp_transaction_linecount = receive_linecount;
       break;
 
       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
     /* 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
          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)
 #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 */
 
       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
 
     /* 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);
       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;
 
       smtp_printf("\r\n", FALSE);
       break;
 
diff --git a/src/src/xclient.c b/src/src/xclient.c
new file mode 100644 (file)
index 0000000..2a8be9b
--- /dev/null
@@ -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 (file)
index 0000000..9dbe36b
--- /dev/null
@@ -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 (symlink)
index 0000000..06b9789
--- /dev/null
@@ -0,0 +1 @@
+4032
\ No newline at end of file
diff --git a/test/confs/4034 b/test/confs/4034
new file mode 120000 (symlink)
index 0000000..06b9789
--- /dev/null
@@ -0,0 +1 @@
+4032
\ No newline at end of file
diff --git a/test/log/4032 b/test/log/4032
new file mode 100644 (file)
index 0000000..b02c0c2
--- /dev/null
@@ -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@test.ex 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@test.ex 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@test.ex 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 (file)
index 0000000..9ec3075
--- /dev/null
@@ -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@test.ex 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@test.ex 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@test.ex 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 (file)
index 0000000..96c50b7
--- /dev/null
@@ -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 (file)
index 0000000..de477a5
--- /dev/null
@@ -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)
index 8d96e13bda5f65f9d77b2657e5c1cd9bbe8af9b5..53e12d412d0ce253dc404e3151aadcb90d7d9e46 100755 (executable)
@@ -1387,6 +1387,9 @@ RESET_AFTER_EXTRA_LINE_READ:
       }
     next if / in limits_advertise_hosts?\? no \(matched "!\*"\)/;
 
       }
     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/;
 
     # 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 (file)
index 0000000..fa0d0b8
--- /dev/null
@@ -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@test.ex>
+??? 250
+RCPT TO:<b@test.ex>
+??? 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@test.ex>
+??? 250
+RCPT TO:<d@test.ex>
+??? 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@test.ex>
+??? 250
+RCPT TO:<d2@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+MAIL FROM:<c2@test.ex>
+??? 250
+XCLIENT NAME=bad.time.for.xclient
+??? 503
+RSET
+??? 250
+XCLIENT NAME=miss.ehlo.after.xclient
+??? 220
+MAIL FROM:<bad@test.ex>
+??? 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 (file)
index 0000000..f3a4ecd
--- /dev/null
@@ -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@test.ex>
+RCPT TO:<b@test.ex>
+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@test.ex>
+RCPT TO:<d@test.ex>
+DATA
+Subject: test
+
+body
+.
+XCLIENT NAME=[TEMPUNAVAIL] ADDR=127.0.0.3 PORT=4243 LOGIN=[UNAVAILABLE]
+EHLO anotherhelo
+MAIL FROM:<c2@test.ex>
+RCPT TO:<d2@test.ex>
+DATA
+Subject: test
+
+body
+.
+MAIL FROM:<c2@test.ex>
+XCLIENT NAME=bad.time.for.xclient
+RSET
+XCLIENT NAME=miss.ehlo.after.xclient
+MAIL FROM:<bad@test.ex>
+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 (file)
index 0000000..5f4d76e
--- /dev/null
@@ -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 (file)
index 0000000..c8a4f10
--- /dev/null
@@ -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@test.ex>
+??? 250
+RCPT TO:<b@test.ex>
+??? 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@test.ex>
+??? 250
+RCPT TO:<d@test.ex>
+??? 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@test.ex>
+??? 250
+RCPT TO:<d2@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+MAIL FROM:<c2@test.ex>
+??? 250
+XCLIENT NAME=bad.time.for.xclient
+??? 503
+RSET
+??? 250
+XCLIENT NAME=miss.ehlo.after.xclient
+??? 220
+MAIL FROM:<bad@test.ex>
+??? 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 (file)
index 0000000..4361afb
--- /dev/null
@@ -0,0 +1,2 @@
+support Experimental_XCLIENT
+feature _HAVE_TLS
diff --git a/test/stdout/4032 b/test/stdout/4032
new file mode 100644 (file)
index 0000000..41c916c
--- /dev/null
@@ -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@test.ex>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<b@test.ex>
+??? 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@test.ex>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<d@test.ex>
+??? 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@test.ex>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<d2@test.ex>
+??? 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@test.ex>
+??? 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@test.ex>
+??? 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 (file)
index 0000000..546ca8b
--- /dev/null
@@ -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\r
+250-myhost.test.ex Hello plainclient [127.0.0.1]\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaX-0005vi-00\r
+
+**** SMTP testing: that is not a real message id!
+
+550 XCLIENT command used when not advertised\r
+221 myhost.test.ex closing connection\r
+### 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\r
+250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT\r
+250 HELP\r
+220 XCLIENT success\r
+250-myhost.test.ex Hello proxylookedupname.net [127.0.0.2]\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaY-0005vi-00\r
+
+**** SMTP testing: that is not a real message id!
+
+220 XCLIENT success\r
+250-myhost.test.ex Hello anotherhelo [127.0.0.3]\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT\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-0005vi-00\r
+
+**** SMTP testing: that is not a real message id!
+
+250 OK\r
+503 mail transaction in progress\r
+250 Reset OK\r
+220 XCLIENT success\r
+503 HELO or EHLO required\r
+221 myhost.test.ex closing connection\r
+###          (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\r
+250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT\r
+250 HELP\r
+501 XCLIENT must have at least one operand\r
+501 XCLIENT: unrecognised parameter 'NONO'\r
+501 XCLIENT: unrecognised parameter 'NAMEfoobar'\r
+501-XCLIENT: unrecognised parameter 'SIXSIX'\r
+501 Too many syntax or protocol errors\r
+###          (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\r
+250-myhost.test.ex Hello xclientproxy [ip4.ip4.ip4.ip4]\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250-XCLIENT ADDR NAME PORT LOGIN DESTADDR DESTPORT\r
+250 HELP\r
+501 XCLIENT: zero-length value for param\r
+501 XCLIENT: missing value for parameter 'NAME'\r
+421 myhost.test.ex lost input connection\r
+
+******** 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