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