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