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