[mod_ssi] produce content in subrequest hook
[lighttpd.git] / src / configfile-glue.c
blobded9838763336c78f4cdc858283c2752c44c3d81
1 #include "first.h"
3 #include "base.h"
4 #include "buffer.h"
5 #include "array.h"
6 #include "log.h"
7 #include "plugin.h"
9 #include "configfile.h"
11 #include <string.h>
12 #include <stdlib.h>
13 #ifndef _WIN32
14 #include <arpa/inet.h>
15 #endif
17 /**
18 * like all glue code this file contains functions which
19 * are the external interface of lighttpd. The functions
20 * are used by the server itself and the plugins.
22 * The main-goal is to have a small library in the end
23 * which is linked against both and which will define
24 * the interface itself in the end.
29 /* handle global options */
31 /* parse config array */
32 int config_insert_values_internal(server *srv, array *ca, const config_values_t cv[], config_scope_type_t scope) {
33 size_t i;
34 data_unset *du;
36 for (i = 0; cv[i].key; i++) {
38 if (NULL == (du = array_get_element(ca, cv[i].key))) {
39 /* no found */
41 continue;
44 if ((T_CONFIG_SCOPE_SERVER == cv[i].scope)
45 && (T_CONFIG_SCOPE_SERVER != scope)) {
46 /* server scope options should only be set in server scope, not in conditionals */
47 log_error_write(srv, __FILE__, __LINE__, "ss",
48 "DEPRECATED: don't set server options in conditionals, variable:",
49 cv[i].key);
52 switch (cv[i].type) {
53 case T_CONFIG_ARRAY:
54 if (du->type == TYPE_ARRAY) {
55 size_t j;
56 data_array *da = (data_array *)du;
58 for (j = 0; j < da->value->used; j++) {
59 data_unset *ds = da->value->data[j];
60 if (ds->type == TYPE_STRING) {
61 array_insert_unique(cv[i].destination, ds->copy(ds));
62 } else {
63 log_error_write(srv, __FILE__, __LINE__, "sssbsd",
64 "the value of an array can only be a string, variable:",
65 cv[i].key, "[", ds->key, "], type:", ds->type);
67 return -1;
70 } else {
71 log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a array of strings like ... = ( \"...\" )");
73 return -1;
75 break;
76 case T_CONFIG_STRING:
77 if (du->type == TYPE_STRING) {
78 data_string *ds = (data_string *)du;
80 buffer_copy_buffer(cv[i].destination, ds->value);
81 } else {
82 log_error_write(srv, __FILE__, __LINE__, "ssss", cv[i].key, "should have been a string like ... = \"...\"");
84 return -1;
86 break;
87 case T_CONFIG_SHORT:
88 switch(du->type) {
89 case TYPE_INTEGER: {
90 data_integer *di = (data_integer *)du;
92 *((unsigned short *)(cv[i].destination)) = di->value;
93 break;
95 case TYPE_STRING: {
96 data_string *ds = (data_string *)du;
98 /* If the value came from an environment variable, then it is a
99 * data_string, although it may contain a number in ASCII
100 * decimal format. We try to interpret the string as a decimal
101 * short before giving up, in order to support setting numeric
102 * values with environment variables (eg, port number).
104 if (ds->value->ptr && *ds->value->ptr) {
105 char *e;
106 long l = strtol(ds->value->ptr, &e, 10);
107 if (e != ds->value->ptr && !*e && l >=0 && l <= 65535) {
108 *((unsigned short *)(cv[i].destination)) = l;
109 break;
113 log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a short:", cv[i].key, ds->value);
115 return -1;
117 default:
118 log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a short integer, range 0 ... 65535");
119 return -1;
121 break;
122 case T_CONFIG_INT:
123 switch(du->type) {
124 case TYPE_INTEGER: {
125 data_integer *di = (data_integer *)du;
127 *((unsigned int *)(cv[i].destination)) = di->value;
128 break;
130 case TYPE_STRING: {
131 data_string *ds = (data_string *)du;
133 if (ds->value->ptr && *ds->value->ptr) {
134 char *e;
135 long l = strtol(ds->value->ptr, &e, 10);
136 if (e != ds->value->ptr && !*e && l >= 0) {
137 *((unsigned int *)(cv[i].destination)) = l;
138 break;
142 log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected an integer:", cv[i].key, ds->value);
144 return -1;
146 default:
147 log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected an integer, range 0 ... 4294967295");
148 return -1;
150 break;
151 case T_CONFIG_BOOLEAN:
152 if (du->type == TYPE_STRING) {
153 data_string *ds = (data_string *)du;
155 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) {
156 *((unsigned short *)(cv[i].destination)) = 1;
157 } else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) {
158 *((unsigned short *)(cv[i].destination)) = 0;
159 } else {
160 log_error_write(srv, __FILE__, __LINE__, "ssbs", "ERROR: unexpected value for key:", cv[i].key, ds->value, "(enable|disable)");
162 return -1;
164 } else {
165 log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: unexpected type for key:", cv[i].key, "(string)", "\"(enable|disable)\"");
167 return -1;
169 break;
170 case T_CONFIG_LOCAL:
171 case T_CONFIG_UNSET:
172 break;
173 case T_CONFIG_UNSUPPORTED:
174 log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination));
176 srv->config_unsupported = 1;
178 break;
179 case T_CONFIG_DEPRECATED:
180 log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination));
182 srv->config_deprecated = 1;
184 break;
188 return 0;
191 int config_insert_values_global(server *srv, array *ca, const config_values_t cv[], config_scope_type_t scope) {
192 size_t i;
193 data_unset *du;
195 for (i = 0; cv[i].key; i++) {
196 data_string *touched;
198 if (NULL == (du = array_get_element(ca, cv[i].key))) {
199 /* no found */
201 continue;
204 /* touched */
205 touched = data_string_init();
207 buffer_copy_string_len(touched->value, CONST_STR_LEN(""));
208 buffer_copy_buffer(touched->key, du->key);
210 array_insert_unique(srv->config_touched, (data_unset *)touched);
213 return config_insert_values_internal(srv, ca, cv, scope);
216 static unsigned short sock_addr_get_port(sock_addr *addr) {
217 #ifdef HAVE_IPV6
218 return ntohs(addr->plain.sa_family ? addr->ipv6.sin6_port : addr->ipv4.sin_port);
219 #else
220 return ntohs(addr->ipv4.sin_port);
221 #endif
224 static const char* cond_result_to_string(cond_result_t cond_result) {
225 switch (cond_result) {
226 case COND_RESULT_UNSET: return "unset";
227 case COND_RESULT_SKIP: return "skipped";
228 case COND_RESULT_FALSE: return "false";
229 case COND_RESULT_TRUE: return "true";
230 default: return "invalid cond_result_t";
234 static int config_addrstr_eq_remote_ip_mask(server *srv, const char *addrstr, int nm_bits, sock_addr *rmt) {
235 /* special-case 0 == nm_bits to mean "all bits of the address" in addrstr */
236 sock_addr val;
237 #ifdef HAVE_INET_PTON
238 if (1 == inet_pton(AF_INET, addrstr, &val.ipv4.sin_addr))
239 #else
240 if (INADDR_NONE != (val.ipv4.sin_addr = inet_addr(addrstr)))
241 #endif
243 /* build netmask */
244 uint32_t nm;
245 if (nm_bits > 32) {
246 log_error_write(srv, __FILE__, __LINE__, "sd", "ERROR: ipv4 netmask too large:", nm_bits);
247 return -1;
249 nm = htonl(~((1u << (32 - (0 != nm_bits ? nm_bits : 32))) - 1));
251 if (rmt->plain.sa_family == AF_INET) {
252 return ((val.ipv4.sin_addr.s_addr & nm) == (rmt->ipv4.sin_addr.s_addr & nm));
253 #ifdef HAVE_IPV6
254 } else if (rmt->plain.sa_family == AF_INET6
255 && IN6_IS_ADDR_V4MAPPED(&rmt->ipv6.sin6_addr)) {
256 #ifdef s6_addr32
257 in_addr_t x = rmt->ipv6.sin6_addr.s6_addr32[3];
258 #else
259 in_addr_t x;
260 memcpy(&x, rmt->ipv6.sin6_addr.s6_addr+12, sizeof(in_addr_t));
261 #endif
262 return ((val.ipv4.sin_addr.s_addr & nm) == (x & nm));
263 #endif
264 } else {
265 return 0;
267 #if defined(HAVE_INET_PTON) && defined(HAVE_IPV6)
268 } else if (1 == inet_pton(AF_INET6, addrstr, &val.ipv6.sin6_addr)) {
269 if (nm_bits > 128) {
270 log_error_write(srv, __FILE__, __LINE__, "sd", "ERROR: ipv6 netmask too large:", nm_bits);
271 return -1;
273 if (rmt->plain.sa_family == AF_INET6) {
274 uint8_t *a = (uint8_t *)&val.ipv6.sin6_addr.s6_addr[0];
275 uint8_t *b = (uint8_t *)&rmt->ipv6.sin6_addr.s6_addr[0];
276 int match;
277 do {
278 match = (nm_bits >= 8)
279 ? *a++ == *b++
280 : (*a >> (8 - nm_bits)) == (*b >> (8 - nm_bits));
281 } while (match && (nm_bits -= 8) > 0);
282 return match;
283 } else if (rmt->plain.sa_family == AF_INET
284 && IN6_IS_ADDR_V4MAPPED(&val.ipv6.sin6_addr)) {
285 uint32_t nm =
286 nm_bits < 128 ? htonl(~(~0u >> (nm_bits > 96 ? nm_bits - 96 : 0))) : ~0u;
287 #ifdef s6_addr32
288 in_addr_t x = val.ipv6.sin6_addr.s6_addr32[3];
289 #else
290 in_addr_t x;
291 memcpy(&x, val.ipv6.sin6_addr.s6_addr+12, sizeof(in_addr_t));
292 #endif
293 return ((x & nm) == (rmt->ipv4.sin_addr.s_addr & nm));
294 } else {
295 return 0;
297 #endif
298 } else {
299 log_error_write(srv, __FILE__, __LINE__, "ss", "ERROR: ip addr is invalid:", addrstr);
300 return -1;
304 static int config_addrbuf_eq_remote_ip_mask(server *srv, buffer *string, char *nm_slash, sock_addr *rmt) {
305 char *err;
306 int nm_bits = strtol(nm_slash + 1, &err, 10);
307 size_t addrstrlen = (size_t)(nm_slash - string->ptr);
308 char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/
310 if (*err) {
311 log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", string, err);
312 return -1;
315 if (nm_bits <= 0) {
316 if (*(nm_slash+1) == '\0') {
317 log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", string);
318 } else {
319 log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask <= 0:", string, err);
321 return -1;
324 if (addrstrlen >= sizeof(addrstr)) {
325 log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: address string too long:", string);
326 return -1;
329 memcpy(addrstr, string->ptr, addrstrlen);
330 addrstr[addrstrlen] = '\0';
332 return config_addrstr_eq_remote_ip_mask(srv, addrstr, nm_bits, rmt);
335 static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc);
337 static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) {
338 buffer *l;
339 server_socket *srv_sock = con->srv_socket;
340 cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
342 /* check parent first */
343 if (dc->parent && dc->parent->context_ndx) {
345 * a nested conditional
347 * if the parent is not decided yet or false, we can't be true either
349 if (con->conf.log_condition_handling) {
350 log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->key);
353 switch (config_check_cond_cached(srv, con, dc->parent)) {
354 case COND_RESULT_UNSET:
355 /* decide later */
356 return COND_RESULT_UNSET;
357 case COND_RESULT_SKIP:
358 case COND_RESULT_FALSE:
359 /* failed precondition */
360 return COND_RESULT_SKIP;
361 case COND_RESULT_TRUE:
362 /* proceed */
363 break;
367 if (dc->prev) {
369 * a else branch; can only be executed if the previous branch
370 * was evaluated as "false" (not unset/skipped/true)
372 if (con->conf.log_condition_handling) {
373 log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->key);
376 /* make sure prev is checked first */
377 switch (config_check_cond_cached(srv, con, dc->prev)) {
378 case COND_RESULT_UNSET:
379 /* decide later */
380 return COND_RESULT_UNSET;
381 case COND_RESULT_SKIP:
382 case COND_RESULT_TRUE:
383 /* failed precondition */
384 return COND_RESULT_SKIP;
385 case COND_RESULT_FALSE:
386 /* proceed */
387 break;
391 if (!con->conditional_is_valid[dc->comp]) {
392 if (con->conf.log_condition_handling) {
393 log_error_write(srv, __FILE__, __LINE__, "dss",
394 dc->comp,
395 dc->key->ptr,
396 "not available yet");
399 return COND_RESULT_UNSET;
402 /* if we had a real result before and weren't cleared just return it */
403 switch (cache->local_result) {
404 case COND_RESULT_TRUE:
405 case COND_RESULT_FALSE:
406 return cache->local_result;
407 default:
408 break;
411 if (CONFIG_COND_ELSE == dc->cond) return COND_RESULT_TRUE;
413 /* pass the rules */
415 switch (dc->comp) {
416 case COMP_HTTP_HOST: {
417 char *ck_colon = NULL, *val_colon = NULL;
419 if (!buffer_string_is_empty(con->uri.authority)) {
422 * append server-port to the HTTP_POST if necessary
425 l = con->uri.authority;
427 switch(dc->cond) {
428 case CONFIG_COND_NE:
429 case CONFIG_COND_EQ:
430 ck_colon = strchr(dc->string->ptr, ':');
431 val_colon = strchr(l->ptr, ':');
433 if (NULL != ck_colon && NULL == val_colon) {
434 /* condition "host:port" but client send "host" */
435 buffer_copy_buffer(srv->cond_check_buf, l);
436 buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":"));
437 buffer_append_int(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr)));
438 l = srv->cond_check_buf;
439 } else if (NULL != val_colon && NULL == ck_colon) {
440 /* condition "host" but client send "host:port" */
441 buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr);
442 l = srv->cond_check_buf;
444 break;
445 default:
446 break;
448 #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
449 } else if (!buffer_string_is_empty(con->tlsext_server_name)) {
450 l = con->tlsext_server_name;
451 #endif
452 } else {
453 l = srv->empty_string;
455 break;
457 case COMP_HTTP_REMOTE_IP: {
458 char *nm_slash;
459 /* handle remoteip limitations
461 * "10.0.0.1" is provided for all comparisions
463 * only for == and != we support
465 * "10.0.0.1/24"
468 if ((dc->cond == CONFIG_COND_EQ ||
469 dc->cond == CONFIG_COND_NE) &&
470 (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) {
471 switch (config_addrbuf_eq_remote_ip_mask(srv, dc->string, nm_slash, &con->dst_addr)) {
472 case 1: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
473 case 0: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
474 case -1: return COND_RESULT_FALSE; /*(error parsing configfile entry)*/
477 l = con->dst_addr_buf;
478 break;
480 case COMP_HTTP_SCHEME:
481 l = con->uri.scheme;
482 break;
484 case COMP_HTTP_URL:
485 l = con->uri.path;
486 break;
488 case COMP_HTTP_QUERY_STRING:
489 l = con->uri.query;
490 break;
492 case COMP_SERVER_SOCKET:
493 l = srv_sock->srv_token;
494 break;
496 case COMP_HTTP_REFERER: {
497 data_string *ds;
499 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) {
500 l = ds->value;
501 } else {
502 l = srv->empty_string;
504 break;
506 case COMP_HTTP_COOKIE: {
507 data_string *ds;
508 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
509 l = ds->value;
510 } else {
511 l = srv->empty_string;
513 break;
515 case COMP_HTTP_USER_AGENT: {
516 data_string *ds;
517 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) {
518 l = ds->value;
519 } else {
520 l = srv->empty_string;
522 break;
524 case COMP_HTTP_REQUEST_METHOD: {
525 const char *method = get_http_method_name(con->request.http_method);
527 /* we only have the request method as const char but we need a buffer for comparing */
529 buffer_copy_string(srv->tmp_buf, method);
531 l = srv->tmp_buf;
533 break;
535 case COMP_HTTP_LANGUAGE: {
536 data_string *ds;
537 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Language"))) {
538 l = ds->value;
539 } else {
540 l = srv->empty_string;
542 break;
544 default:
545 return COND_RESULT_FALSE;
548 if (NULL == l) {
549 if (con->conf.log_condition_handling) {
550 log_error_write(srv, __FILE__, __LINE__, "bsbs", dc->comp_key,
551 "(", l, ") compare to NULL");
553 return COND_RESULT_FALSE;
556 if (con->conf.log_condition_handling) {
557 log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key,
558 "(", l, ") compare to ", dc->string);
560 switch(dc->cond) {
561 case CONFIG_COND_NE:
562 case CONFIG_COND_EQ:
563 if (buffer_is_equal(l, dc->string)) {
564 return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
565 } else {
566 return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
568 break;
569 #ifdef HAVE_PCRE_H
570 case CONFIG_COND_NOMATCH:
571 case CONFIG_COND_MATCH: {
572 int n;
574 #ifndef elementsof
575 #define elementsof(x) (sizeof(x) / sizeof(x[0]))
576 #endif
577 n = pcre_exec(dc->regex, dc->regex_study, CONST_BUF_LEN(l), 0, 0,
578 cache->matches, elementsof(cache->matches));
580 cache->patterncount = n;
581 if (n > 0) {
582 cache->comp_value = l;
583 return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
584 } else {
585 /* cache is already cleared */
586 return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
588 break;
590 #endif
591 default:
592 /* no way */
593 break;
596 return COND_RESULT_FALSE;
599 static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc) {
600 cond_cache_t *caches = con->cond_cache;
602 if (COND_RESULT_UNSET == caches[dc->context_ndx].result) {
603 caches[dc->context_ndx].result = config_check_cond_nocache(srv, con, dc);
604 switch (caches[dc->context_ndx].result) {
605 case COND_RESULT_FALSE:
606 case COND_RESULT_TRUE:
607 /* remember result of local condition for a partial reset */
608 caches[dc->context_ndx].local_result = caches[dc->context_ndx].result;
609 break;
610 default:
611 break;
614 if (con->conf.log_condition_handling) {
615 log_error_write(srv, __FILE__, __LINE__, "dss",
616 dc->context_ndx,
617 "(uncached) result:",
618 cond_result_to_string(caches[dc->context_ndx].result));
620 } else {
621 if (con->conf.log_condition_handling) {
622 log_error_write(srv, __FILE__, __LINE__, "dss",
623 dc->context_ndx,
624 "(cached) result:",
625 cond_result_to_string(caches[dc->context_ndx].result));
628 return caches[dc->context_ndx].result;
631 /* if we reset the cache result for a node, we also need to clear all
632 * child nodes and else-branches*/
633 static void config_cond_clear_node(server *srv, connection *con, data_config *dc) {
634 /* if a node is "unset" all children are unset too */
635 if (con->cond_cache[dc->context_ndx].result != COND_RESULT_UNSET) {
636 size_t i;
638 con->cond_cache[dc->context_ndx].patterncount = 0;
639 con->cond_cache[dc->context_ndx].comp_value = NULL;
640 con->cond_cache[dc->context_ndx].result = COND_RESULT_UNSET;
642 for (i = 0; i < dc->children.used; ++i) {
643 data_config *dc_child = dc->children.data[i];
644 if (NULL == dc_child->prev) {
645 /* only call for first node in if-else chain */
646 config_cond_clear_node(srv, con, dc_child);
649 if (NULL != dc->next) config_cond_clear_node(srv, con, dc->next);
654 * reset the config-cache for a named item
656 * if the item is COND_LAST_ELEMENT we reset all items
658 void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item) {
659 size_t i;
661 for (i = 0; i < srv->config_context->used; i++) {
662 data_config *dc = (data_config *)srv->config_context->data[i];
664 if (item == dc->comp) {
665 /* clear local_result */
666 con->cond_cache[i].local_result = COND_RESULT_UNSET;
667 /* clear result in subtree (including the node itself) */
668 config_cond_clear_node(srv, con, dc);
674 * reset the config cache to its initial state at connection start
676 void config_cond_cache_reset(server *srv, connection *con) {
677 size_t i;
679 /* resetting all entries; no need to follow children as in config_cond_cache_reset_item */
680 for (i = 0; i < srv->config_context->used; i++) {
681 con->cond_cache[i].result = COND_RESULT_UNSET;
682 con->cond_cache[i].local_result = COND_RESULT_UNSET;
683 con->cond_cache[i].patterncount = 0;
684 con->cond_cache[i].comp_value = NULL;
687 for (i = 0; i < COMP_LAST_ELEMENT; i++) {
688 con->conditional_is_valid[i] = 0;
692 int config_check_cond(server *srv, connection *con, data_config *dc) {
693 if (con->conf.log_condition_handling) {
694 log_error_write(srv, __FILE__, __LINE__, "s", "=== start of condition block ===");
696 return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE);
699 int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n)
701 cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
702 if (n >= cache->patterncount) {
703 return 0;
706 n <<= 1; /* n *= 2 */
707 buffer_append_string_len(buf,
708 cache->comp_value->ptr + cache->matches[n],
709 cache->matches[n + 1] - cache->matches[n]);
710 return 1;