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