*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with verifying things. The original code for callout
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);
+ HDEBUG(D_verify) debug_printf_indent("callout cache: no %s record found for %s\n", type, key);
return NULL;
}
if (now - cache_record->time_stamp > expire)
{
- HDEBUG(D_verify) debug_printf("callout cache: %s record expired for %s\n", type, key);
+ HDEBUG(D_verify) debug_printf_indent("callout cache: %s record expired for %s\n", type, key);
return NULL;
}
cache_record->random_result = ccache_unknown;
}
-HDEBUG(D_verify) debug_printf("callout cache: found %s record for %s\n", type, key);
+HDEBUG(D_verify) debug_printf_indent("callout cache: found %s record for %s\n", type, key);
return cache_record;
}
if (options & vopt_callout_no_cache)
{
- HDEBUG(D_verify) debug_printf("callout cache: disabled by no_cache\n");
+ HDEBUG(D_verify) debug_printf_indent("callout cache: disabled by no_cache\n");
}
else if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE)))
{
- HDEBUG(D_verify) debug_printf("callout cache: not available\n");
+ HDEBUG(D_verify) debug_printf_indent("callout cache: not available\n");
}
else
{
|| *from_address == 0 && cache_record->result == ccache_reject_mfnull)
{
HDEBUG(D_verify)
- debug_printf("callout cache: domain gave initial rejection, or "
+ debug_printf_indent("callout cache: domain gave initial rejection, or "
"does not accept HELO or MAIL FROM:<>\n");
setflag(addr, af_verify_nsfail);
addr->user_message = US"(result of an earlier callout reused).";
{
case ccache_accept:
HDEBUG(D_verify)
- debug_printf("callout cache: domain accepts random addresses\n");
+ debug_printf_indent("callout cache: domain accepts random addresses\n");
*failure_ptr = US"random";
dbfn_close(dbm_file);
return TRUE; /* Default yield is OK */
case ccache_reject:
HDEBUG(D_verify)
- debug_printf("callout cache: domain rejects random addresses\n");
+ debug_printf_indent("callout cache: domain rejects random addresses\n");
*opt_ptr = options & ~vopt_callout_random;
new_domain_record->random_result = ccache_reject;
new_domain_record->random_stamp = cache_record->random_stamp;
default:
HDEBUG(D_verify)
- debug_printf("callout cache: need to check random address handling "
+ debug_printf_indent("callout cache: need to check random address handling "
"(not cached or cache expired)\n");
dbfn_close(dbm_file);
return FALSE;
{
setflag(addr, af_verify_pmfail);
HDEBUG(D_verify)
- debug_printf("callout cache: domain does not accept "
+ debug_printf_indent("callout cache: domain does not accept "
"RCPT TO:<postmaster@domain>\n");
*yield = FAIL;
*failure_ptr = US"postmaster";
if (cache_record->postmaster_result == ccache_unknown)
{
HDEBUG(D_verify)
- debug_printf("callout cache: need to check RCPT "
+ debug_printf_indent("callout cache: need to check RCPT "
"TO:<postmaster@domain> (not cached or cache expired)\n");
dbfn_close(dbm_file);
return FALSE;
that the value in the cache record is preserved (with its old timestamp).
*/
- HDEBUG(D_verify) debug_printf("callout cache: domain accepts RCPT "
+ HDEBUG(D_verify) debug_printf_indent("callout cache: domain accepts RCPT "
"TO:<postmaster@domain>\n");
*pm_ptr = NULL;
new_domain_record->postmaster_result = ccache_accept;
if (cache_address_record->result == ccache_accept)
{
HDEBUG(D_verify)
- debug_printf("callout cache: address record is positive\n");
+ debug_printf_indent("callout cache: address record is positive\n");
}
else
{
HDEBUG(D_verify)
- debug_printf("callout cache: address record is negative\n");
+ debug_printf_indent("callout cache: address record is negative\n");
addr->user_message = US"Previous (cached) callout verification failure";
*failure_ptr = US"recipient";
*yield = FAIL;
if (dom_rec->result != ccache_unknown)
if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
{
- HDEBUG(D_verify) debug_printf("callout cache: not available\n");
+ HDEBUG(D_verify) debug_printf_indent("callout cache: not available\n");
}
else
{
(void)dbfn_write(dbm_file, domain, dom_rec,
(int)sizeof(dbdata_callout_cache));
- HDEBUG(D_verify) debug_printf("wrote callout cache domain record for %s:\n"
+ HDEBUG(D_verify) debug_printf_indent("wrote callout cache domain record for %s:\n"
" result=%d postmaster=%d random=%d\n",
domain,
dom_rec->result,
dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE);
if (!dbm_file)
{
- HDEBUG(D_verify) debug_printf("no callout cache available\n");
+ HDEBUG(D_verify) debug_printf_indent("no callout cache available\n");
}
else
{
(void)dbfn_write(dbm_file, address_key, addr_rec,
(int)sizeof(dbdata_callout_cache_address));
- HDEBUG(D_verify) debug_printf("wrote %s callout cache address record for %s\n",
+ HDEBUG(D_verify) debug_printf_indent("wrote %s callout cache address record for %s\n",
addr_rec->result == ccache_accept ? "positive" : "negative",
address_key);
}
{
smtp_transport_options_block *ob =
(smtp_transport_options_block *)addr->transport->options_block;
+ smtp_context * sx = NULL;
/* 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,
int host_af;
int port = 25;
uschar * interface = NULL; /* Outgoing interface to use; NULL => any */
- smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */
if (!host->address)
{
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
addr->message);
+ if (!sx) sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */
+ memset(sx, 0, sizeof(*sx));
+
sx->addrlist = addr;
sx->conn_args.host = host;
sx->conn_args.host_af = host_af,
Arguments:
vaddr contains the address to verify; the next field in this block
must be NULL
- f if not NULL, write the result to this file
+ fp if not NULL, write the result to this file
options various option bits:
vopt_fake_sender => this sender verify is not for the real
sender (it was verify=sender=xxxx or an address from a
BOOL success_on_redirect = (options & vopt_success_on_redirect) != 0;
int i;
int yield = OK;
-int verify_type = expn? v_expn :
- f.address_test_mode? v_none :
- options & vopt_is_recipient? v_recipient : v_sender;
+int verify_type = expn ? v_expn :
+ f.address_test_mode ? v_none :
+ options & vopt_is_recipient ? v_recipient : v_sender;
address_item *addr_list;
address_item *addr_new = NULL;
address_item *addr_remote = NULL;
addr_list = addr->next;
fprintf(fp, "%s", CS addr->address);
-#ifdef EXPERIMENTAL_SRS
+#ifdef EXPERIMENTAL_SRS_ALT
if(addr->prop.srs_sender)
fprintf(fp, " [srs = %s]", addr->prop.srs_sender);
#endif
colon = Ustrchr(h->text, ':');
s = colon + 1;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
/* Loop for multiple addresses in the header, enabling group syntax. Note
that we have to reset this after the header has been scanned. */
{
if (!f.allow_unqualified_recipient) recipient = NULL;
}
- if (recipient == NULL) errmess = US"unqualified address not permitted";
+ if (!recipient) errmess = US"unqualified address not permitted";
}
/* It's an error if no address could be extracted, except for the special
/* Advance to the next address */
s = ss + (terminator ? 1 : 0);
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
} /* Next address */
f.parse_allow_group = FALSE;
colon = Ustrchr(h->text, ':');
s = colon + 1;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
/* Loop for multiple addresses in the header, enabling group syntax. Note
that we have to reset this after the header has been scanned. */
/* Advance to the next address */
s = ss + (terminator ? 1:0);
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
} /* Next address */
f.parse_allow_group = FALSE;
/* If we found an empty address, just carry on with the next one, but
kill the message. */
- if (address == NULL && Ustrcmp(*log_msgptr, "empty address") == 0)
+ if (!address && Ustrcmp(*log_msgptr, "empty address") == 0)
{
*log_msgptr = NULL;
s = ss;
function, and ensure that the failing address gets added to the error
message. */
- if (address == NULL)
+ if (!address)
{
new_ok = FAIL;
while (ss > s && isspace(ss[-1])) ss--;
BOOL isquery = FALSE;
BOOL isiponly = cb->host_name != NULL && cb->host_name[0] == 0;
const uschar *t;
-uschar *semicolon;
+uschar * semicolon, * endname, * opts;
uschar **aliases;
/* Optimize for the special case when the pattern is "*". */
local host's IP addresses. */
if (*ss == '@')
- {
if (ss[1] == 0)
{
if (isiponly) return ERROR;
if (Ustrcmp(ip->address, cb->host_address) == 0) return OK;
return FAIL;
}
- }
/* If the pattern is an IP address, optionally followed by a bitmask count, do
a (possibly masked) comparison with the current IP address. */
return ERROR;
}
-/* See if there is a semicolon in the pattern */
+/* See if there is a semicolon in the pattern, separating a searchtype
+prefix. If there is one then check for comma-sep options. */
-semicolon = Ustrchr(ss, ';');
+if ((semicolon = Ustrchr(ss, ';')))
+ if ((opts = Ustrchr(ss, ',')) && opts < semicolon)
+ {
+ endname = opts++;
+ opts = string_copyn(opts, semicolon - opts);
+ }
+ else
+ {
+ endname = semicolon;
+ opts = NULL;
+ }
/* If we are doing an IP address only match, then all lookups must be IP
address lookups, even if there is no "net-". */
if (isiponly)
- {
iplookup = semicolon != NULL;
- }
/* Otherwise, if the item is of the form net[n]-lookup;<file|query> then it is
a lookup on a masked IP network, in textual form. We obey this code even if we
From release 4.30, the use of net- for query style is no longer needed, but we
retain it for backward compatibility. */
-if (Ustrncmp(ss, "net", 3) == 0 && semicolon != NULL)
+if (Ustrncmp(ss, "net", 3) == 0 && semicolon)
{
mlen = 0;
for (t = ss + 3; isdigit(*t); t++) mlen = mlen * 10 + *t - '0';
if (mlen == 0 && t == ss+3) mlen = -1; /* No mask supplied */
- iplookup = (*t++ == '-');
+ iplookup = *t++ == '-';
}
else
t = ss;
/* Find the search type */
- search_type = search_findtype(t, semicolon - t);
+ search_type = search_findtype(t, endname - t);
if (search_type < 0) 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);
+ result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL, opts);
if (valueptr) *valueptr = result;
return result ? OK : f.search_find_defer ? DEFER: FAIL;
}
outgoing hosts, the name is always given explicitly. If it is NULL, it means we
must use sender_host_name and its aliases, looking them up if necessary. */
-if (cb->host_name != NULL) /* Explicit host name given */
+if (cb->host_name) /* Explicit host name given */
return match_check_string(cb->host_name, ss, -1, TRUE, TRUE, TRUE,
valueptr);
$sender_host_name causes it to be looked up, so we don't need to do the lookup
on spec. */
-if ((semicolon = Ustrchr(ss, ';')) != NULL)
+if ((semicolon = Ustrchr(ss, ';')))
{
- const uschar *affix;
+ const uschar * affix, * opts;
int partial, affixlen, starflags, id;
*semicolon = 0;
- id = search_findtype_partial(ss, &partial, &affix, &affixlen, &starflags);
+ id = search_findtype_partial(ss, &partial, &affix, &affixlen, &starflags,
+ &opts);
*semicolon=';';
if (id < 0) /* Unknown lookup type */
tree_node *t;
dnsbl_cache_block *cb;
int old_pool = store_pool;
-uschar query[256]; /* DNS domain max length */
+uschar * query;
+int qlen;
/* Construct the specific query domainname */
-if (!string_format(query, sizeof(query), "%s.%s", prepend, domain))
+query = string_sprintf("%s.%s", prepend, domain);
+if ((qlen = Ustrlen(query)) >= 256)
{
log_write(0, LOG_MAIN|LOG_PANIC, "dnslist query is too long "
"(ignored): %s...", query);
else
{ /* Set up a tree entry to cache the lookup */
- t = store_get(sizeof(tree_node) + Ustrlen(query), is_tainted(query));
+ t = store_get(sizeof(tree_node) + qlen + 1 + 1, is_tainted(query));
Ustrcpy(t->name, query);
t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block), FALSE);
(void)tree_insertnode(&dnsbl_cache, t);
for (da = cb->rhs; da; da = da->next)
{
int ipsep = ',';
- uschar ip[46];
const uschar *ptr = iplist;
uschar *res;
if (!bitmask)
{
- while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
- if (Ustrcmp(CS da->address, ip) == 0)
+ while ((res = string_nextinlist(&ptr, &ipsep, NULL, 0)))
+ if (Ustrcmp(CS da->address, res) == 0)
break;
}
/* Scan the returned addresses, skipping any that are IPv6 */
- while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
+ while ((res = string_nextinlist(&ptr, &ipsep, NULL, 0)))
{
- if (host_aton(ip, address) != 1) continue;
+ if (host_aton(res, address) != 1) continue;
if ((address[0] & mask) == address[0]) break;
}
}
int defer_return = FAIL;
const uschar *list = *listptr;
uschar *domain;
-uschar buffer[1024];
uschar revadd[128]; /* Long enough for IPv6 address */
/* Indicate that the inverted IP address is not yet set up */
/* Loop through all the domains supplied, until something matches */
-while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+while ((domain = string_nextinlist(&list, &sep, NULL, 0)))
{
int rc;
BOOL bitmask = FALSE;