X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/4550006051177e69459d81db58b374e67beaf02b..1fb7660fc893265f0e37ceb965396d9e005a0b74:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 54dcaa5e5..37cc023d3 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -121,9 +121,7 @@ static BOOL auth_advertised; #ifdef SUPPORT_TLS static BOOL tls_advertised; #endif -#ifdef EXPERIMENTAL_DSN static BOOL dsn_advertised; -#endif static BOOL esmtp; static BOOL helo_required = FALSE; static BOOL helo_verify = FALSE; @@ -135,6 +133,9 @@ static BOOL rcpt_smtp_response_same; static BOOL rcpt_in_progress; static int nonmail_command_count; static BOOL smtp_exit_function_called = 0; +#ifdef EXPERIMENTAL_INTERNATIONAL +static BOOL smtputf8_advertised; +#endif static int synprot_error_count; static int unknown_command_count; static int sync_cmd_limit; @@ -160,6 +161,8 @@ QUIT is also "falsely" labelled as a mail command so that it doesn't up the count of non-mail commands and possibly provoke an error. */ 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 }, @@ -199,7 +202,7 @@ static uschar *smtp_names[] = US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", US"STARTTLS", US"VRFY" }; -static uschar *protocols[] = { +static uschar *protocols_local[] = { US"local-smtp", /* HELO */ US"local-smtps", /* The rare case EHLO->STARTTLS->HELO */ US"local-esmtp", /* EHLO */ @@ -207,12 +210,19 @@ static uschar *protocols[] = { US"local-esmtpa", /* EHLO->AUTH */ US"local-esmtpsa" /* EHLO->STARTTLS->EHLO->AUTH */ }; +static uschar *protocols[] = { + US"smtp", /* HELO */ + US"smtps", /* The rare case EHLO->STARTTLS->HELO */ + US"esmtp", /* EHLO */ + US"esmtps", /* EHLO->STARTTLS->EHLO */ + US"esmtpa", /* EHLO->AUTH */ + US"esmtpsa" /* EHLO->STARTTLS->EHLO->AUTH */ + }; #define pnormal 0 #define pextend 2 #define pcrpted 1 /* added to pextend or pnormal */ #define pauthed 2 /* added to pextend */ -#define pnlocal 6 /* offset to remove "local" */ /* Sanity check and validate optional args to MAIL FROM: envelope */ enum { @@ -220,8 +230,9 @@ enum { #ifndef DISABLE_PRDR ENV_MAIL_OPT_PRDR, #endif -#ifdef EXPERIMENTAL_DSN ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID, +#ifdef EXPERIMENTAL_INTERNATIONAL + ENV_MAIL_OPT_UTF8, #endif ENV_MAIL_OPT_NULL }; @@ -238,9 +249,10 @@ static env_mail_type_t env_mail_type_list[] = { #ifndef DISABLE_PRDR { US"PRDR", ENV_MAIL_OPT_PRDR, FALSE }, #endif -#ifdef EXPERIMENTAL_DSN { US"RET", ENV_MAIL_OPT_RET, TRUE }, { US"ENVID", ENV_MAIL_OPT_ENVID, TRUE }, +#ifdef EXPERIMENTAL_INTERNATIONAL + { US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */ #endif { US"NULL", ENV_MAIL_OPT_NULL, FALSE } }; @@ -1500,11 +1512,11 @@ sender_verified_list = NULL; /* No senders verified */ memset(sender_address_cache, 0, sizeof(sender_address_cache)); memset(sender_domain_cache, 0, sizeof(sender_domain_cache)); -#ifdef EXPERIMENTAL_DSN +prdr_requested = FALSE; + /* Reset the DSN flags */ dsn_ret = 0; dsn_envid = NULL; -#endif authenticated_sender = NULL; #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -1522,6 +1534,9 @@ spf_received = NULL; spf_result = NULL; spf_smtp_comment = NULL; #endif +#ifdef EXPERIMENTAL_INTERNATIONAL +message_smtputf8 = FALSE; +#endif body_linecount = body_zerocount = 0; sender_rate = sender_rate_limit = sender_rate_period = NULL; @@ -1855,8 +1870,9 @@ tls_in.sni = NULL; tls_in.ocsp = OCSP_NOT_REQ; tls_advertised = FALSE; #endif -#ifdef EXPERIMENTAL_DSN dsn_advertised = FALSE; +#ifdef EXPERIMENTAL_INTERNATIONAL +smtputf8_advertised = FALSE; #endif /* Reset ACL connection variables */ @@ -1885,7 +1901,7 @@ reset later if any of EHLO/AUTH/STARTTLS are received. */ else received_protocol = - protocols[pnormal] + ((sender_host_address != NULL)? pnlocal : 0); + (sender_host_address ? protocols : protocols_local) [pnormal]; /* Set up the buffer for inputting using direct read() calls, and arrange to call the local functions instead of the standard C ones. */ @@ -2139,6 +2155,19 @@ if (!sender_host_unknown) set_process_info("handling incoming connection from %s", host_and_ident(FALSE)); + /* Expand smtp_receive_timeout, if needed */ + + if (smtp_receive_timeout_s) + { + uschar * exp; + if ( !(exp = expand_string(smtp_receive_timeout_s)) + || !(*exp) + || (smtp_receive_timeout = readconf_readtime(exp, 0, FALSE)) < 0 + ) + log_write(0, LOG_MAIN|LOG_PANIC, + "bad value for smtp_receive_timeout: '%s'", exp ? exp : US""); + } + /* Start up TLS if tls_on_connect is set. This is for supporting the legacy smtps port for use with older style SSL MTAs. */ @@ -2435,7 +2464,7 @@ 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\")", - host_and_ident(FALSE), smtp_cmd_buffer); + host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); } if (code > 0) @@ -3129,7 +3158,7 @@ value. The values are 2 larger than the required yield of the function. */ while (done <= 0) { - uschar **argv; + const uschar **argv; uschar *etrn_command; uschar *etrn_serialize_key; uschar *errmess; @@ -3137,7 +3166,7 @@ while (done <= 0) uschar *user_msg = NULL; uschar *recipient = NULL; uschar *hello = NULL; - uschar *set_id = NULL; + const uschar *set_id = NULL; uschar *s, *ss; BOOL was_rej_mail = FALSE; BOOL was_rcpt = FALSE; @@ -3147,10 +3176,8 @@ while (done <= 0) int ptr, size, rc; int c, i; auth_instance *au; -#ifdef EXPERIMENTAL_DSN uschar *orcpt = NULL; int flags; -#endif switch(smtp_read_command(TRUE)) { @@ -3294,9 +3321,10 @@ while (done <= 0) sender_host_authenticated = au->name; authentication_failed = FALSE; authenticated_fail_id = NULL; /* Impossible to already be set? */ + received_protocol = - protocols[pextend + pauthed + ((tls_in.active >= 0)? pcrpted:0)] + - ((sender_host_address != NULL)? pnlocal : 0); + (sender_host_address ? protocols : protocols_local) + [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)]; s = ss = US"235 Authentication succeeded"; authenticated_by = au; break; @@ -3396,7 +3424,7 @@ while (done <= 0) { log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "syntax or protocol errors (last command was \"%s\")", - host_and_ident(FALSE), smtp_cmd_buffer); + host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); done = 1; } @@ -3423,7 +3451,7 @@ while (done <= 0) if (sender_host_name == NULL && (deliver_domain = sender_helo_name, /* set $domain */ - match_isinlist(sender_helo_name, &helo_lookup_domains, 0, + match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK) (void)host_name_lookup(); @@ -3492,12 +3520,13 @@ while (done <= 0) auth_advertised = FALSE; pipelining_advertised = FALSE; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_advertised = FALSE; - #endif - #ifdef EXPERIMENTAL_DSN +#endif dsn_advertised = FALSE; - #endif +#ifdef EXPERIMENTAL_INTERNATIONAL + smtputf8_advertised = FALSE; +#endif smtp_code = US"250 "; /* Default response code plus space*/ if (user_msg == NULL) @@ -3581,7 +3610,6 @@ while (done <= 0) s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11); } - #ifdef EXPERIMENTAL_DSN /* Advertise DSN support if configured to do so. */ if (verify_check_host(&dsn_advertise_hosts) != FAIL) { @@ -3589,7 +3617,6 @@ while (done <= 0) s = string_cat(s, &size, &ptr, US"-DSN\r\n", 6); dsn_advertised = TRUE; } - #endif /* Advertise ETRN if there's an ACL checking whether a host is permitted to issue it; a check is made when any host actually tries. */ @@ -3670,7 +3697,7 @@ while (done <= 0) tls_advertise_hosts. We must *not* advertise if we are already in a secure connection. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_in.active < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) { @@ -3678,16 +3705,26 @@ while (done <= 0) s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11); tls_advertised = TRUE; } - #endif +#endif - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ if (prdr_enable) { s = string_cat(s, &size, &ptr, smtp_code, 3); s = string_cat(s, &size, &ptr, US"-PRDR\r\n", 7); } - #endif +#endif + +#ifdef EXPERIMENTAL_INTERNATIONAL + if ( accept_8bitmime + && verify_check_host(&smtputf8_advertise_hosts) != FAIL) + { + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-SMTPUTF8\r\n", 11); + smtputf8_advertised = TRUE; + } +#endif /* Finish off the multiline reply with one that is always available. */ @@ -3700,9 +3737,9 @@ while (done <= 0) s[ptr] = 0; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr); else - #endif +#endif { int i = fwrite(s, 1, ptr, smtp_out); i = i; /* compiler quietening */ @@ -3717,16 +3754,13 @@ while (done <= 0) helo_seen = TRUE; /* Reset the protocol and the state, abandoning any previous message. */ - - received_protocol = (esmtp? - protocols[pextend + - ((sender_host_authenticated != NULL)? pauthed : 0) + - ((tls_in.active >= 0)? pcrpted : 0)] - : - protocols[pnormal + ((tls_in.active >= 0)? pcrpted : 0)]) - + - ((sender_host_address != NULL)? pnlocal : 0); - + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active >= 0 ? pcrpted : 0) + ]; smtp_reset(reset_point); toomany = FALSE; break; /* HELO/EHLO */ @@ -3799,10 +3833,8 @@ while (done <= 0) (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list); mail_args++ ) - { if (strcmpic(name, mail_args->name) == 0) break; - } if (mail_args->need_value && strcmpic(value, US"") == 0) break; @@ -3830,60 +3862,66 @@ while (done <= 0) and "7BIT" as body types, but take no action. */ case ENV_MAIL_OPT_BODY: if (accept_8bitmime) { - if (strcmpic(value, US"8BITMIME") == 0) { + if (strcmpic(value, US"8BITMIME") == 0) body_8bitmime = 8; - } else if (strcmpic(value, US"7BIT") == 0) { + else if (strcmpic(value, US"7BIT") == 0) body_8bitmime = 7; - } else { + else + { body_8bitmime = 0; done = synprot_error(L_smtp_syntax_error, 501, NULL, US"invalid data for BODY"); goto COMMAND_LOOP; - } + } DEBUG(D_receive) debug_printf("8BITMIME: %d\n", body_8bitmime); break; } arg_error = TRUE; break; - #ifdef EXPERIMENTAL_DSN - /* Handle the two DSN options, but only if configured to do so (which will have caused "DSN" to be given in the EHLO response). The code itself is included only if configured in at build time. */ case ENV_MAIL_OPT_RET: - if (dsn_advertised) { + if (dsn_advertised) + { /* Check if RET has already been set */ - if (dsn_ret > 0) { + if (dsn_ret > 0) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"RET can be specified once only"); goto COMMAND_LOOP; - } - dsn_ret = (strcmpic(value, US"HDRS") == 0)? dsn_ret_hdrs : - (strcmpic(value, US"FULL") == 0)? dsn_ret_full : 0; + } + dsn_ret = strcmpic(value, US"HDRS") == 0 + ? dsn_ret_hdrs + : strcmpic(value, US"FULL") == 0 + ? dsn_ret_full + : 0; DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret); /* Check for invalid invalid value, and exit with error */ - if (dsn_ret == 0) { + if (dsn_ret == 0) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"Value for RET is invalid"); goto COMMAND_LOOP; - } - } + } + } break; case ENV_MAIL_OPT_ENVID: - if (dsn_advertised) { + if (dsn_advertised) + { /* Check if the dsn envid has been already set */ - if (dsn_envid != NULL) { + if (dsn_envid != NULL) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"ENVID can be specified once only"); goto COMMAND_LOOP; - } + } dsn_envid = string_copy(value); DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid); - } + } break; - #endif /* Handle the AUTH extension. If the value given is not "<>" and either the ACL says "yes" or there is no ACL but the sending host is @@ -3922,34 +3960,34 @@ while (done <= 0) switch (rc) { case OK: - if (authenticated_by == NULL || - authenticated_by->mail_auth_condition == NULL || - expand_check_condition(authenticated_by->mail_auth_condition, - authenticated_by->name, US"authenticator")) - break; /* Accept the AUTH */ - - ignore_msg = US"server_mail_auth_condition failed"; - if (authenticated_id != NULL) - ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"", - ignore_msg, authenticated_id); + if (authenticated_by == NULL || + authenticated_by->mail_auth_condition == NULL || + expand_check_condition(authenticated_by->mail_auth_condition, + authenticated_by->name, US"authenticator")) + break; /* Accept the AUTH */ + + ignore_msg = US"server_mail_auth_condition failed"; + if (authenticated_id != NULL) + ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"", + ignore_msg, authenticated_id); /* Fall through */ case FAIL: - authenticated_sender = NULL; - log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)", - value, host_and_ident(TRUE), ignore_msg); - break; + authenticated_sender = NULL; + log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)", + value, host_and_ident(TRUE), ignore_msg); + break; /* Should only get DEFER or ERROR here. Put back terminator overrides for error message */ default: - value[-1] = '='; - name[-1] = ' '; - (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg, - log_msg); - goto COMMAND_LOOP; + value[-1] = '='; + name[-1] = ' '; + (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg, + log_msg); + goto COMMAND_LOOP; } } break; @@ -3961,6 +3999,16 @@ while (done <= 0) break; #endif +#ifdef EXPERIMENTAL_INTERNATIONAL + case ENV_MAIL_OPT_UTF8: + if (smtputf8_advertised) + { + DEBUG(D_receive) debug_printf("smtputf8 requested\n"); + message_smtputf8 = allow_utf8_domains = TRUE; + received_protocol = string_sprintf("utf8%s", received_protocol); + } + break; +#endif /* Unknown option. Stick back the terminator characters and break the loop. Do the name-terminator second as extract_option sets value==name when it found no equal-sign. @@ -3993,9 +4041,10 @@ while (done <= 0) /* Now extract the address, first applying any SMTP-time rewriting. The TRUE flag allows "<>" as a sender address. */ - raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)? - rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", - global_rewrite_rules) : smtp_cmd_data; + raw_sender = rewrite_existflags & rewrite_smtp + ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + global_rewrite_rules) + : smtp_cmd_data; /* rfc821_domains = TRUE; << no longer needed */ raw_sender = @@ -4161,8 +4210,7 @@ while (done <= 0) rcpt_fail_count++; break; } - - #ifdef EXPERIMENTAL_DSN + /* Set the DSN flags orcpt and dsn_flags from the session*/ orcpt = NULL; flags = 0; @@ -4245,7 +4293,6 @@ while (done <= 0) break; } } - #endif /* Apply SMTP rewriting then extract the working address. Don't allow "<>" as a recipient address */ @@ -4360,21 +4407,14 @@ while (done <= 0) if (user_msg == NULL) smtp_printf("250 Accepted\r\n"); else smtp_user_msg(US"250", user_msg); receive_add_recipient(recipient, -1); - - #ifdef EXPERIMENTAL_DSN + /* Set the dsn flags in the recipients_list */ - if (orcpt != NULL) - recipients_list[recipients_count-1].orcpt = orcpt; - else - recipients_list[recipients_count-1].orcpt = NULL; + recipients_list[recipients_count-1].orcpt = orcpt; + recipients_list[recipients_count-1].dsn_flags = flags; - if (flags != 0) - recipients_list[recipients_count-1].dsn_flags = flags; - else - recipients_list[recipients_count-1].dsn_flags = 0; - DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", recipients_list[recipients_count-1].orcpt, recipients_list[recipients_count-1].dsn_flags); - #endif - + DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", + recipients_list[recipients_count-1].orcpt, + recipients_list[recipients_count-1].dsn_flags); } /* The recipient was discarded */ @@ -4385,13 +4425,11 @@ while (done <= 0) else smtp_user_msg(US"250", user_msg); rcpt_fail_count++; discarded = TRUE; - log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: " + log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: " "discarded by %s ACL%s%s", host_and_ident(TRUE), - (sender_address_unrewritten != NULL)? - sender_address_unrewritten : sender_address, + sender_address_unrewritten? sender_address_unrewritten : sender_address, smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT", - (log_msg == NULL)? US"" : US": ", - (log_msg == NULL)? US"" : log_msg); + log_msg ? US": " : US"", log_msg ? log_msg : US""); } /* Either the ACL failed the address, or it was deferred. */ @@ -4459,7 +4497,7 @@ while (done <= 0) ACL may have delayed. To handle cutthrough delivery enforce a dummy call to get the DATA command sent. */ - if (acl_smtp_predata == NULL && cutthrough_fd < 0) rc = OK; else + if (acl_smtp_predata == NULL && cutthrough.fd < 0) rc = OK; else { uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept"; enable_dollar_recipients = TRUE; @@ -4635,13 +4673,13 @@ while (done <= 0) set_process_info("handling incoming TLS connection from %s", host_and_ident(FALSE)); } - received_protocol = (esmtp? - protocols[pextend + pcrpted + - ((sender_host_authenticated != NULL)? pauthed : 0)] - : - protocols[pnormal + pcrpted]) - + - ((sender_host_address != NULL)? pnlocal : 0); + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active >= 0 ? pcrpted : 0) + ]; sender_host_authenticated = NULL; authenticated_id = NULL; @@ -4861,7 +4899,7 @@ while (done <= 0) break; } etrn_command = US"exim -R"; - argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R", + argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R", smtp_cmd_data); } @@ -5026,7 +5064,7 @@ while (done <= 0) done = 2; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "unrecognized commands (last was \"%s\")", host_and_ident(FALSE), - smtp_cmd_buffer); + string_printing(smtp_cmd_buffer)); } else done = synprot_error(L_smtp_syntax_error, 500, NULL,