1 #include "first.h" 2 3 #include "base.h" 4 #include "burl.h" 5 #include "chunk.h" 6 #include "ck.h" 7 #include "fdevent.h" 8 #include "fdlog.h" 9 #include "http_etag.h" 10 #include "keyvalue.h" 11 #include "log.h" 12 13 #include "configparser.h" 14 #include "configfile.h" 15 #include "plugin.h" 16 #include "reqpool.h" 17 #include "sock_addr.h" 18 #include "stat_cache.h" 19 #include "sys-crypto.h" 20 21 #include <sys/stat.h> 22 #ifdef HAVE_SYS_WAIT_H 23 #include <sys/wait.h> 24 #endif 25 26 #include <stdlib.h> 27 #include <fcntl.h> 28 #include <unistd.h> 29 #include <errno.h> 30 #include <string.h> 31 #include <ctype.h> 32 #include <limits.h> 33 #include <glob.h> 34 35 #ifdef HAVE_SYSLOG_H 36 # include <syslog.h> 37 #endif 38 39 #ifdef HAVE_PCRE2_H 40 #define PCRE2_CODE_UNIT_WIDTH 8 41 #include <pcre2.h> 42 #endif 43 44 #ifndef PATH_MAX 45 #define PATH_MAX 4096 46 #endif 47 48 typedef struct { 49 PLUGIN_DATA; 50 request_config defaults; 51 } config_data_base; 52 53 static void config_free_config(void * const p_d) { 54 plugin_data_base * const p = p_d; 55 if (NULL == p) return; 56 if (NULL == p->cvlist) { free(p); return; } 57 /* (init i to 0 if global context; to 1 to skip empty global context) */ 58 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 59 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 60 for (; -1 != cpv->k_id; ++cpv) { 61 switch (cpv->k_id) { 62 case 18:/* server.kbytes-per-second */ 63 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); 64 break; 65 default: 66 break; 67 } 68 } 69 } 70 free(p->cvlist); 71 free(p); 72 } 73 74 void config_reset_config_bytes_sec(void * const p_d) { 75 plugin_data_base * const p = p_d; 76 if (NULL == p->cvlist) return; 77 /* (init i to 0 if global context; to 1 to skip empty global context) */ 78 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 79 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 80 for (; -1 != cpv->k_id; ++cpv) { 81 switch (cpv->k_id) { 82 case 18:/* server.kbytes-per-second */ 83 if (cpv->vtype == T_CONFIG_LOCAL) ((off_t *)cpv->v.v)[0] = 0; 84 break; 85 default: 86 break; 87 } 88 } 89 } 90 } 91 92 static void config_merge_config_cpv(request_config * const pconf, const config_plugin_value_t * const cpv) { 93 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 94 case 0: /* server.document-root */ 95 pconf->document_root = cpv->v.b; 96 break; 97 case 1: /* server.name */ 98 pconf->server_name = cpv->v.b; 99 break; 100 case 2: /* server.tag */ 101 pconf->server_tag = cpv->v.b; 102 break; 103 case 3: /* server.max-request-size */ 104 pconf->max_request_size = cpv->v.u; 105 break; 106 case 4: /* server.max-keep-alive-requests */ 107 pconf->max_keep_alive_requests = cpv->v.shrt; 108 break; 109 case 5: /* server.max-keep-alive-idle */ 110 pconf->max_keep_alive_idle = cpv->v.shrt; 111 break; 112 case 6: /* server.max-read-idle */ 113 pconf->max_read_idle = cpv->v.shrt; 114 break; 115 case 7: /* server.max-write-idle */ 116 pconf->max_write_idle = cpv->v.shrt; 117 break; 118 case 8: /* server.errorfile-prefix */ 119 pconf->errorfile_prefix = cpv->v.b; 120 break; 121 case 9: /* server.error-handler */ 122 pconf->error_handler = cpv->v.b; 123 break; 124 case 10:/* server.error-handler-404 */ 125 pconf->error_handler_404 = cpv->v.b; 126 break; 127 case 11:/* server.error-intercept */ 128 pconf->error_intercept = (0 != cpv->v.u); 129 break; 130 case 12:/* server.force-lowercase-filenames */ 131 pconf->force_lowercase_filenames = (0 != cpv->v.u); 132 break; 133 case 13:/* server.follow-symlink */ 134 pconf->follow_symlink = (0 != cpv->v.u); 135 break; 136 case 14:/* server.protocol-http11 */ 137 pconf->allow_http11 = (0 != cpv->v.u); 138 break; 139 case 15:/* server.range-requests */ 140 pconf->range_requests = (0 != cpv->v.u); 141 break; 142 case 16:/* server.stream-request-body */ 143 pconf->stream_request_body = cpv->v.shrt; 144 break; 145 case 17:/* server.stream-response-body */ 146 pconf->stream_response_body = cpv->v.shrt; 147 break; 148 case 18:/* server.kbytes-per-second */ 149 pconf->global_bytes_per_second = (unsigned int)((off_t *)cpv->v.v)[1]; 150 pconf->global_bytes_per_second_cnt_ptr = cpv->v.v; 151 break; 152 case 19:/* connection.kbytes-per-second */ 153 pconf->bytes_per_second = (unsigned int)cpv->v.shrt << 10;/* (*=1024) */ 154 break; 155 case 20:/* mimetype.assign */ 156 pconf->mimetypes = cpv->v.a; 157 break; 158 case 21:/* mimetype.use-xattr */ 159 pconf->use_xattr = (0 != cpv->v.u); 160 break; 161 case 22:/* etag.use-inode */ 162 cpv->v.u 163 ? (pconf->etag_flags |= ETAG_USE_INODE) 164 : (pconf->etag_flags &= ~ETAG_USE_INODE); 165 break; 166 case 23:/* etag.use-mtime */ 167 cpv->v.u 168 ? (pconf->etag_flags |= ETAG_USE_MTIME) 169 : (pconf->etag_flags &= ~ETAG_USE_MTIME); 170 break; 171 case 24:/* etag.use-size */ 172 cpv->v.u 173 ? (pconf->etag_flags |= ETAG_USE_SIZE) 174 : (pconf->etag_flags &= ~ETAG_USE_SIZE); 175 break; 176 case 25:/* debug.log-condition-handling */ 177 pconf->log_condition_handling = (0 != cpv->v.u); 178 break; 179 case 26:/* debug.log-file-not-found */ 180 pconf->log_file_not_found = (0 != cpv->v.u); 181 break; 182 case 27:/* debug.log-request-handling */ 183 pconf->log_request_handling = (0 != cpv->v.u); 184 break; 185 case 28:/* debug.log-request-header */ 186 pconf->log_request_header = (0 != cpv->v.u); 187 break; 188 case 29:/* debug.log-response-header */ 189 pconf->log_response_header = (0 != cpv->v.u); 190 break; 191 case 30:/* debug.log-timeouts */ 192 pconf->log_timeouts = (0 != cpv->v.u); 193 break; 194 case 31:/* debug.log-state-handling */ 195 pconf->log_state_handling = (0 != cpv->v.u); 196 break; 197 case 32:/* server.errorlog */ 198 if (cpv->vtype == T_CONFIG_LOCAL) pconf->errh = cpv->v.v; 199 break; 200 case 33:/* server.breakagelog */ 201 if (cpv->vtype == T_CONFIG_LOCAL) pconf->serrh = cpv->v.v; 202 break; 203 default:/* should not happen */ 204 return; 205 } 206 } 207 208 static void config_merge_config(request_config * const pconf, const config_plugin_value_t *cpv) { 209 do { 210 config_merge_config_cpv(pconf, cpv); 211 } while ((++cpv)->k_id != -1); 212 } 213 214 void config_patch_config(request_st * const r) { 215 config_data_base * const p = r->con->config_data_base; 216 217 /* performed by request_config_reset() */ 218 /*memcpy(&r->conf, &p->defaults, sizeof(request_config));*/ 219 220 for (int i = 1, used = p->nconfig; i < used; ++i) { 221 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 222 config_merge_config(&r->conf, p->cvlist + p->cvlist[i].v.u2[0]); 223 } 224 } 225 226 #if 0 /*(moved to reqpool.c:request_config_reset())*/ 227 void config_reset_config(request_st * const r) { 228 /* initialize request_config (r->conf) from top-level request_config */ 229 config_data_base * const p = r->con->config_data_base; 230 memcpy(&r->conf, &p->defaults, sizeof(request_config)); 231 } 232 #endif 233 234 static void config_burl_normalize_cond (server * const srv) { 235 buffer * const tb = srv->tmp_buf; 236 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 237 data_config * const config =(data_config *)srv->config_context->data[i]; 238 if (COMP_HTTP_QUERY_STRING != config->comp) continue; 239 switch(config->cond) { 240 case CONFIG_COND_NE: 241 case CONFIG_COND_EQ: 242 /* (can use this routine as long as it does not perform 243 * any regex-specific normalization of first arg) */ 244 pcre_keyvalue_burl_normalize_key(&config->string, tb); 245 break; 246 case CONFIG_COND_NOMATCH: 247 case CONFIG_COND_MATCH: 248 pcre_keyvalue_burl_normalize_key(&config->string, tb); 249 break; 250 default: 251 break; 252 } 253 } 254 } 255 256 static int config_pcre_keyvalue (server * const srv) { 257 const int pcre_jit = config_feature_bool(srv, "server.pcre_jit", 1); 258 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 259 data_config * const dc = (data_config *)srv->config_context->data[i]; 260 if (dc->cond != CONFIG_COND_NOMATCH && dc->cond != CONFIG_COND_MATCH) 261 continue; 262 if (!data_config_pcre_compile(dc, pcre_jit, srv->errh)) 263 return 0; 264 } 265 266 return 1; 267 } 268 269 #ifdef USE_OPENSSL_CRYPTO 270 static void config_warn_openssl_module (server *srv) { 271 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 272 const data_config *config = (data_config const*)srv->config_context->data[i]; 273 for (uint32_t j = 0; j < config->value->used; ++j) { 274 data_unset *du = config->value->data[j]; 275 if (0 == strncmp(du->key.ptr, "ssl.", sizeof("ssl.")-1)) { 276 /* mod_openssl should be loaded after mod_extforward */ 277 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_openssl")); 278 log_error(srv->errh, __FILE__, __LINE__, 279 "Warning: please add \"mod_openssl\" to server.modules list " 280 "in lighttpd.conf. A future release of lighttpd 1.4.x " 281 "*will not* automatically load mod_openssl and lighttpd " 282 "*will not* use SSL/TLS where your lighttpd.conf contains " 283 "ssl.* directives"); 284 return; 285 } 286 } 287 } 288 } 289 #endif 290 291 static void config_check_module_duplicates (server *srv) { 292 int dups = 0; 293 data_string ** const data = (data_string **)srv->srvconf.modules->data; 294 const uint32_t used = srv->srvconf.modules->used; 295 for (uint32_t i = 0; i < used; ++i) { 296 const buffer * const m = &data[i]->value; 297 for (uint32_t j = i+1; j < used; ++j) { 298 if (buffer_is_equal(m, &data[j]->value)) { 299 ++dups; 300 break; 301 } 302 } 303 } 304 if (!dups) return; 305 306 array * const modules = array_init(used - dups); 307 for (uint32_t i = 0; i < used; ++i) { 308 const buffer * const m = &data[i]->value; 309 uint32_t j; 310 for (j = 0; j < modules->used; ++j) { 311 buffer *n = &((data_string *)modules->data[j])->value; 312 if (buffer_is_equal(m, n)) break; /* duplicate */ 313 } 314 if (j == modules->used) 315 array_insert_value(modules, BUF_PTR_LEN(m)); 316 } 317 array_free(srv->srvconf.modules); 318 srv->srvconf.modules = modules; 319 } 320 321 __attribute_pure__ 322 static const char * config_has_opt_and_value (const server * const srv, const char * const opt, const uint32_t olen, const char * const v, const uint32_t vlen) { 323 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 324 const data_config * const config = 325 (data_config const *)srv->config_context->data[i]; 326 const data_string * const ds = 327 (data_string *)array_get_element_klen(config->value, opt, olen); 328 if (NULL != ds && ds->type == TYPE_STRING 329 && buffer_eq_slen(&ds->value, v, vlen)) 330 return v; 331 } 332 return NULL; 333 } 334 335 static void config_compat_module_prepend (server *srv, const char *module, uint32_t len) { 336 array *modules = array_init(srv->srvconf.modules->used+4); 337 array_insert_value(modules, module, len); 338 339 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 340 data_string *ds = (data_string *)srv->srvconf.modules->data[i]; 341 array_insert_value(modules, BUF_PTR_LEN(&ds->value)); 342 } 343 344 array_free(srv->srvconf.modules); 345 srv->srvconf.modules = modules; 346 } 347 348 static void config_warn_authn_module (server *srv, const char *module, uint32_t len, const char *v) { 349 buffer * const tb = srv->tmp_buf; 350 buffer_copy_string_len(tb, CONST_STR_LEN("mod_authn_")); 351 buffer_append_string_len(tb, module, len); 352 array_insert_value(srv->srvconf.modules, BUF_PTR_LEN(tb)); 353 log_error(srv->errh, __FILE__, __LINE__, 354 "Warning: please add \"mod_authn_%s\" to server.modules list " 355 "in lighttpd.conf. A future release of lighttpd 1.4.x will " 356 "not automatically load mod_authn_%s and lighttpd will fail " 357 "to start up since your lighttpd.conf uses auth.backend = \"%s\".", 358 module, module, v); 359 } 360 361 static void config_compat_module_load (server *srv) { 362 int prepend_mod_indexfile = 1; 363 int append_mod_dirlisting = 1; 364 int append_mod_staticfile = 1; 365 int append_mod_authn_file = 1; 366 int append_mod_authn_ldap = 1; 367 int append_mod_authn_mysql = 1; 368 int append_mod_openssl = 1; 369 int contains_mod_auth = 0; 370 int prepend_mod_auth = 0; 371 int prepend_mod_vhostdb = 0; 372 const char *dyn_name = NULL; 373 374 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 375 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; 376 377 if (buffer_eq_slen(m, CONST_STR_LEN("mod_indexfile"))) 378 prepend_mod_indexfile = 0; 379 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_staticfile"))) 380 append_mod_staticfile = 0; 381 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_dirlisting"))) 382 append_mod_dirlisting = 0; 383 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_gnutls"))) 384 append_mod_openssl = 0; 385 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_mbedtls"))) 386 append_mod_openssl = 0; 387 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_nss"))) 388 append_mod_openssl = 0; 389 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_openssl"))) 390 append_mod_openssl = 0; 391 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_wolfssl"))) 392 append_mod_openssl = 0; 393 else if (0 == strncmp(m->ptr, "mod_auth", sizeof("mod_auth")-1)) { 394 if (buffer_eq_slen(m, CONST_STR_LEN("mod_auth"))) { 395 if (!contains_mod_auth) { 396 contains_mod_auth = 1; 397 if (dyn_name) 398 log_error(srv->errh, __FILE__, __LINE__, 399 "Warning: mod_auth should be listed in server.modules" 400 " before dynamic backends such as %s", dyn_name); 401 } 402 } 403 else if (!contains_mod_auth) 404 prepend_mod_auth = 1; 405 406 if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_file"))) 407 append_mod_authn_file = 0; 408 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_ldap"))) 409 append_mod_authn_ldap = 0; 410 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_mysql"))) 411 append_mod_authn_mysql = 0; 412 } 413 else if (0 == strncmp(m->ptr, "mod_vhostdb", sizeof("mod_vhostdb")-1)) { 414 if (buffer_eq_slen(m, CONST_STR_LEN("mod_vhostdb"))) 415 prepend_mod_vhostdb |= 2; 416 else if (!(prepend_mod_vhostdb & 2)) 417 prepend_mod_vhostdb |= 1; 418 } 419 else if ( 0 == strncmp(m->ptr, "mod_ajp13", 420 sizeof("mod_ajp13")-1) 421 || 0 == strncmp(m->ptr, "mod_cgi", 422 sizeof("mod_cgi")-1) 423 || 0 == strncmp(m->ptr, "mod_fastcgi", 424 sizeof("mod_fastcgi")-1) 425 || 0 == strncmp(m->ptr, "mod_proxy", 426 sizeof("mod_proxy")-1) 427 || 0 == strncmp(m->ptr, "mod_scgi", 428 sizeof("mod_scgi")-1) 429 || 0 == strncmp(m->ptr, "mod_sockproxy", 430 sizeof("mod_sockproxy")-1) 431 || 0 == strncmp(m->ptr, "mod_wstunnel", 432 sizeof("mod_wstunnel")-1)) { 433 if (NULL == dyn_name) 434 dyn_name = m->ptr; 435 } 436 } 437 438 /* prepend default modules */ 439 440 if (prepend_mod_indexfile) { 441 /* mod_indexfile has to be loaded before mod_fastcgi and friends */ 442 config_compat_module_prepend(srv, CONST_STR_LEN("mod_indexfile")); 443 } 444 445 /* append default modules */ 446 447 if (append_mod_dirlisting) { 448 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting")); 449 } 450 451 if (append_mod_staticfile) { 452 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile")); 453 } 454 455 if (append_mod_openssl) { 456 #ifdef USE_OPENSSL_CRYPTO 457 config_warn_openssl_module(srv); 458 #endif 459 } 460 461 /* mod_auth.c,mod_auth_api.c auth backends were split into separate modules 462 * Automatically load auth backend modules for compatibility with 463 * existing lighttpd 1.4.x configs */ 464 if (contains_mod_auth) { 465 if (append_mod_authn_file) { 466 const char *v; 467 if ( (v=config_has_opt_and_value(srv,CONST_STR_LEN("auth.backend"), 468 CONST_STR_LEN("htdigest"))) 469 ||(v=config_has_opt_and_value(srv,CONST_STR_LEN("auth.backend"), 470 CONST_STR_LEN("htpasswd"))) 471 ||(v=config_has_opt_and_value(srv,CONST_STR_LEN("auth.backend"), 472 CONST_STR_LEN("plain")))) 473 config_warn_authn_module(srv, CONST_STR_LEN("file"), v); 474 } 475 if (append_mod_authn_ldap) { 476 #if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) 477 if (config_has_opt_and_value(srv, CONST_STR_LEN("auth.backend"), 478 CONST_STR_LEN("ldap"))) 479 config_warn_authn_module(srv, CONST_STR_LEN("ldap"), "ldap"); 480 #endif 481 } 482 if (append_mod_authn_mysql) { 483 #if defined(HAVE_MYSQL) 484 if (config_has_opt_and_value(srv, CONST_STR_LEN("auth.backend"), 485 CONST_STR_LEN("mysql"))) 486 config_warn_authn_module(srv, CONST_STR_LEN("mysql"), "mysql"); 487 #endif 488 } 489 } 490 491 if (prepend_mod_auth) { 492 config_compat_module_prepend(srv, CONST_STR_LEN("mod_auth")); 493 } 494 495 if (prepend_mod_vhostdb & 1) { 496 config_compat_module_prepend(srv, CONST_STR_LEN("mod_vhostdb")); 497 } 498 } 499 500 static void config_deprecate_module_compress (server *srv) { 501 int mod_compress_idx = -1; 502 int mod_deflate_idx = -1; 503 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 504 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; 505 if (buffer_eq_slen(m, CONST_STR_LEN("mod_compress"))) 506 mod_compress_idx = (int)i; 507 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_deflate"))) 508 mod_deflate_idx = (int)i; 509 } 510 if (mod_compress_idx < 0) return; 511 512 int has_compress_directive = 0; 513 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 514 const data_config *config = 515 (data_config const *)srv->config_context->data[i]; 516 for (uint32_t j = 0; j < config->value->used; ++j) { 517 buffer *k = &config->value->data[j]->key; 518 if (0 == strncmp(k->ptr, "compress.", sizeof("compress.")-1)) { 519 has_compress_directive = 1; 520 break; 521 } 522 } 523 if (has_compress_directive) { 524 log_error(srv->errh, __FILE__, __LINE__, 525 "Warning: \"mod_compress\" is DEPRECATED and has been replaced " 526 "with \"mod_deflate\". A future release of lighttpd 1.4.x will " 527 "not contain mod_compress and lighttpd may fail to start up"); 528 break; 529 } 530 } 531 532 if (mod_deflate_idx >= 0 || !has_compress_directive) { 533 /* create new modules value list without mod_compress */ 534 array *a = array_init(srv->srvconf.modules->used-1); 535 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 536 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; 537 if (buffer_eq_slen(m, CONST_STR_LEN("mod_compress"))) 538 continue; 539 array_insert_value(a, BUF_PTR_LEN(m)); 540 } 541 array_free(srv->srvconf.modules); 542 srv->srvconf.modules = a; 543 } 544 else { 545 /* replace "mod_compress" value with "mod_deflate" value */ 546 buffer *m = &((data_string *)srv->srvconf.modules->data[mod_compress_idx])->value; 547 buffer_copy_string_len(m, CONST_STR_LEN("mod_deflate")); 548 } 549 } 550 551 static int config_http_parseopts (server *srv, const array *a) { 552 unsigned short int opts = srv->srvconf.http_url_normalize; 553 unsigned short int decode_2f = 1; 554 int rc = 1; 555 for (size_t i = 0; i < a->used; ++i) { 556 const data_string * const ds = (const data_string *)a->data[i]; 557 const buffer *k = &ds->key; 558 unsigned short int opt; 559 int val = config_plugin_value_tobool((data_unset *)ds, 2); 560 if (2 == val) { 561 log_error(srv->errh, __FILE__, __LINE__, 562 "unrecognized value for server.http-parseopts: " 563 "%s => %s (expect \"[enable|disable]\")", k->ptr, ds->value.ptr); 564 rc = 0; 565 } 566 if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize"))) 567 opt = HTTP_PARSEOPT_URL_NORMALIZE; 568 else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-unreserved"))) 569 opt = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED; 570 else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-required"))) 571 opt = HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED; 572 else if (buffer_eq_slen(k, CONST_STR_LEN("url-ctrls-reject"))) 573 opt = HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT; 574 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-backslash-trans"))) 575 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS; 576 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-decode"))) 577 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE; 578 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-reject"))) 579 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT; 580 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-remove"))) 581 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; 582 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-reject"))) 583 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT; 584 else if (buffer_eq_slen(k, CONST_STR_LEN("url-query-20-plus"))) 585 opt = HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS; 586 else if (buffer_eq_slen(k, CONST_STR_LEN("header-strict"))) { 587 srv->srvconf.http_header_strict = val; 588 continue; 589 } 590 else if (buffer_eq_slen(k, CONST_STR_LEN("host-strict"))) { 591 srv->srvconf.http_host_strict = val; 592 continue; 593 } 594 else if (buffer_eq_slen(k, CONST_STR_LEN("host-normalize"))) { 595 srv->srvconf.http_host_normalize = val; 596 continue; 597 } 598 else if (buffer_eq_slen(k, CONST_STR_LEN("method-get-body"))) { 599 srv->srvconf.http_method_get_body = val; 600 continue; 601 } 602 else { 603 log_error(srv->errh, __FILE__, __LINE__, 604 "unrecognized key for server.http-parseopts: %s", k->ptr); 605 rc = 0; 606 continue; 607 } 608 if (val) 609 opts |= opt; 610 else { 611 opts &= ~opt; 612 if (opt == HTTP_PARSEOPT_URL_NORMALIZE) { 613 opts = 0; 614 break; 615 } 616 if (opt == HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE) { 617 decode_2f = 0; 618 } 619 } 620 } 621 if (opts != 0) { 622 opts |= HTTP_PARSEOPT_URL_NORMALIZE; 623 if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE 624 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) 625 == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE 626 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) { 627 log_error(srv->errh, __FILE__, __LINE__, 628 "conflicting options in server.http-parseopts:" 629 "url-path-2f-decode, url-path-2f-reject"); 630 rc = 0; 631 } 632 if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE 633 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) 634 == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE 635 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) { 636 log_error(srv->errh, __FILE__, __LINE__, 637 "conflicting options in server.http-parseopts:" 638 "url-path-dotseg-remove, url-path-dotseg-reject"); 639 rc = 0; 640 } 641 if (!(opts & (HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED 642 |HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED))) { 643 opts |= HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED; 644 if (decode_2f 645 && !(opts & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) 646 opts |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE; 647 } 648 } 649 srv->srvconf.http_url_normalize = opts; 650 return rc; 651 } 652 653 static int config_insert_srvconf(server *srv) { 654 static const config_plugin_keys_t cpk[] = { 655 { CONST_STR_LEN("server.modules"), 656 T_CONFIG_ARRAY_VLIST, 657 T_CONFIG_SCOPE_SERVER } 658 ,{ CONST_STR_LEN("server.compat-module-load"), 659 T_CONFIG_BOOL, 660 T_CONFIG_SCOPE_SERVER } 661 ,{ CONST_STR_LEN("server.systemd-socket-activation"), 662 T_CONFIG_BOOL, 663 T_CONFIG_SCOPE_SERVER } 664 ,{ CONST_STR_LEN("server.port"), 665 T_CONFIG_SHORT, 666 T_CONFIG_SCOPE_SERVER } 667 ,{ CONST_STR_LEN("server.bind"), 668 T_CONFIG_STRING, 669 T_CONFIG_SCOPE_SERVER } 670 ,{ CONST_STR_LEN("server.network-backend"), 671 T_CONFIG_STRING, 672 T_CONFIG_SCOPE_SERVER } 673 ,{ CONST_STR_LEN("server.chroot"), 674 T_CONFIG_STRING, 675 T_CONFIG_SCOPE_SERVER } 676 ,{ CONST_STR_LEN("server.username"), 677 T_CONFIG_STRING, 678 T_CONFIG_SCOPE_SERVER } 679 ,{ CONST_STR_LEN("server.groupname"), 680 T_CONFIG_STRING, 681 T_CONFIG_SCOPE_SERVER } 682 ,{ CONST_STR_LEN("server.errorlog-placeholder-moved-to-config-insert"), 683 T_CONFIG_STRING, 684 T_CONFIG_SCOPE_SERVER } 685 ,{ CONST_STR_LEN("server.breakagelog-placeholder-moved-to-config-insert"), 686 T_CONFIG_STRING, 687 T_CONFIG_SCOPE_SERVER } 688 ,{ CONST_STR_LEN("server.errorlog-use-syslog"), 689 T_CONFIG_BOOL, 690 T_CONFIG_SCOPE_SERVER } 691 ,{ CONST_STR_LEN("server.syslog-facility"), 692 T_CONFIG_STRING, 693 T_CONFIG_SCOPE_SERVER } 694 ,{ CONST_STR_LEN("server.core-files"), 695 T_CONFIG_BOOL, 696 T_CONFIG_SCOPE_SERVER } 697 ,{ CONST_STR_LEN("server.event-handler"), 698 T_CONFIG_STRING, 699 T_CONFIG_SCOPE_SERVER } 700 ,{ CONST_STR_LEN("server.pid-file"), 701 T_CONFIG_STRING, 702 T_CONFIG_SCOPE_SERVER } 703 ,{ CONST_STR_LEN("server.max-worker"), 704 T_CONFIG_SHORT, 705 T_CONFIG_SCOPE_SERVER } 706 ,{ CONST_STR_LEN("server.max-fds"), 707 T_CONFIG_SHORT, 708 T_CONFIG_SCOPE_SERVER } 709 ,{ CONST_STR_LEN("server.max-connections"), 710 T_CONFIG_SHORT, 711 T_CONFIG_SCOPE_SERVER } 712 ,{ CONST_STR_LEN("server.max-request-field-size"), 713 T_CONFIG_INT, 714 T_CONFIG_SCOPE_SERVER } 715 ,{ CONST_STR_LEN("server.chunkqueue-chunk-sz"), 716 T_CONFIG_INT, 717 T_CONFIG_SCOPE_SERVER } 718 ,{ CONST_STR_LEN("server.upload-temp-file-size"), 719 T_CONFIG_INT, 720 T_CONFIG_SCOPE_SERVER } 721 ,{ CONST_STR_LEN("server.upload-dirs"), 722 T_CONFIG_ARRAY_VLIST, 723 T_CONFIG_SCOPE_SERVER } 724 ,{ CONST_STR_LEN("server.http-parseopts"), 725 T_CONFIG_ARRAY_KVSTRING, 726 T_CONFIG_SCOPE_SERVER } 727 ,{ CONST_STR_LEN("server.http-parseopt-header-strict"), 728 T_CONFIG_BOOL, 729 T_CONFIG_SCOPE_SERVER } 730 ,{ CONST_STR_LEN("server.http-parseopt-host-strict"), 731 T_CONFIG_BOOL, 732 T_CONFIG_SCOPE_SERVER } 733 ,{ CONST_STR_LEN("server.http-parseopt-host-normalize"), 734 T_CONFIG_BOOL, 735 T_CONFIG_SCOPE_SERVER } 736 ,{ CONST_STR_LEN("server.reject-expect-100-with-417"), /*(ignored)*/ 737 T_CONFIG_BOOL, 738 T_CONFIG_SCOPE_SERVER } 739 ,{ CONST_STR_LEN("server.stat-cache-engine"), 740 T_CONFIG_STRING, 741 T_CONFIG_SCOPE_SERVER } 742 ,{ CONST_STR_LEN("mimetype.xattr-name"), 743 T_CONFIG_STRING, 744 T_CONFIG_SCOPE_SERVER } 745 ,{ CONST_STR_LEN("ssl.engine"), 746 T_CONFIG_BOOL, 747 T_CONFIG_SCOPE_CONNECTION } 748 ,{ CONST_STR_LEN("debug.log-request-header-on-error"), 749 T_CONFIG_BOOL, 750 T_CONFIG_SCOPE_SERVER } 751 ,{ CONST_STR_LEN("server.feature-flags"), 752 T_CONFIG_ARRAY_KVANY, 753 T_CONFIG_SCOPE_SERVER } 754 ,{ NULL, 0, 755 T_CONFIG_UNSET, 756 T_CONFIG_SCOPE_UNSET } 757 }; 758 759 srv->srvconf.h2proto = 2; /* enable HTTP/2 and h2c by default */ 760 761 int rc = 0; 762 plugin_data_base srvplug; 763 memset(&srvplug, 0, sizeof(srvplug)); 764 plugin_data_base * const p = &srvplug; 765 if (!config_plugin_values_init(srv, p, cpk, "global")) 766 return HANDLER_ERROR; 767 768 int ssl_enabled = 0; /*(directive checked here only to set default port)*/ 769 770 /* process and validate T_CONFIG_SCOPE_SERVER config directives */ 771 if (p->cvlist[0].v.u2[1]) { 772 config_plugin_value_t *cpv = p->cvlist + p->cvlist[0].v.u2[0]; 773 for (; -1 != cpv->k_id; ++cpv) { 774 switch (cpv->k_id) { 775 case 0: /* server.modules */ 776 array_copy_array(srv->srvconf.modules, cpv->v.a); 777 break; 778 case 1: /* server.compat-module-load */ 779 srv->srvconf.compat_module_load = (unsigned short)cpv->v.u; 780 break; 781 case 2: /* server.systemd-socket-activation */ 782 srv->srvconf.systemd_socket_activation=(unsigned short)cpv->v.u; 783 break; 784 case 3: /* server.port */ 785 srv->srvconf.port = cpv->v.shrt; 786 break; 787 case 4: /* server.bind */ 788 if (!buffer_is_blank(cpv->v.b)) 789 srv->srvconf.bindhost = cpv->v.b; 790 break; 791 case 5: /* server.network-backend */ 792 if (!buffer_is_blank(cpv->v.b)) 793 srv->srvconf.network_backend = cpv->v.b; 794 break; 795 case 6: /* server.chroot */ 796 if (!buffer_is_blank(cpv->v.b)) 797 srv->srvconf.changeroot = cpv->v.b; 798 break; 799 case 7: /* server.username */ 800 if (!buffer_is_blank(cpv->v.b)) 801 srv->srvconf.username = cpv->v.b; 802 break; 803 case 8: /* server.groupname */ 804 if (!buffer_is_blank(cpv->v.b)) 805 srv->srvconf.groupname = cpv->v.b; 806 break; 807 case 9: /* server.errorlog */ /* moved to config_insert() */ 808 /*srv->srvconf.errorlog_file = cpv->v.b;*/ 809 break; 810 case 10:/* server.breakagelog */ /* moved to config_insert() */ 811 /*srv->srvconf.breakagelog_file = cpv->v.b;*/ 812 break; 813 case 11:/* server.errorlog-use-syslog */ 814 srv->srvconf.errorlog_use_syslog = (unsigned short)cpv->v.u; 815 break; 816 case 12:/* server.syslog-facility */ 817 if (!buffer_is_blank(cpv->v.b)) 818 srv->srvconf.syslog_facility = cpv->v.b; 819 break; 820 case 13:/* server.core-files */ 821 srv->srvconf.enable_cores = (unsigned short)cpv->v.u; 822 break; 823 case 14:/* server.event-handler */ 824 srv->srvconf.event_handler = cpv->v.b->ptr; 825 break; 826 case 15:/* server.pid-file */ 827 if (!buffer_is_blank(cpv->v.b)) 828 *(const buffer **)&srv->srvconf.pid_file = cpv->v.b; 829 break; 830 case 16:/* server.max-worker */ 831 srv->srvconf.max_worker = (unsigned short)cpv->v.u; 832 break; 833 case 17:/* server.max-fds */ 834 srv->srvconf.max_fds = (unsigned short)cpv->v.u; 835 break; 836 case 18:/* server.max-connections */ 837 srv->srvconf.max_conns = (unsigned short)cpv->v.u; 838 break; 839 case 19:/* server.max-request-field-size */ 840 srv->srvconf.max_request_field_size = cpv->v.u; 841 break; 842 case 20:/* server.chunkqueue-chunk-sz */ 843 chunkqueue_set_chunk_size(cpv->v.u); 844 break; 845 case 21:/* server.upload-temp-file-size */ 846 srv->srvconf.upload_temp_file_size = cpv->v.u; 847 break; 848 case 22:/* server.upload-dirs */ 849 array_copy_array(srv->srvconf.upload_tempdirs, cpv->v.a); 850 break; 851 case 23:/* server.http-parseopts */ 852 if (!config_http_parseopts(srv, cpv->v.a)) 853 rc = HANDLER_ERROR; 854 break; 855 case 24:/* server.http-parseopt-header-strict */ 856 srv->srvconf.http_header_strict = (0 != cpv->v.u); 857 break; 858 case 25:/* server.http-parseopt-host-strict */ 859 srv->srvconf.http_host_strict = (0 != cpv->v.u); 860 break; 861 case 26:/* server.http-parseopt-host-normalize */ 862 srv->srvconf.http_host_normalize = (0 != cpv->v.u); 863 break; 864 case 27:/* server.reject-expect-100-with-417 *//*(ignored)*/ 865 break; 866 case 28:/* server.stat-cache-engine */ 867 if (0 != stat_cache_choose_engine(cpv->v.b, srv->errh)) 868 rc = HANDLER_ERROR; 869 break; 870 case 29:/* mimetype.xattr-name */ 871 stat_cache_xattrname(cpv->v.b->ptr); 872 break; 873 case 30:/* ssl.engine */ 874 ssl_enabled = (0 != cpv->v.u); 875 #if !defined(USE_OPENSSL_CRYPTO) \ 876 && !defined(USE_MBEDTLS_CRYPTO) \ 877 && !defined(USE_NSS_CRYPTO) \ 878 && !defined(USE_GNUTLS_CRYPTO) \ 879 && !defined(USE_WOLFSSL_CRYPTO) 880 if (ssl_enabled) { 881 log_error(srv->errh, __FILE__, __LINE__, 882 "ssl support is missing; " 883 "recompile with e.g. --with-openssl"); 884 rc = HANDLER_ERROR; 885 break; 886 } 887 #endif 888 break; 889 case 31:/* debug.log-request-header-on-error */ 890 srv->srvconf.log_request_header_on_error = (0 != cpv->v.u); 891 break; 892 case 32:/* server.feature-flags */ 893 srv->srvconf.feature_flags = cpv->v.a; 894 srv->srvconf.h2proto = 895 config_plugin_value_tobool( 896 array_get_element_klen(cpv->v.a, 897 CONST_STR_LEN("server.h2proto")), 1); 898 if (srv->srvconf.h2proto) 899 srv->srvconf.h2proto += 900 config_plugin_value_tobool( 901 array_get_element_klen(cpv->v.a, 902 CONST_STR_LEN("server.h2c")), 1); 903 srv->srvconf.absolute_dir_redirect = 904 config_plugin_value_tobool( 905 array_get_element_klen(cpv->v.a, 906 CONST_STR_LEN("server.absolute-dir-redirect")), 0); 907 break; 908 default:/* should not happen */ 909 break; 910 } 911 } 912 } 913 914 if (0 == srv->srvconf.port) 915 srv->srvconf.port = ssl_enabled ? 443 : 80; 916 917 config_check_module_duplicates(srv); 918 919 if (srv->srvconf.compat_module_load) 920 config_compat_module_load(srv); 921 922 config_deprecate_module_compress(srv); 923 924 if (srv->srvconf.http_url_normalize) 925 config_burl_normalize_cond(srv); 926 927 if (!config_pcre_keyvalue(srv)) 928 rc = HANDLER_ERROR; 929 930 free(srvplug.cvlist); 931 return rc; 932 } 933 934 static int config_insert(server *srv) { 935 static const config_plugin_keys_t cpk[] = { 936 { CONST_STR_LEN("server.document-root"), 937 T_CONFIG_STRING, 938 T_CONFIG_SCOPE_CONNECTION } 939 ,{ CONST_STR_LEN("server.name"), 940 T_CONFIG_STRING, 941 T_CONFIG_SCOPE_CONNECTION } 942 ,{ CONST_STR_LEN("server.tag"), 943 T_CONFIG_STRING, 944 T_CONFIG_SCOPE_CONNECTION } 945 ,{ CONST_STR_LEN("server.max-request-size"), 946 T_CONFIG_INT, 947 T_CONFIG_SCOPE_CONNECTION } 948 ,{ CONST_STR_LEN("server.max-keep-alive-requests"), 949 T_CONFIG_SHORT, 950 T_CONFIG_SCOPE_CONNECTION } 951 ,{ CONST_STR_LEN("server.max-keep-alive-idle"), 952 T_CONFIG_SHORT, 953 T_CONFIG_SCOPE_CONNECTION } 954 ,{ CONST_STR_LEN("server.max-read-idle"), 955 T_CONFIG_SHORT, 956 T_CONFIG_SCOPE_CONNECTION } 957 ,{ CONST_STR_LEN("server.max-write-idle"), 958 T_CONFIG_SHORT, 959 T_CONFIG_SCOPE_CONNECTION } 960 ,{ CONST_STR_LEN("server.errorfile-prefix"), 961 T_CONFIG_STRING, 962 T_CONFIG_SCOPE_CONNECTION } 963 ,{ CONST_STR_LEN("server.error-handler"), 964 T_CONFIG_STRING, 965 T_CONFIG_SCOPE_CONNECTION } 966 ,{ CONST_STR_LEN("server.error-handler-404"), 967 T_CONFIG_STRING, 968 T_CONFIG_SCOPE_CONNECTION } 969 ,{ CONST_STR_LEN("server.error-intercept"), 970 T_CONFIG_BOOL, 971 T_CONFIG_SCOPE_CONNECTION } 972 ,{ CONST_STR_LEN("server.force-lowercase-filenames"), 973 T_CONFIG_BOOL, 974 T_CONFIG_SCOPE_CONNECTION } 975 ,{ CONST_STR_LEN("server.follow-symlink"), 976 T_CONFIG_BOOL, 977 T_CONFIG_SCOPE_CONNECTION } 978 ,{ CONST_STR_LEN("server.protocol-http11"), 979 T_CONFIG_BOOL, 980 T_CONFIG_SCOPE_CONNECTION } 981 ,{ CONST_STR_LEN("server.range-requests"), 982 T_CONFIG_BOOL, 983 T_CONFIG_SCOPE_CONNECTION } 984 ,{ CONST_STR_LEN("server.stream-request-body"), 985 T_CONFIG_SHORT, 986 T_CONFIG_SCOPE_CONNECTION } 987 ,{ CONST_STR_LEN("server.stream-response-body"), 988 T_CONFIG_SHORT, 989 T_CONFIG_SCOPE_CONNECTION } 990 ,{ CONST_STR_LEN("server.kbytes-per-second"), 991 T_CONFIG_SHORT, 992 T_CONFIG_SCOPE_CONNECTION } 993 ,{ CONST_STR_LEN("connection.kbytes-per-second"), 994 T_CONFIG_SHORT, 995 T_CONFIG_SCOPE_CONNECTION } 996 ,{ CONST_STR_LEN("mimetype.assign"), 997 T_CONFIG_ARRAY_KVSTRING, 998 T_CONFIG_SCOPE_CONNECTION } 999 ,{ CONST_STR_LEN("mimetype.use-xattr"), 1000 T_CONFIG_BOOL, 1001 T_CONFIG_SCOPE_CONNECTION } 1002 ,{ CONST_STR_LEN("etag.use-inode"), 1003 T_CONFIG_BOOL, 1004 T_CONFIG_SCOPE_CONNECTION } 1005 ,{ CONST_STR_LEN("etag.use-mtime"), 1006 T_CONFIG_BOOL, 1007 T_CONFIG_SCOPE_CONNECTION } 1008 ,{ CONST_STR_LEN("etag.use-size"), 1009 T_CONFIG_BOOL, 1010 T_CONFIG_SCOPE_CONNECTION } 1011 ,{ CONST_STR_LEN("debug.log-condition-handling"), 1012 T_CONFIG_BOOL, 1013 T_CONFIG_SCOPE_CONNECTION } 1014 ,{ CONST_STR_LEN("debug.log-file-not-found"), 1015 T_CONFIG_BOOL, 1016 T_CONFIG_SCOPE_CONNECTION } 1017 ,{ CONST_STR_LEN("debug.log-request-handling"), 1018 T_CONFIG_BOOL, 1019 T_CONFIG_SCOPE_CONNECTION } 1020 ,{ CONST_STR_LEN("debug.log-request-header"), 1021 T_CONFIG_BOOL, 1022 T_CONFIG_SCOPE_CONNECTION } 1023 ,{ CONST_STR_LEN("debug.log-response-header"), 1024 T_CONFIG_BOOL, 1025 T_CONFIG_SCOPE_CONNECTION } 1026 ,{ CONST_STR_LEN("debug.log-timeouts"), 1027 T_CONFIG_BOOL, 1028 T_CONFIG_SCOPE_CONNECTION } 1029 ,{ CONST_STR_LEN("debug.log-state-handling"), 1030 T_CONFIG_BOOL, 1031 T_CONFIG_SCOPE_CONNECTION } 1032 ,{ CONST_STR_LEN("server.errorlog"), 1033 T_CONFIG_STRING, 1034 T_CONFIG_SCOPE_CONNECTION } 1035 ,{ CONST_STR_LEN("server.breakagelog"), 1036 T_CONFIG_STRING, 1037 T_CONFIG_SCOPE_CONNECTION } 1038 ,{ NULL, 0, 1039 T_CONFIG_UNSET, 1040 T_CONFIG_SCOPE_UNSET } 1041 }; 1042 1043 int rc = 0; 1044 config_data_base * const p = calloc(1, sizeof(config_data_base)); 1045 force_assert(p); 1046 srv->config_data_base = p; 1047 1048 if (!config_plugin_values_init(srv, p, cpk, "base")) 1049 return HANDLER_ERROR; 1050 1051 /* process and validate T_CONFIG_SCOPE_CONNECTION config directives 1052 * (init i to 0 if global context; to 1 to skip empty global context) */ 1053 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 1054 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 1055 for (; -1 != cpv->k_id; ++cpv) { 1056 switch (cpv->k_id) { 1057 case 0: /* server.document-root */ 1058 break; 1059 case 1: /* server.name */ 1060 if (buffer_is_blank(cpv->v.b)) 1061 cpv->v.b = NULL; 1062 break; 1063 case 2: /* server.tag */ 1064 if (!buffer_is_blank(cpv->v.b)) { 1065 buffer *b; 1066 *(const buffer **)&b = cpv->v.b; 1067 for (char *t=strchr(b->ptr,'\n'); t; t=strchr(t+2,'\n')) { 1068 /* not expecting admin to define multi-line server.tag, 1069 * but ensure server_tag has proper header continuation, 1070 * if needed */ 1071 if (t[1] == ' ' || t[1] == '\t') continue; 1072 off_t off = t - b->ptr; 1073 size_t len = buffer_clen(b); 1074 buffer_string_prepare_append(b, 1); 1075 t = b->ptr+off; 1076 memmove(t+2, t+1, len - off - 1); 1077 t[1] = ' '; 1078 buffer_commit(b, 1); 1079 } 1080 char *t = b->ptr; /*(make empty if tag is whitespace-only)*/ 1081 while (*t==' ' || *t=='\t' || *t=='\r' || *t=='\n') ++t; 1082 if (*t == '\0') buffer_truncate(b, 0); 1083 } 1084 else if (0 != i) 1085 cpv->v.b = NULL; 1086 break; 1087 case 3: /* server.max-request-size */ 1088 case 4: /* server.max-keep-alive-requests */ 1089 case 5: /* server.max-keep-alive-idle */ 1090 case 6: /* server.max-read-idle */ 1091 case 7: /* server.max-write-idle */ 1092 break; 1093 case 8: /* server.errorfile-prefix */ 1094 case 9: /* server.error-handler */ 1095 case 10:/* server.error-handler-404 */ 1096 if (buffer_is_blank(cpv->v.b)) 1097 cpv->v.b = NULL; 1098 break; 1099 case 11:/* server.error-intercept */ 1100 case 12:/* server.force-lowercase-filenames */ 1101 break; 1102 case 13:/* server.follow-symlink */ 1103 #ifndef HAVE_LSTAT 1104 if (0 == cpv->v.u) 1105 log_error(srv->errh, __FILE__, __LINE__, 1106 "Your system lacks lstat(). " 1107 "We can not differ symlinks from files. " 1108 "Please remove server.follow-symlinks from your config."); 1109 #endif 1110 break; 1111 case 14:/* server.protocol-http11 */ 1112 case 15:/* server.range-requests */ 1113 break; 1114 case 16:/* server.stream-request-body */ 1115 if (cpv->v.shrt & FDEVENT_STREAM_REQUEST_BUFMIN) 1116 cpv->v.shrt |=FDEVENT_STREAM_REQUEST; 1117 break; 1118 case 17:/* server.stream-response-body */ 1119 if (cpv->v.shrt & FDEVENT_STREAM_RESPONSE_BUFMIN) 1120 cpv->v.shrt |=FDEVENT_STREAM_RESPONSE; 1121 break; 1122 case 18:{/*server.kbytes-per-second */ 1123 off_t * const cnt = malloc(2*sizeof(off_t)); 1124 force_assert(cnt); 1125 cnt[0] = 0; 1126 cnt[1] = (off_t)cpv->v.shrt << 10; 1127 cpv->v.v = cnt; 1128 cpv->vtype = T_CONFIG_LOCAL; 1129 break; 1130 } 1131 case 19:/* connection.kbytes-per-second */ 1132 case 20:/* mimetype.assign */ 1133 case 21:/* mimetype.use-xattr */ 1134 case 22:/* etag.use-inode */ 1135 case 23:/* etag.use-mtime */ 1136 case 24:/* etag.use-size */ 1137 case 25:/* debug.log-condition-handling */ 1138 case 26:/* debug.log-file-not-found */ 1139 case 27:/* debug.log-request-handling */ 1140 case 28:/* debug.log-request-header */ 1141 case 29:/* debug.log-response-header */ 1142 case 30:/* debug.log-timeouts */ 1143 case 31:/* debug.log-state-handling */ 1144 case 32:/* server.errorlog *//*must match config_log_error_open*/ 1145 case 33:/* server.breakagelog */ /* match config_log_error_open*/ 1146 break; 1147 default:/* should not happen */ 1148 break; 1149 } 1150 } 1151 } 1152 1153 p->defaults.errh = srv->errh; 1154 p->defaults.max_keep_alive_requests = 100; 1155 p->defaults.max_keep_alive_idle = 5; 1156 p->defaults.max_read_idle = 60; 1157 p->defaults.max_write_idle = 360; 1158 p->defaults.follow_symlink = 1; 1159 p->defaults.allow_http11 = 1; 1160 p->defaults.etag_flags = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE; 1161 p->defaults.range_requests = 1; 1162 /* use 2 to detect later if value is set by user config in global section */ 1163 p->defaults.force_lowercase_filenames = 2; 1164 1165 /*(global, but store in r->conf.http_parseopts)*/ 1166 p->defaults.http_parseopts = 1167 (srv->srvconf.http_header_strict ? HTTP_PARSEOPT_HEADER_STRICT :0) 1168 | (srv->srvconf.http_host_strict ? (HTTP_PARSEOPT_HOST_STRICT 1169 |HTTP_PARSEOPT_HOST_NORMALIZE) :0) 1170 | (srv->srvconf.http_host_normalize ? HTTP_PARSEOPT_HOST_NORMALIZE :0) 1171 | (srv->srvconf.http_method_get_body ? HTTP_PARSEOPT_METHOD_GET_BODY :0); 1172 p->defaults.http_parseopts |= srv->srvconf.http_url_normalize; 1173 p->defaults.mimetypes = &srv->srvconf.empty_array; /*(must not be NULL)*/ 1174 p->defaults.h2proto = srv->srvconf.h2proto; 1175 1176 /* initialize p->defaults from global config context */ 1177 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 1178 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 1179 if (-1 != cpv->k_id) 1180 config_merge_config(&p->defaults, cpv); 1181 } 1182 1183 /* (after processing config defaults) */ 1184 p->defaults.max_request_field_size = srv->srvconf.max_request_field_size; 1185 p->defaults.log_request_header_on_error = 1186 srv->srvconf.log_request_header_on_error; 1187 if (p->defaults.log_request_handling || p->defaults.log_request_header) 1188 p->defaults.log_request_header_on_error = 1; 1189 1190 request_config_set_defaults(&p->defaults); 1191 1192 return rc; 1193 } 1194 1195 int config_finalize(server *srv, const buffer *default_server_tag) { 1196 /* (call after plugins_call_set_defaults()) */ 1197 1198 config_data_base * const p = srv->config_data_base; 1199 1200 /* settings might be enabled during plugins_call_set_defaults() */ 1201 p->defaults.high_precision_timestamps = 1202 srv->srvconf.high_precision_timestamps; 1203 1204 /* configure default server_tag if not set 1205 * (if configured to blank, unset server_tag)*/ 1206 if (!p->defaults.server_tag) 1207 p->defaults.server_tag = default_server_tag; 1208 else if (buffer_is_blank(p->defaults.server_tag)) 1209 p->defaults.server_tag = NULL; 1210 1211 /* dump unused config keys */ 1212 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 1213 array *config = ((data_config *)srv->config_context->data[i])->value; 1214 for (uint32_t j = 0; config && j < config->used; ++j) { 1215 const buffer * const k = &config->data[j]->key; 1216 1217 /* all var.* is known as user defined variable */ 1218 if (strncmp(k->ptr, "var.", sizeof("var.") - 1) == 0) 1219 continue; 1220 1221 if (!array_get_element_klen(srv->srvconf.config_touched, 1222 BUF_PTR_LEN(k))) 1223 log_error(srv->errh, __FILE__, __LINE__, 1224 "WARNING: unknown config-key: %s (ignored)", k->ptr); 1225 } 1226 } 1227 1228 array_free(srv->srvconf.config_touched); 1229 srv->srvconf.config_touched = NULL; 1230 1231 if (srv->srvconf.config_unsupported || srv->srvconf.config_deprecated) { 1232 if (srv->srvconf.config_unsupported) 1233 log_error(srv->errh, __FILE__, __LINE__, 1234 "Configuration contains unsupported keys. Going down."); 1235 if (srv->srvconf.config_deprecated) 1236 log_error(srv->errh, __FILE__, __LINE__, 1237 "Configuration contains deprecated keys. Going down."); 1238 return 0; 1239 } 1240 1241 #ifdef HAVE_PCRE2_H 1242 for (uint32_t i = 1; i < srv->config_context->used; ++i) { 1243 data_config * const dc = 1244 (data_config *)srv->config_context->data[i]; 1245 if ((dc->cond == CONFIG_COND_MATCH || dc->cond == CONFIG_COND_NOMATCH) 1246 && 0 == dc->capture_idx) { 1247 if (__builtin_expect( (NULL == srv->match_data), 0)) { 1248 #if 0 1249 /* calculate max output vector size to save a few bytes; 1250 * currently using hard-coded ovec_max = 10 below 1251 * (increase in code size is probably more than bytes saved) */ 1252 uint32_t ovec_max = 0; 1253 for (uint32_t j = i; j < srv->config_context->used; ++j) { 1254 const data_config * const dc = 1255 (data_config *)srv->config_context->data[j]; 1256 if ((dc->cond == CONFIG_COND_MATCH 1257 || dc->cond == CONFIG_COND_NOMATCH) 1258 && 0 == dc->capture_idx) { 1259 uint32_t v; 1260 if (0==pcre2_pattern_info(dc->code, 1261 PCRE2_INFO_CAPTURECOUNT,&v)) { 1262 if (ovec_max < v) 1263 ovec_max = v; 1264 } 1265 } 1266 } 1267 #else 1268 uint32_t ovec_max = 10; 1269 #endif 1270 srv->match_data = pcre2_match_data_create(ovec_max, NULL); 1271 force_assert(srv->match_data); 1272 } 1273 dc->match_data = srv->match_data; 1274 } 1275 } 1276 #endif 1277 1278 return 1; 1279 } 1280 1281 1282 /* Save some bytes using buffer_append_string() in cold funcs to print config 1283 * (instead of buffer_append_string_len() w/ CONST_STR_LEN() on constant strs)*/ 1284 1285 static void config_print_by_type(const data_unset *du, buffer *b, int depth); 1286 1287 static void config_print_indent(buffer *b, int depth) { 1288 depth <<= 2; 1289 memset(buffer_extend(b, depth), ' ', depth); 1290 } 1291 1292 __attribute_pure__ 1293 static uint32_t config_print_array_max_klen(const array * const a) { 1294 uint32_t maxlen = 0; 1295 for (uint32_t i = 0; i < a->used; ++i) { 1296 uint32_t len = buffer_clen(&a->data[i]->key); 1297 if (maxlen < len) 1298 maxlen = len; 1299 } 1300 return maxlen; 1301 } 1302 1303 static void config_print_array(const array * const a, buffer * const b, int depth) { 1304 if (a->used <= 5 && (!a->used || buffer_is_unset(&a->data[0]->key))) { 1305 int oneline = 1; 1306 for (uint32_t i = 0; i < a->used; ++i) { 1307 data_unset *du = a->data[i]; 1308 if (du->type != TYPE_STRING && du->type != TYPE_INTEGER) { 1309 oneline = 0; 1310 break; 1311 } 1312 } 1313 if (oneline) { 1314 buffer_append_string(b, "("); 1315 for (uint32_t i = 0; i < a->used; ++i) { 1316 if (i != 0) 1317 buffer_append_string(b, ", "); 1318 config_print_by_type(a->data[i], b, depth + 1); 1319 } 1320 buffer_append_string(b, ")"); 1321 return; 1322 } 1323 } 1324 1325 const uint32_t maxlen = config_print_array_max_klen(a); 1326 buffer_append_string(b, "(\n"); 1327 for (uint32_t i = 0; i < a->used; ++i) { 1328 config_print_indent(b, depth + 1); 1329 data_unset *du = a->data[i]; 1330 if (!buffer_is_unset(&du->key)) { 1331 buffer_append_str3(b, CONST_STR_LEN("\""), 1332 BUF_PTR_LEN(&du->key), 1333 CONST_STR_LEN("\"")); 1334 int indent = (int)(maxlen - buffer_clen(&du->key)); 1335 if (indent > 0) 1336 memset(buffer_extend(b, indent), ' ', indent); 1337 buffer_append_string(b, " => "); 1338 } 1339 config_print_by_type(du, b, depth + 1); 1340 buffer_append_string(b, ",\n"); 1341 } 1342 config_print_indent(b, depth); 1343 buffer_append_string(b, ")"); 1344 } 1345 1346 static void config_print_config(const data_unset *d, buffer * const b, int depth) { 1347 data_config *dc = (data_config *)d; 1348 array *a = (array *)dc->value; 1349 1350 if (0 == dc->context_ndx) { 1351 buffer_append_string(b, "config {\n"); 1352 } 1353 else { 1354 if (dc->cond != CONFIG_COND_ELSE) { 1355 buffer_append_string(b, dc->comp_key); 1356 buffer_append_string(b, " "); 1357 } 1358 buffer_append_string(b, "{\n"); 1359 config_print_indent(b, depth + 1); 1360 buffer_append_string(b, "# block "); 1361 buffer_append_int(b, dc->context_ndx); 1362 buffer_append_string(b, "\n"); 1363 } 1364 ++depth; 1365 1366 const uint32_t maxlen = config_print_array_max_klen(a); 1367 for (uint32_t i = 0; i < a->used; ++i) { 1368 config_print_indent(b, depth); 1369 data_unset *du = a->data[i]; 1370 buffer_append_string_buffer(b, &du->key); 1371 int indent = (int)(maxlen - buffer_clen(&du->key)); 1372 if (indent > 0) 1373 memset(buffer_extend(b, indent), ' ', indent); 1374 buffer_append_string(b, " = "); 1375 config_print_by_type(du, b, depth); 1376 buffer_append_string(b, "\n"); 1377 } 1378 1379 buffer_append_string(b, "\n"); 1380 for (uint32_t i = 0; i < dc->children.used; ++i) { 1381 data_config *dcc = dc->children.data[i]; 1382 1383 /* only the 1st block of chaining */ 1384 if (NULL == dcc->prev) { 1385 buffer_append_string(b, "\n"); 1386 config_print_indent(b, depth); 1387 config_print_by_type((data_unset *) dcc, b, depth); 1388 buffer_append_string(b, "\n"); 1389 } 1390 } 1391 1392 --depth; 1393 config_print_indent(b, depth); 1394 buffer_append_string(b, "}"); 1395 if (0 != dc->context_ndx) { 1396 buffer_append_string(b, " # end of "); 1397 buffer_append_string(b, (dc->cond != CONFIG_COND_ELSE) 1398 ? dc->comp_key 1399 : "else"); 1400 } 1401 1402 if (dc->next) { 1403 buffer_append_string(b, "\n"); 1404 config_print_indent(b, depth); 1405 buffer_append_string(b, "else "); 1406 config_print_by_type((data_unset *)dc->next, b, depth); 1407 } 1408 } 1409 1410 static void config_print_string(const data_unset *du, buffer * const b) { 1411 /* print out the string as is, except prepend '"' with backslash */ 1412 const buffer * const vb = &((data_string *)du)->value; 1413 char *dst = buffer_string_prepare_append(b, buffer_clen(vb)*2); 1414 uint32_t n = 0; 1415 dst[n++] = '"'; 1416 if (vb->ptr) { 1417 for (const char *p = vb->ptr; *p; ++p) { 1418 if (*p == '"') 1419 dst[n++] = '\\'; 1420 dst[n++] = *p; 1421 } 1422 } 1423 dst[n++] = '"'; 1424 buffer_commit(b, n); 1425 } 1426 1427 __attribute_cold__ 1428 static void config_print_by_type(const data_unset * const du, buffer * const b, int depth) { 1429 switch (du->type) { 1430 case TYPE_STRING: 1431 config_print_string(du, b); 1432 break; 1433 case TYPE_INTEGER: 1434 buffer_append_int(b, ((data_integer *)du)->value); 1435 break; 1436 case TYPE_ARRAY: 1437 config_print_array(&((data_array *)du)->value, b, depth); 1438 break; 1439 case TYPE_CONFIG: 1440 config_print_config(du, b, depth); 1441 break; 1442 default: 1443 /*if (du->fn->print) du->fn->print(du, b, depth);*/ 1444 break; 1445 } 1446 } 1447 1448 void config_print(server *srv) { 1449 buffer_clear(srv->tmp_buf); 1450 data_unset *dc = srv->config_context->data[0]; 1451 config_print_by_type(dc, srv->tmp_buf, 0); 1452 } 1453 1454 void config_free(server *srv) { 1455 /*request_config_set_defaults(NULL);*//*(not necessary)*/ 1456 config_free_config(srv->config_data_base); 1457 1458 array_free(srv->config_context); 1459 array_free(srv->srvconf.config_touched); 1460 array_free(srv->srvconf.modules); 1461 buffer_free(srv->srvconf.modules_dir); 1462 array_free(srv->srvconf.upload_tempdirs); 1463 #ifdef HAVE_PCRE2_H 1464 if (NULL == srv->match_data) pcre2_match_data_free(srv->match_data); 1465 #endif 1466 } 1467 1468 void config_init(server *srv) { 1469 srv->config_context = array_init(16); 1470 srv->srvconf.config_touched = array_init(128); 1471 1472 srv->srvconf.port = 0; 1473 srv->srvconf.dont_daemonize = 0; 1474 srv->srvconf.preflight_check = 0; 1475 srv->srvconf.compat_module_load = 1; 1476 srv->srvconf.systemd_socket_activation = 0; 1477 1478 srv->srvconf.high_precision_timestamps = 0; 1479 srv->srvconf.max_request_field_size = 8192; 1480 1481 srv->srvconf.http_header_strict = 1; 1482 srv->srvconf.http_host_strict = 1; /*(implies http_host_normalize)*/ 1483 srv->srvconf.http_host_normalize = 0; 1484 srv->srvconf.http_url_normalize = 1485 HTTP_PARSEOPT_URL_NORMALIZE 1486 | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED 1487 | HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT 1488 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE 1489 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; 1490 1491 srv->srvconf.modules = array_init(16); 1492 srv->srvconf.modules_dir = buffer_init_string(LIBRARY_DIR); 1493 srv->srvconf.upload_tempdirs = array_init(2); 1494 } 1495 1496 /** 1497 * open the errorlog 1498 * 1499 * we have 4 possibilities: 1500 * - stderr (default) 1501 * - syslog 1502 * - logfile 1503 * - pipe 1504 * 1505 */ 1506 1507 static void config_log_error_open_syslog(server *srv, log_error_st *errh, const buffer *syslog_facility) { 1508 #ifdef HAVE_SYSLOG_H 1509 /*assert(errh->mode == FDLOG_FD);*/ 1510 /*assert(errh->fd == STDERR_FILENO);*/ 1511 errh->mode = FDLOG_SYSLOG; 1512 errh->fd = -1; 1513 /* perhaps someone wants to use syslog() */ 1514 int facility = -1; 1515 if (syslog_facility) { 1516 static const struct facility_name_st { 1517 const char *name; 1518 int val; 1519 } facility_names[] = { 1520 { "auth", LOG_AUTH } 1521 #ifdef LOG_AUTHPRIV 1522 ,{ "authpriv", LOG_AUTHPRIV } 1523 #endif 1524 #ifdef LOG_CRON 1525 ,{ "cron", LOG_CRON } 1526 #endif 1527 ,{ "daemon", LOG_DAEMON } 1528 #ifdef LOG_FTP 1529 ,{ "ftp", LOG_FTP } 1530 #endif 1531 #ifdef LOG_KERN 1532 ,{ "kern", LOG_KERN } 1533 #endif 1534 #ifdef LOG_LPR 1535 ,{ "lpr", LOG_LPR } 1536 #endif 1537 #ifdef LOG_MAIL 1538 ,{ "mail", LOG_MAIL } 1539 #endif 1540 #ifdef LOG_NEWS 1541 ,{ "news", LOG_NEWS } 1542 #endif 1543 ,{ "security", LOG_AUTH } /* DEPRECATED */ 1544 #ifdef LOG_SYSLOG 1545 ,{ "syslog", LOG_SYSLOG } 1546 #endif 1547 #ifdef LOG_USER 1548 ,{ "user", LOG_USER } 1549 #endif 1550 #ifdef LOG_UUCP 1551 ,{ "uucp", LOG_UUCP } 1552 #endif 1553 ,{ "local0", LOG_LOCAL0 } 1554 ,{ "local1", LOG_LOCAL1 } 1555 ,{ "local2", LOG_LOCAL2 } 1556 ,{ "local3", LOG_LOCAL3 } 1557 ,{ "local4", LOG_LOCAL4 } 1558 ,{ "local5", LOG_LOCAL5 } 1559 ,{ "local6", LOG_LOCAL6 } 1560 ,{ "local7", LOG_LOCAL7 } 1561 }; 1562 for (unsigned int i = 0; i < sizeof(facility_names)/sizeof(facility_names[0]); ++i) { 1563 const struct facility_name_st *f = facility_names+i; 1564 if (0 == strcmp(syslog_facility->ptr, f->name)) { 1565 facility = f->val; 1566 break; 1567 } 1568 } 1569 if (-1 == facility) { 1570 log_error(srv->errh, __FILE__, __LINE__, 1571 "unrecognized server.syslog-facility: \"%s\"; " 1572 "defaulting to \"daemon\" facility", 1573 syslog_facility->ptr); 1574 } 1575 } 1576 openlog("lighttpd", LOG_CONS|LOG_PID, -1==facility ? LOG_DAEMON : facility); 1577 #endif 1578 } 1579 1580 int config_log_error_open(server *srv) { 1581 /* logs are opened after preflight check (srv->srvconf.preflight_check) 1582 * and after dropping privileges instead of being opened during config 1583 * processing */ 1584 #ifdef __clang_analyzer__ 1585 force_assert(srv->errh); 1586 #endif 1587 1588 config_data_base * const p = srv->config_data_base; 1589 log_error_st *serrh = NULL; 1590 1591 /* future: might be slightly faster to have allocated array of open files 1592 * rather than walking config, but only might matter with many directives */ 1593 /* (init i to 0 if global context; to 1 to skip empty global context) */ 1594 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 1595 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 1596 for (; -1 != cpv->k_id; ++cpv) { 1597 const char *fn = NULL; 1598 log_error_st *errh = NULL; 1599 switch (cpv->k_id) { 1600 /* NB: these indexes are repeated below switch() block 1601 * and all must stay in sync with configfile.c */ 1602 case 32:/* server.errorlog */ 1603 if (0 == i) { 1604 if (srv->srvconf.errorlog_use_syslog) continue; 1605 errh = srv->errh; 1606 } 1607 __attribute_fallthrough__ 1608 case 33:/* server.breakagelog */ 1609 if (!buffer_is_blank(cpv->v.b)) fn = cpv->v.b->ptr; 1610 break; 1611 default: 1612 break; 1613 } 1614 1615 if (NULL == fn) continue; 1616 1617 fdlog_st * const fdlog = fdlog_open(fn); 1618 if (NULL == fdlog) { 1619 log_perror(srv->errh, __FILE__, __LINE__, 1620 "opening errorlog '%s' failed", fn); 1621 return -1; 1622 } 1623 1624 if (errh) { 1625 /*(logfiles are opened early in setup; this function is called 1626 * prior to set_defaults hook, and modules should not save a 1627 * pointer to srv->errh until set_defaults hook or later)*/ 1628 p->defaults.errh = srv->errh = fdlog; 1629 log_set_global_errh(srv->errh, 0); 1630 } 1631 cpv->v.v = errh = fdlog; 1632 cpv->vtype = T_CONFIG_LOCAL; 1633 1634 if (0 == i && errh != srv->errh) /*(top-level server.breakagelog)*/ 1635 serrh = errh; 1636 } 1637 } 1638 1639 if (config_feature_bool(srv, "server.errorlog-high-precision", 0)) 1640 log_set_global_errh(srv->errh, 1); 1641 1642 if (srv->srvconf.errorlog_use_syslog) /*(restricted to global scope)*/ 1643 config_log_error_open_syslog(srv, srv->errh, 1644 srv->srvconf.syslog_facility); 1645 else if (srv->errh->mode == FDLOG_FD && !srv->srvconf.dont_daemonize) 1646 srv->errh->fd = -1; 1647 /* We can only log to stderr in dont-daemonize mode; 1648 * if we do daemonize and no errorlog file is specified, 1649 * we log into /dev/null 1650 */ 1651 1652 /* Note: serrh should not be stored in p->defaults.serrh 1653 * If left as NULL, scripts (e.g. mod_cgi and mod_ssi exec) will inherit 1654 * the current STDERR_FILENO, which already is the top-level breakagelog. */ 1655 /*p->defaults.serrh = serrh;*/ 1656 1657 int errfd; 1658 if (NULL != serrh) { 1659 if (srv->errh->mode == FDLOG_FD) { 1660 srv->errh->fd = dup(STDERR_FILENO); 1661 fdevent_setfd_cloexec(srv->errh->fd); 1662 } 1663 1664 errfd = serrh->fd; 1665 if (*serrh->fn == '|') fdlog_pipe_serrh(errfd); /* breakagelog */ 1666 } 1667 else if (!srv->srvconf.dont_daemonize) { 1668 /* move STDERR_FILENO to /dev/null */ 1669 if (-1 == (errfd = fdevent_open_devnull())) { 1670 log_perror(srv->errh,__FILE__,__LINE__,"opening /dev/null failed"); 1671 return -1; 1672 } 1673 } 1674 else { 1675 /*(leave STDERR_FILENO as-is)*/ 1676 errfd = -1; 1677 } 1678 1679 if (0 != fdevent_set_stdin_stdout_stderr(-1, -1, errfd)) { 1680 log_perror(srv->errh, __FILE__, __LINE__, "setting stderr failed"); 1681 #ifdef FD_CLOEXEC 1682 if (-1 != errfd && NULL == serrh) close(errfd); 1683 #endif 1684 return -1; 1685 } 1686 #ifdef FD_CLOEXEC 1687 if (-1 != errfd && NULL == serrh) close(errfd); 1688 #endif 1689 1690 if (NULL != serrh) { 1691 close(errfd); /* serrh->fd */ 1692 serrh->fd = STDERR_FILENO; 1693 } 1694 1695 return 0; 1696 } 1697 1698 void config_log_error_close(server *srv) { 1699 config_data_base * const p = srv->config_data_base; 1700 if (NULL == p) return; 1701 1702 /*(reset serrh just in case; should not be used after this func returns)*/ 1703 p->defaults.serrh = NULL; 1704 1705 fdlog_closeall(srv->errh); /*(close all except srv->errh)*/ 1706 1707 if (srv->errh->mode == FDLOG_SYSLOG) { 1708 srv->errh->mode = FDLOG_FD; 1709 srv->errh->fd = STDERR_FILENO; 1710 #ifdef HAVE_SYSLOG_H 1711 closelog(); 1712 #endif 1713 } 1714 } 1715 1716 1717 1718 typedef struct { 1719 const char *source; 1720 const char *input; 1721 size_t offset; 1722 size_t size; 1723 1724 int line_pos; 1725 int line; 1726 1727 int in_key; 1728 int in_brace; 1729 int in_cond; 1730 int simulate_eol; 1731 } tokenizer_t; 1732 1733 static int config_skip_newline(tokenizer_t *t) { 1734 int skipped = 1; 1735 force_assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n'); 1736 if (t->input[t->offset] == '\r' && t->input[t->offset + 1] == '\n') { 1737 skipped ++; 1738 t->offset ++; 1739 } 1740 t->offset ++; 1741 return skipped; 1742 } 1743 1744 static int config_skip_comment(tokenizer_t *t) { 1745 int i; 1746 force_assert(t->input[t->offset] == '#'); 1747 for (i = 1; t->input[t->offset + i] && 1748 (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r'); 1749 i++); 1750 t->offset += i; 1751 return i; 1752 } 1753 1754 __attribute_cold__ 1755 static int config_tokenizer_err(server *srv, const char *file, unsigned int line, tokenizer_t *t, const char *msg) { 1756 log_error(srv->errh, file, line, "source: %s line: %d pos: %d %s", 1757 t->source, t->line, t->line_pos, msg); 1758 return -1; 1759 } 1760 1761 static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) { 1762 int tid = 0; 1763 size_t i; 1764 1765 if (t->simulate_eol) { 1766 t->simulate_eol = 0; 1767 t->in_key = 1; 1768 tid = TK_EOL; 1769 buffer_copy_string_len(token, CONST_STR_LEN("(EOL)")); 1770 } 1771 1772 while (tid == 0 && t->offset < t->size && t->input[t->offset]) { 1773 char c = t->input[t->offset]; 1774 const char *start = NULL; 1775 1776 switch (c) { 1777 case '=': 1778 if (t->in_brace) { 1779 if (t->input[t->offset + 1] == '>') { 1780 t->offset += 2; 1781 1782 buffer_copy_string_len(token, CONST_STR_LEN("=>")); 1783 1784 tid = TK_ARRAY_ASSIGN; 1785 } else { 1786 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 1787 "use => for assignments in arrays"); 1788 } 1789 } else if (t->in_cond) { 1790 if (t->input[t->offset + 1] == '=') { 1791 t->offset += 2; 1792 1793 buffer_copy_string_len(token, CONST_STR_LEN("==")); 1794 1795 tid = TK_EQ; 1796 } else if (t->input[t->offset + 1] == '~') { 1797 t->offset += 2; 1798 1799 buffer_copy_string_len(token, CONST_STR_LEN("=~")); 1800 1801 tid = TK_MATCH; 1802 } else { 1803 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 1804 "only =~ and == are allowed in the condition"); 1805 } 1806 t->in_key = 1; 1807 t->in_cond = 0; 1808 } else if (t->in_key) { 1809 tid = TK_ASSIGN; 1810 1811 buffer_copy_string_len(token, t->input + t->offset, 1); 1812 1813 t->offset++; 1814 t->line_pos++; 1815 } else { 1816 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 1817 "unexpected equal-sign: ="); 1818 } 1819 1820 break; 1821 case '!': 1822 if (t->in_cond) { 1823 if (t->input[t->offset + 1] == '=') { 1824 t->offset += 2; 1825 1826 buffer_copy_string_len(token, CONST_STR_LEN("!=")); 1827 1828 tid = TK_NE; 1829 } else if (t->input[t->offset + 1] == '~') { 1830 t->offset += 2; 1831 1832 buffer_copy_string_len(token, CONST_STR_LEN("!~")); 1833 1834 tid = TK_NOMATCH; 1835 } else { 1836 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 1837 "only !~ and != are allowed in the condition"); 1838 } 1839 t->in_key = 1; 1840 t->in_cond = 0; 1841 } else { 1842 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 1843 "unexpected exclamation-marks: !"); 1844 } 1845 1846 break; 1847 case '\t': 1848 case ' ': 1849 t->offset++; 1850 t->line_pos++; 1851 break; 1852 case '\n': 1853 case '\r': 1854 if (t->in_brace == 0) { 1855 int done = 0; 1856 while (!done && t->offset < t->size) { 1857 switch (t->input[t->offset]) { 1858 case '\r': 1859 case '\n': 1860 config_skip_newline(t); 1861 t->line_pos = 1; 1862 t->line++; 1863 break; 1864 1865 case '#': 1866 t->line_pos += config_skip_comment(t); 1867 break; 1868 1869 case '\t': 1870 case ' ': 1871 t->offset++; 1872 t->line_pos++; 1873 break; 1874 1875 default: 1876 done = 1; 1877 } 1878 } 1879 t->in_key = 1; 1880 tid = TK_EOL; 1881 buffer_copy_string_len(token, CONST_STR_LEN("(EOL)")); 1882 } else { 1883 config_skip_newline(t); 1884 t->line_pos = 1; 1885 t->line++; 1886 } 1887 break; 1888 case ',': 1889 if (t->in_brace > 0) { 1890 tid = TK_COMMA; 1891 1892 buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)")); 1893 } 1894 1895 t->offset++; 1896 t->line_pos++; 1897 break; 1898 case '"': 1899 /* search for the terminating " */ 1900 start = t->input + t->offset + 1; 1901 buffer_copy_string_len(token, CONST_STR_LEN("")); 1902 1903 for (i = 1; t->input[t->offset + i]; i++) { 1904 if (t->input[t->offset + i] == '\\' && 1905 t->input[t->offset + i + 1] == '"') { 1906 1907 buffer_append_string_len(token, start, t->input + t->offset + i - start); 1908 1909 start = t->input + t->offset + i + 1; 1910 1911 /* skip the " */ 1912 i++; 1913 continue; 1914 } 1915 1916 1917 if (t->input[t->offset + i] == '"') { 1918 tid = TK_STRING; 1919 1920 buffer_append_string_len(token, start, t->input + t->offset + i - start); 1921 1922 break; 1923 } 1924 } 1925 1926 if (t->input[t->offset + i] == '\0') { 1927 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 1928 "missing closing quote"); 1929 } 1930 1931 t->offset += i + 1; 1932 t->line_pos += i + 1; 1933 1934 break; 1935 case '(': 1936 t->offset++; 1937 t->in_brace++; 1938 1939 tid = TK_LPARAN; 1940 1941 buffer_copy_string_len(token, CONST_STR_LEN("(")); 1942 break; 1943 case ')': 1944 t->offset++; 1945 t->in_brace--; 1946 1947 tid = TK_RPARAN; 1948 1949 buffer_copy_string_len(token, CONST_STR_LEN(")")); 1950 break; 1951 case '$': 1952 t->offset++; 1953 1954 tid = TK_DOLLAR; 1955 t->in_cond = 1; 1956 t->in_key = 0; 1957 1958 buffer_copy_string_len(token, CONST_STR_LEN("$")); 1959 1960 break; 1961 1962 case '+': 1963 if (t->input[t->offset + 1] == '=') { 1964 t->offset += 2; 1965 buffer_copy_string_len(token, CONST_STR_LEN("+=")); 1966 tid = TK_APPEND; 1967 } else { 1968 t->offset++; 1969 tid = TK_PLUS; 1970 buffer_copy_string_len(token, CONST_STR_LEN("+")); 1971 } 1972 break; 1973 1974 case ':': 1975 if (t->input[t->offset+1] == '=') { 1976 t->offset += 2; 1977 tid = TK_FORCE_ASSIGN; 1978 buffer_copy_string_len(token, CONST_STR_LEN(":=")); 1979 } else { 1980 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 1981 "unexpected character ':'"); 1982 } 1983 break; 1984 1985 case '{': 1986 t->offset++; 1987 1988 tid = TK_LCURLY; 1989 1990 buffer_copy_string_len(token, CONST_STR_LEN("{")); 1991 1992 break; 1993 1994 case '}': 1995 t->offset++; 1996 1997 tid = TK_RCURLY; 1998 1999 buffer_copy_string_len(token, CONST_STR_LEN("}")); 2000 2001 for (; t->offset < t->size; ++t->offset,++t->line_pos) { 2002 c = t->input[t->offset]; 2003 if (c == '\r' || c == '\n') { 2004 break; 2005 } 2006 else if (c == '#') { 2007 t->line_pos += config_skip_comment(t); 2008 break; 2009 } 2010 else if (c != ' ' && c != '\t') { 2011 t->simulate_eol = 1; 2012 break; 2013 } /* else (c == ' ' || c == '\t') */ 2014 } 2015 2016 break; 2017 2018 case '[': 2019 t->offset++; 2020 2021 tid = TK_LBRACKET; 2022 2023 buffer_copy_string_len(token, CONST_STR_LEN("[")); 2024 2025 break; 2026 2027 case ']': 2028 t->offset++; 2029 2030 tid = TK_RBRACKET; 2031 2032 buffer_copy_string_len(token, CONST_STR_LEN("]")); 2033 2034 break; 2035 case '#': 2036 t->line_pos += config_skip_comment(t); 2037 2038 break; 2039 default: 2040 if (t->in_cond) { 2041 for (i = 0; t->input[t->offset + i] && 2042 (isalpha((unsigned char)t->input[t->offset + i]) 2043 || t->input[t->offset + i] == '_'); ++i); 2044 2045 if (i && t->input[t->offset + i]) { 2046 tid = TK_SRVVARNAME; 2047 buffer_copy_string_len(token, t->input + t->offset, i); 2048 2049 t->offset += i; 2050 t->line_pos += i; 2051 } else { 2052 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 2053 "invalid character in condition"); 2054 } 2055 } else if (isdigit((unsigned char)c)) { 2056 /* take all digits */ 2057 for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++); 2058 2059 /* was there it least a digit ? */ 2060 if (i) { 2061 tid = TK_INTEGER; 2062 2063 buffer_copy_string_len(token, t->input + t->offset, i); 2064 2065 t->offset += i; 2066 t->line_pos += i; 2067 } 2068 } else { 2069 /* the key might consist of [-.0-9a-z] */ 2070 for (i = 0; t->input[t->offset + i] && 2071 (isalnum((unsigned char)t->input[t->offset + i]) || 2072 t->input[t->offset + i] == '.' || 2073 t->input[t->offset + i] == '_' || /* for env.* */ 2074 t->input[t->offset + i] == '-' 2075 ); i++); 2076 2077 if (i && t->input[t->offset + i]) { 2078 buffer_copy_string_len(token, t->input + t->offset, i); 2079 2080 if (strcmp(token->ptr, "include") == 0) { 2081 tid = TK_INCLUDE; 2082 } else if (strcmp(token->ptr, "include_shell") == 0) { 2083 tid = TK_INCLUDE_SHELL; 2084 } else if (strcmp(token->ptr, "global") == 0) { 2085 tid = TK_GLOBAL; 2086 } else if (strcmp(token->ptr, "else") == 0) { 2087 tid = TK_ELSE; 2088 } else { 2089 tid = TK_LKEY; 2090 } 2091 2092 t->offset += i; 2093 t->line_pos += i; 2094 } else if (0 == i 2095 && ((uint8_t *)t->input)[t->offset+0] == 0xc2 2096 && ((uint8_t *)t->input)[t->offset+1] == 0xa0) { 2097 /* treat U+00A0 (c2 a0) "NO-BREAK SPACE" as whitespace */ 2098 /* http://www.fileformat.info/info/unicode/char/a0/index.htm */ 2099 t->offset+=2; 2100 t->line_pos+=2; 2101 } else { 2102 return config_tokenizer_err(srv, __FILE__, __LINE__, t, 2103 "invalid character in variable name"); 2104 } 2105 } 2106 break; 2107 } 2108 } 2109 2110 if (tid) { 2111 *token_id = tid; 2112 return 1; 2113 } else if (t->offset < t->size) { 2114 log_error(srv->errh, __FILE__, __LINE__, "%d, %s", tid, token->ptr); 2115 } 2116 return 0; 2117 } 2118 2119 static int config_parse(server *srv, config_t *context, const char *source, const char *input, size_t isize) { 2120 void *pParser; 2121 buffer *token, *lasttoken; 2122 int token_id = 0; 2123 int ret; 2124 tokenizer_t t; 2125 2126 t.source = source; 2127 t.input = input; 2128 t.size = isize; 2129 t.offset = 0; 2130 t.line = 1; 2131 t.line_pos = 1; 2132 2133 t.in_key = 1; 2134 t.in_brace = 0; 2135 t.in_cond = 0; 2136 t.simulate_eol = 0; 2137 2138 pParser = configparserAlloc( malloc ); 2139 force_assert(pParser); 2140 lasttoken = buffer_init(); 2141 token = buffer_init(); 2142 while((1 == (ret = config_tokenizer(srv, &t, &token_id, token))) && context->ok) { 2143 buffer_copy_buffer(lasttoken, token); 2144 configparser(pParser, token_id, token, context); 2145 2146 token = buffer_init(); 2147 } 2148 buffer_free(token); 2149 2150 if (ret != -1 && context->ok) { 2151 /* add an EOL at EOF, better than say sorry */ 2152 configparser(pParser, TK_EOL, buffer_init_string("(EOL)"), context); 2153 if (context->ok) { 2154 configparser(pParser, 0, NULL, context); 2155 } 2156 } 2157 configparserFree(pParser, free); 2158 2159 if (ret == -1) { 2160 log_error(srv->errh, __FILE__, __LINE__, 2161 "configfile parser failed at: %s", lasttoken->ptr); 2162 } else if (context->ok == 0) { 2163 log_error(srv->errh, __FILE__, __LINE__, "source: %s line: %d pos: %d " 2164 "parser failed somehow near here: %s", 2165 t.source, t.line, t.line_pos, lasttoken->ptr); 2166 ret = -1; 2167 } 2168 buffer_free(lasttoken); 2169 2170 return ret == -1 ? -1 : 0; 2171 } 2172 2173 __attribute_cold__ 2174 static int config_parse_stdin(server *srv, config_t *context) { 2175 buffer * const b = chunk_buffer_acquire(); 2176 size_t dlen; 2177 ssize_t n = -1; 2178 do { 2179 if (n > 0) 2180 buffer_commit(b, n); 2181 size_t avail = buffer_string_space(b); 2182 dlen = buffer_clen(b); 2183 if (__builtin_expect( (avail < 1024), 0)) { 2184 /*(arbitrary limit: 32 MB file; expect < 1 MB)*/ 2185 if (dlen >= 32*1024*1024) { 2186 log_error(srv->errh, __FILE__, __LINE__, 2187 "config read from stdin is way too large"); 2188 break; 2189 } 2190 avail = chunk_buffer_prepare_append(b, b->size+avail); 2191 } 2192 n = read(STDIN_FILENO, b->ptr+dlen, avail); 2193 } while (n > 0 || (n == -1 && errno == EINTR)); 2194 int rc = -1; 2195 if (0 == n) 2196 rc = dlen ? config_parse(srv, context, "-", b->ptr, dlen) : 0; 2197 else 2198 log_perror(srv->errh, __FILE__, __LINE__, "config read from stdin"); 2199 2200 if (dlen) 2201 ck_memzero(b->ptr, dlen); 2202 chunk_buffer_release(b); 2203 return rc; 2204 } 2205 2206 static int config_parse_file_stream(server *srv, config_t *context, const char *fn) { 2207 off_t dlen = 32*1024*1024;/*(arbitrary limit: 32 MB file; expect < 1 MB)*/ 2208 char *data = fdevent_load_file(fn, &dlen, NULL, malloc, free); 2209 if (NULL == data) { 2210 log_perror(srv->errh, __FILE__, __LINE__, 2211 "opening configfile %s failed", fn); 2212 return -1; 2213 } 2214 2215 int rc = 0; 2216 if (dlen) { 2217 rc = config_parse(srv, context, fn, data, (size_t)dlen); 2218 ck_memzero(data, (size_t)dlen); 2219 } 2220 free(data); 2221 return rc; 2222 } 2223 2224 int config_parse_file(server *srv, config_t *context, const char *fn) { 2225 buffer * const filename = buffer_init(); 2226 const size_t fnlen = strlen(fn); 2227 int ret = -1; 2228 #ifdef GLOB_BRACE 2229 int flags = GLOB_BRACE; 2230 #else 2231 int flags = 0; 2232 #endif 2233 glob_t gl; 2234 2235 if ((fn[0] == '/' || fn[0] == '\\') || 2236 (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\')) || 2237 (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\\'))) { 2238 buffer_copy_string_len(filename, fn, fnlen); 2239 } else { 2240 buffer_copy_path_len2(filename, BUF_PTR_LEN(context->basedir), 2241 fn, fnlen); 2242 } 2243 2244 switch (glob(filename->ptr, flags, NULL, &gl)) { 2245 case 0: 2246 for (size_t i = 0; i < gl.gl_pathc; ++i) { 2247 ret = config_parse_file_stream(srv, context, gl.gl_pathv[i]); 2248 if (0 != ret) break; 2249 } 2250 globfree(&gl); 2251 break; 2252 case GLOB_NOMATCH: 2253 if (filename->ptr[strcspn(filename->ptr, "*?[]{}")] != '\0') { /*(contains glob metachars)*/ 2254 ret = 0; /* not an error if no files match glob pattern */ 2255 } 2256 else { 2257 log_error(srv->errh, __FILE__, __LINE__, "include file not found: %s", filename->ptr); 2258 } 2259 break; 2260 case GLOB_ABORTED: 2261 case GLOB_NOSPACE: 2262 log_perror(srv->errh, __FILE__, __LINE__, "glob() %s failed", filename->ptr); 2263 break; 2264 } 2265 2266 buffer_free(filename); 2267 return ret; 2268 } 2269 2270 #ifdef __CYGWIN__ 2271 2272 static char* getCWD(char *buf, size_t sz) { 2273 if (NULL == getcwd(buf, sz)) { 2274 return NULL; 2275 } 2276 for (size_t i = 0; buf[i]; ++i) { 2277 if (buf[i] == '\\') buf[i] = '/'; 2278 } 2279 return buf; 2280 } 2281 2282 #define getcwd(buf, sz) getCWD((buf),(sz)) 2283 2284 #endif /* __CYGWIN__ */ 2285 2286 int config_parse_cmd(server *srv, config_t *context, const char *cmd) { 2287 int ret = 0; 2288 int fds[2]; 2289 char oldpwd[PATH_MAX]; 2290 2291 if (NULL == getcwd(oldpwd, sizeof(oldpwd))) { 2292 log_perror(srv->errh, __FILE__, __LINE__, "getcwd()"); 2293 return -1; 2294 } 2295 2296 if (!buffer_is_blank(context->basedir)) { 2297 if (0 != chdir(context->basedir->ptr)) { 2298 log_perror(srv->errh, __FILE__, __LINE__, 2299 "cannot change directory to %s", context->basedir->ptr); 2300 return -1; 2301 } 2302 } 2303 2304 if (fdevent_pipe_cloexec(fds, 65536)) { 2305 log_perror(srv->errh, __FILE__, __LINE__, "pipe()"); 2306 ret = -1; 2307 } 2308 else { 2309 char *shell = getenv("SHELL"); 2310 char *args[4]; 2311 pid_t pid; 2312 *(const char **)&args[0] = shell ? shell : "/bin/sh"; 2313 *(const char **)&args[1] = "-c"; 2314 *(const char **)&args[2] = cmd; 2315 args[3] = NULL; 2316 2317 pid = fdevent_fork_execve(args[0], args, NULL, -1, fds[1], -1, -1); 2318 if (-1 == pid) { 2319 log_perror(srv->errh, __FILE__, __LINE__, "fork/exec(%s)", cmd); 2320 ret = -1; 2321 } 2322 else { 2323 ssize_t rd; 2324 int wstatus = 0; 2325 buffer *out = buffer_init(); 2326 close(fds[1]); 2327 fds[1] = -1; 2328 do { 2329 rd = read(fds[0], buffer_string_prepare_append(out, 1023), 1023); 2330 if (rd >= 0) buffer_commit(out, (size_t)rd); 2331 } while (rd > 0 || (-1 == rd && errno == EINTR)); 2332 if (0 != rd) { 2333 log_perror(srv->errh, __FILE__, __LINE__, "read \"%s\"", cmd); 2334 ret = -1; 2335 } 2336 close(fds[0]); 2337 fds[0] = -1; 2338 if (pid != fdevent_waitpid(pid, &wstatus, 0)) { 2339 log_perror(srv->errh, __FILE__, __LINE__, "waitpid \"%s\"",cmd); 2340 ret = -1; 2341 } 2342 if (0 != wstatus) { 2343 log_error(srv->errh, __FILE__, __LINE__, 2344 "command \"%s\" exited non-zero: %d", 2345 cmd, WEXITSTATUS(wstatus)); 2346 ret = -1; 2347 } 2348 2349 if (-1 != ret) { 2350 ret = config_parse(srv, context, cmd, BUF_PTR_LEN(out)); 2351 } 2352 buffer_free(out); 2353 } 2354 if (-1 != fds[0]) close(fds[0]); 2355 if (-1 != fds[1]) close(fds[1]); 2356 } 2357 2358 if (0 != chdir(oldpwd)) { 2359 log_perror(srv->errh, __FILE__, __LINE__, 2360 "cannot change directory to %s", oldpwd); 2361 ret = -1; 2362 } 2363 return ret; 2364 } 2365 2366 static int config_remoteip_normalize_ipv6(buffer * const b, buffer * const tb) { 2367 /* $HTTP["remote-ip"] IPv6 accepted with or without '[]' for config compat 2368 * http_request_host_normalize() expects IPv6 with '[]', 2369 * and config processing at runtime expects COMP_HTTP_REMOTE_IP 2370 * compared without '[]', so strip '[]' after normalization */ 2371 buffer_clear(tb); 2372 if (b->ptr[0] != '[') 2373 buffer_append_str3(tb, 2374 CONST_STR_LEN("["), 2375 BUF_PTR_LEN(b), 2376 CONST_STR_LEN("]")); 2377 else 2378 buffer_append_string_buffer(tb, b); 2379 2380 int rc = http_request_host_normalize(tb, 0); 2381 if (0 == rc) { 2382 /* remove surrounding '[]' */ 2383 size_t blen = buffer_clen(tb); 2384 if (blen > 1) buffer_copy_string_len(b, tb->ptr+1, blen-2); 2385 } 2386 return rc; 2387 } 2388 2389 int config_remoteip_normalize(buffer * const b, buffer * const tb) { 2390 if (b->ptr[0] == '/') return 1; /*(skip AF_UNIX /path/file)*/ 2391 2392 const char * const slash = strchr(b->ptr, '/'); /* CIDR mask */ 2393 const char * const colon = strchr(b->ptr, ':'); /* IPv6 */ 2394 unsigned long nm_bits = 0; 2395 2396 if (NULL != slash) { 2397 char *nptr; 2398 nm_bits = strtoul(slash + 1, &nptr, 10); 2399 if (*nptr || 0 == nm_bits || nm_bits > (NULL != colon ? 128 : 32)) { 2400 /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/ 2401 return -1; 2402 } 2403 buffer_truncate(b, (size_t)(slash - b->ptr)); 2404 } 2405 2406 int family = colon ? AF_INET6 : AF_INET; 2407 int rc = (family == AF_INET) 2408 ? http_request_host_normalize(b, 0) 2409 : config_remoteip_normalize_ipv6(b, tb); 2410 2411 uint32_t len = buffer_clen(b); /*(save len before adding CIDR mask)*/ 2412 if (nm_bits) { 2413 buffer_append_string_len(b, CONST_STR_LEN("/")); 2414 buffer_append_int(b, (int)nm_bits); 2415 } 2416 2417 if (0 != rc) { 2418 return -1; 2419 } 2420 2421 /* extend b to hold structured data after end of string: 2422 * nm_bits and memory-aligned sock_addr for AF_INET or AF_INET6 (28 bytes)*/ 2423 char *after = buffer_string_prepare_append(b, 1 + 7 + 28); 2424 ++after; /*(increment to pos after string end '\0')*/ 2425 *(unsigned char *)after = (unsigned char)nm_bits; 2426 sock_addr * const addr = (sock_addr *)(((uintptr_t)after+1+7) & ~7); 2427 if (nm_bits) b->ptr[len] = '\0'; /*(sock_addr_inet_pton() w/o CIDR mask)*/ 2428 rc = sock_addr_inet_pton(addr, b->ptr, family, 0); 2429 if (nm_bits) b->ptr[len] = '/'; 2430 return (1 == rc); 2431 } 2432 2433 2434 static void context_init(server *srv, config_t *context) { 2435 context->srv = srv; 2436 context->ok = 1; 2437 vector_config_weak_init(&context->configs_stack); 2438 context->basedir = buffer_init(); 2439 } 2440 2441 static void context_free(config_t *context) { 2442 vector_config_weak_clear(&context->configs_stack); 2443 buffer_free(context->basedir); 2444 } 2445 2446 __attribute_noinline__ 2447 static void config_vars_init (array *a) { 2448 char dcwd[PATH_MAX]; 2449 if (NULL != getcwd(dcwd, sizeof(dcwd))) 2450 array_set_key_value(a, CONST_STR_LEN("var.CWD"), dcwd, strlen(dcwd)); 2451 2452 *array_get_int_ptr(a, CONST_STR_LEN("var.PID")) = getpid(); 2453 } 2454 2455 int config_read(server *srv, const char *fn) { 2456 config_t context; 2457 data_config *dc; 2458 int ret; 2459 char *pos; 2460 2461 context_init(srv, &context); 2462 context.all_configs = srv->config_context; 2463 2464 #ifdef __WIN32 2465 pos = strrchr(fn, '\\'); 2466 #else 2467 pos = strrchr(fn, '/'); 2468 #endif 2469 if (pos) { 2470 buffer_copy_string_len(context.basedir, fn, pos - fn + 1); 2471 } 2472 2473 dc = data_config_init(); 2474 buffer_copy_string_len(&dc->key, CONST_STR_LEN("global")); 2475 config_vars_init(dc->value); /* default context */ 2476 2477 force_assert(context.all_configs->used == 0); 2478 dc->context_ndx = context.all_configs->used; 2479 array_insert_unique(context.all_configs, (data_unset *)dc); 2480 context.current = dc; 2481 2482 ret = (0 != strcmp(fn, "-")) /*(incompatible with one-shot mode)*/ 2483 ? config_parse_file_stream(srv, &context, fn) 2484 : config_parse_stdin(srv, &context); 2485 2486 /* remains nothing if parser is ok */ 2487 force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used)); 2488 context_free(&context); 2489 2490 if (0 != ret) { 2491 return ret; 2492 } 2493 2494 /* reorder dc->context_ndx to match srv->config_context->data[] index. 2495 * srv->config_context->data[] may have been re-ordered in configparser.y. 2496 * Since the dc->context_ndx (id) is reused by config_insert*() and by 2497 * plugins to index into srv->config_context->data[], reorder into the 2498 * order encountered during config file parsing for least surprise to 2499 * end-users writing config files. Note: this manipulation *breaks* the 2500 * srv->config_context->sorted[] structure, so searching the array by key 2501 * is no longer valid. */ 2502 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 2503 dc = (data_config *)srv->config_context->data[i]; 2504 if (dc->context_ndx == (int)i) continue; 2505 for (uint32_t j = i; j < srv->config_context->used; ++j) { 2506 dc = (data_config *)srv->config_context->data[j]; 2507 if (dc->context_ndx == (int)i) { 2508 srv->config_context->data[j] = srv->config_context->data[i]; 2509 srv->config_context->data[i] = (data_unset *)dc; 2510 break; 2511 } 2512 } 2513 } 2514 2515 if (0 != config_insert_srvconf(srv)) { 2516 return -1; 2517 } 2518 2519 if (0 != config_insert(srv)) { 2520 return -1; 2521 } 2522 2523 return 0; 2524 } 2525 2526 int config_set_defaults(server *srv) { 2527 size_t i; 2528 request_config *s = &((config_data_base *)srv->config_data_base)->defaults; 2529 struct stat st1, st2; 2530 2531 if (fdevent_config(&srv->srvconf.event_handler, srv->errh) <= 0) 2532 return -1; 2533 2534 if (srv->srvconf.changeroot) { 2535 if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) { 2536 log_error(srv->errh, __FILE__, __LINE__, 2537 "server.chroot doesn't exist: %s", 2538 srv->srvconf.changeroot->ptr); 2539 return -1; 2540 } 2541 if (!S_ISDIR(st1.st_mode)) { 2542 log_error(srv->errh, __FILE__, __LINE__, 2543 "server.chroot isn't a directory: %s", 2544 srv->srvconf.changeroot->ptr); 2545 return -1; 2546 } 2547 } 2548 2549 if (!srv->srvconf.upload_tempdirs->used) { 2550 const char *tmpdir = getenv("TMPDIR"); 2551 if (NULL == tmpdir) tmpdir = "/var/tmp"; 2552 array_insert_value(srv->srvconf.upload_tempdirs, tmpdir, strlen(tmpdir)); 2553 } 2554 2555 if (srv->srvconf.upload_tempdirs->used) { 2556 buffer * const tb = srv->tmp_buf; 2557 buffer_clear(tb); 2558 if (srv->srvconf.changeroot) { 2559 buffer_copy_buffer(tb, srv->srvconf.changeroot); 2560 } 2561 const size_t len = buffer_clen(tb); 2562 2563 for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) { 2564 const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i]; 2565 if (len) { 2566 buffer_truncate(tb, len); 2567 buffer_append_path_len(tb, BUF_PTR_LEN(&ds->value)); 2568 } else { 2569 buffer_copy_buffer(tb, &ds->value); 2570 } 2571 if (-1 == stat(tb->ptr, &st1)) { 2572 log_error(srv->errh, __FILE__, __LINE__, 2573 "server.upload-dirs doesn't exist: %s", tb->ptr); 2574 } else if (!S_ISDIR(st1.st_mode)) { 2575 log_error(srv->errh, __FILE__, __LINE__, 2576 "server.upload-dirs isn't a directory: %s", tb->ptr); 2577 } 2578 } 2579 } 2580 2581 chunkqueue_set_tempdirs_default( 2582 srv->srvconf.upload_tempdirs, 2583 srv->srvconf.upload_temp_file_size); 2584 2585 if (!s->document_root || buffer_is_blank(s->document_root)) { 2586 log_error(srv->errh, __FILE__, __LINE__, "server.document-root is not set"); 2587 return -1; 2588 } 2589 2590 if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */ 2591 s->force_lowercase_filenames = 0; /* default to 0 */ 2592 2593 buffer * const tb = srv->tmp_buf; 2594 buffer_copy_string_len_lc(tb, BUF_PTR_LEN(s->document_root)); 2595 2596 if (0 == stat(tb->ptr, &st1)) { 2597 int is_lower = 0; 2598 2599 is_lower = buffer_is_equal(tb, s->document_root); 2600 2601 /* lower-case existed, check upper-case */ 2602 buffer_copy_buffer(tb, s->document_root); 2603 2604 buffer_to_upper(tb); 2605 2606 /* we have to handle the special case that upper and lower-casing results in the same filename 2607 * as in server.document-root = "/" or "/12345/" */ 2608 2609 if (is_lower && buffer_is_equal(tb, s->document_root)) { 2610 /* lower-casing and upper-casing didn't result in 2611 * an other filename, no need to stat(), 2612 * just assume it is case-sensitive. */ 2613 2614 s->force_lowercase_filenames = 0; 2615 } else if (0 == stat(tb->ptr, &st2)) { 2616 2617 /* upper case exists too, doesn't the FS handle this ? */ 2618 2619 /* upper and lower have the same inode -> case-insensitve FS */ 2620 2621 if (st1.st_ino == st2.st_ino) { 2622 /* upper and lower have the same inode -> case-insensitve FS */ 2623 2624 s->force_lowercase_filenames = 1; 2625 } 2626 } 2627 } 2628 } 2629 2630 return 0; 2631 } 2632