X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/63deec8a3ba77fcabf405d9c30fdd65a8b909526..24cda181fb88542cf38db2beae5d0ddb37f59c5c:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index a13af867a..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,19 +192,34 @@ 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 = 0, + CL_HELO, + CL_EHLO, + CL_AUTH, +#ifndef DISABLE_TLS + 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 */ - { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ - { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, - { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE }, - { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, + [CL_RSET] = { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ + [CL_HELO] = { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, + [CL_EHLO] = { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE }, + [CL_AUTH] = { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, #ifndef DISABLE_TLS - { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, - { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, + [CL_STLS] = { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, + [CL_TLAU] = { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, +#endif +#ifdef EXPERIMENTAL_XCLIENT + [CL_XCLI] = { "xclient", sizeof("xclient")-1, XCLIENT_CMD, TRUE, FALSE }, #endif - -/* If you change anything above here, also fix the definitions below. */ { "mail from:", sizeof("mail from:")-1, MAIL_CMD, TRUE, TRUE }, { "rcpt to:", sizeof("rcpt to:")-1, RCPT_CMD, TRUE, TRUE }, @@ -215,24 +233,30 @@ static smtp_cmd_list cmd_list[] = { { "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE } }; -static smtp_cmd_list *cmd_list_end = - cmd_list + sizeof(cmd_list)/sizeof(smtp_cmd_list); - -#define CMD_LIST_RSET 0 -#define CMD_LIST_HELO 1 -#define CMD_LIST_EHLO 2 -#define CMD_LIST_AUTH 3 -#define CMD_LIST_STARTTLS 4 -#define CMD_LIST_TLS_AUTH 5 - -/* This list of names is used for performing the smtp_no_mail logging action. -It must be kept in step with the SCH_xxx enumerations. */ +/* This list of names is used for performing the smtp_no_mail logging action. */ uschar * smtp_names[] = { - US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN", - US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", - US"STARTTLS", US"VRFY" }; + [SCH_NONE] = US"NONE", + [SCH_AUTH] = US"AUTH", + [SCH_DATA] = US"DATA", + [SCH_BDAT] = US"BDAT", + [SCH_EHLO] = US"EHLO", + [SCH_ETRN] = US"ETRN", + [SCH_EXPN] = US"EXPN", + [SCH_HELO] = US"HELO", + [SCH_HELP] = US"HELP", + [SCH_MAIL] = US"MAIL", + [SCH_NOOP] = US"NOOP", + [SCH_QUIT] = US"QUIT", + [SCH_RCPT] = US"RCPT", + [SCH_RSET] = US"RSET", + [SCH_STARTTLS] = US"STARTTLS", + [SCH_VRFY] = US"VRFY", +#ifdef EXPERIMENTAL_XCLIENT + [SCH_XCLIENT] = US"XCLIENT", +#endif + }; static uschar *protocols_local[] = { US"local-smtp", /* HELO */ @@ -1101,518 +1125,6 @@ had_command_sigterm = sig; -#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 * *************************************************/ @@ -1685,7 +1197,7 @@ if (hadnull) return BADCHAR_CMD; to the start of the actual data characters. Check for SMTP synchronization if required. */ -for (smtp_cmd_list * p = cmd_list; p < cmd_list_end; p++) +for (smtp_cmd_list * p = cmd_list; p < cmd_list + nelem(cmd_list); p++) { #ifdef SUPPORT_PROXY /* Only allow QUIT command if Proxy Protocol parsing failed */ @@ -1763,6 +1275,7 @@ return OTHER_CMD; + /************************************************* * Forced closedown of call * *************************************************/ @@ -2277,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 @@ -3021,14 +2533,20 @@ if (!f.sender_host_unknown) 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 */ @@ -3058,7 +2576,7 @@ if (tls_in.on_connect) { if (tls_server_start(&user_msg) != OK) return smtp_log_tls_fail(user_msg); - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + cmd_list[CL_TLAU].is_mail_cmd = TRUE; } #endif @@ -4029,7 +3547,7 @@ smtp_rset_handler(void) HAD(SCH_RSET); incomplete_transaction_log(US"RSET"); smtp_printf("250 Reset OK\r\n", FALSE); -cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE; +cmd_list[CL_RSET].is_mail_cmd = FALSE; if (chunking_state > CHUNKING_OFFERED) chunking_state = CHUNKING_OFFERED; } @@ -4091,11 +3609,11 @@ message_ended = END_NOTSTARTED; chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED; -cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE; -cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE; -cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; +cmd_list[CL_RSET].is_mail_cmd = TRUE; +cmd_list[CL_HELO].is_mail_cmd = TRUE; +cmd_list[CL_EHLO].is_mail_cmd = TRUE; #ifndef DISABLE_TLS -cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; +cmd_list[CL_STLS].is_mail_cmd = TRUE; #endif if (lwr_receive_getc != NULL) @@ -4151,10 +3669,10 @@ while (done <= 0) if ( tls_in.active.sock >= 0 && tls_in.peercert && tls_in.certificate_verified - && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd + && cmd_list[CL_TLAU].is_mail_cmd ) { - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE; + cmd_list[CL_TLAU].is_mail_cmd = FALSE; for (auth_instance * au = auths; au; au = au->next) if (strcmpic(US"tls", au->driver_name) == 0) @@ -4214,7 +3732,7 @@ while (done <= 0) case AUTH_CMD: HAD(SCH_AUTH); authentication_failed = TRUE; - cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE; + cmd_list[CL_AUTH].is_mail_cmd = FALSE; if (!fl.auth_advertised && !f.allow_auth_unadvertised) { @@ -4336,8 +3854,8 @@ while (done <= 0) fl.esmtp = TRUE; HELO_EHLO: /* Common code for HELO and EHLO */ - cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE; - cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE; + cmd_list[CL_HELO].is_mail_cmd = FALSE; + cmd_list[CL_EHLO].is_mail_cmd = FALSE; /* Reject the HELO if its argument was invalid or non-existent. A successful check causes the argument to be saved in malloc store. */ @@ -4675,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) @@ -4741,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 @@ -5685,7 +5244,7 @@ while (done <= 0) cancel_cutthrough_connection(TRUE, US"STARTTLS received"); reset_point = smtp_reset(reset_point); toomany = FALSE; - cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE; + cmd_list[CL_STLS].is_mail_cmd = FALSE; /* There's an attack where more data is read in past the STARTTLS command before TLS is negotiated, then assumed to be part of the secure session @@ -5726,9 +5285,9 @@ while (done <= 0) { if (!tls_remember_esmtp) fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE; - cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; - cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE; - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + cmd_list[CL_EHLO].is_mail_cmd = TRUE; + cmd_list[CL_AUTH].is_mail_cmd = TRUE; + cmd_list[CL_TLAU].is_mail_cmd = TRUE; if (sender_helo_name) { sender_helo_name = NULL; @@ -5838,23 +5397,23 @@ while (done <= 0) case HELP_CMD: HAD(SCH_HELP); - smtp_printf("214-Commands supported:\r\n", TRUE); - { - uschar buffer[256]; - buffer[0] = 0; - Ustrcat(buffer, US" AUTH"); - #ifndef DISABLE_TLS - if (tls_in.active.sock < 0 && - verify_check_host(&tls_advertise_hosts) != FAIL) - Ustrcat(buffer, US" STARTTLS"); - #endif - Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT"); - Ustrcat(buffer, US" NOOP QUIT RSET HELP"); - if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN"); - if (acl_smtp_expn) Ustrcat(buffer, US" EXPN"); - if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY"); - smtp_printf("214%s\r\n", FALSE, buffer); - } + smtp_printf("214-Commands supported:\r\n214", TRUE); + smtp_printf(" AUTH", TRUE); +#ifndef DISABLE_TLS + if (tls_in.active.sock < 0 && + verify_check_host(&tls_advertise_hosts) != FAIL) + smtp_printf(" STARTTLS", TRUE); +#endif + smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", TRUE); + smtp_printf(" NOOP QUIT RSET HELP", 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;