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