X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/df12cced099ee64551adf71b5fa38695db62c3f9..df0dc54a7666ef64b8a6681ab7b50a4836905203:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 7a4eaf996..7a45772ce 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* 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 */ /* Functions for handling an incoming SMTP call. */ @@ -188,16 +189,22 @@ count of non-mail commands and possibly provoke an error. tls_auth is a pseudo-command, never expected in input. It is activated on TLS startup and looks for a tls authenticator. */ +enum { CL_RSET, CL_HELO, CL_EHLO, CL_AUTH, +#ifndef DISABLE_TLS + CL_STLS, CL_TLAU, +#endif +}; + static smtp_cmd_list cmd_list[] = { /* 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 /* If you change anything above here, also fix the definitions below. */ @@ -214,24 +221,27 @@ 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", + }; static uschar *protocols_local[] = { US"local-smtp", /* HELO */ @@ -332,7 +342,7 @@ Returns: nothing */ static void -incomplete_transaction_log(uschar *what) +incomplete_transaction_log(uschar * what) { if (!sender_address /* No transaction in progress */ || !LOGGING(smtp_incomplete_transaction)) @@ -354,13 +364,21 @@ log_write(L_smtp_incomplete_transaction, LOG_MAIN|LOG_SENDER|LOG_RECIPIENTS, +static void +log_close_event(const uschar * reason) +{ +log_write(L_smtp_connection, LOG_MAIN, "%s D=%s closed %s", + smtp_get_connection_info(), string_timesince(&smtp_connection_start), reason); +} + void smtp_command_timeout_exit(void) { log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP command timeout on%s connection from %s", - tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE)); + LOG_MAIN, "SMTP command timeout on%s connection from %s D=%s", + tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE), + string_timesince(&smtp_connection_start)); if (smtp_batched_input) moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */ smtp_notquit_exit(US"command-timeout", US"421", @@ -372,7 +390,7 @@ exim_exit(EXIT_FAILURE); void smtp_command_sigterm_exit(void) { -log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info()); +log_close_event(US"after SIGTERM"); if (smtp_batched_input) moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */ smtp_notquit_exit(US"signal-exit", US"421", @@ -383,9 +401,10 @@ exim_exit(EXIT_FAILURE); void smtp_data_timeout_exit(void) { -log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP data timeout (message abandoned) on connection from %s F=<%s>", - sender_fullhost ? sender_fullhost : US"local process", sender_address); +log_write(L_lost_incoming_connection, LOG_MAIN, + "SMTP data timeout (message abandoned) on connection from %s F=<%s> D=%s", + sender_fullhost ? sender_fullhost : US"local process", sender_address, + string_timesince(&smtp_connection_start)); receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout"); /* Does not return */ } @@ -393,8 +412,7 @@ receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout"); void smtp_data_sigint_exit(void) { -log_write(0, LOG_MAIN, "%s closed after %s", - smtp_get_connection_info(), had_data_sigint == SIGTERM ? "SIGTERM":"SIGINT"); +log_close_event(had_data_sigint == SIGTERM ? US"SIGTERM":US"SIGINT"); receive_bomb_out(US"signal-exit", US"Service not available - SIGTERM or SIGINT received"); /* Does not return */ @@ -840,12 +858,8 @@ while (chunking_data_left) } bdat_pop_receive_functions(); - -if (chunking_state != CHUNKING_LAST) - { - chunking_state = CHUNKING_OFFERED; - DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); - } +chunking_state = CHUNKING_OFFERED; +DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); } @@ -978,7 +992,7 @@ which sometimes uses smtp_printf() and sometimes smtp_respond(). */ if (fl.rcpt_in_progress) { - if (rcpt_smtp_response == NULL) + if (!rcpt_smtp_response) rcpt_smtp_response = string_copy(big_buffer); else if (fl.rcpt_smtp_response_same && Ustrcmp(rcpt_smtp_response, big_buffer) != 0) @@ -1096,503 +1110,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; -} - -/************************************************* -* 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; - -/* 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; - - /* 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; - 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; - 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 - /************************************************* * Read one command line * *************************************************/ @@ -1665,7 +1182,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 */ @@ -1761,7 +1278,7 @@ Returns: nothing */ void -smtp_closedown(uschar *message) +smtp_closedown(uschar * message) { if (!smtp_in || smtp_batched_input) return; receive_swallow_smtp(); @@ -2161,8 +1678,12 @@ prdr_requested = FALSE; #ifdef SUPPORT_I18N message_smtputf8 = FALSE; #endif +#ifdef WITH_CONTENT_SCAN +regex_vars_clear(); +#endif body_linecount = body_zerocount = 0; +lookup_value = NULL; /* Can be set by ACL */ sender_rate = sender_rate_limit = sender_rate_period = NULL; ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */ /* Note that ratelimiters_conn persists across resets. */ @@ -2504,6 +2025,22 @@ else DEBUG(D_receive) #endif +static void +log_connect_tls_drop(const uschar * what, const uschar * log_msg) +{ +gstring * g = s_tlslog(NULL); +uschar * tls = string_from_gstring(g); + +log_write(L_connection_reject, + log_reject_target, "%s%s%s dropped by %s%s%s", + LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", + host_and_ident(TRUE), + tls ? tls : US"", + what, + log_msg ? US": " : US"", log_msg); +} + + /************************************************* * Start an SMTP session * *************************************************/ @@ -2663,32 +2200,32 @@ if (!f.sender_host_unknown) #if !HAVE_IPV6 && !defined(NO_IP_OPTIONS) - #ifdef GLIBC_IP_OPTIONS - #if (!defined __GLIBC__) || (__GLIBC__ < 2) - #define OPTSTYLE 1 - #else - #define OPTSTYLE 2 - #endif - #elif defined DARWIN_IP_OPTIONS - #define OPTSTYLE 2 - #else - #define OPTSTYLE 3 - #endif +# ifdef GLIBC_IP_OPTIONS +# if (!defined __GLIBC__) || (__GLIBC__ < 2) +# define OPTSTYLE 1 +# else +# define OPTSTYLE 2 +# endif +# elif defined DARWIN_IP_OPTIONS +# define OPTSTYLE 2 +# else +# define OPTSTYLE 3 +# endif if (!host_checking && !f.sender_host_notsocket) { - #if OPTSTYLE == 1 +# if OPTSTYLE == 1 EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN; struct ip_options *ipopt = store_get(optlen, GET_UNTAINTED); - #elif OPTSTYLE == 2 +# elif OPTSTYLE == 2 struct ip_opts ipoptblock; struct ip_opts *ipopt = &ipoptblock; EXIM_SOCKLEN_T optlen = sizeof(ipoptblock); - #else +# else struct ipoption ipoptblock; struct ipoption *ipopt = &ipoptblock; EXIM_SOCKLEN_T optlen = sizeof(ipoptblock); - #endif +# endif /* Occasional genuine failures of getsockopt() have been seen - for example, "reset by peer". Therefore, just log and give up on this @@ -2718,19 +2255,19 @@ if (!f.sender_host_unknown) else if (optlen > 0) { - uschar *p = big_buffer; - uschar *pend = big_buffer + big_buffer_size; - uschar *adptr; + uschar * p = big_buffer; + uschar * pend = big_buffer + big_buffer_size; + uschar * adptr; int optcount; struct in_addr addr; - #if OPTSTYLE == 1 - uschar *optstart = US (ipopt->__data); - #elif OPTSTYLE == 2 - uschar *optstart = US (ipopt->ip_opts); - #else - uschar *optstart = US (ipopt->ipopt_list); - #endif +# if OPTSTYLE == 1 + uschar * optstart = US (ipopt->__data); +# elif OPTSTYLE == 2 + uschar * optstart = US (ipopt->ip_opts); +# else + uschar * optstart = US (ipopt->ipopt_list); +# endif DEBUG(D_receive) debug_printf("IP options exist\n"); @@ -2741,59 +2278,65 @@ if (!f.sender_host_unknown) switch (*opt) { case IPOPT_EOL: - opt = NULL; - break; + opt = NULL; + break; case IPOPT_NOP: - opt++; - break; + opt++; + break; case IPOPT_SSRR: case IPOPT_LSRR: - if (!string_format(p, pend-p, " %s [@%s", - (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", - #if OPTSTYLE == 1 - inet_ntoa(*((struct in_addr *)(&(ipopt->faddr)))))) - #elif OPTSTYLE == 2 - inet_ntoa(ipopt->ip_dst))) - #else - inet_ntoa(ipopt->ipopt_dst))) - #endif - { - opt = NULL; - break; - } - - p += Ustrlen(p); - optcount = (opt[1] - 3) / sizeof(struct in_addr); - adptr = opt + 3; - while (optcount-- > 0) - { - memcpy(&addr, adptr, sizeof(addr)); - if (!string_format(p, pend - p - 1, "%s%s", - (optcount == 0)? ":" : "@", inet_ntoa(addr))) - { - opt = NULL; - break; - } - p += Ustrlen(p); - adptr += sizeof(struct in_addr); - } - *p++ = ']'; - opt += opt[1]; - break; + if (! +# if OPTSTYLE == 1 + string_format(p, pend-p, " %s [@%s", + (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", + inet_ntoa(*((struct in_addr *)(&(ipopt->faddr))))) +# elif OPTSTYLE == 2 + string_format(p, pend-p, " %s [@%s", + (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", + inet_ntoa(ipopt->ip_dst)) +# else + string_format(p, pend-p, " %s [@%s", + (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", + inet_ntoa(ipopt->ipopt_dst)) +# endif + ) + { + opt = NULL; + break; + } + + p += Ustrlen(p); + optcount = (opt[1] - 3) / sizeof(struct in_addr); + adptr = opt + 3; + while (optcount-- > 0) + { + memcpy(&addr, adptr, sizeof(addr)); + if (!string_format(p, pend - p - 1, "%s%s", + (optcount == 0)? ":" : "@", inet_ntoa(addr))) + { + opt = NULL; + break; + } + p += Ustrlen(p); + adptr += sizeof(struct in_addr); + } + *p++ = ']'; + opt += opt[1]; + break; default: - { - if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; } - Ustrcat(p, "[ "); - p += 2; - for (int i = 0; i < opt[1]; i++) - p += sprintf(CS p, "%2.2x ", opt[i]); - *p++ = ']'; - } - opt += opt[1]; - break; + { + if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; } + Ustrcat(p, "[ "); + p += 2; + for (int i = 0; i < opt[1]; i++) + p += sprintf(CS p, "%2.2x ", opt[i]); + *p++ = ']'; + } + opt += opt[1]; + break; } *p = 0; @@ -2856,7 +2399,10 @@ if (!f.sender_host_unknown) { log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection " "from %s (host_reject_connection)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n", FALSE); +#ifndef DISABLE_TLS + if (!tls_in.on_connect) +#endif + smtp_printf("554 SMTP service not available\r\n", FALSE); return FALSE; } @@ -2972,25 +2518,19 @@ if (!f.sender_host_unknown) if (smtp_batched_input) return TRUE; -/* If valid Proxy Protocol source is connecting, set up session. -Failure will not allow any SMTP function other than QUIT. */ - -#ifdef SUPPORT_PROXY +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMETAL_XCLIENT) proxy_session = FALSE; -f.proxy_session_failed = FALSE; -if (check_proxy_protocol_host()) - setup_proxy_protocol_host(); #endif -/* Start up TLS if tls_on_connect is set. This is for supporting the legacy -smtps port for use with older style SSL MTAs. */ +#ifdef SUPPORT_PROXY +/* If valid Proxy Protocol source is connecting, set up session. +Failure will not allow any SMTP function other than QUIT. */ -#ifndef DISABLE_TLS -if (tls_in.on_connect) +f.proxy_session_failed = FALSE; +if (proxy_protocol_host()) { - if (tls_server_start(&user_msg) != OK) - return smtp_log_tls_fail(user_msg); - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + os_non_restarting_signal(SIGALRM, command_timeout_handler); + proxy_protocol_setup(); } #endif @@ -3003,11 +2543,28 @@ if (acl_smtp_connect) if ((rc = acl_check(ACL_WHERE_CONNECT, NULL, acl_smtp_connect, &user_msg, &log_msg)) != OK) { - (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg); +#ifndef DISABLE_TLS + if (tls_in.on_connect) + log_connect_tls_drop(US"'connect' ACL", log_msg); + else +#endif + (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg); return FALSE; } } +/* Start up TLS if tls_on_connect is set. This is for supporting the legacy +smtps port for use with older style SSL MTAs. */ + +#ifndef DISABLE_TLS +if (tls_in.on_connect) + { + if (tls_server_start(&user_msg) != OK) + return smtp_log_tls_fail(user_msg); + cmd_list[CL_TLAU].is_mail_cmd = TRUE; + } +#endif + /* Output the initial message for a two-way SMTP connection. It may contain newlines, which then cause a multi-line response to be given. */ @@ -3015,13 +2572,7 @@ code = US"220"; /* Default status code */ esc = US""; /* Default extended status code */ esclen = 0; /* Length of esc */ -if (!user_msg) - { - if (!(s = expand_string(smtp_banner))) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) " - "failed: %s", smtp_banner, expand_string_message); - } -else +if (user_msg) { int codelen = 3; s = user_msg; @@ -3032,6 +2583,17 @@ else esclen = codelen - 4; } } +else if (!(s = expand_string(smtp_banner))) + { + log_write(0, f.expand_string_forcedfail ? LOG_MAIN : LOG_MAIN|LOG_PANIC_DIE, + "Expansion of \"%s\" (smtp_banner) failed: %s", + smtp_banner, expand_string_message); + /* for force-fail */ +#ifndef DISABLE_TLS + if (tls_in.on_connect) tls_close(NULL, TLS_SHUTDOWN_WAIT); +#endif + return FALSE; + } /* Remove any terminating newlines; might as well remove trailing space too */ @@ -3521,14 +3083,26 @@ if (log_reject_target != 0) if (!drop) return 0; -log_write(L_smtp_connection, LOG_MAIN, "%s closed by DROP in ACL", - smtp_get_connection_info()); +log_close_event(US"by DROP in ACL"); /* Run the not-quit ACL, but without any custom messages. This should not be a problem, because we get here only if some other ACL has issued "drop", and in that case, *its* custom messages will have been used above. */ smtp_notquit_exit(US"acl-drop", NULL, NULL); + +/* An overenthusiastic fail2ban/iptables implimentation has been seen to result +in the TCP conn staying open, and retrying, despite this process exiting. A +malicious client could possibly do the same, tying up server netowrking +resources. Close the socket explicitly to try to avoid that (there's a note in +the Linux socket(7) manpage, SO_LINGER para, to the effect that exim() without +close() results in the socket always lingering). */ + +(void) poll_one_fd(fileno(smtp_in), POLLIN, 200); +DEBUG(D_any) debug_printf_indent("SMTP(close)>>\n"); +(void) fclose(smtp_in); +(void) fclose(smtp_out); + return 2; } @@ -3769,7 +3343,7 @@ smtp_respond(code, len, TRUE, user_msg); static int -smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss) +smtp_in_auth(auth_instance *au, uschar ** smtp_resp, uschar ** errmsg) { const uschar *set_id = NULL; int rc; @@ -3833,7 +3407,7 @@ switch(rc) received_protocol = (sender_host_address ? protocols : protocols_local) [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)]; - *s = *ss = US"235 Authentication succeeded"; + *smtp_resp = *errmsg = US"235 Authentication succeeded"; authenticated_by = au; break; } @@ -3846,34 +3420,34 @@ switch(rc) case DEFER: if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); - *s = string_sprintf("435 Unable to authenticate at present%s", + *smtp_resp = string_sprintf("435 Unable to authenticate at present%s", auth_defer_user_msg); - *ss = string_sprintf("435 Unable to authenticate at present%s: %s", + *errmsg = string_sprintf("435 Unable to authenticate at present%s: %s", set_id, auth_defer_msg); break; case BAD64: - *s = *ss = US"501 Invalid base64 data"; + *smtp_resp = *errmsg = US"501 Invalid base64 data"; break; case CANCELLED: - *s = *ss = US"501 Authentication cancelled"; + *smtp_resp = *errmsg = US"501 Authentication cancelled"; break; case UNEXPECTED: - *s = *ss = US"553 Initial data not expected"; + *smtp_resp = *errmsg = US"553 Initial data not expected"; break; case FAIL: if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); - *s = US"535 Incorrect authentication data"; - *ss = string_sprintf("535 Incorrect authentication data%s", set_id); + *smtp_resp = US"535 Incorrect authentication data"; + *errmsg = string_sprintf("535 Incorrect authentication data%s", set_id); break; default: if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); - *s = US"435 Internal error"; - *ss = string_sprintf("435 Internal error%s: return %d from authentication " + *smtp_resp = US"435 Internal error"; + *errmsg = string_sprintf("435 Internal error%s: return %d from authentication " "check", set_id, rc); break; } @@ -3935,16 +3509,14 @@ else tls_close(NULL, TLS_SHUTDOWN_NOWAIT); # endif -log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); +log_close_event(US"by QUIT"); #else # ifndef DISABLE_TLS tls_close(NULL, TLS_SHUTDOWN_WAIT); # endif -log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); +log_close_event(US"by QUIT"); /* Pause, hoping client will FIN first so that they get the TIME_WAIT. The socket should become readble (though with no data) */ @@ -3960,7 +3532,9 @@ 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; } @@ -4020,11 +3594,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) @@ -4080,10 +3654,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) @@ -4100,7 +3674,18 @@ while (done <= 0) if (smtp_in_auth(au, &s, &ss) == OK) { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); } else - { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); } + { + DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); +#ifndef DISABLE_EVENT + { + uschar * save_name = sender_host_authenticated, * logmsg; + sender_host_authenticated = au->name; + if ((logmsg = event_raise(event_action, US"auth:fail", s, NULL))) + log_write(0, LOG_MAIN, "%s", logmsg); + sender_host_authenticated = save_name; + } +#endif + } } break; } @@ -4132,7 +3717,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) { @@ -4190,6 +3775,8 @@ while (done <= 0) { auth_instance * au; + uschar * smtp_resp, * errmsg; + for (au = auths; au; au = au->next) if (strcmpic(s, au->public_name) == 0 && au->server && (au->advertised || f.allow_auth_unadvertised)) @@ -4197,12 +3784,25 @@ while (done <= 0) if (au) { - c = smtp_in_auth(au, &s, &ss); + int rc = smtp_in_auth(au, &smtp_resp, &errmsg); - smtp_printf("%s\r\n", FALSE, s); - if (c != OK) - log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", - au->name, host_and_ident(FALSE), ss); + smtp_printf("%s\r\n", FALSE, smtp_resp); + if (rc != OK) + { + uschar * logmsg = NULL; +#ifndef DISABLE_EVENT + {uschar * save_name = sender_host_authenticated; + sender_host_authenticated = au->name; + logmsg = event_raise(event_action, US"auth:fail", smtp_resp, NULL); + sender_host_authenticated = save_name; + } +#endif + if (logmsg) + log_write(0, LOG_MAIN|LOG_REJECT, "%s", logmsg); + else + log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", + au->name, host_and_ident(FALSE), errmsg); + } } else done = synprot_error(L_smtp_protocol_error, 504, NULL, @@ -4239,8 +3839,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. */ @@ -4391,7 +3991,7 @@ while (done <= 0) else { - char *ss; + char * ss; int codelen = 4; smtp_message_code(&smtp_code, &codelen, &user_msg, NULL, TRUE); s = string_sprintf("%.*s%s", codelen, smtp_code, user_msg); @@ -4411,7 +4011,7 @@ while (done <= 0) if (fl.esmtp) { - g->s[3] = '-'; + g->s[3] = '-'; /* overwrite the space after the SMTP response code */ /* I'm not entirely happy with this, as an MTA is supposed to check that it has enough room to accept a message of maximum size before @@ -4542,9 +4142,9 @@ while (done <= 0) first = FALSE; fl.auth_advertised = TRUE; } - saveptr = g->ptr; + saveptr = gstring_length(g); g = string_catn(g, US" ", 1); - g = string_cat (g, au->public_name); + g = string_cat(g, au->public_name); while (++saveptr < g->ptr) g->s[saveptr] = toupper(g->s[saveptr]); au->advertised = TRUE; } @@ -4607,25 +4207,29 @@ while (done <= 0) /* Terminate the string (for debug), write it, and note that HELO/EHLO has been seen. */ + { + uschar * ehlo_resp; + int len = len_string_from_gstring(g, &ehlo_resp); #ifndef DISABLE_TLS - if (tls_in.active.sock >= 0) - (void)tls_write(NULL, g->s, g->ptr, + if (tls_in.active.sock >= 0) + (void) tls_write(NULL, ehlo_resp, len, # ifndef DISABLE_PIPE_CONNECT - fl.pipe_connect_acceptable && pipeline_connect_sends()); + fl.pipe_connect_acceptable && pipeline_connect_sends()); # else - FALSE); + FALSE); # endif - else + else #endif - (void) fwrite(g->s, 1, g->ptr, smtp_out); + (void) fwrite(ehlo_resp, 1, len, smtp_out); - DEBUG(D_receive) for (const uschar * t, * s = string_from_gstring(g); - s && (t = Ustrchr(s, '\r')); - s = t + 2) /* \r\n */ - debug_printf("%s %.*s\n", - s == g->s ? "SMTP>>" : " ", - (int)(t - s), s); - fl.helo_seen = TRUE; + DEBUG(D_receive) for (const uschar * t, * s = ehlo_resp; + s && (t = Ustrchr(s, '\r')); + s = t + 2) /* \r\n */ + debug_printf("%s %.*s\n", + s == g->s ? "SMTP>>" : " ", + (int)(t - s), s); + fl.helo_seen = TRUE; + } /* Reset the protocol and the state, abandoning any previous message. */ received_protocol = @@ -5081,7 +4685,7 @@ while (done <= 0) count this as a protocol error. Reset was_rej_mail so that further RCPTs get the same treatment. */ - if (sender_address == NULL) + if (!sender_address) { if (f.smtp_in_pipelining_advertised && last_was_rej_mail) { @@ -5100,7 +4704,7 @@ while (done <= 0) /* Check for an operand */ - if (smtp_cmd_data[0] == 0) + if (!smtp_cmd_data[0]) { done = synprot_error(L_smtp_syntax_error, 501, NULL, US"RCPT must have an address operand"); @@ -5391,7 +4995,7 @@ while (done <= 0) #endif if (!discarded && recipients_count <= 0) { - if (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL) + if (fl.rcpt_smtp_response_same && rcpt_smtp_response) { uschar *code = US"503"; int len = Ustrlen(rcpt_smtp_response); @@ -5584,7 +5188,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 @@ -5625,9 +5229,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; @@ -5671,8 +5275,7 @@ while (done <= 0) while (done <= 0) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { case EOF_CMD: - log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF", - smtp_get_connection_info()); + log_close_event(US"by EOF"); smtp_notquit_exit(US"tls-failed", NULL, NULL); done = 2; break; @@ -5694,8 +5297,7 @@ while (done <= 0) smtp_respond(US"221", 3, TRUE, user_msg); else smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); - log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); + log_close_event(US"by QUIT"); done = 2; break; @@ -5739,23 +5341,19 @@ 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); + smtp_printf("\r\n", FALSE); break; @@ -6034,7 +5632,6 @@ while (done <= 0) COMMAND_LOOP: last_was_rej_mail = was_rej_mail; /* Remember some last commands for */ last_was_rcpt = was_rcpt; /* protocol error handling */ - continue; } return done - 2; /* Convert yield values */