X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/059ec3d9952740285fb1ebf47961b8aca2eb1b4a..c14470c30aa0d41ef018c35043616c703b01f80a:/src/src/search.c diff --git a/src/src/search.c b/src/src/search.c index 3dfd80e32..ccad25021 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/search.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2004 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* A set of functions to search databases in various formats. An open @@ -64,14 +62,14 @@ Returns: +ve => valid lookup name; value is offset in lookup_list */ int -search_findtype(uschar *name, int len) +search_findtype(const uschar *name, int len) { int bot = 0; int top = lookup_list_count; while (top > bot) { int mid = (top + bot)/2; - int c = Ustrncmp(name, lookup_list[mid].name, len); + int c = Ustrncmp(name, lookup_list[mid]->name, len); /* If c == 0 we have matched the incoming name with the start of the search type name. However, some search types are substrings of others (e.g. nis and @@ -81,9 +79,9 @@ while (top > bot) are testing. By leaving c == 0 when the lengths are different, and doing a > 0 test below, this all falls out correctly. */ - if (c == 0 && Ustrlen(lookup_list[mid].name) == len) + if (c == 0 && Ustrlen(lookup_list[mid]->name) == len) { - if (lookup_list[mid].find != NULL) return mid; + if (lookup_list[mid]->find != NULL) return mid; search_error_message = string_sprintf("lookup type \"%.*s\" is not " "available (not in the binary - check buildtime LOOKUP configuration)", len, name); @@ -123,12 +121,12 @@ Returns: +ve => valid lookup name; value is offset in lookup_list */ int -search_findtype_partial(uschar *name, int *ptypeptr, uschar **ptypeaff, +search_findtype_partial(const uschar *name, int *ptypeptr, const uschar **ptypeaff, int *afflen, int *starflags) { int len, stype; int pv = -1; -uschar *ss = name; +const uschar *ss = name; *starflags = 0; *ptypeaff = NULL; @@ -184,18 +182,26 @@ else if (len >= 1 && ss[len-1] == '*') } /* Check for the individual search type. Only those that are actually in the -binary are valid. For query-style types, "partial" is an error. */ +binary are valid. For query-style types, "partial" and default types are +erroneous. */ stype = search_findtype(ss, len); -if (pv >= 0 && mac_islookup(stype, lookup_querystyle)) +if (stype >= 0 && mac_islookup(stype, lookup_querystyle)) { - search_error_message = string_sprintf("\"partial\" is not permitted " - "for lookup type \"%s\"", ss); - return -1; + if (pv >= 0) + { + search_error_message = string_sprintf("\"partial\" is not permitted " + "for lookup type \"%s\"", ss); + return -1; + } + if ((*starflags & (SEARCH_STAR|SEARCH_STARAT)) != 0) + { + search_error_message = string_sprintf("defaults using \"*\" or \"*@\" are " + "not permitted for lookup type \"%s\"", ss); + return -1; + } } -/* All is well; pass back the partial type and return the lookup type. */ - *ptypeptr = pv; return stype; } @@ -229,8 +235,8 @@ 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) - lookup_list[c->search_type].close(c->handle); + lookup_list[c->search_type]->close != NULL) + lookup_list[c->search_type]->close(c->handle); } @@ -262,7 +268,7 @@ open_filecount = 0; /* Call the general tidyup entry for any drivers that have one. */ for (i = 0; i < lookup_list_count; i++) - if (lookup_list[i].tidy != NULL) (lookup_list[i].tidy)(); + if (lookup_list[i]->tidy != NULL) (lookup_list[i]->tidy)(); if (search_reset_point != NULL) store_reset(search_reset_point); search_reset_point = NULL; @@ -327,7 +333,7 @@ search_open(uschar *filename, int search_type, int modemask, uid_t *owners, void *handle; tree_node *t; search_cache *c; -lookup_info *lk = lookup_list + search_type; +lookup_info *lk = lookup_list[search_type]; uschar keybuffer[256]; int old_pool = store_pool; @@ -380,7 +386,7 @@ if (lk->type == lookup_absfile && open_filecount >= lookup_open_max) ((search_cache *)(open_bot->data.ptr))->down = NULL; else open_top = NULL; - ((lookup_list + c->search_type)->close)(c->handle); + ((lookup_list[c->search_type])->close)(c->handle); c->handle = NULL; open_filecount--; } @@ -389,7 +395,7 @@ if (lk->type == lookup_absfile && open_filecount >= lookup_open_max) /* If opening is successful, call the file-checking function if there is one, and if all is still well, enter the open database into the tree. */ -handle = lk->open(filename, &search_error_message); +handle = (lk->open)(filename, &search_error_message); if (handle == NULL) { store_pool = old_pool; @@ -460,6 +466,7 @@ internal_search_find(void *handle, uschar *filename, uschar *keystring) { tree_node *t = (tree_node *)handle; search_cache *c = (search_cache *)(t->data.ptr); +expiring_data *e; uschar *data = NULL; int search_type = t->name[0] - '0'; int old_pool = store_pool; @@ -472,7 +479,7 @@ search_find_defer = FALSE; DEBUG(D_lookup) debug_printf("internal_search_find: file=\"%s\"\n " "type=%s key=\"%s\"\n", filename, - lookup_list[search_type].name, keystring); + lookup_list[search_type]->name, keystring); /* Insurance. If the keystring is empty, just fail. */ @@ -485,29 +492,36 @@ store_pool = POOL_SEARCH; /* Look up the data for the key, unless it is already in the cache for this file. No need to check c->item_cache for NULL, tree_search will do so. */ -if ((t = tree_search(c->item_cache, keystring)) == NULL) +if ( (t = tree_search(c->item_cache, keystring)) + && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL)) + ) + { /* Data was in the cache already; set the pointer from the tree node */ + data = e->ptr; + DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n", + keystring, + filename ? US"\n in " : US"", filename ? filename : US""); + } +else { - BOOL do_cache = TRUE; + uint do_cache = UINT_MAX; int keylength = Ustrlen(keystring); DEBUG(D_lookup) { - if (filename != NULL) - debug_printf("file lookup required for %s\n in %s\n", - keystring, filename); - else - debug_printf("database lookup required for %s\n", keystring); + if (t) debug_printf("cached data found but past valid time; "); + debug_printf("%s lookup required for %s%s%s\n", + filename ? US"file" : US"database", + keystring, + filename ? US"\n in " : US"", filename ? filename : US""); } /* Call the code for the different kinds of search. DEFER is handled like FAIL, except that search_find_defer is set so the caller can distinguish if necessary. */ - if (lookup_list[search_type].find(c->handle, filename, keystring, keylength, + if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength, &data, &search_error_message, &do_cache) == DEFER) - { search_find_defer = TRUE; - } /* A record that has been found is now in data, which is either NULL or points to a bit of dynamic store. Cache the result of the lookup if @@ -518,10 +532,22 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL) else if (do_cache) { int len = keylength + 1; - t = store_get(sizeof(tree_node) + len); - memcpy(t->name, keystring, len); - t->data.ptr = data; - tree_insertnode(&c->item_cache, t); + + 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->ptr = data; + } + else + { + e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len); + e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; + e->ptr = data; + t = (tree_node *)(e+1); + memcpy(t->name, keystring, len); + t->data.ptr = e; + tree_insertnode(&c->item_cache, t); + } } /* If caching was disabled, empty the cache tree. We just set the cache @@ -534,34 +560,19 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL) } } -/* Data was in the cache already; set the pointer from the tree node */ - -else - { - data = US t->data.ptr; - DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n", - keystring, - (filename == NULL)? US"" : US"\n in ", - (filename == NULL)? US"" : filename); - } - -/* Debug: output the answer */ - DEBUG(D_lookup) { - if (data == NULL) - { - if (search_find_defer) debug_printf("lookup deferred: %s\n", - search_error_message); - else debug_printf("lookup failed\n"); - } - else debug_printf("lookup yielded: %s\n", data); + if (data) + debug_printf("lookup yielded: %s\n", data); + else if (search_find_defer) + debug_printf("lookup deferred: %s\n", search_error_message); + else debug_printf("lookup failed\n"); } /* Return it in new dynamic store in the regular pool */ store_pool = old_pool; -return (data == NULL)? NULL : string_copy(data); +return data ? string_copy(data) : NULL; } @@ -595,7 +606,7 @@ Returns: a pointer to a dynamic string containing the answer, uschar * search_find(void *handle, uschar *filename, uschar *keystring, int partial, - uschar *affix, int affixlen, int starflags, int *expand_setup) + const uschar *affix, int affixlen, int starflags, int *expand_setup) { tree_node *t = (tree_node *)handle; BOOL set_null_wild = FALSE; @@ -614,7 +625,7 @@ DEBUG(D_lookup) that opens real files. */ if (open_top != (tree_node *)handle && - lookup_list[t->name[0]-'0'].type == lookup_absfile) + lookup_list[t->name[0]-'0']->type == lookup_absfile) { search_cache *c = (search_cache *)(t->data.ptr); tree_node *up = c->up;