* 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. */
/* Functions concerned with verifying things. The original code for callout
}
+/* Cutthrough-multi. If the existing cached cutthrough connection matches
+the one we would make for a subsequent recipient, use it. Send the RCPT TO
+and check the result, nonpipelined as it may be wanted immediately for
+recipient-verification.
+
+It seems simpler to deal with this case separately from the main callout loop.
+We will need to remember it has sent, or not, so that rcpt-acl tail code
+can do it there for the non-rcpt-verify case. For this we keep an addresscount.
+
+Return: TRUE for a definitive result for the recipient
+*/
+static int
+cutthrough_multi(address_item * addr, host_item * host_list,
+ transport_feedback * tf, int * yield)
+{
+BOOL done = FALSE;
+host_item * host;
+
+if (addr->transport == cutthrough.addr.transport)
+ for (host = host_list; host; host = host->next)
+ if (Ustrcmp(host->address, cutthrough.host.address) == 0)
+ {
+ int host_af;
+ uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
+ int port = 25;
+
+ deliver_host = host->name;
+ deliver_host_address = host->address;
+ deliver_host_port = host->port;
+ deliver_domain = addr->domain;
+ transport_name = addr->transport->name;
+
+ host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+
+ if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
+ US"callout") ||
+ !smtp_get_port(tf->port, addr, &port, US"callout"))
+ log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
+ addr->message);
+
+ if ( ( interface == cutthrough.interface
+ || ( interface
+ && cutthrough.interface
+ && Ustrcmp(interface, cutthrough.interface) == 0
+ ) )
+ && port == cutthrough.host.port
+ )
+ {
+ uschar * resp = NULL;
+
+ /* Match! Send the RCPT TO, set done from the response */
+ done =
+ smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n",
+ transport_rcpt_address(addr,
+ addr->transport->rcpt_include_affixes)) >= 0 &&
+ cutthrough_response('2', &resp, CUTTHROUGH_DATA_TIMEOUT) == '2';
+
+ /* This would go horribly wrong if a callout fail was ignored by ACL.
+ We punt by abandoning cutthrough on a reject, like the
+ first-rcpt does. */
+
+ if (done)
+ {
+ address_item * na = store_get(sizeof(address_item));
+ *na = cutthrough.addr;
+ cutthrough.addr = *addr;
+ cutthrough.addr.host_used = &cutthrough.host;
+ cutthrough.addr.next = na;
+
+ cutthrough.nrcpt++;
+ }
+ else
+ {
+ cancel_cutthrough_connection("recipient rejected");
+ if (!resp || errno == ETIMEDOUT)
+ {
+ HDEBUG(D_verify) debug_printf("SMTP timeout\n");
+ }
+ else if (errno == 0)
+ {
+ if (*resp == 0)
+ Ustrcpy(resp, US"connection dropped");
+
+ addr->message =
+ string_sprintf("response to \"%s\" was: %s",
+ big_buffer, string_printing(resp));
+
+ addr->user_message =
+ string_sprintf("Callout verification failed:\n%s", resp);
+
+ /* Hard rejection ends the process */
+
+ if (resp[0] == '5') /* Address rejected */
+ {
+ *yield = FAIL;
+ done = TRUE;
+ }
+ }
+ }
+ }
+ break; /* host_list */
+ }
+if (!done)
+ cancel_cutthrough_connection("incompatible connection");
+return done;
+}
+
+
/*************************************************
* Do callout verification for an address *
*************************************************/
? &recipient_verify_failure : &sender_verify_failure;
dbdata_callout_cache new_domain_record;
dbdata_callout_cache_address new_address_record;
-host_item *host;
time_t callout_start_time;
new_domain_record.result = ccache_unknown;
{
smtp_transport_options_block *ob =
(smtp_transport_options_block *)addr->transport->options_block;
+ host_item * host;
/* The information wasn't available in the cache, so we have to do a real
callout and save the result in the cache for next time, unless no_cache is set,
if (smtp_out && !disable_callout_flush) mac_smtp_fflush();
+ clearflag(addr, af_verify_pmfail); /* postmaster callout flag */
+ clearflag(addr, af_verify_nsfail); /* null sender callout flag */
+
/* cutthrough-multi: if a nonfirst rcpt has the same routing as the first,
and we are holding a cutthrough conn open, we can just append the rcpt to
that conn for verification purposes (and later delivery also). Simplest
-coding means skipping this whole loop and doing the append separately.
-
-We will need to remember it has been appended so that rcpt-acl tail code
-can do it there for the non-rcpt-verify case. For this we keep an addresscount.
-*/
+coding means skipping this whole loop and doing the append separately. */
/* Can we re-use an open cutthrough connection? */
if ( cutthrough.fd >= 0
&& !random_local_part
&& !pm_mailfrom
)
- {
- if (addr->transport == cutthrough.addr.transport)
- for (host = host_list; host; host = host->next)
- if (Ustrcmp(host->address, cutthrough.host.address) == 0)
- {
- int host_af;
- uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
- int port = 25;
-
- deliver_host = host->name;
- deliver_host_address = host->address;
- deliver_host_port = host->port;
- deliver_domain = addr->domain;
- transport_name = addr->transport->name;
-
- host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
-
- if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
- US"callout") ||
- !smtp_get_port(tf->port, addr, &port, US"callout"))
- log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
- addr->message);
-
- if ( ( interface == cutthrough.interface
- || ( interface
- && cutthrough.interface
- && Ustrcmp(interface, cutthrough.interface) == 0
- ) )
- && port == cutthrough.host.port
- )
- {
- uschar * resp = NULL;
-
- /* Match! Send the RCPT TO, append the addr, set done */
- done =
- smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n",
- transport_rcpt_address(addr,
- (addr->transport == NULL)? FALSE :
- addr->transport->rcpt_include_affixes)) >= 0 &&
- cutthrough_response('2', &resp, CUTTHROUGH_DATA_TIMEOUT) == '2';
-
- /* This would go horribly wrong if a callout fail was ignored by ACL.
- We punt by abandoning cutthrough on a reject, like the
- first-rcpt does. */
-
- if (done)
- {
- address_item * na = store_get(sizeof(address_item));
- *na = cutthrough.addr;
- cutthrough.addr = *addr;
- cutthrough.addr.host_used = &cutthrough.host;
- cutthrough.addr.next = na;
-
- cutthrough.nrcpt++;
- }
- else
- {
- cancel_cutthrough_connection("recipient rejected");
- if (!resp || errno == ETIMEDOUT)
- {
- HDEBUG(D_verify) debug_printf("SMTP timeout\n");
- }
- else if (errno == 0)
- {
- if (*resp == 0)
- Ustrcpy(resp, US"connection dropped");
+ done = cutthrough_multi(addr, host_list, tf, &yield);
- addr->message =
- string_sprintf("response to \"%s\" from %s [%s] was: %s",
- big_buffer, host->name, host->address,
- string_printing(resp));
-
- addr->user_message =
- string_sprintf("Callout verification failed:\n%s", resp);
-
- /* Hard rejection ends the process */
-
- if (resp[0] == '5') /* Address rejected */
- {
- yield = FAIL;
- done = TRUE;
- }
- }
- }
- }
- break; /* host_list */
- }
- if (!done)
- cancel_cutthrough_connection("incompatible connection");
- }
-
- /* Now make connections to the hosts and do real callouts. The list of hosts
- is passed in as an argument. */
+ /* If we did not use a cached connection, make connections to the hosts
+ and do real callouts. The list of hosts is passed in as an argument. */
for (host = host_list; host && !done; host = host->next)
{
int port = 25;
uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
smtp_context sx;
- uschar responsebuffer[4096];
-
- clearflag(addr, af_verify_pmfail); /* postmaster callout flag */
- clearflag(addr, af_verify_nsfail); /* null sender callout flag */
-
- /* Skip this host if we don't have an IP address for it. */
if (!host->address)
{
/* Set IPv4 or IPv6 */
- host_af = Ustrchr(host->address, ':') == NULL ? AF_INET : AF_INET6;
+ host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET;
/* Expand and interpret the interface and port strings. The latter will not
be used if there is a host-specific port (e.g. from a manualroute router).
sx.interface = interface;
sx.helo_data = tf->helo_data;
sx.tblock = addr->transport;
+ sx.verify = TRUE;
tls_retry_connection:
/* Set the address state so that errors are recorded in it */
SMTP command to send. If we tried TLS but it failed, try again without
if permitted */
- if ( (yield = smtp_setup_conn(&sx, FALSE, TRUE)) == DEFER
+ if ( (yield = smtp_setup_conn(&sx, FALSE)) == DEFER
&& addr->basic_errno == ERRNO_TLSFAILURE
&& ob->tls_tempfail_tryclear
&& verify_check_given_host(&ob->hosts_require_tls, host) != OK
log_write(0, LOG_MAIN, "TLS session failure:"
" callout unencrypted to %s [%s] (not in hosts_require_tls)",
host->name, host->address);
- yield = smtp_setup_conn(&sx, TRUE, TRUE);
+ addr->transport_return = PENDING_DEFER;
+ yield = smtp_setup_conn(&sx, TRUE);
}
if (yield != OK)
{
- if (addr->message) addr->message = string_sprintf("%s [%s] %s",
- host->name, host->address, addr->message);
errno = addr->basic_errno;
transport_name = NULL;
deliver_host = deliver_host_address = NULL;
addr->authenticator = client_authenticator;
addr->auth_id = client_authenticated_id;
- /* Build a mail-AUTH string (re-using responsebuffer for convenience */
-
- done =
- !smtp_mail_auth_str(responsebuffer, sizeof(responsebuffer), addr, ob)
- && (
- (addr->auth_sndr = client_authenticated_sender),
+ sx.from_addr = from_address;
+ sx.first_addr = sx.sync_addr = addr;
+ sx.ok = FALSE; /*XXX these 3 last might not be needed for verify? */
+ sx.send_rset = TRUE;
+ sx.completed_addr = FALSE;
- /* Send the MAIL command */
-
- (smtp_write_command(&sx.outblock, FALSE,
-#ifdef SUPPORT_I18N
- addr->prop.utf8_msg && !addr->prop.utf8_downcvt
- ? "MAIL FROM:<%s>%s%s SMTPUTF8\r\n"
- :
-#endif
- "MAIL FROM:<%s>%s%s\r\n",
- from_address,
- responsebuffer,
- options & vopt_is_recipient && sx.peer_offered & PEER_OFFERED_SIZE
- ? string_sprintf(" SIZE=%d", message_size + ob->size_addition)
- : US""
-
- ) >= 0)
- )
+ new_domain_record.result =
+ old_domain_cache_result == ccache_reject_mfnull
+ ? ccache_reject_mfnull : ccache_accept;
- && smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
- '2', callout);
+ /* Do the random local part check first. Temporarily replace the recipient
+ with the "random" value */
- deliver_host = deliver_host_address = NULL;
- deliver_domain = save_deliver_domain;
-
- /* If the host does not accept MAIL FROM:<>, arrange to cache this
- information, but again, don't record anything for an I/O error or a defer. Do
- not cache rejections of MAIL when a non-empty sender has been used, because
- that blocks the whole domain for all senders. */
-
- if (!done)
- {
- *failure_ptr = US"mail"; /* At or before MAIL */
- if (errno == 0 && responsebuffer[0] == '5')
- {
- setflag(addr, af_verify_nsfail);
- if (from_address[0] == 0)
- new_domain_record.result = ccache_reject_mfnull;
- }
- }
-
- /* Otherwise, proceed to check a "random" address (if required), then the
- given address, and the postmaster address (if required). Between each check,
- issue RSET, because some servers accept only one recipient after MAIL
- FROM:<>.
-
- Before doing this, set the result in the domain cache record to "accept",
- unless its previous value was ccache_reject_mfnull. In that case, the domain
- rejects MAIL FROM:<> and we want to continue to remember that. When that is
- the case, we have got here only in the case of a recipient verification with
- a non-null sender. */
-
- else
+ if (random_local_part)
{
+ uschar * main_address = addr->address;
const uschar * rcpt_domain = addr->domain;
#ifdef SUPPORT_I18N
}
#endif
- new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull
- ? ccache_reject_mfnull : ccache_accept;
-
- /* Do the random local part check first */
-
- if (random_local_part != NULL)
- {
- uschar randombuffer[1024];
- BOOL random_ok =
- smtp_write_command(&sx.outblock, FALSE,
- "RCPT TO:<%.1000s@%.1000s>\r\n", random_local_part,
- rcpt_domain) >= 0 &&
- smtp_read_response(&sx.inblock, randombuffer,
- sizeof(randombuffer), '2', callout);
-
- /* Remember when we last did a random test */
-
- new_domain_record.random_stamp = time(NULL);
-
- /* If accepted, we aren't going to do any further tests below. */
-
- if (random_ok)
- new_domain_record.random_result = ccache_accept;
-
- /* Otherwise, cache a real negative response, and get back to the right
- state to send RCPT. Unless there's some problem such as a dropped
- connection, we expect to succeed, because the commands succeeded above.
- However, some servers drop the connection after responding to an
- invalid recipient, so on (any) error we drop and remake the connection.
- */
-
- else if (errno == 0)
- {
- /* This would be ok for 1st rcpt a cutthrough, but no way to
- handle a subsequent. So refuse to support any */
- cancel_cutthrough_connection("random-recipient");
-
- if (randombuffer[0] == '5')
- new_domain_record.random_result = ccache_reject;
-
- done =
- smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0 &&
- smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
- '2', callout) &&
+ /* This would be ok for 1st rcpt of a cutthrough (XXX do we have a count?) , but no way to
+ handle a subsequent because of the RSET. So refuse to support any. */
+ cancel_cutthrough_connection("random-recipient");
+
+ addr->address = string_sprintf("%s@%.1000s",
+ random_local_part, rcpt_domain);
+ done = FALSE;
+
+ /* If accepted, we aren't going to do any further tests below.
+ Otherwise, cache a real negative response, and get back to the right
+ state to send RCPT. Unless there's some problem such as a dropped
+ connection, we expect to succeed, because the commands succeeded above.
+ However, some servers drop the connection after responding to an
+ invalid recipient, so on (any) error we drop and remake the connection.
+ XXX We don't care about that for postmaster_full. Should we?
+
+ XXX could we add another flag to the context, and have the common
+ code emit the RSET too? Even pipelined after the RCPT...
+ Then the main-verify call could use it if there's to be a subsequent
+ postmaster-verify.
+ The sync_responses() would need to be taught about it and we'd
+ need another return code filtering out to here.
+ */
- smtp_write_command(&sx.outblock, FALSE,
-#ifdef SUPPORT_I18N
- addr->prop.utf8_msg && !addr->prop.utf8_downcvt
- ? "MAIL FROM:<%s> SMTPUTF8\r\n"
- :
-#endif
- "MAIL FROM:<%s>\r\n",
- from_address) >= 0 &&
- smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
- '2', callout);
+ if (smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0)
+ switch(addr->transport_return)
+ {
+ case PENDING_OK:
+ new_domain_record.random_result = ccache_accept;
+ break;
+ case FAIL:
+ new_domain_record.random_result = ccache_reject;
+
+ /* Between each check, issue RSET, because some servers accept only
+ one recipient after MAIL FROM:<>.
+ XXX We don't care about that for postmaster_full. Should we? */
+
+ if ((done =
+ smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0 &&
+ smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer),
+ '2', callout)))
+ break;
- if (!done)
- {
HDEBUG(D_acl|D_v)
debug_printf("problem after random/rset/mfrom; reopen conn\n");
random_local_part = NULL;
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
(void)close(sx.inblock.sock);
+ sx.inblock.sock = sx.outblock.sock = -1;
#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action,
US"tcp:close", NULL);
#endif
goto tls_retry_connection;
- }
- }
- else done = FALSE; /* Some timeout/connection problem */
- } /* Random check */
+ }
- /* If the host is accepting all local parts, as determined by the "random"
- check, we don't need to waste time doing any further checking. */
+ /* Remember when we last did a random test */
- if (new_domain_record.random_result != ccache_accept && done)
- {
- /* Get the rcpt_include_affixes flag from the transport if there is one,
- but assume FALSE if there is not. */
+ new_domain_record.random_stamp = time(NULL);
- uschar * rcpt = transport_rcpt_address(addr,
- addr->transport ? addr->transport->rcpt_include_affixes : FALSE);
+ /* Re-setup for main verify, or for the error message when failing */
+ addr->address = main_address;
+ addr->transport_return = PENDING_DEFER;
+ sx.first_addr = sx.sync_addr = addr;
+ sx.ok = FALSE;
+ sx.send_rset = TRUE;
+ sx.completed_addr = FALSE;
+ }
+ else
+ done = TRUE;
-#ifdef SUPPORT_I18N
- /*XXX should the conversion be moved into transport_rcpt_address() ? */
- if ( testflag(addr, af_utf8_downcvt)
- && !(rcpt = string_address_utf8_to_alabel(rcpt, NULL))
- )
- {
- errno = ERRNO_EXPANDFAIL;
- *failure_ptr = US"recipient";
- done = FALSE;
- }
- else
-#endif
+ /* Main verify. If the host is accepting all local parts, as determined
+ by the "random" check, we don't need to waste time doing any further
+ checking. */
- done =
- smtp_write_command(&sx.outblock, FALSE, "RCPT TO:<%.1000s>\r\n",
- rcpt) >= 0 &&
- smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
- '2', callout);
+ if (done)
+ {
+ done = FALSE;
+ switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
+ {
+ case 0: switch(addr->transport_return) /* ok so far */
+ {
+ case PENDING_OK: done = TRUE;
+ new_address_record.result = ccache_accept;
+ break;
+ case FAIL: done = TRUE;
+ yield = FAIL;
+ *failure_ptr = US"recipient";
+ new_address_record.result = ccache_reject;
+ break;
+ default: break;
+ }
+ break;
+
+ case -1: /* MAIL response error */
+ *failure_ptr = US"mail";
+ if (errno == 0 && sx.buffer[0] == '5')
+ {
+ setflag(addr, af_verify_nsfail);
+ if (from_address[0] == 0)
+ new_domain_record.result = ccache_reject_mfnull;
+ }
+ break;
+ /* non-MAIL read i/o error */
+ /* non-MAIL response timeout */
+ /* internal error; channel still usable */
+ default: break; /* transmit failed */
+ }
+ }
- if (done)
- new_address_record.result = ccache_accept;
- else if (errno == 0 && responsebuffer[0] == '5')
- {
- *failure_ptr = US"recipient";
- new_address_record.result = ccache_reject;
- }
+ addr->auth_sndr = client_authenticated_sender;
- /* Do postmaster check if requested; if a full check is required, we
- check for RCPT TO:<postmaster> (no domain) in accordance with RFC 821. */
+ deliver_host = deliver_host_address = NULL;
+ deliver_domain = save_deliver_domain;
- if (done && pm_mailfrom)
- {
- /* Could possibly shift before main verify, just above, and be ok
- for cutthrough. But no way to handle a subsequent rcpt, so just
- refuse any */
- cancel_cutthrough_connection("postmaster verify");
- HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n");
-
- done =
- smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0 &&
- smtp_read_response(&sx.inblock, responsebuffer,
- sizeof(responsebuffer), '2', callout) &&
-
- smtp_write_command(&sx.outblock, FALSE,
- "MAIL FROM:<%s>\r\n", pm_mailfrom) >= 0 &&
- smtp_read_response(&sx.inblock, responsebuffer,
- sizeof(responsebuffer), '2', callout) &&
-
- /* First try using the current domain */
-
- ((
- smtp_write_command(&sx.outblock, FALSE,
- "RCPT TO:<postmaster@%.1000s>\r\n", rcpt_domain) >= 0 &&
- smtp_read_response(&sx.inblock, responsebuffer,
- sizeof(responsebuffer), '2', callout)
- )
-
- ||
-
- /* If that doesn't work, and a full check is requested,
- try without the domain. */
-
- (
- (options & vopt_callout_fullpm) != 0 &&
- smtp_write_command(&sx.outblock, FALSE,
- "RCPT TO:<postmaster>\r\n") >= 0 &&
- smtp_read_response(&sx.inblock, responsebuffer,
- sizeof(responsebuffer), '2', callout)
- ));
-
- /* Sort out the cache record */
-
- new_domain_record.postmaster_stamp = time(NULL);
-
- if (done)
- new_domain_record.postmaster_result = ccache_accept;
- else if (errno == 0 && responsebuffer[0] == '5')
- {
- *failure_ptr = US"postmaster";
- setflag(addr, af_verify_pmfail);
- new_domain_record.postmaster_result = ccache_reject;
- }
- }
- } /* Random not accepted */
- } /* MAIL FROM: accepted */
+ /* Do postmaster check if requested; if a full check is required, we
+ check for RCPT TO:<postmaster> (no domain) in accordance with RFC 821. */
+ if (done && pm_mailfrom)
+ {
+ /* Could possibly shift before main verify, just above, and be ok
+ for cutthrough. But no way to handle a subsequent rcpt, so just
+ refuse any */
+ cancel_cutthrough_connection("postmaster verify");
+ HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n");
+
+ done = smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0
+ && smtp_read_response(&sx.inblock, sx.buffer,
+ sizeof(sx.buffer), '2', callout);
+
+ if (done)
+ {
+ uschar * main_address = addr->address;
+
+ /*XXX oops, affixes */
+ addr->address = string_sprintf("postmaster@%.1000s", addr->domain);
+ addr->transport_return = PENDING_DEFER;
+
+ sx.from_addr = pm_mailfrom;
+ sx.first_addr = sx.sync_addr = addr;
+ sx.ok = FALSE;
+ sx.send_rset = TRUE;
+ sx.completed_addr = FALSE;
+
+ if( smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0
+ && addr->transport_return == PENDING_OK
+ )
+ done = TRUE;
+ else
+ done = (options & vopt_callout_fullpm) != 0
+ && smtp_write_command(&sx.outblock, FALSE,
+ "RCPT TO:<postmaster>\r\n") >= 0
+ && smtp_read_response(&sx.inblock, sx.buffer,
+ sizeof(sx.buffer), '2', callout);
+
+ /* Sort out the cache record */
+
+ new_domain_record.postmaster_stamp = time(NULL);
+
+ if (done)
+ new_domain_record.postmaster_result = ccache_accept;
+ else if (errno == 0 && sx.buffer[0] == '5')
+ {
+ *failure_ptr = US"postmaster";
+ setflag(addr, af_verify_pmfail);
+ new_domain_record.postmaster_result = ccache_reject;
+ }
+
+ addr->address = main_address;
+ }
+ }
/* For any failure of the main check, other than a negative response, we just
close the connection and carry on. We can identify a negative response by the
fact that errno is zero. For I/O errors it will be non-zero
is not to be widely broadcast. */
no_conn:
- if (!done) switch(errno)
+ switch(errno)
{
case ETIMEDOUT:
HDEBUG(D_verify) debug_printf("SMTP timeout\n");
extern int acl_where; /* src/acl.c */
errno = 0;
addr->message = string_sprintf(
- "response to \"EHLO\" from %s [%s] did not include SMTPUTF8",
- host->name, host->address);
+ "response to \"EHLO\" did not include SMTPUTF8");
addr->user_message = acl_where == ACL_WHERE_RCPT
? US"533 no support for internationalised mailbox name"
: US"550 mailbox unavailable";
break;
case 0:
- if (*responsebuffer == 0) Ustrcpy(responsebuffer, US"connection dropped");
+ if (*sx.buffer == 0) Ustrcpy(sx.buffer, US"connection dropped");
- addr->message =
- string_sprintf("response to \"%s\" from %s [%s] was: %s",
- big_buffer, host->name, host->address,
- string_printing(responsebuffer));
+ /*XXX test here is ugly; seem to have a split of responsibility for
+ building this message. Need to reationalise. Where is it done
+ before here, and when not?
+ Not == 5xx resp to MAIL on main-verify
+ */
+ if (!addr->message) addr->message =
+ string_sprintf("response to \"%s\" was: %s",
+ big_buffer, string_printing(sx.buffer));
addr->user_message = options & vopt_is_recipient
- ? string_sprintf("Callout verification failed:\n%s", responsebuffer)
+ ? string_sprintf("Callout verification failed:\n%s", sx.buffer)
: string_sprintf("Called: %s\nSent: %s\nResponse: %s",
- host->address, big_buffer, responsebuffer);
+ host->address, big_buffer, sx.buffer);
/* Hard rejection ends the process */
- if (responsebuffer[0] == '5') /* Address rejected */
+ if (sx.buffer[0] == '5') /* Address rejected */
{
yield = FAIL;
done = TRUE;
(void) smtp_write_command(&sx.outblock, FALSE, "QUIT\r\n");
/* Wait a short time for response, and discard it */
- smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
+ smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer),
'2', 1);
}
}
}
+ if (!done || yield != OK)
+ addr->message = string_sprintf("%s [%s] : %s", host->name, host->address,
+ addr->message);
} /* Loop through all hosts, while !done */
}