* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */
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 */
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 },
[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 },
[SCH_RSET] = US"RSET",
[SCH_STARTTLS] = US"STARTTLS",
[SCH_VRFY] = US"VRFY",
+#ifdef EXPERIMENTAL_XCLIENT
+ [SCH_XCLIENT] = US"XCLIENT",
+#endif
};
static uschar *protocols_local[] = {
-#ifdef SUPPORT_PROXY
-/*************************************************
-* Check if host is required proxy host *
-*************************************************/
-/* The function determines if inbound host will be a regular smtp host
-or if it is configured that it must use Proxy Protocol. A local
-connection cannot.
-
-Arguments: none
-Returns: bool
-*/
-
-static BOOL
-check_proxy_protocol_host()
-{
-int rc;
-
-if ( sender_host_address
- && (rc = verify_check_this_host(CUSS &hosts_proxy, NULL, NULL,
- sender_host_address, NULL)) == OK)
- {
- DEBUG(D_receive)
- debug_printf("Detected proxy protocol configured host\n");
- proxy_session = TRUE;
- }
-return proxy_session;
-}
-
-
-/*************************************************
-* Read data until newline or end of buffer *
-*************************************************/
-/* While SMTP is server-speaks-first, TLS is client-speaks-first, so we can't
-read an entire buffer and assume there will be nothing past a proxy protocol
-header. Our approach normally is to use stdio, but again that relies upon
-"STARTTLS\r\n" and a server response before the client starts TLS handshake, or
-reading _nothing_ before client TLS handshake. So we don't want to use the
-usual buffering reads which may read enough to block TLS starting.
-
-So unfortunately we're down to "read one byte at a time, with a syscall each,
-and expect a little overhead", for all proxy-opened connections which are v1,
-just to handle the TLS-on-connect case. Since SSL functions wrap the
-underlying fd, we can't assume that we can feed them any already-read content.
-
-We need to know where to read to, the max capacity, and we'll read until we
-get a CR and one more character. Let the caller scream if it's CR+!LF.
-
-Return the amount read.
-*/
-
-static int
-swallow_until_crlf(int fd, uschar *base, int already, int capacity)
-{
-uschar *to = base + already;
-uschar *cr;
-int have = 0;
-int ret;
-int last = 0;
-
-/* For "PROXY UNKNOWN\r\n" we, at time of writing, expect to have read
-up through the \r; for the _normal_ case, we haven't yet seen the \r. */
-
-cr = memchr(base, '\r', already);
-if (cr != NULL)
- {
- if ((cr - base) < already - 1)
- {
- /* \r and presumed \n already within what we have; probably not
- actually proxy protocol, but abort cleanly. */
- return 0;
- }
- /* \r is last character read, just need one more. */
- last = 1;
- }
-
-while (capacity > 0)
- {
- do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout);
- if (ret == -1)
- return -1;
- have++;
- if (last)
- return have;
- if (*to == '\r')
- last = 1;
- capacity--;
- to++;
- }
-
-/* reached end without having room for a final newline, abort */
-errno = EOVERFLOW;
-return -1;
-}
-
-
-static void
-proxy_debug(uschar * buf, unsigned start, unsigned end)
-{
-debug_printf("PROXY<<");
-while (start < end) debug_printf(" %02x", buf[start++]);
-debug_printf("\n");
-}
-
-
-/*************************************************
-* Setup host for proxy protocol *
-*************************************************/
-/* The function configures the connection based on a header from the
-inbound host to use Proxy Protocol. The specification is very exact
-so exit with an error if do not find the exact required pieces. This
-includes an incorrect number of spaces separating args.
-
-Arguments: none
-Returns: Boolean success
-*/
-
-static void
-setup_proxy_protocol_host()
-{
-union {
- struct {
- uschar line[108];
- } v1;
- struct {
- uschar sig[12];
- uint8_t ver_cmd;
- uint8_t fam;
- uint16_t len;
- union {
- struct { /* TCP/UDP over IPv4, len = 12 */
- uint32_t src_addr;
- uint32_t dst_addr;
- uint16_t src_port;
- uint16_t dst_port;
- } ip4;
- struct { /* TCP/UDP over IPv6, len = 36 */
- uint8_t src_addr[16];
- uint8_t dst_addr[16];
- uint16_t src_port;
- uint16_t dst_port;
- } ip6;
- struct { /* AF_UNIX sockets, len = 216 */
- uschar src_addr[108];
- uschar dst_addr[108];
- } unx;
- } addr;
- } v2;
-} hdr;
-
-/* Temp variables used in PPv2 address:port parsing */
-uint16_t tmpport;
-char tmpip[INET_ADDRSTRLEN];
-struct sockaddr_in tmpaddr;
-char tmpip6[INET6_ADDRSTRLEN];
-struct sockaddr_in6 tmpaddr6;
-
-/* We can't read "all data until end" because while SMTP is
-server-speaks-first, the TLS handshake is client-speaks-first, so for
-TLS-on-connect ports the proxy protocol header will usually be immediately
-followed by a TLS handshake, and with N TLS libraries, we can't reliably
-reinject data for reading by those. So instead we first read "enough to be
-safely read within the header, and figure out how much more to read".
-For v1 we will later read to the end-of-line, for v2 we will read based upon
-the stated length.
-
-The v2 sig is 12 octets, and another 4 gets us the length, so we know how much
-data is needed total. For v1, where the line looks like:
-PROXY TCPn L3src L3dest SrcPort DestPort \r\n
-
-However, for v1 there's also `PROXY UNKNOWN\r\n` which is only 15 octets.
-We seem to support that. So, if we read 14 octets then we can tell if we're
-v2 or v1. If we're v1, we can continue reading as normal.
-
-If we're v2, we can't slurp up the entire header. We need the length in the
-15th & 16th octets, then to read everything after that.
-
-So to safely handle v1 and v2, with client-sent-first supported correctly,
-we have to do a minimum of 3 read calls, not 1. Eww.
-*/
-
-# define PROXY_INITIAL_READ 14
-# define PROXY_V2_HEADER_SIZE 16
-# if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE
-# error Code bug in sizes of data to read for proxy usage
-# endif
-
-int get_ok = 0;
-int size, ret;
-int fd = fileno(smtp_in);
-const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
-uschar * iptype; /* To display debug info */
-socklen_t vslen = sizeof(struct timeval);
-BOOL yield = FALSE;
-
-os_non_restarting_signal(SIGALRM, command_timeout_handler);
-ALARM(proxy_protocol_timeout);
-
-do
- {
- /* The inbound host was declared to be a Proxy Protocol host, so
- don't do a PEEK into the data, actually slurp up enough to be
- "safe". Can't take it all because TLS-on-connect clients follow
- immediately with TLS handshake. */
- ret = read(fd, &hdr, PROXY_INITIAL_READ);
- } while (ret == -1 && errno == EINTR && !had_command_timeout);
-
-if (ret == -1)
- goto proxyfail;
-DEBUG(D_receive) proxy_debug(US &hdr, 0, ret);
-
-/* For v2, handle reading the length, and then the rest. */
-if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
- {
- int retmore;
- uint8_t ver;
-
- DEBUG(D_receive) debug_printf("v2\n");
-
- /* First get the length fields. */
- do
- {
- retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ);
- } while (retmore == -1 && errno == EINTR && !had_command_timeout);
- if (retmore == -1)
- goto proxyfail;
- DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
-
- ret += retmore;
-
- ver = (hdr.v2.ver_cmd & 0xf0) >> 4;
-
- /* May 2014: haproxy combined the version and command into one byte to
- allow two full bytes for the length field in order to proxy SSL
- connections. SSL Proxy is not supported in this version of Exim, but
- must still separate values here. */
-
- if (ver != 0x02)
- {
- DEBUG(D_receive) debug_printf("Invalid Proxy Protocol version: %d\n", ver);
- goto proxyfail;
- }
-
- /* The v2 header will always be 16 bytes per the spec. */
- size = 16 + ntohs(hdr.v2.len);
- DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n",
- size, (int)sizeof(hdr));
-
- /* We should now have 16 octets (PROXY_V2_HEADER_SIZE), and we know the total
- amount that we need. Double-check that the size is not unreasonable, then
- get the rest. */
- if (size > sizeof(hdr))
- {
- DEBUG(D_receive) debug_printf("PROXYv2 header size unreasonably large; security attack?\n");
- goto proxyfail;
- }
-
- do
- {
- do
- {
- retmore = read(fd, (uschar*)&hdr + ret, size-ret);
- } while (retmore == -1 && errno == EINTR && !had_command_timeout);
- if (retmore == -1)
- goto proxyfail;
- DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
- ret += retmore;
- DEBUG(D_receive) debug_printf("PROXYv2: have %d/%d required octets\n", ret, size);
- } while (ret < size);
-
- } /* end scope for getting rest of data for v2 */
-
-/* At this point: if PROXYv2, we've read the exact size required for all data;
-if PROXYv1 then we've read "less than required for any valid line" and should
-read the rest". */
-
-if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
- {
- uint8_t cmd = (hdr.v2.ver_cmd & 0x0f);
-
- switch (cmd)
- {
- case 0x01: /* PROXY command */
- switch (hdr.v2.fam)
- {
- case 0x11: /* TCPv4 address type */
- iptype = US"IPv4";
- tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr;
- inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
- if (!string_is_ip_address(US tmpip, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
- goto proxyfail;
- }
- proxy_local_address = sender_host_address;
- sender_host_address = string_copy(US tmpip);
- tmpport = ntohs(hdr.v2.addr.ip4.src_port);
- proxy_local_port = sender_host_port;
- sender_host_port = tmpport;
- /* Save dest ip/port */
- tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr;
- inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
- if (!string_is_ip_address(US tmpip, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
- goto proxyfail;
- }
- proxy_external_address = string_copy(US tmpip);
- tmpport = ntohs(hdr.v2.addr.ip4.dst_port);
- proxy_external_port = tmpport;
- goto done;
- case 0x21: /* TCPv6 address type */
- iptype = US"IPv6";
- memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16);
- inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
- if (!string_is_ip_address(US tmpip6, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
- goto proxyfail;
- }
- proxy_local_address = sender_host_address;
- sender_host_address = string_copy(US tmpip6);
- tmpport = ntohs(hdr.v2.addr.ip6.src_port);
- proxy_local_port = sender_host_port;
- sender_host_port = tmpport;
- /* Save dest ip/port */
- memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16);
- inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
- if (!string_is_ip_address(US tmpip6, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
- goto proxyfail;
- }
- proxy_external_address = string_copy(US tmpip6);
- tmpport = ntohs(hdr.v2.addr.ip6.dst_port);
- proxy_external_port = tmpport;
- goto done;
- default:
- DEBUG(D_receive)
- debug_printf("Unsupported PROXYv2 connection type: 0x%02x\n",
- hdr.v2.fam);
- goto proxyfail;
- }
- /* Unsupported protocol, keep local connection address */
- break;
- case 0x00: /* LOCAL command */
- /* Keep local connection address for LOCAL */
- iptype = US"local";
- break;
- default:
- DEBUG(D_receive)
- debug_printf("Unsupported PROXYv2 command: 0x%x\n", cmd);
- goto proxyfail;
- }
- }
-else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0)
- {
- uschar *p;
- uschar *end;
- uschar *sp; /* Utility variables follow */
- int tmp_port;
- int r2;
- char *endc;
-
- /* get the rest of the line */
- r2 = swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret);
- if (r2 == -1)
- goto proxyfail;
- ret += r2;
-
- p = string_copy(hdr.v1.line);
- end = memchr(p, '\r', ret - 1);
-
- if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n')
- {
- DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n");
- goto proxyfail;
- }
- *end = '\0'; /* Terminate the string */
- size = end + 2 - p; /* Skip header + CRLF */
- DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n");
- DEBUG(D_receive) debug_printf("Bytes read not within PROXY header: %d\n", ret - size);
- /* Step through the string looking for the required fields. Ensure
- strict adherence to required formatting, exit for any error. */
- p += 5;
- if (!isspace(*(p++)))
- {
- DEBUG(D_receive) debug_printf("Missing space after PROXY command\n");
- goto proxyfail;
- }
- if (!Ustrncmp(p, CCS"TCP4", 4))
- iptype = US"IPv4";
- else if (!Ustrncmp(p,CCS"TCP6", 4))
- iptype = US"IPv6";
- else if (!Ustrncmp(p,CCS"UNKNOWN", 7))
- {
- iptype = US"Unknown";
- goto done;
- }
- else
- {
- DEBUG(D_receive) debug_printf("Invalid TCP type\n");
- goto proxyfail;
- }
-
- p += Ustrlen(iptype);
- if (!isspace(*(p++)))
- {
- DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n");
- goto proxyfail;
- }
- /* Find the end of the arg */
- if ((sp = Ustrchr(p, ' ')) == NULL)
- {
- DEBUG(D_receive)
- debug_printf("Did not find proxied src %s\n", iptype);
- goto proxyfail;
- }
- *sp = '\0';
- if(!string_is_ip_address(p, NULL))
- {
- DEBUG(D_receive)
- debug_printf("Proxied src arg is not an %s address\n", iptype);
- goto proxyfail;
- }
- proxy_local_address = sender_host_address;
- sender_host_address = p;
- p = sp + 1;
- if ((sp = Ustrchr(p, ' ')) == NULL)
- {
- DEBUG(D_receive)
- debug_printf("Did not find proxy dest %s\n", iptype);
- goto proxyfail;
- }
- *sp = '\0';
- if(!string_is_ip_address(p, NULL))
- {
- DEBUG(D_receive)
- debug_printf("Proxy dest arg is not an %s address\n", iptype);
- goto proxyfail;
- }
- proxy_external_address = p;
- p = sp + 1;
- if ((sp = Ustrchr(p, ' ')) == NULL)
- {
- DEBUG(D_receive) debug_printf("Did not find proxied src port\n");
- goto proxyfail;
- }
- *sp = '\0';
- tmp_port = strtol(CCS p, &endc, 10);
- if (*endc || tmp_port == 0)
- {
- DEBUG(D_receive)
- debug_printf("Proxied src port '%s' not an integer\n", p);
- goto proxyfail;
- }
- proxy_local_port = sender_host_port;
- sender_host_port = tmp_port;
- p = sp + 1;
- if ((sp = Ustrchr(p, '\0')) == NULL)
- {
- DEBUG(D_receive) debug_printf("Did not find proxy dest port\n");
- goto proxyfail;
- }
- tmp_port = strtol(CCS p, &endc, 10);
- if (*endc || tmp_port == 0)
- {
- DEBUG(D_receive)
- debug_printf("Proxy dest port '%s' not an integer\n", p);
- goto proxyfail;
- }
- proxy_external_port = tmp_port;
- /* Already checked for /r /n above. Good V1 header received. */
- }
-else
- {
- /* Wrong protocol */
- DEBUG(D_receive) debug_printf("Invalid proxy protocol version negotiation\n");
- (void) swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret);
- goto proxyfail;
- }
-
-done:
- DEBUG(D_receive)
- debug_printf("Valid %s sender from Proxy Protocol header\n", iptype);
- yield = proxy_session;
-
-/* Don't flush any potential buffer contents. Any input on proxyfail
-should cause a synchronization failure */
-
-proxyfail:
- DEBUG(D_receive) if (had_command_timeout)
- debug_printf("Timeout while reading proxy header\n");
-
-bad:
- if (yield)
- {
- sender_host_name = NULL;
- (void) host_name_lookup();
- host_build_sender_fullhost();
- }
- else
- {
- f.proxy_session_failed = TRUE;
- DEBUG(D_receive)
- debug_printf("Failure to extract proxied host, only QUIT allowed\n");
- }
-
-ALARM(0);
-return;
-}
-#endif /*SUPPORT_PROXY*/
-
/*************************************************
* Read one command line *
*************************************************/
+
/*************************************************
* Forced closedown of call *
*************************************************/
{
const uschar * hostname = sender_fullhost
? sender_fullhost : sender_host_address;
+gstring * g = string_catn(NULL, US"SMTP connection", 15);
+
+if (LOGGING(connection_id))
+ g = string_fmt_append(g, " Ci=%lu", connection_id);
+g = string_catn(g, US" from ", 6);
if (host_checking)
- return string_sprintf("SMTP connection from %s", hostname);
+ g = string_cat(g, hostname);
-if (f.sender_host_unknown || f.sender_host_notsocket)
- return string_sprintf("SMTP connection from %s", sender_ident);
+else if (f.sender_host_unknown || f.sender_host_notsocket)
+ g = string_cat(g, sender_ident);
-if (f.is_inetd)
- return string_sprintf("SMTP connection from %s (via inetd)", hostname);
+else if (f.is_inetd)
+ g = string_append(g, 2, hostname, US" (via inetd)");
-if (LOGGING(incoming_interface) && interface_address)
- return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname,
- interface_address, interface_port);
+else if (LOGGING(incoming_interface) && interface_address)
+ g = string_fmt_append(g, "%s I=[%s]:%d", hostname, interface_address, interface_port);
-return string_sprintf("SMTP connection from %s", hostname);
+else
+ g = string_cat(g, hostname);
+
+gstring_release_unused(g);
+return string_from_gstring(g);
}
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
if (smtp_batched_input) return TRUE;
+#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMETAL_XCLIENT)
+proxy_session = FALSE;
+#endif
+
+#ifdef SUPPORT_PROXY
/* If valid Proxy Protocol source is connecting, set up session.
Failure will not allow any SMTP function other than QUIT. */
-#ifdef SUPPORT_PROXY
-proxy_session = FALSE;
f.proxy_session_failed = FALSE;
-if (check_proxy_protocol_host())
- setup_proxy_protocol_host();
+if (proxy_protocol_host())
+ {
+ os_non_restarting_signal(SIGALRM, command_timeout_handler);
+ proxy_protocol_setup();
+ }
#endif
/* Run the connect ACL if it exists */
/* Now output the banner */
/*XXX the ehlo-resp code does its own tls/nontls bit. Maybe subroutine that? */
-smtp_printf("%s",
+smtp_printf("%Y",
#ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable && pipeline_connect_sends(),
#else
FALSE,
#endif
- string_from_gstring(ss));
+ ss);
/* Attempt to see if we sent the banner before the last ACK of the 3-way
handshake arrived. If so we must have managed a TFO. */
{
yield = 1;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\", %s)",
+ "syntax or protocol errors (last command was \"%s\", %Y)",
host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
- string_from_gstring(s_connhad_log(NULL))
+ s_connhad_log(NULL)
);
}
va_start(ap, defaultrespond);
g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap);
va_end(ap);
- smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
+ smtp_printf("%s %Y\r\n", FALSE, code, g);
}
mac_smtp_fflush();
}
if (++synprot_error_count > smtp_max_synprot_errors)
{
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\", %s)",
+ "syntax or protocol errors (last command was \"%s\", %Y)",
host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
- string_from_gstring(s_connhad_log(NULL))
+ s_connhad_log(NULL)
);
done = 1;
}
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)
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
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;
BOOL rc;
etrn_command = smtp_etrn_command;
deliver_domain = smtp_cmd_data;
- rc = transport_set_up_command(&argv, smtp_etrn_command, TRUE, 0, NULL,
- FALSE, US"ETRN processing", &error);
+ rc = transport_set_up_command(&argv, smtp_etrn_command, TSUC_EXPAND_ARGS, 0, NULL,
+ US"ETRN processing", &error);
deliver_domain = NULL;
if (!rc)
{
case BADSYN_CMD:
SYNC_FAILURE:
- if (smtp_inend >= smtp_inbuffer + IN_BUFFER_SIZE)
- smtp_inend = smtp_inbuffer + IN_BUFFER_SIZE - 1;
- c = smtp_inend - smtp_inptr;
- if (c > 150) c = 150; /* limit logged amount */
- smtp_inptr[c] = 0;
- incomplete_transaction_log(US"sync failure");
- log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
- "(next input sent too soon: pipelining was%s advertised): "
- "rejected \"%s\" %s next input=\"%s\"",
- f.smtp_in_pipelining_advertised ? "" : " not",
- smtp_cmd_buffer, host_and_ident(TRUE),
- string_printing(smtp_inptr));
- smtp_notquit_exit(US"synchronization-error", US"554",
- US"SMTP synchronization error");
- done = 1; /* Pretend eof - drops connection */
- break;
+ {
+ unsigned nchars = 150;
+ uschar * buf = receive_getbuf(&nchars); /* destructive read */
+ buf[nchars] = '\0';
+ incomplete_transaction_log(US"sync failure");
+ log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
+ "(next input sent too soon: pipelining was%s advertised): "
+ "rejected \"%s\" %s next input=\"%s\" (%u bytes)",
+ f.smtp_in_pipelining_advertised ? "" : " not",
+ smtp_cmd_buffer, host_and_ident(TRUE),
+ string_printing(buf), nchars);
+ smtp_notquit_exit(US"synchronization-error", US"554",
+ US"SMTP synchronization error");
+ done = 1; /* Pretend eof - drops connection */
+ break;
+ }
case TOO_MANY_NONMAIL_CMD:
s = smtp_cmd_buffer;
- while (*s != 0 && !isspace(*s)) s++;
+ while (*s && !isspace(*s)) s++;
incomplete_transaction_log(US"too many non-mail commands");
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
"nonmail commands (last was \"%.*s\")", host_and_ident(FALSE),