* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Functions for matching strings */
const uschar *origsubject; /* caseful; keep these two first, in */
const uschar *subject; /* step with the block below */
int expand_setup;
- BOOL use_partial;
- BOOL caseless;
- BOOL at_is_special;
+ mcs_flags flags; /* MCS_* defs in macros.h */
} check_string_block;
const uschar *origaddress; /* caseful; keep these two first, in */
uschar *address; /* step with the block above */
int expand_setup;
- BOOL caseless;
+ mcs_flags flags; /* MCS_CASELESS, MCS_TEXTONLY_RE */
} check_address_block;
*/
static int
-check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error)
+check_string(void * arg, const uschar * pattern, const uschar ** valueptr,
+ uschar ** error)
{
-const check_string_block *cb = arg;
+const check_string_block * cb = arg;
int search_type, partial, affixlen, starflags;
int expand_setup = cb->expand_setup;
const uschar * affix, * opts;
uschar *keyquery, *result, *semicolon;
void *handle;
-error = error; /* Keep clever compilers from complaining */
-
-if (valueptr != NULL) *valueptr = NULL; /* For non-lookup matches */
+if (valueptr) *valueptr = NULL;
/* For regular expressions, use cb->origsubject rather than cb->subject so that
it works if the pattern uses (?-i) to turn off case-independence, overriding
expand_nmax = -1;
if (expand_setup == 0)
{
- expand_nstring[0] = s;
+ expand_nstring[0] = s; /* $0 (might be) the matched subject in full */
expand_nlength[0] = Ustrlen(s);
}
else if (expand_setup > 0) expand_setup--;
if (pattern[0] == '^')
{
- const pcre *re = regex_must_compile(pattern, cb->caseless, FALSE);
- return ((expand_setup < 0)?
- pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) >= 0
- :
- regex_match_and_setup(re, s, 0, expand_setup)
- )?
- OK : FAIL;
+ const pcre2_code * re = regex_must_compile(pattern,
+ cb->flags & (MCS_CACHEABLE | MCS_CASELESS), FALSE);
+ if (expand_setup < 0
+ ? !regex_match(re, s, -1, NULL)
+ : !regex_match_and_setup(re, s, 0, expand_setup)
+ )
+ return FAIL;
+ if (valueptr) *valueptr = pattern; /* "value" gets the RE */
+ return OK;
}
/* Tail match */
if (pattern[0] == '*')
{
- BOOL yield;
int slen = Ustrlen(s);
int patlen; /* Sun compiler doesn't like non-constant initializer */
patlen = Ustrlen(++pattern);
if (patlen > slen) return FAIL;
- yield = cb->caseless?
- (strncmpic(s + slen - patlen, pattern, patlen) == 0) :
- (Ustrncmp(s + slen - patlen, pattern, patlen) == 0);
- if (yield && expand_setup >= 0)
+ if (cb->flags & MCS_CASELESS
+ ? strncmpic(s + slen - patlen, pattern, patlen) != 0
+ : Ustrncmp(s + slen - patlen, pattern, patlen) != 0)
+ return FAIL;
+ if (expand_setup >= 0)
{
- expand_nstring[++expand_setup] = s;
+ expand_nstring[++expand_setup] = s; /* write a $n, the matched subject variable-part */
expand_nlength[expand_setup] = slen - patlen;
- expand_nmax = expand_setup;
+ expand_nmax = expand_setup; /* commit also $0, the matched subject */
}
- return yield? OK : FAIL;
+ if (valueptr) *valueptr = pattern - 1; /* "value" gets the (original) pattern */
+ return OK;
}
/* Match a special item starting with @ if so enabled. On its own, "@" matches
cases we have to do some more work. If we don't recognize a special pattern,
just fall through - the match will fail. */
-if (cb->at_is_special && pattern[0] == '@')
+if (cb->flags & MCS_AT_SPECIAL && pattern[0] == '@')
{
if (pattern[1] == 0)
{
if (Ustrcmp(pattern, "@[]") == 0)
{
int slen = Ustrlen(s);
- if (s[0] != '[' && s[slen-1] != ']') return FAIL;
+ if (s[0] != '[' && s[slen-1] != ']') return FAIL; /*XXX should this be || ? */
for (ip_address_item * ip = host_find_interfaces(); ip; ip = ip->next)
if (Ustrncmp(ip->address, s+1, slen - 2) == 0
&& ip->address[slen - 2] == 0)
+ {
+ if (expand_setup >= 0) expand_nmax = expand_setup; /* commit $0, the IP addr */
+ if (valueptr) *valueptr = pattern; /* "value" gets the pattern */
return OK;
+ }
return FAIL;
}
else goto NOT_AT_SPECIAL;
if (strncmpic(ss, US"/ignore=", 8) == 0) ignore_target_hosts = ss + 8;
- else if (*ss != 0) goto NOT_AT_SPECIAL;
+ else if (*ss) goto NOT_AT_SPECIAL;
h.next = NULL;
h.name = s;
return DEFER;
}
- if (rc == HOST_FOUND_LOCAL && !secy) return OK;
- if (prim) return FAIL;
- return removed? OK : FAIL;
+ if ((rc != HOST_FOUND_LOCAL || secy) && (prim || !removed))
+ return FAIL;
+
+ if (expand_setup >= 0) expand_nmax = expand_setup; /* commit $0, the matched subject */
+ if (valueptr) *valueptr = pattern; /* "value" gets the patterm */
+ return OK;
/*** The above line used to be the following line, but this is incorrect,
because host_find_bydns() may return HOST_NOT_FOUND if it removed some MX
if ((semicolon = Ustrchr(pattern, ';')) == NULL)
{
- BOOL yield = cb->caseless?
- (strcmpic(s, pattern) == 0) : (Ustrcmp(s, pattern) == 0);
- if (yield && expand_setup >= 0) expand_nmax = expand_setup;
- return yield? OK : FAIL;
+ if (cb->flags & MCS_CASELESS ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0)
+ return FAIL;
+ if (expand_setup >= 0) expand_nmax = expand_setup; /* $0 gets the matched subject */
+ if (valueptr) *valueptr = pattern; /* "value" gets the pattern */
+ return OK;
}
/* Otherwise we have a lookup item. The lookup type, including partial, etc. is
/* Partial matching is not appropriate for certain lookups (e.g. when looking
up user@domain for sender rejection). There's a flag to disable it. */
-if (!cb->use_partial) partial = -1;
+if (!(cb->flags & MCS_PARTIAL)) partial = -1;
/* Set the parameters for the three different kinds of lookup. */
-keyquery = semicolon + 1;
-while (isspace(*keyquery)) keyquery++;
-
-if (mac_islookup(search_type, lookup_absfilequery))
- {
- filename = keyquery;
- while (*keyquery != 0 && !isspace(*keyquery)) keyquery++;
- filename = string_copyn(filename, keyquery - filename);
- while (isspace(*keyquery)) keyquery++;
- }
-
-else if (!mac_islookup(search_type, lookup_querystyle))
- {
- filename = keyquery;
- keyquery = s;
- }
+keyquery = search_args(search_type, s, semicolon+1, &filename, opts);
/* Now do the actual lookup; throw away the data returned unless it was asked
for; partial matching is all handled inside search_find(). Note that there is
result = search_find(handle, filename, keyquery, partial, affix, affixlen,
starflags, &expand_setup, opts);
-if (!result) return f.search_find_defer? DEFER : FAIL;
+if (!result) return f.search_find_defer ? DEFER : FAIL;
if (valueptr) *valueptr = result;
expand_nmax = expand_setup;
s the subject string to be checked
pattern the pattern to check it against
expand_setup expansion setup option (see check_string())
- use_partial if FALSE, override any partial- search types
- caseless TRUE for caseless matching where possible
- at_is_special TRUE to recognize @, @[], etc.
+ flags
+ use_partial if FALSE, override any partial- search types
+ caseless TRUE for caseless matching where possible
+ at_is_special TRUE to recognize @, @[], etc.
valueptr if not NULL, and a file lookup was done, return the result
here instead of discarding it; else set it to point to NULL
*/
int
-match_check_string(const uschar *s, const uschar *pattern, int expand_setup,
- BOOL use_partial, BOOL caseless, BOOL at_is_special, const uschar **valueptr)
+match_check_string(const uschar * s, const uschar * pattern, int expand_setup,
+ mcs_flags flags, const uschar ** valueptr)
{
check_string_block cb;
cb.origsubject = s;
-cb.subject = caseless? string_copylc(s) : string_copy(s);
+cb.subject = flags & MCS_CASELESS ? string_copylc(s) : string_copy(s);
cb.expand_setup = expand_setup;
-cb.use_partial = use_partial;
-cb.caseless = caseless;
-cb.at_is_special = at_is_special;
+cb.flags = flags;
return check_string(&cb, pattern, valueptr, NULL);
}
{
case MCL_STRING:
case MCL_DOMAIN:
- case MCL_LOCALPART:
- return ((check_string_block *)arg)->subject;
-
- case MCL_HOST:
- return ((check_host_block *)arg)->host_address;
-
- case MCL_ADDRESS:
- return ((check_address_block *)arg)->address;
+ case MCL_LOCALPART: return ((check_string_block *)arg)->subject;
+ case MCL_HOST: return ((check_host_block *)arg)->host_address;
+ case MCL_ADDRESS: return ((check_address_block *)arg)->address;
}
return US""; /* In practice, should never happen */
}
void *arg, int type, const uschar *name, const uschar **valueptr)
{
int yield = OK;
-unsigned int *original_cache_bits = *cache_ptr;
-BOOL include_unknown = FALSE;
-BOOL ignore_unknown = FALSE;
-BOOL include_defer = FALSE;
-BOOL ignore_defer = FALSE;
-const uschar *list;
-uschar *sss;
-uschar *ot = NULL;
-uschar buffer[1024];
+unsigned int * original_cache_bits = *cache_ptr;
+BOOL include_unknown = FALSE, ignore_unknown = FALSE,
+ include_defer = FALSE, ignore_defer = FALSE;
+const uschar * list;
+uschar * sss;
+uschar * ot = NULL;
+BOOL textonly_re;
/* Save time by not scanning for the option name when we don't need it. */
HDEBUG(D_any)
{
- uschar *listname = readconf_find_option(listptr);
- if (listname[0] != 0) ot = string_sprintf("%s in %s?", name, listname);
+ uschar * listname = readconf_find_option(listptr);
+ if (*listname) ot = string_sprintf("%s in %s?", name, listname);
}
/* If the list is empty, the answer is no. Skip the debugging output for
if (!*listptr)
{
- HDEBUG(D_lists) if (ot) debug_printf("%s no (option unset)\n", ot);
+ HDEBUG(D_lists) if (ot) debug_printf_indent("%s no (option unset)\n", ot);
return FAIL;
}
{
list = *listptr;
type -= MCL_NOEXPAND; /* Remove the "no expand" flag */
+ textonly_re = TRUE;
}
else
{
{
check_string_block *cb = (check_string_block *)arg;
deliver_domain = string_copy(cb->subject);
- list = expand_cstring(*listptr);
+ list = expand_string_2(*listptr, &textonly_re);
deliver_domain = NULL;
}
else
- list = expand_cstring(*listptr);
+ list = expand_string_2(*listptr, &textonly_re);
if (!list)
{
if (f.expand_string_forcedfail)
{
- HDEBUG(D_lists) debug_printf("expansion of \"%s\" forced failure: "
+ HDEBUG(D_lists) debug_printf_indent("expansion of \"%s\" forced failure: "
"assume not in this list\n", *listptr);
return FAIL;
}
}
}
+if (textonly_re) switch (type)
+ {
+ case MCL_STRING:
+ case MCL_DOMAIN:
+ case MCL_LOCALPART: ((check_string_block *)arg)->flags |= MCS_CACHEABLE; break;
+ case MCL_HOST: ((check_host_block *)arg)->flags |= MCS_CACHEABLE; break;
+ case MCL_ADDRESS: ((check_address_block *)arg)->flags |= MCS_CACHEABLE; break;
+ }
+
/* For an unnamed list, use the expanded version in comments */
+#define LIST_LIMIT_PR 2048
-HDEBUG(D_any) if (ot == NULL) ot = string_sprintf("%s in \"%s\"?", name, list);
+HDEBUG(D_any) if (!ot)
+ {
+ int n, m;
+ gstring * g = string_fmt_append(NULL, "%s in \"%n%.*s%n\"",
+ name, &n, LIST_LIMIT_PR, list, &m);
+ if (m - n >= LIST_LIMIT_PR) g = string_catn(g, US"...", 3);
+ g = string_catn(g, US"?", 1);
+ gstring_release_unused(g);
+ ot = string_from_gstring(g);
+ }
+HDEBUG(D_lists)
+ {
+ debug_printf_indent("%s\n", ot);
+ expand_level++;
+ }
/* Now scan the list and process each item in turn, until one of them matches,
or we hit an error. */
-while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
{
uschar * ss = sss;
+ HDEBUG(D_lists) debug_printf_indent("list element: %s\n", ss);
+
/* Address lists may contain +caseful, to restore caseful matching of the
local part. We have to know the layout of the control block, unfortunately.
The lower cased address is in a temporary buffer, so we just copy the local
if (at)
Ustrncpy(cb->address, cb->origaddress, at - cb->origaddress);
- cb->caseless = FALSE;
+ cb->flags &= ~MCS_CASELESS;
continue;
}
}
{
check_string_block *cb = (check_string_block *)arg;
Ustrcpy(US cb->subject, cb->origsubject);
- cb->caseless = FALSE;
+ cb->flags &= ~MCS_CASELESS;
continue;
}
}
{
if (*ss == '+' && anchorptr)
{
- int bits = 0;
- int offset = 0;
- int shift = 0;
- unsigned int *use_cache_bits = original_cache_bits;
- uschar *cached = US"";
- namedlist_block *nb;
+ int bits = 0, offset = 0, shift = 0;
+ unsigned int * use_cache_bits = original_cache_bits;
+ uschar * cached = US"";
+ namedlist_block * nb;
tree_node * t;
+ DEBUG(D_lists)
+ { debug_printf_indent(" start sublist %s\n", ss+1); expand_level += 2; }
+
if (!(t = tree_search(*anchorptr, ss+1)))
{
log_write(0, LOG_MAIN|LOG_PANIC, "unknown named%s list \"%s\"",
type == MCL_ADDRESS ? " address" :
type == MCL_LOCALPART ? " local part" : "",
ss);
- return DEFER;
+ goto DEFER_RETURN;
}
nb = t->data.ptr;
if (bits == 0)
{
- switch (match_check_list(&(nb->string), 0, anchorptr, &use_cache_bits,
- func, arg, type, name, valueptr))
+ int res = match_check_list(&(nb->string), 0, anchorptr, &use_cache_bits,
+ func, arg, type, name, valueptr);
+ DEBUG(D_lists)
+ { expand_level -= 2; debug_printf_indent(" end sublist %s\n", ss+1); }
+
+ switch (res)
{
case OK: bits = 1; break;
case FAIL: bits = 3; break;
so we use the permanent store pool */
store_pool = POOL_PERM;
- p = store_get(sizeof(namedlist_cacheblock), FALSE);
+ p = store_get(sizeof(namedlist_cacheblock), GET_UNTAINTED);
p->key = string_copy(get_check_key(arg, type));
p->next = nb->cache_data;
nb->cache_data = p;
if (*valueptr)
- DEBUG(D_lists) debug_printf("data from lookup saved for "
- "cache for %s: %s\n", ss, *valueptr);
+ DEBUG(D_lists) debug_printf_indent("data from lookup saved for "
+ "cache for %s: key '%s' value '%s'\n", ss, p->key, *valueptr);
}
}
}
else
{
- DEBUG(D_lists) debug_printf("cached %s match for %s\n",
- ((bits & (-bits)) == bits)? "yes" : "no", ss);
+ DEBUG(D_lists)
+ {
+ expand_level -= 2;
+ debug_printf_indent("cached %s match for %s\n",
+ (bits & (-bits)) == bits ? "yes" : "no", ss);
+ }
cached = US" - cached";
if (valueptr)
*valueptr = p->data;
break;
}
- DEBUG(D_lists) debug_printf("cached lookup data = %s\n", *valueptr);
+ DEBUG(D_lists) debug_printf_indent("cached lookup data = %s\n", *valueptr);
}
}
if ((bits & (-bits)) == bits) /* Only one of the two bits is set */
{
- HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\"%s)\n", ot,
- (yield == OK)? "yes" : "no", sss, cached);
- return yield;
+ HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\"%s)\n", ot,
+ yield == OK ? "yes" : "no", sss, cached);
+ goto YIELD_RETURN;
}
}
switch ((func)(arg, ss, valueptr, &error))
{
case OK:
- HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\")\n", ot,
+ HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\")\n", ot,
(yield == OK)? "yes" : "no", sss);
- return yield;
+ goto YIELD_RETURN;
case DEFER:
if (!error)
error = string_sprintf("DNS lookup of \"%s\" deferred", ss);
if (ignore_defer)
{
- HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n",
+ HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_defer\n",
error);
break;
}
case ERROR:
if (ignore_unknown)
{
- HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n",
+ HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_unknown\n",
error);
}
else
{
- HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot,
+ HDEBUG(D_lists) debug_printf_indent("%s %s (%s)\n", ot,
include_unknown? "yes":"no", error);
if (!include_unknown)
{
if (listname[0] == 0)
listname = string_sprintf("\"%s\"", *listptr);
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- string_open_failed(errno, "%s when checking %s", sss, listname));
+ string_open_failed("%s when checking %s", sss, listname));
}
/* Trailing comments are introduced by #, but in an address list or local
sss = ss + 1;
}
- ss = filebuffer + Ustrlen(filebuffer); /* trailing space */
+ ss = filebuffer + Ustrlen(filebuffer); /* trailing space */
while (ss > filebuffer && isspace(ss[-1])) ss--;
*ss = 0;
ss = filebuffer;
- while (isspace(*ss)) ss++; /* leading space */
+ while (isspace(*ss)) ss++; /* leading space */
- if (*ss == 0) continue; /* ignore empty */
+ if (!*ss) continue; /* ignore empty */
- file_yield = yield; /* positive yield */
- sss = ss; /* for debugging */
+ file_yield = yield; /* positive yield */
+ sss = ss; /* for debugging */
- if (*ss == '!') /* negation */
+ if (*ss == '!') /* negation */
{
file_yield = (file_yield == OK)? FAIL : OK;
while (isspace((*(++ss))));
{
case OK:
(void)fclose(f);
- HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\" in %s)\n", ot,
+ HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\" in %s)\n", ot,
yield == OK ? "yes" : "no", sss, filename);
- return file_yield;
+
+ /* The "pattern" being matched came from the file; we use a stack-local.
+ Copy it to allocated memory now we know it matched. */
+
+ if (valueptr) *valueptr = string_copy(ss);
+ yield = file_yield;
+ goto YIELD_RETURN;
case DEFER:
if (!error)
error = string_sprintf("DNS lookup of %s deferred", ss);
if (ignore_defer)
{
- HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n",
+ HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_defer\n",
error);
break;
}
(void)fclose(f);
- if (include_defer)
- {
- log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error);
- return OK;
- }
- goto DEFER_RETURN;
+ if (!include_defer)
+ goto DEFER_RETURN;
+ log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error);
+ goto OK_RETURN;
case ERROR: /* host name lookup failed - this can only */
if (ignore_unknown) /* be for an incoming host (not outgoing) */
{
- HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n",
+ HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_unknown\n",
error);
}
else
{
- HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot,
+ HDEBUG(D_lists) debug_printf_indent("%s %s (%s)\n", ot,
include_unknown? "yes":"no", error);
(void)fclose(f);
if (!include_unknown)
{
if (LOGGING(unknown_in_list))
log_write(0, LOG_MAIN, "list matching forced to fail: %s", error);
- return FAIL;
+ goto FAIL_RETURN;
}
log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error);
- return OK;
+ goto OK_RETURN;
}
}
}
/* End of list reached: if the last item was negated yield OK, else FAIL. */
HDEBUG(D_lists)
- debug_printf("%s %s (end of list)\n", ot, yield == OK ? "no":"yes");
-return yield == OK ? FAIL : OK;
+ HDEBUG(D_lists)
+ {
+ expand_level--;
+ debug_printf_indent("%s %s (end of list)\n", ot, yield == OK ? "no":"yes");
+ }
+ return yield == OK ? FAIL : OK;
/* Something deferred */
DEFER_RETURN:
-HDEBUG(D_lists) debug_printf("%s list match deferred for %s\n", ot, sss);
-return DEFER;
+ HDEBUG(D_lists)
+ {
+ expand_level--;
+ debug_printf_indent("%s list match deferred for %s\n", ot, sss);
+ }
+ return DEFER;
+
+FAIL_RETURN:
+ yield = FAIL;
+ goto YIELD_RETURN;
+
+OK_RETURN:
+ yield = OK;
+
+YIELD_RETURN:
+ HDEBUG(D_lists) expand_level--;
+ return yield;
}
s string to search for
listptr ptr to ptr to colon separated list of patterns, or NULL
sep a separator value for the list (see string_nextinlist())
+ or zero for auto
anchorptr ptr to tree for named items, or NULL if no named items
cache_bits ptr to cache_bits for ditto, or NULL if not caching
type MCL_DOMAIN when matching a domain list
unsigned int *local_cache_bits = cache_bits;
check_string_block cb;
cb.origsubject = s;
-cb.subject = caseless? string_copylc(s) : string_copy(s);
-cb.expand_setup = (sep > UCHAR_MAX)? 0 : -1;
-cb.use_partial = TRUE;
-cb.caseless = caseless;
-cb.at_is_special = (type == MCL_DOMAIN || type == MCL_DOMAIN + MCL_NOEXPAND);
-if (valueptr != NULL) *valueptr = NULL;
+cb.subject = caseless ? string_copylc(s) : string_copy(s);
+cb.flags = caseless ? MCS_PARTIAL+MCS_CASELESS : MCS_PARTIAL;
+switch (type & ~MCL_NOEXPAND)
+ {
+ case MCL_DOMAIN: cb.flags |= MCS_AT_SPECIAL; /*FALLTHROUGH*/
+ case MCL_LOCALPART: cb.expand_setup = 0; break;
+ default: cb.expand_setup = sep > UCHAR_MAX ? 0 : -1; break;
+ }
+if (valueptr) *valueptr = NULL;
return match_check_list(listptr, sep, anchorptr, &local_cache_bits,
check_string, &cb, type, s, valueptr);
}
*/
static int
-check_address(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error)
+check_address(void * arg, const uschar * pattern, const uschar ** valueptr,
+ uschar ** error)
{
-check_address_block *cb = (check_address_block *)arg;
+check_address_block * cb = (check_address_block *)arg;
check_string_block csb;
int rc;
int expand_inc = 0;
-unsigned int *null = NULL;
-const uschar *listptr;
-uschar *subject = cb->address;
-const uschar *s;
-uschar *pdomain, *sdomain;
-
-error = error; /* Keep clever compilers from complaining */
-
-DEBUG(D_lists) debug_printf("address match test: subject=%s pattern=%s\n",
+unsigned int * null = NULL;
+const uschar * listptr;
+uschar * subject = cb->address;
+const uschar * s;
+uschar * pdomain, * sdomain;
+uschar * value = NULL;
+
+DEBUG(D_lists) debug_printf_indent("address match test: subject=%s pattern=%s\n",
subject, pattern);
/* Find the subject's domain */
/* The only case where a subject may not have a domain is if the subject is
empty. Otherwise, a subject with no domain is a serious configuration error. */
-if (sdomain == NULL && *subject != 0)
+if (!sdomain && *subject)
{
log_write(0, LOG_MAIN|LOG_PANIC, "no @ found in the subject of an "
"address list match: subject=\"%s\" pattern=\"%s\"", subject, pattern);
This may be the empty address. */
if (*pattern == '^')
- return match_check_string(subject, pattern, cb->expand_setup, TRUE,
- cb->caseless, FALSE, NULL);
+ return match_check_string(subject, pattern, cb->expand_setup,
+ cb->flags | MCS_PARTIAL, NULL);
/* Handle a pattern that is just a lookup. Skip over possible lookup names
(letters, digits, hyphens). Skip over a possible * or *@ at the end. Then we
must have a semicolon for it to be a lookup. */
-for (s = pattern; isalnum(*s) || *s == '-'; s++);
+for (s = pattern; isalnum(*s) || *s == '-'; s++) ;
if (*s == '*') s++;
if (*s == '@') s++;
if (Ustrncmp(pattern, "partial-", 8) == 0)
log_write(0, LOG_MAIN|LOG_PANIC, "partial matching is not applicable to "
"whole-address lookups: ignored \"partial-\" in \"%s\"", pattern);
- return match_check_string(subject, pattern, -1, FALSE, cb->caseless, FALSE,
- valueptr);
+ return match_check_string(subject, pattern, -1, cb->flags, valueptr);
}
/* For the remaining cases, an empty subject matches only an empty pattern,
because other patterns expect to have a local part and a domain to match
against. */
-if (*subject == 0) return (*pattern == 0)? OK : FAIL;
+if (!*subject) return *pattern ? FAIL : OK;
/* If the pattern starts with "@@" we have a split lookup, where the domain is
looked up to obtain a list of local parts. If the subject's local part is just
{
int watchdog = 50;
uschar *list, *ss;
- uschar buffer[1024];
if (sdomain == subject + 1 && *subject == '*') return FAIL;
{
int sep = 0;
- if ((rc = match_check_string(key, pattern + 2, -1, TRUE, FALSE, FALSE,
- CUSS &list)) != OK) return rc;
+ if ((rc = match_check_string(key, pattern + 2, -1, MCS_PARTIAL, CUSS &list))
+ != OK)
+ return rc;
/* Check for chaining from the last item; set up the next key if one
is found. */
ss = Ustrrchr(list, ':');
- if (ss == NULL) ss = list; else ss++;
- while (isspace(*ss)) ss++;
+ if (!ss) ss = list; else ss++;
+ Uskip_whitespace(&ss);
if (*ss == '>')
{
*ss++ = 0;
- while (isspace(*ss)) ss++;
+ Uskip_whitespace(&ss);
key = string_copy(ss);
}
else key = NULL;
/* Look up the local parts provided by the list; negation is permitted.
If a local part has to begin with !, a regex can be used. */
- while ((ss = string_nextinlist(CUSS &list, &sep, buffer, sizeof(buffer))))
+ while ((ss = string_nextinlist(CUSS &list, &sep, NULL, 0)))
{
int local_yield;
else local_yield = OK;
*sdomain = 0;
- rc = match_check_string(subject, ss, -1, TRUE, cb->caseless, FALSE,
- valueptr);
+ rc = match_check_string(subject, ss, -1, cb->flags + MCS_PARTIAL, valueptr);
*sdomain = '@';
switch(rc)
/* We get here if the pattern is not a lookup or a regular expression. If it
contains an @ there is both a local part and a domain. */
-pdomain = Ustrrchr(pattern, '@');
-if (pdomain != NULL)
+if ((pdomain = Ustrrchr(pattern, '@')))
{
int pllen, sllen;
{
int cllen = pllen - 1;
if (sllen < cllen) return FAIL;
- if (cb->caseless)
- {
- if (strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0)
- return FAIL;
- }
- else
- {
- if (Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0)
+ if (cb->flags & MCS_CASELESS
+ ? strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0
+ : Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0)
return FAIL;
- }
if (cb->expand_setup > 0)
{
expand_nstring[cb->expand_setup] = subject;
expand_nlength[cb->expand_setup] = sllen - cllen;
expand_inc = 1;
}
+ value = string_copyn(pattern + 1, cllen);
}
else
{
if (sllen != pllen) return FAIL;
- if (cb->caseless)
- {
- if (strncmpic(subject, pattern, sllen) != 0) return FAIL;
- }
- else
- {
- if (Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
- }
+ if (cb->flags & MCS_CASELESS
+ ? strncmpic(subject, pattern, sllen) != 0
+ : Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
}
+ value = string_copyn(pattern, sllen);
}
/* If the local part matched, or was not being checked, check the domain using
original code read as follows:
return match_check_string(sdomain + 1,
- (pdomain == NULL)? pattern : pdomain + 1,
- cb->expand_setup + expand_inc, TRUE, cb->caseless, TRUE, NULL);
+ pdomain ? pdomain + 1 : pattern,
+ cb->expand_setup + expand_inc, cb->flags, NULL);
This supported only literal domains and *.x.y patterns. In order to allow for
-named domain lists (so that you can right, for example, "senders=+xxxx"), it
+named domain lists (so that you can write, for example, "senders=+xxxx"), it
was changed to use the list scanning function. */
csb.origsubject = sdomain + 1;
-csb.subject = (cb->caseless)? string_copylc(sdomain+1) : string_copy(sdomain+1);
+csb.subject = cb->flags & MCS_CASELESS
+ ? string_copylc(sdomain+1) : string_copy(sdomain+1);
csb.expand_setup = cb->expand_setup + expand_inc;
-csb.use_partial = TRUE;
-csb.caseless = cb->caseless;
-csb.at_is_special = TRUE;
-
-listptr = (pdomain == NULL)? pattern : pdomain + 1;
-if (valueptr != NULL) *valueptr = NULL;
-
-return match_check_list(
- &listptr, /* list of one item */
- UCHAR_MAX+1, /* impossible separator; single item */
- &domainlist_anchor, /* it's a domain list */
- &null, /* ptr to NULL means no caching */
- check_string, /* the function to do one test */
- &csb, /* its data */
- MCL_DOMAIN + MCL_NOEXPAND, /* domain list; don't expand */
- csb.subject, /* string for messages */
- valueptr); /* where to pass back lookup data */
+csb.flags = MCS_PARTIAL | MCS_AT_SPECIAL | cb->flags & MCS_CASELESS;
+
+listptr = pdomain ? pdomain + 1 : pattern;
+if (valueptr) *valueptr = NULL;
+
+ {
+ const uschar * dvalue = NULL;
+ rc = match_check_list(
+ &listptr, /* list of one item */
+ UCHAR_MAX+1, /* impossible separator; single item */
+ &domainlist_anchor, /* it's a domain list */
+ &null, /* ptr to NULL means no caching */
+ check_string, /* the function to do one test */
+ &csb, /* its data */
+ MCL_DOMAIN + MCL_NOEXPAND, /* domain list; don't expand */
+ csb.subject, /* string for messages */
+ &dvalue); /* where to pass back lookup data */
+ if (valueptr && (value || dvalue))
+ *valueptr = string_sprintf("%s@%s",
+ value ? value : US"", dvalue ? dvalue : US"");
+ }
+return rc;
}
provided that "caseless" is set. (It is FALSE for calls for matching rewriting
patterns.) Otherwise just the domain is lower cases. A magic item "+caseful" in
the list can be used to restore a caseful copy of the local part from the
-original address. */
+original address.
+Limit the subject address size to avoid mem-exhaustion attacks. The size chosen
+is historical (we used to use big_buffer here). */
-if ((len = Ustrlen(address)) > 255) len = 255;
+if ((len = Ustrlen(address)) > BIG_BUFFER_SIZE) len = BIG_BUFFER_SIZE;
ab.address = string_copyn(address, len);
for (uschar * p = ab.address + len - 1; p >= ab.address; p--)
ab.origaddress = address;
/* ab.address is above */
ab.expand_setup = expand_setup;
-ab.caseless = caseless;
+ab.flags = caseless ? MCS_CASELESS : 0;
return match_check_list(listptr, sep, &addresslist_anchor, &local_cache_bits,
- check_address, &ab, MCL_ADDRESS + (expand? 0:MCL_NOEXPAND), address,
+ check_address, &ab, MCL_ADDRESS + (expand ? 0 : MCL_NOEXPAND), address,
valueptr);
}