* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with verifying things. The original code for callout
time_t now;
dbdata_callout_cache *cache_record;
-cache_record = dbfn_read_with_length(dbm_file, key, &length);
-
-if (cache_record == NULL)
+if (!(cache_record = dbfn_read_with_length(dbm_file, key, &length)))
{
HDEBUG(D_verify) debug_printf("callout cache: no %s record found for %s\n", type, key);
return NULL;
host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET;
- if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
- US"callout") ||
- !smtp_get_port(tf->port, addr, &port, US"callout"))
+ 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);
+ smtp_port_for_connect(host, port);
+
if ( ( interface == cutthrough.interface
|| ( interface
&& cutthrough.interface
&& Ustrcmp(interface, cutthrough.interface) == 0
) )
- && port == cutthrough.host.port
+ && host->port == cutthrough.host.port
)
{
uschar * resp = NULL;
postmaster-verify.
The sync_responses() would need to be taught about it and we'd
need another return code filtering out to here.
+
+ Avoid using a SIZE option on the MAIL for all randon-rcpt checks.
*/
+ sx.avoid_option = OPTION_SIZE;
+
/* Remember when we last did a random test */
new_domain_record.random_stamp = time(NULL);
if (smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0)
switch(addr->transport_return)
{
- case PENDING_OK:
+ case PENDING_OK: /* random was accepted, unfortunately */
new_domain_record.random_result = ccache_accept;
- break;
- case FAIL:
+ yield = OK; /* Only usable verify result we can return */
+ done = TRUE;
+ goto no_conn;
+ case FAIL: /* rejected: the preferred result */
new_domain_record.random_result = ccache_reject;
+ sx.avoid_option = 0;
/* Between each check, issue RSET, because some servers accept only
one recipient after MAIL FROM:<>.
debug_printf_indent("problem after random/rset/mfrom; reopen conn\n");
random_local_part = NULL;
#ifdef SUPPORT_TLS
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
(void)close(sx.inblock.sock);
sx.send_rset = TRUE;
sx.completed_addr = FALSE;
goto tls_retry_connection;
+ case DEFER: /* 4xx response to random */
+ break; /* Just to be clear. ccache_unknown, !done. */
}
/* Re-setup for main verify, or for the error message when failing */
else
done = TRUE;
- /* 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. */
+ /* Main verify. For rcpt-verify use SIZE if we know it and we're not cacheing;
+ for sndr-verify never use it. */
if (done)
{
+ if (!(options & vopt_is_recipient && options & vopt_callout_no_cache))
+ sx.avoid_option = OPTION_SIZE;
+
done = FALSE;
switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
{
case PENDING_OK: done = TRUE;
new_address_record.result = ccache_accept;
break;
- case FAIL: done = TRUE;
+ case FAIL: done = TRUE;
yield = FAIL;
*failure_ptr = US"recipient";
new_address_record.result = ccache_reject;
break;
- default: break;
+ default: break;
}
break;
sx.ok = FALSE;
sx.send_rset = TRUE;
sx.completed_addr = FALSE;
+ sx.avoid_option = OPTION_SIZE;
if( smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0
&& addr->transport_return == PENDING_OK
if (*sx.buffer == 0) Ustrcpy(sx.buffer, US"connection dropped");
/*XXX test here is ugly; seem to have a split of responsibility for
- building this message. Need to reationalise. Where is it done
+ building this message. Need to rationalise. Where is it done
before here, and when not?
Not == 5xx resp to MAIL on main-verify
*/
if (sx.inblock.sock >= 0)
{
#ifdef SUPPORT_TLS
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
(void)close(sx.inblock.sock);
if(
#ifdef SUPPORT_TLS
- (tls_out.active == cutthrough.fd) ? tls_write(FALSE, ctblock.buffer, n) :
+ tls_out.active == cutthrough.fd ? tls_write(FALSE, ctblock.buffer, n, FALSE) :
#endif
send(cutthrough.fd, ctblock.buffer, n, 0) > 0
)
tctx.addr = &cutthrough.addr;
tctx.check_string = US".";
tctx.escape_string = US"..";
+/*XXX check under spool_files_wireformat. Might be irrelevant */
tctx.options = topt_use_crlf;
if (!transport_headers_send(&tctx, &cutthrough_write_chunk))
_cutthrough_puts(US"QUIT\r\n", 6); /* avoid recursion */
_cutthrough_flush_send();
cutthrough.fd = -1; /* avoid recursion via read timeout */
+ cutthrough.nrcpt = 0; /* permit re-cutthrough on subsequent message */
/* Wait a short time for response, and discard it */
cutthrough_response(fd, '2', NULL, 1);
#ifdef SUPPORT_TLS
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
(void)close(fd);
va_start(ap, format);
if (smtp_out && (f == smtp_out))
- smtp_vprintf(format, ap);
+ smtp_vprintf(format, FALSE, ap);
else
vfprintf(f, format, ap);
va_end(ap);
transport. */
transport_feedback tf = {
- NULL, /* interface (=> any) */
- US"smtp", /* port */
- US"smtp", /* protocol */
- NULL, /* hosts */
- US"$smtp_active_hostname", /* helo_data */
- FALSE, /* hosts_override */
- FALSE, /* hosts_randomize */
- FALSE, /* gethostbyname */
- TRUE, /* qualify_single */
- FALSE /* search_parents */
+ .interface = NULL, /* interface (=> any) */
+ .port = US"smtp",
+ .protocol = US"smtp",
+ .hosts = NULL,
+ .helo_data = US"$smtp_active_hostname",
+ .hosts_override = FALSE,
+ .hosts_randomize = FALSE,
+ .gethostbyname = FALSE,
+ .qualify_single = TRUE,
+ .search_parents = FALSE
};
/* If verification yielded a remote transport, we want to use that
additional host items being inserted into the chain. Hence we must
save the next host first. */
- flags = HOST_FIND_BY_A;
+ flags = HOST_FIND_BY_A | HOST_FIND_BY_AAAA;
if (tf.qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
if (tf.search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
*************************************************/
/* This function checks those header lines that contain addresses, and verifies
-that all the addresses therein are syntactially correct.
+that all the addresses therein are 5322-syntactially correct.
Arguments:
msgptr where to put an error message
uschar *colon, *s;
int yield = OK;
-for (h = header_list; h != NULL && yield == OK; h = h->next)
+for (h = header_list; h && yield == OK; h = h->next)
{
if (h->type != htype_from &&
h->type != htype_reply_to &&
parse_allow_group = TRUE;
- while (*s != 0)
+ while (*s)
{
uschar *ss = parse_find_address_end(s, FALSE);
uschar *recipient, *errmess;
/* Permit an unqualified address only if the message is local, or if the
sending host is configured to be permitted to send them. */
- if (recipient != NULL && domain == 0)
+ if (recipient && !domain)
{
if (h->type == htype_from || h->type == htype_sender)
{
/* It's an error if no address could be extracted, except for the special
case of an empty address. */
- if (recipient == NULL && Ustrcmp(errmess, "empty address") != 0)
+ if (!recipient && Ustrcmp(errmess, "empty address") != 0)
{
uschar *verb = US"is";
uschar *t = ss;
/* deconst cast ok as we're passing a non-const to string_printing() */
*msgptr = US string_printing(
string_sprintf("%s: failing address in \"%.*s:\" header %s: %.*s",
- errmess, tt - h->text, h->text, verb, len, s));
+ errmess, (int)(tt - h->text), h->text, verb, len, s));
yield = FAIL;
break; /* Out of address loop */
/* Advance to the next address */
- s = ss + (terminator? 1:0);
+ s = ss + (terminator ? 1 : 0);
while (isspace(*s)) s++;
} /* Next address */
while (ss > s && isspace(ss[-1])) ss--;
*log_msgptr = string_sprintf("syntax error in '%.*s' header when "
"scanning for sender: %s in \"%.*s\"",
- endname - h->text, h->text, *log_msgptr, ss - s, s);
+ (int)(endname - h->text), h->text, *log_msgptr, (int)(ss - s), s);
yield = FAIL;
done = TRUE;
break;
{
*verrno = vaddr->basic_errno;
if (smtp_return_error_details)
- {
*user_msgptr = string_sprintf("Rejected after DATA: "
"could not verify \"%.*s\" header address\n%s: %s",
- endname - h->text, h->text, vaddr->address, vaddr->message);
- }
+ (int)(endname - h->text), h->text, vaddr->address, vaddr->message);
}
/* Success or defer */
int sock, host_af, qlen;
int received_sender_port, received_interface_port, n;
uschar *p;
+blob early_data;
uschar buffer[2048];
/* Default is no ident. Check whether we want to do an ident check for this
goto END_OFF;
}
+/* Construct and send the query. */
+
+qlen = snprintf(CS buffer, sizeof(buffer), "%d , %d\r\n",
+ sender_host_port, interface_port);
+early_data.data = buffer;
+early_data.len = qlen;
+
if (ip_connect(sock, host_af, sender_host_address, port,
- rfc1413_query_timeout, TRUE) < 0)
+ rfc1413_query_timeout, &early_data) < 0)
{
if (errno == ETIMEDOUT && LOGGING(ident_timeout))
log_write(0, LOG_MAIN, "ident connection to %s timed out",
goto END_OFF;
}
-/* Construct and send the query. */
-
-sprintf(CS buffer, "%d , %d\r\n", sender_host_port, interface_port);
-qlen = Ustrlen(buffer);
-if (send(sock, buffer, qlen, 0) < 0)
- {
- DEBUG(D_ident) debug_printf("ident send failed: %s\n", strerror(errno));
- goto END_OFF;
- }
-
/* Read a response line. We put it into the rest of the buffer, using several
recv() calls if necessary. */
/* Not a query-style lookup; must ensure the host name is present, and then we
do a check on the name and all its aliases. */
-if (sender_host_name == NULL)
+if (!sender_host_name)
{
HDEBUG(D_host_lookup)
debug_printf("sender host name required, to match against %s\n", ss);
/* Match on the sender host name, using the general matching function */
-switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE,
- valueptr))
+switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE, valueptr))
{
case OK: return OK;
case DEFER: return DEFER;
/* If there are aliases, try matching on them. */
aliases = sender_host_aliases;
-while (*aliases != NULL)
- {
+while (*aliases)
switch(match_check_string(*aliases++, ss, -1, TRUE, TRUE, TRUE, valueptr))
{
case OK: return OK;
case DEFER: return DEFER;
}
- }
return FAIL;
}
int rc;
unsigned int *local_cache_bits = cache_bits;
const uschar *save_host_address = deliver_host_address;
-check_host_block cb;
-cb.host_name = host_name;
-cb.host_address = host_address;
+check_host_block cb = { .host_name = host_name, .host_address = host_address };
-if (valueptr != NULL) *valueptr = NULL;
+if (valueptr) *valueptr = NULL;
/* If the host address starts off ::ffff: it is an IPv6 address in
IPv4-compatible mode. Find the IPv4 part for checking against IPv4
addresses. */
-cb.host_ipv4 = (Ustrncmp(host_address, "::ffff:", 7) == 0)?
- host_address + 7 : host_address;
+cb.host_ipv4 = Ustrncmp(host_address, "::ffff:", 7) == 0
+ ? host_address + 7 : host_address;
/* During the running of the check, put the IP address into $host_address. In
the case of calls from the smtp transport, it will already be there. However,
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
- {
if (rr->type == T_A)
{
dns_address *da = dns_address_from_rr(&dnsa, rr);
if (da)
{
*addrp = da;
- while (da->next != NULL) da = da->next;
- addrp = &(da->next);
+ while (da->next) da = da->next;
+ addrp = &da->next;
if (ttl > rr->ttl) ttl = rr->ttl;
}
}
- }
/* If we didn't find any A records, change the return code. This can
happen when there is a CNAME record but there are no A records for what
it points to. */
- if (cb->rhs == NULL) cb->rc = DNS_NODATA;
+ if (!cb->rhs) cb->rc = DNS_NODATA;
}
cb->expiry = time(NULL)+ttl;
records. For A6 records (currently not expected to be used) there may be
multiple addresses from a single record. */
- for (da = cb->rhs->next; da != NULL; da = da->next)
+ for (da = cb->rhs->next; da; da = da->next)
addlist = string_sprintf("%s, %s", addlist, da->address);
HDEBUG(D_dnsbl) debug_printf("DNS lookup for %s succeeded (yielding %s)\n",
/* Address list check; this can be either for equality, or via a bitmask.
In the latter case, all the bits must match. */
- if (iplist != NULL)
+ if (iplist)
{
- for (da = cb->rhs; da != NULL; da = da->next)
+ for (da = cb->rhs; da; da = da->next)
{
int ipsep = ',';
uschar ip[46];
/* Handle exact matching */
if (!bitmask)
- {
- while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))) != NULL)
- {
- if (Ustrcmp(CS da->address, ip) == 0) break;
- }
- }
+ {
+ while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
+ if (Ustrcmp(CS da->address, ip) == 0)
+ break;
+ }
/* Handle bitmask matching */
/* Scan the returned addresses, skipping any that are IPv6 */
- while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))) != NULL)
+ while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
{
if (host_aton(ip, address) != 1) continue;
if ((address[0] & mask) == address[0]) break;
switch(match_type)
{
case 0:
- res = US"was no match";
- break;
+ res = US"was no match"; break;
case MT_NOT:
- res = US"was an exclude match";
- break;
+ res = US"was an exclude match"; break;
case MT_ALL:
- res = US"was an IP address that did not match";
- break;
+ res = US"was an IP address that did not match"; break;
case MT_NOT|MT_ALL:
- res = US"were no IP addresses that did not match";
- break;
+ res = US"were no IP addresses that did not match"; break;
}
debug_printf("=> but we are not accepting this block class because\n");
debug_printf("=> there %s for %s%c%s\n",
{
dns_record *rr;
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
- rr != NULL;
+ rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
if (rr->type == T_TXT) break;
- if (rr != NULL)
+ if (rr)
{
int len = (rr->data)[0];
if (len > 511) len = 127;
store_pool = POOL_PERM;
- cb->text = string_sprintf("%.*s", len, (const uschar *)(rr->data+1));
+ cb->text = string_sprintf("%.*s", len, CUS (rr->data+1));
store_pool = old_pool;
}
}