X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fmatch.c;h=65d44198e6a25b82f81e31a5ee09b9b3c130eaa2;hb=43ba45ce62100bc1dbc9b04b5d869f59026783f5;hp=08ec0eeabb29f73295cd2158ed0648e1d3534625;hpb=72934ba73e5ac5fbd64b56dc684e3371a9651909;p=exim.git diff --git a/src/src/match.c b/src/src/match.c index 08ec0eeab..65d44198e 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -2,7 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2017 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for matching strings */ @@ -97,7 +98,7 @@ check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar * const check_string_block *cb = arg; int search_type, partial, affixlen, starflags; int expand_setup = cb->expand_setup; -const uschar *affix; +const uschar * affix, * opts; uschar *s; uschar *filename = NULL; uschar *keyquery, *result, *semicolon; @@ -105,7 +106,7 @@ 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 @@ -119,7 +120,7 @@ expand_nmax until the match is assured. */ 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--; @@ -129,35 +130,37 @@ required. */ 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 pcre * re = regex_must_compile(pattern, cb->caseless, FALSE); + if (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) + ) + 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->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 @@ -175,13 +178,16 @@ if (cb->at_is_special && pattern[0] == '@') if (Ustrcmp(pattern, "@[]") == 0) { - ip_address_item *ip; int slen = Ustrlen(s); - if (s[0] != '[' && s[slen-1] != ']') return FAIL; - for (ip = host_find_interfaces(); ip != NULL; ip = ip->next) + 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; } @@ -209,7 +215,7 @@ if (cb->at_is_special && pattern[0] == '@') 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; @@ -231,9 +237,12 @@ if (cb->at_is_special && pattern[0] == '@') 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 @@ -253,10 +262,11 @@ NOT_AT_SPECIAL: 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->caseless ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0) + return FAIL; + if (expand_setup >= 0) expand_nmax = expand_setup; /* Original code! $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 @@ -264,7 +274,7 @@ the part of the string preceding the semicolon. */ *semicolon = 0; search_type = search_findtype_partial(pattern, &partial, &affix, &affixlen, - &starflags); + &starflags, &opts); *semicolon = ';'; if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message); @@ -277,14 +287,14 @@ if (!cb->use_partial) partial = -1; /* Set the parameters for the three different kinds of lookup. */ keyquery = semicolon + 1; -while (isspace(*keyquery)) keyquery++; +Uskip_whitespace(&keyquery); if (mac_islookup(search_type, lookup_absfilequery)) { filename = keyquery; - while (*keyquery != 0 && !isspace(*keyquery)) keyquery++; + while (*keyquery && !isspace(*keyquery)) keyquery++; filename = string_copyn(filename, keyquery - filename); - while (isspace(*keyquery)) keyquery++; + Uskip_whitespace(&keyquery); } else if (!mac_islookup(search_type, lookup_querystyle)) @@ -297,14 +307,13 @@ else if (!mac_islookup(search_type, lookup_querystyle)) for; partial matching is all handled inside search_find(). 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, keyquery, partial, affix, affixlen, - starflags, &expand_setup); + starflags, &expand_setup, opts); -if (result == NULL) return search_find_defer? DEFER : FAIL; -if (valueptr != NULL) *valueptr = result; +if (!result) return f.search_find_defer ? DEFER : FAIL; +if (valueptr) *valueptr = result; expand_nmax = expand_setup; return OK; @@ -341,7 +350,7 @@ match_check_string(const uschar *s, const uschar *pattern, int expand_setup, { check_string_block cb; cb.origsubject = s; -cb.subject = caseless? string_copylc(s) : string_copy(s); +cb.subject = caseless ? string_copylc(s) : string_copy(s); cb.expand_setup = expand_setup; cb.use_partial = use_partial; cb.caseless = caseless; @@ -373,13 +382,13 @@ switch(type) case MCL_STRING: case MCL_DOMAIN: case MCL_LOCALPART: - return ((check_string_block *)arg)->subject; + return ((check_string_block *)arg)->subject; case MCL_HOST: - return ((check_host_block *)arg)->host_address; + return ((check_host_block *)arg)->host_address; case MCL_ADDRESS: - return ((check_address_block *)arg)->address; + return ((check_address_block *)arg)->address; } return US""; /* In practice, should never happen */ } @@ -448,7 +457,6 @@ BOOL ignore_defer = FALSE; const uschar *list; uschar *sss; uschar *ot = NULL; -uschar buffer[1024]; /* Save time by not scanning for the option name when we don't need it. */ @@ -494,7 +502,7 @@ else if (!list) { - if (expand_string_forcedfail) + if (f.expand_string_forcedfail) { HDEBUG(D_lists) debug_printf("expansion of \"%s\" forced failure: " "assume not in this list\n", *listptr); @@ -508,12 +516,12 @@ else /* For an unnamed list, use the expanded version in comments */ -HDEBUG(D_any) if (ot == NULL) ot = string_sprintf("%s in \"%s\"?", name, list); +HDEBUG(D_any) if (!ot) ot = string_sprintf("%s in \"%s\"?", name, list); /* 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; @@ -543,7 +551,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) if (Ustrcmp(ss, "+caseful") == 0) { check_string_block *cb = (check_string_block *)arg; - Ustrcpy(cb->subject, cb->origsubject); + Ustrcpy(US cb->subject, cb->origsubject); cb->caseless = FALSE; continue; } @@ -668,7 +676,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) so we use the permanent store pool */ store_pool = POOL_PERM; - p = store_get(sizeof(namedlist_cacheblock)); + p = store_get(sizeof(namedlist_cacheblock), FALSE); p->key = string_copy(get_check_key(arg, type)); @@ -679,7 +687,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) nb->cache_data = p; if (*valueptr) DEBUG(D_lists) debug_printf("data from lookup saved for " - "cache for %s: %s\n", ss, *valueptr); + "cache for %s: key '%s' value '%s'\n", ss, p->key, *valueptr); } } } @@ -691,14 +699,14 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) else { DEBUG(D_lists) debug_printf("cached %s match for %s\n", - ((bits & (-bits)) == bits)? "yes" : "no", ss); + (bits & (-bits)) == bits ? "yes" : "no", ss); cached = US" - cached"; if (valueptr) { const uschar *key = get_check_key(arg, type); - namedlist_cacheblock *p; - for (p = nb->cache_data; p; p = p->next) + + for (namedlist_cacheblock * p = nb->cache_data; p; p = p->next) if (Ustrcmp(key, p->key) == 0) { *valueptr = p->data; @@ -953,12 +961,17 @@ match_isinlist(const uschar *s, const uschar **listptr, int sep, 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.subject = caseless ? string_copylc(s) : string_copy(s); +cb.at_is_special = FALSE; +switch (type & ~MCL_NOEXPAND) + { + case MCL_DOMAIN: cb.at_is_special = TRUE; /*FALLTHROUGH*/ + case MCL_LOCALPART: cb.expand_setup = 0; break; + default: cb.expand_setup = sep > UCHAR_MAX ? 0 : -1; break; + } cb.use_partial = TRUE; cb.caseless = caseless; -cb.at_is_special = (type == MCL_DOMAIN || type == MCL_DOMAIN + MCL_NOEXPAND); -if (valueptr != NULL) *valueptr = NULL; +if (valueptr) *valueptr = NULL; return match_check_list(listptr, sep, anchorptr, &local_cache_bits, check_string, &cb, type, s, valueptr); } @@ -1066,7 +1079,6 @@ looked up to obtain a list of local parts. If the subject's local part is just if (pattern[0] == '@' && pattern[1] == '@') { int watchdog = 50; - const uschar *key; uschar *list, *ss; uschar buffer[1024]; @@ -1075,7 +1087,7 @@ if (pattern[0] == '@' && pattern[1] == '@') /* Loop for handling chains. The last item in any list may be of the form ">name" in order to chain on to another list. */ - for (key = sdomain + 1; key != NULL && watchdog-- > 0; ) + for (const uschar * key = sdomain + 1; key && watchdog-- > 0; ) { int sep = 0; @@ -1171,16 +1183,10 @@ if (pdomain != NULL) { int cllen = pllen - 1; if (sllen < cllen) return FAIL; - if (cb->caseless) - { - if (strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0) + if (cb->caseless + ? strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0 + : Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0) return FAIL; - } - else - { - if (Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0) - return FAIL; - } if (cb->expand_setup > 0) { expand_nstring[cb->expand_setup] = subject; @@ -1191,14 +1197,9 @@ if (pdomain != NULL) 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->caseless + ? strncmpic(subject, pattern, sllen) != 0 + : Ustrncmp(subject, pattern, sllen) != 0) return FAIL; } } @@ -1207,7 +1208,7 @@ the generalized function, which supports file lookups (which may defer). The original code read as follows: return match_check_string(sdomain + 1, - (pdomain == NULL)? pattern : pdomain + 1, + pdomain ? pdomain + 1 : pattern, cb->expand_setup + expand_inc, TRUE, cb->caseless, TRUE, NULL); This supported only literal domains and *.x.y patterns. In order to allow for @@ -1215,14 +1216,14 @@ named domain lists (so that you can right, 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->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; +listptr = pdomain ? pdomain + 1 : pattern; +if (valueptr) *valueptr = NULL; return match_check_list( &listptr, /* list of one item */ @@ -1278,9 +1279,9 @@ match_address_list(const uschar *address, BOOL caseless, BOOL expand, const uschar **listptr, unsigned int *cache_bits, int expand_setup, int sep, const uschar **valueptr) { -uschar *p; check_address_block ab; unsigned int *local_cache_bits = cache_bits; +int len; /* RFC 2505 recommends that for spam checking, local parts should be caselessly compared. Therefore, Exim now forces the entire address into lower case here, @@ -1289,8 +1290,10 @@ 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. */ -sprintf(CS big_buffer, "%.*s", big_buffer_size - 1, address); -for (p = big_buffer + Ustrlen(big_buffer) - 1; p >= big_buffer; p--) +if ((len = Ustrlen(address)) > 255) len = 255; +ab.address = string_copyn(address, len); + +for (uschar * p = ab.address + len - 1; p >= ab.address; p--) { if (!caseless && *p == '@') break; *p = tolower(*p); @@ -1311,7 +1314,7 @@ if (expand_setup == 0) /* Set up the data to be passed ultimately to check_address. */ ab.origaddress = address; -ab.address = big_buffer; +/* ab.address is above */ ab.expand_setup = expand_setup; ab.caseless = caseless;