X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/f6c332bd03c89f108c7fe531156cb18d7888ba35..5b4569757c6dc749c250f065705f65c938bffb2e:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ac013470b..4db67f871 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/transports/smtp.c,v 1.28 2006/10/30 16:41:04 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2006 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -21,6 +19,8 @@ before the lower case letters). Some live in the transport_instance block so as to be publicly visible; these are flagged with opt_public. */ optionlist smtp_transport_options[] = { + { "address_retry_include_sender", opt_bool, + (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) }, { "allow_localhost", opt_bool, (void *)offsetof(smtp_transport_options_block, allow_localhost) }, { "authenticated_sender", opt_stringptr, @@ -37,19 +37,19 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, data_timeout) }, { "delay_after_cutoff", opt_bool, (void *)offsetof(smtp_transport_options_block, delay_after_cutoff) }, -#ifdef EXPERIMENTAL_DOMAINKEYS - { "dk_canon", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dk_canon) }, - { "dk_domain", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dk_domain) }, - { "dk_headers", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dk_headers) }, - { "dk_private_key", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dk_private_key) }, - { "dk_selector", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dk_selector) }, - { "dk_strict", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dk_strict) }, +#ifndef DISABLE_DKIM + { "dkim_canon", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dkim_canon) }, + { "dkim_domain", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dkim_domain) }, + { "dkim_private_key", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dkim_private_key) }, + { "dkim_selector", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dkim_selector) }, + { "dkim_sign_headers", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dkim_sign_headers) }, + { "dkim_strict", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dkim_strict) }, #endif { "dns_qualify_single", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, @@ -61,34 +61,44 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, final_timeout) }, { "gethostbyname", opt_bool, (void *)offsetof(smtp_transport_options_block, gethostbyname) }, +#ifdef SUPPORT_TLS + { "gnutls_require_kx", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) }, + { "gnutls_require_mac", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, gnutls_require_mac) }, + { "gnutls_require_protocols", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, gnutls_require_proto) }, +#endif { "helo_data", opt_stringptr, (void *)offsetof(smtp_transport_options_block, helo_data) }, { "hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts) }, { "hosts_avoid_esmtp", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) }, - #ifdef SUPPORT_TLS + { "hosts_avoid_pipelining", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) }, +#ifdef SUPPORT_TLS { "hosts_avoid_tls", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) }, - #endif +#endif { "hosts_max_try", opt_int, (void *)offsetof(smtp_transport_options_block, hosts_max_try) }, { "hosts_max_try_hardlimit", opt_int, (void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) }, - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS { "hosts_nopass_tls", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) }, - #endif +#endif { "hosts_override", opt_bool, (void *)offsetof(smtp_transport_options_block, hosts_override) }, { "hosts_randomize", opt_bool, (void *)offsetof(smtp_transport_options_block, hosts_randomize) }, { "hosts_require_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_auth) }, - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS { "hosts_require_tls", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_tls) }, - #endif +#endif { "hosts_try_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_auth) }, { "interface", opt_stringptr, @@ -111,20 +121,22 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, serialize_hosts) }, { "size_addition", opt_int, (void *)offsetof(smtp_transport_options_block, size_addition) } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS ,{ "tls_certificate", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_certificate) }, { "tls_crl", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_crl) }, { "tls_privatekey", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_privatekey) }, - { "tls_require_ciphers", opt_stringptr, + { "tls_require_ciphers", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) }, + { "tls_sni", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, tls_sni) }, { "tls_tempfail_tryclear", opt_bool, (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) }, { "tls_verify_certificates", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) } - #endif +#endif }; /* Size of the options list. An extern variable has to be used so that its @@ -150,6 +162,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* hosts_require_auth */ NULL, /* hosts_require_tls */ NULL, /* hosts_avoid_tls */ + NULL, /* hosts_avoid_pipelining */ NULL, /* hosts_avoid_esmtp */ NULL, /* hosts_nopass_tls */ 5*60, /* command_timeout */ @@ -159,6 +172,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { 1024, /* size_addition */ 5, /* hosts_max_try */ 50, /* hosts_max_try_hardlimit */ + TRUE, /* address_retry_include_sender */ FALSE, /* allow_localhost */ FALSE, /* authenticated_sender_force */ FALSE, /* gethostbyname */ @@ -170,22 +184,26 @@ smtp_transport_options_block smtp_transport_option_defaults = { TRUE, /* keepalive */ FALSE, /* lmtp_ignore_quota */ TRUE /* retry_include_ip_address */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ NULL, /* tls_crl */ NULL, /* tls_privatekey */ NULL, /* tls_require_ciphers */ + NULL, /* gnutls_require_kx */ + NULL, /* gnutls_require_mac */ + NULL, /* gnutls_require_proto */ NULL, /* tls_verify_certificates */ - TRUE /* tls_tempfail_tryclear */ - #endif - #ifdef EXPERIMENTAL_DOMAINKEYS - ,NULL, /* dk_canon */ - NULL, /* dk_domain */ - NULL, /* dk_headers */ - NULL, /* dk_private_key */ - NULL, /* dk_selector */ - NULL /* dk_strict */ - #endif + TRUE, /* tls_tempfail_tryclear */ + NULL /* tls_sni */ +#endif +#ifndef DISABLE_DKIM + ,NULL, /* dkim_canon */ + NULL, /* dkim_domain */ + NULL, /* dkim_private_key */ + NULL, /* dkim_selector */ + NULL, /* dkim_sign_headers */ + NULL /* dkim_strict */ +#endif }; @@ -287,7 +305,8 @@ if (tblock->retry_use_local_part == TRUE_UNSET) /* Set the default port according to the protocol */ if (ob->port == NULL) - ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" : US"smtp"; + ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" : + (strcmpic(ob->protocol, US"smtps") == 0)? US"smtps" : US"smtp"; /* Set up the setup entry point, to be called before subprocesses for this transport. */ @@ -555,19 +574,21 @@ 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 - count the number of responses to read - 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 + 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 + 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 @@ -580,7 +601,8 @@ 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 pending_MAIL, + 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) { @@ -637,10 +659,16 @@ while (count-- > 0) addr->transport_return = PENDING_OK; /* If af_dr_retry_exists is set, there was a routing delay on this address; - ensure that any address-specific retry record is expunged. */ + ensure that any address-specific retry record is expunged. We do this both + for the basic key and for the version that also includes the sender. */ if (testflag(addr, af_dr_retry_exists)) + { + uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key, + sender_address); + retry_add_item(addr, altkey, rf_delete); retry_add_item(addr, addr->address_retry_key, rf_delete); + } } /* Timeout while reading the response */ @@ -705,10 +733,17 @@ while (count-- > 0) update_waiting = FALSE; - /* Add a retry item for the address so that it doesn't get tried - again too soon. */ + /* 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. */ - retry_add_item(addr, addr->address_retry_key, 0); + 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); } } } /* Loop for next RCPT response */ @@ -756,7 +791,8 @@ return yield; /* If continue_hostname is not null, we get here only when continuing to deliver down an existing channel. The channel was passed as the standard -input. +input. TLS is never active on a passed channel; the previous process always +closes it down before passing the connection on. Otherwise, we have to make a connection to the remote host, and do the initial protocol exchange. @@ -811,6 +847,7 @@ time_t start_delivery_time = time(NULL); smtp_transport_options_block *ob = (smtp_transport_options_block *)(tblock->options_block); BOOL lmtp = strcmpic(ob->protocol, US"lmtp") == 0; +BOOL smtps = strcmpic(ob->protocol, US"smtps") == 0; BOOL ok = FALSE; BOOL send_rset = TRUE; BOOL send_quit = TRUE; @@ -824,7 +861,7 @@ smtp_outblock outblock; int max_rcpt = tblock->max_addresses; uschar *igquotstr = US""; uschar *local_authenticated_sender = authenticated_sender; -uschar *helo_data; +uschar *helo_data = NULL; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; uschar *p; @@ -853,16 +890,14 @@ outblock.ptr = outbuffer; outblock.cmd_count = 0; outblock.authenticating = FALSE; -/* Expand the greeting message */ +/* Reset the parameters of a TLS session. */ -helo_data = expand_string(ob->helo_data); -if (helo_data == NULL) - { - uschar *message = string_sprintf("failed to expand helo_data: %s", - expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); - return ERROR; - } +tls_bits = 0; +tls_cipher = NULL; +tls_peerdn = NULL; +#ifndef USE_GNUTLS +tls_sni = NULL; +#endif /* If an authenticated_sender override has been specified for this transport instance, expand it. If the expansion is forced to fail, and there was already @@ -887,6 +922,14 @@ if (ob->authenticated_sender != NULL) else if (new[0] != 0) local_authenticated_sender = new; } +#ifndef SUPPORT_TLS +if (smtps) + { + set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE); + return ERROR; + } +#endif + /* Make a connection to the host if this isn't a continued delivery, and handle the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled specially so they can be identified for retries. */ @@ -895,7 +938,8 @@ if (continue_hostname == NULL) { inblock.sock = outblock.sock = smtp_connect(host, host_af, port, interface, ob->connect_timeout, - ob->keepalive); + ob->keepalive); /* This puts port into host->port */ + if (inblock.sock < 0) { set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, @@ -903,12 +947,33 @@ if (continue_hostname == NULL) return DEFER; } + /* Expand the greeting message while waiting for the initial response. (Makes + sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is + delayed till here so that $sending_interface and $sending_port are set. */ + + helo_data = expand_string(ob->helo_data); + /* The first thing is to wait for an initial OK response. The dreaded "goto" is nevertheless a reasonably clean way of programming this kind of logic, where you want to escape on any error. */ - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + if (!smtps) + { + if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + ob->command_timeout)) goto RESPONSE_FAILED; + + /* Now check if the helo_data expansion went well, and sign off cleanly if + it didn't. */ + + if (helo_data == NULL) + { + uschar *message = string_sprintf("failed to expand helo_data: %s", + expand_string_message); + set_errno(addrlist, 0, message, DEFER, FALSE); + yield = DEFER; + goto SEND_QUIT; + } + } /** Debugging without sending a message addrlist->transport_return = DEFER; @@ -948,6 +1013,20 @@ goto SEND_QUIT; esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL, host->name, host->address, NULL) != OK; + /* Alas; be careful, since this goto is not an error-out, so conceivably + we might set data between here and the target which we assume to exist + and be usable. I can see this coming back to bite us. */ + #ifdef SUPPORT_TLS + if (smtps) + { + tls_offered = TRUE; + suppress_tls = FALSE; + ob->tls_tempfail_tryclear = FALSE; + smtp_command = US"SSL-on-connect"; + goto TLS_NEGOTIATE; + } + #endif + if (esmtp) { if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", @@ -1001,6 +1080,7 @@ else { inblock.sock = outblock.sock = fileno(stdin); smtp_command = big_buffer; + host->port = port; /* Record the port that was used */ } /* If TLS is available on this connection, whether continued or not, attempt to @@ -1030,23 +1110,32 @@ if (tls_offered && !suppress_tls && if (!smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2', ob->command_timeout)) { - Ustrncpy(buffer, buffer2, sizeof(buffer)); if (errno != 0 || buffer2[0] == 0 || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)) + { + Ustrncpy(buffer, buffer2, sizeof(buffer)); goto RESPONSE_FAILED; + } } /* STARTTLS accepted: try to negotiate a TLS session. */ else + TLS_NEGOTIATE: { - int rc = tls_client_start(inblock.sock, host, addrlist, + int rc = tls_client_start(inblock.sock, + host, + addrlist, NULL, /* No DH param */ ob->tls_certificate, ob->tls_privatekey, + ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl, ob->tls_require_ciphers, + ob->gnutls_require_mac, + ob->gnutls_require_kx, + ob->gnutls_require_proto, ob->command_timeout); /* TLS negotiation failed; give an error. From outside, this function may @@ -1074,12 +1163,50 @@ if (tls_offered && !suppress_tls && } } -/* If we started TLS, redo the EHLO/LHLO exchange over the secure channel. */ +/* if smtps, we'll have smtp_command set to something else; always safe to +reset it here. */ +smtp_command = big_buffer; + +/* If we started TLS, redo the EHLO/LHLO exchange over the secure channel. If +helo_data is null, we are dealing with a connection that was passed from +another process, and so we won't have expanded helo_data above. We have to +expand it here. $sending_ip_address and $sending_port are set up right at the +start of the Exim process (in exim.c). */ if (tls_active >= 0) { - if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : "EHLO", - helo_data) < 0) + char *greeting_cmd; + if (helo_data == NULL) + { + helo_data = expand_string(ob->helo_data); + if (helo_data == NULL) + { + uschar *message = string_sprintf("failed to expand helo_data: %s", + expand_string_message); + set_errno(addrlist, 0, message, DEFER, FALSE); + yield = DEFER; + goto SEND_QUIT; + } + } + + /* For SMTPS we need to wait for the initial OK response. */ + if (smtps) + { + if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + ob->command_timeout)) goto RESPONSE_FAILED; + } + + if (esmtp) + greeting_cmd = "EHLO"; + else + { + greeting_cmd = "HELO"; + DEBUG(D_transport) + debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n"); + } + + if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", + lmtp? "LHLO" : greeting_cmd, helo_data) < 0) goto SEND_FAILED; if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) @@ -1130,9 +1257,12 @@ if (continue_hostname == NULL PCRE_EOPT, NULL, 0) >= 0; /* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched - the current host, esmtp will be false, so PIPELINING can never be used. */ + the current host, esmtp will be false, so PIPELINING can never be used. If + the current host matches hosts_avoid_pipelining, don't do it. */ smtp_use_pipelining = esmtp && + verify_check_this_host(&(ob->hosts_avoid_pipelining), NULL, host->name, + host->address, NULL) != OK && pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0, PCRE_EOPT, NULL, 0) >= 0; @@ -1166,14 +1296,25 @@ if (continue_hostname == NULL DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n"); /* Scan the configured authenticators looking for one which is configured - for use as a client and whose name matches an authentication mechanism - supported by the server. If one is found, attempt to authenticate by - calling its client function. */ + for use as a client, which is not suppressed by client_condition, and + whose name matches an authentication mechanism supported by the server. + If one is found, attempt to authenticate by calling its client function. + */ for (au = auths; !smtp_authenticated && au != NULL; au = au->next) { uschar *p = names; - if (!au->client) continue; + if (!au->client || + (au->client_condition != NULL && + !expand_check_condition(au->client_condition, au->name, + US"client authenticator"))) + { + DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n", + au->name, + (au->client)? "client_condition is false" : + "not configured as a client"); + continue; + } /* Loop to scan supported server mechanisms */ @@ -1280,6 +1421,7 @@ if (tblock->filter_command != NULL) sprintf(CS buffer, "%.50s transport", tblock->name); rc = transport_set_up_command(&transport_filter_argv, tblock->filter_command, TRUE, DEFER, addrlist, buffer, NULL); + transport_filter_timeout = tblock->filter_timeout; /* On failure, copy the error to all addresses, abandon the SMTP call, and yield ERROR. */ @@ -1404,8 +1546,9 @@ for (addr = first_addr; if (count > 0) { switch(sync_responses(first_addr, tblock->rcpt_include_affixes, - &sync_addr, host, count, pending_MAIL, 0, &inblock, - ob->command_timeout, buffer, sizeof(buffer))) + &sync_addr, host, count, ob->address_retry_include_sender, + pending_MAIL, 0, &inblock, ob->command_timeout, buffer, + sizeof(buffer))) { case 3: ok = TRUE; /* 2xx & 5xx => OK & progress made */ case 2: completed_address = TRUE; /* 5xx (only) => progress made */ @@ -1453,8 +1596,8 @@ if (ok || (smtp_use_pipelining && !mua_wrapper)) int count = smtp_write_command(&outblock, FALSE, "DATA\r\n"); if (count < 0) goto SEND_FAILED; switch(sync_responses(first_addr, tblock->rcpt_include_affixes, &sync_addr, - host, count, pending_MAIL, ok? +1 : -1, &inblock, - ob->command_timeout, buffer, sizeof(buffer))) + host, count, ob->address_retry_include_sender, pending_MAIL, + ok? +1 : -1, &inblock, ob->command_timeout, buffer, sizeof(buffer))) { case 3: ok = TRUE; /* 2xx & 5xx => OK & progress made */ case 2: completed_address = TRUE; /* 5xx (only) => progress made */ @@ -1488,23 +1631,22 @@ if (!ok) ok = TRUE; else DEBUG(D_transport|D_v) debug_printf(" SMTP>> writing message and terminating \".\"\n"); transport_count = 0; -#ifdef EXPERIMENTAL_DOMAINKEYS - if ( (ob->dk_private_key != NULL) && (ob->dk_selector != NULL) ) - ok = dk_transport_write_message(addrlist, inblock.sock, - topt_use_crlf | topt_end_dot | topt_escape_headers | - (tblock->body_only? topt_no_headers : 0) | - (tblock->headers_only? topt_no_body : 0) | - (tblock->return_path_add? topt_add_return_path : 0) | - (tblock->delivery_date_add? topt_add_delivery_date : 0) | - (tblock->envelope_to_add? topt_add_envelope_to : 0), - 0, /* No size limit */ - tblock->add_headers, tblock->remove_headers, - US".", US"..", /* Escaping strings */ - tblock->rewrite_rules, tblock->rewrite_existflags, - ob->dk_private_key, ob->dk_domain, ob->dk_selector, - ob->dk_canon, ob->dk_headers, ob->dk_strict); - else -#endif +#ifndef DISABLE_DKIM + ok = dkim_transport_write_message(addrlist, inblock.sock, + topt_use_crlf | topt_end_dot | topt_escape_headers | + (tblock->body_only? topt_no_headers : 0) | + (tblock->headers_only? topt_no_body : 0) | + (tblock->return_path_add? topt_add_return_path : 0) | + (tblock->delivery_date_add? topt_add_delivery_date : 0) | + (tblock->envelope_to_add? topt_add_envelope_to : 0), + 0, /* No size limit */ + tblock->add_headers, tblock->remove_headers, + US".", US"..", /* Escaping strings */ + tblock->rewrite_rules, tblock->rewrite_existflags, + ob->dkim_private_key, ob->dkim_domain, ob->dkim_selector, + ob->dkim_canon, ob->dkim_strict, ob->dkim_sign_headers + ); +#else ok = transport_write_message(addrlist, inblock.sock, topt_use_crlf | topt_end_dot | topt_escape_headers | (tblock->body_only? topt_no_headers : 0) | @@ -1516,6 +1658,7 @@ if (!ok) ok = TRUE; else tblock->add_headers, tblock->remove_headers, US".", US"..", /* Escaping strings */ tblock->rewrite_rules, tblock->rewrite_existflags); +#endif /* transport_write_message() uses write() because it is called from other places to write to non-sockets. This means that under some OS (e.g. Solaris) @@ -1629,6 +1772,11 @@ if (!ok) ok = TRUE; else continue; } completed_address = TRUE; /* NOW we can set this flag */ + if ((log_extra_selector & LX_smtp_confirmation) != 0) + { + uschar *s = string_printing(buffer); + conf = (s == buffer)? (uschar *)string_copy(s) : s; + } } /* SMTP, or success return from LMTP for this address. Pass back the @@ -1660,7 +1808,7 @@ if (!ok) ok = TRUE; else /* Ensure the journal file is pushed out to disk. */ - if (fsync(journal_fd) < 0) + if (EXIMfsync(journal_fd) < 0) log_write(0, LOG_MAIN|LOG_PANIC, "failed to fsync journal: %s", strerror(errno)); } @@ -1899,9 +2047,12 @@ if (completed_address && ok && send_quit) if (tls_active >= 0) { tls_close(TRUE); - ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 && - smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout); + if (smtps) + ok = FALSE; + else + ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 && + smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + ob->command_timeout); } #endif @@ -2159,6 +2310,15 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL)) host_build_hostlist(&hostlist, s, ob->hosts_randomize); + /* Check that the expansion yielded something useful. */ + if (hostlist == NULL) + { + addrlist->message = + string_sprintf("%s transport has empty hosts setting", tblock->name); + addrlist->transport_return = PANIC; + return FALSE; /* Only top address has status */ + } + /* If there was no expansion of hosts, save the host list for next time. */