-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.4 2005/01/04 16:36:28 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.11 2005/04/28 13:06:32 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
(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) },
+#endif
{ "dns_qualify_single", opt_bool,
(void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
{ "dns_search_parents", opt_bool,
10*60, /* final timeout */
1024, /* size_addition */
5, /* hosts_max_try */
- 50, /* hosts_max_try_hardlimit */
+ 50, /* hosts_max_try_hardlimit */
FALSE, /* allow_localhost */
FALSE, /* gethostbyname */
TRUE, /* dns_qualify_single */
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
};
status means that an address is not currently being processed.
Arguments:
- addrlist points to a chain of addresses
- errno_value to put in each address's errno field
- msg to put in each address's message field
- rc to put in each address's transport_return field
+ addrlist points to a chain of addresses
+ errno_value to put in each address's errno field
+ msg to put in each address's message field
+ rc to put in each address's transport_return field
+ pass_message if TRUE, set the "pass message" flag in the address
If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in
the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate
*/
static
-void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc)
+void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
+ BOOL pass_message)
{
address_item *addr;
int orvalue = 0;
if (addr->transport_return < PENDING) continue;
addr->basic_errno = errno_value;
addr->more_errno |= orvalue;
- if (msg != NULL) addr->message = msg;
+ if (msg != NULL)
+ {
+ addr->message = msg;
+ if (pass_message) setflag(addr, af_pass_message);
+ }
addr->transport_return = rc;
}
}
chosen.
Arguments:
- host the current host, to get its name for messages
- errno_value pointer to the errno value
- more_errno from the top address for use with ERRNO_FILTER_FAIL
- buffer the SMTP response buffer
- yield where to put a one-digit SMTP response code
- message where to put an errror message
-
-Returns: TRUE if an SMTP "QUIT" command should be sent, else FALSE
+ host the current host, to get its name for messages
+ errno_value pointer to the errno value
+ more_errno from the top address for use with ERRNO_FILTER_FAIL
+ buffer the SMTP response buffer
+ yield where to put a one-digit SMTP response code
+ message where to put an errror message
+ pass_message set TRUE if message is an SMTP response
+
+Returns: TRUE if an SMTP "QUIT" command should be sent, else FALSE
*/
static BOOL check_response(host_item *host, int *errno_value, int more_errno,
- uschar *buffer, int *yield, uschar **message)
+ uschar *buffer, int *yield, uschar **message, BOOL *pass_message)
{
uschar *pl = US"";
if (*errno_value == ERRNO_FILTER_FAIL)
{
*message = US string_sprintf("transport filter process failed (%d)%s",
- more_errno,
+ more_errno,
(more_errno == EX_EXECFAILED)? ": unable to execute command" : "");
return FALSE;
}
if (buffer[0] != 0)
{
uschar *s = string_printing(buffer);
- *message = US string_sprintf("SMTP error from remote mailer after %s%s: "
+ *message = US string_sprintf("SMTP error from remote mail server after %s%s: "
"host %s [%s]: %s", pl, smtp_command, host->name, host->address, s);
+ *pass_message = TRUE;
*yield = buffer[0];
return TRUE;
}
uschar *message = string_sprintf("SMTP timeout while connected to %s [%s] "
"after RCPT TO:<%s>", host->name, host->address,
transport_rcpt_address(addr, include_affixes));
- set_errno(addrlist, save_errno, message, DEFER);
+ set_errno(addrlist, save_errno, message, DEFER, FALSE);
retry_add_item(addr, addr->address_retry_key, 0);
host->update_waiting = FALSE;
return -1;
else
{
addr->message =
- string_sprintf("SMTP error from remote mailer after RCPT TO:<%s>: "
+ string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: "
"host %s [%s]: %s", transport_rcpt_address(addr, include_affixes),
host->name, host->address, string_printing(buffer));
+ setflag(addr, af_pass_message);
deliver_msglog("%s %s\n", tod_stamp(tod_log), addr->message);
/* The response was 5xx */
{
int code;
uschar *msg;
+ BOOL pass_message;
if (pending_DATA > 0 || (yield & 1) != 0) return -3;
- (void)check_response(host, &errno, 0, buffer, &code, &msg);
+ (void)check_response(host, &errno, 0, 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);
}
BOOL completed_address = FALSE;
BOOL esmtp = TRUE;
BOOL pending_MAIL;
+BOOL pass_message = FALSE;
smtp_inblock inblock;
smtp_outblock outblock;
int max_rcpt = tblock->max_addresses;
{
uschar *message = string_sprintf("failed to expand helo_data: %s",
expand_string_message);
- set_errno(addrlist, 0, message, DEFER);
+ set_errno(addrlist, 0, message, DEFER, FALSE);
return ERROR;
}
{
uschar *message = string_sprintf("failed to expand "
"authenticated_sender: %s", expand_string_message);
- set_errno(addrlist, 0, message, DEFER);
+ set_errno(addrlist, 0, message, DEFER, FALSE);
return ERROR;
}
}
if (inblock.sock < 0)
{
set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
- NULL, DEFER);
+ NULL, DEFER, FALSE);
return DEFER;
}
for (addr = addrlist; addr != NULL; addr = addr->next)
{
- addr->cipher = tls_cipher;
- addr->peerdn = tls_peerdn;
+ if (addr->transport_return == PENDING_DEFER)
+ {
+ addr->cipher = tls_cipher;
+ addr->peerdn = tls_peerdn;
+ }
}
}
}
case ERROR:
yield = ERROR;
- set_errno(addrlist, 0, string_copy(buffer), DEFER);
+ set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE);
goto SEND_QUIT;
}
{
yield = DEFER;
set_errno(addrlist, ERRNO_AUTHFAIL,
- string_sprintf("authentication required but %s", fail_reason), DEFER);
+ string_sprintf("authentication required but %s", fail_reason), DEFER,
+ FALSE);
goto SEND_QUIT;
}
}
if (!rc)
{
- set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER);
+ set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
+ FALSE);
yield = ERROR;
goto SEND_QUIT;
}
}
if (badaddr != NULL)
{
- set_errno(addrlist, 0, badaddr->message, FAIL);
+ set_errno(addrlist, 0, badaddr->message, FAIL,
+ testflag(badaddr, af_pass_message));
ok = FALSE;
}
}
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
ok = transport_write_message(addrlist, inblock.sock,
topt_use_crlf | topt_end_dot | topt_escape_headers |
(tblock->body_only? topt_no_headers : 0) |
save_errno = errno;
message = NULL;
send_quit = check_response(host, &save_errno, addrlist->more_errno,
- buffer, &code, &message);
+ buffer, &code, &message, &pass_message);
goto FAILED;
SEND_FAILED:
{
if (code == '5')
{
- set_errno(addrlist, save_errno, message, FAIL);
+ set_errno(addrlist, save_errno, message, FAIL, pass_message);
}
else
{
- set_errno(addrlist, save_errno, message, DEFER);
+ set_errno(addrlist, save_errno, message, DEFER, pass_message);
yield = DEFER;
}
}
{
yield = (save_errno == ERRNO_CHHEADER_FAIL ||
save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
- set_errno(addrlist, save_errno, message, DEFER);
+ set_errno(addrlist, save_errno, message, DEFER, pass_message);
}
/* Otherwise we have a message-specific error response from the remote
{
if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */
- set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER);
+ set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER,
+ pass_message);
/* If there's an errno, the message contains just the identity of
the host. */
))
{
uschar *msg;
+ BOOL pass_message;
if (send_rset)
{
ob->command_timeout)))
{
int code;
- send_quit = check_response(host, &errno, 0, buffer, &code, &msg);
+ send_quit = check_response(host, &errno, 0, buffer, &code, &msg,
+ &pass_message);
if (!send_quit)
{
DEBUG(D_transport) debug_printf("%s\n", msg);
/* If RSET failed and there are addresses left, they get deferred. */
- else set_errno(first_addr, errno, msg, DEFER);
+ else set_errno(first_addr, errno, msg, DEFER, FALSE);
}
}
int hosts_retry = 0;
int hosts_serial = 0;
int hosts_total = 0;
-int total_hosts_tried = 0;
+int total_hosts_tried = 0;
address_item *addr;
BOOL expired = TRUE;
BOOL continuing = continue_hostname != NULL;
. If there are any addresses whose status is still DEFER, carry on to the
next host/IPaddress, unless we have tried the number of hosts given
by hosts_max_try or hosts_max_try_hardlimit; otherwise return. Note that
- there is some fancy logic for hosts_max_try that means its limit can be
- overstepped in some circumstances.
+ there is some fancy logic for hosts_max_try that means its limit can be
+ overstepped in some circumstances.
If we get to the end of the list, all hosts have deferred at least one address,
or not reached their retry times. If delay_after_cutoff is unset, it requests a
int unexpired_hosts_tried = 0;
for (host = hostlist;
- host != NULL &&
+ host != NULL &&
unexpired_hosts_tried < ob->hosts_max_try &&
total_hosts_tried < ob->hosts_max_try_hardlimit;
host = nexthost)
uschar *serialize_key = NULL;
/* Default next host is next host. :-) But this can vary if the
- hosts_max_try limit is hit (see below). */
+ hosts_max_try limit is hit (see below). It may also be reset if a host
+ address is looked up here (in case the host was multihomed). */
nexthost = host->next;
/* Find by name if so configured, or if it's an IP address. We don't
just copy the IP address, because we need the test-for-local to happen. */
- if (ob->gethostbyname || string_is_ip_address(host->name, NULL))
+ if (ob->gethostbyname || string_is_ip_address(host->name, NULL) > 0)
rc = host_find_byname(host, NULL, &canonical_name, TRUE);
else
{
continue; /* With next host */
}
+ /* Reset the default next host in case a multihomed host whose addresses
+ are not looked up till just above added to the host list. */
+
+ nexthost = host->next;
+
/* If queue_smtp is set (-odqs or the first part of a 2-stage run), or the
domain is in queue_smtp_domains, we don't actually want to attempt any
deliveries. When doing a queue run, queue_smtp_domains is always unset. If
if (dont_deliver)
{
host_item *host2;
- set_errno(addrlist, 0, NULL, OK);
+ set_errno(addrlist, 0, NULL, OK, FALSE);
for (addr = addrlist; addr != NULL; addr = addr->next)
{
addr->host_used = host;
/* This is for real. If the host is expired, we don't count it for
hosts_max_retry. This ensures that all hosts must expire before an address
- is timed out, unless hosts_max_try_hardlimit (which protects against
+ is timed out, unless hosts_max_try_hardlimit (which protects against
lunatic DNS configurations) is reached.
-
+
If the host is not expired and we are about to hit the hosts_max_retry
limit, check to see if there is a subsequent hosts with a different MX
value. If so, make that the next host, and don't count this one. This is a
because of hosts_max_try or hosts_max_try_hardlimit. In the former case, this
means we need to behave as if some hosts were skipped because their retry
time had not come. Specifically, this prevents the address from timing out.
- However, if we have hit hosts_max_try_hardlimit, we want to behave as if all
+ However, if we have hit hosts_max_try_hardlimit, we want to behave as if all
hosts were tried. */
if (host != NULL)
"hosts were tried\n");
}
else
- {
+ {
DEBUG(D_transport)
debug_printf("hosts_max_try limit caused some hosts to be skipped\n");
setflag(addr, af_retry_skipped);
- }
+ }
}
if (queue_smtp) /* no deliveries attempted */