xref: /lighttpd1.4/src/configfile-glue.c (revision f2e4e83a)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "buffer.h"
5 #include "array.h"
6 #include "log.h"
7 #include "http_header.h"
8 #include "sock_addr.h"
9 
10 #include "configfile.h"
11 #include "plugin.h"
12 
13 #include <string.h>
14 #include <stdlib.h>     /* strtol */
15 
16 /**
17  * like all glue code this file contains functions which
18  * are the external interface of lighttpd. The functions
19  * are used by the server itself and the plugins.
20  *
21  * The main-goal is to have a small library in the end
22  * which is linked against both and which will define
23  * the interface itself in the end.
24  *
25  */
26 
27 /* internal reference to srv->config_context array of (data_config *) */
28 static struct {
29     const data_config * const *data; /* (srv->config_context->data) */
30     uint32_t used;                   /* (srv->config_context->used) */
31 } config_reference;
32 
33 
34 void config_get_config_cond_info(config_cond_info * const cfginfo, uint32_t idx) {
35     const data_config * const dc = (data_config *)config_reference.data[idx];
36     cfginfo->comp = dc->comp;
37     cfginfo->cond = dc->cond;
38     cfginfo->string = &dc->string;
39     cfginfo->comp_tag = dc->comp_tag;
40     cfginfo->comp_key = dc->comp_key;
41     cfginfo->op = dc->op;
42 }
43 
44 int config_plugin_value_tobool (data_unset *du, int default_value)
45 {
46     if (NULL == du) return default_value;
47     if (du->type == TYPE_STRING) {
48         const buffer *b = &((const data_string *)du)->value;
49         if (buffer_eq_icase_slen(b, CONST_STR_LEN("enable"))
50             || buffer_eq_icase_slen(b, CONST_STR_LEN("true")))
51             return 1;
52         else if (buffer_eq_icase_slen(b, CONST_STR_LEN("disable"))
53                  || buffer_eq_icase_slen(b, CONST_STR_LEN("false")))
54             return 0;
55         else
56             return default_value;
57     }
58     else if (du->type == TYPE_INTEGER)
59         return (0 != ((const data_integer *)du)->value);
60     else
61         return default_value;
62 }
63 
64 int config_plugin_values_init_block(server * const srv, const array * const ca, const config_plugin_keys_t * const cpk, const char * const mname, config_plugin_value_t *cpv) {
65     /*(cpv must be list with sufficient elements to store all matches + 1)*/
66 
67     int rc = 1; /* default is success */
68 
69     for (int i = 0; cpk[i].ktype != T_CONFIG_UNSET; ++i) {
70         data_unset * const du =
71           array_get_element_klen(ca, cpk[i].k, cpk[i].klen);
72         if (NULL == du) continue; /* not found */
73 
74         cpv->k_id = i;
75         cpv->vtype = cpk[i].ktype;
76 
77         switch (cpk[i].ktype) {
78           case T_CONFIG_ARRAY:
79           case T_CONFIG_ARRAY_KVANY:
80           case T_CONFIG_ARRAY_KVARRAY:
81           case T_CONFIG_ARRAY_KVSTRING:
82           case T_CONFIG_ARRAY_VLIST:
83             if (du->type == TYPE_ARRAY) {
84                 cpv->v.a = &((const data_array *)du)->value;
85             }
86             else {
87                 log_error(srv->errh, __FILE__, __LINE__,
88                   "%s should have been a list like "
89                   "%s = ( \"...\" )", cpk[i].k, cpk[i].k);
90                 rc = 0;
91                 continue;
92             }
93             switch (cpk[i].ktype) {
94               case T_CONFIG_ARRAY_KVANY:
95                 if (!array_is_kvany(cpv->v.a)) {
96                     log_error(srv->errh, __FILE__, __LINE__,
97                       "%s should have been a list of key => values like "
98                       "%s = ( \"...\" => \"...\", \"...\" => \"...\" )",
99                     cpk[i].k, cpk[i].k);
100                     rc = 0;
101                     continue;
102                 }
103                 break;
104               case T_CONFIG_ARRAY_KVARRAY:
105                 if (!array_is_kvarray(cpv->v.a)) {
106                     log_error(srv->errh, __FILE__, __LINE__,
107                       "%s should have been a list of key => list like "
108                       "%s = ( \"...\" => ( \"...\" => \"...\" ) )",
109                     cpk[i].k, cpk[i].k);
110                     rc = 0;
111                     continue;
112                 }
113                 break;
114               case T_CONFIG_ARRAY_KVSTRING:
115                 if (!array_is_kvstring(cpv->v.a)) {
116                     log_error(srv->errh, __FILE__, __LINE__,
117                       "%s should have been a list of key => string values like "
118                       "%s = ( \"...\" => \"...\", \"...\" => \"...\" )",
119                     cpk[i].k, cpk[i].k);
120                     rc = 0;
121                     continue;
122                 }
123                 break;
124               case T_CONFIG_ARRAY_VLIST:
125                 if (!array_is_vlist(cpv->v.a)) {
126                     log_error(srv->errh, __FILE__, __LINE__,
127                       "%s should have been a list of string values like "
128                       "%s = ( \"...\", \"...\" )",
129                     cpk[i].k, cpk[i].k);
130                     rc = 0;
131                     continue;
132                 }
133                 break;
134               /*case T_CONFIG_ARRAY:*/
135               default:
136                 break;
137             }
138             break;
139           case T_CONFIG_STRING:
140             if (du->type == TYPE_STRING) {
141                 cpv->v.b = &((const data_string *)du)->value;
142             }
143             else {
144                 log_error(srv->errh, __FILE__, __LINE__,
145                   "%s should have been a string like ... = \"...\"", cpk[i].k);
146                 rc = 0;
147                 continue;
148             }
149             break;
150           case T_CONFIG_SHORT:
151             switch(du->type) {
152               case TYPE_INTEGER:
153                 cpv->v.shrt =
154                   (unsigned short)((const data_integer *)du)->value;
155                 break;
156               case TYPE_STRING: {
157                 /* If the value came from an environment variable, then it is
158                  * a data_string, although it may contain a number in ASCII
159                  * decimal format.  We try to interpret the string as a decimal
160                  * short before giving up, in order to support setting numeric
161                  * values with environment variables (e.g. port number).
162                  */
163                 const char * const v = ((const data_string *)du)->value.ptr;
164                 if (v && *v) {
165                     char *e;
166                     long l = strtol(v, &e, 10);
167                     if (e != v && !*e && l >= 0 && l <= 65535) {
168                         cpv->v.shrt = (unsigned short)l;
169                         break;
170                     }
171                 }
172                 log_error(srv->errh, __FILE__, __LINE__,
173                   "got a string but expected a short: %s %s", cpk[i].k, v);
174                 rc = 0;
175                 continue;
176               }
177               default:
178                 log_error(srv->errh, __FILE__, __LINE__,
179                   "unexpected type for key: %s %d expected a short integer, "
180                   "range 0 ... 65535", cpk[i].k, du->type);
181                 rc = 0;
182                 continue;
183             }
184             break;
185           case T_CONFIG_INT:
186             switch(du->type) {
187               case TYPE_INTEGER:
188                 cpv->v.u = ((const data_integer *)du)->value;
189                 break;
190               case TYPE_STRING: {
191                 const char * const v = ((const data_string *)du)->value.ptr;
192                 if (v && *v) {
193                     char *e;
194                     long l = strtol(v, &e, 10);
195                     if (e != v && !*e && l >= 0) {
196                         cpv->v.shrt = (unsigned int)l;
197                         break;
198                     }
199                 }
200                 log_error(srv->errh, __FILE__, __LINE__,
201                   "got a string but expected an integer: %s %s",cpk[i].k,v);
202                 rc = 0;
203                 continue;
204               }
205               default:
206                 log_error(srv->errh, __FILE__, __LINE__,
207                   "unexpected type for key: %s %d expected an integer, "
208                   "range 0 ... 4294967295", cpk[i].k, du->type);
209                 rc = 0;
210                 continue;
211             }
212             break;
213           case T_CONFIG_BOOL:
214             {
215                 int v = config_plugin_value_tobool(du, -1);
216                 if (-1 == v) {
217                     log_error(srv->errh, __FILE__, __LINE__,
218                       "ERROR: unexpected type for key: %s (string) "
219                       "\"(enable|disable)\"", cpk[i].k);
220                     rc = 0;
221                     continue;
222                 }
223                 cpv->v.u = v;
224             }
225             break;
226           case T_CONFIG_LOCAL:
227           case T_CONFIG_UNSET:
228             continue;
229           case T_CONFIG_UNSUPPORTED:
230             log_error(srv->errh, __FILE__, __LINE__,
231               "ERROR: found unsupported key: %s (%s)", cpk[i].k, mname);
232             srv->srvconf.config_unsupported = 1;
233             continue;
234           case T_CONFIG_DEPRECATED:
235             log_error(srv->errh, __FILE__, __LINE__,
236               "ERROR: found deprecated key: %s (%s)", cpk[i].k, mname);
237             srv->srvconf.config_deprecated = 1;
238             continue;
239         }
240 
241         ++cpv;
242     }
243 
244     cpv->k_id = -1; /* indicate list end */
245 
246     return rc;
247 }
248 
249 int config_plugin_values_init(server * const srv, void *p_d, const config_plugin_keys_t * const cpk, const char * const mname) {
250     plugin_data_base * const p = (plugin_data_base *)p_d;
251     array * const touched = srv->srvconf.config_touched;
252     unsigned char matches[4096];   /*directives matches (4k is way too many!)*/
253     unsigned short contexts[4096]; /*conditions matches (4k is way too many!)*/
254     uint32_t n = 0;
255     int rc = 1; /* default is success */
256     force_assert(sizeof(matches) >= srv->config_context->used);
257 
258     /* save config reference data for later internal use
259      * (config_plugin_values_init() is called with same srv->config_context) */
260     config_reference.data = (const data_config * const *)srv->config_context->data;
261     config_reference.used = srv->config_context->used;
262 
263     /* traverse config contexts twice: once to count, once to store matches */
264 
265     for (uint32_t u = 0; u < srv->config_context->used; ++u) {
266         const array *ca =
267           ((data_config const *)srv->config_context->data[u])->value;
268 
269         matches[n] = 0;
270         for (int i = 0; cpk[i].ktype != T_CONFIG_UNSET; ++i) {
271             data_unset * const du =
272               array_get_element_klen(ca, cpk[i].k, cpk[i].klen);
273             if (NULL == du) continue; /* not found */
274 
275             ++matches[n];
276 
277             array_set_key_value(touched,cpk[i].k,cpk[i].klen,CONST_STR_LEN(""));
278 
279             if (cpk[i].scope == T_CONFIG_SCOPE_SERVER && 0 != u) {
280                 /* server scope options should be set only in server scope */
281                 log_error(srv->errh, __FILE__, __LINE__,
282                   "DEPRECATED: do not set server options in conditionals, "
283                   "variable: %s", cpk[i].k);
284             }
285         }
286         if (matches[n]) contexts[n++] = (unsigned short)u;
287     }
288 
289     uint32_t elts = 0;
290     for (uint32_t u = 0; u < n; ++u) elts += matches[u];
291     p->nconfig = n;
292     /*(+1 to include global scope, whether or not any directives exist)*/
293     /*(+n for extra element to end each list)*/
294     p->cvlist = (config_plugin_value_t *)
295       calloc(1+n+n+elts, sizeof(config_plugin_value_t));
296     force_assert(p->cvlist);
297 
298     elts = 1+n;
299     /* shift past first element if no directives in global scope */
300     const uint32_t shft = (0 != n && 0 != contexts[0]);
301     if (shft) ++p->nconfig;
302     for (uint32_t u = 0; u < n; ++u) {
303         config_plugin_value_t * const cpv = p->cvlist+shft+u;
304         cpv->k_id = (int)contexts[u];
305         cpv->v.u2[0] = elts;
306         cpv->v.u2[1] = matches[u];
307         elts += matches[u]+1;     /* +1 to end list with cpv->k_id = -1 */
308     }
309 
310     for (uint32_t u = 0; u < n; ++u) {
311         const array *ca =
312           ((data_config const *)srv->config_context->data[contexts[u]])->value;
313         config_plugin_value_t *cpv = p->cvlist + p->cvlist[shft+u].v.u2[0];
314         if (!config_plugin_values_init_block(srv, ca, cpk, mname, cpv))
315             rc = 0;
316     }
317 
318     return rc;
319 }
320 
321 __attribute_cold__
322 __attribute_noinline__
323 static void config_cond_result_trace(request_st * const r, const data_config * const dc, const int cached) {
324     cond_cache_t * const cache = &r->cond_cache[dc->context_ndx];
325     const char *msg;
326     switch (cache->result) {
327       case COND_RESULT_UNSET: msg = "unset"; break;
328       case COND_RESULT_SKIP:  msg = "skipped"; break;
329       case COND_RESULT_FALSE: msg = "false"; break;
330       case COND_RESULT_TRUE:  msg = "true"; break;
331       default:                msg = "invalid cond_result_t"; break;
332     }
333     log_error(r->conf.errh, __FILE__, __LINE__, "%d (%s) result: %s",
334               dc->context_ndx, &"uncached"[cached ? 2 : 0], msg);
335 }
336 
337 static cond_result_t config_check_cond_nocache(request_st *r, const data_config *dc, int debug_cond, cond_cache_t *cache);
338 
339 static cond_result_t config_check_cond_nocache_calc(request_st * const r, const data_config * const dc, const int debug_cond, cond_cache_t * const cache) {
340     cache->result = config_check_cond_nocache(r, dc, debug_cond, cache);
341     switch (cache->result) {
342       case COND_RESULT_FALSE:
343       case COND_RESULT_TRUE:
344         /* remember result of local condition for a partial reset */
345         cache->local_result = cache->result;
346         break;
347       default:
348         break;
349     }
350     if (debug_cond) config_cond_result_trace(r, dc, 0);
351     return cache->result;
352 }
353 
354 static cond_result_t config_check_cond_cached(request_st * const r, const data_config * const dc, const int debug_cond) {
355     cond_cache_t * const cache = &r->cond_cache[dc->context_ndx];
356     if (COND_RESULT_UNSET != cache->result) {
357         if (debug_cond) config_cond_result_trace(r, dc, 1);
358         return cache->result;
359     }
360     return config_check_cond_nocache_calc(r, dc, debug_cond, cache);
361 }
362 
363 static int config_addrstr_eq_remote_ip_mask(request_st * const r, const char * const addrstr, const int nm_bits, sock_addr * const rmt) {
364 	/* special-case 0 == nm_bits to mean "all bits of the address" in addrstr */
365 	sock_addr addr;
366 	if (1 == sock_addr_inet_pton(&addr, addrstr, AF_INET, 0)) {
367 		if (nm_bits > 32) {
368 			log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: ipv4 netmask too large: %d", nm_bits);
369 			return -1;
370 		}
371 	} else if (1 == sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) {
372 		if (nm_bits > 128) {
373 			log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: ipv6 netmask too large: %d", nm_bits);
374 			return -1;
375 		}
376 	} else {
377 		log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: ip addr is invalid: %s", addrstr);
378 		return -1;
379 	}
380 	return sock_addr_is_addr_eq_bits(&addr, rmt, nm_bits);
381 }
382 
383 static int config_addrbuf_eq_remote_ip_mask(request_st * const r, const buffer * const string, char * const nm_slash, sock_addr * const rmt) {
384 	char *err;
385 	int nm_bits = strtol(nm_slash + 1, &err, 10);
386 	size_t addrstrlen = (size_t)(nm_slash - string->ptr);
387 	char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/
388 
389 	if (*err) {
390 		log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: non-digit found in netmask: %s %s", string->ptr, err);
391 		return -1;
392 	}
393 
394 	if (nm_bits <= 0) {
395 		if (*(nm_slash+1) == '\0') {
396 			log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: no number after / %s", string->ptr);
397 		} else {
398 			log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: invalid netmask <= 0: %s %s", string->ptr, err);
399 		}
400 		return -1;
401 	}
402 
403 	if (addrstrlen >= sizeof(addrstr)) {
404 		log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: address string too long: %s", string->ptr);
405 		return -1;
406 	}
407 
408 	memcpy(addrstr, string->ptr, addrstrlen);
409 	addrstr[addrstrlen] = '\0';
410 
411 	return config_addrstr_eq_remote_ip_mask(r, addrstr, nm_bits, rmt);
412 }
413 
414 static int data_config_pcre_exec(const data_config *dc, cond_cache_t *cache, const buffer *b, cond_match_t *cond_match);
415 
416 static cond_result_t config_check_cond_nocache(request_st * const r, const data_config * const dc, const int debug_cond, cond_cache_t * const cache) {
417 	static struct const_char_buffer {
418 	  const char *ptr;
419 	  uint32_t used;
420 	  uint32_t size;
421 	} empty_string = { "", 1, 0 };
422 
423 	/* check parent first */
424 	if (dc->parent && dc->parent->context_ndx) {
425 		/**
426 		 * a nested conditional
427 		 *
428 		 * if the parent is not decided yet or false, we can't be true either
429 		 */
430 		if (debug_cond) {
431 			log_error(r->conf.errh, __FILE__, __LINE__, "go parent %s", dc->parent->key.ptr);
432 		}
433 
434 		switch (config_check_cond_cached(r, dc->parent, debug_cond)) {
435 		case COND_RESULT_UNSET:
436 			/* decide later */
437 			return COND_RESULT_UNSET;
438 		case COND_RESULT_SKIP:
439 		case COND_RESULT_FALSE:
440 			/* failed precondition */
441 			return COND_RESULT_SKIP;
442 		case COND_RESULT_TRUE:
443 			/* proceed */
444 			break;
445 		}
446 	}
447 
448 	if (dc->prev) {
449 		/**
450 		 * a else branch; can only be executed if the previous branch
451 		 * was evaluated as "false" (not unset/skipped/true)
452 		 */
453 		if (debug_cond) {
454 			log_error(r->conf.errh, __FILE__, __LINE__, "go prev %s", dc->prev->key.ptr);
455 		}
456 
457 		/* make sure prev is checked first */
458 		switch (config_check_cond_cached(r, dc->prev, debug_cond)) {
459 		case COND_RESULT_UNSET:
460 			/* decide later */
461 			return COND_RESULT_UNSET;
462 		case COND_RESULT_SKIP:
463 		case COND_RESULT_TRUE:
464 			/* failed precondition */
465 			return COND_RESULT_SKIP;
466 		case COND_RESULT_FALSE:
467 			/* proceed */
468 			break;
469 		}
470 	}
471 
472 	if (!(r->conditional_is_valid & (1 << dc->comp))) {
473 		if (debug_cond) {
474 			log_error(r->conf.errh, __FILE__, __LINE__,
475 			  "%d %s not available yet", dc->comp, dc->key.ptr);
476 		}
477 
478 		return COND_RESULT_UNSET;
479 	}
480 
481 	/* if we had a real result before and weren't cleared just return it */
482 	switch (cache->local_result) {
483 	case COND_RESULT_TRUE:
484 	case COND_RESULT_FALSE:
485 		return cache->local_result;
486 	default:
487 		break;
488 	}
489 
490 	if (CONFIG_COND_ELSE == dc->cond) return COND_RESULT_TRUE;
491 
492 	/* pass the rules */
493 
494 	buffer *l;
495 	switch (dc->comp) {
496 	case COMP_HTTP_HOST:
497 
498 		l = &r->uri.authority;
499 
500 		if (buffer_string_is_empty(l)) {
501 			l = (buffer *)&empty_string;
502 			break;
503 		}
504 
505 		switch(dc->cond) {
506 		case CONFIG_COND_NE:
507 		case CONFIG_COND_EQ: {
508 			unsigned short port = sock_addr_get_port(&r->con->srv_socket->addr);
509 			if (0 == port) break;
510 			const char *ck_colon = strchr(dc->string.ptr, ':');
511 			const char *val_colon = strchr(l->ptr, ':');
512 
513 			/* append server-port if necessary */
514 			if (NULL != ck_colon && NULL == val_colon) {
515 				/* condition "host:port" but client send "host" */
516 				buffer *tb = r->tmp_buf;
517 				buffer_copy_buffer(tb, l);
518 				buffer_append_string_len(tb, CONST_STR_LEN(":"));
519 				buffer_append_int(tb, port);
520 				l = tb;
521 			} else if (NULL != val_colon && NULL == ck_colon) {
522 				/* condition "host" but client send "host:port" */
523 				buffer *tb = r->tmp_buf;
524 				buffer_copy_string_len(tb, l->ptr, val_colon - l->ptr);
525 				l = tb;
526 			}
527 			break;
528 		}
529 		default:
530 			break;
531 		}
532 
533 		break;
534 	case COMP_HTTP_REMOTE_IP: {
535 		char *nm_slash;
536 		/* handle remoteip limitations
537 		 *
538 		 * "10.0.0.1" is provided for all comparisons
539 		 *
540 		 * only for == and != we support
541 		 *
542 		 * "10.0.0.1/24"
543 		 */
544 
545 		if ((dc->cond == CONFIG_COND_EQ ||
546 		     dc->cond == CONFIG_COND_NE) &&
547 		    (NULL != (nm_slash = strchr(dc->string.ptr, '/')))) {
548 			switch (config_addrbuf_eq_remote_ip_mask(r, &dc->string, nm_slash, &r->con->dst_addr)) {
549 			case  1: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
550 			case  0: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
551 			case -1: return COND_RESULT_FALSE; /*(error parsing configfile entry)*/
552 			}
553 		}
554 		l = r->con->dst_addr_buf;
555 		break;
556 	}
557 	case COMP_HTTP_SCHEME:
558 		l = &r->uri.scheme;
559 		break;
560 
561 	case COMP_HTTP_URL:
562 		l = &r->uri.path;
563 		break;
564 
565 	case COMP_HTTP_QUERY_STRING:
566 		l = &r->uri.query;
567 		if (NULL == l->ptr) l = (buffer *)&empty_string;
568 		break;
569 
570 	case COMP_SERVER_SOCKET:
571 		l = r->con->srv_socket->srv_token;
572 		break;
573 
574 	case COMP_HTTP_REQUEST_HEADER:
575 		*((const buffer **)&l) = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(dc->comp_tag));
576 		if (NULL == l) l = (buffer *)&empty_string;
577 		break;
578 	case COMP_HTTP_REQUEST_METHOD:
579 		l = r->tmp_buf;
580 		buffer_clear(l);
581 		http_method_append(l, r->http_method);
582 		break;
583 	default:
584 		return COND_RESULT_FALSE;
585 	}
586 
587 	if (NULL == l) { /*(should not happen)*/
588 		log_error(r->conf.errh, __FILE__, __LINE__,
589 			"%s () compare to NULL", dc->comp_key->ptr);
590 		return COND_RESULT_FALSE;
591 	}
592 	else if (debug_cond) {
593 		log_error(r->conf.errh, __FILE__, __LINE__,
594 			"%s (%s) compare to %s", dc->comp_key->ptr, l->ptr, dc->string.ptr);
595 	}
596 
597 	switch(dc->cond) {
598 	case CONFIG_COND_NE:
599 	case CONFIG_COND_EQ:
600 		if (buffer_is_equal(l, &dc->string)) {
601 			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
602 		} else {
603 			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
604 		}
605 	case CONFIG_COND_NOMATCH:
606 	case CONFIG_COND_MATCH: {
607 		cond_match_t *cond_match = r->cond_match + dc->context_ndx;
608 		if (data_config_pcre_exec(dc, cache, l, cond_match) > 0) {
609 			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
610 		} else {
611 			/* cache is already cleared */
612 			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
613 		}
614 	}
615 	default:
616 		/* no way */
617 		break;
618 	}
619 
620 	return COND_RESULT_FALSE;
621 }
622 
623 __attribute_noinline__
624 static cond_result_t config_check_cond_calc(request_st * const r, const int context_ndx, cond_cache_t * const cache) {
625     const data_config * const dc = config_reference.data[context_ndx];
626     const int debug_cond = r->conf.log_condition_handling;
627     if (debug_cond) {
628         log_error(r->conf.errh, __FILE__, __LINE__,
629                   "=== start of condition block ===");
630     }
631     return config_check_cond_nocache_calc(r, dc, debug_cond, cache);
632 }
633 
634 /* future: might make static inline in header for plugins */
635 int config_check_cond(request_st * const r, const int context_ndx) {
636     cond_cache_t * const cache = &r->cond_cache[context_ndx];
637     return COND_RESULT_TRUE
638         == (COND_RESULT_UNSET != cache->result
639               ? (cond_result_t)cache->result
640               : config_check_cond_calc(r, context_ndx, cache));
641 }
642 
643 /* if we reset the cache result for a node, we also need to clear all
644  * child nodes and else-branches*/
645 static void config_cond_clear_node(cond_cache_t * const cond_cache, const data_config * const dc) {
646 	/* if a node is "unset" all children are unset too */
647 	if (cond_cache[dc->context_ndx].result != COND_RESULT_UNSET) {
648 		cond_cache[dc->context_ndx].result = COND_RESULT_UNSET;
649 
650 		for (uint32_t i = 0; i < dc->children.used; ++i) {
651 			const data_config *dc_child = dc->children.data[i];
652 			if (NULL == dc_child->prev) {
653 				/* only call for first node in if-else chain */
654 				config_cond_clear_node(cond_cache, dc_child);
655 			}
656 		}
657 		if (NULL != dc->next) config_cond_clear_node(cond_cache, dc->next);
658 	}
659 }
660 
661 /**
662  * reset the config-cache for a named item
663  *
664  * if the item is COND_LAST_ELEMENT we reset all items
665  */
666 void config_cond_cache_reset_item(request_st * const r, comp_key_t item) {
667 	cond_cache_t * const cond_cache = r->cond_cache;
668 	const data_config * const * const data = config_reference.data;
669 	const uint32_t used = config_reference.used;
670 	for (uint32_t i = 0; i < used; ++i) {
671 		const data_config * const dc = data[i];
672 
673 		if (item == dc->comp) {
674 			/* clear local_result */
675 			cond_cache[i].local_result = COND_RESULT_UNSET;
676 			/* clear result in subtree (including the node itself) */
677 			config_cond_clear_node(cond_cache, dc);
678 		}
679 	}
680 }
681 
682 /**
683  * reset the config cache to its initial state at connection start
684  */
685 void config_cond_cache_reset(request_st * const r) {
686 	/* resetting all entries; no need to follow children as in config_cond_cache_reset_item */
687 	/* static_assert(0 == COND_RESULT_UNSET); */
688 	const uint32_t used = config_reference.used;
689 	if (used > 1)
690 		memset(r->cond_cache, 0, used*sizeof(cond_cache_t));
691 }
692 
693 #ifdef HAVE_PCRE_H
694 #include <pcre.h>
695 #endif
696 
697 static int data_config_pcre_exec(const data_config *dc, cond_cache_t *cache, const buffer *b, cond_match_t *cond_match) {
698 #ifdef HAVE_PCRE_H
699     #ifndef elementsof
700     #define elementsof(x) (sizeof(x) / sizeof(x[0]))
701     #endif
702     cache->patterncount =
703       pcre_exec(dc->regex, dc->regex_study, CONST_BUF_LEN(b), 0, 0,
704                 cond_match->matches, elementsof(cond_match->matches));
705     if (cache->patterncount > 0)
706         cond_match->comp_value = b; /*holds pointer to b (!) for pattern subst*/
707     return cache->patterncount;
708 #else
709     UNUSED(dc);
710     UNUSED(cache);
711     UNUSED(b);
712     UNUSED(cond_match);
713     return 0;
714 #endif
715 }
716