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 = ck_calloc(1, sizeof(config_data_base)); 1090 srv->config_data_base = p; 1091 1092 if (!config_plugin_values_init(srv, p, cpk, "base")) 1093 return HANDLER_ERROR; 1094 1095 /* process and validate T_CONFIG_SCOPE_CONNECTION config directives 1096 * (init i to 0 if global context; to 1 to skip empty global context) */ 1097 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 1098 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 1099 for (; -1 != cpv->k_id; ++cpv) { 1100 switch (cpv->k_id) { 1101 case 0: /* server.document-root */ 1102 break; 1103 case 1: /* server.name */ 1104 if (buffer_is_blank(cpv->v.b)) 1105 cpv->v.b = NULL; 1106 break; 1107 case 2: /* server.tag */ 1108 if (!buffer_is_blank(cpv->v.b)) { 1109 buffer *b; 1110 *(const buffer **)&b = cpv->v.b; 1111 for (char *t=strchr(b->ptr,'\n'); t; t=strchr(t+2,'\n')) { 1112 /* not expecting admin to define multi-line server.tag, 1113 * but ensure server_tag has proper header continuation, 1114 * if needed */ 1115 if (t[1] == ' ' || t[1] == '\t') continue; 1116 off_t off = t - b->ptr; 1117 size_t len = buffer_clen(b); 1118 buffer_string_prepare_append(b, 1); 1119 t = b->ptr+off; 1120 memmove(t+2, t+1, len - off - 1); 1121 t[1] = ' '; 1122 buffer_commit(b, 1); 1123 } 1124 char *t = b->ptr; /*(make empty if tag is whitespace-only)*/ 1125 while (*t==' ' || *t=='\t' || *t=='\r' || *t=='\n') ++t; 1126 if (*t == '\0') buffer_truncate(b, 0); 1127 if (buffer_is_blank(b) && 0 != i) 1128 cpv->v.b = NULL; 1129 else { /* prep for use by h2.c:h2_send_headers() */ 1130 buffer_string_prepare_append(b, 6); 1131 memcpy(b->ptr+buffer_clen(b)+1, "server", 6); 1132 } 1133 } 1134 else if (0 != i) 1135 cpv->v.b = NULL; 1136 break; 1137 case 3: /* server.max-request-size */ 1138 case 4: /* server.max-keep-alive-requests */ 1139 case 5: /* server.max-keep-alive-idle */ 1140 case 6: /* server.max-read-idle */ 1141 case 7: /* server.max-write-idle */ 1142 break; 1143 case 8: /* server.errorfile-prefix */ 1144 case 9: /* server.error-handler */ 1145 case 10:/* server.error-handler-404 */ 1146 if (buffer_is_blank(cpv->v.b)) 1147 cpv->v.b = NULL; 1148 break; 1149 case 11:/* server.error-intercept */ 1150 case 12:/* server.force-lowercase-filenames */ 1151 break; 1152 case 13:/* server.follow-symlink */ 1153 #ifndef HAVE_LSTAT 1154 if (0 == cpv->v.u) 1155 log_error(srv->errh, __FILE__, __LINE__, 1156 "Your system lacks lstat(). " 1157 "We can not differ symlinks from files. " 1158 "Please remove server.follow-symlinks from your config."); 1159 #endif 1160 break; 1161 case 14:/* server.protocol-http11 */ 1162 case 15:/* server.range-requests */ 1163 break; 1164 case 16:/* server.stream-request-body */ 1165 if (cpv->v.shrt & FDEVENT_STREAM_REQUEST_BUFMIN) 1166 cpv->v.shrt |=FDEVENT_STREAM_REQUEST; 1167 break; 1168 case 17:/* server.stream-response-body */ 1169 if (cpv->v.shrt & FDEVENT_STREAM_RESPONSE_BUFMIN) 1170 cpv->v.shrt |=FDEVENT_STREAM_RESPONSE; 1171 break; 1172 case 18:{/*server.kbytes-per-second */ 1173 off_t * const cnt = ck_malloc(2*sizeof(off_t)); 1174 cnt[0] = 0; 1175 cnt[1] = (off_t)cpv->v.shrt << 10; 1176 cpv->v.v = cnt; 1177 cpv->vtype = T_CONFIG_LOCAL; 1178 break; 1179 } 1180 case 19:/* connection.kbytes-per-second */ 1181 break; 1182 case 20:{/* mimetype.assign */ 1183 /* translate "application/javascript" to "text/javascript" */ 1184 data_string * const ds = (data_string *) 1185 array_get_data_unset(cpv->v.a, CONST_STR_LEN(".js")); 1186 if (NULL != ds /*(note: this does not catch w/ ";charset=...")*/ 1187 && buffer_eq_slen(&ds->value, 1188 CONST_STR_LEN("application/javascript"))) 1189 buffer_copy_string_len(&ds->value, 1190 CONST_STR_LEN("text/javascript")); 1191 break; 1192 } 1193 case 21:/* mimetype.use-xattr */ 1194 case 22:/* etag.use-inode */ 1195 case 23:/* etag.use-mtime */ 1196 case 24:/* etag.use-size */ 1197 case 25:/* debug.log-condition-handling */ 1198 case 26:/* debug.log-file-not-found */ 1199 case 27:/* debug.log-request-handling */ 1200 case 28:/* debug.log-request-header */ 1201 case 29:/* debug.log-response-header */ 1202 case 30:/* debug.log-timeouts */ 1203 case 31:/* debug.log-state-handling */ 1204 case 32:/* server.errorlog *//*must match config_log_error_open*/ 1205 case 33:/* server.breakagelog */ /* match config_log_error_open*/ 1206 break; 1207 default:/* should not happen */ 1208 break; 1209 } 1210 } 1211 } 1212 1213 p->defaults.errh = srv->errh; 1214 p->defaults.max_keep_alive_requests = 1000; 1215 p->defaults.max_keep_alive_idle = 5; 1216 p->defaults.max_read_idle = 60; 1217 p->defaults.max_write_idle = 360; 1218 p->defaults.follow_symlink = 1; 1219 p->defaults.allow_http11 = 1; 1220 p->defaults.etag_flags = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE; 1221 p->defaults.range_requests = 1; 1222 /* use 2 to detect later if value is set by user config in global section */ 1223 p->defaults.force_lowercase_filenames = 2; 1224 1225 /*(global, but store in r->conf.http_parseopts)*/ 1226 p->defaults.http_parseopts = 1227 (srv->srvconf.http_header_strict ? HTTP_PARSEOPT_HEADER_STRICT :0) 1228 | (srv->srvconf.http_host_strict ? (HTTP_PARSEOPT_HOST_STRICT 1229 |HTTP_PARSEOPT_HOST_NORMALIZE) :0) 1230 | (srv->srvconf.http_host_normalize ? HTTP_PARSEOPT_HOST_NORMALIZE :0) 1231 | (srv->srvconf.http_method_get_body ? HTTP_PARSEOPT_METHOD_GET_BODY :0); 1232 p->defaults.http_parseopts |= srv->srvconf.http_url_normalize; 1233 p->defaults.mimetypes = &srv->srvconf.empty_array; /*(must not be NULL)*/ 1234 p->defaults.h2proto = srv->srvconf.h2proto; 1235 1236 /* initialize p->defaults from global config context */ 1237 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 1238 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 1239 if (-1 != cpv->k_id) 1240 config_merge_config(&p->defaults, cpv); 1241 } 1242 1243 /* (after processing config defaults) */ 1244 p->defaults.max_request_field_size = srv->srvconf.max_request_field_size; 1245 p->defaults.log_request_header_on_error = 1246 srv->srvconf.log_request_header_on_error; 1247 if (p->defaults.log_request_handling || p->defaults.log_request_header) 1248 p->defaults.log_request_header_on_error = 1; 1249 1250 request_config_set_defaults(&p->defaults); 1251 1252 return rc; 1253 } 1254 1255 int config_finalize(server *srv, const buffer *default_server_tag) { 1256 /* (call after plugins_call_set_defaults()) */ 1257 1258 config_data_base * const p = srv->config_data_base; 1259 1260 /* settings might be enabled during plugins_call_set_defaults() */ 1261 p->defaults.high_precision_timestamps = 1262 srv->srvconf.high_precision_timestamps = 1263 config_feature_bool(srv, "server.metrics-high-precision", 1264 srv->srvconf.high_precision_timestamps); 1265 1266 /* configure default server_tag if not set 1267 * (if configured to blank, unset server_tag)*/ 1268 if (!p->defaults.server_tag) 1269 p->defaults.server_tag = default_server_tag; 1270 else if (buffer_is_blank(p->defaults.server_tag)) 1271 p->defaults.server_tag = NULL; 1272 1273 /* dump unused config keys */ 1274 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 1275 array *config = ((data_config *)srv->config_context->data[i])->value; 1276 for (uint32_t j = 0; config && j < config->used; ++j) { 1277 const buffer * const k = &config->data[j]->key; 1278 1279 /* all var.* is known as user defined variable */ 1280 if (strncmp(k->ptr, "var.", sizeof("var.") - 1) == 0) 1281 continue; 1282 /* mod_dirlisting not loaded if dir-listing.activate not enabled */ 1283 if (strncmp(k->ptr, "dir-listing.", sizeof("dir-listing.") - 1) == 0 1284 && strcmp(k->ptr, "dir-listing.activate") != 0) 1285 continue; 1286 1287 if (!array_get_element_klen(srv->srvconf.config_touched, 1288 BUF_PTR_LEN(k))) 1289 log_error(srv->errh, __FILE__, __LINE__, 1290 "WARNING: unknown config-key: %s (ignored)", k->ptr); 1291 } 1292 } 1293 1294 array_free(srv->srvconf.config_touched); 1295 srv->srvconf.config_touched = NULL; 1296 1297 if (srv->srvconf.config_unsupported || srv->srvconf.config_deprecated) { 1298 if (srv->srvconf.config_unsupported) 1299 log_error(srv->errh, __FILE__, __LINE__, 1300 "Configuration contains unsupported keys. Going down."); 1301 if (srv->srvconf.config_deprecated) 1302 log_error(srv->errh, __FILE__, __LINE__, 1303 "Configuration contains deprecated keys. Going down."); 1304 return 0; 1305 } 1306 1307 /* check if condition regex captures are used by modules (redirect,rewrite) 1308 * and convert back to regex if condition was simplified to non-regex by 1309 * configparser_simplify_regex() */ 1310 if (__builtin_expect( (srv->config_captures != 0), 0)) { 1311 for (uint32_t i = 1; i < srv->config_context->used; ++i) { 1312 data_config * const dc = 1313 (data_config *)srv->config_context->data[i]; 1314 if (__builtin_expect( (0 == dc->capture_idx), 1)) 1315 continue; 1316 switch (dc->cond) { 1317 case CONFIG_COND_EQ: 1318 case CONFIG_COND_PREFIX: 1319 case CONFIG_COND_SUFFIX: 1320 break; 1321 /*case CONFIG_COND_NE:*/ 1322 /*case CONFIG_COND_MATCH:*/ 1323 /*case CONFIG_COND_NOMATCH:*/ 1324 /*case CONFIG_COND_ELSE:*/ 1325 default: 1326 continue; 1327 } 1328 buffer * const b = &dc->string; 1329 if (dc->cond != CONFIG_COND_SUFFIX || b->ptr[0] == '.') { 1330 buffer_extend(b, 1); 1331 memmove(b->ptr+1, b->ptr, buffer_clen(b)-1); 1332 b->ptr[0] = (dc->cond == CONFIG_COND_SUFFIX) ? '\\' : '^'; 1333 } 1334 if (dc->cond != CONFIG_COND_PREFIX) 1335 buffer_append_char(b, '$'); 1336 dc->cond = CONFIG_COND_MATCH; 1337 /*(config_pcre_keyvalue())*/ 1338 const int pcre_jit = config_feature_bool(srv, "server.pcre_jit", 1); 1339 if (!data_config_pcre_compile(dc, pcre_jit, srv->errh)) 1340 return 0; 1341 } 1342 } 1343 1344 #ifdef HAVE_PCRE2_H 1345 for (uint32_t i = 1; i < srv->config_context->used; ++i) { 1346 data_config * const dc = 1347 (data_config *)srv->config_context->data[i]; 1348 if ((dc->cond == CONFIG_COND_MATCH || dc->cond == CONFIG_COND_NOMATCH) 1349 && 0 == dc->capture_idx) { 1350 if (__builtin_expect( (NULL == srv->match_data), 0)) { 1351 #if 0 1352 /* calculate max output vector size to save a few bytes; 1353 * currently using hard-coded ovec_max = 10 below 1354 * (increase in code size is probably more than bytes saved) */ 1355 uint32_t ovec_max = 0; 1356 for (uint32_t j = i; j < srv->config_context->used; ++j) { 1357 const data_config * const dc = 1358 (data_config *)srv->config_context->data[j]; 1359 if ((dc->cond == CONFIG_COND_MATCH 1360 || dc->cond == CONFIG_COND_NOMATCH) 1361 && 0 == dc->capture_idx) { 1362 uint32_t v; 1363 if (0==pcre2_pattern_info(dc->code, 1364 PCRE2_INFO_CAPTURECOUNT,&v)) { 1365 if (ovec_max < v) 1366 ovec_max = v; 1367 } 1368 } 1369 } 1370 #else 1371 uint32_t ovec_max = 10; 1372 #endif 1373 srv->match_data = pcre2_match_data_create(ovec_max, NULL); 1374 force_assert(srv->match_data); 1375 } 1376 dc->match_data = srv->match_data; 1377 } 1378 } 1379 #endif 1380 1381 return 1; 1382 } 1383 1384 1385 /* Save some bytes using buffer_append_string() in cold funcs to print config 1386 * (instead of buffer_append_string_len() w/ CONST_STR_LEN() on constant strs)*/ 1387 1388 static void config_print_by_type(const data_unset *du, buffer *b, int depth); 1389 1390 static void config_print_indent(buffer *b, int depth) { 1391 depth <<= 2; 1392 memset(buffer_extend(b, depth), ' ', depth); 1393 } 1394 1395 __attribute_pure__ 1396 static uint32_t config_print_array_max_klen(const array * const a) { 1397 uint32_t maxlen = 0; 1398 for (uint32_t i = 0; i < a->used; ++i) { 1399 uint32_t len = buffer_clen(&a->data[i]->key); 1400 if (maxlen < len) 1401 maxlen = len; 1402 } 1403 return maxlen; 1404 } 1405 1406 static void config_print_array(const array * const a, buffer * const b, int depth) { 1407 if (a->used <= 5 && (!a->used || buffer_is_unset(&a->data[0]->key))) { 1408 int oneline = 1; 1409 for (uint32_t i = 0; i < a->used; ++i) { 1410 data_unset *du = a->data[i]; 1411 if (du->type != TYPE_STRING && du->type != TYPE_INTEGER) { 1412 oneline = 0; 1413 break; 1414 } 1415 } 1416 if (oneline) { 1417 buffer_append_string(b, "("); 1418 for (uint32_t i = 0; i < a->used; ++i) { 1419 if (i != 0) 1420 buffer_append_string(b, ", "); 1421 config_print_by_type(a->data[i], b, depth + 1); 1422 } 1423 buffer_append_string(b, ")"); 1424 return; 1425 } 1426 } 1427 1428 const uint32_t maxlen = config_print_array_max_klen(a); 1429 buffer_append_string(b, "(\n"); 1430 for (uint32_t i = 0; i < a->used; ++i) { 1431 config_print_indent(b, depth + 1); 1432 data_unset *du = a->data[i]; 1433 if (!buffer_is_unset(&du->key)) { 1434 buffer_append_str3(b, CONST_STR_LEN("\""), 1435 BUF_PTR_LEN(&du->key), 1436 CONST_STR_LEN("\"")); 1437 int indent = (int)(maxlen - buffer_clen(&du->key)); 1438 if (indent > 0) 1439 memset(buffer_extend(b, indent), ' ', indent); 1440 buffer_append_string(b, " => "); 1441 } 1442 config_print_by_type(du, b, depth + 1); 1443 buffer_append_string(b, ",\n"); 1444 } 1445 config_print_indent(b, depth); 1446 buffer_append_string(b, ")"); 1447 } 1448 1449 static void config_print_config(const data_unset *d, buffer * const b, int depth) { 1450 data_config *dc = (data_config *)d; 1451 array *a = (array *)dc->value; 1452 1453 if (0 == dc->context_ndx) { 1454 buffer_append_string(b, "config {\n"); 1455 } 1456 else { 1457 if (dc->cond != CONFIG_COND_ELSE) { 1458 buffer_append_string(b, dc->comp_key); 1459 buffer_append_string(b, " "); 1460 } 1461 buffer_append_string(b, "{\n"); 1462 config_print_indent(b, depth + 1); 1463 buffer_append_string(b, "# block "); 1464 buffer_append_int(b, dc->context_ndx); 1465 buffer_append_string(b, "\n"); 1466 } 1467 ++depth; 1468 1469 const uint32_t maxlen = config_print_array_max_klen(a); 1470 for (uint32_t i = 0; i < a->used; ++i) { 1471 config_print_indent(b, depth); 1472 data_unset *du = a->data[i]; 1473 buffer_append_string_buffer(b, &du->key); 1474 int indent = (int)(maxlen - buffer_clen(&du->key)); 1475 if (indent > 0) 1476 memset(buffer_extend(b, indent), ' ', indent); 1477 buffer_append_string(b, " = "); 1478 config_print_by_type(du, b, depth); 1479 buffer_append_string(b, "\n"); 1480 } 1481 1482 buffer_append_string(b, "\n"); 1483 for (uint32_t i = 0; i < dc->children.used; ++i) { 1484 data_config *dcc = dc->children.data[i]; 1485 1486 /* only the 1st block of chaining */ 1487 if (NULL == dcc->prev) { 1488 buffer_append_string(b, "\n"); 1489 config_print_indent(b, depth); 1490 config_print_by_type((data_unset *) dcc, b, depth); 1491 buffer_append_string(b, "\n"); 1492 } 1493 } 1494 1495 --depth; 1496 config_print_indent(b, depth); 1497 buffer_append_string(b, "}"); 1498 if (0 != dc->context_ndx) { 1499 buffer_append_string(b, " # end of "); 1500 buffer_append_string(b, (dc->cond != CONFIG_COND_ELSE) 1501 ? dc->comp_key 1502 : "else"); 1503 } 1504 1505 if (dc->next) { 1506 buffer_append_string(b, "\n"); 1507 config_print_indent(b, depth); 1508 buffer_append_string(b, "else "); 1509 config_print_by_type((data_unset *)dc->next, b, depth); 1510 } 1511 } 1512 1513 static void config_print_string(const data_unset *du, buffer * const b) { 1514 /* print out the string as is, except prepend '"' with backslash */ 1515 const buffer * const vb = &((data_string *)du)->value; 1516 char *dst = buffer_string_prepare_append(b, buffer_clen(vb)*2); 1517 uint32_t n = 0; 1518 dst[n++] = '"'; 1519 if (vb->ptr) { 1520 for (const char *p = vb->ptr; *p; ++p) { 1521 if (*p == '"') 1522 dst[n++] = '\\'; 1523 dst[n++] = *p; 1524 } 1525 } 1526 dst[n++] = '"'; 1527 buffer_commit(b, n); 1528 } 1529 1530 __attribute_cold__ 1531 static void config_print_by_type(const data_unset * const du, buffer * const b, int depth) { 1532 switch (du->type) { 1533 case TYPE_STRING: 1534 config_print_string(du, b); 1535 break; 1536 case TYPE_INTEGER: 1537 buffer_append_int(b, ((data_integer *)du)->value); 1538 break; 1539 case TYPE_ARRAY: 1540 config_print_array(&((data_array *)du)->value, b, depth); 1541 break; 1542 case TYPE_CONFIG: 1543 config_print_config(du, b, depth); 1544 break; 1545 default: 1546 /*if (du->fn->print) du->fn->print(du, b, depth);*/ 1547 break; 1548 } 1549 } 1550 1551 void config_print(server *srv) { 1552 buffer_clear(srv->tmp_buf); 1553 data_unset *dc = srv->config_context->data[0]; 1554 config_print_by_type(dc, srv->tmp_buf, 0); 1555 } 1556 1557 void config_free(server *srv) { 1558 /*request_config_set_defaults(NULL);*//*(not necessary)*/ 1559 config_free_config(srv->config_data_base); 1560 1561 array_free(srv->config_context); 1562 array_free(srv->srvconf.config_touched); 1563 array_free(srv->srvconf.modules); 1564 array_free(srv->srvconf.upload_tempdirs); 1565 #ifdef HAVE_PCRE2_H 1566 if (NULL == srv->match_data) pcre2_match_data_free(srv->match_data); 1567 #endif 1568 } 1569 1570 void config_init(server *srv) { 1571 srv->config_context = array_init(16); 1572 srv->srvconf.config_touched = array_init(128); 1573 1574 srv->srvconf.port = 0; 1575 srv->srvconf.dont_daemonize = 0; 1576 srv->srvconf.preflight_check = 0; 1577 srv->srvconf.compat_module_load = 1; 1578 srv->srvconf.systemd_socket_activation = 0; 1579 1580 srv->srvconf.high_precision_timestamps = 0; 1581 srv->srvconf.max_request_field_size = 8192; 1582 1583 srv->srvconf.http_header_strict = 1; 1584 srv->srvconf.http_host_strict = 1; /*(implies http_host_normalize)*/ 1585 srv->srvconf.http_host_normalize = 0; 1586 srv->srvconf.http_url_normalize = 1587 HTTP_PARSEOPT_URL_NORMALIZE 1588 | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED 1589 | HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT 1590 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE 1591 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE 1592 | HTTP_PARSEOPT_URL_NORMALIZE_INVALID_UTF8_REJECT; 1593 1594 srv->srvconf.modules = array_init(16); 1595 srv->srvconf.modules_dir = LIBRARY_DIR; 1596 srv->srvconf.upload_tempdirs = array_init(2); 1597 } 1598 1599 /** 1600 * open the errorlog 1601 * 1602 * we have 4 possibilities: 1603 * - stderr (default) 1604 * - syslog 1605 * - logfile 1606 * - pipe 1607 * 1608 */ 1609 1610 static void config_log_error_open_syslog(server *srv, log_error_st *errh, const buffer *syslog_facility) { 1611 #ifdef HAVE_SYSLOG_H 1612 /*assert(errh->mode == FDLOG_FD);*/ 1613 /*assert(errh->fd == STDERR_FILENO);*/ 1614 errh->mode = FDLOG_SYSLOG; 1615 errh->fd = -1; 1616 /* perhaps someone wants to use syslog() */ 1617 int facility = -1; 1618 if (syslog_facility) { 1619 static const struct facility_name_st { 1620 const char *name; 1621 int val; 1622 } facility_names[] = { 1623 { "auth", LOG_AUTH } 1624 #ifdef LOG_AUTHPRIV 1625 ,{ "authpriv", LOG_AUTHPRIV } 1626 #endif 1627 #ifdef LOG_CRON 1628 ,{ "cron", LOG_CRON } 1629 #endif 1630 ,{ "daemon", LOG_DAEMON } 1631 #ifdef LOG_FTP 1632 ,{ "ftp", LOG_FTP } 1633 #endif 1634 #ifdef LOG_KERN 1635 ,{ "kern", LOG_KERN } 1636 #endif 1637 #ifdef LOG_LPR 1638 ,{ "lpr", LOG_LPR } 1639 #endif 1640 #ifdef LOG_MAIL 1641 ,{ "mail", LOG_MAIL } 1642 #endif 1643 #ifdef LOG_NEWS 1644 ,{ "news", LOG_NEWS } 1645 #endif 1646 ,{ "security", LOG_AUTH } /* DEPRECATED */ 1647 #ifdef LOG_SYSLOG 1648 ,{ "syslog", LOG_SYSLOG } 1649 #endif 1650 #ifdef LOG_USER 1651 ,{ "user", LOG_USER } 1652 #endif 1653 #ifdef LOG_UUCP 1654 ,{ "uucp", LOG_UUCP } 1655 #endif 1656 ,{ "local0", LOG_LOCAL0 } 1657 ,{ "local1", LOG_LOCAL1 } 1658 ,{ "local2", LOG_LOCAL2 } 1659 ,{ "local3", LOG_LOCAL3 } 1660 ,{ "local4", LOG_LOCAL4 } 1661 ,{ "local5", LOG_LOCAL5 } 1662 ,{ "local6", LOG_LOCAL6 } 1663 ,{ "local7", LOG_LOCAL7 } 1664 }; 1665 for (unsigned int i = 0; i < sizeof(facility_names)/sizeof(facility_names[0]); ++i) { 1666 const struct facility_name_st *f = facility_names+i; 1667 if (0 == strcmp(syslog_facility->ptr, f->name)) { 1668 facility = f->val; 1669 break; 1670 } 1671 } 1672 if (-1 == facility) { 1673 log_error(srv->errh, __FILE__, __LINE__, 1674 "unrecognized server.syslog-facility: \"%s\"; " 1675 "defaulting to \"daemon\" facility", 1676 syslog_facility->ptr); 1677 } 1678 } 1679 openlog("lighttpd", LOG_CONS|LOG_PID, -1==facility ? LOG_DAEMON : facility); 1680 #endif 1681 } 1682 1683 int config_log_error_open(server *srv) { 1684 /* logs are opened after preflight check (srv->srvconf.preflight_check) 1685 * and after dropping privileges instead of being opened during config 1686 * processing */ 1687 #ifdef __clang_analyzer__ 1688 force_assert(srv->errh); 1689 #endif 1690 1691 config_data_base * const p = srv->config_data_base; 1692 log_error_st *serrh = NULL; 1693 1694 /* future: might be slightly faster to have allocated array of open files 1695 * rather than walking config, but only might matter with many directives */ 1696 /* (init i to 0 if global context; to 1 to skip empty global context) */ 1697 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 1698 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 1699 for (; -1 != cpv->k_id; ++cpv) { 1700 const char *fn = NULL; 1701 log_error_st *errh = NULL; 1702 switch (cpv->k_id) { 1703 /* NB: these indexes are repeated below switch() block 1704 * and all must stay in sync with configfile.c */ 1705 case 32:/* server.errorlog */ 1706 if (0 == i) { 1707 if (srv->srvconf.errorlog_use_syslog) continue; 1708 errh = srv->errh; 1709 } 1710 __attribute_fallthrough__ 1711 case 33:/* server.breakagelog */ 1712 if (!buffer_is_blank(cpv->v.b)) fn = cpv->v.b->ptr; 1713 break; 1714 default: 1715 break; 1716 } 1717 1718 if (NULL == fn) continue; 1719 1720 fdlog_st * const fdlog = fdlog_open(fn); 1721 if (NULL == fdlog) { 1722 log_perror(srv->errh, __FILE__, __LINE__, 1723 "opening errorlog '%s' failed", fn); 1724 return -1; 1725 } 1726 1727 if (errh) { 1728 /*(logfiles are opened early in setup; this function is called 1729 * prior to set_defaults hook, and modules should not save a 1730 * pointer to srv->errh until set_defaults hook or later)*/ 1731 p->defaults.errh = srv->errh = fdlog; 1732 log_set_global_errh(srv->errh, 0); 1733 } 1734 cpv->v.v = errh = fdlog; 1735 cpv->vtype = T_CONFIG_LOCAL; 1736 1737 if (0 == i && errh != srv->errh) /*(top-level server.breakagelog)*/ 1738 serrh = errh; 1739 } 1740 } 1741 1742 if (config_feature_bool(srv, "server.errorlog-high-precision", 0)) 1743 log_set_global_errh(srv->errh, 1); 1744 1745 if (srv->srvconf.errorlog_use_syslog) /*(restricted to global scope)*/ 1746 config_log_error_open_syslog(srv, srv->errh, 1747 srv->srvconf.syslog_facility); 1748 else if (srv->errh->mode == FDLOG_FD && !srv->srvconf.dont_daemonize) 1749 srv->errh->fd = -1; 1750 /* We can only log to stderr in dont-daemonize mode; 1751 * if we do daemonize and no errorlog file is specified, 1752 * we log into /dev/null 1753 */ 1754 1755 /* Note: serrh should not be stored in p->defaults.serrh 1756 * If left as NULL, scripts (e.g. mod_cgi and mod_ssi exec) will inherit 1757 * the current STDERR_FILENO, which already is the top-level breakagelog. */ 1758 /*p->defaults.serrh = serrh;*/ 1759 1760 int errfd; 1761 if (NULL != serrh) { 1762 if (srv->errh->mode == FDLOG_FD) { 1763 srv->errh->fd = dup(STDERR_FILENO); 1764 fdevent_setfd_cloexec(srv->errh->fd); 1765 } 1766 1767 errfd = serrh->fd; 1768 if (*serrh->fn == '|') fdlog_pipe_serrh(errfd); /* breakagelog */ 1769 } 1770 else if (!srv->srvconf.dont_daemonize) { 1771 /* move STDERR_FILENO to /dev/null */ 1772 if (-1 == (errfd = fdevent_open_devnull())) { 1773 log_perror(srv->errh,__FILE__,__LINE__,"opening /dev/null failed"); 1774 return -1; 1775 } 1776 } 1777 else { 1778 /*(leave STDERR_FILENO as-is)*/ 1779 errfd = -1; 1780 } 1781 1782 if (0 != fdevent_set_stdin_stdout_stderr(-1, -1, errfd)) { 1783 log_perror(srv->errh, __FILE__, __LINE__, "setting stderr failed"); 1784 #ifdef FD_CLOEXEC 1785 if (-1 != errfd && NULL == serrh) close(errfd); 1786 #endif 1787 return -1; 1788 } 1789 #ifdef FD_CLOEXEC 1790 if (-1 != errfd && NULL == serrh) close(errfd); 1791 #endif 1792 1793 if (NULL != serrh) { 1794 close(errfd); /* serrh->fd */ 1795 serrh->fd = STDERR_FILENO; 1796 } 1797 1798 return 0; 1799 } 1800 1801 void config_log_error_close(server *srv) { 1802 config_data_base * const p = srv->config_data_base; 1803 if (NULL == p) return; 1804 1805 /*(reset serrh just in case; should not be used after this func returns)*/ 1806 p->defaults.serrh = NULL; 1807 1808 fdlog_closeall(srv->errh); /*(close all except srv->errh)*/ 1809 1810 if (srv->errh->mode == FDLOG_SYSLOG) { 1811 srv->errh->mode = FDLOG_FD; 1812 srv->errh->fd = STDERR_FILENO; 1813 #ifdef HAVE_SYSLOG_H 1814 closelog(); 1815 #endif 1816 } 1817 } 1818 1819 1820 1821 typedef struct { 1822 const char *source; 1823 const char *input; 1824 int offset; 1825 int size; 1826 1827 int line_pos; 1828 int line; 1829 1830 int in_key; 1831 int parens; 1832 int in_cond; 1833 int simulate_eol; 1834 log_error_st *errh; 1835 } tokenizer_t; 1836 1837 __attribute_pure__ 1838 static int config_skip_newline(const tokenizer_t * const t) { 1839 const char * const s = t->input + t->offset; 1840 /*force_assert(s[0] == '\r' || s[0] == '\n');*/ 1841 return 1 + (s[0] == '\r' && s[1] == '\n'); 1842 } 1843 1844 __attribute_noinline__ 1845 __attribute_pure__ 1846 static int config_skip_comment(const tokenizer_t * const t) { 1847 /*assert(t->input[t->offset] == '#');*/ 1848 const char *s = t->input + t->offset; 1849 do { ++s; } while (*s && *s != '\r' && *s != '\n'); 1850 return (int)(s - t->input); 1851 } 1852 1853 __attribute_cold__ 1854 static int config_tokenizer_err(tokenizer_t *t, const char *file, unsigned int line, const char *msg) { 1855 log_error(t->errh, file, line, "source: %s line: %d pos: %d %s", 1856 t->source, t->line, t->offset - t->line_pos, msg); 1857 return -1; 1858 } 1859 1860 static int config_tokenizer(tokenizer_t *t, buffer *token) { 1861 if (t->simulate_eol) { 1862 t->simulate_eol = 0; 1863 t->in_key = 1; 1864 buffer_copy_string_len(token, CONST_STR_LEN("(EOL)")); 1865 return TK_EOL; 1866 } 1867 1868 while (t->offset < t->size) { 1869 const char * const s = t->input + t->offset; 1870 switch (s[0]) { 1871 case '\t': 1872 case ' ': 1873 t->offset++; 1874 break; 1875 case '=': 1876 if (t->parens) { 1877 if (s[1] != '>') 1878 return config_tokenizer_err(t, __FILE__, __LINE__, 1879 "use => for assignments in arrays"); 1880 t->offset += 2; 1881 buffer_copy_string_len(token, s, 2); /* "=>" */ 1882 return TK_ARRAY_ASSIGN; 1883 } 1884 else if (t->in_cond) { 1885 int tid; 1886 switch (s[1]) { 1887 case '=': tid = TK_EQ; break; 1888 case '~': tid = TK_MATCH; break; 1889 case '^': tid = TK_PREFIX; break; 1890 case '$': tid = TK_SUFFIX; break; 1891 default: 1892 return config_tokenizer_err(t, __FILE__, __LINE__, 1893 "only == =~ =^ =$ are allowed in the condition"); 1894 } 1895 t->offset += 2; 1896 t->in_key = 1; 1897 t->in_cond = 0; 1898 buffer_copy_string_len(token, s, 2); /* "==" "=~" "=^" "=$" */ 1899 return tid; 1900 } 1901 else if (t->in_key) { 1902 t->offset++; 1903 buffer_copy_string_len(token, s, 1); /* "=" */ 1904 return TK_ASSIGN; 1905 } 1906 else 1907 return config_tokenizer_err(t, __FILE__, __LINE__, 1908 "unexpected equal-sign: ="); 1909 case '!': 1910 if (t->in_cond) { 1911 int tid; 1912 switch (s[1]) { 1913 case '=': tid = TK_NE; break; 1914 case '~': tid = TK_NOMATCH; break; 1915 default: 1916 return config_tokenizer_err(t, __FILE__, __LINE__, 1917 "only !~ and != are allowed in the condition"); 1918 } 1919 t->offset += 2; 1920 t->in_key = 1; 1921 t->in_cond = 0; 1922 buffer_copy_string_len(token, s, 2); /* "!=" "!~" */ 1923 return tid; 1924 } 1925 else 1926 return config_tokenizer_err(t, __FILE__, __LINE__, 1927 "unexpected exclamation-marks: !"); 1928 case '\n': 1929 case '\r': 1930 do { 1931 switch (t->input[t->offset]) { 1932 case '\r': 1933 case '\n': 1934 t->offset += config_skip_newline(t); 1935 t->line_pos = t->offset; 1936 t->line++; 1937 continue; 1938 case '#': 1939 t->offset = config_skip_comment(t); 1940 continue; 1941 case '\t': 1942 case ' ': 1943 t->offset++; 1944 continue; 1945 default: 1946 break; 1947 } 1948 break; 1949 } while (t->offset < t->size); 1950 if (!t->parens) { 1951 t->in_key = 1; 1952 buffer_copy_string_len(token, CONST_STR_LEN("(EOL)")); 1953 return TK_EOL; 1954 } 1955 break; 1956 case ',': 1957 t->offset++; 1958 if (t->parens) { 1959 buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)")); 1960 return TK_COMMA; 1961 } 1962 break; 1963 case '"': 1964 { 1965 /* search for the terminating " */ 1966 const char *start = s + 1; /*buffer_blank(token);*/ 1967 buffer_copy_string_len(token, CONST_STR_LEN("")); 1968 1969 int i; 1970 for (i = 1; s[i] && s[i] != '"'; ++i) { 1971 if (s[i] == '\\' && s[i+1] == '"') { 1972 buffer_append_string_len(token, start, s + i - start); 1973 start = s + ++i; /* step over '"' */ 1974 } 1975 } 1976 1977 if (s[i] == '\0') { 1978 return config_tokenizer_err(t, __FILE__, __LINE__, 1979 "missing closing quote"); 1980 } 1981 1982 t->offset += i + 1; 1983 buffer_append_string_len(token, start, s + i - start); 1984 return TK_STRING; 1985 } 1986 case '(': 1987 t->offset++; 1988 t->parens++; 1989 buffer_copy_string_len(token, s, 1); /* "(" */ 1990 return TK_LPARAN; 1991 case ')': 1992 if (!t->parens) 1993 return config_tokenizer_err(t, __FILE__, __LINE__, 1994 "close-parens seen open-parens"); 1995 t->offset++; 1996 t->parens--; 1997 buffer_copy_string_len(token, s, 1); /* ")" */ 1998 return TK_RPARAN; 1999 case '$': 2000 t->offset++; 2001 t->in_cond = 1; 2002 t->in_key = 0; 2003 buffer_copy_string_len(token, s, 1); /* "$" */ 2004 return TK_DOLLAR; 2005 case '+': 2006 if (s[1] == '=') { 2007 t->offset += 2; 2008 buffer_copy_string_len(token, s, 2); /* "+=" */ 2009 return TK_APPEND; 2010 } 2011 else { 2012 t->offset++; 2013 buffer_copy_string_len(token, s, 1); /* "+" */ 2014 return TK_PLUS; 2015 } 2016 case ':': 2017 if (s[1] != '=') 2018 return config_tokenizer_err(t, __FILE__, __LINE__, 2019 "unexpected character ':'"); 2020 t->offset += 2; 2021 buffer_copy_string_len(token, s, 2); /* ":=" */ 2022 return TK_FORCE_ASSIGN; 2023 case '{': 2024 t->offset++; 2025 buffer_copy_string_len(token, s, 1); /* "{" */ 2026 return TK_LCURLY; 2027 case '}': 2028 while (++t->offset < t->size) { 2029 int c = t->input[t->offset]; 2030 if (c == '\r' || c == '\n') { 2031 break; 2032 } 2033 else if (c == '#') { 2034 t->offset = config_skip_comment(t); 2035 break; 2036 } 2037 else if (c != ' ' && c != '\t') { 2038 t->simulate_eol = 1; 2039 break; 2040 } /* else (c == ' ' || c == '\t') */ 2041 } 2042 buffer_copy_string_len(token, s, 1); /* "}" */ 2043 return TK_RCURLY; 2044 case '[': 2045 t->offset++; 2046 buffer_copy_string_len(token, s, 1); /* "[" */ 2047 return TK_LBRACKET; 2048 case ']': 2049 t->offset++; 2050 buffer_copy_string_len(token, s, 1); /* "]" */ 2051 return TK_RBRACKET; 2052 case '#': 2053 t->offset = config_skip_comment(t); 2054 break; 2055 case '\0': 2056 config_tokenizer_err(t, __FILE__, __LINE__, "stray NUL"); 2057 return 0; 2058 default: 2059 if (t->in_cond) { 2060 int i = 0; 2061 while (light_isalpha(s[i]) || s[i] == '_') ++i; 2062 if (i && s[i]) { 2063 t->offset += i; 2064 buffer_copy_string_len(token, s, i); 2065 return TK_SRVVARNAME; 2066 } 2067 else 2068 return config_tokenizer_err(t, __FILE__, __LINE__, 2069 "invalid character in condition"); 2070 } 2071 else if (light_isdigit(s[0])) { 2072 /* take all digits */ 2073 int i = 1; 2074 while (light_isdigit(s[i])) ++i; 2075 t->offset += i; 2076 buffer_copy_string_len(token, s, i); 2077 return TK_INTEGER; 2078 } 2079 else { 2080 /* the key might consist of [-.0-9a-z] */ 2081 int i = 0; 2082 while (light_isalnum(s[i]) 2083 || s[i] == '.' 2084 || s[i] == '_' /* for env.* */ 2085 || s[i] == '-') ++i; 2086 2087 if (i && s[i]) { 2088 t->offset += i; 2089 buffer_copy_string_len(token, s, i); 2090 if (0 == strcmp(token->ptr, "include")) 2091 return TK_INCLUDE; 2092 else if (0 == strcmp(token->ptr, "include_shell")) 2093 return TK_INCLUDE_SHELL; 2094 else if (0 == strcmp(token->ptr, "global")) 2095 return TK_GLOBAL; 2096 else if (0 == strcmp(token->ptr, "else")) 2097 return TK_ELSE; 2098 else 2099 return TK_LKEY; 2100 } 2101 else if (0 == i 2102 && ((uint8_t *)s)[0] == 0xc2 2103 && ((uint8_t *)s)[1] == 0xa0) { 2104 /* treat U+00A0 (c2 a0) "NO-BREAK SPACE" as whitespace */ 2105 /* http://www.fileformat.info/info/unicode/char/a0/index.htm */ 2106 t->offset+=2; 2107 } 2108 else 2109 return config_tokenizer_err(t, __FILE__, __LINE__, 2110 "invalid character in variable name"); 2111 } 2112 break; 2113 } 2114 } 2115 return 0; 2116 } 2117 2118 static int config_parse(server *srv, config_t *context, const char *source, const char *input, int isize) { 2119 tokenizer_t t; 2120 buffer * const lasttoken = buffer_init(); 2121 int ret; 2122 2123 t.source = source; 2124 t.input = input; 2125 t.size = isize; 2126 t.offset = 0; 2127 t.line = 1; 2128 t.line_pos = 0; 2129 2130 t.in_key = 1; 2131 t.parens = 0; 2132 t.in_cond = 0; 2133 t.simulate_eol = 0; 2134 t.errh = srv->errh; 2135 2136 void * const pParser = configparserAlloc( malloc ); 2137 force_assert(pParser); 2138 do { 2139 ret = config_tokenizer(&t, lasttoken); 2140 if (__builtin_expect( (ret <= 0), 0)) 2141 break; 2142 buffer * const token = buffer_init(); 2143 buffer_copy_buffer(token, lasttoken); 2144 configparser(pParser, ret, token, context); 2145 /*token = NULL;*/ 2146 } while (context->ok); 2147 2148 if (ret != -1 && context->ok) { 2149 /* add an EOL at EOF, better than say sorry */ 2150 buffer * const token = buffer_init(); 2151 buffer_copy_string(token, "(EOL)"); 2152 configparser(pParser, TK_EOL, token, context); 2153 /*token = NULL;*/ 2154 if (context->ok) { 2155 configparser(pParser, 0, NULL, context); 2156 } 2157 } 2158 configparserFree(pParser, free); 2159 2160 if (ret == -1) { 2161 log_error(t.errh, __FILE__, __LINE__, 2162 "configfile parser failed at: %s", lasttoken->ptr); 2163 } else if (context->ok == 0) { 2164 log_error(t.errh, __FILE__, __LINE__, "source: %s line: %d pos: %d " 2165 "parser failed somehow near here: %s", 2166 t.source, t.line, t.offset - t.line_pos, lasttoken->ptr); 2167 ret = -1; 2168 } 2169 buffer_free(lasttoken); 2170 2171 return ret == -1 ? -1 : 0; 2172 } 2173 2174 __attribute_cold__ 2175 static int config_parse_stdin(server *srv, config_t *context) { 2176 buffer * const b = chunk_buffer_acquire(); 2177 size_t dlen; 2178 ssize_t n = -1; 2179 do { 2180 if (n > 0) 2181 buffer_commit(b, n); 2182 size_t avail = buffer_string_space(b); 2183 dlen = buffer_clen(b); 2184 if (__builtin_expect( (avail < 1024), 0)) { 2185 /*(arbitrary limit: 32 MB file; expect < 1 MB)*/ 2186 if (dlen >= 32*1024*1024) { 2187 log_error(srv->errh, __FILE__, __LINE__, 2188 "config read from stdin is way too large"); 2189 break; 2190 } 2191 avail = chunk_buffer_prepare_append(b, b->size+avail); 2192 } 2193 n = read(STDIN_FILENO, b->ptr+dlen, avail); 2194 } while (n > 0 || (n == -1 && errno == EINTR)); 2195 int rc = -1; 2196 if (0 == n) 2197 rc = dlen ? config_parse(srv, context, "-", b->ptr, (int)dlen) : 0; 2198 else 2199 log_perror(srv->errh, __FILE__, __LINE__, "config read from stdin"); 2200 2201 if (dlen) 2202 ck_memzero(b->ptr, dlen); 2203 chunk_buffer_release(b); 2204 return rc; 2205 } 2206 2207 static int config_parse_file_stream(server *srv, config_t *context, const char *fn) { 2208 off_t dlen = 32*1024*1024;/*(arbitrary limit: 32 MB file; expect < 1 MB)*/ 2209 char *data = fdevent_load_file(fn, &dlen, NULL, malloc, free); 2210 if (NULL == data) { 2211 log_perror(srv->errh, __FILE__, __LINE__, 2212 "opening configfile %s failed", fn); 2213 return -1; 2214 } 2215 2216 int rc = 0; 2217 if (dlen) { 2218 rc = config_parse(srv, context, fn, data, (int)dlen); 2219 ck_memzero(data, (size_t)dlen); 2220 } 2221 free(data); 2222 return rc; 2223 } 2224 2225 int config_parse_file(server *srv, config_t *context, const char *fn) { 2226 buffer * const filename = buffer_init(); 2227 const size_t fnlen = strlen(fn); 2228 int ret = -1; 2229 #ifdef GLOB_BRACE 2230 int flags = GLOB_BRACE; 2231 #else 2232 int flags = 0; 2233 #endif 2234 glob_t gl; 2235 2236 if (buffer_is_blank(context->basedir) || 2237 (fn[0] == '/' || fn[0] == '\\') || 2238 (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\')) || 2239 (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\\'))) { 2240 buffer_copy_string_len(filename, fn, fnlen); 2241 } else { 2242 buffer_copy_path_len2(filename, BUF_PTR_LEN(context->basedir), 2243 fn, fnlen); 2244 } 2245 2246 switch (glob(filename->ptr, flags, NULL, &gl)) { 2247 case 0: 2248 for (size_t i = 0; i < gl.gl_pathc; ++i) { 2249 ret = config_parse_file_stream(srv, context, gl.gl_pathv[i]); 2250 if (0 != ret) break; 2251 } 2252 globfree(&gl); 2253 break; 2254 case GLOB_NOMATCH: 2255 if (filename->ptr[strcspn(filename->ptr, "*?[]{}")] != '\0') { /*(contains glob metachars)*/ 2256 ret = 0; /* not an error if no files match glob pattern */ 2257 } 2258 else { 2259 log_error(srv->errh, __FILE__, __LINE__, "include file not found: %s", filename->ptr); 2260 } 2261 break; 2262 case GLOB_ABORTED: 2263 case GLOB_NOSPACE: 2264 log_perror(srv->errh, __FILE__, __LINE__, "glob() %s failed", filename->ptr); 2265 break; 2266 } 2267 2268 buffer_free(filename); 2269 return ret; 2270 } 2271 2272 #ifdef __CYGWIN__ 2273 2274 static char* getCWD(char *buf, size_t sz) { 2275 if (NULL == getcwd(buf, sz)) { 2276 return NULL; 2277 } 2278 for (size_t i = 0; buf[i]; ++i) { 2279 if (buf[i] == '\\') buf[i] = '/'; 2280 } 2281 return buf; 2282 } 2283 2284 #define getcwd(buf, sz) getCWD((buf),(sz)) 2285 2286 #endif /* __CYGWIN__ */ 2287 2288 int config_parse_cmd(server *srv, config_t *context, const char *cmd) { 2289 int ret = 0; 2290 int fds[2]; 2291 char oldpwd[PATH_MAX]; 2292 2293 if (NULL == getcwd(oldpwd, sizeof(oldpwd))) { 2294 log_perror(srv->errh, __FILE__, __LINE__, "getcwd()"); 2295 return -1; 2296 } 2297 2298 if (!buffer_is_blank(context->basedir)) { 2299 if (0 != chdir(context->basedir->ptr)) { 2300 log_perror(srv->errh, __FILE__, __LINE__, 2301 "cannot change directory to %s", context->basedir->ptr); 2302 return -1; 2303 } 2304 } 2305 2306 if (fdevent_pipe_cloexec(fds, 65536)) { 2307 log_perror(srv->errh, __FILE__, __LINE__, "pipe()"); 2308 ret = -1; 2309 } 2310 else { 2311 char *shell = getenv("SHELL"); 2312 char *args[4]; 2313 pid_t pid; 2314 if (shell && (0 == strcmp(shell, "/usr/bin/false") 2315 || 0 == strcmp(shell, "/bin/false"))) 2316 shell = NULL; 2317 *(const char **)&args[0] = shell ? shell : "/bin/sh"; 2318 *(const char **)&args[1] = "-c"; 2319 *(const char **)&args[2] = cmd; 2320 args[3] = NULL; 2321 2322 pid = fdevent_fork_execve(args[0], args, NULL, -1, fds[1], -1, -1); 2323 if (-1 == pid) { 2324 log_perror(srv->errh, __FILE__, __LINE__, "fork/exec(%s)", cmd); 2325 ret = -1; 2326 } 2327 else { 2328 ssize_t rd; 2329 int wstatus = 0; 2330 buffer *out = buffer_init(); 2331 close(fds[1]); 2332 fds[1] = -1; 2333 do { 2334 rd = read(fds[0], buffer_string_prepare_append(out, 1023), 1023); 2335 if (rd >= 0) buffer_commit(out, (size_t)rd); 2336 } while (rd > 0 || (-1 == rd && errno == EINTR)); 2337 if (0 != rd) { 2338 log_perror(srv->errh, __FILE__, __LINE__, "read \"%s\"", cmd); 2339 ret = -1; 2340 } 2341 close(fds[0]); 2342 fds[0] = -1; 2343 if (pid != fdevent_waitpid(pid, &wstatus, 0)) { 2344 log_perror(srv->errh, __FILE__, __LINE__, "waitpid \"%s\"",cmd); 2345 ret = -1; 2346 } 2347 if (0 != wstatus) { 2348 log_error(srv->errh, __FILE__, __LINE__, 2349 "command \"%s\" exited non-zero: %d", 2350 cmd, WEXITSTATUS(wstatus)); 2351 ret = -1; 2352 } 2353 2354 if (-1 != ret) { 2355 ret = config_parse(srv, context, cmd, BUF_PTR_LEN(out)); 2356 } 2357 buffer_free(out); 2358 } 2359 if (-1 != fds[0]) close(fds[0]); 2360 if (-1 != fds[1]) close(fds[1]); 2361 } 2362 2363 if (0 != chdir(oldpwd)) { 2364 log_perror(srv->errh, __FILE__, __LINE__, 2365 "cannot change directory to %s", oldpwd); 2366 ret = -1; 2367 } 2368 return ret; 2369 } 2370 2371 static int config_remoteip_normalize_ipv6(buffer * const b, buffer * const tb) { 2372 /* $HTTP["remote-ip"] IPv6 accepted with or without '[]' for config compat 2373 * http_request_host_normalize() expects IPv6 with '[]', 2374 * and config processing at runtime expects COMP_HTTP_REMOTE_IP 2375 * compared without '[]', so strip '[]' after normalization */ 2376 buffer_clear(tb); 2377 if (b->ptr[0] != '[') 2378 buffer_append_str3(tb, 2379 CONST_STR_LEN("["), 2380 BUF_PTR_LEN(b), 2381 CONST_STR_LEN("]")); 2382 else 2383 buffer_append_string_buffer(tb, b); 2384 2385 int rc = http_request_host_normalize(tb, 0); 2386 if (0 == rc) { 2387 /* remove surrounding '[]' */ 2388 size_t blen = buffer_clen(tb); 2389 if (blen > 1) buffer_copy_string_len(b, tb->ptr+1, blen-2); 2390 } 2391 return rc; 2392 } 2393 2394 int config_remoteip_normalize(buffer * const b, buffer * const tb) { 2395 if (b->ptr[0] == '/') return 1; /*(skip AF_UNIX /path/file)*/ 2396 2397 const char * const slash = strchr(b->ptr, '/'); /* CIDR mask */ 2398 const char * const colon = strchr(b->ptr, ':'); /* IPv6 */ 2399 unsigned long nm_bits = 0; 2400 2401 if (NULL != slash) { 2402 char *nptr; 2403 nm_bits = strtoul(slash + 1, &nptr, 10); 2404 if (*nptr || 0 == nm_bits || nm_bits > (NULL != colon ? 128 : 32)) { 2405 /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/ 2406 return -1; 2407 } 2408 buffer_truncate(b, (size_t)(slash - b->ptr)); 2409 } 2410 2411 int family = colon ? AF_INET6 : AF_INET; 2412 int rc = (family == AF_INET) 2413 ? http_request_host_normalize(b, 0) 2414 : config_remoteip_normalize_ipv6(b, tb); 2415 2416 uint32_t len = buffer_clen(b); /*(save len before adding CIDR mask)*/ 2417 if (nm_bits) { 2418 buffer_append_char(b, '/'); 2419 buffer_append_int(b, (int)nm_bits); 2420 } 2421 2422 if (0 != rc) { 2423 return -1; 2424 } 2425 2426 /* extend b to hold structured data after end of string: 2427 * nm_bits and memory-aligned sock_addr for AF_INET or AF_INET6 (28 bytes)*/ 2428 char *after = buffer_string_prepare_append(b, 1 + 7 + 28); 2429 ++after; /*(increment to pos after string end '\0')*/ 2430 *(unsigned char *)after = (unsigned char)nm_bits; 2431 sock_addr * const addr = (sock_addr *)(((uintptr_t)after+1+7) & ~7); 2432 if (nm_bits) b->ptr[len] = '\0'; /*(sock_addr_inet_pton() w/o CIDR mask)*/ 2433 rc = sock_addr_inet_pton(addr, b->ptr, family, 0); 2434 if (nm_bits) b->ptr[len] = '/'; 2435 return (1 == rc); 2436 } 2437 2438 2439 static void context_init(server *srv, config_t *context) { 2440 context->srv = srv; 2441 context->ok = 1; 2442 context->configs_stack.data = NULL; 2443 context->configs_stack.used = 0; 2444 context->configs_stack.size = 0; 2445 context->basedir = buffer_init(); 2446 } 2447 2448 static void context_free(config_t *context) { 2449 free(context->configs_stack.data); 2450 buffer_free(context->basedir); 2451 } 2452 2453 __attribute_noinline__ 2454 static void config_vars_init (array *a) { 2455 char dcwd[PATH_MAX]; 2456 if (NULL != getcwd(dcwd, sizeof(dcwd))) 2457 array_set_key_value(a, CONST_STR_LEN("var.CWD"), dcwd, strlen(dcwd)); 2458 2459 *array_get_int_ptr(a, CONST_STR_LEN("var.PID")) = getpid(); 2460 } 2461 2462 int config_read(server *srv, const char *fn) { 2463 config_t context; 2464 data_config *dc; 2465 int ret; 2466 char *pos; 2467 2468 context_init(srv, &context); 2469 context.all_configs = srv->config_context; 2470 2471 #ifdef __WIN32 2472 pos = strrchr(fn, '\\'); 2473 #else 2474 pos = strrchr(fn, '/'); 2475 #endif 2476 if (pos) { 2477 buffer_copy_string_len(context.basedir, fn, pos - fn + 1); 2478 } 2479 2480 dc = data_config_init(); 2481 buffer_copy_string_len(&dc->key, CONST_STR_LEN("global")); 2482 config_vars_init(dc->value); /* default context */ 2483 2484 force_assert(context.all_configs->used == 0); 2485 dc->context_ndx = context.all_configs->used; 2486 array_insert_unique(context.all_configs, (data_unset *)dc); 2487 context.current = dc; 2488 2489 ret = (0 != strcmp(fn, "-")) /*(incompatible with one-shot mode)*/ 2490 ? config_parse_file_stream(srv, &context, fn) 2491 : config_parse_stdin(srv, &context); 2492 2493 /* remains nothing if parser is ok */ 2494 force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used)); 2495 context_free(&context); 2496 2497 if (0 != ret) { 2498 return ret; 2499 } 2500 2501 /* reorder dc->context_ndx to match srv->config_context->data[] index. 2502 * srv->config_context->data[] may have been re-ordered in configparser.y. 2503 * Since the dc->context_ndx (id) is reused by config_insert*() and by 2504 * plugins to index into srv->config_context->data[], reorder into the 2505 * order encountered during config file parsing for least surprise to 2506 * end-users writing config files. Note: this manipulation *breaks* the 2507 * srv->config_context->sorted[] structure, so searching the array by key 2508 * is no longer valid. */ 2509 for (uint32_t i = 0; i < srv->config_context->used; ++i) { 2510 dc = (data_config *)srv->config_context->data[i]; 2511 if (dc->context_ndx == (int)i) continue; 2512 for (uint32_t j = i; j < srv->config_context->used; ++j) { 2513 dc = (data_config *)srv->config_context->data[j]; 2514 if (dc->context_ndx == (int)i) { 2515 srv->config_context->data[j] = srv->config_context->data[i]; 2516 srv->config_context->data[i] = (data_unset *)dc; 2517 break; 2518 } 2519 } 2520 } 2521 2522 if (0 != config_insert_srvconf(srv)) { 2523 return -1; 2524 } 2525 2526 if (0 != config_insert(srv)) { 2527 return -1; 2528 } 2529 2530 return 0; 2531 } 2532 2533 int config_set_defaults(server *srv) { 2534 size_t i; 2535 request_config *s = &((config_data_base *)srv->config_data_base)->defaults; 2536 struct stat st1, st2; 2537 2538 if (fdevent_config(&srv->srvconf.event_handler, srv->errh) <= 0) 2539 return -1; 2540 2541 if (srv->srvconf.changeroot) { 2542 if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) { 2543 log_error(srv->errh, __FILE__, __LINE__, 2544 "server.chroot doesn't exist: %s", 2545 srv->srvconf.changeroot->ptr); 2546 return -1; 2547 } 2548 if (!S_ISDIR(st1.st_mode)) { 2549 log_error(srv->errh, __FILE__, __LINE__, 2550 "server.chroot isn't a directory: %s", 2551 srv->srvconf.changeroot->ptr); 2552 return -1; 2553 } 2554 } 2555 2556 if (!srv->srvconf.upload_tempdirs->used) { 2557 const char *tmpdir = getenv("TMPDIR"); 2558 if (NULL == tmpdir) tmpdir = "/var/tmp"; 2559 array_insert_value(srv->srvconf.upload_tempdirs, tmpdir, strlen(tmpdir)); 2560 } 2561 2562 if (srv->srvconf.upload_tempdirs->used) { 2563 buffer * const tb = srv->tmp_buf; 2564 buffer_clear(tb); 2565 if (srv->srvconf.changeroot) { 2566 buffer_copy_buffer(tb, srv->srvconf.changeroot); 2567 } 2568 const size_t len = buffer_clen(tb); 2569 2570 for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) { 2571 const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i]; 2572 if (len) { 2573 buffer_truncate(tb, len); 2574 buffer_append_path_len(tb, BUF_PTR_LEN(&ds->value)); 2575 } else { 2576 buffer_copy_buffer(tb, &ds->value); 2577 } 2578 if (-1 == stat(tb->ptr, &st1)) { 2579 log_error(srv->errh, __FILE__, __LINE__, 2580 "server.upload-dirs doesn't exist: %s", tb->ptr); 2581 } else if (!S_ISDIR(st1.st_mode)) { 2582 log_error(srv->errh, __FILE__, __LINE__, 2583 "server.upload-dirs isn't a directory: %s", tb->ptr); 2584 } 2585 } 2586 } 2587 2588 chunkqueue_set_tempdirs_default( 2589 srv->srvconf.upload_tempdirs, 2590 srv->srvconf.upload_temp_file_size); 2591 2592 if (!s->document_root || buffer_is_blank(s->document_root)) { 2593 log_error(srv->errh, __FILE__, __LINE__, "server.document-root is not set"); 2594 return -1; 2595 } 2596 2597 if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */ 2598 s->force_lowercase_filenames = 0; /* default to 0 */ 2599 2600 buffer * const tb = srv->tmp_buf; 2601 buffer_copy_string_len_lc(tb, BUF_PTR_LEN(s->document_root)); 2602 2603 if (0 == stat(tb->ptr, &st1)) { 2604 int is_lower = 0; 2605 2606 is_lower = buffer_is_equal(tb, s->document_root); 2607 2608 /* lower-case existed, check upper-case */ 2609 buffer_copy_buffer(tb, s->document_root); 2610 2611 buffer_to_upper(tb); 2612 2613 /* we have to handle the special case that upper and lower-casing results in the same filename 2614 * as in server.document-root = "/" or "/12345/" */ 2615 2616 if (is_lower && buffer_is_equal(tb, s->document_root)) { 2617 /* lower-casing and upper-casing didn't result in 2618 * an other filename, no need to stat(), 2619 * just assume it is case-sensitive. */ 2620 2621 s->force_lowercase_filenames = 0; 2622 } else if (0 == stat(tb->ptr, &st2)) { 2623 2624 /* upper case exists too, doesn't the FS handle this ? */ 2625 2626 /* upper and lower have the same inode -> case-insensitve FS */ 2627 2628 if (st1.st_ino == st2.st_ino) { 2629 /* upper and lower have the same inode -> case-insensitve FS */ 2630 2631 s->force_lowercase_filenames = 1; 2632 } 2633 } 2634 } 2635 } 2636 2637 return 0; 2638 } 2639