X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/9be4572b6242b4d297f8b030bdd54bd345a72c06..b9df1829d6afa37ef6576d04fc8845c1d20269b0:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index f3247245d..90ec87b30 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2,18 +2,12 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2017 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" #include "smtp.h" -#define PENDING 256 -#define PENDING_DEFER (PENDING + DEFER) -#define PENDING_OK (PENDING + OK) - -#define DELIVER_BUFFER_SIZE 4096 - /* Options specific to the smtp transport. This transport also supports LMTP over TCP/IP. The options must be in alphabetic order (note that "_" comes @@ -327,7 +321,7 @@ gid = gid; /* Pass back options if required. This interface is getting very messy. */ -if (tf != NULL) +if (tf) { tf->interface = ob->interface; tf->port = ob->port; @@ -346,11 +340,8 @@ host lists, provided that the local host wasn't present in the original host list. */ if (!testflag(addrlist, af_local_host_removed)) - { - for (; addrlist != NULL; addrlist = addrlist->next) - if (addrlist->fallback_hosts == NULL) - addrlist->fallback_hosts = ob->fallback_hostlist; - } + for (; addrlist; addrlist = addrlist->next) + if (!addrlist->fallback_hosts) addrlist->fallback_hosts = ob->fallback_hostlist; return OK; } @@ -458,7 +449,7 @@ for (addr = addrlist; addr; addr = addr->next) { addr->basic_errno = errno_value; addr->more_errno |= orvalue; - if (msg != NULL) + if (msg) { addr->message = msg; if (pass_message) setflag(addr, af_pass_message); @@ -519,82 +510,62 @@ check_response(host_item *host, int *errno_value, int more_errno, uschar *buffer, int *yield, uschar **message, BOOL *pass_message) { uschar * pl = pipelining_active ? US"pipelined " : US""; +const uschar * s; *yield = '4'; /* Default setting is to give a temporary error */ -/* Handle response timeout */ - -if (*errno_value == ETIMEDOUT) +switch(*errno_value) { - *message = US string_sprintf("SMTP timeout after %s%s", - pl, smtp_command); - if (transport_count > 0) - *message = US string_sprintf("%s (%d bytes written)", *message, - transport_count); - return FALSE; - } - -/* Handle malformed SMTP response */ - -if (*errno_value == ERRNO_SMTPFORMAT) - { - const uschar *malfresp = string_printing(buffer); - while (isspace(*malfresp)) malfresp++; - *message = *malfresp == 0 - ? string_sprintf("Malformed SMTP reply (an empty line) " - "in response to %s%s", pl, smtp_command) - : string_sprintf("Malformed SMTP reply in response to %s%s: %s", - pl, smtp_command, malfresp); - return FALSE; - } - -/* Handle a failed filter process error; can't send QUIT as we mustn't -end the DATA. */ - -if (*errno_value == ERRNO_FILTER_FAIL) - { - *message = US string_sprintf("transport filter process failed (%d)%s", - more_errno, - (more_errno == EX_EXECFAILED)? ": unable to execute command" : ""); - return FALSE; - } - -/* Handle a failed add_headers expansion; can't send QUIT as we mustn't -end the DATA. */ - -if (*errno_value == ERRNO_CHHEADER_FAIL) - { - *message = - US string_sprintf("failed to expand headers_add or headers_remove: %s", - expand_string_message); - return FALSE; - } - -/* Handle failure to write a complete data block */ - -if (*errno_value == ERRNO_WRITEINCOMPLETE) - { - *message = US string_sprintf("failed to write a data block"); - return FALSE; - } + case ETIMEDOUT: /* Handle response timeout */ + *message = US string_sprintf("SMTP timeout after %s%s", + pl, smtp_command); + if (transport_count > 0) + *message = US string_sprintf("%s (%d bytes written)", *message, + transport_count); + return FALSE; + + case ERRNO_SMTPFORMAT: /* Handle malformed SMTP response */ + s = string_printing(buffer); + while (isspace(*s)) s++; + *message = *s == 0 + ? string_sprintf("Malformed SMTP reply (an empty line) " + "in response to %s%s", pl, smtp_command) + : string_sprintf("Malformed SMTP reply in response to %s%s: %s", + pl, smtp_command, s); + return FALSE; + + case ERRNO_FILTER_FAIL: /* Handle a failed filter process error; + can't send QUIT as we mustn't end the DATA. */ + *message = string_sprintf("transport filter process failed (%d)%s", + more_errno, + more_errno == EX_EXECFAILED ? ": unable to execute command" : ""); + return FALSE; + + case ERRNO_CHHEADER_FAIL: /* Handle a failed add_headers expansion; + can't send QUIT as we mustn't end the DATA. */ + *message = + string_sprintf("failed to expand headers_add or headers_remove: %s", + expand_string_message); + return FALSE; + + case ERRNO_WRITEINCOMPLETE: /* failure to write a complete data block */ + *message = string_sprintf("failed to write a data block"); + return FALSE; #ifdef SUPPORT_I18N -/* Handle lack of advertised SMTPUTF8, for international message */ -if (*errno_value == ERRNO_UTF8_FWD) - { - *message = US"utf8 support required but not offered for forwarding"; - DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message); - return TRUE; - } + case ERRNO_UTF8_FWD: /* no advertised SMTPUTF8, for international message */ + *message = US"utf8 support required but not offered for forwarding"; + DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message); + return TRUE; #endif + } /* Handle error responses from the remote mailer. */ if (buffer[0] != 0) { - const uschar *s = string_printing(buffer); - *message = US string_sprintf("SMTP error from remote mail server after %s%s: " - "%s", pl, smtp_command, s); + *message = string_sprintf("SMTP error from remote mail server after %s%s: " + "%s", pl, smtp_command, s = string_printing(buffer)); *pass_message = TRUE; *yield = buffer[0]; return TRUE; @@ -610,9 +581,10 @@ if (*errno_value == 0 || *errno_value == ECONNRESET) { *errno_value = ERRNO_SMTPCLOSED; *message = US string_sprintf("Remote host closed connection " - "in response to %s%s", pl, smtp_command); + "in response to %s%s", pl, smtp_command); } -else *message = US string_sprintf("%s [%s]", host->name, host->address); +else + *message = US string_sprintf("%s [%s]", host->name, host->address); return FALSE; } @@ -744,21 +716,11 @@ subsequent general error, it will get reset accordingly. If not, it will get converted to OK at the end. Arguments: - addrlist the complete address list - include_affixes TRUE if affixes include in RCPT - sync_addr ptr to the ptr of the one to start scanning at (updated) - host the host we are connected to + sx smtp connection context count the number of responses to read - address_retry_ - include_sender true if 4xx retry is to include the sender it its key - pending_MAIL true if the first response is for MAIL pending_DATA 0 if last command sent was not DATA +1 if previously had a good recipient -1 if not previously had a good recipient - inblock incoming SMTP block - timeout timeout value - buffer buffer for reading response - buffsize size of buffer Returns: 3 if at least one address had 2xx and one had 5xx 2 if at least one address had 5xx but none had 2xx @@ -770,39 +732,38 @@ Returns: 3 if at least one address had 2xx and one had 5xx */ static int -sync_responses(address_item *addrlist, BOOL include_affixes, - address_item **sync_addr, host_item *host, int count, - BOOL address_retry_include_sender, BOOL pending_MAIL, - int pending_DATA, smtp_inblock *inblock, int timeout, uschar *buffer, - int buffsize) +sync_responses(smtp_context * sx, int count, int pending_DATA) { -address_item *addr = *sync_addr; +address_item *addr = sx->sync_addr; +smtp_transport_options_block *ob = + (smtp_transport_options_block *)sx->tblock->options_block; int yield = 0; /* Handle the response for a MAIL command. On error, reinstate the original command in big_buffer for error message use, and flush any further pending responses before returning, except after I/O errors and timeouts. */ -if (pending_MAIL) +if (sx->pending_MAIL) { count--; - if (!smtp_read_response(inblock, buffer, buffsize, '2', timeout)) + if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), + '2', ob->command_timeout)) { DEBUG(D_transport) debug_printf("bad response for MAIL\n"); Ustrcpy(big_buffer, mail_command); /* Fits, because it came from there! */ - if (errno == 0 && buffer[0] != 0) + if (errno == 0 && sx->buffer[0] != 0) { uschar flushbuffer[4096]; int save_errno = 0; - if (buffer[0] == '4') + if (sx->buffer[0] == '4') { save_errno = ERRNO_MAIL4XX; - addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } while (count-- > 0) { - if (!smtp_read_response(inblock, flushbuffer, sizeof(flushbuffer), - '2', timeout) + if (!smtp_read_response(&sx->inblock, flushbuffer, sizeof(flushbuffer), + '2', ob->command_timeout) && (errno != 0 || flushbuffer[0] == 0)) break; } @@ -813,7 +774,7 @@ if (pending_MAIL) while (count-- > 0) /* Mark any pending addrs with the host used */ { while (addr->transport_return != PENDING_DEFER) addr = addr->next; - addr->host_used = host; + addr->host_used = sx->host; addr = addr->next; } return -3; @@ -831,9 +792,10 @@ while (count-- > 0) while (addr->transport_return != PENDING_DEFER) addr = addr->next; /* The address was accepted */ - addr->host_used = host; + addr->host_used = sx->host; - if (smtp_read_response(inblock, buffer, buffsize, '2', timeout)) + if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), + '2', ob->command_timeout)) { yield |= 1; addr->transport_return = PENDING_OK; @@ -856,8 +818,8 @@ while (count-- > 0) else if (errno == ETIMEDOUT) { uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>", - transport_rcpt_address(addr, include_affixes)); - set_errno_nohost(addrlist, ETIMEDOUT, message, DEFER, FALSE); + transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes)); + set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; return -1; @@ -868,10 +830,10 @@ while (count-- > 0) big_buffer for which we are checking the response, so the error message makes sense. */ - else if (errno != 0 || buffer[0] == 0) + else if (errno != 0 || sx->buffer[0] == 0) { string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>", - transport_rcpt_address(addr, include_affixes)); + transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes)); return -2; } @@ -881,14 +843,15 @@ while (count-- > 0) { addr->message = string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: " - "%s", transport_rcpt_address(addr, include_affixes), - string_printing(buffer)); + "%s", transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes), + string_printing(sx->buffer)); setflag(addr, af_pass_message); - msglog_line(host, addr->message); + if (!sx->verify) + msglog_line(sx->host, addr->message); /* The response was 5xx */ - if (buffer[0] == '5') + if (sx->buffer[0] == '5') { addr->transport_return = FAIL; yield |= 2; @@ -900,40 +863,42 @@ while (count-- > 0) { addr->transport_return = DEFER; addr->basic_errno = ERRNO_RCPT4XX; - addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; + if (!sx->verify) + { #ifndef DISABLE_EVENT - event_defer_errno = addr->more_errno; - msg_event_raise(US"msg:rcpt:host:defer", addr); + event_defer_errno = addr->more_errno; + msg_event_raise(US"msg:rcpt:host:defer", addr); #endif - /* Log temporary errors if there are more hosts to be tried. - If not, log this last one in the == line. */ + /* Log temporary errors if there are more hosts to be tried. + If not, log this last one in the == line. */ - if (host->next) - log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message); + if (sx->host->next) + log_write(0, LOG_MAIN, "H=%s [%s]: %s", + sx->host->name, sx->host->address, addr->message); #ifndef DISABLE_EVENT - else - msg_event_raise(US"msg:rcpt:defer", addr); + else + msg_event_raise(US"msg:rcpt:defer", addr); #endif - /* Do not put this message on the list of those waiting for specific - hosts, as otherwise it is likely to be tried too often. */ + /* Do not put this message on the list of those waiting for specific + hosts, as otherwise it is likely to be tried too often. */ - update_waiting = FALSE; + update_waiting = FALSE; - /* Add a retry item for the address so that it doesn't get tried again - too soon. If address_retry_include_sender is true, add the sender address - to the retry key. */ + /* Add a retry item for the address so that it doesn't get tried again + too soon. If address_retry_include_sender is true, add the sender address + to the retry key. */ - if (address_retry_include_sender) - { - uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key, - sender_address); - retry_add_item(addr, altkey, 0); - } - else retry_add_item(addr, addr->address_retry_key, 0); + retry_add_item(addr, + ob->address_retry_include_sender + ? string_sprintf("%s:<%s>", addr->address_retry_key, sender_address) + : addr->address_retry_key, + 0); + } } } } /* Loop for next RCPT response */ @@ -941,27 +906,28 @@ while (count-- > 0) /* Update where to start at for the next block of responses, unless we have already handled all the addresses. */ -if (addr != NULL) *sync_addr = addr->next; +if (addr) sx->sync_addr = addr->next; /* Handle a response to DATA. If we have not had any good recipients, either previously or in this block, the response is ignored. */ if (pending_DATA != 0 && - !smtp_read_response(inblock, buffer, buffsize, '3', timeout)) + !smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), + '3', ob->command_timeout)) { int code; uschar *msg; BOOL pass_message; if (pending_DATA > 0 || (yield & 1) != 0) { - if (errno == 0 && buffer[0] == '4') + if (errno == 0 && sx->buffer[0] == '4') { errno = ERRNO_DATA4XX; - addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } return -3; } - (void)check_response(host, &errno, 0, buffer, &code, &msg, &pass_message); + (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message); DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining " "is in use and there were no good recipients\n", msg); } @@ -1319,9 +1285,11 @@ return Ustrcmp(current_local_identity, message_local_identity) == 0; -uschar -ehlo_response(uschar * buf, size_t bsize, uschar checks) +static uschar +ehlo_response(uschar * buf, uschar checks) { +size_t bsize = Ustrlen(buf); + #ifdef SUPPORT_TLS if ( checks & PEER_OFFERED_TLS && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) @@ -1384,16 +1352,15 @@ smtp_chunk_cmd_callback(int fd, transport_ctx * tctx, { smtp_transport_options_block * ob = (smtp_transport_options_block *)(tctx->tblock->options_block); +smtp_context * sx = tctx->smtp_context; int cmd_count = 0; int prev_cmd_count; -uschar * buffer = tctx->buffer; - /* Write SMTP chunk header command */ if (chunk_size > 0) { - if((cmd_count = smtp_write_command(tctx->outblock, FALSE, "BDAT %u%s\r\n", + if((cmd_count = smtp_write_command(&sx->outblock, FALSE, "BDAT %u%s\r\n", chunk_size, flags & tc_chunk_last ? " LAST" : "") ) < 0) return ERROR; @@ -1401,13 +1368,13 @@ if (chunk_size > 0) data_command = string_copy(big_buffer); /* Save for later error message */ } -prev_cmd_count = cmd_count += tctx->cmd_count; +prev_cmd_count = cmd_count += sx->cmd_count; /* Reap responses for any previous, but not one we just emitted */ if (chunk_size > 0) prev_cmd_count--; -if (tctx->pending_BDAT) +if (sx->pending_BDAT) prev_cmd_count--; if (flags & tc_reap_prev && prev_cmd_count > 0) @@ -1415,53 +1382,47 @@ if (flags & tc_reap_prev && prev_cmd_count > 0) DEBUG(D_transport) debug_printf("look for %d responses" " for previous pipelined cmds\n", prev_cmd_count); - switch(sync_responses(tctx->first_addr, tctx->tblock->rcpt_include_affixes, - tctx->sync_addr, tctx->host, prev_cmd_count, - ob->address_retry_include_sender, - tctx->pending_MAIL, 0, - tctx->inblock, - ob->command_timeout, - buffer, DELIVER_BUFFER_SIZE)) + switch(sync_responses(sx, prev_cmd_count, 0)) { case 1: /* 2xx (only) => OK */ - case 3: tctx->good_RCPT = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: *tctx->completed_address = TRUE; /* 5xx (only) => progress made */ + case 3: sx->good_RCPT = TRUE; /* 2xx & 5xx => OK & progress made */ + case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ case 0: break; /* No 2xx or 5xx, but no probs */ case -1: /* Timeout on RCPT */ default: return ERROR; /* I/O error, or any MAIL/DATA error */ } cmd_count = 1; - if (!tctx->pending_BDAT) + if (!sx->pending_BDAT) pipelining_active = FALSE; } /* Reap response for an outstanding BDAT */ -if (tctx->pending_BDAT) +if (sx->pending_BDAT) { DEBUG(D_transport) debug_printf("look for one response for BDAT\n"); - if (!smtp_read_response(tctx->inblock, buffer, DELIVER_BUFFER_SIZE, '2', + if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout)) { - if (errno == 0 && buffer[0] == '4') + if (errno == 0 && sx->buffer[0] == '4') { errno = ERRNO_DATA4XX; /*XXX does this actually get used? */ - tctx->first_addr->more_errno |= - ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + sx->addrlist->more_errno |= + ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } return ERROR; } cmd_count--; - tctx->pending_BDAT = FALSE; + sx->pending_BDAT = FALSE; pipelining_active = FALSE; } else if (chunk_size > 0) - tctx->pending_BDAT = TRUE; + sx->pending_BDAT = TRUE; -tctx->cmd_count = cmd_count; +sx->cmd_count = cmd_count; return OK; } @@ -1471,65 +1432,11 @@ return OK; * Make connection for given message * *************************************************/ -typedef struct { - address_item * addrlist; - host_item * host; - int host_af; - int port; - uschar * interface; - - BOOL lmtp:1; - BOOL smtps:1; - BOOL ok:1; - BOOL send_rset:1; - BOOL send_quit:1; - BOOL setting_up:1; - BOOL esmtp:1; - BOOL esmtp_sent:1; - BOOL pending_MAIL:1; -#ifndef DISABLE_PRDR - BOOL prdr_active:1; -#endif -#ifdef SUPPORT_I18N - BOOL utf8_needed:1; -#endif - BOOL dsn_all_lasthop:1; -#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) - BOOL dane:1; - BOOL dane_required:1; -#endif - - int max_rcpt; - - uschar peer_offered; - uschar * igquotstr; - uschar * helo_data; -#ifdef EXPERIMENTAL_DSN_INFO - uschar * smtp_greeting; - uschar * helo_response; -#endif - - smtp_inblock inblock; - smtp_outblock outblock; - uschar buffer[DELIVER_BUFFER_SIZE]; - uschar inbuffer[4096]; - uschar outbuffer[4096]; - - transport_instance * tblock; - smtp_transport_options_block * ob; -} smtp_context; - /* Arguments: ctx connection context - message_defer return set TRUE if yield is OK, but all addresses were deferred - because of a non-recipient, non-host failure, that is, a - 4xx response to MAIL FROM, DATA, or ".". This is a defer - that is specific to the message. suppress_tls if TRUE, don't attempt a TLS connection - this is set for a second attempt after TLS initialization fails - verify TRUE if connection is for a verify callout, FALSE for - a delivery attempt Returns: OK - the connection was made and the delivery attempted; fd is set in the conn context, tls_out set up. @@ -1541,20 +1448,17 @@ Returns: OK - the connection was made and the delivery attempted; to expand */ int -smtp_setup_conn(smtp_context * sx, BOOL * message_defer, BOOL suppress_tls, - BOOL verify) +smtp_setup_conn(smtp_context * sx, BOOL suppress_tls) { #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) dns_answer tlsa_dnsa; #endif BOOL pass_message = FALSE; - uschar * message = NULL; -int save_errno; int yield = OK; int rc; -sx->ob = (smtp_transport_options_block *)(sx->tblock->options_block); +sx->ob = (smtp_transport_options_block *) sx->tblock->options_block; sx->lmtp = strcmpic(sx->ob->protocol, US"lmtp") == 0; sx->smtps = strcmpic(sx->ob->protocol, US"smtps") == 0; @@ -1574,15 +1478,14 @@ sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->hos #endif if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999; -sx->helo_data = NULL; sx->peer_offered = 0; sx->igquotstr = US""; +if (!sx->helo_data) sx->helo_data = sx->ob->helo_data; #ifdef EXPERIMENTAL_DSN_INFO sx->smtp_greeting = NULL; sx->helo_response = NULL; #endif -*message_defer = FALSE; smtp_command = US"initial connection"; sx->buffer[0] = '\0'; @@ -1615,7 +1518,8 @@ tls_out.ocsp = OCSP_NOT_REQ; /* Flip the legacy TLS-related variables over to the outbound set in case they're used in the context of the transport. Don't bother resetting -afterward as we're in a subprocess. */ +afterward (when being used by a transport) as we're in a subprocess. +For verify, unflipped once the callout is dealt with */ tls_modify_variables(&tls_out); @@ -1634,6 +1538,9 @@ specially so they can be identified for retries. */ if (continue_hostname == NULL) { + if (sx->verify) + HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", sx->interface, sx->port); + /* This puts port into host->port */ sx->inblock.sock = sx->outblock.sock = smtp_connect(sx->host, sx->host_af, sx->port, sx->interface, @@ -1641,8 +1548,18 @@ if (continue_hostname == NULL) if (sx->inblock.sock < 0) { - set_errno_nohost(sx->addrlist, errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno, - NULL, DEFER, FALSE); + uschar * msg = NULL; + if (sx->verify) + { + msg = US strerror(errno); + HDEBUG(D_verify) debug_printf("connect: %s\n", msg); + } + set_errno_nohost(sx->addrlist, + errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno, + sx->verify ? string_sprintf("could not connect: %s", msg) + : NULL, + DEFER, FALSE); + sx->send_quit = FALSE; return DEFER; } @@ -1684,18 +1601,26 @@ if (continue_hostname == NULL) sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is delayed till here so that $sending_interface and $sending_port are set. */ - sx->helo_data = expand_string(sx->ob->helo_data); + if (sx->helo_data) + if (!(sx->helo_data = expand_string(sx->helo_data))) + if (sx->verify) + log_write(0, LOG_MAIN|LOG_PANIC, + "<%s>: failed to expand transport's helo_data value for callout: %s", + sx->addrlist->address, expand_string_message); + #ifdef SUPPORT_I18N if (sx->helo_data) { - uschar * errstr = NULL; - if ((sx->helo_data = string_domain_utf8_to_alabel(sx->helo_data, &errstr)), errstr) - { - errstr = string_sprintf("failed to expand helo_data: %s", errstr); - set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); - yield = DEFER; - goto SEND_QUIT; - } + expand_string_message = NULL; + if ((sx->helo_data = string_domain_utf8_to_alabel(sx->helo_data, + &expand_string_message)), + expand_string_message) + if (sx->verify) + log_write(0, LOG_MAIN|LOG_PANIC, + "<%s>: failed to expand transport's helo_data value for callout: %s", + sx->addrlist->address, expand_string_message); + else + sx->helo_data = NULL; } #endif @@ -1847,12 +1772,8 @@ goto SEND_QUIT; if (rsp != sx->buffer && rsp[0] == 0 && (errno == 0 || errno == ECONNRESET)) { - sx->send_quit = FALSE; - save_errno = ERRNO_SMTPCLOSED; - message = string_sprintf("Remote host closed connection " - "in response to %s (EHLO response was: %s)", - smtp_command, sx->buffer); - goto FAILED; + errno = ERRNO_SMTPCLOSED; + goto EHLOHELO_FAILED; } Ustrncpy(sx->buffer, rsp, sizeof(sx->buffer)/2); goto RESPONSE_FAILED; @@ -1863,7 +1784,7 @@ goto SEND_QUIT; if (sx->esmtp || sx->lmtp) { - sx->peer_offered = ehlo_response(sx->buffer, Ustrlen(sx->buffer), + sx->peer_offered = ehlo_response(sx->buffer, PEER_OFFERED_TLS /* others checked later */ ); @@ -1892,6 +1813,7 @@ else sx->inblock.sock = sx->outblock.sock = fileno(stdin); smtp_command = big_buffer; sx->host->port = sx->port; /* Record the port that was used */ + sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */ } /* If TLS is available on this connection, whether continued or not, attempt to @@ -1905,7 +1827,10 @@ for error analysis. */ #ifdef SUPPORT_TLS if ( smtp_peer_options & PEER_OFFERED_TLS && !suppress_tls - && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK) + && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK + && ( !sx->verify + || verify_check_given_host(&sx->ob->hosts_verify_avoid_tls, sx->host) != OK + ) ) { uschar buffer2[4096]; if (smtp_write_command(&sx->outblock, FALSE, "STARTTLS\r\n") < 0) @@ -1927,6 +1852,7 @@ if ( smtp_peer_options & PEER_OFFERED_TLS ) { Ustrncpy(sx->buffer, buffer2, sizeof(sx->buffer)); + sx->buffer[sizeof(sx->buffer)-1] = '\0'; goto RESPONSE_FAILED; } } @@ -1937,11 +1863,12 @@ if ( smtp_peer_options & PEER_OFFERED_TLS TLS_NEGOTIATE: { address_item * addr; - int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock + uschar * errstr; + int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock, # ifdef EXPERIMENTAL_DANE - , sx->dane ? &tlsa_dnsa : NULL + sx->dane ? &tlsa_dnsa : NULL, # endif - ); + &errstr); /* TLS negotiation failed; give an error. From outside, this function may be called again to try in clear on a new connection, if the options permit @@ -1951,12 +1878,12 @@ if ( smtp_peer_options & PEER_OFFERED_TLS { # ifdef EXPERIMENTAL_DANE if (sx->dane) log_write(0, LOG_MAIN, - "DANE attempt failed; no TLS connection to %s [%s]", - sx->host->name, sx->host->address); + "DANE attempt failed; TLS connection to %s [%s]: %s", + sx->host->name, sx->host->address, errstr); # endif - save_errno = ERRNO_TLSFAILURE; - message = US"failure while setting up TLS session"; + errno = ERRNO_TLSFAILURE; + message = string_sprintf("TLS session: %s", errstr); sx->send_quit = FALSE; goto TLS_FAILED; } @@ -2042,7 +1969,7 @@ else if ( sx->smtps || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK ) { - save_errno = ERRNO_TLSREQUIRED; + errno = ERRNO_TLSREQUIRED; message = string_sprintf("a TLS session is required, but %s", smtp_peer_options & PEER_OFFERED_TLS ? "an attempt to start TLS failed" : "the server did not offer TLS support"); @@ -2063,7 +1990,7 @@ if (continue_hostname == NULL { if (sx->esmtp || sx->lmtp) { - sx->peer_offered = ehlo_response(sx->buffer, Ustrlen(sx->buffer), + sx->peer_offered = ehlo_response(sx->buffer, 0 /* no TLS */ | (sx->lmtp && sx->ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0) | PEER_OFFERED_CHUNKING @@ -2165,35 +2092,36 @@ return OK; { int code; - uschar * set_message; RESPONSE_FAILED: - { - save_errno = errno; message = NULL; - sx->send_quit = check_response(sx->host, &save_errno, sx->addrlist->more_errno, + sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno, sx->buffer, &code, &message, &pass_message); goto FAILED; - } SEND_FAILED: - { - save_errno = errno; code = '4'; message = US string_sprintf("send() to %s [%s] failed: %s", - sx->host->name, sx->host->address, strerror(save_errno)); + sx->host->name, sx->host->address, strerror(errno)); sx->send_quit = FALSE; goto FAILED; - } /* This label is jumped to directly when a TLS negotiation has failed, or was not done for a host for which it is required. Values will be set - in message and save_errno, and setting_up will always be true. Treat as + in message and errno, and setting_up will always be true. Treat as a temporary error. */ + EHLOHELO_FAILED: + code = '4'; + message = string_sprintf("Remote host closed connection in response to %s" + " (EHLO response was: %s)", smtp_command, sx->buffer); + sx->send_quit = FALSE; + goto FAILED; + #ifdef SUPPORT_TLS TLS_FAILED: - code = '4'; + code = '4'; + goto FAILED; #endif /* The failure happened while setting up the call; see if the failure was @@ -2203,9 +2131,8 @@ return OK; whatever), defer all addresses, and yield DEFER, so that the host is not tried again for a while. */ - FAILED: +FAILED: sx->ok = FALSE; /* For when reached by GOTO */ - set_message = message; yield = code == '5' #ifdef SUPPORT_I18N @@ -2213,7 +2140,7 @@ return OK; #endif ? FAIL : DEFER; - set_errno(sx->addrlist, save_errno, set_message, yield, pass_message, sx->host + set_errno(sx->addrlist, errno, message, yield, pass_message, sx->host #ifdef EXPERIMENTAL_DSN_INFO , sx->smtp_greeting, sx->helo_response #endif @@ -2226,8 +2153,6 @@ SEND_QUIT: if (sx->send_quit) (void)smtp_write_command(&sx->outblock, FALSE, "QUIT\r\n"); -/*END_OFF:*/ - #ifdef SUPPORT_TLS tls_close(FALSE, TRUE); #endif @@ -2242,15 +2167,17 @@ writing RSET might have failed, or there may be other addresses whose hosts are specified in the transports, and therefore not visible at top level, in which case continue_more won't get set. */ -HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n"); +HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); if (sx->send_quit) { shutdown(sx->outblock.sock, SHUT_WR); if (fcntl(sx->inblock.sock, F_SETFL, O_NONBLOCK) == 0) for (rc = 16; read(sx->inblock.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;) rc--; /* drain socket */ + sx->send_quit = FALSE; } (void)close(sx->inblock.sock); +sx->inblock.sock = sx->outblock.sock = -1; #ifndef DISABLE_EVENT (void) event_raise(sx->tblock->event_action, US"tcp:close", NULL); @@ -2399,6 +2326,169 @@ if (sx->peer_offered & PEER_OFFERED_DSN && !(addr->dsn_flags & rf_dsnlasthop)) } + +/* +Return: + 0 good, rcpt results in addr->transport_return (PENDING_OK, DEFER, FAIL) + -1 MAIL response error + -2 any non-MAIL read i/o error + -3 non-MAIL response timeout + -4 internal error; channel still usable + -5 transmit failed + */ + +int +smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield) +{ +address_item * addr; +int address_count; +int rc; + +if (build_mailcmd_options(sx, sx->first_addr) != OK) + { + *yield = ERROR; + return -4; + } + +/* From here until we send the DATA command, we can make use of PIPELINING +if the server host supports it. The code has to be able to check the responses +at any point, for when the buffer fills up, so we write it totally generally. +When PIPELINING is off, each command written reports that it has flushed the +buffer. */ + +sx->pending_MAIL = TRUE; /* The block starts with MAIL */ + + { + uschar * s = sx->from_addr; +#ifdef SUPPORT_I18N + uschar * errstr = NULL; + + /* If we must downconvert, do the from-address here. Remember we had to + for the to-addresses (done below), and also (ugly) for re-doing when building + the delivery log line. */ + + if ( sx->addrlist->prop.utf8_msg + && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & PEER_OFFERED_UTF8)) + ) + { + if (s = string_address_utf8_to_alabel(s, &errstr), errstr) + { + set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); + *yield = ERROR; + return -4; + } + setflag(sx->addrlist, af_utf8_downcvt); + } +#endif + + rc = smtp_write_command(&sx->outblock, pipelining_active, + "MAIL FROM:<%s>%s\r\n", s, sx->buffer); + } + +mail_command = string_copy(big_buffer); /* Save for later error message */ + +switch(rc) + { + case -1: /* Transmission error */ + return -5; + + case +1: /* Cmd was sent */ + if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2', + sx->ob->command_timeout)) + { + if (errno == 0 && sx->buffer[0] == '4') + { + errno = ERRNO_MAIL4XX; + sx->addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; + } + return -1; + } + sx->pending_MAIL = FALSE; + break; + + /* otherwise zero: command queued for pipeline */ + } + +/* Pass over all the relevant recipient addresses for this host, which are the +ones that have status PENDING_DEFER. If we are using PIPELINING, we can send +several before we have to read the responses for those seen so far. This +checking is done by a subroutine because it also needs to be done at the end. +Send only up to max_rcpt addresses at a time, leaving next_addr pointing to +the next one if not all are sent. + +In the MUA wrapper situation, we want to flush the PIPELINING buffer for the +last address because we want to abort if any recipients have any kind of +problem, temporary or permanent. We know that all recipient addresses will have +the PENDING_DEFER status, because only one attempt is ever made, and we know +that max_rcpt will be large, so all addresses will be done at once. + +For verify we flush the pipeline after any (the only) rcpt address. */ + +for (addr = sx->first_addr, address_count = 0; + addr && address_count < sx->max_rcpt; + addr = addr->next) if (addr->transport_return == PENDING_DEFER) + { + int count; + BOOL no_flush; + uschar * rcpt_addr; + + addr->dsn_aware = sx->peer_offered & PEER_OFFERED_DSN + ? dsn_support_yes : dsn_support_no; + + address_count++; + no_flush = pipelining_active && !sx->verify && (!mua_wrapper || addr->next); + + build_rcptcmd_options(sx, addr); + + /* Now send the RCPT command, and process outstanding responses when + necessary. After a timeout on RCPT, we just end the function, leaving the + yield as OK, because this error can often mean that there is a problem with + just one address, so we don't want to delay the host. */ + + rcpt_addr = transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes); + +#ifdef SUPPORT_I18N + if ( testflag(sx->addrlist, af_utf8_downcvt) + && !(rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, NULL)) + ) + { + /*XXX could we use a per-address errstr here? Not fail the whole send? */ + errno = ERRNO_EXPANDFAIL; + return -5; /*XXX too harsh? */ + } +#endif + + count = smtp_write_command(&sx->outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", + rcpt_addr, sx->igquotstr, sx->buffer); + + if (count < 0) return -5; + if (count > 0) + { + switch(sync_responses(sx, count, 0)) + { + case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */ + case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ + break; + + case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ + if (!sx->lmtp) /* can't tell about progress yet */ + sx->completed_addr = TRUE; + case 0: /* No 2xx or 5xx, but no probs */ + break; + + case -1: return -3; /* Timeout on RCPT */ + case -2: return -2; /* non-MAIL read i/o error */ + default: return -1; /* any MAIL error */ + } + sx->pending_MAIL = FALSE; /* Dealt with MAIL */ + } + } /* Loop for next address */ + +sx->next_addr = addr; +return 0; +} + + /************************************************* * Deliver address list to given host * *************************************************/ @@ -2449,17 +2539,11 @@ smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port, BOOL *message_defer, BOOL suppress_tls) { address_item *addr; -address_item *sync_addr; -address_item *first_addr = addrlist; int yield = OK; -int address_count; int save_errno; int rc; time_t start_delivery_time = time(NULL); -BOOL completed_address = FALSE; - - BOOL pass_message = FALSE; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; @@ -2468,24 +2552,27 @@ uschar *p; smtp_context sx; suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ +*message_defer = FALSE; sx.addrlist = addrlist; sx.host = host; sx.host_af = host_af, sx.port = port; sx.interface = interface; +sx.helo_data = NULL; sx.tblock = tblock; +sx.verify = FALSE; /* Get the channel set up ready for a message (MAIL FROM being the next SMTP command to send */ -if ((rc = smtp_setup_conn(&sx, message_defer, suppress_tls, FALSE)) != OK) +if ((rc = smtp_setup_conn(&sx, suppress_tls)) != OK) return rc; /* If there is a filter command specified for this transport, we can now set it up. This cannot be done until the identify of the host is known. */ -if (tblock->filter_command != NULL) +if (tblock->filter_command) { BOOL rc; uschar fbuf[64]; @@ -2526,154 +2613,24 @@ code was to use a goto to jump back to this point when there is another transaction to handle. */ SEND_MESSAGE: -sync_addr = first_addr; +sx.from_addr = return_path; +sx.first_addr = sx.sync_addr = addrlist; sx.ok = FALSE; sx.send_rset = TRUE; -completed_address = FALSE; +sx.completed_addr = FALSE; /* Initiate a message transfer. */ -if (build_mailcmd_options(&sx, first_addr) != OK) - { - yield = ERROR; - goto SEND_QUIT; - } - -/* From here until we send the DATA command, we can make use of PIPELINING -if the server host supports it. The code has to be able to check the responses -at any point, for when the buffer fills up, so we write it totally generally. -When PIPELINING is off, each command written reports that it has flushed the -buffer. */ - -sx.pending_MAIL = TRUE; /* The block starts with MAIL */ - +switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) { - uschar * s = return_path; -#ifdef SUPPORT_I18N - uschar * errstr = NULL; - - /* If we must downconvert, do the from-address here. Remember we had to - for the to-addresses (done below), and also (ugly) for re-doing when building - the delivery log line. */ - - if ( addrlist->prop.utf8_msg - && (addrlist->prop.utf8_downcvt || !(sx.peer_offered & PEER_OFFERED_UTF8)) - ) - { - if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr) - { - set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); - yield = ERROR; - goto SEND_QUIT; - } - setflag(addrlist, af_utf8_downcvt); - } -#endif - - rc = smtp_write_command(&sx.outblock, pipelining_active, - "MAIL FROM:<%s>%s\r\n", s, sx.buffer); + case 0: break; + case -1: case -2: goto RESPONSE_FAILED; + case -3: goto END_OFF; + case -4: goto SEND_QUIT; + default: goto SEND_FAILED; } -mail_command = string_copy(big_buffer); /* Save for later error message */ - -switch(rc) - { - case -1: /* Transmission error */ - goto SEND_FAILED; - - case +1: /* Block was sent */ - if (!smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2', - sx.ob->command_timeout)) - { - if (errno == 0 && sx.buffer[0] == '4') - { - errno = ERRNO_MAIL4XX; - addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; - } - goto RESPONSE_FAILED; - } - sx.pending_MAIL = FALSE; - break; - } - -/* Pass over all the relevant recipient addresses for this host, which are the -ones that have status PENDING_DEFER. If we are using PIPELINING, we can send -several before we have to read the responses for those seen so far. This -checking is done by a subroutine because it also needs to be done at the end. -Send only up to max_rcpt addresses at a time, leaving first_addr pointing to -the next one if not all are sent. - -In the MUA wrapper situation, we want to flush the PIPELINING buffer for the -last address because we want to abort if any recipients have any kind of -problem, temporary or permanent. We know that all recipient addresses will have -the PENDING_DEFER status, because only one attempt is ever made, and we know -that max_rcpt will be large, so all addresses will be done at once. */ - -for (addr = first_addr, address_count = 0; - addr && address_count < sx.max_rcpt; - addr = addr->next) if (addr->transport_return == PENDING_DEFER) - { - int count; - BOOL no_flush; - uschar * rcpt_addr; - - addr->dsn_aware = sx.peer_offered & PEER_OFFERED_DSN - ? dsn_support_yes : dsn_support_no; - - address_count++; - no_flush = pipelining_active && (!mua_wrapper || addr->next); - - build_rcptcmd_options(&sx, addr); - - /* Now send the RCPT command, and process outstanding responses when - necessary. After a timeout on RCPT, we just end the function, leaving the - yield as OK, because this error can often mean that there is a problem with - just one address, so we don't want to delay the host. */ - - rcpt_addr = transport_rcpt_address(addr, tblock->rcpt_include_affixes); - -#ifdef SUPPORT_I18N - if ( testflag(addrlist, af_utf8_downcvt) - && !(rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, NULL)) - ) - { - /*XXX could we use a per-address errstr here? Not fail the whole send? */ - errno = ERRNO_EXPANDFAIL; - goto SEND_FAILED; - } -#endif - - count = smtp_write_command(&sx.outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", - rcpt_addr, sx.igquotstr, sx.buffer); - - if (count < 0) goto SEND_FAILED; - if (count > 0) - { - switch(sync_responses(first_addr, tblock->rcpt_include_affixes, - &sync_addr, host, count, sx.ob->address_retry_include_sender, - sx.pending_MAIL, 0, &sx.inblock, sx.ob->command_timeout, sx.buffer, - sizeof(sx.buffer))) - { - case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: completed_address = TRUE; /* 5xx (only) => progress made */ - break; - - case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ - if (!sx.lmtp) completed_address = TRUE; /* can't tell about progress yet */ - case 0: /* No 2xx or 5xx, but no probs */ - break; - - case -1: goto END_OFF; /* Timeout on RCPT */ - default: goto RESPONSE_FAILED; /* I/O error, or any MAIL error */ - } - sx.pending_MAIL = FALSE; /* Dealt with MAIL */ - } - } /* Loop for next address */ - -/*XXX potential break point for verify-callouts here. The MAIL and -RCPT handling is relevant there */ - /* If we are an MUA wrapper, abort if any RCPTs were rejected, either permanently or temporarily. We should have flushed and synced after the last RCPT. */ @@ -2681,7 +2638,7 @@ RCPT. */ if (mua_wrapper) { address_item *badaddr; - for (badaddr = first_addr; badaddr; badaddr = badaddr->next) + for (badaddr = sx.first_addr; badaddr; badaddr = badaddr->next) if (badaddr->transport_return != PENDING_OK) { /*XXX could we find a better errno than 0 here? */ @@ -2706,16 +2663,14 @@ if ( !(sx.peer_offered & PEER_OFFERED_CHUNKING) int count = smtp_write_command(&sx.outblock, FALSE, "DATA\r\n"); if (count < 0) goto SEND_FAILED; - switch(sync_responses(first_addr, tblock->rcpt_include_affixes, &sync_addr, - host, count, sx.ob->address_retry_include_sender, sx.pending_MAIL, - sx.ok ? +1 : -1, &sx.inblock, sx.ob->command_timeout, sx.buffer, sizeof(sx.buffer))) + switch(sync_responses(&sx, count, sx.ok ? +1 : -1)) { case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: completed_address = TRUE; /* 5xx (only) => progress made */ + case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */ break; case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ - if (!sx.lmtp) completed_address = TRUE; /* can't tell about progress yet */ + if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ case 0: break; /* No 2xx or 5xx, but no probs */ case -1: goto END_OFF; /* Timeout on RCPT */ @@ -2735,7 +2690,7 @@ well as body. Set the appropriate timeout value to be used for each chunk. if (!(sx.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok) { /* Save the first address of the next batch. */ - first_addr = addr; + sx.first_addr = sx.next_addr; sx.ok = TRUE; } @@ -2763,23 +2718,16 @@ else tctx.check_string = tctx.escape_string = NULL; tctx.options |= topt_use_bdat; tctx.chunk_cb = smtp_chunk_cmd_callback; - tctx.inblock = &sx.inblock; - tctx.outblock = &sx.outblock; - tctx.host = host; - tctx.first_addr = first_addr; - tctx.sync_addr = &sync_addr; - tctx.pending_MAIL = sx.pending_MAIL; - tctx.pending_BDAT = FALSE; - tctx.good_RCPT = sx.ok; - tctx.completed_address = &completed_address; - tctx.cmd_count = 0; - tctx.buffer = sx.buffer; + sx.pending_BDAT = FALSE; + sx.good_RCPT = sx.ok; + sx.cmd_count = 0; + tctx.smtp_context = &sx; } else tctx.options |= topt_end_dot; /* Save the first address of the next batch. */ - first_addr = addr; + sx.first_addr = sx.next_addr; /* Responses from CHUNKING commands go in buffer. Otherwise, there has not been a response. */ @@ -2797,7 +2745,8 @@ else transport_count = 0; #ifndef DISABLE_DKIM - sx.ok = dkim_transport_write_message(sx.inblock.sock, &tctx, &sx.ob->dkim); + sx.ok = dkim_transport_write_message(sx.inblock.sock, &tctx, &sx.ob->dkim, + CUSS &message); #else sx.ok = transport_write_message(sx.inblock.sock, &tctx, 0); #endif @@ -2814,7 +2763,8 @@ else Or, when CHUNKING, it can be a protocol-detected failure. */ if (!sx.ok) - goto RESPONSE_FAILED; + if (message) goto SEND_FAILED; + else goto RESPONSE_FAILED; /* We used to send the terminating "." explicitly here, but because of buffering effects at both ends of TCP/IP connections, you don't gain @@ -2824,20 +2774,17 @@ else smtp_command = US"end of data"; - if (sx.peer_offered & PEER_OFFERED_CHUNKING && tctx.cmd_count > 1) + if (sx.peer_offered & PEER_OFFERED_CHUNKING && sx.cmd_count > 1) { /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */ - switch(sync_responses(first_addr, tblock->rcpt_include_affixes, &sync_addr, - host, tctx.cmd_count-1, sx.ob->address_retry_include_sender, - sx.pending_MAIL, 0, - &sx.inblock, sx.ob->command_timeout, sx.buffer, sizeof(sx.buffer))) + switch(sync_responses(&sx, sx.cmd_count-1, 0)) { case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: completed_address = TRUE; /* 5xx (only) => progress made */ + case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */ break; case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ - if (!sx.lmtp) completed_address = TRUE; /* can't tell about progress yet */ + if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ case 0: break; /* No 2xx or 5xx, but no probs */ case -1: goto END_OFF; /* Timeout on RCPT */ @@ -2921,7 +2868,7 @@ else /* Process all transported addresses - for LMTP or PRDR, read a status for each one. */ - for (addr = addrlist; addr != first_addr; addr = addr->next) + for (addr = addrlist; addr != sx.first_addr; addr = addr->next) { if (addr->transport_return != PENDING_OK) continue; @@ -2962,7 +2909,7 @@ else } continue; } - completed_address = TRUE; /* NOW we can set this flag */ + sx.completed_addr = TRUE; /* NOW we can set this flag */ if (LOGGING(smtp_confirmation)) { const uschar *s = string_printing(sx.buffer); @@ -3021,14 +2968,14 @@ else errno = ERRNO_DATA4XX; addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; } - for (addr = addrlist; addr != first_addr; addr = addr->next) + for (addr = addrlist; addr != sx.first_addr; addr = addr->next) if (sx.buffer[0] == '5' || addr->transport_return == OK) addr->transport_return = PENDING_OK; /* allow set_errno action */ goto RESPONSE_FAILED; } /* Update the journal, or setup retry. */ - for (addr = addrlist; addr != first_addr; addr = addr->next) + for (addr = addrlist; addr != sx.first_addr; addr = addr->next) if (addr->transport_return == OK) { if (testflag(addr, af_homonym)) @@ -3082,8 +3029,8 @@ if (!sx.ok) { save_errno = errno; code = '4'; - message = US string_sprintf("send() to %s [%s] failed: %s", - host->name, host->address, strerror(save_errno)); + message = string_sprintf("send() to %s [%s] failed: %s", + host->name, host->address, message ? message : strerror(save_errno)); sx.send_quit = FALSE; goto FAILED; } @@ -3212,9 +3159,9 @@ hosts_nopass_tls. */ DEBUG(D_transport) debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d " "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit, - sx.send_rset, continue_more, yield, first_addr ? "not " : ""); + sx.send_rset, continue_more, yield, sx.first_addr ? "not " : ""); -if (completed_address && sx.ok && sx.send_quit) +if (sx.completed_addr && sx.ok && sx.send_quit) { BOOL more; smtp_compare_t t_compare; @@ -3222,7 +3169,7 @@ if (completed_address && sx.ok && sx.send_quit) t_compare.tblock = tblock; t_compare.current_sender_address = sender_address; - if ( first_addr != NULL + if ( sx.first_addr != NULL || continue_more || ( ( tls_out.active < 0 || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK @@ -3240,7 +3187,7 @@ if (completed_address && sx.ok && sx.send_quit) if (! (sx.ok = smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0)) { msg = US string_sprintf("send() to %s [%s] failed: %s", host->name, - host->address, strerror(save_errno)); + host->address, strerror(errno)); sx.send_quit = FALSE; } else if (! (sx.ok = smtp_read_response(&sx.inblock, sx.buffer, @@ -3260,7 +3207,7 @@ if (completed_address && sx.ok && sx.send_quit) if (sx.ok) { - if (first_addr != NULL) /* More addresses still to be sent */ + if (sx.first_addr != NULL) /* More addresses still to be sent */ { /* in this run of the transport */ continue_sequence++; /* Causes * in logging */ goto SEND_MESSAGE; @@ -3298,7 +3245,7 @@ propagate it from the initial /* If RSET failed and there are addresses left, they get deferred. */ - else set_errno(first_addr, errno, msg, DEFER, FALSE, host + else set_errno(sx.first_addr, errno, msg, DEFER, FALSE, host #ifdef EXPERIMENTAL_DSN_INFO , sx.smtp_greeting, sx.helo_response #endif @@ -3343,7 +3290,7 @@ writing RSET might have failed, or there may be other addresses whose hosts are specified in the transports, and therefore not visible at top level, in which case continue_more won't get set. */ -HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n"); +HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); if (sx.send_quit) { shutdown(sx.outblock.sock, SHUT_WR); @@ -3386,7 +3333,7 @@ void smtp_transport_closedown(transport_instance *tblock) { smtp_transport_options_block *ob = - (smtp_transport_options_block *)(tblock->options_block); + (smtp_transport_options_block *)tblock->options_block; smtp_inblock inblock; smtp_outblock outblock; uschar buffer[256]; @@ -3773,7 +3720,7 @@ for (cutoff_retry = 0; commonly points to a configuration error, but the best action is still to carry on for the next host. */ - if (rc == HOST_FIND_AGAIN || rc == HOST_FIND_FAILED) + if (rc == HOST_FIND_AGAIN || rc == HOST_FIND_SECURITY || rc == HOST_FIND_FAILED) { retry_add_item(addrlist, string_sprintf("R:%s", host->name), 0); expired = FALSE; @@ -3786,8 +3733,11 @@ for (cutoff_retry = 0; { if (addr->transport_return != DEFER) continue; addr->basic_errno = ERRNO_UNKNOWNHOST; - addr->message = - string_sprintf("failed to lookup IP address for %s", host->name); + addr->message = string_sprintf( + rc == HOST_FIND_SECURITY + ? "lookup of IP address for %s was insecure" + : "failed to lookup IP address for %s", + host->name); } continue; } @@ -3926,6 +3876,7 @@ for (cutoff_retry = 0; { case hwhy_retry: hosts_retry++; break; case hwhy_failed: hosts_fail++; break; + case hwhy_insecure: case hwhy_deferred: hosts_defer++; break; } @@ -4109,8 +4060,9 @@ for (cutoff_retry = 0; && verify_check_given_host(&ob->hosts_require_tls, host) != OK ) { - log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " - "to %s [%s] (not in hosts_require_tls)", host->name, host->address); + log_write(0, LOG_MAIN, + "%s: delivering unencrypted to H=%s [%s] (not in hosts_require_tls)", + first_addr->message, host->name, host->address); first_addr = prepare_addresses(addrlist, host); rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, &message_defer, TRUE);