1 /* 2 * uhttpd - Tiny single-threaded httpd 3 * 4 * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org> 5 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org> 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #ifndef _DEFAULT_SOURCE 21 # define _DEFAULT_SOURCE 22 #endif 23 24 #define _BSD_SOURCE 25 #define _GNU_SOURCE 26 #define _XOPEN_SOURCE 700 27 #include <sys/types.h> 28 #include <sys/socket.h> 29 #include <netinet/in.h> 30 31 #include <getopt.h> 32 #include <errno.h> 33 #include <netdb.h> 34 #include <signal.h> 35 36 #include <libubox/usock.h> 37 #include <libubox/utils.h> 38 39 #include "uhttpd.h" 40 #include "tls.h" 41 42 char uh_buf[4096]; 43 44 static int run_server(void) 45 { 46 uloop_init(); 47 uh_setup_listeners(); 48 uh_plugin_post_init(); 49 uloop_run(); 50 51 return 0; 52 } 53 54 static void uh_config_parse(void) 55 { 56 const char *path = conf.file; 57 FILE *c; 58 char line[512]; 59 char *col1; 60 char *col2; 61 char *eol; 62 63 if (!path) 64 path = "/etc/httpd.conf"; 65 66 c = fopen(path, "r"); 67 if (!c) 68 return; 69 70 memset(line, 0, sizeof(line)); 71 72 while (fgets(line, sizeof(line) - 1, c)) { 73 if ((line[0] == '/') && (strchr(line, ':') != NULL)) { 74 if (!(col1 = strchr(line, ':')) || (*col1++ = 0) || 75 !(col2 = strchr(col1, ':')) || (*col2++ = 0) || 76 !(eol = strchr(col2, '\n')) || (*eol++ = 0)) 77 continue; 78 79 uh_auth_add(line, col1, col2); 80 } else if (!strncmp(line, "I:", 2)) { 81 if (!(col1 = strchr(line, ':')) || (*col1++ = 0) || 82 !(eol = strchr(col1, '\n')) || (*eol++ = 0)) 83 continue; 84 85 uh_index_add(strdup(col1)); 86 } else if (!strncmp(line, "E404:", 5)) { 87 if (!(col1 = strchr(line, ':')) || (*col1++ = 0) || 88 !(eol = strchr(col1, '\n')) || (*eol++ = 0)) 89 continue; 90 91 conf.error_handler = strdup(col1); 92 } 93 else if ((line[0] == '*') && (strchr(line, ':') != NULL)) { 94 if (!(col1 = strchr(line, '*')) || (*col1++ = 0) || 95 !(col2 = strchr(col1, ':')) || (*col2++ = 0) || 96 !(eol = strchr(col2, '\n')) || (*eol++ = 0)) 97 continue; 98 99 uh_interpreter_add(col1, col2); 100 } 101 } 102 103 fclose(c); 104 } 105 106 static int add_listener_arg(char *arg, bool tls) 107 { 108 char *host = NULL; 109 char *port = arg; 110 char *s; 111 int l; 112 113 s = strrchr(arg, ':'); 114 if (s) { 115 host = arg; 116 port = s + 1; 117 *s = 0; 118 } 119 120 if (host && *host == '[') { 121 l = strlen(host); 122 if (l >= 2) { 123 host[l-1] = 0; 124 host++; 125 } 126 } 127 128 return uh_socket_bind(host, port, tls); 129 } 130 131 static int usage(const char *name) 132 { 133 fprintf(stderr, 134 "Usage: %s -p [addr:]port -h docroot\n" 135 " -f Do not fork to background\n" 136 " -c file Configuration file, default is '/etc/httpd.conf'\n" 137 " -p [addr:]port Bind to specified address and port, multiple allowed\n" 138 #ifdef HAVE_TLS 139 " -s [addr:]port Like -p but provide HTTPS on this port\n" 140 " -C file ASN.1 server certificate file\n" 141 " -K file ASN.1 server private key file\n" 142 " -P ciphers Colon separated list of allowed TLS ciphers\n" 143 " -q Redirect all HTTP requests to HTTPS\n" 144 #endif 145 " -h directory Specify the document root, default is '.'\n" 146 " -E string Use given virtual URL as 404 error handler\n" 147 " -b string Use given charset for directory listings, default to UTF-8\n" 148 " -I string Use given filename as index for directories, multiple allowed\n" 149 " -S Do not follow symbolic links outside of the docroot\n" 150 " -D Do not allow directory listings, send 403 instead\n" 151 " -R Enable RFC1918 filter\n" 152 " -n count Maximum allowed number of concurrent script requests\n" 153 " -N count Maximum allowed number of concurrent connections\n" 154 #ifdef HAVE_LUA 155 " -l string URL prefix for Lua handler\n" 156 " -L file Path to Lua handler script, -l and -L may be repeated in pairs\n" 157 #endif 158 #ifdef HAVE_UCODE 159 " -o string URL prefix for ucode handler\n" 160 " -O file Path to ucode handler script, -o and -O may be repeated in pairs\n" 161 #endif 162 #ifdef HAVE_UBUS 163 " -u string URL prefix for UBUS via JSON-RPC handler\n" 164 " -U file Override ubus socket path\n" 165 " -a Do not authenticate JSON-RPC requests against UBUS session api\n" 166 " -X Enable CORS HTTP headers on JSON-RPC api\n" 167 " -e Events subscription reconnection time (retry value)\n" 168 #endif 169 " -x string URL prefix for CGI handler, default is '/cgi-bin'\n" 170 " -y alias[=path] URL alias handle\n" 171 " -i .ext=path Use interpreter at path for files with the given extension\n" 172 " -t seconds CGI, Lua and UBUS script timeout in seconds, default is 60\n" 173 " -T seconds Network timeout in seconds, default is 30\n" 174 " -k seconds HTTP keepalive timeout\n" 175 " -A seconds TCP keepalive timeout, default is unset\n" 176 " -d string URL decode given string\n" 177 " -r string Specify basic auth realm\n" 178 " -m string MD5 crypt given string\n" 179 "\n", name 180 ); 181 return 1; 182 } 183 184 static void init_defaults_pre(void) 185 { 186 conf.script_timeout = 60; 187 conf.network_timeout = 30; 188 conf.http_keepalive = 20; 189 conf.max_script_requests = 3; 190 conf.max_connections = 100; 191 conf.realm = "Protected Area"; 192 conf.cgi_prefix = "/cgi-bin"; 193 conf.cgi_path = "/sbin:/usr/sbin:/bin:/usr/bin"; 194 INIT_LIST_HEAD(&conf.cgi_alias); 195 INIT_LIST_HEAD(&conf.lua_prefix); 196 #if HAVE_UCODE 197 INIT_LIST_HEAD(&conf.ucode_prefix); 198 #endif 199 } 200 201 static void init_defaults_post(void) 202 { 203 uh_index_add("index.html"); 204 uh_index_add("index.htm"); 205 uh_index_add("default.html"); 206 uh_index_add("default.htm"); 207 208 if (conf.cgi_prefix) { 209 char *str = malloc(strlen(conf.docroot) + strlen(conf.cgi_prefix) + 1); 210 211 strcpy(str, conf.docroot); 212 strcat(str, conf.cgi_prefix); 213 conf.cgi_docroot_path = str; 214 conf.cgi_prefix_len = strlen(conf.cgi_prefix); 215 }; 216 } 217 218 static void fixup_prefix(char *str) 219 { 220 int len; 221 222 if (!str || !str[0]) 223 return; 224 225 len = strlen(str) - 1; 226 227 while (len >= 0 && str[len] == '/') 228 len--; 229 230 str[len + 1] = 0; 231 } 232 233 #ifdef HAVE_LUA 234 static void add_lua_prefix(const char *prefix, const char *handler) { 235 struct lua_prefix *p; 236 char *pprefix, *phandler; 237 238 p = calloc_a(sizeof(*p), 239 &pprefix, strlen(prefix) + 1, 240 &phandler, strlen(handler) + 1); 241 242 if (!p) 243 return; 244 245 p->prefix = strcpy(pprefix, prefix); 246 p->handler = strcpy(phandler, handler); 247 248 list_add_tail(&p->list, &conf.lua_prefix); 249 } 250 #endif 251 252 #ifdef HAVE_UCODE 253 static void add_ucode_prefix(const char *prefix, const char *handler) { 254 struct ucode_prefix *p; 255 char *pprefix, *phandler; 256 257 p = calloc_a(sizeof(*p), 258 &pprefix, strlen(prefix) + 1, 259 &phandler, strlen(handler) + 1); 260 261 if (!p) 262 return; 263 264 p->prefix = strcpy(pprefix, prefix); 265 p->handler = strcpy(phandler, handler); 266 267 list_add_tail(&p->list, &conf.ucode_prefix); 268 } 269 #endif 270 271 int main(int argc, char **argv) 272 { 273 struct alias *alias; 274 bool nofork = false; 275 char *port; 276 int opt, ch; 277 int cur_fd; 278 int bound = 0; 279 #ifdef HAVE_TLS 280 int n_tls = 0; 281 const char *tls_key = NULL, *tls_crt = NULL, *tls_ciphers = NULL; 282 #endif 283 #ifdef HAVE_LUA 284 const char *lua_prefix = NULL, *lua_handler = NULL; 285 #endif 286 #ifdef HAVE_UCODE 287 const char *ucode_prefix = NULL, *ucode_handler = NULL; 288 #endif 289 290 BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX); 291 292 uh_dispatch_add(&cgi_dispatch); 293 init_defaults_pre(); 294 signal(SIGPIPE, SIG_IGN); 295 296 while ((ch = getopt(argc, argv, "A:ab:C:c:Dd:E:e:fh:H:I:i:K:k:L:l:m:N:n:O:o:P:p:qRr:Ss:T:t:U:u:Xx:y:")) != -1) { 297 switch(ch) { 298 #ifdef HAVE_TLS 299 case 'C': 300 tls_crt = optarg; 301 break; 302 303 case 'K': 304 tls_key = optarg; 305 break; 306 307 case 'P': 308 tls_ciphers = optarg; 309 break; 310 311 case 'q': 312 conf.tls_redirect = 1; 313 break; 314 315 case 's': 316 n_tls++; 317 /* fall through */ 318 #else 319 case 'C': 320 case 'K': 321 case 'P': 322 case 'q': 323 case 's': 324 fprintf(stderr, "uhttpd: TLS support not compiled, " 325 "ignoring -%c\n", ch); 326 break; 327 #endif 328 case 'p': 329 optarg = strdup(optarg); 330 bound += add_listener_arg(optarg, (ch == 's')); 331 free(optarg); 332 break; 333 334 case 'h': 335 if (!realpath(optarg, uh_buf)) { 336 fprintf(stderr, "Error: Invalid directory %s: %s\n", 337 optarg, strerror(errno)); 338 exit(1); 339 } 340 conf.docroot = strdup(uh_buf); 341 break; 342 343 case 'H': 344 if (uh_handler_add(optarg)) { 345 fprintf(stderr, "Error: Failed to load handler script %s\n", 346 optarg); 347 exit(1); 348 } 349 break; 350 351 case 'E': 352 if (optarg[0] != '/') { 353 fprintf(stderr, "Error: Invalid error handler: %s\n", 354 optarg); 355 exit(1); 356 } 357 conf.error_handler = optarg; 358 break; 359 360 case 'I': 361 if (optarg[0] == '/') { 362 fprintf(stderr, "Error: Invalid index page: %s\n", 363 optarg); 364 exit(1); 365 } 366 uh_index_add(optarg); 367 break; 368 369 case 'b': 370 conf.dirlist_charset = optarg; 371 break; 372 373 case 'S': 374 conf.no_symlinks = 1; 375 break; 376 377 case 'D': 378 conf.no_dirlists = 1; 379 break; 380 381 case 'R': 382 conf.rfc1918_filter = 1; 383 break; 384 385 case 'n': 386 conf.max_script_requests = atoi(optarg); 387 break; 388 389 case 'N': 390 conf.max_connections = atoi(optarg); 391 break; 392 393 case 'x': 394 fixup_prefix(optarg); 395 conf.cgi_prefix = optarg; 396 break; 397 398 case 'y': 399 alias = calloc(1, sizeof(*alias)); 400 if (!alias) { 401 fprintf(stderr, "Error: failed to allocate alias\n"); 402 exit(1); 403 } 404 alias->alias = strdup(optarg); 405 alias->path = strchr(alias->alias, '='); 406 if (alias->path) 407 *alias->path++ = 0; 408 list_add(&alias->list, &conf.cgi_alias); 409 break; 410 411 case 'i': 412 optarg = strdup(optarg); 413 port = strchr(optarg, '='); 414 if (optarg[0] != '.' || !port) { 415 fprintf(stderr, "Error: Invalid interpreter: %s\n", 416 optarg); 417 exit(1); 418 } 419 420 *port++ = 0; 421 uh_interpreter_add(optarg, port); 422 break; 423 424 case 't': 425 conf.script_timeout = atoi(optarg); 426 break; 427 428 case 'T': 429 conf.network_timeout = atoi(optarg); 430 break; 431 432 case 'k': 433 conf.http_keepalive = atoi(optarg); 434 break; 435 436 case 'A': 437 conf.tcp_keepalive = atoi(optarg); 438 break; 439 440 case 'f': 441 nofork = 1; 442 break; 443 444 case 'd': 445 optarg = strdup(optarg); 446 port = alloca(strlen(optarg) + 1); 447 if (!port) 448 return -1; 449 450 /* "decode" plus to space to retain compat */ 451 for (opt = 0; optarg[opt]; opt++) 452 if (optarg[opt] == '+') 453 optarg[opt] = ' '; 454 455 /* opt now contains strlen(optarg) -- no need to re-scan */ 456 if (uh_urldecode(port, opt, optarg, opt) < 0) { 457 fprintf(stderr, "uhttpd: invalid encoding\n"); 458 return -1; 459 } 460 461 printf("%s", port); 462 return 0; 463 break; 464 465 /* basic auth realm */ 466 case 'r': 467 conf.realm = optarg; 468 break; 469 470 /* md5 crypt */ 471 case 'm': 472 printf("%s\n", crypt(optarg, "$1$")); 473 return 0; 474 break; 475 476 /* config file */ 477 case 'c': 478 conf.file = optarg; 479 break; 480 481 #ifdef HAVE_LUA 482 case 'l': 483 case 'L': 484 if (ch == 'l') { 485 if (lua_prefix) 486 fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n", 487 ch, lua_prefix); 488 489 lua_prefix = optarg; 490 } 491 else { 492 if (lua_handler) 493 fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n", 494 ch, lua_handler); 495 496 lua_handler = optarg; 497 } 498 499 if (lua_prefix && lua_handler) { 500 add_lua_prefix(lua_prefix, lua_handler); 501 lua_prefix = NULL; 502 lua_handler = NULL; 503 } 504 505 break; 506 #else 507 case 'l': 508 case 'L': 509 fprintf(stderr, "uhttpd: Lua support not compiled, " 510 "ignoring -%c\n", ch); 511 break; 512 #endif 513 #ifdef HAVE_UCODE 514 case 'o': 515 case 'O': 516 if (ch == 'o') { 517 if (ucode_prefix) 518 fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n", 519 ch, ucode_prefix); 520 521 ucode_prefix = optarg; 522 } 523 else { 524 if (ucode_handler) 525 fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n", 526 ch, ucode_handler); 527 528 ucode_handler = optarg; 529 } 530 531 if (ucode_prefix && ucode_handler) { 532 add_ucode_prefix(ucode_prefix, ucode_handler); 533 ucode_prefix = NULL; 534 ucode_handler = NULL; 535 } 536 537 break; 538 #else 539 case 'o': 540 case 'O': 541 fprintf(stderr, "uhttpd: ucode support not compiled, " 542 "ignoring -%c\n", ch); 543 break; 544 #endif 545 #ifdef HAVE_UBUS 546 case 'a': 547 conf.ubus_noauth = 1; 548 break; 549 550 case 'u': 551 conf.ubus_prefix = optarg; 552 break; 553 554 case 'U': 555 conf.ubus_socket = optarg; 556 break; 557 558 case 'X': 559 conf.ubus_cors = 1; 560 break; 561 562 case 'e': 563 conf.events_retry = atoi(optarg); 564 break; 565 #else 566 case 'a': 567 case 'u': 568 case 'U': 569 case 'X': 570 case 'e': 571 fprintf(stderr, "uhttpd: UBUS support not compiled, " 572 "ignoring -%c\n", ch); 573 break; 574 #endif 575 default: 576 return usage(argv[0]); 577 } 578 } 579 580 uh_config_parse(); 581 582 if (!conf.docroot) { 583 if (!realpath(".", uh_buf)) { 584 fprintf(stderr, "Error: Unable to determine work dir\n"); 585 return 1; 586 } 587 conf.docroot = strdup(uh_buf); 588 } 589 590 init_defaults_post(); 591 592 if (!bound) { 593 fprintf(stderr, "Error: No sockets bound, unable to continue\n"); 594 return 1; 595 } 596 597 #ifdef HAVE_TLS 598 if (n_tls) { 599 if (!tls_crt || !tls_key) { 600 fprintf(stderr, "Please specify a certificate and " 601 "a key file to enable SSL support\n"); 602 return 1; 603 } 604 605 if (uh_tls_init(tls_key, tls_crt, tls_ciphers)) 606 return 1; 607 } 608 #endif 609 610 #ifdef HAVE_LUA 611 if (lua_handler || lua_prefix) { 612 fprintf(stderr, "Need handler and prefix to enable Lua support\n"); 613 return 1; 614 } 615 616 if (!list_empty(&conf.lua_prefix) && uh_plugin_init("uhttpd_lua.so")) 617 return 1; 618 #endif 619 #ifdef HAVE_UCODE 620 if (ucode_handler || ucode_prefix) { 621 fprintf(stderr, "Need handler and prefix to enable ucode support\n"); 622 return 1; 623 } 624 625 if (!list_empty(&conf.ucode_prefix) && uh_plugin_init("uhttpd_ucode.so")) 626 return 1; 627 #endif 628 #ifdef HAVE_UBUS 629 if (conf.ubus_prefix && uh_plugin_init("uhttpd_ubus.so")) 630 return 1; 631 #endif 632 633 /* fork (if not disabled) */ 634 if (!nofork) { 635 switch (fork()) { 636 case -1: 637 perror("fork()"); 638 exit(1); 639 640 case 0: 641 /* daemon setup */ 642 if (chdir("/")) 643 perror("chdir()"); 644 645 cur_fd = open("/dev/null", O_WRONLY); 646 if (cur_fd > 0) { 647 dup2(cur_fd, 0); 648 dup2(cur_fd, 1); 649 dup2(cur_fd, 2); 650 } 651 652 break; 653 654 default: 655 exit(0); 656 } 657 } 658 659 return run_server(); 660 } 661
This page was automatically generated by LXR 0.3.1. • OpenWrt