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