Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
f9ba5e22 | 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
1e1ddfac | 6 | /* Copyright (c) The Exim Maintainers 2020 */ |
059ec3d9 PH |
7 | /* See the file NOTICE for conditions of use and distribution. */ |
8 | ||
9 | /* Functions for matching strings */ | |
10 | ||
11 | ||
12 | #include "exim.h" | |
13 | ||
14 | ||
15 | /* Argument block for the check_string() function. This is used for general | |
16 | strings, domains, and local parts. */ | |
17 | ||
18 | typedef struct check_string_block { | |
1dc92d5a JH |
19 | const uschar *origsubject; /* caseful; keep these two first, in */ |
20 | const uschar *subject; /* step with the block below */ | |
059ec3d9 PH |
21 | int expand_setup; |
22 | BOOL use_partial; | |
23 | BOOL caseless; | |
24 | BOOL at_is_special; | |
25 | } check_string_block; | |
26 | ||
27 | ||
28 | /* Argument block for the check_address() function. This is used for whole | |
29 | addresses. */ | |
30 | ||
31 | typedef struct check_address_block { | |
55414b25 | 32 | const uschar *origaddress; /* caseful; keep these two first, in */ |
059ec3d9 PH |
33 | uschar *address; /* step with the block above */ |
34 | int expand_setup; | |
35 | BOOL caseless; | |
36 | } check_address_block; | |
37 | ||
38 | ||
39 | ||
40 | /************************************************* | |
41 | * Generalized string match * | |
42 | *************************************************/ | |
43 | ||
44 | /* This function does a single match of a subject against a pattern, and | |
45 | optionally sets up the numeric variables according to what it matched. It is | |
46 | called from match_isinlist() via match_check_list() when scanning a list, and | |
47 | from match_check_string() when testing just a single item. The subject and | |
48 | options arguments are passed in a check_string_block so as to make it easier to | |
49 | pass them through match_check_list. | |
50 | ||
51 | The possible types of pattern are: | |
52 | ||
53 | . regular expression - starts with ^ | |
54 | . tail match - starts with * | |
55 | . lookup - starts with search type | |
56 | . if at_is_special is set in the argument block: | |
57 | @ matches the primary host name | |
58 | @[] matches a local IP address in brackets | |
59 | @mx_any matches any domain with an MX to the local host | |
60 | @mx_primary matches any domain with a primary MX to the local host | |
61 | @mx_secondary matches any domain with a secondary MX to the local host | |
62 | . literal - anything else | |
63 | ||
64 | Any of the @mx_xxx options can be followed by "/ignore=<list>" where <list> is | |
65 | a list of IP addresses that are to be ignored (typically 127.0.0.1). | |
66 | ||
67 | Arguments: | |
68 | arg check_string_block pointer - see below | |
69 | pattern the pattern to be matched | |
70 | valueptr if not NULL, and a lookup is done, return the result here | |
71 | instead of discarding it; else set it to point to NULL | |
72 | error for error messages (not used in this function; it never | |
73 | returns ERROR) | |
74 | ||
75 | Contents of the argument block: | |
366fc9f0 PH |
76 | origsubject the subject in its original casing |
77 | subject the subject string to be checked, lowercased if caseless | |
059ec3d9 PH |
78 | expand_setup if < 0, don't set up any numeric expansion variables; |
79 | if = 0, set $0 to whole subject, and either | |
80 | $1 to what matches * or | |
81 | $1, $2, ... to r.e. bracketed items | |
82 | if > 0, don't set $0, but do set either | |
83 | $n to what matches *, or | |
84 | $n, $n+1, ... to r.e. bracketed items | |
85 | (where n = expand_setup) | |
86 | use_partial if FALSE, override any partial- search types | |
87 | caseless TRUE for caseless matching where possible | |
88 | at_is_special enable special handling of items starting with @ | |
89 | ||
90 | Returns: OK if matched | |
91 | FAIL if not matched | |
92 | DEFER if lookup deferred | |
93 | */ | |
94 | ||
95 | static int | |
55414b25 | 96 | check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) |
059ec3d9 | 97 | { |
1dc92d5a | 98 | const check_string_block *cb = arg; |
059ec3d9 PH |
99 | int search_type, partial, affixlen, starflags; |
100 | int expand_setup = cb->expand_setup; | |
67a57a5a | 101 | const uschar * affix, * opts; |
366fc9f0 | 102 | uschar *s; |
059ec3d9 PH |
103 | uschar *filename = NULL; |
104 | uschar *keyquery, *result, *semicolon; | |
105 | void *handle; | |
106 | ||
107 | error = error; /* Keep clever compilers from complaining */ | |
108 | ||
43ba45ce | 109 | if (valueptr) *valueptr = NULL; |
059ec3d9 | 110 | |
366fc9f0 PH |
111 | /* For regular expressions, use cb->origsubject rather than cb->subject so that |
112 | it works if the pattern uses (?-i) to turn off case-independence, overriding | |
113 | "caseless". */ | |
114 | ||
1dc92d5a | 115 | s = string_copy(pattern[0] == '^' ? cb->origsubject : cb->subject); |
366fc9f0 | 116 | |
059ec3d9 PH |
117 | /* If required to set up $0, initialize the data but don't turn on by setting |
118 | expand_nmax until the match is assured. */ | |
119 | ||
120 | expand_nmax = -1; | |
121 | if (expand_setup == 0) | |
122 | { | |
824ac956 | 123 | expand_nstring[0] = s; /* $0 (might be) the matched subject in full */ |
059ec3d9 PH |
124 | expand_nlength[0] = Ustrlen(s); |
125 | } | |
126 | else if (expand_setup > 0) expand_setup--; | |
127 | ||
128 | /* Regular expression match: compile, match, and set up $ variables if | |
129 | required. */ | |
130 | ||
131 | if (pattern[0] == '^') | |
132 | { | |
fa0798c0 | 133 | const pcre * re = regex_must_compile(pattern, cb->caseless, FALSE); |
824ac956 JH |
134 | if (expand_setup < 0 |
135 | ? pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) < 0 | |
136 | : !regex_match_and_setup(re, s, 0, expand_setup) | |
137 | ) | |
138 | return FAIL; | |
824ac956 JH |
139 | if (valueptr) *valueptr = pattern; /* "value" gets the RE */ |
140 | return OK; | |
059ec3d9 PH |
141 | } |
142 | ||
143 | /* Tail match */ | |
144 | ||
145 | if (pattern[0] == '*') | |
146 | { | |
059ec3d9 PH |
147 | int slen = Ustrlen(s); |
148 | int patlen; /* Sun compiler doesn't like non-constant initializer */ | |
149 | ||
150 | patlen = Ustrlen(++pattern); | |
151 | if (patlen > slen) return FAIL; | |
fa0798c0 JH |
152 | if (cb->caseless |
153 | ? strncmpic(s + slen - patlen, pattern, patlen) != 0 | |
154 | : Ustrncmp(s + slen - patlen, pattern, patlen) != 0) | |
155 | return FAIL; | |
156 | if (expand_setup >= 0) | |
059ec3d9 | 157 | { |
824ac956 | 158 | expand_nstring[++expand_setup] = s; /* write a $n, the matched subject variable-part */ |
059ec3d9 | 159 | expand_nlength[expand_setup] = slen - patlen; |
43ba45ce | 160 | expand_nmax = expand_setup; /* commit also $0, the matched subject */ |
059ec3d9 | 161 | } |
2a53e246 | 162 | if (valueptr) *valueptr = pattern - 1; /* "value" gets the (original) pattern */ |
fa0798c0 | 163 | return OK; |
059ec3d9 PH |
164 | } |
165 | ||
166 | /* Match a special item starting with @ if so enabled. On its own, "@" matches | |
167 | the primary host name - implement this by changing the pattern. For the other | |
168 | cases we have to do some more work. If we don't recognize a special pattern, | |
169 | just fall through - the match will fail. */ | |
170 | ||
171 | if (cb->at_is_special && pattern[0] == '@') | |
172 | { | |
173 | if (pattern[1] == 0) | |
174 | { | |
175 | pattern = primary_hostname; | |
176 | goto NOT_AT_SPECIAL; /* Handle as exact string match */ | |
177 | } | |
178 | ||
179 | if (Ustrcmp(pattern, "@[]") == 0) | |
180 | { | |
059ec3d9 | 181 | int slen = Ustrlen(s); |
824ac956 | 182 | if (s[0] != '[' && s[slen-1] != ']') return FAIL; /*XXX should this be || ? */ |
d7978c0f | 183 | for (ip_address_item * ip = host_find_interfaces(); ip; ip = ip->next) |
95d1f782 PH |
184 | if (Ustrncmp(ip->address, s+1, slen - 2) == 0 |
185 | && ip->address[slen - 2] == 0) | |
fa0798c0 | 186 | { |
43ba45ce | 187 | if (expand_setup >= 0) expand_nmax = expand_setup; /* commit $0, the IP addr */ |
824ac956 | 188 | if (valueptr) *valueptr = pattern; /* "value" gets the pattern */ |
95d1f782 | 189 | return OK; |
fa0798c0 | 190 | } |
059ec3d9 PH |
191 | return FAIL; |
192 | } | |
193 | ||
194 | if (strncmpic(pattern, US"@mx_", 4) == 0) | |
195 | { | |
196 | int rc; | |
197 | host_item h; | |
198 | BOOL prim = FALSE; | |
199 | BOOL secy = FALSE; | |
200 | BOOL removed = FALSE; | |
55414b25 JH |
201 | const uschar *ss = pattern + 4; |
202 | const uschar *ignore_target_hosts = NULL; | |
059ec3d9 PH |
203 | |
204 | if (strncmpic(ss, US"any", 3) == 0) ss += 3; | |
205 | else if (strncmpic(ss, US"primary", 7) == 0) | |
206 | { | |
207 | ss += 7; | |
208 | prim = TRUE; | |
209 | } | |
210 | else if (strncmpic(ss, US"secondary", 9) == 0) | |
211 | { | |
212 | ss += 9; | |
213 | secy = TRUE; | |
214 | } | |
215 | else goto NOT_AT_SPECIAL; | |
216 | ||
217 | if (strncmpic(ss, US"/ignore=", 8) == 0) ignore_target_hosts = ss + 8; | |
fa0798c0 | 218 | else if (*ss) goto NOT_AT_SPECIAL; |
059ec3d9 PH |
219 | |
220 | h.next = NULL; | |
221 | h.name = s; | |
222 | h.address = NULL; | |
223 | ||
224 | rc = host_find_bydns(&h, | |
225 | ignore_target_hosts, | |
226 | HOST_FIND_BY_MX, /* search only for MX, not SRV or A */ | |
227 | NULL, /* service name not relevant */ | |
228 | NULL, /* srv_fail_domains not relevant */ | |
229 | NULL, /* mx_fail_domains not relevant */ | |
7cd171b7 | 230 | NULL, /* no dnssec request/require XXX ? */ |
059ec3d9 PH |
231 | NULL, /* no feedback FQDN */ |
232 | &removed); /* feedback if local removed */ | |
233 | ||
234 | if (rc == HOST_FIND_AGAIN) | |
235 | { | |
236 | search_error_message = string_sprintf("DNS lookup of \"%s\" deferred", s); | |
237 | return DEFER; | |
238 | } | |
239 | ||
43ba45ce JH |
240 | if ((rc != HOST_FOUND_LOCAL || secy) && (prim || !removed)) |
241 | return FAIL; | |
824ac956 | 242 | |
43ba45ce JH |
243 | if (expand_setup >= 0) expand_nmax = expand_setup; /* commit $0, the matched subject */ |
244 | if (valueptr) *valueptr = pattern; /* "value" gets the patterm */ | |
fa0798c0 | 245 | return OK; |
059ec3d9 PH |
246 | |
247 | /*** The above line used to be the following line, but this is incorrect, | |
248 | because host_find_bydns() may return HOST_NOT_FOUND if it removed some MX | |
249 | hosts, but the remaining ones were non-existent. All we are interested in | |
250 | is whether or not it removed some hosts. | |
251 | ||
252 | return (rc == HOST_FOUND && removed)? OK : FAIL; | |
253 | ***/ | |
254 | } | |
255 | } | |
256 | ||
257 | /* Escape point from code for specials that start with "@" */ | |
258 | ||
259 | NOT_AT_SPECIAL: | |
260 | ||
261 | /* This is an exact string match if there is no semicolon in the pattern. */ | |
262 | ||
263 | if ((semicolon = Ustrchr(pattern, ';')) == NULL) | |
264 | { | |
fa0798c0 JH |
265 | if (cb->caseless ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0) |
266 | return FAIL; | |
824ac956 JH |
267 | if (expand_setup >= 0) expand_nmax = expand_setup; /* Original code! $0 gets the matched subject */ |
268 | if (valueptr) *valueptr = pattern; /* "value" gets the pattern */ | |
fa0798c0 | 269 | return OK; |
059ec3d9 PH |
270 | } |
271 | ||
272 | /* Otherwise we have a lookup item. The lookup type, including partial, etc. is | |
273 | the part of the string preceding the semicolon. */ | |
274 | ||
275 | *semicolon = 0; | |
276 | search_type = search_findtype_partial(pattern, &partial, &affix, &affixlen, | |
67a57a5a | 277 | &starflags, &opts); |
059ec3d9 PH |
278 | *semicolon = ';'; |
279 | if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", | |
280 | search_error_message); | |
281 | ||
282 | /* Partial matching is not appropriate for certain lookups (e.g. when looking | |
283 | up user@domain for sender rejection). There's a flag to disable it. */ | |
284 | ||
285 | if (!cb->use_partial) partial = -1; | |
286 | ||
13b685f9 | 287 | /* Set the parameters for the three different kinds of lookup. */ |
059ec3d9 PH |
288 | |
289 | keyquery = semicolon + 1; | |
137ae145 | 290 | Uskip_whitespace(&keyquery); |
059ec3d9 | 291 | |
13b685f9 PH |
292 | if (mac_islookup(search_type, lookup_absfilequery)) |
293 | { | |
294 | filename = keyquery; | |
137ae145 | 295 | while (*keyquery && !isspace(*keyquery)) keyquery++; |
13b685f9 | 296 | filename = string_copyn(filename, keyquery - filename); |
137ae145 | 297 | Uskip_whitespace(&keyquery); |
13b685f9 PH |
298 | } |
299 | ||
300 | else if (!mac_islookup(search_type, lookup_querystyle)) | |
059ec3d9 PH |
301 | { |
302 | filename = keyquery; | |
303 | keyquery = s; | |
304 | } | |
305 | ||
306 | /* Now do the actual lookup; throw away the data returned unless it was asked | |
307 | for; partial matching is all handled inside search_find(). Note that there is | |
308 | no search_close() because of the caching arrangements. */ | |
309 | ||
8768d548 JH |
310 | if (!(handle = search_open(filename, search_type, 0, NULL, NULL))) |
311 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message); | |
059ec3d9 | 312 | result = search_find(handle, filename, keyquery, partial, affix, affixlen, |
67a57a5a | 313 | starflags, &expand_setup, opts); |
059ec3d9 | 314 | |
137ae145 | 315 | if (!result) return f.search_find_defer ? DEFER : FAIL; |
8768d548 | 316 | if (valueptr) *valueptr = result; |
059ec3d9 PH |
317 | |
318 | expand_nmax = expand_setup; | |
319 | return OK; | |
320 | } | |
321 | ||
322 | ||
323 | ||
324 | /************************************************* | |
325 | * Public interface to check_string() * | |
326 | *************************************************/ | |
327 | ||
328 | /* This function is called from several places where is it most convenient to | |
329 | pass the arguments individually. It places them in a check_string_block | |
330 | structure, and then calls check_string(). | |
331 | ||
332 | Arguments: | |
333 | s the subject string to be checked | |
334 | pattern the pattern to check it against | |
335 | expand_setup expansion setup option (see check_string()) | |
336 | use_partial if FALSE, override any partial- search types | |
337 | caseless TRUE for caseless matching where possible | |
338 | at_is_special TRUE to recognize @, @[], etc. | |
339 | valueptr if not NULL, and a file lookup was done, return the result | |
340 | here instead of discarding it; else set it to point to NULL | |
341 | ||
342 | Returns: OK if matched | |
343 | FAIL if not matched | |
344 | DEFER if lookup deferred | |
345 | */ | |
346 | ||
347 | int | |
55414b25 JH |
348 | match_check_string(const uschar *s, const uschar *pattern, int expand_setup, |
349 | BOOL use_partial, BOOL caseless, BOOL at_is_special, const uschar **valueptr) | |
059ec3d9 PH |
350 | { |
351 | check_string_block cb; | |
352 | cb.origsubject = s; | |
43ba45ce | 353 | cb.subject = caseless ? string_copylc(s) : string_copy(s); |
059ec3d9 PH |
354 | cb.expand_setup = expand_setup; |
355 | cb.use_partial = use_partial; | |
356 | cb.caseless = caseless; | |
357 | cb.at_is_special = at_is_special; | |
358 | return check_string(&cb, pattern, valueptr, NULL); | |
359 | } | |
360 | ||
361 | ||
362 | ||
363 | /************************************************* | |
364 | * Get key string from check block * | |
365 | *************************************************/ | |
366 | ||
367 | /* When caching the data from a lookup for a named list, we have to save the | |
368 | key that was found, because other lookups of different keys on the same list | |
369 | may occur. This function has knowledge of the different lookup types, and | |
370 | extracts the appropriate key. | |
371 | ||
372 | Arguments: | |
373 | arg the check block | |
374 | type MCL_STRING, MCL_DOMAIN, MCL_HOST, MCL_ADDRESS, or MCL_LOCALPART | |
375 | */ | |
376 | ||
1dc92d5a | 377 | static const uschar * |
059ec3d9 PH |
378 | get_check_key(void *arg, int type) |
379 | { | |
380 | switch(type) | |
381 | { | |
382 | case MCL_STRING: | |
383 | case MCL_DOMAIN: | |
384 | case MCL_LOCALPART: | |
824ac956 | 385 | return ((check_string_block *)arg)->subject; |
059ec3d9 PH |
386 | |
387 | case MCL_HOST: | |
824ac956 | 388 | return ((check_host_block *)arg)->host_address; |
059ec3d9 PH |
389 | |
390 | case MCL_ADDRESS: | |
824ac956 | 391 | return ((check_address_block *)arg)->address; |
059ec3d9 PH |
392 | } |
393 | return US""; /* In practice, should never happen */ | |
394 | } | |
395 | ||
396 | ||
397 | ||
398 | /************************************************* | |
399 | * Scan list and run matching function * | |
400 | *************************************************/ | |
401 | ||
402 | /* This function scans a list of patterns, and runs a matching function for | |
403 | each item in the list. It is called from the functions that match domains, | |
404 | local parts, hosts, and addresses, because its overall structure is the same in | |
405 | all cases. However, the details of each particular match is different, so it | |
406 | calls back to a given function do perform an actual match. | |
407 | ||
408 | We can't quite keep the different types anonymous here because they permit | |
409 | different special cases. A pity. | |
410 | ||
411 | If a list item starts with !, that implies negation if the subject matches the | |
412 | rest of the item (ignoring white space after the !). The result when the end of | |
413 | the list is reached is FALSE unless the last item on the list is negated, in | |
414 | which case it is TRUE. A file name in the list causes its lines to be | |
415 | interpolated as if items in the list. An item starting with + is a named | |
416 | sublist, obtained by searching the tree pointed to by anchorptr, with possible | |
417 | cached match results in cache_bits. | |
418 | ||
419 | Arguments: | |
420 | listptr pointer to the pointer to the list | |
421 | sep separator character for string_nextinlist(); | |
422 | normally zero for a standard list; | |
423 | sometimes UCHAR_MAX+1 for single items; | |
424 | anchorptr -> tree of named items, or NULL if no named items | |
425 | cache_ptr pointer to pointer to cache bits for named items, or | |
426 | pointer to NULL if not caching; may get set NULL if an | |
427 | uncacheable named list is encountered | |
428 | func function to call back to do one test | |
429 | arg pointer to pass to the function; the string to be matched is | |
430 | in the structure it points to | |
431 | type MCL_STRING, MCL_DOMAIN, MCL_HOST, MCL_ADDRESS, or MCL_LOCALPART | |
432 | these are used for some special handling | |
433 | MCL_NOEXPAND (whose value is greater than any of them) may | |
434 | be added to any value to suppress expansion of the list | |
435 | name string to use in debugging info | |
436 | valueptr where to pass back data from a lookup | |
437 | ||
438 | Returns: OK if matched a non-negated item | |
439 | OK if hit end of list after a negated item | |
440 | FAIL if expansion force-failed | |
441 | FAIL if matched a negated item | |
442 | FAIL if hit end of list after a non-negated item | |
485aa451 | 443 | DEFER if a something deferred or expansion failed |
059ec3d9 PH |
444 | */ |
445 | ||
446 | int | |
55414b25 JH |
447 | match_check_list(const uschar **listptr, int sep, tree_node **anchorptr, |
448 | unsigned int **cache_ptr, int (*func)(void *,const uschar *,const uschar **,uschar **), | |
449 | void *arg, int type, const uschar *name, const uschar **valueptr) | |
059ec3d9 PH |
450 | { |
451 | int yield = OK; | |
452 | unsigned int *original_cache_bits = *cache_ptr; | |
453 | BOOL include_unknown = FALSE; | |
454 | BOOL ignore_unknown = FALSE; | |
d52120f2 PH |
455 | BOOL include_defer = FALSE; |
456 | BOOL ignore_defer = FALSE; | |
55414b25 | 457 | const uschar *list; |
059ec3d9 PH |
458 | uschar *sss; |
459 | uschar *ot = NULL; | |
059ec3d9 PH |
460 | |
461 | /* Save time by not scanning for the option name when we don't need it. */ | |
462 | ||
463 | HDEBUG(D_any) | |
464 | { | |
465 | uschar *listname = readconf_find_option(listptr); | |
466 | if (listname[0] != 0) ot = string_sprintf("%s in %s?", name, listname); | |
467 | } | |
468 | ||
469 | /* If the list is empty, the answer is no. Skip the debugging output for | |
470 | an unnamed list. */ | |
471 | ||
72934ba7 | 472 | if (!*listptr) |
059ec3d9 | 473 | { |
72934ba7 | 474 | HDEBUG(D_lists) if (ot) debug_printf("%s no (option unset)\n", ot); |
059ec3d9 PH |
475 | return FAIL; |
476 | } | |
477 | ||
478 | /* Expand the list before we scan it. A forced expansion gives the answer | |
479 | "not in list"; other expansion errors cause DEFER to be returned. However, | |
480 | if the type value is greater than or equal to than MCL_NOEXPAND, do not expand | |
481 | the list. */ | |
482 | ||
483 | if (type >= MCL_NOEXPAND) | |
484 | { | |
485 | list = *listptr; | |
486 | type -= MCL_NOEXPAND; /* Remove the "no expand" flag */ | |
487 | } | |
488 | else | |
489 | { | |
1bf43b78 PH |
490 | /* If we are searching a domain list, and $domain is not set, set it to the |
491 | subject that is being sought for the duration of the expansion. */ | |
492 | ||
72934ba7 | 493 | if (type == MCL_DOMAIN && !deliver_domain) |
1bf43b78 PH |
494 | { |
495 | check_string_block *cb = (check_string_block *)arg; | |
1dc92d5a | 496 | deliver_domain = string_copy(cb->subject); |
55414b25 | 497 | list = expand_cstring(*listptr); |
1bf43b78 PH |
498 | deliver_domain = NULL; |
499 | } | |
72934ba7 JH |
500 | else |
501 | list = expand_cstring(*listptr); | |
1bf43b78 | 502 | |
72934ba7 | 503 | if (!list) |
059ec3d9 | 504 | { |
8768d548 | 505 | if (f.expand_string_forcedfail) |
059ec3d9 PH |
506 | { |
507 | HDEBUG(D_lists) debug_printf("expansion of \"%s\" forced failure: " | |
508 | "assume not in this list\n", *listptr); | |
509 | return FAIL; | |
510 | } | |
511 | log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand \"%s\" while checking " | |
512 | "a list: %s", *listptr, expand_string_message); | |
513 | return DEFER; | |
514 | } | |
515 | } | |
516 | ||
517 | /* For an unnamed list, use the expanded version in comments */ | |
518 | ||
ba74fb8d | 519 | HDEBUG(D_any) if (!ot) ot = string_sprintf("%s in \"%s\"?", name, list); |
059ec3d9 PH |
520 | |
521 | /* Now scan the list and process each item in turn, until one of them matches, | |
522 | or we hit an error. */ | |
523 | ||
ba74fb8d | 524 | while ((sss = string_nextinlist(&list, &sep, NULL, 0))) |
059ec3d9 | 525 | { |
72934ba7 | 526 | uschar * ss = sss; |
059ec3d9 PH |
527 | |
528 | /* Address lists may contain +caseful, to restore caseful matching of the | |
529 | local part. We have to know the layout of the control block, unfortunately. | |
530 | The lower cased address is in a temporary buffer, so we just copy the local | |
531 | part back to the start of it (if a local part exists). */ | |
532 | ||
533 | if (type == MCL_ADDRESS) | |
534 | { | |
535 | if (Ustrcmp(ss, "+caseful") == 0) | |
536 | { | |
537 | check_address_block *cb = (check_address_block *)arg; | |
538 | uschar *at = Ustrrchr(cb->origaddress, '@'); | |
72934ba7 JH |
539 | |
540 | if (at) | |
059ec3d9 PH |
541 | Ustrncpy(cb->address, cb->origaddress, at - cb->origaddress); |
542 | cb->caseless = FALSE; | |
543 | continue; | |
544 | } | |
545 | } | |
546 | ||
547 | /* Similar processing for local parts */ | |
548 | ||
549 | else if (type == MCL_LOCALPART) | |
550 | { | |
551 | if (Ustrcmp(ss, "+caseful") == 0) | |
552 | { | |
553 | check_string_block *cb = (check_string_block *)arg; | |
f3ebb786 | 554 | Ustrcpy(US cb->subject, cb->origsubject); |
059ec3d9 PH |
555 | cb->caseless = FALSE; |
556 | continue; | |
557 | } | |
558 | } | |
559 | ||
d52120f2 PH |
560 | /* If the host item is "+include_unknown" or "+ignore_unknown", remember it |
561 | in case there's a subsequent failed reverse lookup. There is similar | |
562 | processing for "defer". */ | |
059ec3d9 | 563 | |
d52120f2 | 564 | else if (type == MCL_HOST && *ss == '+') |
059ec3d9 PH |
565 | { |
566 | if (Ustrcmp(ss, "+include_unknown") == 0) | |
567 | { | |
568 | include_unknown = TRUE; | |
569 | ignore_unknown = FALSE; | |
570 | continue; | |
571 | } | |
572 | if (Ustrcmp(ss, "+ignore_unknown") == 0) | |
573 | { | |
574 | ignore_unknown = TRUE; | |
575 | include_unknown = FALSE; | |
576 | continue; | |
577 | } | |
d52120f2 PH |
578 | if (Ustrcmp(ss, "+include_defer") == 0) |
579 | { | |
580 | include_defer = TRUE; | |
581 | ignore_defer = FALSE; | |
582 | continue; | |
583 | } | |
584 | if (Ustrcmp(ss, "+ignore_defer") == 0) | |
585 | { | |
586 | ignore_defer = TRUE; | |
587 | include_defer = FALSE; | |
588 | continue; | |
589 | } | |
059ec3d9 PH |
590 | } |
591 | ||
592 | /* Starting with ! specifies a negative item. It is theoretically possible | |
593 | for a local part to start with !. In that case, a regex has to be used. */ | |
594 | ||
595 | if (*ss == '!') | |
596 | { | |
597 | yield = FAIL; | |
598 | while (isspace((*(++ss)))); | |
599 | } | |
72934ba7 JH |
600 | else |
601 | yield = OK; | |
059ec3d9 PH |
602 | |
603 | /* If the item does not begin with '/', it might be a + item for a named | |
604 | list. Otherwise, it is just a single list entry that has to be matched. | |
605 | We recognize '+' only when supplied with a tree of named lists. */ | |
606 | ||
607 | if (*ss != '/') | |
608 | { | |
72934ba7 | 609 | if (*ss == '+' && anchorptr) |
059ec3d9 PH |
610 | { |
611 | int bits = 0; | |
612 | int offset = 0; | |
613 | int shift = 0; | |
614 | unsigned int *use_cache_bits = original_cache_bits; | |
615 | uschar *cached = US""; | |
616 | namedlist_block *nb; | |
72934ba7 JH |
617 | tree_node * t; |
618 | ||
619 | if (!(t = tree_search(*anchorptr, ss+1))) | |
620 | { | |
621 | log_write(0, LOG_MAIN|LOG_PANIC, "unknown named%s list \"%s\"", | |
622 | type == MCL_DOMAIN ? " domain" : | |
623 | type == MCL_HOST ? " host" : | |
624 | type == MCL_ADDRESS ? " address" : | |
625 | type == MCL_LOCALPART ? " local part" : "", | |
059ec3d9 | 626 | ss); |
72934ba7 JH |
627 | return DEFER; |
628 | } | |
059ec3d9 PH |
629 | nb = t->data.ptr; |
630 | ||
631 | /* If the list number is negative, it means that this list is not | |
632 | cacheable because it contains expansion items. */ | |
633 | ||
634 | if (nb->number < 0) use_cache_bits = NULL; | |
635 | ||
636 | /* If we have got a cache pointer, get the bits. This is not an "else" | |
637 | because the pointer may be NULL from the start if caching is not | |
638 | required. */ | |
639 | ||
72934ba7 | 640 | if (use_cache_bits) |
059ec3d9 PH |
641 | { |
642 | offset = (nb->number)/16; | |
643 | shift = ((nb->number)%16)*2; | |
644 | bits = use_cache_bits[offset] & (3 << shift); | |
645 | } | |
646 | ||
647 | /* Not previously tested or no cache - run the full test */ | |
648 | ||
649 | if (bits == 0) | |
650 | { | |
651 | switch (match_check_list(&(nb->string), 0, anchorptr, &use_cache_bits, | |
652 | func, arg, type, name, valueptr)) | |
653 | { | |
654 | case OK: bits = 1; break; | |
655 | case FAIL: bits = 3; break; | |
656 | case DEFER: goto DEFER_RETURN; | |
657 | } | |
658 | ||
659 | /* If this list was uncacheable, or a sublist turned out to be | |
660 | uncacheable, the value of use_cache_bits will now be NULL, even if it | |
661 | wasn't before. Ensure that this is passed up to the next level. | |
662 | Otherwise, remember the result of the search in the cache. */ | |
663 | ||
72934ba7 | 664 | if (!use_cache_bits) |
059ec3d9 | 665 | *cache_ptr = NULL; |
059ec3d9 PH |
666 | else |
667 | { | |
668 | use_cache_bits[offset] |= bits << shift; | |
669 | ||
72934ba7 | 670 | if (valueptr) |
059ec3d9 PH |
671 | { |
672 | int old_pool = store_pool; | |
673 | namedlist_cacheblock *p; | |
674 | ||
675 | /* Cached data for hosts persists over more than one message, | |
676 | so we use the permanent store pool */ | |
677 | ||
678 | store_pool = POOL_PERM; | |
f3ebb786 | 679 | p = store_get(sizeof(namedlist_cacheblock), FALSE); |
059ec3d9 | 680 | p->key = string_copy(get_check_key(arg, type)); |
60dc5e56 PH |
681 | |
682 | ||
72934ba7 | 683 | p->data = *valueptr ? string_copy(*valueptr) : NULL; |
059ec3d9 PH |
684 | store_pool = old_pool; |
685 | ||
059ec3d9 PH |
686 | p->next = nb->cache_data; |
687 | nb->cache_data = p; | |
72934ba7 | 688 | if (*valueptr) |
059ec3d9 | 689 | DEBUG(D_lists) debug_printf("data from lookup saved for " |
a368cc11 | 690 | "cache for %s: key '%s' value '%s'\n", ss, p->key, *valueptr); |
059ec3d9 PH |
691 | } |
692 | } | |
693 | } | |
694 | ||
695 | /* Previously cached; to find a lookup value, search a chain of values | |
696 | and compare keys. Typically, there is only one such, but it is possible | |
697 | for different keys to have matched the same named list. */ | |
698 | ||
699 | else | |
700 | { | |
701 | DEBUG(D_lists) debug_printf("cached %s match for %s\n", | |
a368cc11 | 702 | (bits & (-bits)) == bits ? "yes" : "no", ss); |
72934ba7 | 703 | |
059ec3d9 | 704 | cached = US" - cached"; |
72934ba7 | 705 | if (valueptr) |
059ec3d9 | 706 | { |
1dc92d5a | 707 | const uschar *key = get_check_key(arg, type); |
d7978c0f JH |
708 | |
709 | for (namedlist_cacheblock * p = nb->cache_data; p; p = p->next) | |
059ec3d9 PH |
710 | if (Ustrcmp(key, p->key) == 0) |
711 | { | |
712 | *valueptr = p->data; | |
713 | break; | |
714 | } | |
059ec3d9 PH |
715 | DEBUG(D_lists) debug_printf("cached lookup data = %s\n", *valueptr); |
716 | } | |
717 | } | |
718 | ||
719 | /* Result of test is indicated by value in bits. For each test, we | |
720 | have 00 => untested, 01 => tested yes, 11 => tested no. */ | |
721 | ||
722 | if ((bits & (-bits)) == bits) /* Only one of the two bits is set */ | |
723 | { | |
724 | HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\"%s)\n", ot, | |
725 | (yield == OK)? "yes" : "no", sss, cached); | |
726 | return yield; | |
727 | } | |
728 | } | |
729 | ||
730 | /* Run the provided function to do the individual test. */ | |
731 | ||
732 | else | |
733 | { | |
72934ba7 | 734 | uschar * error = NULL; |
059ec3d9 PH |
735 | switch ((func)(arg, ss, valueptr, &error)) |
736 | { | |
737 | case OK: | |
72934ba7 JH |
738 | HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\")\n", ot, |
739 | (yield == OK)? "yes" : "no", sss); | |
740 | return yield; | |
059ec3d9 PH |
741 | |
742 | case DEFER: | |
72934ba7 JH |
743 | if (!error) |
744 | error = string_sprintf("DNS lookup of \"%s\" deferred", ss); | |
745 | if (ignore_defer) | |
746 | { | |
747 | HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", | |
748 | error); | |
749 | break; | |
750 | } | |
751 | if (include_defer) | |
752 | { | |
753 | log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); | |
754 | return OK; | |
755 | } | |
756 | if (!search_error_message) search_error_message = error; | |
757 | goto DEFER_RETURN; | |
059ec3d9 | 758 | |
32d668a5 PH |
759 | /* The ERROR return occurs when checking hosts, when either a forward |
760 | or reverse lookup has failed. It can also occur in a match_ip list if a | |
761 | non-IP address item is encountered. The error string gives details of | |
059ec3d9 PH |
762 | which it was. */ |
763 | ||
764 | case ERROR: | |
72934ba7 JH |
765 | if (ignore_unknown) |
766 | { | |
767 | HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n", | |
768 | error); | |
769 | } | |
770 | else | |
771 | { | |
772 | HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot, | |
773 | include_unknown? "yes":"no", error); | |
774 | if (!include_unknown) | |
775 | { | |
776 | if (LOGGING(unknown_in_list)) | |
777 | log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); | |
778 | return FAIL; | |
779 | } | |
780 | log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error); | |
781 | return OK; | |
782 | } | |
059ec3d9 PH |
783 | } |
784 | } | |
785 | } | |
786 | ||
787 | /* If the item is a file name, we read the file and do a match attempt | |
788 | on each line in the file, including possibly more negation processing. */ | |
789 | ||
790 | else | |
791 | { | |
792 | int file_yield = yield; /* In case empty file */ | |
72934ba7 JH |
793 | uschar * filename = ss; |
794 | FILE * f = Ufopen(filename, "rb"); | |
059ec3d9 PH |
795 | uschar filebuffer[1024]; |
796 | ||
797 | /* ot will be null in non-debugging cases, and anyway, we get better | |
798 | wording by reworking it. */ | |
799 | ||
72934ba7 | 800 | if (!f) |
059ec3d9 | 801 | { |
72934ba7 | 802 | uschar * listname = readconf_find_option(listptr); |
059ec3d9 PH |
803 | if (listname[0] == 0) |
804 | listname = string_sprintf("\"%s\"", *listptr); | |
805 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", | |
806 | string_open_failed(errno, "%s when checking %s", sss, listname)); | |
807 | } | |
808 | ||
809 | /* Trailing comments are introduced by #, but in an address list or local | |
810 | part list, the # must be preceded by white space or the start of the line, | |
811 | because the # character is a legal character in local parts. */ | |
812 | ||
813 | while (Ufgets(filebuffer, sizeof(filebuffer), f) != NULL) | |
814 | { | |
815 | uschar *error; | |
816 | uschar *sss = filebuffer; | |
817 | ||
818 | while ((ss = Ustrchr(sss, '#')) != NULL) | |
819 | { | |
820 | if ((type != MCL_ADDRESS && type != MCL_LOCALPART) || | |
821 | ss == filebuffer || isspace(ss[-1])) | |
822 | { | |
823 | *ss = 0; | |
824 | break; | |
825 | } | |
826 | sss = ss + 1; | |
827 | } | |
828 | ||
829 | ss = filebuffer + Ustrlen(filebuffer); /* trailing space */ | |
830 | while (ss > filebuffer && isspace(ss[-1])) ss--; | |
831 | *ss = 0; | |
832 | ||
833 | ss = filebuffer; | |
834 | while (isspace(*ss)) ss++; /* leading space */ | |
835 | ||
836 | if (*ss == 0) continue; /* ignore empty */ | |
837 | ||
838 | file_yield = yield; /* positive yield */ | |
839 | sss = ss; /* for debugging */ | |
840 | ||
841 | if (*ss == '!') /* negation */ | |
842 | { | |
843 | file_yield = (file_yield == OK)? FAIL : OK; | |
844 | while (isspace((*(++ss)))); | |
845 | } | |
846 | ||
847 | switch ((func)(arg, ss, valueptr, &error)) | |
848 | { | |
849 | case OK: | |
72934ba7 JH |
850 | (void)fclose(f); |
851 | HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\" in %s)\n", ot, | |
852 | yield == OK ? "yes" : "no", sss, filename); | |
853 | return file_yield; | |
059ec3d9 PH |
854 | |
855 | case DEFER: | |
72934ba7 JH |
856 | if (!error) |
857 | error = string_sprintf("DNS lookup of %s deferred", ss); | |
858 | if (ignore_defer) | |
859 | { | |
860 | HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", | |
861 | error); | |
862 | break; | |
863 | } | |
864 | (void)fclose(f); | |
865 | if (include_defer) | |
866 | { | |
867 | log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); | |
868 | return OK; | |
869 | } | |
870 | goto DEFER_RETURN; | |
871 | ||
872 | case ERROR: /* host name lookup failed - this can only */ | |
873 | if (ignore_unknown) /* be for an incoming host (not outgoing) */ | |
874 | { | |
875 | HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n", | |
876 | error); | |
877 | } | |
878 | else | |
879 | { | |
880 | HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot, | |
881 | include_unknown? "yes":"no", error); | |
882 | (void)fclose(f); | |
883 | if (!include_unknown) | |
884 | { | |
885 | if (LOGGING(unknown_in_list)) | |
886 | log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); | |
887 | return FAIL; | |
888 | } | |
889 | log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error); | |
890 | return OK; | |
891 | } | |
059ec3d9 PH |
892 | } |
893 | } | |
894 | ||
895 | /* At the end of the file, leave the yield setting at the final setting | |
896 | for the file, in case this is the last item in the list. */ | |
897 | ||
898 | yield = file_yield; | |
f1e894f3 | 899 | (void)fclose(f); |
059ec3d9 PH |
900 | } |
901 | } /* Loop for the next item on the top-level list */ | |
902 | ||
903 | /* End of list reached: if the last item was negated yield OK, else FAIL. */ | |
904 | ||
905 | HDEBUG(D_lists) | |
72934ba7 JH |
906 | debug_printf("%s %s (end of list)\n", ot, yield == OK ? "no":"yes"); |
907 | return yield == OK ? FAIL : OK; | |
059ec3d9 | 908 | |
485aa451 | 909 | /* Something deferred */ |
059ec3d9 PH |
910 | |
911 | DEFER_RETURN: | |
485aa451 | 912 | HDEBUG(D_lists) debug_printf("%s list match deferred for %s\n", ot, sss); |
059ec3d9 PH |
913 | return DEFER; |
914 | } | |
915 | ||
916 | ||
917 | /************************************************* | |
918 | * Match in colon-separated list * | |
919 | *************************************************/ | |
920 | ||
921 | /* This function is used for domain lists and local part lists. It is not used | |
922 | for host lists or address lists, which have additional interpretation of the | |
923 | patterns. Some calls of it set sep > UCHAR_MAX in order to use its matching | |
924 | facilities on single items. When this is done, it arranges to set the numerical | |
925 | variables as a result of the match. | |
926 | ||
927 | This function is now just a short interface to match_check_list(), which does | |
928 | list scanning in a general way. A good compiler will optimize the tail | |
929 | recursion. | |
930 | ||
931 | Arguments: | |
932 | s string to search for | |
933 | listptr ptr to ptr to colon separated list of patterns, or NULL | |
934 | sep a separator value for the list (see string_nextinlist()) | |
935 | anchorptr ptr to tree for named items, or NULL if no named items | |
936 | cache_bits ptr to cache_bits for ditto, or NULL if not caching | |
937 | type MCL_DOMAIN when matching a domain list | |
938 | MCL_LOCALPART when matching a local part list (address lists | |
939 | have their own function) | |
940 | MCL_STRING for others (e.g. list of ciphers) | |
941 | MCL_NOEXPAND (whose value is greater than any of them) may | |
942 | be added to any value to suppress expansion of the list | |
943 | caseless TRUE for (mostly) caseless matching - passed directly to | |
944 | match_check_string() | |
945 | valueptr pointer to where any lookup data is to be passed back, | |
946 | or NULL (just passed on to match_check_string) | |
947 | ||
948 | Returns: OK if matched a non-negated item | |
949 | OK if hit end of list after a negated item | |
950 | FAIL if expansion force-failed | |
951 | FAIL if matched a negated item | |
952 | FAIL if hit end of list after a non-negated item | |
953 | DEFER if a lookup deferred | |
954 | */ | |
955 | ||
956 | int | |
55414b25 | 957 | match_isinlist(const uschar *s, const uschar **listptr, int sep, |
1dc92d5a | 958 | tree_node **anchorptr, |
55414b25 | 959 | unsigned int *cache_bits, int type, BOOL caseless, const uschar **valueptr) |
059ec3d9 PH |
960 | { |
961 | unsigned int *local_cache_bits = cache_bits; | |
962 | check_string_block cb; | |
963 | cb.origsubject = s; | |
43ba45ce JH |
964 | cb.subject = caseless ? string_copylc(s) : string_copy(s); |
965 | cb.at_is_special = FALSE; | |
966 | switch (type & ~MCL_NOEXPAND) | |
967 | { | |
968 | case MCL_DOMAIN: cb.at_is_special = TRUE; /*FALLTHROUGH*/ | |
969 | case MCL_LOCALPART: cb.expand_setup = 0; break; | |
970 | default: cb.expand_setup = sep > UCHAR_MAX ? 0 : -1; break; | |
971 | } | |
059ec3d9 PH |
972 | cb.use_partial = TRUE; |
973 | cb.caseless = caseless; | |
43ba45ce | 974 | if (valueptr) *valueptr = NULL; |
059ec3d9 PH |
975 | return match_check_list(listptr, sep, anchorptr, &local_cache_bits, |
976 | check_string, &cb, type, s, valueptr); | |
977 | } | |
978 | ||
979 | ||
980 | ||
981 | /************************************************* | |
982 | * Match address to single address-list item * | |
983 | *************************************************/ | |
984 | ||
985 | /* This function matches an address to an item from an address list. It is | |
986 | called from match_address_list() via match_check_list(). That is why most of | |
987 | its arguments are in an indirect block. | |
988 | ||
989 | Arguments: | |
990 | arg the argument block (see below) | |
991 | pattern the pattern to match | |
992 | valueptr where to return a value | |
993 | error for error messages (not used in this function; it never | |
994 | returns ERROR) | |
995 | ||
996 | The argument block contains: | |
997 | address the start of the subject address; when called from retry.c | |
998 | it may be *@domain if the local part isn't relevant | |
999 | origaddress the original, un-case-forced address (not used here, but used | |
1000 | in match_check_list() when +caseful is encountered) | |
1001 | expand_setup controls setting up of $n variables | |
1002 | caseless TRUE for caseless local part matching | |
1003 | ||
1004 | Returns: OK for a match | |
1005 | FAIL for no match | |
1006 | DEFER if a lookup deferred | |
1007 | */ | |
1008 | ||
1009 | static int | |
55414b25 | 1010 | check_address(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) |
059ec3d9 PH |
1011 | { |
1012 | check_address_block *cb = (check_address_block *)arg; | |
1013 | check_string_block csb; | |
1014 | int rc; | |
1015 | int expand_inc = 0; | |
1016 | unsigned int *null = NULL; | |
55414b25 | 1017 | const uschar *listptr; |
059ec3d9 | 1018 | uschar *subject = cb->address; |
55414b25 JH |
1019 | const uschar *s; |
1020 | uschar *pdomain, *sdomain; | |
059ec3d9 PH |
1021 | |
1022 | error = error; /* Keep clever compilers from complaining */ | |
1023 | ||
333b9d0d | 1024 | DEBUG(D_lists) debug_printf("address match test: subject=%s pattern=%s\n", |
059ec3d9 PH |
1025 | subject, pattern); |
1026 | ||
f0917727 PH |
1027 | /* Find the subject's domain */ |
1028 | ||
1029 | sdomain = Ustrrchr(subject, '@'); | |
1030 | ||
1031 | /* The only case where a subject may not have a domain is if the subject is | |
1032 | empty. Otherwise, a subject with no domain is a serious configuration error. */ | |
1033 | ||
1034 | if (sdomain == NULL && *subject != 0) | |
1035 | { | |
1036 | log_write(0, LOG_MAIN|LOG_PANIC, "no @ found in the subject of an " | |
1037 | "address list match: subject=\"%s\" pattern=\"%s\"", subject, pattern); | |
1038 | return FAIL; | |
1039 | } | |
1040 | ||
8e669ac1 | 1041 | /* Handle a regular expression, which must match the entire incoming address. |
90af77f4 | 1042 | This may be the empty address. */ |
059ec3d9 PH |
1043 | |
1044 | if (*pattern == '^') | |
1045 | return match_check_string(subject, pattern, cb->expand_setup, TRUE, | |
1046 | cb->caseless, FALSE, NULL); | |
1047 | ||
90af77f4 PH |
1048 | /* Handle a pattern that is just a lookup. Skip over possible lookup names |
1049 | (letters, digits, hyphens). Skip over a possible * or *@ at the end. Then we | |
1050 | must have a semicolon for it to be a lookup. */ | |
059ec3d9 PH |
1051 | |
1052 | for (s = pattern; isalnum(*s) || *s == '-'; s++); | |
1053 | if (*s == '*') s++; | |
1054 | if (*s == '@') s++; | |
1055 | ||
8e669ac1 | 1056 | /* If it is a straight lookup, do a lookup for the whole address. This may be |
90af77f4 PH |
1057 | the empty address. Partial matching doesn't make sense here, so we ignore it, |
1058 | but write a panic log entry. However, *@ matching will be honoured. */ | |
059ec3d9 PH |
1059 | |
1060 | if (*s == ';') | |
1061 | { | |
1062 | if (Ustrncmp(pattern, "partial-", 8) == 0) | |
1063 | log_write(0, LOG_MAIN|LOG_PANIC, "partial matching is not applicable to " | |
1064 | "whole-address lookups: ignored \"partial-\" in \"%s\"", pattern); | |
1065 | return match_check_string(subject, pattern, -1, FALSE, cb->caseless, FALSE, | |
1066 | valueptr); | |
1067 | } | |
1068 | ||
8e669ac1 PH |
1069 | /* For the remaining cases, an empty subject matches only an empty pattern, |
1070 | because other patterns expect to have a local part and a domain to match | |
90af77f4 PH |
1071 | against. */ |
1072 | ||
1073 | if (*subject == 0) return (*pattern == 0)? OK : FAIL; | |
1074 | ||
059ec3d9 PH |
1075 | /* If the pattern starts with "@@" we have a split lookup, where the domain is |
1076 | looked up to obtain a list of local parts. If the subject's local part is just | |
1077 | "*" (called from retry) the match always fails. */ | |
1078 | ||
1079 | if (pattern[0] == '@' && pattern[1] == '@') | |
1080 | { | |
1081 | int watchdog = 50; | |
55414b25 | 1082 | uschar *list, *ss; |
059ec3d9 PH |
1083 | uschar buffer[1024]; |
1084 | ||
1085 | if (sdomain == subject + 1 && *subject == '*') return FAIL; | |
1086 | ||
1087 | /* Loop for handling chains. The last item in any list may be of the form | |
1088 | ">name" in order to chain on to another list. */ | |
1089 | ||
d7978c0f | 1090 | for (const uschar * key = sdomain + 1; key && watchdog-- > 0; ) |
059ec3d9 PH |
1091 | { |
1092 | int sep = 0; | |
1093 | ||
1094 | if ((rc = match_check_string(key, pattern + 2, -1, TRUE, FALSE, FALSE, | |
55414b25 | 1095 | CUSS &list)) != OK) return rc; |
059ec3d9 PH |
1096 | |
1097 | /* Check for chaining from the last item; set up the next key if one | |
1098 | is found. */ | |
1099 | ||
1100 | ss = Ustrrchr(list, ':'); | |
1101 | if (ss == NULL) ss = list; else ss++; | |
1102 | while (isspace(*ss)) ss++; | |
1103 | if (*ss == '>') | |
1104 | { | |
1105 | *ss++ = 0; | |
1106 | while (isspace(*ss)) ss++; | |
1107 | key = string_copy(ss); | |
1108 | } | |
1109 | else key = NULL; | |
1110 | ||
1111 | /* Look up the local parts provided by the list; negation is permitted. | |
1112 | If a local part has to begin with !, a regex can be used. */ | |
1113 | ||
55414b25 | 1114 | while ((ss = string_nextinlist(CUSS &list, &sep, buffer, sizeof(buffer)))) |
059ec3d9 PH |
1115 | { |
1116 | int local_yield; | |
1117 | ||
1118 | if (*ss == '!') | |
1119 | { | |
1120 | local_yield = FAIL; | |
1121 | while (isspace((*(++ss)))); | |
1122 | } | |
1123 | else local_yield = OK; | |
1124 | ||
1125 | *sdomain = 0; | |
1126 | rc = match_check_string(subject, ss, -1, TRUE, cb->caseless, FALSE, | |
1127 | valueptr); | |
1128 | *sdomain = '@'; | |
1129 | ||
1130 | switch(rc) | |
1131 | { | |
1132 | case OK: | |
1133 | return local_yield; | |
1134 | ||
1135 | case DEFER: | |
1136 | return DEFER; | |
1137 | } | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | /* End of chain loop; panic if too many times */ | |
1142 | ||
1143 | if (watchdog <= 0) | |
1144 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Loop detected in lookup of " | |
1145 | "local part of %s in %s", subject, pattern); | |
1146 | ||
1147 | /* Otherwise the local part check has failed, so the whole match | |
1148 | fails. */ | |
1149 | ||
1150 | return FAIL; | |
1151 | } | |
1152 | ||
1153 | ||
1154 | /* We get here if the pattern is not a lookup or a regular expression. If it | |
1155 | contains an @ there is both a local part and a domain. */ | |
1156 | ||
1157 | pdomain = Ustrrchr(pattern, '@'); | |
1158 | if (pdomain != NULL) | |
1159 | { | |
1160 | int pllen, sllen; | |
1161 | ||
1162 | /* If the domain in the pattern is empty or one of the special cases [] or | |
1163 | mx_{any,primary,secondary}, and the local part in the pattern ends in "@", | |
1164 | we have a pattern of the form <something>@@, <something>@@[], or | |
1165 | <something>@@mx_{any,primary,secondary}. These magic "domains" are | |
1166 | automatically interpreted in match_check_string. We just need to arrange that | |
1167 | the leading @ is included in the domain. */ | |
1168 | ||
1169 | if (pdomain > pattern && pdomain[-1] == '@' && | |
1170 | (pdomain[1] == 0 || | |
1171 | Ustrcmp(pdomain+1, "[]") == 0 || | |
1172 | Ustrcmp(pdomain+1, "mx_any") == 0 || | |
1173 | Ustrcmp(pdomain+1, "mx_primary") == 0 || | |
1174 | Ustrcmp(pdomain+1, "mx_secondary") == 0)) | |
1175 | pdomain--; | |
1176 | ||
1177 | pllen = pdomain - pattern; | |
1178 | sllen = sdomain - subject; | |
1179 | ||
1180 | /* Compare the local parts in the subject and the pattern */ | |
1181 | ||
1182 | if (*pattern == '*') | |
1183 | { | |
1184 | int cllen = pllen - 1; | |
1185 | if (sllen < cllen) return FAIL; | |
fa0798c0 JH |
1186 | if (cb->caseless |
1187 | ? strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0 | |
1188 | : Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0) | |
059ec3d9 | 1189 | return FAIL; |
059ec3d9 PH |
1190 | if (cb->expand_setup > 0) |
1191 | { | |
1192 | expand_nstring[cb->expand_setup] = subject; | |
1193 | expand_nlength[cb->expand_setup] = sllen - cllen; | |
1194 | expand_inc = 1; | |
1195 | } | |
1196 | } | |
1197 | else | |
1198 | { | |
1199 | if (sllen != pllen) return FAIL; | |
fa0798c0 JH |
1200 | if (cb->caseless |
1201 | ? strncmpic(subject, pattern, sllen) != 0 | |
1202 | : Ustrncmp(subject, pattern, sllen) != 0) return FAIL; | |
059ec3d9 PH |
1203 | } |
1204 | } | |
1205 | ||
1206 | /* If the local part matched, or was not being checked, check the domain using | |
1207 | the generalized function, which supports file lookups (which may defer). The | |
1208 | original code read as follows: | |
1209 | ||
1210 | return match_check_string(sdomain + 1, | |
fa0798c0 | 1211 | pdomain ? pdomain + 1 : pattern, |
059ec3d9 PH |
1212 | cb->expand_setup + expand_inc, TRUE, cb->caseless, TRUE, NULL); |
1213 | ||
1214 | This supported only literal domains and *.x.y patterns. In order to allow for | |
1215 | named domain lists (so that you can right, for example, "senders=+xxxx"), it | |
1216 | was changed to use the list scanning function. */ | |
1217 | ||
1218 | csb.origsubject = sdomain + 1; | |
fa0798c0 | 1219 | csb.subject = cb->caseless ? string_copylc(sdomain+1) : string_copy(sdomain+1); |
059ec3d9 PH |
1220 | csb.expand_setup = cb->expand_setup + expand_inc; |
1221 | csb.use_partial = TRUE; | |
1222 | csb.caseless = cb->caseless; | |
1223 | csb.at_is_special = TRUE; | |
1224 | ||
fa0798c0 JH |
1225 | listptr = pdomain ? pdomain + 1 : pattern; |
1226 | if (valueptr) *valueptr = NULL; | |
059ec3d9 PH |
1227 | |
1228 | return match_check_list( | |
1229 | &listptr, /* list of one item */ | |
1230 | UCHAR_MAX+1, /* impossible separator; single item */ | |
1231 | &domainlist_anchor, /* it's a domain list */ | |
1232 | &null, /* ptr to NULL means no caching */ | |
1233 | check_string, /* the function to do one test */ | |
1234 | &csb, /* its data */ | |
1235 | MCL_DOMAIN + MCL_NOEXPAND, /* domain list; don't expand */ | |
1236 | csb.subject, /* string for messages */ | |
1237 | valueptr); /* where to pass back lookup data */ | |
1238 | } | |
1239 | ||
1240 | ||
1241 | ||
1242 | ||
1243 | /************************************************* | |
1244 | * Test whether address matches address list * | |
1245 | *************************************************/ | |
1246 | ||
1247 | /* This function is given an address and a list of things to match it against. | |
1248 | The list may contain individual addresses, regular expressions, lookup | |
1249 | specifications, and indirection via bare files. Negation is supported. The | |
1250 | address to check can consist of just a domain, which will then match only | |
1251 | domain items or items specified as *@domain. | |
1252 | ||
1253 | Domains are always lower cased before the match. Local parts are also lower | |
1254 | cased unless "caseless" is false. The work of actually scanning the list is | |
1255 | done by match_check_list(), with an appropriate block of arguments and a | |
1256 | callback to check_address(). During caseless matching, it will recognize | |
1257 | +caseful and revert to caseful matching. | |
1258 | ||
1259 | Arguments: | |
1260 | address address to test | |
1261 | caseless TRUE to start in caseless state | |
1262 | expand TRUE to allow list expansion | |
1263 | listptr list to check against | |
1264 | cache_bits points to cache bits for named address lists, or NULL | |
1265 | expand_setup controls setting up of $n variables - passed through | |
1266 | to check_address (q.v.) | |
1267 | sep separator character for the list; | |
1268 | may be 0 to get separator from the list; | |
1269 | may be UCHAR_MAX+1 for one-item list | |
1270 | valueptr where to return a lookup value, or NULL | |
1271 | ||
1272 | Returns: OK for a positive match, or end list after a negation; | |
1273 | FAIL for a negative match, or end list after non-negation; | |
1274 | DEFER if a lookup deferred | |
1275 | */ | |
1276 | ||
1277 | int | |
55414b25 JH |
1278 | match_address_list(const uschar *address, BOOL caseless, BOOL expand, |
1279 | const uschar **listptr, unsigned int *cache_bits, int expand_setup, int sep, | |
1280 | const uschar **valueptr) | |
059ec3d9 | 1281 | { |
059ec3d9 PH |
1282 | check_address_block ab; |
1283 | unsigned int *local_cache_bits = cache_bits; | |
f3ebb786 | 1284 | int len; |
059ec3d9 PH |
1285 | |
1286 | /* RFC 2505 recommends that for spam checking, local parts should be caselessly | |
1287 | compared. Therefore, Exim now forces the entire address into lower case here, | |
1288 | provided that "caseless" is set. (It is FALSE for calls for matching rewriting | |
1289 | patterns.) Otherwise just the domain is lower cases. A magic item "+caseful" in | |
1290 | the list can be used to restore a caseful copy of the local part from the | |
1291 | original address. */ | |
1292 | ||
f3ebb786 JH |
1293 | if ((len = Ustrlen(address)) > 255) len = 255; |
1294 | ab.address = string_copyn(address, len); | |
1295 | ||
1296 | for (uschar * p = ab.address + len - 1; p >= ab.address; p--) | |
059ec3d9 PH |
1297 | { |
1298 | if (!caseless && *p == '@') break; | |
1299 | *p = tolower(*p); | |
1300 | } | |
1301 | ||
1302 | /* If expand_setup is zero, we need to set up $0 to the whole thing, in | |
1303 | case there is a match. Can't use the built-in facilities of match_check_string | |
1304 | (via check_address), as we may just be calling that for part of the address | |
1305 | (the domain). */ | |
1306 | ||
1307 | if (expand_setup == 0) | |
1308 | { | |
1309 | expand_nstring[0] = string_copy(address); | |
1310 | expand_nlength[0] = Ustrlen(address); | |
1311 | expand_setup++; | |
1312 | } | |
1313 | ||
1314 | /* Set up the data to be passed ultimately to check_address. */ | |
1315 | ||
1316 | ab.origaddress = address; | |
f3ebb786 | 1317 | /* ab.address is above */ |
059ec3d9 PH |
1318 | ab.expand_setup = expand_setup; |
1319 | ab.caseless = caseless; | |
1320 | ||
1321 | return match_check_list(listptr, sep, &addresslist_anchor, &local_cache_bits, | |
1322 | check_address, &ab, MCL_ADDRESS + (expand? 0:MCL_NOEXPAND), address, | |
1323 | valueptr); | |
1324 | } | |
1325 | ||
36d295f1 JH |
1326 | /* Simpler version of match_address_list; always caseless, expanding, |
1327 | no cache bits, no value-return. | |
1328 | ||
1329 | Arguments: | |
1330 | address address to test | |
1331 | listptr list to check against | |
1332 | sep separator character for the list; | |
1333 | may be 0 to get separator from the list; | |
1334 | may be UCHAR_MAX+1 for one-item list | |
1335 | ||
1336 | Returns: OK for a positive match, or end list after a negation; | |
1337 | FAIL for a negative match, or end list after non-negation; | |
1338 | DEFER if a lookup deferred | |
1339 | */ | |
1340 | ||
1341 | int | |
1342 | match_address_list_basic(const uschar *address, const uschar **listptr, int sep) | |
1343 | { | |
1344 | return match_address_list(address, TRUE, TRUE, listptr, NULL, -1, sep, NULL); | |
1345 | } | |
1346 | ||
059ec3d9 | 1347 | /* End of match.c */ |