X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/f6c332bd03c89f108c7fe531156cb18d7888ba35..41c7c167f4d3552804bfaf7278d72fc448b851ff:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ac013470b..cf09010cb 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/transports/smtp.c,v 1.28 2006/10/30 16:41:04 ph10 Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.c,v 1.33 2007/01/30 15:10:59 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2006 */ +/* Copyright (c) University of Cambridge 1995 - 2007 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -21,6 +21,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, @@ -61,6 +63,14 @@ 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, @@ -159,6 +169,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 */ @@ -175,6 +186,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { 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 @@ -555,19 +569,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 +596,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) { @@ -705,10 +722,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 */ @@ -824,7 +848,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,17 +877,6 @@ outblock.ptr = outbuffer; outblock.cmd_count = 0; outblock.authenticating = FALSE; -/* Expand the greeting message */ - -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; - } - /* 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 an authenticated_sender for this message, the original value will be used. @@ -903,6 +916,12 @@ 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. */ @@ -910,6 +929,18 @@ if (continue_hostname == NULL) 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; goto SEND_QUIT; @@ -1040,13 +1071,18 @@ if (tls_offered && !suppress_tls && else { - 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_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,10 +1110,27 @@ if (tls_offered && !suppress_tls && } } -/* If we started TLS, redo the EHLO/LHLO exchange over the secure channel. */ +/* 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 (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; + } + } + if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : "EHLO", helo_data) < 0) goto SEND_FAILED; @@ -1404,8 +1457,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 +1507,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 */ @@ -1660,7 +1714,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)); }