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