diff options
-rw-r--r-- | src/src/smtp_in.c | 2893 |
1 files changed, 1447 insertions, 1446 deletions
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index aa85add3e..daaab9172 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -187,10 +187,10 @@ static smtp_cmd_list cmd_list[] = { { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE }, { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, - #endif +#endif /* If you change anything above here, also fix the definitions below. */ @@ -2556,7 +2556,7 @@ if (!sender_host_unknown) How to do this properly in IPv6 is not yet known. */ - #if !HAVE_IPV6 && !defined(NO_IP_OPTIONS) +#if !HAVE_IPV6 && !defined(NO_IP_OPTIONS) #ifdef GLIBC_IP_OPTIONS #if (!defined __GLIBC__) || (__GLIBC__ < 2) @@ -2711,7 +2711,7 @@ if (!sender_host_unknown) else DEBUG(D_receive) debug_printf("no IP options found\n"); } - #endif /* HAVE_IPV6 && !defined(NO_IP_OPTIONS) */ +#endif /* HAVE_IPV6 && !defined(NO_IP_OPTIONS) */ /* Set keep-alive in socket options. The option is on by default. This setting is an attempt to get rid of some hanging connections that stick in @@ -3527,14 +3527,14 @@ else if (sender_helo_name[0] == '[') helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address, Ustrlen(sender_host_address)) == 0; - #if HAVE_IPV6 +#if HAVE_IPV6 if (!helo_verified) { if (strncmpic(sender_host_address, US"::ffff:", 7) == 0) helo_verified = Ustrncmp(sender_helo_name + 1, sender_host_address + 7, Ustrlen(sender_host_address) - 7) == 0; } - #endif +#endif HDEBUG(D_receive) { if (helo_verified) debug_printf("matched host address\n"); } @@ -3956,86 +3956,86 @@ while (done <= 0) AUTHS will eventually hit the nonmail threshold. */ case AUTH_CMD: - HAD(SCH_AUTH); - authentication_failed = TRUE; - cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE; + HAD(SCH_AUTH); + authentication_failed = TRUE; + cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE; - if (!auth_advertised && !allow_auth_unadvertised) - { - done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"AUTH command used when not advertised"); - break; - } - if (sender_host_authenticated) - { - done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"already authenticated"); - break; - } - if (sender_address) - { - done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"not permitted in mail transaction"); - break; - } + if (!auth_advertised && !allow_auth_unadvertised) + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"AUTH command used when not advertised"); + break; + } + if (sender_host_authenticated) + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"already authenticated"); + break; + } + if (sender_address) + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"not permitted in mail transaction"); + break; + } - /* Check the ACL */ + /* Check the ACL */ - if ( acl_smtp_auth - && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, - &user_msg, &log_msg)) != OK - ) - { - done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); - break; - } + if ( acl_smtp_auth + && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, + &user_msg, &log_msg)) != OK + ) + { + done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); + break; + } - /* Find the name of the requested authentication mechanism. */ + /* Find the name of the requested authentication mechanism. */ - s = smtp_cmd_data; - while ((c = *smtp_cmd_data) != 0 && !isspace(c)) - { - if (!isalnum(c) && c != '-' && c != '_') - { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"invalid character in authentication mechanism name"); - goto COMMAND_LOOP; - } - smtp_cmd_data++; - } + s = smtp_cmd_data; + while ((c = *smtp_cmd_data) != 0 && !isspace(c)) + { + if (!isalnum(c) && c != '-' && c != '_') + { + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"invalid character in authentication mechanism name"); + goto COMMAND_LOOP; + } + smtp_cmd_data++; + } - /* If not at the end of the line, we must be at white space. Terminate the - name and move the pointer on to any data that may be present. */ + /* If not at the end of the line, we must be at white space. Terminate the + name and move the pointer on to any data that may be present. */ - if (*smtp_cmd_data != 0) - { - *smtp_cmd_data++ = 0; - while (isspace(*smtp_cmd_data)) smtp_cmd_data++; - } + if (*smtp_cmd_data != 0) + { + *smtp_cmd_data++ = 0; + while (isspace(*smtp_cmd_data)) smtp_cmd_data++; + } - /* Search for an authentication mechanism which is configured for use - as a server and which has been advertised (unless, sigh, allow_auth_ - unadvertised is set). */ + /* Search for an authentication mechanism which is configured for use + as a server and which has been advertised (unless, sigh, allow_auth_ + unadvertised is set). */ - for (au = auths; au; au = au->next) - if (strcmpic(s, au->public_name) == 0 && au->server && - (au->advertised || allow_auth_unadvertised)) - break; + for (au = auths; au; au = au->next) + if (strcmpic(s, au->public_name) == 0 && au->server && + (au->advertised || allow_auth_unadvertised)) + break; - if (au) - { - c = smtp_in_auth(au, &s, &ss); + if (au) + { + c = smtp_in_auth(au, &s, &ss); - 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); - } - else - done = synprot_error(L_smtp_protocol_error, 504, NULL, - string_sprintf("%s authentication mechanism not supported", s)); + 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); + } + else + done = synprot_error(L_smtp_protocol_error, 504, NULL, + string_sprintf("%s authentication mechanism not supported", s)); - break; /* AUTH_CMD */ + break; /* AUTH_CMD */ /* The HELO/EHLO commands are permitted to appear in the middle of a session as well as at the beginning. They have the effect of a reset in @@ -4054,409 +4054,409 @@ while (done <= 0) it did the reset first. */ case HELO_CMD: - HAD(SCH_HELO); - hello = US"HELO"; - esmtp = FALSE; - goto HELO_EHLO; + HAD(SCH_HELO); + hello = US"HELO"; + esmtp = FALSE; + goto HELO_EHLO; case EHLO_CMD: - HAD(SCH_EHLO); - hello = US"EHLO"; - esmtp = TRUE; + HAD(SCH_EHLO); + hello = US"EHLO"; + 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[CMD_LIST_HELO].is_mail_cmd = FALSE; + cmd_list[CMD_LIST_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. */ + /* Reject the HELO if its argument was invalid or non-existent. A + successful check causes the argument to be saved in malloc store. */ - if (!check_helo(smtp_cmd_data)) - { - smtp_printf("501 Syntactically invalid %s argument(s)\r\n", FALSE, hello); + if (!check_helo(smtp_cmd_data)) + { + smtp_printf("501 Syntactically invalid %s argument(s)\r\n", FALSE, hello); - log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically " - "invalid argument(s): %s", hello, host_and_ident(FALSE), - (*smtp_cmd_argument == 0)? US"(no argument given)" : - string_printing(smtp_cmd_argument)); + log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically " + "invalid argument(s): %s", hello, host_and_ident(FALSE), + *smtp_cmd_argument == 0 ? US"(no argument given)" : + string_printing(smtp_cmd_argument)); - 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\")", - host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); - done = 1; - } + 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\")", + host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); + done = 1; + } - break; - } + break; + } - /* If sender_host_unknown is true, we have got here via the -bs interface, - not called from inetd. Otherwise, we are running an IP connection and the - host address will be set. If the helo name is the primary name of this - host and we haven't done a reverse lookup, force one now. If helo_required - is set, ensure that the HELO name matches the actual host. If helo_verify - is set, do the same check, but softly. */ + /* If sender_host_unknown is true, we have got here via the -bs interface, + not called from inetd. Otherwise, we are running an IP connection and the + host address will be set. If the helo name is the primary name of this + host and we haven't done a reverse lookup, force one now. If helo_required + is set, ensure that the HELO name matches the actual host. If helo_verify + is set, do the same check, but softly. */ - if (!sender_host_unknown) - { - BOOL old_helo_verified = helo_verified; - uschar *p = smtp_cmd_data; + if (!sender_host_unknown) + { + BOOL old_helo_verified = helo_verified; + uschar *p = smtp_cmd_data; - while (*p != 0 && !isspace(*p)) { *p = tolower(*p); p++; } - *p = 0; + while (*p != 0 && !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. */ + /* Force a reverse lookup if HELO quoted something in helo_lookup_domains + because otherwise the log can be confusing. */ - if ( !sender_host_name - && (deliver_domain = sender_helo_name, /* set $domain */ - match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0, - &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK) - (void)host_name_lookup(); + if ( !sender_host_name + && (deliver_domain = sender_helo_name, /* set $domain */ + match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK) + (void)host_name_lookup(); - /* Rebuild the fullhost info to include the HELO name (and the real name - if it was looked up.) */ + /* Rebuild the fullhost info to include the HELO name (and the real name + if it was looked up.) */ - host_build_sender_fullhost(); /* Rebuild */ - set_process_info("handling%s incoming connection from %s", - (tls_in.active.sock >= 0)? " TLS" : "", host_and_ident(FALSE)); + host_build_sender_fullhost(); /* Rebuild */ + set_process_info("handling%s incoming connection from %s", + tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE)); - /* Verify if configured. This doesn't give much security, but it does - make some people happy to be able to do it. If helo_required is set, - (host matches helo_verify_hosts) failure forces rejection. If helo_verify - is set (host matches helo_try_verify_hosts), it does not. This is perhaps - now obsolescent, since the verification can now be requested selectively - at ACL time. */ + /* Verify if configured. This doesn't give much security, but it does + make some people happy to be able to do it. If helo_required is set, + (host matches helo_verify_hosts) failure forces rejection. If helo_verify + is set (host matches helo_try_verify_hosts), it does not. This is perhaps + now obsolescent, since the verification can now be requested selectively + at ACL time. */ - helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE; - if (helo_required || helo_verify) - { - BOOL tempfail = !smtp_verify_helo(); - if (!helo_verified) - { - if (helo_required) - { - smtp_printf("%d %s argument does not match calling host\r\n", FALSE, - tempfail? 451 : 550, hello); - log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s", - tempfail? "temporarily " : "", - hello, sender_helo_name, host_and_ident(FALSE)); - helo_verified = old_helo_verified; - break; /* End of HELO/EHLO processing */ - } - HDEBUG(D_all) debug_printf("%s verification failed but host is in " - "helo_try_verify_hosts\n", hello); - } - } - } + helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE; + if (helo_required || helo_verify) + { + BOOL tempfail = !smtp_verify_helo(); + if (!helo_verified) + { + if (helo_required) + { + smtp_printf("%d %s argument does not match calling host\r\n", FALSE, + tempfail? 451 : 550, hello); + log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s", + tempfail? "temporarily " : "", + hello, sender_helo_name, host_and_ident(FALSE)); + helo_verified = old_helo_verified; + break; /* End of HELO/EHLO processing */ + } + HDEBUG(D_all) debug_printf("%s verification failed but host is in " + "helo_try_verify_hosts\n", hello); + } + } + } #ifdef SUPPORT_SPF - /* set up SPF context */ - spf_init(sender_helo_name, sender_host_address); + /* set up SPF context */ + spf_init(sender_helo_name, sender_host_address); #endif - /* Apply an ACL check if one is defined; afterwards, recheck - synchronization in case the client started sending in a delay. */ + /* Apply an ACL check if one is defined; afterwards, recheck + synchronization in case the client started sending in a delay. */ - if (acl_smtp_helo) - if ((rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, - &user_msg, &log_msg)) != OK) - { - done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg); - if (sender_helo_name) + if (acl_smtp_helo) + if ((rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, + &user_msg, &log_msg)) != OK) { - store_free(sender_helo_name); - sender_helo_name = NULL; + done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg); + if (sender_helo_name) + { + store_free(sender_helo_name); + sender_helo_name = NULL; + } + host_build_sender_fullhost(); /* Rebuild */ + break; } - host_build_sender_fullhost(); /* Rebuild */ - break; - } - else if (!check_sync()) goto SYNC_FAILURE; + else if (!check_sync()) goto SYNC_FAILURE; - /* Generate an OK reply. The default string includes the ident if present, - and also the IP address if present. Reflecting back the ident is intended - as a deterrent to mail forgers. For maximum efficiency, and also because - some broken systems expect each response to be in a single packet, arrange - that the entire reply is sent in one write(). */ + /* Generate an OK reply. The default string includes the ident if present, + and also the IP address if present. Reflecting back the ident is intended + as a deterrent to mail forgers. For maximum efficiency, and also because + some broken systems expect each response to be in a single packet, arrange + that the entire reply is sent in one write(). */ - auth_advertised = FALSE; - smtp_in_pipelining_advertised = FALSE; + auth_advertised = FALSE; + smtp_in_pipelining_advertised = FALSE; #ifdef SUPPORT_TLS - tls_advertised = FALSE; + tls_advertised = FALSE; # ifdef EXPERIMENTAL_REQUIRETLS - requiretls_advertised = FALSE; + requiretls_advertised = FALSE; # endif #endif - dsn_advertised = FALSE; + dsn_advertised = FALSE; #ifdef SUPPORT_I18N - smtputf8_advertised = FALSE; + smtputf8_advertised = FALSE; #endif - smtp_code = US"250 "; /* Default response code plus space*/ - if (!user_msg) - { - s = string_sprintf("%.3s %s Hello %s%s%s", - smtp_code, - smtp_active_hostname, - sender_ident ? sender_ident : US"", - sender_ident ? US" at " : US"", - sender_host_name ? sender_host_name : sender_helo_name); - g = string_cat(NULL, s); - - if (sender_host_address) - { - g = string_catn(g, US" [", 2); - g = string_cat (g, sender_host_address); - g = string_catn(g, US"]", 1); - } - } + smtp_code = US"250 "; /* Default response code plus space*/ + if (!user_msg) + { + s = string_sprintf("%.3s %s Hello %s%s%s", + smtp_code, + smtp_active_hostname, + sender_ident ? sender_ident : US"", + sender_ident ? US" at " : US"", + sender_host_name ? sender_host_name : sender_helo_name); + g = string_cat(NULL, s); + + if (sender_host_address) + { + g = string_catn(g, US" [", 2); + g = string_cat (g, sender_host_address); + g = string_catn(g, US"]", 1); + } + } - /* A user-supplied EHLO greeting may not contain more than one line. Note - that the code returned by smtp_message_code() includes the terminating - whitespace character. */ + /* A user-supplied EHLO greeting may not contain more than one line. Note + that the code returned by smtp_message_code() includes the terminating + whitespace character. */ - else - { - 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); - if ((ss = strpbrk(CS s, "\r\n")) != NULL) - { - log_write(0, LOG_MAIN|LOG_PANIC, "EHLO/HELO response must not contain " - "newlines: message truncated: %s", string_printing(s)); - *ss = 0; - } - g = string_cat(NULL, s); - } + else + { + 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); + if ((ss = strpbrk(CS s, "\r\n")) != NULL) + { + log_write(0, LOG_MAIN|LOG_PANIC, "EHLO/HELO response must not contain " + "newlines: message truncated: %s", string_printing(s)); + *ss = 0; + } + g = string_cat(NULL, s); + } - g = string_catn(g, US"\r\n", 2); + g = string_catn(g, US"\r\n", 2); - /* If we received EHLO, we must create a multiline response which includes - the functions supported. */ + /* If we received EHLO, we must create a multiline response which includes + the functions supported. */ - if (esmtp) - { - g->s[3] = '-'; + if (esmtp) + { + g->s[3] = '-'; - /* 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 - it sends this. However, there seems little point in not sending it. - The actual size check happens later at MAIL FROM time. By postponing it - till then, VRFY and EXPN can be used after EHLO when space is short. */ + /* 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 + it sends this. However, there seems little point in not sending it. + The actual size check happens later at MAIL FROM time. By postponing it + till then, VRFY and EXPN can be used after EHLO when space is short. */ - if (thismessage_size_limit > 0) - { - sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code, - thismessage_size_limit); - g = string_cat(g, big_buffer); - } - else - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-SIZE\r\n", 7); - } + if (thismessage_size_limit > 0) + { + sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code, + thismessage_size_limit); + g = string_cat(g, big_buffer); + } + else + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-SIZE\r\n", 7); + } - /* Exim does not do protocol conversion or data conversion. It is 8-bit - clean; if it has an 8-bit character in its hand, it just sends it. It - cannot therefore specify 8BITMIME and remain consistent with the RFCs. - However, some users want this option simply in order to stop MUAs - mangling messages that contain top-bit-set characters. It is therefore - provided as an option. */ + /* Exim does not do protocol conversion or data conversion. It is 8-bit + clean; if it has an 8-bit character in its hand, it just sends it. It + cannot therefore specify 8BITMIME and remain consistent with the RFCs. + However, some users want this option simply in order to stop MUAs + mangling messages that contain top-bit-set characters. It is therefore + provided as an option. */ - if (accept_8bitmime) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-8BITMIME\r\n", 11); - } + if (accept_8bitmime) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-8BITMIME\r\n", 11); + } - /* Advertise DSN support if configured to do so. */ - if (verify_check_host(&dsn_advertise_hosts) != FAIL) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-DSN\r\n", 6); - dsn_advertised = TRUE; - } + /* Advertise DSN support if configured to do so. */ + if (verify_check_host(&dsn_advertise_hosts) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-DSN\r\n", 6); + dsn_advertised = TRUE; + } - /* 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. */ + /* 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. */ - if (acl_smtp_etrn) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-ETRN\r\n", 7); - } - if (acl_smtp_vrfy) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-VRFY\r\n", 7); - } - if (acl_smtp_expn) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-EXPN\r\n", 7); - } + if (acl_smtp_etrn) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-ETRN\r\n", 7); + } + if (acl_smtp_vrfy) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-VRFY\r\n", 7); + } + if (acl_smtp_expn) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-EXPN\r\n", 7); + } - /* Exim is quite happy with pipelining, so let the other end know that - it is safe to use it, unless advertising is disabled. */ + /* Exim is quite happy with pipelining, so let the other end know that + it is safe to use it, unless advertising is disabled. */ - if (pipelining_enable && - verify_check_host(&pipelining_advertise_hosts) == OK) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-PIPELINING\r\n", 13); - sync_cmd_limit = NON_SYNC_CMD_PIPELINING; - smtp_in_pipelining_advertised = TRUE; - } + if (pipelining_enable && + verify_check_host(&pipelining_advertise_hosts) == OK) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-PIPELINING\r\n", 13); + sync_cmd_limit = NON_SYNC_CMD_PIPELINING; + smtp_in_pipelining_advertised = TRUE; + } - /* If any server authentication mechanisms are configured, advertise - them if the current host is in auth_advertise_hosts. The problem with - advertising always is that some clients then require users to - authenticate (and aren't configurable otherwise) even though it may not - be necessary (e.g. if the host is in host_accept_relay). + /* If any server authentication mechanisms are configured, advertise + them if the current host is in auth_advertise_hosts. The problem with + advertising always is that some clients then require users to + authenticate (and aren't configurable otherwise) even though it may not + be necessary (e.g. if the host is in host_accept_relay). - RFC 2222 states that SASL mechanism names contain only upper case - letters, so output the names in upper case, though we actually recognize - them in either case in the AUTH command. */ + RFC 2222 states that SASL mechanism names contain only upper case + letters, so output the names in upper case, though we actually recognize + them in either case in the AUTH command. */ - if ( auths + if ( auths #ifdef AUTH_TLS - && !sender_host_authenticated + && !sender_host_authenticated #endif - && verify_check_host(&auth_advertise_hosts) == OK - ) - { - auth_instance *au; - BOOL first = TRUE; - for (au = auths; au; au = au->next) + && verify_check_host(&auth_advertise_hosts) == OK + ) { - au->advertised = FALSE; - if (au->server) + auth_instance *au; + BOOL first = TRUE; + for (au = auths; au; au = au->next) { - DEBUG(D_auth+D_expand) debug_printf_indent( - "Evaluating advertise_condition for %s athenticator\n", - au->public_name); - if ( !au->advertise_condition - || expand_check_condition(au->advertise_condition, au->name, - US"authenticator") - ) + au->advertised = FALSE; + if (au->server) { - int saveptr; - if (first) + DEBUG(D_auth+D_expand) debug_printf_indent( + "Evaluating advertise_condition for %s athenticator\n", + au->public_name); + if ( !au->advertise_condition + || expand_check_condition(au->advertise_condition, au->name, + US"authenticator") + ) { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-AUTH", 5); - first = FALSE; - auth_advertised = TRUE; + int saveptr; + if (first) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-AUTH", 5); + first = FALSE; + auth_advertised = TRUE; + } + saveptr = g->ptr; + g = string_catn(g, US" ", 1); + g = string_cat (g, au->public_name); + while (++saveptr < g->ptr) g->s[saveptr] = toupper(g->s[saveptr]); + au->advertised = TRUE; } - saveptr = g->ptr; - g = string_catn(g, US" ", 1); - g = string_cat (g, au->public_name); - while (++saveptr < g->ptr) g->s[saveptr] = toupper(g->s[saveptr]); - au->advertised = TRUE; } } - } - if (!first) g = string_catn(g, US"\r\n", 2); - } + if (!first) g = string_catn(g, US"\r\n", 2); + } - /* RFC 3030 CHUNKING */ + /* RFC 3030 CHUNKING */ - if (verify_check_host(&chunking_advertise_hosts) != FAIL) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-CHUNKING\r\n", 11); - chunking_offered = TRUE; - chunking_state = CHUNKING_OFFERED; - } + if (verify_check_host(&chunking_advertise_hosts) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-CHUNKING\r\n", 11); + chunking_offered = TRUE; + chunking_state = CHUNKING_OFFERED; + } - /* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer) - if it has been included in the binary, and the host matches - tls_advertise_hosts. We must *not* advertise if we are already in a - secure connection. */ + /* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer) + if it has been included in the binary, and the host matches + tls_advertise_hosts. We must *not* advertise if we are already in a + secure connection. */ #ifdef SUPPORT_TLS - if (tls_in.active.sock < 0 && - verify_check_host(&tls_advertise_hosts) != FAIL) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-STARTTLS\r\n", 11); - tls_advertised = TRUE; - } + if (tls_in.active.sock < 0 && + verify_check_host(&tls_advertise_hosts) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-STARTTLS\r\n", 11); + tls_advertised = TRUE; + } # ifdef EXPERIMENTAL_REQUIRETLS - /* Advertise REQUIRETLS only once we are in a secure connection */ - if ( tls_in.active.sock >= 0 - && verify_check_host(&tls_advertise_requiretls) != FAIL) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-REQUIRETLS\r\n", 13); - requiretls_advertised = TRUE; - } + /* Advertise REQUIRETLS only once we are in a secure connection */ + if ( tls_in.active.sock >= 0 + && verify_check_host(&tls_advertise_requiretls) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-REQUIRETLS\r\n", 13); + requiretls_advertised = TRUE; + } # endif #endif #ifndef DISABLE_PRDR - /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ - if (prdr_enable) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-PRDR\r\n", 7); - } + /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ + if (prdr_enable) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-PRDR\r\n", 7); + } #endif #ifdef SUPPORT_I18N - if ( accept_8bitmime - && verify_check_host(&smtputf8_advertise_hosts) != FAIL) - { - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US"-SMTPUTF8\r\n", 11); - smtputf8_advertised = TRUE; - } + if ( accept_8bitmime + && verify_check_host(&smtputf8_advertise_hosts) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-SMTPUTF8\r\n", 11); + smtputf8_advertised = TRUE; + } #endif - /* Finish off the multiline reply with one that is always available. */ + /* Finish off the multiline reply with one that is always available. */ - g = string_catn(g, smtp_code, 3); - g = string_catn(g, US" HELP\r\n", 7); - } + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US" HELP\r\n", 7); + } - /* Terminate the string (for debug), write it, and note that HELO/EHLO - has been seen. */ + /* Terminate the string (for debug), write it, and note that HELO/EHLO + has been seen. */ #ifdef SUPPORT_TLS - if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else + if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else #endif - { - int i = fwrite(g->s, 1, g->ptr, smtp_out); i = i; /* compiler quietening */ - } - DEBUG(D_receive) - { - uschar *cr; + { + int i = fwrite(g->s, 1, g->ptr, smtp_out); i = i; /* compiler quietening */ + } + DEBUG(D_receive) + { + uschar *cr; - (void) string_from_gstring(g); - while ((cr = Ustrchr(g->s, '\r')) != NULL) /* lose CRs */ - memmove(cr, cr + 1, (g->ptr--) - (cr - g->s)); - debug_printf("SMTP>> %s", g->s); - } - helo_seen = TRUE; + (void) string_from_gstring(g); + while ((cr = Ustrchr(g->s, '\r')) != NULL) /* lose CRs */ + memmove(cr, cr + 1, (g->ptr--) - (cr - g->s)); + debug_printf("SMTP>> %s", g->s); + } + helo_seen = TRUE; - /* Reset the protocol and the state, abandoning any previous message. */ - received_protocol = - (sender_host_address ? protocols : protocols_local) - [ (esmtp - ? pextend + (sender_host_authenticated ? pauthed : 0) - : pnormal) - + (tls_in.active.sock >= 0 ? pcrpted : 0) - ]; - cancel_cutthrough_connection(TRUE, US"sent EHLO response"); - smtp_reset(reset_point); - toomany = FALSE; - break; /* HELO/EHLO */ + /* Reset the protocol and the state, abandoning any previous message. */ + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active.sock >= 0 ? pcrpted : 0) + ]; + cancel_cutthrough_connection(TRUE, US"sent EHLO response"); + smtp_reset(reset_point); + toomany = FALSE; + break; /* HELO/EHLO */ /* The MAIL command requires an address as an operand. All we do @@ -4466,448 +4466,448 @@ while (done <= 0) it is the canonical extracted address which is all that is kept. */ case MAIL_CMD: - HAD(SCH_MAIL); - smtp_mailcmd_count++; /* Count for limit and ratelimit */ - was_rej_mail = TRUE; /* Reset if accepted */ - env_mail_type_t * mail_args; /* Sanity check & validate args */ + HAD(SCH_MAIL); + smtp_mailcmd_count++; /* Count for limit and ratelimit */ + was_rej_mail = TRUE; /* Reset if accepted */ + env_mail_type_t * mail_args; /* Sanity check & validate args */ - if (helo_required && !helo_seen) - { - smtp_printf("503 HELO or EHLO required\r\n", FALSE); - log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " - "HELO/EHLO given", host_and_ident(FALSE)); - break; - } + if (helo_required && !helo_seen) + { + smtp_printf("503 HELO or EHLO required\r\n", FALSE); + log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " + "HELO/EHLO given", host_and_ident(FALSE)); + break; + } - if (sender_address) - { - done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"sender already given"); - break; - } + if (sender_address) + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"sender already given"); + break; + } - if (!*smtp_cmd_data) - { - done = synprot_error(L_smtp_protocol_error, 501, NULL, - US"MAIL must have an address operand"); - break; - } + if (!*smtp_cmd_data) + { + done = synprot_error(L_smtp_protocol_error, 501, NULL, + US"MAIL must have an address operand"); + break; + } - /* Check to see if the limit for messages per connection would be - exceeded by accepting further messages. */ + /* Check to see if the limit for messages per connection would be + exceeded by accepting further messages. */ - if (smtp_accept_max_per_connection > 0 && - smtp_mailcmd_count > smtp_accept_max_per_connection) - { - smtp_printf("421 too many messages in this connection\r\n", FALSE); - log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many " - "messages in one connection", host_and_ident(TRUE)); - break; - } - - /* Reset for start of message - even if this is going to fail, we - obviously need to throw away any previous data. */ + if (smtp_accept_max_per_connection > 0 && + smtp_mailcmd_count > smtp_accept_max_per_connection) + { + smtp_printf("421 too many messages in this connection\r\n", FALSE); + log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many " + "messages in one connection", host_and_ident(TRUE)); + break; + } - cancel_cutthrough_connection(TRUE, US"MAIL received"); - smtp_reset(reset_point); - toomany = FALSE; - sender_data = recipient_data = NULL; + /* Reset for start of message - even if this is going to fail, we + obviously need to throw away any previous data. */ - /* Loop, checking for ESMTP additions to the MAIL FROM command. */ + cancel_cutthrough_connection(TRUE, US"MAIL received"); + smtp_reset(reset_point); + toomany = FALSE; + sender_data = recipient_data = NULL; - if (esmtp) for(;;) - { - uschar *name, *value, *end; - unsigned long int size; - BOOL arg_error = FALSE; + /* Loop, checking for ESMTP additions to the MAIL FROM command. */ - if (!extract_option(&name, &value)) break; + if (esmtp) for(;;) + { + uschar *name, *value, *end; + unsigned long int size; + BOOL arg_error = FALSE; - for (mail_args = env_mail_type_list; - mail_args->value != ENV_MAIL_OPT_NULL; - mail_args++ - ) - if (strcmpic(name, mail_args->name) == 0) - break; - if (mail_args->need_value && strcmpic(value, US"") == 0) - break; + if (!extract_option(&name, &value)) break; - switch(mail_args->value) - { - /* Handle SIZE= by reading the value. We don't do the check till later, - in order to be able to log the sender address on failure. */ - case ENV_MAIL_OPT_SIZE: - if (((size = Ustrtoul(value, &end, 10)), *end == 0)) - { - if ((size == ULONG_MAX && errno == ERANGE) || size > INT_MAX) - size = INT_MAX; - message_size = (int)size; - } - else - arg_error = TRUE; - break; + for (mail_args = env_mail_type_list; + mail_args->value != ENV_MAIL_OPT_NULL; + mail_args++ + ) + if (strcmpic(name, mail_args->name) == 0) + break; + if (mail_args->need_value && strcmpic(value, US"") == 0) + break; - /* If this session was initiated with EHLO and accept_8bitmime is set, - Exim will have indicated that it supports the BODY=8BITMIME option. In - fact, it does not support this according to the RFCs, in that it does not - take any special action for forwarding messages containing 8-bit - characters. That is why accept_8bitmime is not the default setting, but - some sites want the action that is provided. We recognize both "8BITMIME" - and "7BIT" as body types, but take no action. */ - case ENV_MAIL_OPT_BODY: - if (accept_8bitmime) { - if (strcmpic(value, US"8BITMIME") == 0) - body_8bitmime = 8; - else if (strcmpic(value, US"7BIT") == 0) - body_8bitmime = 7; - else + switch(mail_args->value) + { + /* Handle SIZE= by reading the value. We don't do the check till later, + in order to be able to log the sender address on failure. */ + case ENV_MAIL_OPT_SIZE: + if (((size = Ustrtoul(value, &end, 10)), *end == 0)) { - 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); + if ((size == ULONG_MAX && errno == ERANGE) || size > INT_MAX) + size = INT_MAX; + message_size = (int)size; + } + else + arg_error = TRUE; break; - } - arg_error = TRUE; - break; - /* 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. */ + /* If this session was initiated with EHLO and accept_8bitmime is set, + Exim will have indicated that it supports the BODY=8BITMIME option. In + fact, it does not support this according to the RFCs, in that it does not + take any special action for forwarding messages containing 8-bit + characters. That is why accept_8bitmime is not the default setting, but + some sites want the action that is provided. We recognize both "8BITMIME" + and "7BIT" as body types, but take no action. */ + case ENV_MAIL_OPT_BODY: + if (accept_8bitmime) { + if (strcmpic(value, US"8BITMIME") == 0) + body_8bitmime = 8; + else if (strcmpic(value, US"7BIT") == 0) + body_8bitmime = 7; + 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; - case ENV_MAIL_OPT_RET: - if (dsn_advertised) - { - /* Check if RET has already been set */ - if (dsn_ret > 0) - { - done = 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; - DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret); - /* Check for invalid invalid value, and exit with error */ - if (dsn_ret == 0) + /* 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) { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"Value for RET is invalid"); - goto COMMAND_LOOP; + /* Check if RET has already been set */ + if (dsn_ret > 0) + { + done = 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; + DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret); + /* Check for invalid invalid value, and exit with error */ + if (dsn_ret == 0) + { + done = 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) - { - /* Check if the dsn envid has been already set */ - if (dsn_envid) + break; + case ENV_MAIL_OPT_ENVID: + if (dsn_advertised) { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"ENVID can be specified once only"); - goto COMMAND_LOOP; + /* Check if the dsn envid has been already set */ + if (dsn_envid) + { + done = 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); } - dsn_envid = string_copy(value); - DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid); - } - break; - - /* 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 - authenticated, we set it up as the authenticated sender. However, if the - authenticator set a condition to be tested, we ignore AUTH on MAIL unless - the condition is met. The value of AUTH is an xtext, which means that +, - = and cntrl chars are coded in hex; however "<>" is unaffected by this - coding. */ - case ENV_MAIL_OPT_AUTH: - if (Ustrcmp(value, "<>") != 0) - { - int rc; - uschar *ignore_msg; + break; - if (auth_xtextdecode(value, &authenticated_sender) < 0) - { - /* Put back terminator overrides for error message */ - value[-1] = '='; - name[-1] = ' '; - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"invalid data for AUTH"); - goto COMMAND_LOOP; - } - if (!acl_smtp_mailauth) - { - ignore_msg = US"client not authenticated"; - rc = sender_host_authenticated ? OK : FAIL; - } - else - { - ignore_msg = US"rejected by ACL"; - rc = acl_check(ACL_WHERE_MAILAUTH, NULL, acl_smtp_mailauth, - &user_msg, &log_msg); - } + /* 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 + authenticated, we set it up as the authenticated sender. However, if the + authenticator set a condition to be tested, we ignore AUTH on MAIL unless + the condition is met. The value of AUTH is an xtext, which means that +, + = and cntrl chars are coded in hex; however "<>" is unaffected by this + coding. */ + case ENV_MAIL_OPT_AUTH: + if (Ustrcmp(value, "<>") != 0) + { + int rc; + uschar *ignore_msg; - 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); - - /* 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; - - /* Should only get DEFER or ERROR here. Put back terminator - overrides for error message */ - - default: + if (auth_xtextdecode(value, &authenticated_sender) < 0) + { + /* Put back terminator overrides for error message */ value[-1] = '='; name[-1] = ' '; - (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg, - log_msg); + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"invalid data for AUTH"); goto COMMAND_LOOP; - } - } - break; + } + if (!acl_smtp_mailauth) + { + ignore_msg = US"client not authenticated"; + rc = sender_host_authenticated ? OK : FAIL; + } + else + { + ignore_msg = US"rejected by ACL"; + rc = acl_check(ACL_WHERE_MAILAUTH, NULL, acl_smtp_mailauth, + &user_msg, &log_msg); + } + + 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); + + /* 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; + + /* 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; + } + } + break; #ifndef DISABLE_PRDR - case ENV_MAIL_OPT_PRDR: - if (prdr_enable) - prdr_requested = TRUE; - break; + case ENV_MAIL_OPT_PRDR: + if (prdr_enable) + prdr_requested = TRUE; + break; #endif #ifdef SUPPORT_I18N - case ENV_MAIL_OPT_UTF8: - if (!smtputf8_advertised) - { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"SMTPUTF8 used when not advertised"); - goto COMMAND_LOOP; - } + case ENV_MAIL_OPT_UTF8: + if (!smtputf8_advertised) + { + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"SMTPUTF8 used when not advertised"); + goto COMMAND_LOOP; + } - DEBUG(D_receive) debug_printf("smtputf8 requested\n"); - message_smtputf8 = allow_utf8_domains = TRUE; - if (Ustrncmp(received_protocol, US"utf8", 4) != 0) - { - int old_pool = store_pool; - store_pool = POOL_PERM; - received_protocol = string_sprintf("utf8%s", received_protocol); - store_pool = old_pool; - } - break; + DEBUG(D_receive) debug_printf("smtputf8 requested\n"); + message_smtputf8 = allow_utf8_domains = TRUE; + if (Ustrncmp(received_protocol, US"utf8", 4) != 0) + { + int old_pool = store_pool; + store_pool = POOL_PERM; + received_protocol = string_sprintf("utf8%s", received_protocol); + store_pool = old_pool; + } + break; #endif #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) - case ENV_MAIL_OPT_REQTLS: - { - const uschar * list = value; - int sep = ','; - const uschar * opt; - uschar * r, * t; - - if (!requiretls_advertised) + case ENV_MAIL_OPT_REQTLS: { - done = synprot_error(L_smtp_syntax_error, 555, NULL, - US"unadvertised MAIL option: REQUIRETLS"); - goto COMMAND_LOOP; - } + const uschar * list = value; + int sep = ','; + const uschar * opt; + uschar * r, * t; + + if (!requiretls_advertised) + { + done = synprot_error(L_smtp_syntax_error, 555, NULL, + US"unadvertised MAIL option: REQUIRETLS"); + goto COMMAND_LOOP; + } - DEBUG(D_receive) debug_printf("requiretls requested\n"); - tls_requiretls = REQUIRETLS_MSG; + DEBUG(D_receive) debug_printf("requiretls requested\n"); + tls_requiretls = REQUIRETLS_MSG; - r = string_copy_malloc(received_protocol); - if ((t = Ustrrchr(r, 's'))) *t = 'S'; - received_protocol = r; - } - break; + r = string_copy_malloc(received_protocol); + if ((t = Ustrrchr(r, 's'))) *t = 'S'; + received_protocol = r; + } + break; #endif - /* No valid 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. - An error for a malformed address will occur. */ - case ENV_MAIL_OPT_NULL: - value[-1] = '='; - name[-1] = ' '; - arg_error = TRUE; - break; + /* No valid 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. + An error for a malformed address will occur. */ + case ENV_MAIL_OPT_NULL: + value[-1] = '='; + name[-1] = ' '; + arg_error = TRUE; + break; - default: assert(0); - } - /* Break out of for loop if switch() had bad argument or - when start of the email address is reached */ - if (arg_error) break; - } + default: assert(0); + } + /* Break out of for loop if switch() had bad argument or + when start of the email address is reached */ + if (arg_error) break; + } #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) - if (tls_requiretls & REQUIRETLS_MSG) - { - /* Ensure headers-only bounces whether a RET option was given or not. */ + if (tls_requiretls & REQUIRETLS_MSG) + { + /* Ensure headers-only bounces whether a RET option was given or not. */ - DEBUG(D_receive) if (dsn_ret == dsn_ret_full) - debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n"); - dsn_ret = dsn_ret_hdrs; - } + DEBUG(D_receive) if (dsn_ret == dsn_ret_full) + debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n"); + dsn_ret = dsn_ret_hdrs; + } #endif - /* If we have passed the threshold for rate limiting, apply the current - delay, and update it for next time, provided this is a limited host. */ - - if (smtp_mailcmd_count > smtp_rlm_threshold && - verify_check_host(&smtp_ratelimit_hosts) == OK) - { - DEBUG(D_receive) debug_printf("rate limit MAIL: delay %.3g sec\n", - smtp_delay_mail/1000.0); - millisleep((int)smtp_delay_mail); - smtp_delay_mail *= smtp_rlm_factor; - if (smtp_delay_mail > (double)smtp_rlm_limit) - smtp_delay_mail = (double)smtp_rlm_limit; - } + /* If we have passed the threshold for rate limiting, apply the current + delay, and update it for next time, provided this is a limited host. */ - /* Now extract the address, first applying any SMTP-time rewriting. The - TRUE flag allows "<>" as a sender address. */ + if (smtp_mailcmd_count > smtp_rlm_threshold && + verify_check_host(&smtp_ratelimit_hosts) == OK) + { + DEBUG(D_receive) debug_printf("rate limit MAIL: delay %.3g sec\n", + smtp_delay_mail/1000.0); + millisleep((int)smtp_delay_mail); + smtp_delay_mail *= smtp_rlm_factor; + if (smtp_delay_mail > (double)smtp_rlm_limit) + smtp_delay_mail = (double)smtp_rlm_limit; + } - raw_sender = rewrite_existflags & rewrite_smtp - ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", - global_rewrite_rules) - : smtp_cmd_data; + /* Now extract the address, first applying any SMTP-time rewriting. The + TRUE flag allows "<>" as a sender address. */ - raw_sender = - parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain, - TRUE); + raw_sender = rewrite_existflags & rewrite_smtp + ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + global_rewrite_rules) + : smtp_cmd_data; - if (!raw_sender) - { - done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess); - break; - } + raw_sender = + parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain, + TRUE); - sender_address = raw_sender; + if (!raw_sender) + { + done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess); + break; + } - /* If there is a configured size limit for mail, check that this message - doesn't exceed it. The check is postponed to this point so that the sender - can be logged. */ + sender_address = raw_sender; - if (thismessage_size_limit > 0 && message_size > thismessage_size_limit) - { - smtp_printf("552 Message size exceeds maximum permitted\r\n", FALSE); - log_write(L_size_reject, - LOG_MAIN|LOG_REJECT, "rejected MAIL FROM:<%s> %s: " - "message too big: size%s=%d max=%d", - sender_address, - host_and_ident(TRUE), - (message_size == INT_MAX)? ">" : "", - message_size, - thismessage_size_limit); - sender_address = NULL; - break; - } + /* If there is a configured size limit for mail, check that this message + doesn't exceed it. The check is postponed to this point so that the sender + can be logged. */ - /* Check there is enough space on the disk unless configured not to. - When smtp_check_spool_space is set, the check is for thismessage_size_limit - plus the current message - i.e. we accept the message only if it won't - reduce the space below the threshold. Add 5000 to the size to allow for - overheads such as the Received: line and storing of recipients, etc. - By putting the check here, even when SIZE is not given, it allow VRFY - and EXPN etc. to be used when space is short. */ - - if (!receive_check_fs( - (smtp_check_spool_space && message_size >= 0)? - message_size + 5000 : 0)) - { - smtp_printf("452 Space shortage, please try later\r\n", FALSE); - sender_address = NULL; - break; - } + if (thismessage_size_limit > 0 && message_size > thismessage_size_limit) + { + smtp_printf("552 Message size exceeds maximum permitted\r\n", FALSE); + log_write(L_size_reject, + LOG_MAIN|LOG_REJECT, "rejected MAIL FROM:<%s> %s: " + "message too big: size%s=%d max=%d", + sender_address, + host_and_ident(TRUE), + (message_size == INT_MAX)? ">" : "", + message_size, + thismessage_size_limit); + sender_address = NULL; + break; + } - /* If sender_address is unqualified, reject it, unless this is a locally - generated message, or the sending host or net is permitted to send - unqualified addresses - typically local machines behaving as MUAs - - in which case just qualify the address. The flag is set above at the start - of the SMTP connection. */ + /* Check there is enough space on the disk unless configured not to. + When smtp_check_spool_space is set, the check is for thismessage_size_limit + plus the current message - i.e. we accept the message only if it won't + reduce the space below the threshold. Add 5000 to the size to allow for + overheads such as the Received: line and storing of recipients, etc. + By putting the check here, even when SIZE is not given, it allow VRFY + and EXPN etc. to be used when space is short. */ + + if (!receive_check_fs( + (smtp_check_spool_space && message_size >= 0)? + message_size + 5000 : 0)) + { + smtp_printf("452 Space shortage, please try later\r\n", FALSE); + sender_address = NULL; + break; + } - if (!sender_domain && *sender_address) - if (allow_unqualified_sender) - { - sender_domain = Ustrlen(sender_address) + 1; - sender_address = rewrite_address_qualify(sender_address, FALSE); - DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", - raw_sender); - } - else - { - smtp_printf("501 %s: sender address must contain a domain\r\n", FALSE, - smtp_cmd_data); - log_write(L_smtp_syntax_error, - LOG_MAIN|LOG_REJECT, - "unqualified sender rejected: <%s> %s%s", - raw_sender, - host_and_ident(TRUE), - host_lookup_msg); - sender_address = NULL; - break; - } + /* If sender_address is unqualified, reject it, unless this is a locally + generated message, or the sending host or net is permitted to send + unqualified addresses - typically local machines behaving as MUAs - + in which case just qualify the address. The flag is set above at the start + of the SMTP connection. */ - /* Apply an ACL check if one is defined, before responding. Afterwards, - when pipelining is not advertised, do another sync check in case the ACL - delayed and the client started sending in the meantime. */ + if (!sender_domain && *sender_address) + if (allow_unqualified_sender) + { + sender_domain = Ustrlen(sender_address) + 1; + sender_address = rewrite_address_qualify(sender_address, FALSE); + DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", + raw_sender); + } + else + { + smtp_printf("501 %s: sender address must contain a domain\r\n", FALSE, + smtp_cmd_data); + log_write(L_smtp_syntax_error, + LOG_MAIN|LOG_REJECT, + "unqualified sender rejected: <%s> %s%s", + raw_sender, + host_and_ident(TRUE), + host_lookup_msg); + sender_address = NULL; + break; + } - if (acl_smtp_mail) - { - rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg); - if (rc == OK && !smtp_in_pipelining_advertised && !check_sync()) - goto SYNC_FAILURE; - } - else - rc = OK; + /* Apply an ACL check if one is defined, before responding. Afterwards, + when pipelining is not advertised, do another sync check in case the ACL + delayed and the client started sending in the meantime. */ - if (rc == OK || rc == DISCARD) - { - BOOL more = pipeline_response(); + if (acl_smtp_mail) + { + rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg); + if (rc == OK && !smtp_in_pipelining_advertised && !check_sync()) + goto SYNC_FAILURE; + } + else + rc = OK; - if (!user_msg) - smtp_printf("%s%s%s", more, US"250 OK", - #ifndef DISABLE_PRDR - prdr_requested ? US", PRDR Requested" : US"", - #else - US"", - #endif - US"\r\n"); + if (rc == OK || rc == DISCARD) + { + BOOL more = pipeline_response(); + + if (!user_msg) + smtp_printf("%s%s%s", more, US"250 OK", + #ifndef DISABLE_PRDR + prdr_requested ? US", PRDR Requested" : US"", + #else + US"", + #endif + US"\r\n"); + else + { + #ifndef DISABLE_PRDR + if (prdr_requested) + user_msg = string_sprintf("%s%s", user_msg, US", PRDR Requested"); + #endif + smtp_user_msg(US"250", user_msg); + } + smtp_delay_rcpt = smtp_rlr_base; + recipients_discarded = (rc == DISCARD); + was_rej_mail = FALSE; + } else - { - #ifndef DISABLE_PRDR - if (prdr_requested) - user_msg = string_sprintf("%s%s", user_msg, US", PRDR Requested"); - #endif - smtp_user_msg(US"250", user_msg); - } - smtp_delay_rcpt = smtp_rlr_base; - recipients_discarded = (rc == DISCARD); - was_rej_mail = FALSE; - } - else - { - done = smtp_handle_acl_fail(ACL_WHERE_MAIL, rc, user_msg, log_msg); - sender_address = NULL; - } - break; + { + done = smtp_handle_acl_fail(ACL_WHERE_MAIL, rc, user_msg, log_msg); + sender_address = NULL; + } + break; /* The RCPT command requires an address as an operand. There may be any @@ -4916,259 +4916,259 @@ while (done <= 0) are not used, as we keep only the extracted address. */ case RCPT_CMD: - HAD(SCH_RCPT); - rcpt_count++; - was_rcpt = rcpt_in_progress = TRUE; + HAD(SCH_RCPT); + rcpt_count++; + was_rcpt = rcpt_in_progress = TRUE; - /* There must be a sender address; if the sender was rejected and - pipelining was advertised, we assume the client was pipelining, and do not - count this as a protocol error. Reset was_rej_mail so that further RCPTs - get the same treatment. */ + /* There must be a sender address; if the sender was rejected and + pipelining was advertised, we assume the client was pipelining, and do not + count this as a protocol error. Reset was_rej_mail so that further RCPTs + get the same treatment. */ - if (sender_address == NULL) - { - if (smtp_in_pipelining_advertised && last_was_rej_mail) - { - smtp_printf("503 sender not yet given\r\n", FALSE); - was_rej_mail = TRUE; - } - else - { - done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"sender not yet given"); - was_rcpt = FALSE; /* Not a valid RCPT */ - } - rcpt_fail_count++; - break; - } + if (sender_address == NULL) + { + if (smtp_in_pipelining_advertised && last_was_rej_mail) + { + smtp_printf("503 sender not yet given\r\n", FALSE); + was_rej_mail = TRUE; + } + else + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"sender not yet given"); + was_rcpt = FALSE; /* Not a valid RCPT */ + } + rcpt_fail_count++; + break; + } - /* Check for an operand */ + /* Check for an operand */ - if (smtp_cmd_data[0] == 0) - { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"RCPT must have an address operand"); - rcpt_fail_count++; - break; - } + if (smtp_cmd_data[0] == 0) + { + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"RCPT must have an address operand"); + rcpt_fail_count++; + break; + } - /* Set the DSN flags orcpt and dsn_flags from the session*/ - orcpt = NULL; - flags = 0; + /* Set the DSN flags orcpt and dsn_flags from the session*/ + orcpt = NULL; + flags = 0; - if (esmtp) for(;;) - { - uschar *name, *value; + if (esmtp) for(;;) + { + uschar *name, *value; - if (!extract_option(&name, &value)) - break; + if (!extract_option(&name, &value)) + break; - if (dsn_advertised && strcmpic(name, US"ORCPT") == 0) - { - /* Check whether orcpt has been already set */ - if (orcpt) + if (dsn_advertised && strcmpic(name, US"ORCPT") == 0) { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"ORCPT can be specified once only"); - goto COMMAND_LOOP; - } - orcpt = string_copy(value); - DEBUG(D_receive) debug_printf("DSN orcpt: %s\n", orcpt); - } + /* Check whether orcpt has been already set */ + if (orcpt) + { + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"ORCPT can be specified once only"); + goto COMMAND_LOOP; + } + orcpt = string_copy(value); + DEBUG(D_receive) debug_printf("DSN orcpt: %s\n", orcpt); + } - else if (dsn_advertised && strcmpic(name, US"NOTIFY") == 0) - { - /* Check if the notify flags have been already set */ - if (flags > 0) + else if (dsn_advertised && strcmpic(name, US"NOTIFY") == 0) { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"NOTIFY can be specified once only"); - goto COMMAND_LOOP; - } - if (strcmpic(value, US"NEVER") == 0) - flags |= rf_notify_never; - else - { - uschar *p = value; - while (*p != 0) - { - uschar *pp = p; - while (*pp != 0 && *pp != ',') pp++; - if (*pp == ',') *pp++ = 0; - if (strcmpic(p, US"SUCCESS") == 0) - { - DEBUG(D_receive) debug_printf("DSN: Setting notify success\n"); - flags |= rf_notify_success; - } - else if (strcmpic(p, US"FAILURE") == 0) - { - DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n"); - flags |= rf_notify_failure; - } - else if (strcmpic(p, US"DELAY") == 0) - { - DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n"); - flags |= rf_notify_delay; - } - else + /* Check if the notify flags have been already set */ + if (flags > 0) + { + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"NOTIFY can be specified once only"); + goto COMMAND_LOOP; + } + if (strcmpic(value, US"NEVER") == 0) + flags |= rf_notify_never; + else + { + uschar *p = value; + while (*p != 0) { - /* Catch any strange values */ - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"Invalid value for NOTIFY parameter"); - goto COMMAND_LOOP; - } - p = pp; - } - DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags); - } - } + uschar *pp = p; + while (*pp != 0 && *pp != ',') pp++; + if (*pp == ',') *pp++ = 0; + if (strcmpic(p, US"SUCCESS") == 0) + { + DEBUG(D_receive) debug_printf("DSN: Setting notify success\n"); + flags |= rf_notify_success; + } + else if (strcmpic(p, US"FAILURE") == 0) + { + DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n"); + flags |= rf_notify_failure; + } + else if (strcmpic(p, US"DELAY") == 0) + { + DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n"); + flags |= rf_notify_delay; + } + else + { + /* Catch any strange values */ + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"Invalid value for NOTIFY parameter"); + goto COMMAND_LOOP; + } + p = pp; + } + DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags); + } + } - /* Unknown option. Stick back the terminator characters and break - the loop. An error for a malformed address will occur. */ + /* Unknown option. Stick back the terminator characters and break + the loop. An error for a malformed address will occur. */ - else - { - DEBUG(D_receive) debug_printf("Invalid RCPT option: %s : %s\n", name, value); - name[-1] = ' '; - value[-1] = '='; - break; - } - } + else + { + DEBUG(D_receive) debug_printf("Invalid RCPT option: %s : %s\n", name, value); + name[-1] = ' '; + value[-1] = '='; + break; + } + } - /* Apply SMTP rewriting then extract the working address. Don't allow "<>" - as a recipient address */ + /* Apply SMTP rewriting then extract the working address. Don't allow "<>" + as a recipient address */ - recipient = rewrite_existflags & rewrite_smtp - ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", - global_rewrite_rules) - : smtp_cmd_data; + recipient = rewrite_existflags & rewrite_smtp + ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + global_rewrite_rules) + : smtp_cmd_data; - if (!(recipient = parse_extract_address(recipient, &errmess, &start, &end, - &recipient_domain, FALSE))) - { - done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess); - rcpt_fail_count++; - break; - } + if (!(recipient = parse_extract_address(recipient, &errmess, &start, &end, + &recipient_domain, FALSE))) + { + done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess); + rcpt_fail_count++; + break; + } - /* If the recipient address is unqualified, reject it, unless this is a - locally generated message. However, unqualified addresses are permitted - from a configured list of hosts and nets - typically when behaving as - MUAs rather than MTAs. Sad that SMTP is used for both types of traffic, - really. The flag is set at the start of the SMTP connection. + /* If the recipient address is unqualified, reject it, unless this is a + locally generated message. However, unqualified addresses are permitted + from a configured list of hosts and nets - typically when behaving as + MUAs rather than MTAs. Sad that SMTP is used for both types of traffic, + really. The flag is set at the start of the SMTP connection. - RFC 1123 talks about supporting "the reserved mailbox postmaster"; I always - assumed this meant "reserved local part", but the revision of RFC 821 and - friends now makes it absolutely clear that it means *mailbox*. Consequently - we must always qualify this address, regardless. */ + RFC 1123 talks about supporting "the reserved mailbox postmaster"; I always + assumed this meant "reserved local part", but the revision of RFC 821 and + friends now makes it absolutely clear that it means *mailbox*. Consequently + we must always qualify this address, regardless. */ - if (!recipient_domain) - if (!(recipient_domain = qualify_recipient(&recipient, smtp_cmd_data, - US"recipient"))) - { - rcpt_fail_count++; - break; - } + if (!recipient_domain) + if (!(recipient_domain = qualify_recipient(&recipient, smtp_cmd_data, + US"recipient"))) + { + rcpt_fail_count++; + break; + } - /* Check maximum allowed */ + /* Check maximum allowed */ - if (rcpt_count > recipients_max && recipients_max > 0) - { - if (recipients_max_reject) - { - rcpt_fail_count++; - smtp_printf("552 too many recipients\r\n", FALSE); - if (!toomany) - log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: message " - "rejected: sender=<%s> %s", sender_address, host_and_ident(TRUE)); - } - else - { - rcpt_defer_count++; - smtp_printf("452 too many recipients\r\n", FALSE); - if (!toomany) - log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: excess " - "temporarily rejected: sender=<%s> %s", sender_address, - host_and_ident(TRUE)); - } + if (rcpt_count > recipients_max && recipients_max > 0) + { + if (recipients_max_reject) + { + rcpt_fail_count++; + smtp_printf("552 too many recipients\r\n", FALSE); + if (!toomany) + log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: message " + "rejected: sender=<%s> %s", sender_address, host_and_ident(TRUE)); + } + else + { + rcpt_defer_count++; + smtp_printf("452 too many recipients\r\n", FALSE); + if (!toomany) + log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: excess " + "temporarily rejected: sender=<%s> %s", sender_address, + host_and_ident(TRUE)); + } - toomany = TRUE; - break; - } + toomany = TRUE; + break; + } - /* If we have passed the threshold for rate limiting, apply the current - delay, and update it for next time, provided this is a limited host. */ + /* If we have passed the threshold for rate limiting, apply the current + delay, and update it for next time, provided this is a limited host. */ - if (rcpt_count > smtp_rlr_threshold && - verify_check_host(&smtp_ratelimit_hosts) == OK) - { - DEBUG(D_receive) debug_printf("rate limit RCPT: delay %.3g sec\n", - smtp_delay_rcpt/1000.0); - millisleep((int)smtp_delay_rcpt); - smtp_delay_rcpt *= smtp_rlr_factor; - if (smtp_delay_rcpt > (double)smtp_rlr_limit) - smtp_delay_rcpt = (double)smtp_rlr_limit; - } + if (rcpt_count > smtp_rlr_threshold && + verify_check_host(&smtp_ratelimit_hosts) == OK) + { + DEBUG(D_receive) debug_printf("rate limit RCPT: delay %.3g sec\n", + smtp_delay_rcpt/1000.0); + millisleep((int)smtp_delay_rcpt); + smtp_delay_rcpt *= smtp_rlr_factor; + if (smtp_delay_rcpt > (double)smtp_rlr_limit) + smtp_delay_rcpt = (double)smtp_rlr_limit; + } - /* If the MAIL ACL discarded all the recipients, we bypass ACL checking - for them. Otherwise, check the access control list for this recipient. As - there may be a delay in this, re-check for a synchronization error - afterwards, unless pipelining was advertised. */ + /* If the MAIL ACL discarded all the recipients, we bypass ACL checking + for them. Otherwise, check the access control list for this recipient. As + there may be a delay in this, re-check for a synchronization error + afterwards, unless pipelining was advertised. */ - if (recipients_discarded) - rc = DISCARD; - else - if ( (rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg, - &log_msg)) == OK - && !smtp_in_pipelining_advertised && !check_sync()) - goto SYNC_FAILURE; + if (recipients_discarded) + rc = DISCARD; + else + if ( (rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg, + &log_msg)) == OK + && !smtp_in_pipelining_advertised && !check_sync()) + goto SYNC_FAILURE; - /* The ACL was happy */ + /* The ACL was happy */ - if (rc == OK) - { - BOOL more = pipeline_response(); + if (rc == OK) + { + BOOL more = pipeline_response(); - if (user_msg) - smtp_user_msg(US"250", user_msg); - else - smtp_printf("250 Accepted\r\n", more); - receive_add_recipient(recipient, -1); + if (user_msg) + smtp_user_msg(US"250", user_msg); + else + smtp_printf("250 Accepted\r\n", more); + receive_add_recipient(recipient, -1); - /* Set the dsn flags in the recipients_list */ - recipients_list[recipients_count-1].orcpt = orcpt; - recipients_list[recipients_count-1].dsn_flags = flags; + /* Set the dsn flags in the recipients_list */ + recipients_list[recipients_count-1].orcpt = orcpt; + recipients_list[recipients_count-1].dsn_flags = flags; - DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", - recipients_list[recipients_count-1].orcpt, - recipients_list[recipients_count-1].dsn_flags); - } + 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 */ + /* The recipient was discarded */ - else if (rc == DISCARD) - { - if (user_msg) - smtp_user_msg(US"250", user_msg); - else - smtp_printf("250 Accepted\r\n", FALSE); - rcpt_fail_count++; - discarded = TRUE; - 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? sender_address_unrewritten : sender_address, - smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT", - log_msg ? US": " : US"", log_msg ? log_msg : US""); - } + else if (rc == DISCARD) + { + if (user_msg) + smtp_user_msg(US"250", user_msg); + else + smtp_printf("250 Accepted\r\n", FALSE); + rcpt_fail_count++; + discarded = TRUE; + 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? sender_address_unrewritten : sender_address, + smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT", + log_msg ? US": " : US"", log_msg ? log_msg : US""); + } - /* Either the ACL failed the address, or it was deferred. */ + /* Either the ACL failed the address, or it was deferred. */ - else - { - if (rc == FAIL) rcpt_fail_count++; else rcpt_defer_count++; - done = smtp_handle_acl_fail(ACL_WHERE_RCPT, rc, user_msg, log_msg); - } - break; + else + { + if (rc == FAIL) rcpt_fail_count++; else rcpt_defer_count++; + done = smtp_handle_acl_fail(ACL_WHERE_RCPT, rc, user_msg, log_msg); + } + break; /* The DATA command is legal only if it follows successful MAIL FROM @@ -5192,10 +5192,10 @@ while (done <= 0) with the DATA rejection (an idea suggested by Tony Finch). */ case BDAT_CMD: - HAD(SCH_BDAT); { int n; + HAD(SCH_BDAT); if (chunking_state != CHUNKING_OFFERED) { done = synprot_error(L_smtp_protocol_error, 503, NULL, @@ -5233,88 +5233,88 @@ while (done <= 0) } case DATA_CMD: - HAD(SCH_DATA); - dot_ends = TRUE; - - DATA_BDAT: /* Common code for DATA and BDAT */ - if (!discarded && recipients_count <= 0) - { - if (rcpt_smtp_response_same && rcpt_smtp_response != NULL) - { - uschar *code = US"503"; - int len = Ustrlen(rcpt_smtp_response); - smtp_respond(code, 3, FALSE, 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); - } - if (smtp_in_pipelining_advertised && last_was_rcpt) - smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE, - smtp_names[smtp_connection_had[smtp_ch_index-1]]); - else - done = synprot_error(L_smtp_protocol_error, 503, NULL, - smtp_connection_had[smtp_ch_index-1] == SCH_DATA - ? US"valid RCPT command must precede DATA" - : US"valid RCPT command must precede BDAT"); + HAD(SCH_DATA); + dot_ends = TRUE; - if (chunking_state > CHUNKING_OFFERED) - bdat_flush_data(); - break; - } + DATA_BDAT: /* Common code for DATA and BDAT */ + if (!discarded && recipients_count <= 0) + { + if (rcpt_smtp_response_same && rcpt_smtp_response != NULL) + { + uschar *code = US"503"; + int len = Ustrlen(rcpt_smtp_response); + smtp_respond(code, 3, FALSE, 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); + } + if (smtp_in_pipelining_advertised && last_was_rcpt) + smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE, + smtp_names[smtp_connection_had[smtp_ch_index-1]]); + else + done = synprot_error(L_smtp_protocol_error, 503, NULL, + smtp_connection_had[smtp_ch_index-1] == SCH_DATA + ? US"valid RCPT command must precede DATA" + : US"valid RCPT command must precede BDAT"); - if (toomany && recipients_max_reject) - { - sender_address = NULL; /* This will allow a new MAIL without RSET */ - sender_address_unrewritten = NULL; - smtp_printf("554 Too many recipients\r\n", FALSE); - break; - } + if (chunking_state > CHUNKING_OFFERED) + bdat_flush_data(); + break; + } - if (chunking_state > CHUNKING_OFFERED) - rc = OK; /* 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 (toomany && recipients_max_reject) + { + sender_address = NULL; /* This will allow a new MAIL without RSET */ + sender_address_unrewritten = NULL; + smtp_printf("554 Too many recipients\r\n", FALSE); + break; + } - if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0) - rc = OK; + if (chunking_state > CHUNKING_OFFERED) + rc = OK; /* No predata ACL or go-ahead output for BDAT */ else { - uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept"; - enable_dollar_recipients = TRUE; - rc = acl_check(ACL_WHERE_PREDATA, NULL, acl, &user_msg, - &log_msg); - enable_dollar_recipients = FALSE; - if (rc == OK && !check_sync()) - goto SYNC_FAILURE; + /* 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 (rc != OK) - { /* Either the ACL failed the address, or it was deferred. */ - done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg); - break; + if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0) + rc = OK; + else + { + uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept"; + enable_dollar_recipients = TRUE; + rc = acl_check(ACL_WHERE_PREDATA, NULL, acl, &user_msg, + &log_msg); + enable_dollar_recipients = FALSE; + if (rc == OK && !check_sync()) + goto SYNC_FAILURE; + + if (rc != OK) + { /* Either the ACL failed the address, or it was deferred. */ + done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg); + break; + } } - } - if (user_msg) - smtp_user_msg(US"354", user_msg); - else - smtp_printf( - "354 Enter message, ending with \".\" on a line by itself\r\n", FALSE); - } + if (user_msg) + smtp_user_msg(US"354", user_msg); + else + smtp_printf( + "354 Enter message, ending with \".\" on a line by itself\r\n", FALSE); + } #ifdef TCP_QUICKACK - if (smtp_in) /* all ACKs needed to ramp window up for bulk data */ - (void) setsockopt(fileno(smtp_in), IPPROTO_TCP, TCP_QUICKACK, - US &on, sizeof(on)); + if (smtp_in) /* all ACKs needed to ramp window up for bulk data */ + (void) setsockopt(fileno(smtp_in), IPPROTO_TCP, TCP_QUICKACK, + US &on, sizeof(on)); #endif - done = 3; - message_ended = END_NOTENDED; /* Indicate in middle of data */ + done = 3; + message_ended = END_NOTENDED; /* Indicate in middle of data */ - break; + break; case VRFY_CMD: @@ -5372,173 +5372,173 @@ while (done <= 0) case EXPN_CMD: - HAD(SCH_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); - else - { - BOOL save_log_testing_mode = log_testing_mode; - address_test_mode = log_testing_mode = TRUE; - (void) verify_address(deliver_make_addr(smtp_cmd_data, FALSE), - smtp_out, vopt_is_recipient | vopt_qualify | vopt_expn, -1, -1, -1, - NULL, NULL, NULL); - address_test_mode = FALSE; - log_testing_mode = save_log_testing_mode; /* true for -bh */ - } - break; + HAD(SCH_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); + else + { + BOOL save_log_testing_mode = log_testing_mode; + address_test_mode = log_testing_mode = TRUE; + (void) verify_address(deliver_make_addr(smtp_cmd_data, FALSE), + smtp_out, vopt_is_recipient | vopt_qualify | vopt_expn, -1, -1, -1, + NULL, NULL, NULL); + address_test_mode = FALSE; + log_testing_mode = save_log_testing_mode; /* true for -bh */ + } + break; #ifdef SUPPORT_TLS case STARTTLS_CMD: - HAD(SCH_STARTTLS); - if (!tls_advertised) - { - done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"STARTTLS command used when not advertised"); - break; - } + HAD(SCH_STARTTLS); + if (!tls_advertised) + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"STARTTLS command used when not advertised"); + break; + } - /* Apply an ACL check if one is defined */ + /* Apply an ACL check if one is defined */ - if ( acl_smtp_starttls - && (rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls, - &user_msg, &log_msg)) != OK - ) - { - done = smtp_handle_acl_fail(ACL_WHERE_STARTTLS, rc, user_msg, log_msg); - break; - } + if ( acl_smtp_starttls + && (rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls, + &user_msg, &log_msg)) != OK + ) + { + done = smtp_handle_acl_fail(ACL_WHERE_STARTTLS, rc, user_msg, log_msg); + break; + } - /* RFC 2487 is not clear on when this command may be sent, though it - does state that all information previously obtained from the client - must be discarded if a TLS session is started. It seems reasonable to - do an implied RSET when STARTTLS is received. */ - - incomplete_transaction_log(US"STARTTLS"); - cancel_cutthrough_connection(TRUE, US"STARTTLS received"); - smtp_reset(reset_point); - toomany = FALSE; - cmd_list[CMD_LIST_STARTTLS].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 - when used afterwards; we use segregated input buffers, so are not - vulnerable, but we want to note when it happens and, for sheer paranoia, - ensure that the buffer is "wiped". - Pipelining sync checks will normally have protected us too, unless disabled - by configuration. */ - - if (receive_smtp_buffered()) - { - DEBUG(D_any) - debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n"); - if (tls_in.active.sock < 0) - smtp_inend = smtp_inptr = smtp_inbuffer; - /* and if TLS is already active, tls_server_start() should fail */ - } + /* RFC 2487 is not clear on when this command may be sent, though it + does state that all information previously obtained from the client + must be discarded if a TLS session is started. It seems reasonable to + do an implied RSET when STARTTLS is received. */ - /* There is nothing we value in the input buffer and if TLS is successfully - negotiated, we won't use this buffer again; if TLS fails, we'll just read - fresh content into it. The buffer contains arbitrary content from an - untrusted remote source; eg: NOOP <shellcode>\r\nSTARTTLS\r\n - It seems safest to just wipe away the content rather than leave it as a - target to jump to. */ + incomplete_transaction_log(US"STARTTLS"); + cancel_cutthrough_connection(TRUE, US"STARTTLS received"); + smtp_reset(reset_point); + toomany = FALSE; + cmd_list[CMD_LIST_STARTTLS].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 + when used afterwards; we use segregated input buffers, so are not + vulnerable, but we want to note when it happens and, for sheer paranoia, + ensure that the buffer is "wiped". + Pipelining sync checks will normally have protected us too, unless disabled + by configuration. */ + + if (receive_smtp_buffered()) + { + DEBUG(D_any) + debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n"); + if (tls_in.active.sock < 0) + smtp_inend = smtp_inptr = smtp_inbuffer; + /* and if TLS is already active, tls_server_start() should fail */ + } - memset(smtp_inbuffer, 0, IN_BUFFER_SIZE); + /* There is nothing we value in the input buffer and if TLS is successfully + negotiated, we won't use this buffer again; if TLS fails, we'll just read + fresh content into it. The buffer contains arbitrary content from an + untrusted remote source; eg: NOOP <shellcode>\r\nSTARTTLS\r\n + It seems safest to just wipe away the content rather than leave it as a + target to jump to. */ - /* Attempt to start up a TLS session, and if successful, discard all - knowledge that was obtained previously. At least, that's what the RFC says, - and that's what happens by default. However, in order to work round YAEB, - there is an option to remember the esmtp state. Sigh. + memset(smtp_inbuffer, 0, IN_BUFFER_SIZE); - We must allow for an extra EHLO command and an extra AUTH command after - STARTTLS that don't add to the nonmail command count. */ + /* Attempt to start up a TLS session, and if successful, discard all + knowledge that was obtained previously. At least, that's what the RFC says, + and that's what happens by default. However, in order to work round YAEB, + there is an option to remember the esmtp state. Sigh. - s = NULL; - if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK) - { - if (!tls_remember_esmtp) - helo_seen = esmtp = auth_advertised = 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; - if (sender_helo_name) - { - store_free(sender_helo_name); - sender_helo_name = NULL; - host_build_sender_fullhost(); /* Rebuild */ - set_process_info("handling incoming TLS connection from %s", - host_and_ident(FALSE)); - } - received_protocol = - (sender_host_address ? protocols : protocols_local) - [ (esmtp - ? pextend + (sender_host_authenticated ? pauthed : 0) - : pnormal) - + (tls_in.active.sock >= 0 ? pcrpted : 0) - ]; + We must allow for an extra EHLO command and an extra AUTH command after + STARTTLS that don't add to the nonmail command count. */ - sender_host_auth_pubname = sender_host_authenticated = NULL; - authenticated_id = NULL; - sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING; - DEBUG(D_tls) debug_printf("TLS active\n"); - break; /* Successful STARTTLS */ - } - else - (void) smtp_log_tls_fail(s); + s = NULL; + if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK) + { + if (!tls_remember_esmtp) + helo_seen = esmtp = auth_advertised = 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; + if (sender_helo_name) + { + store_free(sender_helo_name); + sender_helo_name = NULL; + host_build_sender_fullhost(); /* Rebuild */ + set_process_info("handling incoming TLS connection from %s", + host_and_ident(FALSE)); + } + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active.sock >= 0 ? pcrpted : 0) + ]; + + sender_host_auth_pubname = sender_host_authenticated = NULL; + authenticated_id = NULL; + sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING; + DEBUG(D_tls) debug_printf("TLS active\n"); + break; /* Successful STARTTLS */ + } + else + (void) smtp_log_tls_fail(s); - /* Some local configuration problem was discovered before actually trying - to do a TLS handshake; give a temporary error. */ + /* Some local configuration problem was discovered before actually trying + to do a TLS handshake; give a temporary error. */ - if (rc == DEFER) - { - smtp_printf("454 TLS currently unavailable\r\n", FALSE); - break; - } + if (rc == DEFER) + { + smtp_printf("454 TLS currently unavailable\r\n", FALSE); + break; + } - /* Hard failure. Reject everything except QUIT or closed connection. One - cause for failure is a nested STARTTLS, in which case tls_in.active remains - set, but we must still reject all incoming commands. */ + /* Hard failure. Reject everything except QUIT or closed connection. One + cause for failure is a nested STARTTLS, in which case tls_in.active remains + set, but we must still reject all incoming commands. */ - DEBUG(D_tls) debug_printf("TLS failed to start\n"); - 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()); - smtp_notquit_exit(US"tls-failed", NULL, NULL); - done = 2; - break; + DEBUG(D_tls) debug_printf("TLS failed to start\n"); + 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()); + smtp_notquit_exit(US"tls-failed", NULL, NULL); + done = 2; + break; - /* It is perhaps arguable as to which exit ACL should be called here, - but as it is probably a situation that almost never arises, it - probably doesn't matter. We choose to call the real QUIT ACL, which in - some sense is perhaps "right". */ - - case QUIT_CMD: - user_msg = NULL; - 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); - 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()); - done = 2; - break; + /* It is perhaps arguable as to which exit ACL should be called here, + but as it is probably a situation that almost never arises, it + probably doesn't matter. We choose to call the real QUIT ACL, which in + some sense is perhaps "right". */ + + case QUIT_CMD: + user_msg = NULL; + 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); + 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()); + done = 2; + break; - default: - smtp_printf("554 Security failure\r\n", FALSE); - break; - } - tls_close(NULL, TLS_SHUTDOWN_NOWAIT); - break; + default: + smtp_printf("554 Security failure\r\n", FALSE); + break; + } + tls_close(NULL, TLS_SHUTDOWN_NOWAIT); + break; #endif @@ -5547,23 +5547,23 @@ while (done <= 0) message. */ case QUIT_CMD: - smtp_quit_handler(&user_msg, &log_msg); - done = 2; - break; + smtp_quit_handler(&user_msg, &log_msg); + done = 2; + break; case RSET_CMD: - smtp_rset_handler(); - cancel_cutthrough_connection(TRUE, US"RSET received"); - smtp_reset(reset_point); - toomany = FALSE; - break; + smtp_rset_handler(); + cancel_cutthrough_connection(TRUE, US"RSET received"); + smtp_reset(reset_point); + toomany = FALSE; + break; case NOOP_CMD: - HAD(SCH_NOOP); - smtp_printf("250 OK\r\n", FALSE); - break; + HAD(SCH_NOOP); + smtp_printf("250 OK\r\n", FALSE); + break; /* Show ETRN/EXPN/VRFY if there's an ACL for checking hosts; if actually @@ -5572,287 +5572,288 @@ while (done <= 0) response. */ case HELP_CMD: - HAD(SCH_HELP); - smtp_printf("214-Commands supported:\r\n", TRUE); - { - uschar buffer[256]; - buffer[0] = 0; - Ustrcat(buffer, " AUTH"); - #ifdef SUPPORT_TLS - if (tls_in.active.sock < 0 && - verify_check_host(&tls_advertise_hosts) != FAIL) - Ustrcat(buffer, " STARTTLS"); - #endif - Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT"); - Ustrcat(buffer, " NOOP QUIT RSET HELP"); - if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN"); - if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN"); - if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY"); - smtp_printf("214%s\r\n", FALSE, buffer); - } - break; + HAD(SCH_HELP); + smtp_printf("214-Commands supported:\r\n", TRUE); + { + uschar buffer[256]; + buffer[0] = 0; + Ustrcat(buffer, " AUTH"); + #ifdef SUPPORT_TLS + if (tls_in.active.sock < 0 && + verify_check_host(&tls_advertise_hosts) != FAIL) + Ustrcat(buffer, " STARTTLS"); + #endif + Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT"); + Ustrcat(buffer, " NOOP QUIT RSET HELP"); + if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN"); + if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN"); + if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY"); + smtp_printf("214%s\r\n", FALSE, buffer); + } + break; case EOF_CMD: - incomplete_transaction_log(US"connection lost"); - smtp_notquit_exit(US"connection-lost", US"421", - US"%s lost input connection", smtp_active_hostname); - - /* Don't log by default unless in the middle of a message, as some mailers - just drop the call rather than sending QUIT, and it clutters up the logs. - */ - - if (sender_address || recipients_count > 0) - log_write(L_lost_incoming_connection, LOG_MAIN, - "unexpected %s while reading SMTP command from %s%s%s D=%s", - sender_host_unknown ? "EOF" : "disconnection", - tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"", - host_and_ident(FALSE), smtp_read_error, - string_timesince(&smtp_connection_start) - ); + incomplete_transaction_log(US"connection lost"); + smtp_notquit_exit(US"connection-lost", US"421", + US"%s lost input connection", smtp_active_hostname); + + /* Don't log by default unless in the middle of a message, as some mailers + just drop the call rather than sending QUIT, and it clutters up the logs. + */ + + if (sender_address || recipients_count > 0) + log_write(L_lost_incoming_connection, LOG_MAIN, + "unexpected %s while reading SMTP command from %s%s%s D=%s", + sender_host_unknown ? "EOF" : "disconnection", + tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"", + host_and_ident(FALSE), smtp_read_error, + string_timesince(&smtp_connection_start) + ); - else - log_write(L_smtp_connection, LOG_MAIN, "%s %slost%s D=%s", - smtp_get_connection_info(), - tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"", - smtp_read_error, - string_timesince(&smtp_connection_start) - ); - - done = 1; - break; + else + log_write(L_smtp_connection, LOG_MAIN, "%s %slost%s D=%s", + smtp_get_connection_info(), + tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"", + smtp_read_error, + string_timesince(&smtp_connection_start) + ); + + done = 1; + break; case ETRN_CMD: - HAD(SCH_ETRN); - if (sender_address) - { - done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"ETRN is not permitted inside a transaction"); - break; - } + HAD(SCH_ETRN); + if (sender_address) + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"ETRN is not permitted inside a transaction"); + break; + } - log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_cmd_argument, - host_and_ident(FALSE)); + log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_cmd_argument, + host_and_ident(FALSE)); - if ((rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, - &user_msg, &log_msg)) != OK) - { - done = smtp_handle_acl_fail(ACL_WHERE_ETRN, rc, user_msg, log_msg); - break; - } + if ((rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, + &user_msg, &log_msg)) != OK) + { + done = smtp_handle_acl_fail(ACL_WHERE_ETRN, rc, user_msg, log_msg); + break; + } - /* Compute the serialization key for this command. */ + /* Compute the serialization key for this command. */ - etrn_serialize_key = string_sprintf("etrn-%s\n", smtp_cmd_data); + etrn_serialize_key = string_sprintf("etrn-%s\n", smtp_cmd_data); - /* If a command has been specified for running as a result of ETRN, we - permit any argument to ETRN. If not, only the # standard form is permitted, - since that is strictly the only kind of ETRN that can be implemented - according to the RFC. */ + /* If a command has been specified for running as a result of ETRN, we + permit any argument to ETRN. If not, only the # standard form is permitted, + since that is strictly the only kind of ETRN that can be implemented + according to the RFC. */ - 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, - 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); - break; - } - } + 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, + 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); + break; + } + } - /* Else set up to call Exim with the -R option. */ + /* Else set up to call Exim with the -R option. */ - else - { - if (*smtp_cmd_data++ != '#') - { - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"argument must begin with #"); - break; - } - etrn_command = US"exim -R"; - argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, - *queue_name ? 4 : 2, - US"-R", smtp_cmd_data, - US"-MCG", queue_name); - } + else + { + if (*smtp_cmd_data++ != '#') + { + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"argument must begin with #"); + break; + } + etrn_command = US"exim -R"; + argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, + *queue_name ? 4 : 2, + US"-R", smtp_cmd_data, + US"-MCG", queue_name); + } - /* If we are host-testing, don't actually do anything. */ + /* If we are host-testing, don't actually do anything. */ - if (host_checking) - { - HDEBUG(D_any) - { - 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); - else smtp_user_msg(US"250", user_msg); - break; - } + if (host_checking) + { + HDEBUG(D_any) + { + 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); + else smtp_user_msg(US"250", user_msg); + break; + } - /* If ETRN queue runs are to be serialized, check the database to - ensure one isn't already running. */ + /* If ETRN queue runs are to be serialized, check the database to + ensure one isn't already running. */ - if (smtp_etrn_serialize && !enq_start(etrn_serialize_key, 1)) - { - smtp_printf("458 Already processing %s\r\n", FALSE, smtp_cmd_data); - break; - } + if (smtp_etrn_serialize && !enq_start(etrn_serialize_key, 1)) + { + smtp_printf("458 Already processing %s\r\n", FALSE, smtp_cmd_data); + break; + } - /* Fork a child process and run the command. We don't want to have to - wait for the process at any point, so set SIGCHLD to SIG_IGN before - forking. It should be set that way anyway for external incoming SMTP, - but we save and restore to be tidy. If serialization is required, we - actually run the command in yet another process, so we can wait for it - to complete and then remove the serialization lock. */ + /* Fork a child process and run the command. We don't want to have to + wait for the process at any point, so set SIGCHLD to SIG_IGN before + forking. It should be set that way anyway for external incoming SMTP, + but we save and restore to be tidy. If serialization is required, we + actually run the command in yet another process, so we can wait for it + to complete and then remove the serialization lock. */ - oldsignal = signal(SIGCHLD, SIG_IGN); + oldsignal = signal(SIGCHLD, SIG_IGN); - if ((pid = fork()) == 0) - { - smtp_input = FALSE; /* This process is not associated with the */ - (void)fclose(smtp_in); /* SMTP call any more. */ - (void)fclose(smtp_out); + if ((pid = fork()) == 0) + { + smtp_input = FALSE; /* This process is not associated with the */ + (void)fclose(smtp_in); /* SMTP call any more. */ + (void)fclose(smtp_out); - signal(SIGCHLD, SIG_DFL); /* Want to catch child */ + signal(SIGCHLD, SIG_DFL); /* Want to catch child */ - /* If not serializing, do the exec right away. Otherwise, fork down - into another process. */ + /* If not serializing, do the exec right away. Otherwise, fork down + into another process. */ - if (!smtp_etrn_serialize || (pid = fork()) == 0) - { - DEBUG(D_exec) debug_print_argv(argv); - exim_nullstd(); /* Ensure std{in,out,err} exist */ - execv(CS argv[0], (char *const *)argv); - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "exec of \"%s\" (ETRN) failed: %s", - etrn_command, strerror(errno)); - _exit(EXIT_FAILURE); /* paranoia */ - } + if (!smtp_etrn_serialize || (pid = fork()) == 0) + { + DEBUG(D_exec) debug_print_argv(argv); + exim_nullstd(); /* Ensure std{in,out,err} exist */ + execv(CS argv[0], (char *const *)argv); + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "exec of \"%s\" (ETRN) failed: %s", + etrn_command, strerror(errno)); + _exit(EXIT_FAILURE); /* paranoia */ + } - /* Obey this if smtp_serialize and the 2nd fork yielded non-zero. That - is, we are in the first subprocess, after forking again. All we can do - for a failing fork is to log it. Otherwise, wait for the 2nd process to - complete, before removing the serialization. */ + /* Obey this if smtp_serialize and the 2nd fork yielded non-zero. That + is, we are in the first subprocess, after forking again. All we can do + for a failing fork is to log it. Otherwise, wait for the 2nd process to + complete, before removing the serialization. */ - if (pid < 0) - log_write(0, LOG_MAIN|LOG_PANIC, "2nd fork for serialized ETRN " - "failed: %s", strerror(errno)); - else - { - int status; - DEBUG(D_any) debug_printf("waiting for serialized ETRN process %d\n", - (int)pid); - (void)wait(&status); - DEBUG(D_any) debug_printf("serialized ETRN process %d ended\n", - (int)pid); - } + if (pid < 0) + log_write(0, LOG_MAIN|LOG_PANIC, "2nd fork for serialized ETRN " + "failed: %s", strerror(errno)); + else + { + int status; + DEBUG(D_any) debug_printf("waiting for serialized ETRN process %d\n", + (int)pid); + (void)wait(&status); + DEBUG(D_any) debug_printf("serialized ETRN process %d ended\n", + (int)pid); + } - enq_end(etrn_serialize_key); - _exit(EXIT_SUCCESS); - } + enq_end(etrn_serialize_key); + _exit(EXIT_SUCCESS); + } - /* Back in the top level SMTP process. Check that we started a subprocess - and restore the signal state. */ + /* Back in the top level SMTP process. Check that we started a subprocess + and restore the signal state. */ - if (pid < 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); - if (smtp_etrn_serialize) enq_end(etrn_serialize_key); - } - else - { - if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE); - else smtp_user_msg(US"250", user_msg); - } + if (pid < 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); + if (smtp_etrn_serialize) enq_end(etrn_serialize_key); + } + else + { + if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE); + else smtp_user_msg(US"250", user_msg); + } - signal(SIGCHLD, oldsignal); - break; + signal(SIGCHLD, oldsignal); + break; case BADARG_CMD: - done = synprot_error(L_smtp_syntax_error, 501, NULL, - US"unexpected argument data"); - break; + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"unexpected argument data"); + break; /* This currently happens only for NULLs, but could be extended. */ case BADCHAR_CMD: - 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); - break; + 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); + 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\"", - 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; + 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\"", + 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; case TOO_MANY_NONMAIL_CMD: - s = smtp_cmd_buffer; - while (*s != 0 && !isspace(*s)) s++; - incomplete_transaction_log(US"too many non-mail commands"); - log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " - "nonmail commands (last was \"%.*s\")", host_and_ident(FALSE), - (int)(s - smtp_cmd_buffer), smtp_cmd_buffer); - smtp_notquit_exit(US"bad-commands", US"554", US"Too many nonmail commands"); - done = 1; /* Pretend eof - drops connection */ - break; + s = smtp_cmd_buffer; + while (*s != 0 && !isspace(*s)) s++; + incomplete_transaction_log(US"too many non-mail commands"); + log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " + "nonmail commands (last was \"%.*s\")", host_and_ident(FALSE), + (int)(s - smtp_cmd_buffer), smtp_cmd_buffer); + smtp_notquit_exit(US"bad-commands", US"554", US"Too many nonmail commands"); + done = 1; /* Pretend eof - drops connection */ + break; #ifdef SUPPORT_PROXY case PROXY_FAIL_IGNORE_CMD: - smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", FALSE); - break; + smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", FALSE); + break; #endif default: - if (unknown_command_count++ >= smtp_max_unknown_commands) - { - log_write(L_smtp_syntax_error, LOG_MAIN, - "SMTP syntax error in \"%s\" %s %s", - string_printing(smtp_cmd_buffer), host_and_ident(TRUE), - US"unrecognized command"); - incomplete_transaction_log(US"unrecognized command"); - smtp_notquit_exit(US"bad-commands", US"500", - US"Too many unrecognized commands"); - 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), - string_printing(smtp_cmd_buffer)); - } - else - done = synprot_error(L_smtp_syntax_error, 500, NULL, - US"unrecognized command"); - break; + if (unknown_command_count++ >= smtp_max_unknown_commands) + { + log_write(L_smtp_syntax_error, LOG_MAIN, + "SMTP syntax error in \"%s\" %s %s", + string_printing(smtp_cmd_buffer), host_and_ident(TRUE), + US"unrecognized command"); + incomplete_transaction_log(US"unrecognized command"); + smtp_notquit_exit(US"bad-commands", US"500", + US"Too many unrecognized commands"); + 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), + string_printing(smtp_cmd_buffer)); + } + else + done = synprot_error(L_smtp_syntax_error, 500, NULL, + US"unrecognized command"); + break; } /* This label is used by goto's inside loops that want to break out to |