X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/df0dc54a7666ef64b8a6681ab7b50a4836905203..3f77bd134b64c532748b83c3931df07058268b5e:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 7a45772ce..70e4b95df 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2,7 +2,7 @@ * 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 */ @@ -75,6 +75,9 @@ enum { ETRN_CMD, /* This by analogy with TURN from the RFC */ STARTTLS_CMD, /* Required by the STARTTLS RFC */ TLS_AUTH_CMD, /* auto-command at start of SSL */ +#ifdef EXPERIMENTAL_XCLIENT + XCLIENT_CMD, /* per xlexkiro implementation */ +#endif /* This is a dummy to identify the non-sync commands when pipelining */ @@ -189,14 +192,22 @@ count of non-mail commands and possibly provoke an error. tls_auth is a pseudo-command, never expected in input. It is activated on TLS startup and looks for a tls authenticator. */ -enum { CL_RSET, CL_HELO, CL_EHLO, CL_AUTH, +enum { + CL_RSET = 0, + CL_HELO, + CL_EHLO, + CL_AUTH, #ifndef DISABLE_TLS - CL_STLS, CL_TLAU, + CL_STLS, + CL_TLAU, +#endif +#ifdef EXPERIMENTAL_XCLIENT + CL_XCLI, #endif }; static smtp_cmd_list cmd_list[] = { - /* name len cmd has_arg is_mail_cmd */ + /* name len cmd has_arg is_mail_cmd */ [CL_RSET] = { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ [CL_HELO] = { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, @@ -206,8 +217,9 @@ static smtp_cmd_list cmd_list[] = { [CL_STLS] = { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, [CL_TLAU] = { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, #endif - -/* If you change anything above here, also fix the definitions below. */ +#ifdef EXPERIMENTAL_XCLIENT + [CL_XCLI] = { "xclient", sizeof("xclient")-1, XCLIENT_CMD, TRUE, FALSE }, +#endif { "mail from:", sizeof("mail from:")-1, MAIL_CMD, TRUE, TRUE }, { "rcpt to:", sizeof("rcpt to:")-1, RCPT_CMD, TRUE, TRUE }, @@ -241,6 +253,9 @@ uschar * smtp_names[] = [SCH_RSET] = US"RSET", [SCH_STARTTLS] = US"STARTTLS", [SCH_VRFY] = US"VRFY", +#ifdef EXPERIMENTAL_XCLIENT + [SCH_XCLIENT] = US"XCLIENT", +#endif }; static uschar *protocols_local[] = { @@ -749,7 +764,7 @@ for(;;) return EOD; } - smtp_printf("250 %u byte chunk received\r\n", FALSE, chunking_datasize); + smtp_printf("250 %u byte chunk received\r\n", SP_NO_MORE, chunking_datasize); chunking_state = CHUNKING_OFFERED; DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); @@ -787,7 +802,7 @@ next_cmd: case NOOP_CMD: HAD(SCH_NOOP); - smtp_printf("250 OK\r\n", FALSE); + smtp_printf("250 OK\r\n", SP_NO_MORE); goto next_cmd; case BDAT_CMD: @@ -1212,7 +1227,7 @@ for (smtp_cmd_list * p = cmd_list; p < cmd_list + nelem(cmd_list); p++) follow the sender address. */ smtp_cmd_argument = smtp_cmd_buffer + p->len; - while (isspace(*smtp_cmd_argument)) smtp_cmd_argument++; + Uskip_whitespace(&smtp_cmd_argument); Ustrcpy(smtp_data_buffer, smtp_cmd_argument); smtp_cmd_data = smtp_data_buffer; @@ -1260,6 +1275,7 @@ return OTHER_CMD; + /************************************************* * Forced closedown of call * *************************************************/ @@ -1282,7 +1298,7 @@ smtp_closedown(uschar * message) { if (!smtp_in || smtp_batched_input) return; receive_swallow_smtp(); -smtp_printf("421 %s\r\n", FALSE, message); +smtp_printf("421 %s\r\n", SP_NO_MORE, message); for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { @@ -1291,16 +1307,16 @@ for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) case QUIT_CMD: f.smtp_in_quit = TRUE; - smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); + smtp_printf("221 %s closing connection\r\n", SP_NO_MORE, smtp_active_hostname); mac_smtp_fflush(); return; case RSET_CMD: - smtp_printf("250 Reset OK\r\n", FALSE); + smtp_printf("250 Reset OK\r\n", SP_NO_MORE); break; default: - smtp_printf("421 %s\r\n", FALSE, message); + smtp_printf("421 %s\r\n", SP_NO_MORE, message); break; } } @@ -1326,21 +1342,29 @@ smtp_get_connection_info(void) { 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); + +else if (f.sender_host_unknown || f.sender_host_notsocket) + g = string_cat(g, sender_ident ? sender_ident : US"NULL"); -if (f.sender_host_unknown || f.sender_host_notsocket) - return string_sprintf("SMTP connection from %s", sender_ident); +else if (f.is_inetd) + g = string_append(g, 2, hostname, US" (via inetd)"); -if (f.is_inetd) - return string_sprintf("SMTP connection from %s (via inetd)", hostname); +else if (LOGGING(incoming_interface) && interface_address) + g = string_fmt_append(g, "%s I=[%s]:%d", hostname, interface_address, interface_port); -if (LOGGING(incoming_interface) && interface_address) - return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname, - interface_address, interface_port); +else + g = string_cat(g, hostname); -return string_sprintf("SMTP connection from %s", hostname); +gstring_release_unused(g); +return string_from_gstring(g); } @@ -1774,7 +1798,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 @@ -1853,10 +1876,11 @@ while (done <= 0) /* Check maximum number allowed */ - if (recipients_max > 0 && recipients_count + 1 > recipients_max) + if ( recipients_max_expanded > 0 + && recipients_count + 1 > recipients_max_expanded) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients", - recipients_max_reject? "552": "452"); + recipients_max_reject ? "552": "452"); /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a recipient address */ @@ -1922,6 +1946,9 @@ while (done <= 0) case HELP_CMD: case NOOP_CMD: case ETRN_CMD: +#ifdef EXPERIMENTAL_WELLKNOWN + case WELLKNOWN_CMD: +#endif bsmtp_transaction_linecount = receive_linecount; break; @@ -2028,16 +2055,19 @@ else DEBUG(D_receive) 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); +if (log_reject_target) + { + 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); + } } @@ -2145,6 +2175,7 @@ lwr_receive_ungetc = NULL; /* Set up the message size limit; this may be host-specific */ +GET_OPTION("message_size_limit"); thismessage_size_limit = expand_string_integer(message_size_limit, TRUE); if (expand_string_message) { @@ -2243,7 +2274,7 @@ if (!f.sender_host_unknown) { log_write(0, LOG_MAIN, "getsockopt() failed from %s: %s", host_and_ident(FALSE), strerror(errno)); - smtp_printf("451 SMTP service not available\r\n", FALSE); + smtp_printf("451 SMTP service not available\r\n", SP_NO_MORE); return FALSE; } } @@ -2347,7 +2378,7 @@ if (!f.sender_host_unknown) log_write(0, LOG_MAIN|LOG_REJECT, "connection from %s refused (IP options)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n", FALSE); + smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE); return FALSE; } @@ -2402,7 +2433,7 @@ if (!f.sender_host_unknown) #ifndef DISABLE_TLS if (!tls_in.on_connect) #endif - smtp_printf("554 SMTP service not available\r\n", FALSE); + smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE); return FALSE; } @@ -2433,7 +2464,7 @@ if (!f.sender_host_unknown) log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection from %s " "(tcp wrappers)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n", FALSE); + smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE); } else { @@ -2443,7 +2474,7 @@ if (!f.sender_host_unknown) log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "temporarily refused connection from %s " "(tcp wrappers errno=%d)", host_and_ident(FALSE), save_errno); - smtp_printf("451 Temporary local problem - please try later\r\n", FALSE); + smtp_printf("451 Temporary local problem - please try later\r\n", SP_NO_MORE); } return FALSE; } @@ -2463,7 +2494,7 @@ if (!f.sender_host_unknown) host_and_ident(FALSE), smtp_accept_count - 1, smtp_accept_max, smtp_accept_reserve, (rc == DEFER)? " (lookup deferred)" : ""); smtp_printf("421 %s: Too many concurrent SMTP connections; " - "please try again later\r\n", FALSE, smtp_active_hostname); + "please try again later\r\n", SP_NO_MORE, smtp_active_hostname); return FALSE; } reserved_host = TRUE; @@ -2484,7 +2515,7 @@ if (!f.sender_host_unknown) LOG_MAIN, "temporarily refused connection from %s: not in " "reserve list and load average = %.2f", host_and_ident(FALSE), (double)load_average/1000.0); - smtp_printf("421 %s: Too much load; please try again later\r\n", FALSE, + smtp_printf("421 %s: Too much load; please try again later\r\n", SP_NO_MORE, smtp_active_hostname); return FALSE; } @@ -2514,6 +2545,11 @@ if (!f.sender_host_unknown) fl.helo_accept_junk = verify_check_host(&helo_accept_junk_hosts) == OK; } +/* Expand recipients_max, if needed */ + { + uschar * rme = expand_string(recipients_max); + recipients_max_expanded = atoi(CCS rme); + } /* For batch SMTP input we are now done. */ if (smtp_batched_input) return TRUE; @@ -2537,6 +2573,7 @@ if (proxy_protocol_host()) /* Run the connect ACL if it exists */ user_msg = NULL; +GET_OPTION("acl_smtp_connect"); if (acl_smtp_connect) { int rc; @@ -2583,16 +2620,20 @@ if (user_msg) esclen = codelen - 4; } } -else if (!(s = expand_string(smtp_banner))) +else { - 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; + GET_OPTION("smtp_banner"); + 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 */ @@ -2658,20 +2699,20 @@ if (!check_sync()) "synchronization error (input sent without waiting for greeting): " "rejected connection from %s input=\"%s\"", host_and_ident(TRUE), string_printing(string_copyn(smtp_inptr, n))); - smtp_printf("554 SMTP synchronization error\r\n", FALSE); + smtp_printf("554 SMTP synchronization error\r\n", SP_NO_MORE); return FALSE; } /* 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, + SP_NO_MORE, #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. */ @@ -2720,18 +2761,18 @@ if (++synprot_error_count > smtp_max_synprot_errors) { 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) ); } if (code > 0) { - smtp_printf("%d%c%s%s%s\r\n", FALSE, code, yield == 1 ? '-' : ' ', + smtp_printf("%d%c%s%s%s\r\n", SP_NO_MORE, code, yield == 1 ? '-' : ' ', data ? data : US"", data ? US": " : US"", errmess); if (yield == 1) - smtp_printf("%d Too many syntax or protocol errors\r\n", FALSE, code); + smtp_printf("%d Too many syntax or protocol errors\r\n", SP_NO_MORE, code); } return yield; @@ -2758,7 +2799,7 @@ Returns: nothing */ void -smtp_respond(uschar* code, int codelen, BOOL final, uschar *msg) +smtp_respond(uschar * code, int codelen, BOOL final, uschar * msg) { int esclen = 0; uschar *esc = US""; @@ -2807,7 +2848,7 @@ for (;;) } else { - smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg); + smtp_printf("%.3s-%.*s%.*s\r\n", SP_MORE, code, esclen, esc, (int)(nl - msg), msg); msg = nl + 1; Uskip_whitespace(&msg); } @@ -2913,7 +2954,7 @@ Returns: 0 in most cases */ int -smtp_handle_acl_fail(int where, int rc, uschar *user_msg, uschar *log_msg) +smtp_handle_acl_fail(int where, int rc, uschar * user_msg, uschar * log_msg) { BOOL drop = rc == FAIL_DROP; int codelen = 3; @@ -2953,8 +2994,8 @@ switch (where) if (where == ACL_WHERE_AUTH) /* avoid logging auth creds */ { - uschar * s; - for (s = smtp_cmd_data; *s && !isspace(*s); ) s++; + uschar * s = smtp_cmd_data; + Uskip_nonwhite(&s); lim = s - smtp_cmd_data; /* atop after method */ } what = string_sprintf("%s %.*s", acl_wherenames[where], lim, place); @@ -2999,7 +3040,7 @@ if (sender_verified_failed && string_sprintf(": %s", sender_verified_failed->message)); if (rc == FAIL && sender_verified_failed->user_message) - smtp_respond(smtp_code, codelen, FALSE, string_sprintf( + smtp_respond(smtp_code, codelen, SR_NOT_FINAL, string_sprintf( testflag(sender_verified_failed, af_verify_pmfail)? "Postmaster verification failed while checking <%s>\n%s\n" "Several RFCs state that you are required to have a postmaster\n" @@ -3031,7 +3072,7 @@ always a 5xx one - see comments at the start of this function. If the original rc was FAIL_DROP we drop the connection and yield 2. */ if (rc == FAIL) - smtp_respond(smtp_code, codelen, TRUE, + smtp_respond(smtp_code, codelen, SR_FINAL, user_msg ? user_msg : US"Administrative prohibition"); /* Send temporary failure response to the command. Don't give any details, @@ -3049,12 +3090,12 @@ else && sender_verified_failed && sender_verified_failed->message ) - smtp_respond(smtp_code, codelen, FALSE, sender_verified_failed->message); + smtp_respond(smtp_code, codelen, SR_NOT_FINAL, sender_verified_failed->message); - smtp_respond(smtp_code, codelen, TRUE, user_msg); + smtp_respond(smtp_code, codelen, SR_FINAL, user_msg); } else - smtp_respond(smtp_code, codelen, TRUE, + smtp_respond(smtp_code, codelen, SR_FINAL, US"Temporary local problem - please try later"); /* Log the incident to the logs that are specified by log_reject_target @@ -3062,7 +3103,7 @@ else the connection is not forcibly to be dropped, return 0. Otherwise, log why it is closing if required and return 2. */ -if (log_reject_target != 0) +if (log_reject_target) { #ifndef DISABLE_TLS gstring * g = s_tlslog(NULL); @@ -3153,6 +3194,7 @@ fl.smtp_exit_function_called = TRUE; /* Call the not-QUIT ACL, if there is one, unless no reason is given. */ +GET_OPTION("acl_smtp_notquit"); if (acl_smtp_notquit && reason) { smtp_notquit_reason = reason; @@ -3171,7 +3213,7 @@ responses are all internal, they should be reasonable size. */ if (code && defaultrespond) { if (user_msg) - smtp_respond(code, 3, TRUE, user_msg); + smtp_respond(code, 3, SR_FINAL, user_msg); else { gstring * g; @@ -3180,7 +3222,7 @@ if (code && defaultrespond) 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", SP_NO_MORE, code, g); } mac_smtp_fflush(); } @@ -3337,7 +3379,7 @@ smtp_user_msg(uschar *code, uschar *user_msg) { int len = 3; smtp_message_code(&code, &len, &user_msg, NULL, TRUE); -smtp_respond(code, len, TRUE, user_msg); +smtp_respond(code, len, SR_FINAL, user_msg); } @@ -3472,7 +3514,7 @@ if (f.allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0) *recipient = US rewrite_address_qualify(*recipient, TRUE); return rd; } -smtp_printf("501 %s: recipient address must contain a domain\r\n", FALSE, +smtp_printf("501 %s: recipient address must contain a domain\r\n", SP_NO_MORE, smtp_cmd_data); log_write(L_smtp_syntax_error, LOG_MAIN|LOG_REJECT, "unqualified %s rejected: <%s> %s%s", @@ -3489,6 +3531,7 @@ smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp) HAD(SCH_QUIT); f.smtp_in_quit = TRUE; incomplete_transaction_log(US"QUIT"); +GET_OPTION("acl_smtp_quit"); if ( acl_smtp_quit && acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp) == ERROR) @@ -3500,9 +3543,9 @@ if ( acl_smtp_quit #endif if (*user_msgp) - smtp_respond(US"221", 3, TRUE, *user_msgp); + smtp_respond(US"221", 3, SR_FINAL, *user_msgp); else - smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); + smtp_printf("221 %s closing connection\r\n", SP_NO_MORE, smtp_active_hostname); #ifdef SERVERSIDE_CLOSE_NOWAIT # ifndef DISABLE_TLS @@ -3531,7 +3574,7 @@ smtp_rset_handler(void) { HAD(SCH_RSET); incomplete_transaction_log(US"RSET"); -smtp_printf("250 Reset OK\r\n", FALSE); +smtp_printf("250 Reset OK\r\n", SP_NO_MORE); cmd_list[CL_RSET].is_mail_cmd = FALSE; if (chunking_state > CHUNKING_OFFERED) chunking_state = CHUNKING_OFFERED; @@ -3662,6 +3705,7 @@ while (done <= 0) for (auth_instance * au = auths; au; au = au->next) if (strcmpic(US"tls", au->driver_name) == 0) { + GET_OPTION("acl_smtp_auth"); if ( acl_smtp_auth && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg)) != OK @@ -3740,6 +3784,7 @@ while (done <= 0) /* Check the ACL */ + GET_OPTION("acl_smtp_auth"); if ( acl_smtp_auth && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg)) != OK @@ -3765,8 +3810,8 @@ while (done <= 0) if (*smtp_cmd_data) { - *smtp_cmd_data++ = 0; - while (isspace(*smtp_cmd_data)) smtp_cmd_data++; + *smtp_cmd_data++ = '\0'; + Uskip_whitespace(&smtp_cmd_data); } /* Search for an authentication mechanism which is configured for use @@ -3786,7 +3831,7 @@ while (done <= 0) { int rc = smtp_in_auth(au, &smtp_resp, &errmsg); - smtp_printf("%s\r\n", FALSE, smtp_resp); + smtp_printf("%s\r\n", SP_NO_MORE, smtp_resp); if (rc != OK) { uschar * logmsg = NULL; @@ -3847,7 +3892,7 @@ while (done <= 0) if (!check_helo(smtp_cmd_data)) { - smtp_printf("501 Syntactically invalid %s argument(s)\r\n", FALSE, hello); + smtp_printf("501 Syntactically invalid %s argument(s)\r\n", SP_NO_MORE, hello); log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically " "invalid argument(s): %s", hello, host_and_ident(FALSE), @@ -3857,9 +3902,9 @@ while (done <= 0) 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; } @@ -3877,10 +3922,10 @@ while (done <= 0) if (!f.sender_host_unknown) { BOOL old_helo_verified = f.helo_verified; - uschar *p = smtp_cmd_data; + uschar * p = smtp_cmd_data; - while (*p != 0 && !isspace(*p)) { *p = tolower(*p); p++; } - *p = 0; + while (*p && !isspace(*p)) { *p = tolower(*p); p++; } + *p = '\0'; /* Force a reverse lookup if HELO quoted something in helo_lookup_domains because otherwise the log can be confusing. */ @@ -3912,7 +3957,7 @@ while (done <= 0) { if (fl.helo_verify_required) { - smtp_printf("%d %s argument does not match calling host\r\n", FALSE, + smtp_printf("%d %s argument does not match calling host\r\n", SP_NO_MORE, tempfail? 451 : 550, hello); log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s", tempfail? "temporarily " : "", @@ -3934,6 +3979,7 @@ while (done <= 0) /* Apply an ACL check if one is defined; afterwards, recheck synchronization in case the client started sending in a delay. */ + GET_OPTION("acl_smtp_helo"); if (acl_smtp_helo) if ((rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, &user_msg, &log_msg)) != OK) @@ -4028,15 +4074,15 @@ while (done <= 0) g = string_catn(g, US"-SIZE\r\n", 7); } -#ifdef EXPERIMENTAL_ESMTP_LIMITS - if ( (smtp_mailcmd_max > 0 || recipients_max) +#ifndef DISABLE_ESMTP_LIMITS + if ( (smtp_mailcmd_max > 0 || recipients_max_expanded > 0) && verify_check_host(&limits_advertise_hosts) == OK) { g = string_fmt_append(g, "%.3s-LIMITS", smtp_code); if (smtp_mailcmd_max > 0) g = string_fmt_append(g, " MAILMAX=%d", smtp_mailcmd_max); - if (recipients_max) - g = string_fmt_append(g, " RCPTMAX=%d", recipients_max); + if (recipients_max_expanded > 0) + g = string_fmt_append(g, " RCPTMAX=%d", recipients_max_expanded); g = string_catn(g, US"\r\n", 2); } #endif @@ -4065,16 +4111,19 @@ while (done <= 0) /* Advertise ETRN/VRFY/EXPN if there's are ACL checking whether a host is permitted to issue them; a check is made when any host actually tries. */ + GET_OPTION("acl_smtp_etrn"); if (acl_smtp_etrn) { g = string_catn(g, smtp_code, 3); g = string_catn(g, US"-ETRN\r\n", 7); } + GET_OPTION("acl_smtp_vrfy"); if (acl_smtp_vrfy) { g = string_catn(g, smtp_code, 3); g = string_catn(g, US"-VRFY\r\n", 7); } + GET_OPTION("acl_smtp_expn"); if (acl_smtp_expn) { g = string_catn(g, smtp_code, 3); @@ -4178,7 +4227,13 @@ while (done <= 0) fl.tls_advertised = TRUE; } #endif - +#ifdef EXPERIMENTAL_XCLIENT + if (proxy_session || verify_check_host(&hosts_xclient) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = xclient_smtp_advertise_str(g); + } +#endif #ifndef DISABLE_PRDR /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ if (prdr_enable) @@ -4244,6 +4299,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", SP_NO_MORE, 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", SP_NO_MORE, 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 @@ -4262,7 +4352,7 @@ while (done <= 0) if ( fl.helo_verify_required || verify_check_host(&hosts_require_helo) == OK) { - smtp_printf("503 HELO or EHLO required\r\n", FALSE); + smtp_printf("503 HELO or EHLO required\r\n", SP_NO_MORE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " "HELO/EHLO given", host_and_ident(FALSE)); break; @@ -4289,7 +4379,7 @@ while (done <= 0) if (smtp_mailcmd_max > 0 && smtp_mailcmd_count > smtp_mailcmd_max) { - smtp_printf("421 too many messages in this connection\r\n", FALSE); + smtp_printf("421 too many messages in this connection\r\n", SP_NO_MORE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many " "messages in one connection", host_and_ident(TRUE)); break; @@ -4429,6 +4519,7 @@ while (done <= 0) US"invalid data for AUTH"); goto COMMAND_LOOP; } + GET_OPTION("acl_smtp_mailauth"); if (!acl_smtp_mailauth) { ignore_msg = US"client not authenticated"; @@ -4562,7 +4653,7 @@ while (done <= 0) if (thismessage_size_limit > 0 && message_size > thismessage_size_limit) { - smtp_printf("552 Message size exceeds maximum permitted\r\n", FALSE); + smtp_printf("552 Message size exceeds maximum permitted\r\n", SP_NO_MORE); log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected MAIL FROM:<%s> %s: " "message too big: size%s=%d max=%d", @@ -4587,7 +4678,7 @@ while (done <= 0) smtp_check_spool_space && message_size >= 0 ? message_size + 5000 : 0)) { - smtp_printf("452 Space shortage, please try later\r\n", FALSE); + smtp_printf("452 Space shortage, please try later\r\n", SP_NO_MORE); sender_address = NULL; break; } @@ -4609,7 +4700,7 @@ while (done <= 0) } else { - smtp_printf("501 %s: sender address must contain a domain\r\n", FALSE, + smtp_printf("501 %s: sender address must contain a domain\r\n", SP_NO_MORE, smtp_cmd_data); log_write(L_smtp_syntax_error, LOG_MAIN|LOG_REJECT, @@ -4625,6 +4716,7 @@ while (done <= 0) when pipelining is not advertised, do another sync check in case the ACL delayed and the client started sending in the meantime. */ + GET_OPTION("acl_smtp_mail"); if (acl_smtp_mail) { rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg); @@ -4689,7 +4781,7 @@ while (done <= 0) { if (f.smtp_in_pipelining_advertised && last_was_rej_mail) { - smtp_printf("503 sender not yet given\r\n", FALSE); + smtp_printf("503 sender not yet given\r\n", SP_NO_MORE); was_rej_mail = TRUE; } else @@ -4833,12 +4925,13 @@ while (done <= 0) /* Check maximum allowed */ - if (rcpt_count+1 < 0 || rcpt_count > recipients_max && recipients_max > 0) + if ( rcpt_count+1 < 0 + || rcpt_count > recipients_max_expanded && recipients_max_expanded > 0) { if (recipients_max_reject) { rcpt_fail_count++; - smtp_printf("552 too many recipients\r\n", FALSE); + smtp_printf("552 too many recipients\r\n", SP_NO_MORE); if (!toomany) log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: message " "rejected: sender=<%s> %s", sender_address, host_and_ident(TRUE)); @@ -4846,7 +4939,7 @@ while (done <= 0) else { rcpt_defer_count++; - smtp_printf("452 too many recipients\r\n", FALSE); + smtp_printf("452 too many recipients\r\n", SP_NO_MORE); if (!toomany) log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: excess " "temporarily rejected: sender=<%s> %s", sender_address, @@ -4879,10 +4972,13 @@ while (done <= 0) if (f.recipients_discarded) rc = DISCARD; else + { + GET_OPTION("acl_smtp_rcpt"); if ( (rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg, &log_msg)) == OK && !f.smtp_in_pipelining_advertised && !check_sync()) goto SYNC_FAILURE; + } /* The ACL was happy */ @@ -4912,7 +5008,7 @@ while (done <= 0) if (user_msg) smtp_user_msg(US"250", user_msg); else - smtp_printf("250 Accepted\r\n", FALSE); + smtp_printf("250 Accepted\r\n", SP_NO_MORE); rcpt_fail_count++; discarded = TRUE; log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: " @@ -4999,15 +5095,15 @@ while (done <= 0) { uschar *code = US"503"; int len = Ustrlen(rcpt_smtp_response); - smtp_respond(code, 3, FALSE, US"All RCPT commands were rejected with " + smtp_respond(code, 3, SR_NOT_FINAL, US"All RCPT commands were rejected with " "this error:"); /* Responses from smtp_printf() will have \r\n on the end */ if (len > 2 && rcpt_smtp_response[len-2] == '\r') rcpt_smtp_response[len-2] = 0; - smtp_respond(code, 3, FALSE, rcpt_smtp_response); + smtp_respond(code, 3, SR_NOT_FINAL, rcpt_smtp_response); } if (f.smtp_in_pipelining_advertised && last_was_rcpt) - smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE, + smtp_printf("503 Valid RCPT command must precede %s\r\n", SP_NO_MORE, smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]); else done = synprot_error(L_smtp_protocol_error, 503, NULL, @@ -5027,7 +5123,7 @@ while (done <= 0) { sender_address = NULL; /* This will allow a new MAIL without RSET */ sender_address_unrewritten = NULL; - smtp_printf("554 Too many recipients\r\n", FALSE); + smtp_printf("554 Too many recipients\r\n", SP_NO_MORE); if (chunking_state > CHUNKING_OFFERED) { @@ -5038,13 +5134,14 @@ while (done <= 0) } if (chunking_state > CHUNKING_OFFERED) - rc = OK; /* No predata ACL or go-ahead output for BDAT */ + rc = OK; /* There is no predata ACL or go-ahead output for BDAT */ else { - /* If there is an ACL, re-check the synchronization afterwards, since the - ACL may have delayed. To handle cutthrough delivery enforce a dummy call - to get the DATA command sent. */ + /* If there is a predata-ACL, re-check the synchronization afterwards, + since the ACL may have delayed. To handle cutthrough delivery enforce a + dummy call to get the DATA command sent. */ + GET_OPTION("acl_smtp_predata"); if (!acl_smtp_predata && cutthrough.cctx.sock < 0) rc = OK; else @@ -5068,7 +5165,7 @@ while (done <= 0) smtp_user_msg(US"354", user_msg); else smtp_printf( - "354 Enter message, ending with \".\" on a line by itself\r\n", FALSE); + "354 Enter message, ending with \".\" on a line by itself\r\n", SP_NO_MORE); } if (f.bdat_readers_wanted) @@ -5094,7 +5191,7 @@ while (done <= 0) if (!(address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end, &recipient_domain, FALSE))) { - smtp_printf("501 %s\r\n", FALSE, errmess); + smtp_printf("501 %s\r\n", SP_NO_MORE, errmess); break; } @@ -5103,6 +5200,7 @@ while (done <= 0) US"verify"))) break; + GET_OPTION("acl_smtp_vrfy"); if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy, &user_msg, &log_msg)) != OK) done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg); @@ -5133,7 +5231,7 @@ while (done <= 0) break; } - smtp_printf("%s\r\n", FALSE, s); + smtp_printf("%s\r\n", SP_NO_MORE, s); } break; } @@ -5141,6 +5239,7 @@ while (done <= 0) case EXPN_CMD: HAD(SCH_EXPN); + GET_OPTION("acl_smtp_expn"); rc = acl_check(ACL_WHERE_EXPN, NULL, acl_smtp_expn, &user_msg, &log_msg); if (rc != OK) done = smtp_handle_acl_fail(ACL_WHERE_EXPN, rc, user_msg, log_msg); @@ -5170,6 +5269,7 @@ while (done <= 0) /* Apply an ACL check if one is defined */ + GET_OPTION("acl_smtp_starttls"); if ( acl_smtp_starttls && (rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls, &user_msg, &log_msg)) != OK @@ -5261,7 +5361,7 @@ while (done <= 0) if (rc == DEFER) { - smtp_printf("454 TLS currently unavailable\r\n", FALSE); + smtp_printf("454 TLS currently unavailable\r\n", SP_NO_MORE); break; } @@ -5288,21 +5388,22 @@ while (done <= 0) case QUIT_CMD: f.smtp_in_quit = TRUE; user_msg = NULL; + GET_OPTION("acl_smtp_quit"); if ( acl_smtp_quit && ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, &log_msg)) == ERROR)) log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", log_msg); if (user_msg) - smtp_respond(US"221", 3, TRUE, user_msg); + smtp_respond(US"221", 3, SR_FINAL, user_msg); else - smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); + smtp_printf("221 %s closing connection\r\n", SP_NO_MORE, smtp_active_hostname); log_close_event(US"by QUIT"); done = 2; break; default: - smtp_printf("554 Security failure\r\n", FALSE); + smtp_printf("554 Security failure\r\n", SP_NO_MORE); break; } tls_close(NULL, TLS_SHUTDOWN_NOWAIT); @@ -5330,7 +5431,7 @@ while (done <= 0) case NOOP_CMD: HAD(SCH_NOOP); - smtp_printf("250 OK\r\n", FALSE); + smtp_printf("250 OK\r\n", SP_NO_MORE); break; @@ -5341,19 +5442,23 @@ while (done <= 0) case HELP_CMD: HAD(SCH_HELP); - smtp_printf("214-Commands supported:\r\n214", TRUE); - smtp_printf(" AUTH", TRUE); + smtp_printf("214-Commands supported:\r\n214", SP_MORE); + smtp_printf(" AUTH", SP_MORE); #ifndef DISABLE_TLS if (tls_in.active.sock < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) - smtp_printf(" STARTTLS", TRUE); + smtp_printf(" STARTTLS", SP_MORE); #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); + smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", SP_MORE); + smtp_printf(" NOOP QUIT RSET HELP", SP_MORE); + if (acl_smtp_etrn) smtp_printf(" ETRN", SP_MORE); + if (acl_smtp_expn) smtp_printf(" EXPN", SP_MORE); + if (acl_smtp_vrfy) smtp_printf(" VRFY", SP_MORE); +#ifdef EXPERIMENTAL_XCLIENT + if (proxy_session || verify_check_host(&hosts_xclient) != FAIL) + smtp_printf(" XCLIENT", SP_MORE); +#endif + smtp_printf("\r\n", SP_NO_MORE); break; @@ -5403,6 +5508,7 @@ while (done <= 0) log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_cmd_argument, host_and_ident(FALSE)); + GET_OPTION("acl_smtp_etrn"); if ((rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, &user_msg, &log_msg)) != OK) { @@ -5419,20 +5525,21 @@ while (done <= 0) since that is strictly the only kind of ETRN that can be implemented according to the RFC. */ + GET_OPTION("smtp_etrn_command"); if (smtp_etrn_command) { uschar *error; 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) { log_write(0, LOG_MAIN|LOG_PANIC, "failed to set up ETRN command: %s", error); - smtp_printf("458 Internal failure\r\n", FALSE); + smtp_printf("458 Internal failure\r\n", SP_NO_MORE); break; } } @@ -5463,7 +5570,7 @@ while (done <= 0) debug_printf("ETRN command is: %s\n", etrn_command); debug_printf("ETRN command execution skipped\n"); } - if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE); + if (user_msg == NULL) smtp_printf("250 OK\r\n", SP_NO_MORE); else smtp_user_msg(US"250", user_msg); break; } @@ -5474,7 +5581,7 @@ while (done <= 0) if (smtp_etrn_serialize && !enq_start(etrn_serialize_key, 1)) { - smtp_printf("458 Already processing %s\r\n", FALSE, smtp_cmd_data); + smtp_printf("458 Already processing %s\r\n", SP_NO_MORE, smtp_cmd_data); break; } @@ -5539,12 +5646,12 @@ while (done <= 0) { log_write(0, LOG_MAIN|LOG_PANIC, "fork of process for ETRN failed: %s", strerror(errno)); - smtp_printf("458 Unable to fork process\r\n", FALSE); + smtp_printf("458 Unable to fork process\r\n", SP_NO_MORE); if (smtp_etrn_serialize) enq_end(etrn_serialize_key); } else if (!user_msg) - smtp_printf("250 OK\r\n", FALSE); + smtp_printf("250 OK\r\n", SP_NO_MORE); else smtp_user_msg(US"250", user_msg); @@ -5564,33 +5671,33 @@ while (done <= 0) done = synprot_error(L_smtp_syntax_error, 0, NULL, /* Just logs */ US"NUL character(s) present (shown as '?')"); smtp_printf("501 NUL characters are not allowed in SMTP commands\r\n", - FALSE); + SP_NO_MORE); break; 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++; + Uskip_nonwhite(&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), @@ -5601,7 +5708,7 @@ while (done <= 0) #ifdef SUPPORT_PROXY case PROXY_FAIL_IGNORE_CMD: - smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", FALSE); + smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", SP_NO_MORE); break; #endif