Commit | Line | Data |
---|---|---|
0a49a7a4 | 1 | /* $Cambridge: exim/src/src/search.c,v 1.7 2009/11/16 19:50:37 nm4 Exp $ */ |
059ec3d9 PH |
2 | |
3 | /************************************************* | |
4 | * Exim - an Internet mail transport agent * | |
5 | *************************************************/ | |
6 | ||
0a49a7a4 | 7 | /* Copyright (c) University of Cambridge 1995 - 2009 */ |
059ec3d9 PH |
8 | /* See the file NOTICE for conditions of use and distribution. */ |
9 | ||
10 | /* A set of functions to search databases in various formats. An open | |
11 | database is represented by a void * value which is returned from a lookup- | |
12 | specific "open" function. These are now all held in individual modules in the | |
13 | lookups subdirectory and the functions here form a generic interface. | |
14 | ||
15 | Caching is used to improve performance. Open files are cached until a tidyup | |
16 | function is called, and for each file the result of the last lookup is cached. | |
17 | However, if too many files are opened, some of those that are not in use have | |
18 | to be closed. Those open items that use real files are kept on a LRU chain to | |
19 | help with this. | |
20 | ||
21 | All the data is held in permanent store so as to be independent of the stacking | |
22 | pool that is reset from time to time. In fact, we use malloc'd store so that it | |
23 | can be freed when the caches are tidied up. It isn't actually clear whether | |
24 | this is a benefit or not, to be honest. */ | |
25 | ||
26 | #include "exim.h" | |
27 | ||
28 | ||
29 | /* Tree in which to cache open files until tidyup called. */ | |
30 | ||
31 | static tree_node *search_tree = NULL; | |
32 | ||
33 | /* Two-way chain of open databases that use real files. This is maintained in | |
34 | recently-used order for the purposes of closing the least recently used when | |
35 | too many files are open. */ | |
36 | ||
37 | static tree_node *open_top = NULL; | |
38 | static tree_node *open_bot = NULL; | |
39 | ||
40 | /* Count of open databases that use real files */ | |
41 | ||
42 | static int open_filecount = 0; | |
43 | ||
44 | /* Allow us to reset store used for lookups and lookup caching */ | |
45 | ||
46 | static void *search_reset_point = NULL; | |
47 | ||
48 | ||
49 | ||
50 | /************************************************* | |
51 | * Validate a plain lookup type name * | |
52 | *************************************************/ | |
53 | ||
54 | /* Only those names that are recognized and whose code is included in the | |
55 | binary give an OK response. Use a binary chop search now that the list has got | |
56 | so long. | |
57 | ||
58 | Arguments: | |
59 | name lookup type name - not necessarily zero terminated (e.g. dbm*) | |
60 | len length of the name | |
61 | ||
62 | Returns: +ve => valid lookup name; value is offset in lookup_list | |
63 | -ve => invalid name; message in search_error_message. | |
64 | */ | |
65 | ||
66 | int | |
67 | search_findtype(uschar *name, int len) | |
68 | { | |
69 | int bot = 0; | |
70 | int top = lookup_list_count; | |
71 | while (top > bot) | |
72 | { | |
73 | int mid = (top + bot)/2; | |
e6d225ae | 74 | int c = Ustrncmp(name, lookup_list[mid]->name, len); |
059ec3d9 PH |
75 | |
76 | /* If c == 0 we have matched the incoming name with the start of the search | |
77 | type name. However, some search types are substrings of others (e.g. nis and | |
78 | nisplus) so we need to check that the lengths are the same. The length of the | |
79 | type name cannot be shorter (else c would not be 0); if it is not equal it | |
80 | must be longer, and in that case, the incoming name comes before the name we | |
81 | are testing. By leaving c == 0 when the lengths are different, and doing a | |
82 | > 0 test below, this all falls out correctly. */ | |
83 | ||
e6d225ae | 84 | if (c == 0 && Ustrlen(lookup_list[mid]->name) == len) |
059ec3d9 | 85 | { |
e6d225ae | 86 | if (lookup_list[mid]->find != NULL) return mid; |
059ec3d9 PH |
87 | search_error_message = string_sprintf("lookup type \"%.*s\" is not " |
88 | "available (not in the binary - check buildtime LOOKUP configuration)", | |
89 | len, name); | |
90 | return -1; | |
91 | } | |
92 | ||
93 | if (c > 0) bot = mid + 1; else top = mid; | |
94 | } | |
95 | ||
96 | search_error_message = string_sprintf("unknown lookup type \"%.*s\"",len,name); | |
97 | return -1; | |
98 | } | |
99 | ||
100 | ||
101 | ||
102 | /************************************************* | |
103 | * Validate a full lookup type name * | |
104 | *************************************************/ | |
105 | ||
106 | /* This function recognizes the "partial-" prefix and also terminating * and *@ | |
107 | suffixes. | |
108 | ||
109 | Arguments: | |
110 | name the full lookup type name | |
111 | ptypeptr where to put the partial type | |
112 | after subtraction of 1024 or 2048: | |
113 | negative => no partial matching | |
114 | non-negative => minimum number of non-wild components | |
115 | ptypeaff where to put a pointer to the affix | |
116 | the affix is within name if supplied therein | |
117 | otherwise it's a literal string | |
118 | afflen the length of the affix | |
119 | starflags where to put the SEARCH_STAR and SEARCH_STARAT flags | |
120 | ||
121 | Returns: +ve => valid lookup name; value is offset in lookup_list | |
122 | -ve => invalid name; message in search_error_message. | |
123 | */ | |
124 | ||
125 | int | |
126 | search_findtype_partial(uschar *name, int *ptypeptr, uschar **ptypeaff, | |
127 | int *afflen, int *starflags) | |
128 | { | |
129 | int len, stype; | |
130 | int pv = -1; | |
131 | uschar *ss = name; | |
132 | ||
133 | *starflags = 0; | |
134 | *ptypeaff = NULL; | |
135 | ||
136 | /* Check for a partial matching type. It must start with "partial", optionally | |
137 | followed by a sequence of digits. If this is followed by "-", the affix is the | |
138 | default "*." string. Otherwise we expect an affix in parentheses. Affixes are a | |
139 | limited number of characters, not including parens. */ | |
140 | ||
141 | if (Ustrncmp(name, "partial", 7) == 0) | |
142 | { | |
143 | ss += 7; | |
144 | if (isdigit (*ss)) | |
145 | { | |
146 | pv = 0; | |
147 | while (isdigit(*ss)) pv = pv*10 + *ss++ - '0'; | |
148 | } | |
149 | else pv = 2; /* Default number of wild components */ | |
150 | ||
151 | if (*ss == '(') | |
152 | { | |
153 | *ptypeaff = ++ss; | |
154 | while (ispunct(*ss) && *ss != ')') ss++; | |
155 | if (*ss != ')') goto BAD_TYPE; | |
156 | *afflen = ss++ - *ptypeaff; | |
157 | } | |
158 | else if (*ss++ == '-') | |
159 | { | |
160 | *ptypeaff = US "*."; | |
161 | *afflen = 2; | |
162 | } | |
163 | else | |
164 | { | |
165 | BAD_TYPE: | |
166 | search_error_message = string_sprintf("format error in lookup type \"%s\"", | |
167 | name); | |
168 | return -1; | |
169 | } | |
170 | } | |
171 | ||
172 | /* Now we are left with a lookup name, possibly followed by * or *@. */ | |
173 | ||
174 | len = Ustrlen(ss); | |
175 | if (len >= 2 && Ustrncmp(ss + len - 2, "*@", 2) == 0) | |
176 | { | |
177 | *starflags |= SEARCH_STARAT; | |
178 | len -= 2; | |
179 | } | |
180 | else if (len >= 1 && ss[len-1] == '*') | |
181 | { | |
182 | *starflags |= SEARCH_STAR; | |
183 | len--; | |
184 | } | |
185 | ||
186 | /* Check for the individual search type. Only those that are actually in the | |
d7837193 PH |
187 | binary are valid. For query-style types, "partial" and default types are |
188 | erroneous. */ | |
059ec3d9 PH |
189 | |
190 | stype = search_findtype(ss, len); | |
d7837193 | 191 | if (stype >= 0 && mac_islookup(stype, lookup_querystyle)) |
059ec3d9 | 192 | { |
d7837193 PH |
193 | if (pv >= 0) |
194 | { | |
195 | search_error_message = string_sprintf("\"partial\" is not permitted " | |
196 | "for lookup type \"%s\"", ss); | |
197 | return -1; | |
198 | } | |
199 | if ((*starflags & (SEARCH_STAR|SEARCH_STARAT)) != 0) | |
200 | { | |
201 | search_error_message = string_sprintf("defaults using \"*\" or \"*@\" are " | |
202 | "not permitted for lookup type \"%s\"", ss); | |
203 | return -1; | |
204 | } | |
059ec3d9 PH |
205 | } |
206 | ||
059ec3d9 PH |
207 | *ptypeptr = pv; |
208 | return stype; | |
209 | } | |
210 | ||
211 | ||
212 | ||
213 | /************************************************* | |
214 | * Release cached resources * | |
215 | *************************************************/ | |
216 | ||
217 | /* When search_open is called it caches the "file" that it opens in | |
218 | search_tree. The name of the tree node is a concatenation of the search type | |
219 | with the file name. For query-style lookups, the file name is empty. Real files | |
220 | are normally closed only when this tidyup routine is called, typically at the | |
221 | end of sections of code where a number of lookups might occur. However, if too | |
222 | many files are open simultaneously, some get closed beforehand. They can't be | |
223 | removed from the tree. There is also a general tidyup function which is called | |
224 | for the lookup driver, if it exists. | |
225 | ||
226 | First, there is an internal, recursive subroutine. | |
227 | ||
228 | Argument: a pointer to a search_openfile tree node | |
229 | Returns: nothing | |
230 | */ | |
231 | ||
232 | static void | |
233 | tidyup_subtree(tree_node *t) | |
234 | { | |
235 | search_cache *c = (search_cache *)(t->data.ptr); | |
236 | if (t->left != NULL) tidyup_subtree(t->left); | |
237 | if (t->right != NULL) tidyup_subtree(t->right); | |
238 | if (c != NULL && | |
239 | c->handle != NULL && | |
e6d225ae DW |
240 | lookup_list[c->search_type]->close != NULL) |
241 | lookup_list[c->search_type]->close(c->handle); | |
059ec3d9 PH |
242 | } |
243 | ||
244 | ||
245 | /* The external entry point | |
246 | ||
247 | Argument: none | |
248 | Returns: nothing | |
249 | */ | |
250 | ||
251 | void | |
252 | search_tidyup(void) | |
253 | { | |
254 | int i; | |
255 | int old_pool = store_pool; | |
256 | ||
257 | DEBUG(D_lookup) debug_printf("search_tidyup called\n"); | |
258 | ||
259 | /* Close individually each cached open file. */ | |
260 | ||
261 | store_pool = POOL_SEARCH; | |
262 | if (search_tree != NULL) | |
263 | { | |
264 | tidyup_subtree(search_tree); | |
265 | search_tree = NULL; | |
266 | } | |
267 | open_top = open_bot = NULL; | |
268 | open_filecount = 0; | |
269 | ||
270 | /* Call the general tidyup entry for any drivers that have one. */ | |
271 | ||
272 | for (i = 0; i < lookup_list_count; i++) | |
e6d225ae | 273 | if (lookup_list[i]->tidy != NULL) (lookup_list[i]->tidy)(); |
059ec3d9 PH |
274 | |
275 | if (search_reset_point != NULL) store_reset(search_reset_point); | |
276 | search_reset_point = NULL; | |
277 | store_pool = old_pool; | |
278 | } | |
279 | ||
280 | ||
281 | ||
282 | ||
283 | /************************************************* | |
284 | * Open search database * | |
285 | *************************************************/ | |
286 | ||
287 | /* A mode, and lists of owners and groups, are passed over for checking in | |
288 | the cases where the database is one or more files. Return NULL, with a message | |
289 | pointed to by message, in cases of error. | |
290 | ||
291 | For search types that use a file or files, check up on the mode after | |
292 | opening. It is tempting to do a stat before opening the file, and use it as | |
293 | an existence check. However, doing that opens a small security loophole in | |
294 | that the status could be changed before the file is opened. Can't quite see | |
295 | what problems this might lead to, but you can't be too careful where security | |
296 | is concerned. Fstat() on an open file can normally be expected to succeed, | |
297 | but there are some NFS states where it does not. | |
298 | ||
299 | There are two styles of query: (1) in the "single-key+file" style, a single | |
300 | key string and a file name are given, for example, for linear searches, DBM | |
301 | files, or for NIS. (2) In the "query" style, no "filename" is given; instead | |
302 | just a single query string is passed. This applies to multiple-key lookup | |
303 | types such as NIS+. | |
304 | ||
305 | Before opening, scan the tree of cached files to see if this file is already | |
306 | open for the correct search type. If so, return the saved handle. If not, put | |
307 | the handle in the tree for possible subsequent use. See search_tidyup above for | |
308 | closing all the cached files. | |
309 | ||
310 | A count of open databases which use real files is maintained, and if this | |
311 | gets too large, we have to close a cached file. Its entry remains in the tree, | |
312 | but is marked closed. | |
313 | ||
314 | Arguments: | |
315 | filename the name of the file for single-key+file style lookups, | |
316 | NULL for query-style lookups | |
317 | search_type the type of search required | |
318 | modemask if a real single file is used, this specifies mode bits that | |
319 | must not be set; otherwise it is ignored | |
320 | owners if a real single file is used, this specifies the possible | |
321 | owners of the file; otherwise it is ignored | |
322 | owngroups if a real single file is used, this specifies the possible | |
323 | group owners of the file; otherwise it is ignored | |
324 | ||
325 | Returns: an identifying handle for the open database; | |
326 | this is the pointer to the tree block in the | |
327 | cache of open files; return NULL on open failure, with | |
328 | a message in search_error_message | |
329 | */ | |
330 | ||
331 | void * | |
332 | search_open(uschar *filename, int search_type, int modemask, uid_t *owners, | |
333 | gid_t *owngroups) | |
334 | { | |
335 | void *handle; | |
336 | tree_node *t; | |
337 | search_cache *c; | |
e6d225ae | 338 | lookup_info *lk = lookup_list[search_type]; |
059ec3d9 PH |
339 | uschar keybuffer[256]; |
340 | int old_pool = store_pool; | |
341 | ||
342 | /* Change to the search store pool and remember our reset point */ | |
343 | ||
344 | store_pool = POOL_SEARCH; | |
345 | if (search_reset_point == NULL) search_reset_point = store_get(0); | |
346 | ||
347 | DEBUG(D_lookup) debug_printf("search_open: %s \"%s\"\n", lk->name, | |
348 | (filename == NULL)? US"NULL" : filename); | |
349 | ||
350 | /* See if we already have this open for this type of search, and if so, | |
351 | pass back the tree block as the handle. The key for the tree node is the search | |
352 | type plus '0' concatenated with the file name. There may be entries in the tree | |
353 | with closed files if a lot of files have been opened. */ | |
354 | ||
355 | sprintf(CS keybuffer, "%c%.254s", search_type + '0', | |
356 | (filename == NULL)? US"" : filename); | |
357 | ||
358 | if ((t = tree_search(search_tree, keybuffer)) != NULL) | |
359 | { | |
360 | c = (search_cache *)(t->data.ptr); | |
361 | if (c->handle != NULL) | |
362 | { | |
363 | DEBUG(D_lookup) debug_printf(" cached open\n"); | |
364 | store_pool = old_pool; | |
365 | return t; | |
366 | } | |
367 | DEBUG(D_lookup) debug_printf(" cached closed\n"); | |
368 | } | |
369 | ||
370 | /* Otherwise, we need to open the file or database - each search type has its | |
371 | own code, which is now split off into separately compiled modules. Before doing | |
372 | this, if the search type is one that uses real files, check on the number that | |
373 | we are holding open in the cache. If the limit is reached, close the least | |
374 | recently used one. */ | |
375 | ||
376 | if (lk->type == lookup_absfile && open_filecount >= lookup_open_max) | |
377 | { | |
378 | if (open_bot == NULL) | |
379 | log_write(0, LOG_MAIN|LOG_PANIC, "too many lookups open, but can't find " | |
380 | "one to close"); | |
381 | else | |
382 | { | |
383 | search_cache *c = (search_cache *)(open_bot->data.ptr); | |
384 | DEBUG(D_lookup) debug_printf("Too many lookup files open\n closing %s\n", | |
385 | open_bot->name); | |
386 | open_bot = c->up; | |
387 | if (open_bot != NULL) | |
388 | ((search_cache *)(open_bot->data.ptr))->down = NULL; | |
389 | else | |
390 | open_top = NULL; | |
e6d225ae | 391 | ((lookup_list[c->search_type])->close)(c->handle); |
059ec3d9 PH |
392 | c->handle = NULL; |
393 | open_filecount--; | |
394 | } | |
395 | } | |
396 | ||
397 | /* If opening is successful, call the file-checking function if there is one, | |
398 | and if all is still well, enter the open database into the tree. */ | |
399 | ||
19897d52 | 400 | handle = (lk->open)(filename, &search_error_message); |
059ec3d9 PH |
401 | if (handle == NULL) |
402 | { | |
403 | store_pool = old_pool; | |
404 | return NULL; | |
405 | } | |
406 | ||
407 | if (lk->check != NULL && | |
408 | !lk->check(handle, filename, modemask, owners, owngroups, | |
409 | &search_error_message)) | |
410 | { | |
411 | lk->close(handle); | |
412 | store_pool = old_pool; | |
413 | return NULL; | |
414 | } | |
415 | ||
416 | /* If this is a search type that uses real files, keep count. */ | |
417 | ||
418 | if (lk->type == lookup_absfile) open_filecount++; | |
419 | ||
420 | /* If we found a previously opened entry in the tree, re-use it; otherwise | |
421 | insert a new entry. On re-use, leave any cached lookup data and the lookup | |
422 | count alone. */ | |
423 | ||
424 | if (t == NULL) | |
425 | { | |
426 | t = store_get(sizeof(tree_node) + Ustrlen(keybuffer)); | |
427 | t->data.ptr = c = store_get(sizeof(search_cache)); | |
428 | c->item_cache = NULL; | |
429 | Ustrcpy(t->name, keybuffer); | |
430 | tree_insertnode(&search_tree, t); | |
431 | } | |
432 | else c = t->data.ptr; | |
433 | ||
434 | c->handle = handle; | |
435 | c->search_type = search_type; | |
436 | c->up = c->down = NULL; | |
437 | ||
438 | store_pool = old_pool; | |
439 | return t; | |
440 | } | |
441 | ||
442 | ||
443 | ||
444 | ||
445 | ||
446 | /************************************************* | |
447 | * Internal function: Find one item in database * | |
448 | *************************************************/ | |
449 | ||
450 | /* The answer is always put into dynamic store. The last lookup for each handle | |
451 | is cached. | |
452 | ||
453 | Arguments: | |
454 | handle the handle from search_open; points to tree node | |
455 | filename the filename that was handed to search_open, or | |
456 | NULL for query-style searches | |
457 | keystring the keystring for single-key+file lookups, or | |
458 | the querystring for query-style lookups | |
459 | ||
460 | Returns: a pointer to a dynamic string containing the answer, | |
461 | or NULL if the query failed or was deferred; in the | |
462 | latter case, search_find_defer is set TRUE; after an unusual | |
463 | failure, there may be a message in search_error_message. | |
464 | */ | |
465 | ||
466 | static uschar * | |
467 | internal_search_find(void *handle, uschar *filename, uschar *keystring) | |
468 | { | |
469 | tree_node *t = (tree_node *)handle; | |
470 | search_cache *c = (search_cache *)(t->data.ptr); | |
471 | uschar *data = NULL; | |
472 | int search_type = t->name[0] - '0'; | |
473 | int old_pool = store_pool; | |
474 | ||
475 | /* Lookups that return DEFER may not always set an error message. So that | |
476 | the callers don't have to test for NULL, set an empty string. */ | |
477 | ||
478 | search_error_message = US""; | |
479 | search_find_defer = FALSE; | |
480 | ||
481 | DEBUG(D_lookup) debug_printf("internal_search_find: file=\"%s\"\n " | |
482 | "type=%s key=\"%s\"\n", filename, | |
e6d225ae | 483 | lookup_list[search_type]->name, keystring); |
059ec3d9 PH |
484 | |
485 | /* Insurance. If the keystring is empty, just fail. */ | |
486 | ||
487 | if (keystring[0] == 0) return NULL; | |
488 | ||
489 | /* Use the special store pool for search data */ | |
490 | ||
491 | store_pool = POOL_SEARCH; | |
492 | ||
493 | /* Look up the data for the key, unless it is already in the cache for this | |
494 | file. No need to check c->item_cache for NULL, tree_search will do so. */ | |
495 | ||
496 | if ((t = tree_search(c->item_cache, keystring)) == NULL) | |
497 | { | |
498 | BOOL do_cache = TRUE; | |
499 | int keylength = Ustrlen(keystring); | |
500 | ||
501 | DEBUG(D_lookup) | |
502 | { | |
503 | if (filename != NULL) | |
504 | debug_printf("file lookup required for %s\n in %s\n", | |
505 | keystring, filename); | |
506 | else | |
507 | debug_printf("database lookup required for %s\n", keystring); | |
508 | } | |
509 | ||
510 | /* Call the code for the different kinds of search. DEFER is handled | |
511 | like FAIL, except that search_find_defer is set so the caller can | |
512 | distinguish if necessary. */ | |
513 | ||
e6d225ae | 514 | if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength, |
059ec3d9 PH |
515 | &data, &search_error_message, &do_cache) == DEFER) |
516 | { | |
517 | search_find_defer = TRUE; | |
518 | } | |
519 | ||
520 | /* A record that has been found is now in data, which is either NULL | |
521 | or points to a bit of dynamic store. Cache the result of the lookup if | |
522 | caching is permitted. Lookups can disable caching, when they did something | |
523 | that changes their data. The mysql and pgsql lookups do this when an | |
524 | UPDATE/INSERT query was executed. */ | |
525 | ||
526 | else if (do_cache) | |
527 | { | |
528 | int len = keylength + 1; | |
529 | t = store_get(sizeof(tree_node) + len); | |
530 | memcpy(t->name, keystring, len); | |
531 | t->data.ptr = data; | |
532 | tree_insertnode(&c->item_cache, t); | |
533 | } | |
534 | ||
535 | /* If caching was disabled, empty the cache tree. We just set the cache | |
536 | pointer to NULL here, because we cannot release the store at this stage. */ | |
537 | ||
538 | else | |
539 | { | |
540 | DEBUG(D_lookup) debug_printf("lookup forced cache cleanup\n"); | |
541 | c->item_cache = NULL; | |
542 | } | |
543 | } | |
544 | ||
545 | /* Data was in the cache already; set the pointer from the tree node */ | |
546 | ||
547 | else | |
548 | { | |
549 | data = US t->data.ptr; | |
550 | DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n", | |
551 | keystring, | |
552 | (filename == NULL)? US"" : US"\n in ", | |
553 | (filename == NULL)? US"" : filename); | |
554 | } | |
555 | ||
556 | /* Debug: output the answer */ | |
557 | ||
558 | DEBUG(D_lookup) | |
559 | { | |
560 | if (data == NULL) | |
561 | { | |
562 | if (search_find_defer) debug_printf("lookup deferred: %s\n", | |
563 | search_error_message); | |
564 | else debug_printf("lookup failed\n"); | |
565 | } | |
566 | else debug_printf("lookup yielded: %s\n", data); | |
567 | } | |
568 | ||
569 | /* Return it in new dynamic store in the regular pool */ | |
570 | ||
571 | store_pool = old_pool; | |
572 | return (data == NULL)? NULL : string_copy(data); | |
573 | } | |
574 | ||
575 | ||
576 | ||
577 | ||
578 | /************************************************* | |
579 | * Find one item in database, possibly wildcarded * | |
580 | *************************************************/ | |
581 | ||
582 | /* This function calls the internal function above; once only if there | |
583 | is no partial matching, but repeatedly when partial matching is requested. | |
584 | ||
585 | Arguments: | |
586 | handle the handle from search_open | |
587 | filename the filename that was handed to search_open, or | |
588 | NULL for query-style searches | |
589 | keystring the keystring for single-key+file lookups, or | |
590 | the querystring for query-style lookups | |
591 | partial -1 means no partial matching; | |
592 | otherwise it's the minimum number of components; | |
593 | affix the affix string for partial matching | |
594 | affixlen the length of the affix string | |
595 | starflags SEARCH_STAR and SEARCH_STARAT flags | |
596 | expand_setup pointer to offset for setting up expansion strings; | |
597 | don't do any if < 0 | |
598 | ||
599 | Returns: a pointer to a dynamic string containing the answer, | |
600 | or NULL if the query failed or was deferred; in the | |
601 | latter case, search_find_defer is set TRUE | |
602 | */ | |
603 | ||
604 | uschar * | |
605 | search_find(void *handle, uschar *filename, uschar *keystring, int partial, | |
606 | uschar *affix, int affixlen, int starflags, int *expand_setup) | |
607 | { | |
608 | tree_node *t = (tree_node *)handle; | |
609 | BOOL set_null_wild = FALSE; | |
610 | uschar *yield; | |
611 | ||
612 | DEBUG(D_lookup) | |
613 | { | |
614 | if (partial < 0) affixlen = 99; /* So that "NULL" prints */ | |
615 | debug_printf("search_find: file=\"%s\"\n key=\"%s\" " | |
616 | "partial=%d affix=%.*s starflags=%x\n", | |
617 | (filename == NULL)? US"NULL" : filename, | |
618 | keystring, partial, affixlen, affix, starflags); | |
619 | } | |
620 | ||
621 | /* Arrange to put this database at the top of the LRU chain if it is a type | |
622 | that opens real files. */ | |
623 | ||
624 | if (open_top != (tree_node *)handle && | |
e6d225ae | 625 | lookup_list[t->name[0]-'0']->type == lookup_absfile) |
059ec3d9 PH |
626 | { |
627 | search_cache *c = (search_cache *)(t->data.ptr); | |
628 | tree_node *up = c->up; | |
629 | tree_node *down = c->down; | |
630 | ||
631 | /* Cut it out of the list. A newly opened file will a NULL up pointer. | |
632 | Otherwise there will be a non-NULL up pointer, since we checked above that | |
633 | this block isn't already at the top of the list. */ | |
634 | ||
635 | if (up != NULL) | |
636 | { | |
637 | ((search_cache *)(up->data.ptr))->down = down; | |
638 | if (down != NULL) | |
639 | ((search_cache *)(down->data.ptr))->up = up; | |
640 | else open_bot = up; | |
641 | } | |
642 | ||
643 | /* Now put it at the head of the list. */ | |
644 | ||
645 | c->up = NULL; | |
646 | c->down = open_top; | |
647 | if (open_top == NULL) open_bot = t; else | |
648 | ((search_cache *)(open_top->data.ptr))->up = t; | |
649 | open_top = t; | |
650 | } | |
651 | ||
652 | DEBUG(D_lookup) | |
653 | { | |
654 | tree_node *t = open_top; | |
655 | debug_printf("LRU list:\n"); | |
656 | while (t != NULL) | |
657 | { | |
658 | search_cache *c = (search_cache *)(t->data.ptr); | |
659 | debug_printf(" %s\n", t->name); | |
660 | if (t == open_bot) debug_printf(" End\n"); | |
661 | t = c->down; | |
662 | } | |
663 | } | |
664 | ||
665 | /* First of all, try to match the key string verbatim. If matched a complete | |
666 | entry but could have been partial, flag to set up variables. */ | |
667 | ||
668 | yield = internal_search_find(handle, filename, keystring); | |
669 | if (search_find_defer) return NULL; | |
670 | if (yield != NULL) { if (partial >= 0) set_null_wild = TRUE; } | |
671 | ||
672 | /* Not matched a complete entry; handle partial lookups, but only if the full | |
673 | search didn't defer. Don't use string_sprintf() to construct the initial key, | |
674 | just in case the original key is too long for the string_sprintf() buffer (it | |
675 | *has* happened!). The case of a zero-length affix has to be treated specially. | |
676 | */ | |
677 | ||
678 | else if (partial >= 0) | |
679 | { | |
680 | int len = Ustrlen(keystring); | |
681 | uschar *keystring2; | |
682 | ||
683 | /* Try with the affix on the front, except for a zero-length affix */ | |
684 | ||
685 | if (affixlen == 0) keystring2 = keystring; else | |
686 | { | |
687 | keystring2 = store_get(len + affixlen + 1); | |
688 | Ustrncpy(keystring2, affix, affixlen); | |
689 | Ustrcpy(keystring2 + affixlen, keystring); | |
690 | DEBUG(D_lookup) debug_printf("trying partial match %s\n", keystring2); | |
691 | yield = internal_search_find(handle, filename, keystring2); | |
692 | if (search_find_defer) return NULL; | |
693 | } | |
694 | ||
695 | /* The key in its entirety did not match a wild entry; try chopping off | |
696 | leading components. */ | |
697 | ||
698 | if (yield == NULL) | |
699 | { | |
700 | int dotcount = 0; | |
701 | uschar *keystring3 = keystring2 + affixlen; | |
702 | uschar *s = keystring3; | |
703 | while (*s != 0) if (*s++ == '.') dotcount++; | |
704 | ||
705 | while (dotcount-- >= partial) | |
706 | { | |
707 | while (*keystring3 != 0 && *keystring3 != '.') keystring3++; | |
708 | ||
709 | /* If we get right to the end of the string (which will be the last time | |
710 | through this loop), we've failed if the affix is null. Otherwise do one | |
711 | last lookup for the affix itself, but if it is longer than 1 character, | |
712 | remove the last character if it is ".". */ | |
713 | ||
714 | if (*keystring3 == 0) | |
715 | { | |
716 | if (affixlen < 1) break; | |
717 | if (affixlen > 1 && affix[affixlen-1] == '.') affixlen--; | |
718 | Ustrncpy(keystring2, affix, affixlen); | |
719 | keystring2[affixlen] = 0; | |
720 | keystring3 = keystring2; | |
721 | } | |
722 | else | |
723 | { | |
724 | keystring3 -= affixlen - 1; | |
725 | if (affixlen > 0) Ustrncpy(keystring3, affix, affixlen); | |
726 | } | |
727 | ||
728 | DEBUG(D_lookup) debug_printf("trying partial match %s\n", keystring3); | |
729 | yield = internal_search_find(handle, filename, keystring3); | |
730 | if (search_find_defer) return NULL; | |
731 | if (yield != NULL) | |
732 | { | |
733 | /* First variable is the wild part; second is the fixed part. Take care | |
734 | to get it right when keystring3 is just "*". */ | |
735 | ||
736 | if (expand_setup != NULL && *expand_setup >= 0) | |
737 | { | |
738 | int fixedlength = Ustrlen(keystring3) - affixlen; | |
739 | int wildlength = Ustrlen(keystring) - fixedlength - 1; | |
740 | *expand_setup += 1; | |
741 | expand_nstring[*expand_setup] = keystring; | |
742 | expand_nlength[*expand_setup] = wildlength; | |
743 | *expand_setup += 1; | |
744 | expand_nstring[*expand_setup] = keystring + wildlength + 1; | |
745 | expand_nlength[*expand_setup] = (fixedlength < 0)? 0 : fixedlength; | |
746 | } | |
747 | break; | |
748 | } | |
749 | keystring3 += affixlen; | |
750 | } | |
751 | } | |
752 | ||
753 | else set_null_wild = TRUE; /* Matched a wild entry without any wild part */ | |
754 | } | |
755 | ||
756 | /* If nothing has been matched, but the option to look for "*@" is set, try | |
757 | replacing everthing to the left of @ by *. After a match, the wild part | |
758 | is set to the string to the left of the @. */ | |
759 | ||
760 | if (yield == NULL && (starflags & SEARCH_STARAT) != 0) | |
761 | { | |
762 | uschar *atat = Ustrrchr(keystring, '@'); | |
763 | if (atat != NULL && atat > keystring) | |
764 | { | |
765 | int savechar; | |
766 | savechar = *(--atat); | |
767 | *atat = '*'; | |
768 | ||
769 | DEBUG(D_lookup) debug_printf("trying default match %s\n", atat); | |
770 | yield = internal_search_find(handle, filename, atat); | |
771 | *atat = savechar; | |
772 | if (search_find_defer) return NULL; | |
773 | ||
774 | if (yield != NULL && expand_setup != NULL && *expand_setup >= 0) | |
775 | { | |
776 | *expand_setup += 1; | |
777 | expand_nstring[*expand_setup] = keystring; | |
778 | expand_nlength[*expand_setup] = atat - keystring + 1; | |
779 | *expand_setup += 1; | |
780 | expand_nstring[*expand_setup] = keystring; | |
781 | expand_nlength[*expand_setup] = 0; | |
782 | } | |
783 | } | |
784 | } | |
785 | ||
786 | /* If we still haven't matched anything, and the option to look for "*" is set, | |
787 | try that. If we do match, the first variable (the wild part) is the whole key, | |
788 | and the second is empty. */ | |
789 | ||
790 | if (yield == NULL && (starflags & (SEARCH_STAR|SEARCH_STARAT)) != 0) | |
791 | { | |
792 | DEBUG(D_lookup) debug_printf("trying to match *\n"); | |
793 | yield = internal_search_find(handle, filename, US"*"); | |
794 | if (yield != NULL && expand_setup != NULL && *expand_setup >= 0) | |
795 | { | |
796 | *expand_setup += 1; | |
797 | expand_nstring[*expand_setup] = keystring; | |
798 | expand_nlength[*expand_setup] = Ustrlen(keystring); | |
799 | *expand_setup += 1; | |
800 | expand_nstring[*expand_setup] = keystring; | |
801 | expand_nlength[*expand_setup] = 0; | |
802 | } | |
803 | } | |
804 | ||
805 | /* If this was a potentially partial lookup, and we matched either a | |
806 | complete non-wild domain entry, or we matched a wild-carded entry without | |
807 | chopping off any of the domain components, set up the expansion variables | |
808 | (if required) so that the first one is empty, and the second one is the | |
809 | fixed part of the domain. The set_null_wild flag is set only when yield is not | |
810 | NULL. */ | |
811 | ||
812 | if (set_null_wild && expand_setup != NULL && *expand_setup >= 0) | |
813 | { | |
814 | *expand_setup += 1; | |
815 | expand_nstring[*expand_setup] = keystring; | |
816 | expand_nlength[*expand_setup] = 0; | |
817 | *expand_setup += 1; | |
818 | expand_nstring[*expand_setup] = keystring; | |
819 | expand_nlength[*expand_setup] = Ustrlen(keystring); | |
820 | } | |
821 | ||
822 | return yield; | |
823 | } | |
824 | ||
825 | /* End of search.c */ |