X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d447dbd160a0fb503ed1e763f3f23d28744b6ddd..0006e6d8e151bb4b34452da30da60184736bb5ae:/src/src/search.c diff --git a/src/src/search.c b/src/src/search.c index d67bdc59e..94a58897f 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2015 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* A set of functions to search databases in various formats. An open @@ -115,6 +116,7 @@ Arguments: otherwise it's a literal string afflen the length of the affix starflags where to put the SEARCH_STAR and SEARCH_STARAT flags + opts where to put the options Returns: +ve => valid lookup name; value is offset in lookup_list -ve => invalid name; message in search_error_message. @@ -122,11 +124,12 @@ Returns: +ve => valid lookup name; value is offset in lookup_list int search_findtype_partial(const uschar *name, int *ptypeptr, const uschar **ptypeaff, - int *afflen, int *starflags) + int *afflen, int *starflags, const uschar ** opts) { int len, stype; int pv = -1; const uschar *ss = name; +const uschar * t; *starflags = 0; *ptypeaff = NULL; @@ -167,19 +170,26 @@ if (Ustrncmp(name, "partial", 7) == 0) } } -/* Now we are left with a lookup name, possibly followed by * or *@. */ +/* Now we are left with a lookup name, possibly followed by * or *@, +and then by options starting with a "," */ len = Ustrlen(ss); -if (len >= 2 && Ustrncmp(ss + len - 2, "*@", 2) == 0) +if ((t = Ustrchr(ss, '*'))) { - *starflags |= SEARCH_STARAT; - len -= 2; + len = t - ss; + *starflags |= (t[1] == '@' ? SEARCH_STARAT : SEARCH_STAR); } -else if (len >= 1 && ss[len-1] == '*') +else + t = ss; + +if ((t = Ustrchr(t, ','))) { - *starflags |= SEARCH_STAR; - len--; + int l = t - ss; + if (l < len) len = l; + *opts = string_copy(t+1); } +else + *opts = NULL; /* Check for the individual search type. Only those that are actually in the binary are valid. For query-style types, "partial" and default types are @@ -230,12 +240,10 @@ Returns: nothing static void tidyup_subtree(tree_node *t) { -search_cache *c = (search_cache *)(t->data.ptr); -if (t->left != NULL) tidyup_subtree(t->left); -if (t->right != NULL) tidyup_subtree(t->right); -if (c != NULL && - c->handle != NULL && - lookup_list[c->search_type]->close != NULL) +search_cache * c = (search_cache *)(t->data.ptr); +if (t->left) tidyup_subtree(t->left); +if (t->right) tidyup_subtree(t->right); +if (c && c->handle && lookup_list[c->search_type]->close) lookup_list[c->search_type]->close(c->handle); } @@ -454,6 +462,7 @@ Arguments: NULL for query-style searches keystring the keystring for single-key+file lookups, or the querystring for query-style lookups + opts type-specific options Returns: a pointer to a dynamic string containing the answer, or NULL if the query failed or was deferred; in the @@ -462,7 +471,8 @@ Returns: a pointer to a dynamic string containing the answer, */ static uschar * -internal_search_find(void * handle, const uschar * filename, uschar * keystring) +internal_search_find(void * handle, const uschar * filename, uschar * keystring, + const uschar * opts) { tree_node * t = (tree_node *)handle; search_cache * c = (search_cache *)(t->data.ptr); @@ -478,8 +488,9 @@ search_error_message = US""; f.search_find_defer = FALSE; DEBUG(D_lookup) debug_printf_indent("internal_search_find: file=\"%s\"\n " - "type=%s key=\"%s\"\n", filename, - lookup_list[search_type]->name, keystring); + "type=%s key=\"%s\" opts=%s%s%s\n", filename, + lookup_list[search_type]->name, keystring, + opts ? "\"" : "", opts, opts ? "\"" : ""); /* Insurance. If the keystring is empty, just fail. */ @@ -494,6 +505,7 @@ file. No need to check c->item_cache for NULL, tree_search will do so. */ if ( (t = tree_search(c->item_cache, keystring)) && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL)) + && (!opts && !e->opts || opts && e->opts && Ustrcmp(opts, e->opts) == 0) ) { /* Data was in the cache already; set the pointer from the tree node */ data = e->data.ptr; @@ -508,7 +520,9 @@ else DEBUG(D_lookup) { - if (t) debug_printf_indent("cached data found but past valid time; "); + if (t) + debug_printf_indent("cached data found but %s; ", + e->expiry && e->expiry <= time(NULL) ? "out-of-date" : "wrong opts"); debug_printf_indent("%s lookup required for %s%s%s\n", filename ? US"file" : US"database", keystring, @@ -520,7 +534,7 @@ else distinguish if necessary. */ if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength, - &data, &search_error_message, &do_cache) == DEFER) + &data, &search_error_message, &do_cache, opts) == DEFER) f.search_find_defer = TRUE; /* A record that has been found is now in data, which is either NULL @@ -531,23 +545,20 @@ else else if (do_cache) { - int len = keylength + 1; - - if (t) /* Previous, out-of-date cache entry. Update with the */ - { /* new result and forget the old one */ - e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; - e->data.ptr = data; - } - else + if (!t) /* No existing entry. Create new one. */ { + int len = keylength + 1; e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring)); - e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; - e->data.ptr = data; t = (tree_node *)(e+1); memcpy(t->name, keystring, len); t->data.ptr = e; tree_insertnode(&c->item_cache, t); } + /* Else previous, out-of-date cache entry. Update with the */ + /* new result and forget the old one */ + e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; + e->opts = opts ? string_copy(opts) : NULL; + e->data.ptr = data; } /* If caching was disabled, empty the cache tree. We just set the cache @@ -556,7 +567,7 @@ else else { DEBUG(D_lookup) debug_printf_indent("lookup forced cache cleanup\n"); - c->item_cache = NULL; + c->item_cache = NULL; /* forget all lookups on this connection */ } } @@ -598,6 +609,7 @@ Arguments: starflags SEARCH_STAR and SEARCH_STARAT flags expand_setup pointer to offset for setting up expansion strings; don't do any if < 0 + opts type-specific options Returns: a pointer to a dynamic string containing the answer, or NULL if the query failed or was deferred; in the @@ -607,7 +619,7 @@ Returns: a pointer to a dynamic string containing the answer, uschar * search_find(void * handle, const uschar * filename, uschar * keystring, int partial, const uschar * affix, int affixlen, int starflags, - int * expand_setup) + int * expand_setup, const uschar * opts) { tree_node *t = (tree_node *)handle; BOOL set_null_wild = FALSE; @@ -617,9 +629,11 @@ DEBUG(D_lookup) { if (partial < 0) affixlen = 99; /* So that "NULL" prints */ debug_printf_indent("search_find: file=\"%s\"\n key=\"%s\" " - "partial=%d affix=%.*s starflags=%x\n", - (filename == NULL)? US"NULL" : filename, - keystring, partial, affixlen, affix, starflags); + "partial=%d affix=%.*s starflags=%x opts=%s%s%s\n", + filename ? filename : US"NULL", + keystring, partial, affixlen, affix, starflags, + opts ? "\"" : "", opts, opts ? "\"" : ""); + } /* Arrange to put this database at the top of the LRU chain if it is a type @@ -669,7 +683,7 @@ DEBUG(D_lookup) /* First of all, try to match the key string verbatim. If matched a complete entry but could have been partial, flag to set up variables. */ -yield = internal_search_find(handle, filename, keystring); +yield = internal_search_find(handle, filename, keystring, opts); if (f.search_find_defer) return NULL; if (yield) { if (partial >= 0) set_null_wild = TRUE; } @@ -694,14 +708,14 @@ else if (partial >= 0) Ustrncpy(keystring2, affix, affixlen); Ustrcpy(keystring2 + affixlen, keystring); DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2); - yield = internal_search_find(handle, filename, keystring2); + yield = internal_search_find(handle, filename, keystring2, opts); if (f.search_find_defer) return NULL; } /* The key in its entirety did not match a wild entry; try chopping off leading components. */ - if (yield == NULL) + if (!yield) { int dotcount = 0; uschar *keystring3 = keystring2 + affixlen; @@ -732,7 +746,7 @@ else if (partial >= 0) } DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring3); - yield = internal_search_find(handle, filename, keystring3); + yield = internal_search_find(handle, filename, keystring3, opts); if (f.search_find_defer) return NULL; if (yield) { @@ -773,7 +787,7 @@ if (!yield && starflags & SEARCH_STARAT) *atat = '*'; DEBUG(D_lookup) debug_printf_indent("trying default match %s\n", atat); - yield = internal_search_find(handle, filename, atat); + yield = internal_search_find(handle, filename, atat, opts); *atat = savechar; if (f.search_find_defer) return NULL; @@ -796,7 +810,7 @@ and the second is empty. */ if (!yield && starflags & (SEARCH_STAR|SEARCH_STARAT)) { DEBUG(D_lookup) debug_printf_indent("trying to match *\n"); - yield = internal_search_find(handle, filename, US"*"); + yield = internal_search_find(handle, filename, US"*", opts); if (yield && expand_setup && *expand_setup >= 0) { *expand_setup += 1; @@ -825,6 +839,19 @@ if (set_null_wild && expand_setup && *expand_setup >= 0) expand_nlength[*expand_setup] = Ustrlen(keystring); } +/* If we have a result, check the options to see if the key was wanted rather +than the result. Return a de-tainted version of the key on the grounds that +it have been validated by the lookup. */ + +if (yield && opts) + { + int sep = ','; + uschar * ele; + while ((ele = string_nextinlist(&opts, &sep, NULL, 0))) + if (Ustrcmp(ele, "ret=key") == 0) + { yield = string_copy_taint(keystring, FALSE); break; } + } + return yield; }