X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/80fea873648ca2ab2e592999a336c59cf054ab55..584e96c65f12aca9414450b656504af6e3f7a399:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 86d566dc9..8994c6edb 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -82,6 +82,15 @@ enum { MAIL_CMD, RCPT_CMD, RSET_CMD, + /* RFC3030 section 2: "After all MAIL and RCPT responses are collected and + processed the message is sent using a series of BDAT commands" + implies that BDAT should be synchronized. However, we see Google, at least, + sending MAIL,RCPT,BDAT-LAST in a single packet, clearly not waiting for + processing of the RPCT response(s). We shall do the same, and not require + synch for BDAT. */ + + BDAT_CMD, + /* This is a dummy to identify the non-sync commands when not pipelining */ NON_SYNC_CMD_NON_PIPELINING, @@ -182,6 +191,7 @@ static smtp_cmd_list cmd_list[] = { { "mail from:", sizeof("mail from:")-1, MAIL_CMD, TRUE, TRUE }, { "rcpt to:", sizeof("rcpt to:")-1, RCPT_CMD, TRUE, TRUE }, { "data", sizeof("data")-1, DATA_CMD, FALSE, TRUE }, + { "bdat", sizeof("bdat")-1, BDAT_CMD, TRUE, TRUE }, { "quit", sizeof("quit")-1, QUIT_CMD, FALSE, TRUE }, { "noop", sizeof("noop")-1, NOOP_CMD, TRUE, FALSE }, { "etrn", sizeof("etrn")-1, ETRN_CMD, TRUE, FALSE }, @@ -205,9 +215,9 @@ It must be kept in step with the SCH_xxx enumerations. */ static uschar *smtp_names[] = { - US"NONE", US"AUTH", US"DATA", US"EHLO", US"ETRN", US"EXPN", US"HELO", - US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", US"STARTTLS", - US"VRFY" }; + US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN", + US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", + US"STARTTLS", US"VRFY" }; static uschar *protocols_local[] = { US"local-smtp", /* HELO */ @@ -289,6 +299,13 @@ static int smtp_had_eof; static int smtp_had_error; +/* forward declarations */ +int bdat_ungetc(int ch); +static int smtp_read_command(BOOL check_sync); +static int synprot_error(int type, int code, uschar *data, uschar *errmess); +static void smtp_quit_handler(uschar **, uschar **); +static void smtp_rset_handler(void); + /************************************************* * SMTP version of getc() * *************************************************/ @@ -335,6 +352,131 @@ if (smtp_inptr >= smtp_inend) return *smtp_inptr++; } +#ifndef DISABLE_DKIM +void +smtp_get_cache(void) +{ +int n = smtp_inend - smtp_inptr; +if (n > 0) + dkim_exim_verify_feed(smtp_inptr, n); +} +#endif + + +/* Get a byte from the smtp input, in CHUNKING mode. Handle ack of the +previous BDAT chunk and getting new ones when we run out. Uses the +underlying smtp_getc or tls_getc both for that and for getting the +(buffered) data byte. EOD signals (an expected) no further data. +ERR signals a protocol error, and EOF a closed input stream. + +Called from read_bdat_smtp() in receive.c for the message body, but also +by the headers read loop in receive_msg(); manipulates chunking_state +to handle the BDAT command/response. +Placed here due to the correlation with the above smtp_getc(), which it wraps, +and also by the need to do smtp command/response handling. + +Arguments: none +Returns: the next character or ERR, EOD or EOF +*/ + +int +bdat_getc(void) +{ +uschar * user_msg = NULL; +uschar * log_msg; + +for(;;) + { + if (chunking_data_left-- > 0) + return lwr_receive_getc(); + + receive_getc = lwr_receive_getc; + receive_ungetc = lwr_receive_ungetc; + + /* If not the last, ack the received chunk. The last response is delayed + until after the data ACL decides on it */ + + if (chunking_state == CHUNKING_LAST) + { +#ifndef DISABLE_DKIM + dkim_exim_verify_feed(".\r\n", 3); /* for consistency with .-term MAIL */ +#endif + return EOD; + } + + chunking_state = CHUNKING_OFFERED; + smtp_printf("250 %u byte chunk received\r\n", chunking_datasize); + + /* Expect another BDAT cmd from input. RFC 3030 says nothing about + QUIT, RSET or NOOP but handling them seems obvious */ + +next_cmd: + switch(smtp_read_command(TRUE)) + { + default: + (void) synprot_error(L_smtp_protocol_error, 503, NULL, + US"only BDAT permissible after non-LAST BDAT"); + + repeat_until_rset: + switch(smtp_read_command(TRUE)) + { + case QUIT_CMD: smtp_quit_handler(&user_msg, &log_msg); /*FALLTHROUGH */ + case EOF_CMD: return EOF; + case RSET_CMD: smtp_rset_handler(); return ERR; + default: if (synprot_error(L_smtp_protocol_error, 503, NULL, + US"only RSET accepted now") > 0) + return EOF; + goto repeat_until_rset; + } + + case QUIT_CMD: + smtp_quit_handler(&user_msg, &log_msg); + /*FALLTHROUGH*/ + case EOF_CMD: + return EOF; + + case RSET_CMD: + smtp_rset_handler(); + return ERR; + + case NOOP_CMD: + HAD(SCH_NOOP); + smtp_printf("250 OK\r\n"); + goto next_cmd; + + case BDAT_CMD: + { + int n; + + if (sscanf(CS smtp_cmd_data, "%u %n", &chunking_datasize, &n) < 1) + { + (void) synprot_error(L_smtp_protocol_error, 501, NULL, + US"missing size for BDAT command"); + return ERR; + } + chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0 + ? CHUNKING_LAST : CHUNKING_ACTIVE; + chunking_data_left = chunking_datasize; + + if (chunking_datasize == 0) + if (chunking_state == CHUNKING_LAST) + return EOD; + else + { + (void) synprot_error(L_smtp_protocol_error, 504, NULL, + US"zero size for BDAT command"); + goto repeat_until_rset; + } + + receive_getc = bdat_getc; + receive_ungetc = bdat_ungetc; + break; /* to top of main loop */ + } + } + } +} + + /************************************************* @@ -353,11 +495,18 @@ Returns: the character int smtp_ungetc(int ch) { -*(--smtp_inptr) = ch; +*--smtp_inptr = ch; return ch; } +int +bdat_ungetc(int ch) +{ +chunking_data_left++; +return lwr_receive_ungetc(ch); +} + /************************************************* @@ -1520,14 +1669,6 @@ sender_verified_list = NULL; /* No senders verified */ memset(sender_address_cache, 0, sizeof(sender_address_cache)); memset(sender_domain_cache, 0, sizeof(sender_domain_cache)); -#ifndef DISABLE_PRDR -prdr_requested = FALSE; -#endif - -/* Reset the DSN flags */ -dsn_ret = 0; -dsn_envid = NULL; - authenticated_sender = NULL; #ifdef EXPERIMENTAL_BRIGHTMAIL bmi_run = 0; @@ -1538,6 +1679,11 @@ dkim_signers = NULL; dkim_disable_verify = FALSE; dkim_collect_input = FALSE; #endif +dsn_ret = 0; +dsn_envid = NULL; +#ifndef DISABLE_PRDR +prdr_requested = FALSE; +#endif #ifdef EXPERIMENTAL_SPF spf_header_comment = NULL; spf_received = NULL; @@ -1729,16 +1875,15 @@ while (done <= 0) /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a recipient address */ - recipient = ((rewrite_existflags & rewrite_smtp) != 0)? - 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; - /* rfc821_domains = TRUE; << no longer needed */ recipient = parse_extract_address(recipient, &errmess, &start, &end, &recipient_domain, FALSE); - /* rfc821_domains = FALSE; << no longer needed */ - if (recipient == NULL) + if (!recipient) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess); @@ -1919,6 +2064,7 @@ smtp_inbuffer = (uschar *)malloc(in_buffer_size); if (smtp_inbuffer == NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP input buffer"); receive_getc = smtp_getc; +receive_get_cache = smtp_get_cache; receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; @@ -2394,20 +2540,20 @@ do /* At least once, in case we have an empty string */ { int len; uschar *linebreak = Ustrchr(p, '\n'); - ss = string_cat(ss, &size, &ptr, code, 3); + ss = string_catn(ss, &size, &ptr, code, 3); if (linebreak == NULL) { len = Ustrlen(p); - ss = string_cat(ss, &size, &ptr, US" ", 1); + ss = string_catn(ss, &size, &ptr, US" ", 1); } else { len = linebreak - p; - ss = string_cat(ss, &size, &ptr, US"-", 1); + ss = string_catn(ss, &size, &ptr, US"-", 1); } - ss = string_cat(ss, &size, &ptr, esc, esclen); - ss = string_cat(ss, &size, &ptr, p, len); - ss = string_cat(ss, &size, &ptr, US"\r\n", 2); + ss = string_catn(ss, &size, &ptr, esc, esclen); + ss = string_catn(ss, &size, &ptr, p, len); + ss = string_catn(ss, &size, &ptr, US"\r\n", 2); p += len; if (linebreak != NULL) p++; } @@ -2837,14 +2983,18 @@ is closing if required and return 2. */ if (log_reject_target != 0) { #ifdef SUPPORT_TLS - uschar * s = s_tlslog(NULL, NULL, NULL); - if (!s) s = US""; + uschar * tls = s_tlslog(NULL, NULL, NULL); + if (!tls) tls = US""; #else - uschar * s = US""; + uschar * tls = US""; #endif - log_write(0, log_reject_target, "%s%s %s%srejected %s%s", - host_and_ident(TRUE), s, - sender_info, (rc == FAIL)? US"" : US"temporarily ", what, log_msg); + log_write(0, log_reject_target, "%s%s%s %s%srejected %s%s", + LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", + host_and_ident(TRUE), + tls, + sender_info, + rc == FAIL ? US"" : US"temporarily ", + what, log_msg); } if (!drop) return 0; @@ -3211,6 +3361,68 @@ return rc; + + +static int +qualify_recipient(uschar ** recipient, uschar * smtp_cmd_data, uschar * tag) +{ +int rd; +if (allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0) + { + DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", + *recipient); + rd = Ustrlen(recipient) + 1; + *recipient = rewrite_address_qualify(*recipient, TRUE); + return rd; + } +smtp_printf("501 %s: recipient address must contain a domain\r\n", + smtp_cmd_data); +log_write(L_smtp_syntax_error, + LOG_MAIN|LOG_REJECT, "unqualified %s rejected: <%s> %s%s", + tag, *recipient, host_and_ident(TRUE), host_lookup_msg); +return 0; +} + + + + +static void +smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp) +{ +HAD(SCH_QUIT); +incomplete_transaction_log(US"QUIT"); +if (acl_smtp_quit != NULL) + { + int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp); + if (rc == ERROR) + log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", + *log_msgp); + } +if (*user_msgp) + smtp_respond(US"221", 3, TRUE, *user_msgp); +else + smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); + +#ifdef SUPPORT_TLS +tls_close(TRUE, TRUE); +#endif + +log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", + smtp_get_connection_info()); +} + + +static void +smtp_rset_handler(void) +{ +HAD(SCH_RSET); +incomplete_transaction_log(US"RSET"); +smtp_printf("250 Reset OK\r\n"); +cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE; +} + + + /************************************************* * Initialize for SMTP incoming message * *************************************************/ @@ -3257,6 +3469,8 @@ for the host). Note: we do NOT reset AUTH at this point. */ smtp_reset(reset_point); message_ended = END_NOTSTARTED; +chunking_state = chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED; + cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE; cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; @@ -3592,10 +3806,9 @@ while (done <= 0) if (sender_host_address != NULL) { - s = string_cat(s, &size, &ptr, US" [", 2); - s = string_cat(s, &size, &ptr, sender_host_address, - Ustrlen(sender_host_address)); - s = string_cat(s, &size, &ptr, US"]", 1); + s = string_catn(s, &size, &ptr, US" [", 2); + s = string_cat (s, &size, &ptr, sender_host_address); + s = string_catn(s, &size, &ptr, US"]", 1); } } @@ -3619,7 +3832,7 @@ while (done <= 0) size = ptr + 1; } - s = string_cat(s, &size, &ptr, US"\r\n", 2); + s = string_catn(s, &size, &ptr, US"\r\n", 2); /* If we received EHLO, we must create a multiline response which includes the functions supported. */ @@ -3638,12 +3851,12 @@ while (done <= 0) { sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code, thismessage_size_limit); - s = string_cat(s, &size, &ptr, big_buffer, Ustrlen(big_buffer)); + s = string_cat(s, &size, &ptr, big_buffer); } else { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-SIZE\r\n", 7); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-SIZE\r\n", 7); } /* Exim does not do protocol conversion or data conversion. It is 8-bit @@ -3655,15 +3868,15 @@ while (done <= 0) if (accept_8bitmime) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-8BITMIME\r\n", 11); } /* Advertise DSN support if configured to do so. */ if (verify_check_host(&dsn_advertise_hosts) != FAIL) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-DSN\r\n", 6); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-DSN\r\n", 6); dsn_advertised = TRUE; } @@ -3672,8 +3885,8 @@ while (done <= 0) if (acl_smtp_etrn != NULL) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-ETRN\r\n", 7); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-ETRN\r\n", 7); } /* Advertise EXPN if there's an ACL checking whether a host is @@ -3681,8 +3894,8 @@ while (done <= 0) if (acl_smtp_expn != NULL) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-EXPN\r\n", 7); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-EXPN\r\n", 7); } /* Exim is quite happy with pipelining, so let the other end know that @@ -3691,8 +3904,8 @@ while (done <= 0) if (pipelining_enable && verify_check_host(&pipelining_advertise_hosts) == OK) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-PIPELINING\r\n", 13); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-PIPELINING\r\n", 13); sync_cmd_limit = NON_SYNC_CMD_PIPELINING; pipelining_advertised = TRUE; } @@ -3725,24 +3938,33 @@ while (done <= 0) int saveptr; if (first) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-AUTH", 5); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-AUTH", 5); first = FALSE; auth_advertised = TRUE; } saveptr = ptr; - s = string_cat(s, &size, &ptr, US" ", 1); - s = string_cat(s, &size, &ptr, au->public_name, - Ustrlen(au->public_name)); + s = string_catn(s, &size, &ptr, US" ", 1); + s = string_cat (s, &size, &ptr, au->public_name); while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); au->advertised = TRUE; } else au->advertised = FALSE; - if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2); + if (!first) s = string_catn(s, &size, &ptr, US"\r\n", 2); } + /* RFC 3030 CHUNKING */ + + if (verify_check_host(&chunking_advertise_hosts) != FAIL) + { + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, 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 @@ -3752,8 +3974,8 @@ while (done <= 0) if (tls_in.active < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-STARTTLS\r\n", 11); tls_advertised = TRUE; } #endif @@ -3762,8 +3984,8 @@ while (done <= 0) /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ if (prdr_enable) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-PRDR\r\n", 7); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-PRDR\r\n", 7); } #endif @@ -3771,16 +3993,16 @@ while (done <= 0) if ( accept_8bitmime && verify_check_host(&smtputf8_advertise_hosts) != FAIL) { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-SMTPUTF8\r\n", 11); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US"-SMTPUTF8\r\n", 11); smtputf8_advertised = TRUE; } #endif /* Finish off the multiline reply with one that is always available. */ - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US" HELP\r\n", 7); + s = string_catn(s, &size, &ptr, smtp_code, 3); + s = string_catn(s, &size, &ptr, US" HELP\r\n", 7); } /* Terminate the string (for debug), write it, and note that HELO/EHLO @@ -4099,13 +4321,11 @@ while (done <= 0) global_rewrite_rules) : smtp_cmd_data; - /* rfc821_domains = TRUE; << no longer needed */ raw_sender = parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain, TRUE); - /* rfc821_domains = FALSE; << no longer needed */ - if (raw_sender == NULL) + if (!raw_sender) { done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess); break; @@ -4350,16 +4570,13 @@ while (done <= 0) /* Apply SMTP rewriting then extract the working address. Don't allow "<>" as a recipient address */ - recipient = ((rewrite_existflags & rewrite_smtp) != 0)? - rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", - global_rewrite_rules) : smtp_cmd_data; - - /* rfc821_domains = TRUE; << no longer needed */ - recipient = parse_extract_address(recipient, &errmess, &start, &end, - &recipient_domain, FALSE); - /* rfc821_domains = FALSE; << no longer needed */ + recipient = rewrite_existflags & rewrite_smtp + ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + global_rewrite_rules) + : smtp_cmd_data; - if (recipient == NULL) + 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++; @@ -4378,27 +4595,12 @@ while (done <= 0) we must always qualify this address, regardless. */ if (recipient_domain == 0) - { - if (allow_unqualified_recipient || - strcmpic(recipient, US"postmaster") == 0) - { - DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", - recipient); - recipient_domain = Ustrlen(recipient) + 1; - recipient = rewrite_address_qualify(recipient, TRUE); - } - else + if (!(recipient_domain = qualify_recipient(&recipient, smtp_cmd_data, + US"recipient"))) { rcpt_fail_count++; - smtp_printf("501 %s: recipient address must contain a domain\r\n", - smtp_cmd_data); - log_write(L_smtp_syntax_error, - LOG_MAIN|LOG_REJECT, "unqualified recipient rejected: " - "<%s> %s%s", recipient, host_and_ident(TRUE), - host_lookup_msg); break; } - } /* Check maximum allowed */ @@ -4515,8 +4717,44 @@ while (done <= 0) (often indicating some kind of system error), it is helpful to include it with the DATA rejection (an idea suggested by Tony Finch). */ + case BDAT_CMD: + HAD(SCH_BDAT); + { + int n; + + if (chunking_state != CHUNKING_OFFERED) + { + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"BDAT command used when CHUNKING not advertised"); + break; + } + + /* grab size, endmarker */ + + if (sscanf(CS smtp_cmd_data, "%u %n", &chunking_datasize, &n) < 1) + { + done = synprot_error(L_smtp_protocol_error, 501, NULL, + US"missing size for BDAT command"); + break; + } + chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0 + ? CHUNKING_LAST : CHUNKING_ACTIVE; + chunking_data_left = chunking_datasize; + + lwr_receive_getc = receive_getc; + lwr_receive_ungetc = receive_ungetc; + receive_getc = bdat_getc; + receive_ungetc = bdat_ungetc; + + DEBUG(D_any) + debug_printf("chunking state %d\n", (int)chunking_state); + goto DATA_BDAT; + } + case DATA_CMD: HAD(SCH_DATA); + + DATA_BDAT: /* Common code for DATA and BDAT */ if (!discarded && recipients_count <= 0) { if (rcpt_smtp_response_same && rcpt_smtp_response != NULL) @@ -4531,10 +4769,13 @@ while (done <= 0) smtp_respond(code, 3, FALSE, rcpt_smtp_response); } if (pipelining_advertised && last_was_rcpt) - smtp_printf("503 Valid RCPT command must precede DATA\r\n"); + smtp_printf("503 Valid RCPT command must precede %s\r\n", + smtp_names[smtp_connection_had[smtp_ch_index-1]]); else done = synprot_error(L_smtp_protocol_error, 503, NULL, - US"valid RCPT command must precede DATA"); + smtp_connection_had[smtp_ch_index-1] == SCH_DATA + ? US"valid RCPT command must precede DATA" + : US"valid RCPT command must precede BDAT"); break; } @@ -4546,35 +4787,45 @@ while (done <= 0) break; } - /* 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 (acl_smtp_predata == NULL && cutthrough.fd < 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 (chunking_state > CHUNKING_OFFERED) + { /* No predata ACL or go-ahead output for BDAT */ + rc = OK; } - - if (rc == OK) + else { - uschar * code; - code = US"354"; - if (user_msg == NULL) - smtp_printf("%s Enter message, ending with \".\" on a line by itself\r\n", code); - else smtp_user_msg(code, user_msg); - done = 3; - message_ended = END_NOTENDED; /* Indicate in middle of data */ + /* 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 (acl_smtp_predata == NULL && cutthrough.fd < 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"); } - /* Either the ACL failed the address, or it was deferred. */ + done = 3; + message_ended = END_NOTENDED; /* Indicate in middle of data */ - else - done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg); break; @@ -4584,18 +4835,26 @@ while (done <= 0) HAD(SCH_VRFY); - if(!(address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end, - &recipient_domain, FALSE))) + if (!(address = parse_extract_address(smtp_cmd_data, &errmess, + &start, &end, &recipient_domain, FALSE))) + { smtp_printf("501 %s\r\n", errmess); + break; + } + + if (recipient_domain == 0) + if (!(recipient_domain = qualify_recipient(&address, smtp_cmd_data, + US"verify"))) + break; - else if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy, + if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy, &user_msg, &log_msg)) != OK) done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg); else { - uschar *s = NULL; + uschar * s = NULL; + address_item * addr = deliver_make_addr(address, FALSE); - address_item *addr = deliver_make_addr(address, FALSE); switch(verify_address(addr, NULL, vopt_is_recipient | vopt_qualify, -1, -1, -1, NULL, NULL, NULL)) { @@ -4685,7 +4944,7 @@ while (done <= 0) if (receive_smtp_buffered()) { DEBUG(D_any) - debug_printf("Non-empty input buffer after STARTTLS; naive attack?"); + debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n"); if (tls_in.active < 0) smtp_inend = smtp_inptr = smtp_inbuffer; /* and if TLS is already active, tls_server_start() should fail */ @@ -4802,37 +5061,15 @@ while (done <= 0) message. */ case QUIT_CMD: - HAD(SCH_QUIT); - incomplete_transaction_log(US"QUIT"); - if (acl_smtp_quit != NULL) - { - rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, &log_msg); - if (rc == ERROR) - log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", - log_msg); - } - if (user_msg == NULL) - smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); - else - smtp_respond(US"221", 3, TRUE, user_msg); - - #ifdef SUPPORT_TLS - tls_close(TRUE, TRUE); - #endif - + smtp_quit_handler(&user_msg, &log_msg); done = 2; - log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); break; case RSET_CMD: - HAD(SCH_RSET); - incomplete_transaction_log(US"RSET"); + smtp_rset_handler(); smtp_reset(reset_point); toomany = FALSE; - smtp_printf("250 Reset OK\r\n"); - cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE; break; @@ -4859,7 +5096,7 @@ while (done <= 0) verify_check_host(&tls_advertise_hosts) != FAIL) Ustrcat(buffer, " STARTTLS"); #endif - Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA"); + 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"); @@ -4949,8 +5186,10 @@ while (done <= 0) break; } etrn_command = US"exim -R"; - argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R", - smtp_cmd_data); + 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. */