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