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