[core] fix s6_addr type-punned compiler warning
[lighttpd.git] / src / configfile-glue.c
blob1e62b62696720c2225073a93b68d27a195b4f43b
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 /* pass the rules */
413 switch (dc->comp) {
414 case COMP_HTTP_HOST: {
415 char *ck_colon = NULL, *val_colon = NULL;
417 if (!buffer_string_is_empty(con->uri.authority)) {
420 * append server-port to the HTTP_POST if necessary
423 l = con->uri.authority;
425 switch(dc->cond) {
426 case CONFIG_COND_NE:
427 case CONFIG_COND_EQ:
428 ck_colon = strchr(dc->string->ptr, ':');
429 val_colon = strchr(l->ptr, ':');
431 if (NULL != ck_colon && NULL == val_colon) {
432 /* condition "host:port" but client send "host" */
433 buffer_copy_buffer(srv->cond_check_buf, l);
434 buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":"));
435 buffer_append_int(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr)));
436 l = srv->cond_check_buf;
437 } else if (NULL != val_colon && NULL == ck_colon) {
438 /* condition "host" but client send "host:port" */
439 buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr);
440 l = srv->cond_check_buf;
442 break;
443 default:
444 break;
446 #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
447 } else if (!buffer_string_is_empty(con->tlsext_server_name)) {
448 l = con->tlsext_server_name;
449 #endif
450 } else {
451 l = srv->empty_string;
453 break;
455 case COMP_HTTP_REMOTE_IP: {
456 char *nm_slash;
457 /* handle remoteip limitations
459 * "10.0.0.1" is provided for all comparisions
461 * only for == and != we support
463 * "10.0.0.1/24"
466 if ((dc->cond == CONFIG_COND_EQ ||
467 dc->cond == CONFIG_COND_NE) &&
468 (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) {
469 switch (config_addrbuf_eq_remote_ip_mask(srv, dc->string, nm_slash, &con->dst_addr)) {
470 case 1: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
471 case 0: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
472 case -1: return COND_RESULT_FALSE; /*(error parsing configfile entry)*/
475 l = con->dst_addr_buf;
476 break;
478 case COMP_HTTP_SCHEME:
479 l = con->uri.scheme;
480 break;
482 case COMP_HTTP_URL:
483 l = con->uri.path;
484 break;
486 case COMP_HTTP_QUERY_STRING:
487 l = con->uri.query;
488 break;
490 case COMP_SERVER_SOCKET:
491 l = srv_sock->srv_token;
492 break;
494 case COMP_HTTP_REFERER: {
495 data_string *ds;
497 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) {
498 l = ds->value;
499 } else {
500 l = srv->empty_string;
502 break;
504 case COMP_HTTP_COOKIE: {
505 data_string *ds;
506 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
507 l = ds->value;
508 } else {
509 l = srv->empty_string;
511 break;
513 case COMP_HTTP_USER_AGENT: {
514 data_string *ds;
515 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) {
516 l = ds->value;
517 } else {
518 l = srv->empty_string;
520 break;
522 case COMP_HTTP_REQUEST_METHOD: {
523 const char *method = get_http_method_name(con->request.http_method);
525 /* we only have the request method as const char but we need a buffer for comparing */
527 buffer_copy_string(srv->tmp_buf, method);
529 l = srv->tmp_buf;
531 break;
533 case COMP_HTTP_LANGUAGE: {
534 data_string *ds;
535 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Language"))) {
536 l = ds->value;
537 } else {
538 l = srv->empty_string;
540 break;
542 default:
543 return COND_RESULT_FALSE;
546 if (NULL == l) {
547 if (con->conf.log_condition_handling) {
548 log_error_write(srv, __FILE__, __LINE__, "bsbs", dc->comp_key,
549 "(", l, ") compare to NULL");
551 return COND_RESULT_FALSE;
554 if (con->conf.log_condition_handling) {
555 log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key,
556 "(", l, ") compare to ", dc->string);
558 switch(dc->cond) {
559 case CONFIG_COND_NE:
560 case CONFIG_COND_EQ:
561 if (buffer_is_equal(l, dc->string)) {
562 return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
563 } else {
564 return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
566 break;
567 #ifdef HAVE_PCRE_H
568 case CONFIG_COND_NOMATCH:
569 case CONFIG_COND_MATCH: {
570 int n;
572 #ifndef elementsof
573 #define elementsof(x) (sizeof(x) / sizeof(x[0]))
574 #endif
575 n = pcre_exec(dc->regex, dc->regex_study, CONST_BUF_LEN(l), 0, 0,
576 cache->matches, elementsof(cache->matches));
578 cache->patterncount = n;
579 if (n > 0) {
580 cache->comp_value = l;
581 return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
582 } else {
583 /* cache is already cleared */
584 return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
586 break;
588 #endif
589 default:
590 /* no way */
591 break;
594 return COND_RESULT_FALSE;
597 static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc) {
598 cond_cache_t *caches = con->cond_cache;
600 if (COND_RESULT_UNSET == caches[dc->context_ndx].result) {
601 caches[dc->context_ndx].result = config_check_cond_nocache(srv, con, dc);
602 switch (caches[dc->context_ndx].result) {
603 case COND_RESULT_FALSE:
604 case COND_RESULT_TRUE:
605 /* remember result of local condition for a partial reset */
606 caches[dc->context_ndx].local_result = caches[dc->context_ndx].result;
607 break;
608 default:
609 break;
612 if (con->conf.log_condition_handling) {
613 log_error_write(srv, __FILE__, __LINE__, "dss",
614 dc->context_ndx,
615 "(uncached) result:",
616 cond_result_to_string(caches[dc->context_ndx].result));
618 } else {
619 if (con->conf.log_condition_handling) {
620 log_error_write(srv, __FILE__, __LINE__, "dss",
621 dc->context_ndx,
622 "(cached) result:",
623 cond_result_to_string(caches[dc->context_ndx].result));
626 return caches[dc->context_ndx].result;
629 /* if we reset the cache result for a node, we also need to clear all
630 * child nodes and else-branches*/
631 static void config_cond_clear_node(server *srv, connection *con, data_config *dc) {
632 /* if a node is "unset" all children are unset too */
633 if (con->cond_cache[dc->context_ndx].result != COND_RESULT_UNSET) {
634 size_t i;
636 con->cond_cache[dc->context_ndx].patterncount = 0;
637 con->cond_cache[dc->context_ndx].comp_value = NULL;
638 con->cond_cache[dc->context_ndx].result = COND_RESULT_UNSET;
640 for (i = 0; i < dc->children.used; ++i) {
641 data_config *dc_child = dc->children.data[i];
642 if (NULL == dc_child->prev) {
643 /* only call for first node in if-else chain */
644 config_cond_clear_node(srv, con, dc_child);
647 if (NULL != dc->next) config_cond_clear_node(srv, con, dc->next);
652 * reset the config-cache for a named item
654 * if the item is COND_LAST_ELEMENT we reset all items
656 void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item) {
657 size_t i;
659 for (i = 0; i < srv->config_context->used; i++) {
660 data_config *dc = (data_config *)srv->config_context->data[i];
662 if (item == dc->comp) {
663 /* clear local_result */
664 con->cond_cache[i].local_result = COND_RESULT_UNSET;
665 /* clear result in subtree (including the node itself) */
666 config_cond_clear_node(srv, con, dc);
672 * reset the config cache to its initial state at connection start
674 void config_cond_cache_reset(server *srv, connection *con) {
675 size_t i;
677 /* resetting all entries; no need to follow children as in config_cond_cache_reset_item */
678 for (i = 0; i < srv->config_context->used; i++) {
679 con->cond_cache[i].result = COND_RESULT_UNSET;
680 con->cond_cache[i].local_result = COND_RESULT_UNSET;
681 con->cond_cache[i].patterncount = 0;
682 con->cond_cache[i].comp_value = NULL;
685 for (i = 0; i < COMP_LAST_ELEMENT; i++) {
686 con->conditional_is_valid[i] = 0;
690 int config_check_cond(server *srv, connection *con, data_config *dc) {
691 if (con->conf.log_condition_handling) {
692 log_error_write(srv, __FILE__, __LINE__, "s", "=== start of condition block ===");
694 return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE);
697 int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n)
699 cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
700 if (n >= cache->patterncount) {
701 return 0;
704 n <<= 1; /* n *= 2 */
705 buffer_append_string_len(buf,
706 cache->comp_value->ptr + cache->matches[n],
707 cache->matches[n + 1] - cache->matches[n]);
708 return 1;