* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with verifying things. The original code for callout
#define MT_NOT 1
#define MT_ALL 2
-static uschar cutthrough_response(char, uschar **);
+static uschar cutthrough_response(char, uschar **, int);
+
/*************************************************
int callout, int callout_overall, int callout_connect, int options,
uschar *se_mailfrom, uschar *pm_mailfrom)
{
-BOOL is_recipient = (options & vopt_is_recipient) != 0;
-BOOL callout_no_cache = (options & vopt_callout_no_cache) != 0;
-BOOL callout_random = (options & vopt_callout_random) != 0;
-
int yield = OK;
int old_domain_cache_result = ccache_accept;
BOOL done = FALSE;
uschar *from_address;
uschar *random_local_part = NULL;
const uschar *save_deliver_domain = deliver_domain;
-uschar **failure_ptr = is_recipient?
- &recipient_verify_failure : &sender_verify_failure;
+uschar **failure_ptr = options & vopt_is_recipient
+ ? &recipient_verify_failure : &sender_verify_failure;
open_db dbblock;
open_db *dbm_file = NULL;
dbdata_callout_cache new_domain_record;
address_key = addr->address;
from_address = US"";
-if (is_recipient)
+if (options & vopt_is_recipient)
{
if (options & vopt_callout_recipsender)
{
address_key = string_sprintf("%s/<%s>", addr->address, sender_address);
from_address = sender_address;
+ if (cutthrough.delivery) options |= vopt_callout_no_cache;
}
else if (options & vopt_callout_recippmaster)
{
/* Open the callout cache database, it it exists, for reading only at this
stage, unless caching has been disabled. */
-if (callout_no_cache)
+if (options & vopt_callout_no_cache)
{
HDEBUG(D_verify) debug_printf("callout cache: disabled by no_cache\n");
}
/* If a cache database is available see if we can avoid the need to do an
actual callout by making use of previously-obtained data. */
-if (dbm_file != NULL)
+if (dbm_file)
{
dbdata_callout_cache_address *cache_address_record;
dbdata_callout_cache *cache_record = get_callout_cache_record(dbm_file,
/* If an unexpired cache record was found for this domain, see if the callout
process can be short-circuited. */
- if (cache_record != NULL)
+ if (cache_record)
{
/* In most cases, if an early command (up to and including MAIL FROM:<>)
was rejected, there is no point carrying on. The callout fails. However, if
the data in the new record. If a random check is required but hasn't been
done, skip the remaining cache processing. */
- if (callout_random) switch(cache_record->random_result)
+ if (options & vopt_callout_random) switch(cache_record->random_result)
{
case ccache_accept:
- HDEBUG(D_verify)
- debug_printf("callout cache: domain accepts random addresses\n");
- goto END_CALLOUT; /* Default yield is OK */
+ HDEBUG(D_verify)
+ debug_printf("callout cache: domain accepts random addresses\n");
+ goto END_CALLOUT; /* Default yield is OK */
case ccache_reject:
- HDEBUG(D_verify)
- debug_printf("callout cache: domain rejects random addresses\n");
- callout_random = FALSE;
- new_domain_record.random_result = ccache_reject;
- new_domain_record.random_stamp = cache_record->random_stamp;
- break;
+ HDEBUG(D_verify)
+ debug_printf("callout cache: domain rejects random addresses\n");
+ options &= ~vopt_callout_random;
+ new_domain_record.random_result = ccache_reject;
+ new_domain_record.random_stamp = cache_record->random_stamp;
+ break;
default:
- HDEBUG(D_verify)
- debug_printf("callout cache: need to check random address handling "
- "(not cached or cache expired)\n");
- goto END_CACHE;
+ HDEBUG(D_verify)
+ debug_printf("callout cache: need to check random address handling "
+ "(not cached or cache expired)\n");
+ goto END_CACHE;
}
/* If a postmaster check is requested, but there was a previous failure,
but has not been done before, we are going to have to do a callout, so skip
remaining cache processing. */
- if (pm_mailfrom != NULL)
+ if (pm_mailfrom)
{
if (cache_record->postmaster_result == ccache_reject)
{
callout_cache_positive_expire,
callout_cache_negative_expire);
- if (cache_address_record != NULL)
+ if (cache_address_record)
{
if (cache_address_record->result == ccache_accept)
{
with a random local part, ensure that such a local part is available. If not,
log the fact, but carry on without randomming. */
- if (callout_random && callout_random_local_part != NULL)
+ if (options & vopt_callout_random && callout_random_local_part != NULL)
if (!(random_local_part = expand_string(callout_random_local_part)))
log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
"callout_random_local_part: %s", expand_string_message);
and cause the client to time out. So in this case we forgo the PIPELINING
optimization. */
- if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush();
+ if (smtp_out && !disable_callout_flush) mac_smtp_fflush();
/* 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
&& port == cutthrough.host.port
)
{
- uschar * resp;
+ uschar * resp = NULL;
/* Match! Send the RCPT TO, append the addr, set done */
done =
transport_rcpt_address(addr,
(addr->transport == NULL)? FALSE :
addr->transport->rcpt_include_affixes)) >= 0 &&
- cutthrough_response('2', &resp) == '2';
+ 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
else
{
cancel_cutthrough_connection("recipient rejected");
- if (errno == ETIMEDOUT)
+ if (!resp || errno == ETIMEDOUT)
{
HDEBUG(D_verify) debug_printf("SMTP timeout\n");
}
/* Now make connections to the hosts and do real callouts. The list of hosts
is passed in as an argument. */
- for (host = host_list; host != NULL && !done; host = host->next)
+ for (host = host_list; host && !done; host = host->next)
{
smtp_inblock inblock;
smtp_outblock outblock;
/* Skip this host if we don't have an IP address for it. */
- if (host->address == NULL)
+ if (!host->address)
{
DEBUG(D_verify) debug_printf("no IP address for host name %s: skipping\n",
host->name);
/* Set IPv4 or IPv6 */
- host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+ host_af = Ustrchr(host->address, ':') == NULL ? AF_INET : AF_INET6;
/* 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).
addr->transport);
if (inblock.sock < 0)
{
+ HDEBUG(D_verify) debug_printf("connect: %s\n", strerror(errno));
addr->message = string_sprintf("could not connect to %s [%s]: %s",
host->name, host->address, strerror(errno));
transport_name = NULL;
if (host->dnssec == DS_YES)
{
- if( ( dane_required
- || verify_check_given_host(&ob->hosts_try_dane, host) == OK
- )
- && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK
+ if( dane_required
+ || verify_check_given_host(&ob->hosts_try_dane, host) == OK
)
- return rc;
+ {
+ if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required)) != OK)
+ return rc;
+ dane = TRUE;
+ }
}
else if (dane_required)
{
/* Expand the helo_data string to find the host name to use. */
- if (tf->helo_data != NULL)
+ if (tf->helo_data)
{
- uschar *s = expand_string(tf->helo_data);
- if (s == NULL)
+ uschar * s = expand_string(tf->helo_data);
+ if (!s)
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
"helo_data value for callout: %s", addr->address,
expand_string_message);
if (!smtps || (smtps && tls_out.active >= 0))
#endif
{
+#ifdef TCP_QUICKACK
+ (void) setsockopt(inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+#endif
if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
goto RESPONSE_FAILED;
: 0;
}
- size_str = peer_offered & PEER_OFFERED_SIZE
+ size_str = options & vopt_is_recipient && peer_offered & PEER_OFFERED_SIZE
? string_sprintf(" SIZE=%d", message_size + ob->size_addition) : US"";
#ifdef SUPPORT_TLS
- tls_offered = !!(peer_offered & PEER_OFFERED_TLS);
+ smtp_peer_options |= peer_offered & PEER_OFFERED_TLS;
#endif
/* If TLS is available on this connection attempt to
if (!smtps && !smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2',
ob->command_timeout))
{
- if (errno != 0 || buffer2[0] == 0 ||
- (buffer2[0] == '4' && !ob->tls_tempfail_tryclear))
+ if ( errno != 0
+ || buffer2[0] == 0
+ || buffer2[0] == '4' && !ob->tls_tempfail_tryclear
+ )
{
Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
done= FALSE;
int oldtimeout = ob->command_timeout;
int rc;
- tls_negotiate:
ob->command_timeout = callout;
rc = tls_client_start(inblock.sock, host, addr, addr->transport
# ifdef EXPERIMENTAL_DANE
connection, if the options permit it for this host. */
if (rc != OK)
{
- if (rc == DEFER)
- {
- (void)close(inblock.sock);
+ HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
+ (void)close(inblock.sock);
# ifndef DISABLE_EVENT
- (void) event_raise(addr->transport->event_action,
- US"tcp:close", NULL);
-# endif
-# ifdef EXPERIMENTAL_DANE
- if (dane)
- {
- if (!dane_required)
- {
- log_write(0, LOG_MAIN, "DANE attempt failed;"
- " trying CA-root TLS to %s [%s] (not in hosts_require_dane)",
- host->name, host->address);
- dane = FALSE;
- goto tls_negotiate;
- }
- }
- else
+ (void) event_raise(addr->transport->event_action,
+ US"tcp:close", NULL);
# endif
- if ( ob->tls_tempfail_tryclear
- && !smtps
- && 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);
- suppress_tls = TRUE;
- goto tls_retry_connection;
- }
+ if ( ob->tls_tempfail_tryclear
+ && !smtps
+ && 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);
+ suppress_tls = TRUE;
+ goto tls_retry_connection;
}
/*save_errno = ERRNO_TLSFAILURE;*/
/* Need proper integration with the proper transport mechanism. */
if (cutthrough.delivery)
{
+#ifndef DISABLE_DKIM
+ uschar * s;
+#endif
if (addr->transport->filter_command)
{
cutthrough.delivery = FALSE;
HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
}
#ifndef DISABLE_DKIM
- if (ob->dkim_domain)
+ else if ((s = ob->dkim.dkim_domain) && (s = expand_string(s)) && *s)
{
cutthrough.delivery = FALSE;
HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
#ifdef SUPPORT_TLS
tls_close(FALSE, TRUE);
#endif
+ HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
(void)close(inblock.sock);
#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action,
big_buffer, host->name, host->address,
string_printing(responsebuffer));
- addr->user_message = is_recipient?
- string_sprintf("Callout verification failed:\n%s", responsebuffer)
- :
- string_sprintf("Called: %s\nSent: %s\nResponse: %s",
+ addr->user_message = options & vopt_is_recipient
+ ? string_sprintf("Callout verification failed:\n%s", responsebuffer)
+ : string_sprintf("Called: %s\nSent: %s\nResponse: %s",
host->address, big_buffer, responsebuffer);
/* Hard rejection ends the process */
&& rcpt_count == 1
&& done
&& yield == OK
- && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender
+ && (options & (vopt_callout_recipsender|vopt_callout_recippmaster|vopt_success_on_redirect))
+ == vopt_callout_recipsender
&& !random_local_part
&& !pm_mailfrom
&& cutthrough.fd < 0
&& !lmtp
)
{
+ HDEBUG(D_acl|D_v) debug_printf("holding verify callout open for cutthrough delivery\n");
+
cutthrough.fd = outblock.sock; /* We assume no buffer in use in the outblock */
cutthrough.nrcpt = 1;
cutthrough.interface = interface;
{
/* Ensure no cutthrough on multiple address verifies */
if (options & vopt_callout_recipsender)
- cancel_cutthrough_connection("multiple verify calls");
- if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
+ cancel_cutthrough_connection("not usable for cutthrough");
+ if (send_quit)
+ {
+ (void) smtp_write_command(&outblock, FALSE, "QUIT\r\n");
+
+ /* Wait a short time for response, and discard it */
+ smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
+ '2', 1);
+ }
#ifdef SUPPORT_TLS
tls_close(FALSE, TRUE);
#endif
+ HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
(void)close(inblock.sock);
#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
implying some kind of I/O error. We don't want to write the cache in that case.
Otherwise the value is ccache_accept, ccache_reject, or ccache_reject_mfnull. */
-if (!callout_no_cache && new_domain_record.result != ccache_unknown)
+if ( !(options & vopt_callout_no_cache)
+ && new_domain_record.result != ccache_unknown)
{
if ((dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE))
== NULL)
{
(void)dbfn_write(dbm_file, addr->domain, &new_domain_record,
(int)sizeof(dbdata_callout_cache));
- HDEBUG(D_verify) debug_printf("wrote callout cache domain record:\n"
+ HDEBUG(D_verify) debug_printf("wrote callout cache domain record for %s:\n"
" result=%d postmaster=%d random=%d\n",
+ addr->domain,
new_domain_record.result,
new_domain_record.postmaster_result,
new_domain_record.random_result);
if (done)
{
- if (!callout_no_cache && new_address_record.result != ccache_unknown)
+ if ( !(options & vopt_callout_no_cache)
+ && new_address_record.result != ccache_unknown)
{
- if (dbm_file == NULL)
+ if (!dbm_file)
dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE);
- if (dbm_file == NULL)
+ if (!dbm_file)
{
HDEBUG(D_verify) debug_printf("no callout cache available\n");
}
{
(void)dbfn_write(dbm_file, address_key, &new_address_record,
(int)sizeof(dbdata_callout_cache_address));
- HDEBUG(D_verify) debug_printf("wrote %s callout cache address record\n",
- (new_address_record.result == ccache_accept)? "positive" : "negative");
+ HDEBUG(D_verify) debug_printf("wrote %s callout cache address record for %s\n",
+ new_address_record.result == ccache_accept ? "positive" : "negative",
+ address_key);
}
}
} /* done */
else /* !done */
{
- uschar *dullmsg = string_sprintf("Could not complete %s verify callout",
- is_recipient? "recipient" : "sender");
+ uschar * dullmsg = string_sprintf("Could not complete %s verify callout",
+ options & vopt_is_recipient ? "recipient" : "sender");
yield = DEFER;
- if (host_list->next != NULL || addr->message == NULL) addr->message = dullmsg;
+ if (host_list->next || !addr->message)
+ addr->message = dullmsg;
- addr->user_message = (!smtp_return_error_details)? dullmsg :
- string_sprintf("%s for <%s>.\n"
+ addr->user_message = smtp_return_error_details
+ ? string_sprintf("%s for <%s>.\n"
"The mail server(s) for the domain may be temporarily unreachable, or\n"
"they may be permanently unreachable from this server. In the latter case,\n%s",
dullmsg, addr->address,
- is_recipient?
- "the address will never be accepted."
- :
- "you need to change the address or create an MX record for its domain\n"
- "if it is supposed to be generally accessible from the Internet.\n"
- "Talk to your mail administrator for details.");
+ options & vopt_is_recipient
+ ? "the address will never be accepted."
+ : "you need to change the address or create an MX record for its domain\n"
+ "if it is supposed to be generally accessible from the Internet.\n"
+ "Talk to your mail administrator for details.")
+ : dullmsg;
/* Force a specific error code */
/* Come here from within the cache-reading code on fast-track exit. */
END_CALLOUT:
-if (dbm_file != NULL) dbfn_close(dbm_file);
+if (dbm_file) dbfn_close(dbm_file);
return yield;
}
/* Called after recipient-acl to get a cutthrough connection open when
one was requested and a recipient-verify wasn't subsequently done.
*/
-void
+int
open_cutthrough_connection( address_item * addr )
{
address_item addr2;
+int rc;
/* Use a recipient-verify-callout to set up the cutthrough connection. */
/* We must use a copy of the address for verification, because it might
addr2 = *addr;
HDEBUG(D_acl) debug_printf("----------- %s cutthrough setup ------------\n",
rcpt_count > 1 ? "more" : "start");
-(void) verify_address(&addr2, NULL,
+rc = verify_address(&addr2, NULL,
vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache,
CUTTHROUGH_CMD_TIMEOUT, -1, -1,
NULL, NULL, NULL);
+addr->message = addr2.message;
+addr->user_message = addr2.user_message;
HDEBUG(D_acl) debug_printf("----------- end cutthrough setup ------------\n");
-return;
+return rc;
}
/* Get and check response from cutthrough target */
static uschar
-cutthrough_response(char expect, uschar ** copy)
+cutthrough_response(char expect, uschar ** copy, int timeout)
{
smtp_inblock inblock;
uschar inbuffer[4096];
inblock.ptrend = inbuffer;
inblock.sock = cutthrough.fd;
/* this relies on (inblock.sock == tls_out.active) */
-if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT))
+if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, timeout))
cancel_cutthrough_connection("target timeout on read");
if(copy != NULL)
cutthrough_flush_send();
/* Assume nothing buffered. If it was it gets ignored. */
-return cutthrough_response('3', NULL) == '3';
+return cutthrough_response('3', NULL, CUTTHROUGH_DATA_TIMEOUT) == '3';
}
-/* fd and use_crlf args only to match write_chunk() */
+/* fd and tctx args only to match write_chunk() */
static BOOL
-cutthrough_write_chunk(int fd, uschar * s, int len, BOOL use_crlf)
+cutthrough_write_chunk(int fd, transport_ctx * tctx, uschar * s, int len)
{
uschar * s2;
while(s && (s2 = Ustrchr(s, '\n')))
BOOL
cutthrough_headers_send(void)
{
+transport_ctx tctx;
+
if(cutthrough.fd < 0)
return FALSE;
*/
HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n");
-if (!transport_headers_send(&cutthrough.addr, cutthrough.fd,
- cutthrough.addr.transport->add_headers,
- cutthrough.addr.transport->remove_headers,
- &cutthrough_write_chunk, TRUE,
- cutthrough.addr.transport->rewrite_rules,
- cutthrough.addr.transport->rewrite_existflags))
+tctx.tblock = cutthrough.addr.transport;
+tctx.addr = &cutthrough.addr;
+tctx.check_string = US".";
+tctx.escape_string = US"..";
+tctx.options = topt_use_crlf;
+
+if (!transport_headers_send(cutthrough.fd, &tctx, &cutthrough_write_chunk))
return FALSE;
HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n");
HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> QUIT\n");
_cutthrough_puts(US"QUIT\r\n", 6); /* avoid recursion */
_cutthrough_flush_send();
- /* No wait for response */
+
+ /* Wait a short time for response, and discard it */
+ cutthrough_response('2', NULL, 1);
#ifdef SUPPORT_TLS
tls_close(FALSE, TRUE);
#endif
+ HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
(void)close(cutthrough.fd);
cutthrough.fd = -1;
HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown (%s) ------------\n", why);
)
return cutthrough.addr.message;
-res = cutthrough_response('2', &cutthrough.addr.message);
+res = cutthrough_response('2', &cutthrough.addr.message, CUTTHROUGH_DATA_TIMEOUT);
for (addr = &cutthrough.addr; addr; addr = addr->next)
{
addr->message = cutthrough.addr.message;
{
BOOL allok = TRUE;
BOOL full_info = (f == NULL)? FALSE : (debug_selector != 0);
-BOOL is_recipient = (options & vopt_is_recipient) != 0;
BOOL expn = (options & vopt_expn) != 0;
BOOL success_on_redirect = (options & vopt_success_on_redirect) != 0;
int i;
int yield = OK;
int verify_type = expn? v_expn :
address_test_mode? v_none :
- is_recipient? v_recipient : v_sender;
+ options & vopt_is_recipient? v_recipient : v_sender;
address_item *addr_list;
address_item *addr_new = NULL;
address_item *addr_remote = NULL;
address_item *addr_local = NULL;
address_item *addr_succeed = NULL;
-uschar **failure_ptr = is_recipient?
- &recipient_verify_failure : &sender_verify_failure;
+uschar **failure_ptr = options & vopt_is_recipient
+ ? &recipient_verify_failure : &sender_verify_failure;
uschar *ko_prefix, *cr;
uschar *address = vaddr->address;
uschar *save_sender;
*failure_ptr = US"qualify";
return FAIL;
}
- address = rewrite_address_qualify(address, is_recipient);
+ address = rewrite_address_qualify(address, options & vopt_is_recipient);
}
DEBUG(D_verify)
if (global_rewrite_rules != NULL)
{
uschar *old = address;
- address = rewrite_address(address, is_recipient, FALSE,
+ address = rewrite_address(address, options & vopt_is_recipient, FALSE,
global_rewrite_rules, rewrite_existflags);
if (address != old)
{
/* Flip the legacy TLS-related variables over to the outbound set in case
they're used in the context of a transport used by verification. Reset them
-at exit from this routine. */
+at exit from this routine (so no returns allowed from here on). */
tls_modify_variables(&tls_out);
save_sender = sender_address;
+/* Observability variable for router/transport use */
+
+verify_mode = options & vopt_is_recipient ? US"R" : US"S";
+
/* Update the address structure with the possibly qualified and rewritten
address. Set it up as the starting address on the chain of new addresses. */
full_info is set, and this can only be set locally. Remote enquiries just get
information about the top level address, not anything that it generated. */
-while (addr_new != NULL)
+while (addr_new)
{
int rc;
address_item *addr = addr_new;
/* Just in case some router parameter refers to it. */
- return_path = (addr->prop.errors_address != NULL)?
- addr->prop.errors_address : sender_address;
+ return_path = addr->prop.errors_address
+ ? addr->prop.errors_address : sender_address;
/* Split the address into domain and local part, handling the %-hack if
necessary, and then route it. While routing a sender address, set
$sender_address to <> because that is what it will be if we were trying to
send a bounce to the sender. */
- if (routed != NULL) *routed = FALSE;
+ if (routed) *routed = FALSE;
if ((rc = deliver_split_address(addr)) == OK)
{
- if (!is_recipient) sender_address = null_sender;
+ if (!(options & vopt_is_recipient)) sender_address = null_sender;
rc = route_address(addr, &addr_local, &addr_remote, &addr_new,
&addr_succeed, verify_type);
sender_address = save_sender; /* Put back the real sender */
if (rc == OK)
{
- if (routed != NULL) *routed = TRUE;
+ if (routed) *routed = TRUE;
if (callout > 0)
{
- host_item *host_list = addr->host_list;
transport_instance * tp;
+ host_item * host_list = addr->host_list;
/* Make up some data for use in the case where there is no remote
transport. */
transport is configured to override the router's hosts, we must build a
host list of the transport's hosts, and find the IP addresses */
- if (tf.hosts != NULL && (host_list == NULL || tf.hosts_override))
+ if (tf.hosts && (!host_list || tf.hosts_override))
{
uschar *s;
const uschar *save_deliver_domain = deliver_domain;
deliver_domain = save_deliver_domain;
deliver_localpart = save_deliver_localpart;
- if (s == NULL)
+ if (!s)
{
log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand list of hosts "
"\"%s\" in %s transport for callout: %s", tf.hosts,
if (tf.qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
if (tf.search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
- for (host = host_list; host != NULL; host = nexthost)
+ for (host = host_list; host; host = nexthost)
{
nexthost = host->next;
if (tf.gethostbyname ||
/* Can only do a callout if we have at least one host! If the callout
fails, it will have set ${sender,recipient}_verify_failure. */
- if (host_list != NULL)
+ if (host_list)
{
HDEBUG(D_verify) debug_printf("Attempting full verification using callout\n");
if (host_checking && !host_checking_callout)
#ifdef SUPPORT_TLS
deliver_set_expansions(addr);
#endif
- verify_mode = is_recipient ? US"R" : US"S";
rc = do_callout(addr, host_list, &tf, callout, callout_overall,
callout_connect, options, se_mailfrom, pm_mailfrom);
- verify_mode = NULL;
}
}
else
if (rc == FAIL)
{
allok = FALSE;
- if (f != NULL)
+ if (f)
{
address_item *p = addr->parent;
respond_printf(f, "%s%s %s", ko_prefix,
- full_info? addr->address : address,
- address_test_mode? "is undeliverable" : "failed to verify");
+ full_info ? addr->address : address,
+ address_test_mode ? "is undeliverable" : "failed to verify");
if (!expn && admin_user)
{
if (addr->basic_errno > 0)
respond_printf(f, ": %s", strerror(addr->basic_errno));
- if (addr->message != NULL)
+ if (addr->message)
respond_printf(f, ": %s", addr->message);
}
/* Show parents iff doing full info */
- if (full_info) while (p != NULL)
+ if (full_info) while (p)
{
respond_printf(f, "%s\n <-- %s", cr, p->address);
p = p->parent;
cancel_cutthrough_connection("routing hard fail");
if (!full_info)
- {
+ {
yield = copy_error(vaddr, addr, FAIL);
goto out;
- }
- else yield = FAIL;
+ }
+ yield = FAIL;
}
/* Soft failure */
else if (rc == DEFER)
{
allok = FALSE;
- if (f != NULL)
+ if (f)
{
address_item *p = addr->parent;
respond_printf(f, "%s%s cannot be resolved at this time", ko_prefix,
{
if (addr->basic_errno > 0)
respond_printf(f, ": %s", strerror(addr->basic_errno));
- if (addr->message != NULL)
+ if (addr->message)
respond_printf(f, ": %s", addr->message);
else if (addr->basic_errno <= 0)
respond_printf(f, ": unknown error");
/* Show parents iff doing full info */
- if (full_info) while (p != NULL)
+ if (full_info) while (p)
{
respond_printf(f, "%s\n <-- %s", cr, p->address);
p = p->parent;
yield = copy_error(vaddr, addr, DEFER);
goto out;
}
- else if (yield == OK) yield = DEFER;
+ if (yield == OK) yield = DEFER;
}
/* If we are handling EXPN, we do not want to continue to route beyond
else if (expn)
{
uschar *ok_prefix = US"250-";
- if (addr_new == NULL)
- {
- if (addr_local == NULL && addr_remote == NULL)
+
+ if (!addr_new)
+ if (!addr_local && !addr_remote)
respond_printf(f, "250 mail to <%s> is discarded\r\n", address);
else
respond_printf(f, "250 <%s>\r\n", address);
- }
- else while (addr_new != NULL)
+
+ else do
{
address_item *addr2 = addr_new;
addr_new = addr2->next;
- if (addr_new == NULL) ok_prefix = US"250 ";
+ if (!addr_new) ok_prefix = US"250 ";
respond_printf(f, "%s<%s>\r\n", ok_prefix, addr2->address);
- }
+ } while (addr_new);
yield = OK;
goto out;
}
just a single new address as a special case, and continues on to verify the
generated address. */
- if (!full_info && /* Stop if short info wanted AND */
- (((addr_new == NULL || /* No new address OR */
- addr_new->next != NULL || /* More than one new address OR */
- testflag(addr_new, af_pfr))) /* New address is pfr */
- || /* OR */
- (addr_new != NULL && /* At least one new address AND */
- success_on_redirect))) /* success_on_redirect is set */
+ if ( !full_info /* Stop if short info wanted AND */
+ && ( ( !addr_new /* No new address OR */
+ || addr_new->next /* More than one new address OR */
+ || testflag(addr_new, af_pfr) /* New address is pfr */
+ )
+ || /* OR */
+ ( addr_new /* At least one new address AND */
+ && success_on_redirect /* success_on_redirect is set */
+ ) )
+ )
{
- if (f != NULL) fprintf(f, "%s %s\n", address,
- address_test_mode? "is deliverable" : "verified");
+ if (f) fprintf(f, "%s %s\n",
+ address, address_test_mode ? "is deliverable" : "verified");
/* If we have carried on to verify a child address, we want the value
of $address_data to be that of the child */
vaddr->prop.address_data = addr->prop.address_data;
+
+ /* If stopped because more than one new address, cannot cutthrough */
+
+ if (addr_new && addr_new->next)
+ cancel_cutthrough_connection("multiple addresses from routing");
+
yield = OK;
goto out;
}
or autoreplies, and there were no errors or deferments, the message is to be
discarded, usually because of the use of :blackhole: in an alias file. */
-if (allok && addr_local == NULL && addr_remote == NULL)
+if (allok && !addr_local && !addr_remote)
{
fprintf(f, "mail to %s is discarded\n", address);
goto out;
the -bv or -bt case). */
out:
+verify_mode = NULL;
tls_modify_variables(&tls_in);
return yield;
to the incoming interface address. If the sender host address is an IPv6
address, the incoming interface address will also be IPv6. */
-host_af = (Ustrchr(sender_host_address, ':') == NULL)? AF_INET : AF_INET6;
-sock = ip_socket(SOCK_STREAM, host_af);
-if (sock < 0) return;
+host_af = Ustrchr(sender_host_address, ':') == NULL ? AF_INET : AF_INET6;
+if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return;
if (ip_bind(sock, host_af, interface_address, 0) < 0)
{
goto END_OFF;
}
-if (ip_connect(sock, host_af, sender_host_address, port, rfc1413_query_timeout)
- < 0)
+if (ip_connect(sock, host_af, sender_host_address, port,
+ rfc1413_query_timeout, TRUE) < 0)
{
if (errno == ETIMEDOUT && LOGGING(ident_timeout))
- {
log_write(0, LOG_MAIN, "ident connection to %s timed out",
sender_host_address);
- }
else
- {
DEBUG(D_ident) debug_printf("ident connection to %s failed: %s\n",
sender_host_address, strerror(errno));
- }
goto END_OFF;
}
/* Now do the actual lookup; note that there is no search_close() because
of the caching arrangements. */
- handle = search_open(filename, search_type, 0, NULL, NULL);
- if (handle == NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- search_error_message);
+ if (!(handle = search_open(filename, search_type, 0, NULL, NULL)))
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message);
+
result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL);
if (valueptr != NULL) *valueptr = result;
return (result != NULL)? OK : search_find_defer? DEFER: FAIL;