/** * Given a url this functions tries to find the physical path on the server. * @cl the client that made the request * @url the requested URL * @return NULL on error */ static struct path_info *path_lookup(struct client *cl, const char *url) { static char path_phys[PATH_MAX]; static char path_info[PATH_MAX]; static struct path_info p; int docroot_len = strlen(DOCUMENT_ROOT); char *pathptr = NULL; bool slash; int i = 0; int len; struct stat s; /* Return NULL when the URL is undefined */ if (url == NULL) return NULL; memset(&p, 0, sizeof(p)); path_phys[0] = 0; path_info[0] = 0; /* Start the canonical path with the document root */ strcpy(uh_buf, DOCUMENT_ROOT); /* Separate query string from url */ if ((pathptr = strchr(url, '?')) != NULL) { p.query = pathptr[1] ? pathptr + 1 : NULL; /* URL decode component without query */ if (pathptr > url) { if (uh_urldecode(&uh_buf[docroot_len], sizeof(uh_buf) - docroot_len - 1, url, pathptr - url ) < 0) return NULL; } } /* Decode the full url when there is no querystring */ else if (uh_urldecode(&uh_buf[docroot_len], sizeof(uh_buf) - docroot_len - 1, url, strlen(url) ) < 0) return NULL; /* Create canonical path */ len = strlen(uh_buf); slash = len && uh_buf[len - 1] == '/'; len = min(len, sizeof(path_phys) - 1); for (i = len; i >= 0; i--) { char ch = uh_buf[i]; bool exists; if (ch != 0 && ch != '/') continue; uh_buf[i] = 0; exists = !!canonpath(uh_buf, path_phys); uh_buf[i] = ch; if (!exists) continue; /* Test the current path */ if (stat(path_phys, &p.stat)) continue; snprintf(path_info, sizeof(path_info), "%s", uh_buf + i); break; } /* Check whether found path is within docroot */ if (strncmp(path_phys, DOCUMENT_ROOT, docroot_len) != 0 || (path_phys[docroot_len] != 0 && path_phys[docroot_len] != '/')){ return NULL; } /* Check if the found file is a regular file */ if (p.stat.st_mode & S_IFREG) { p.root = DOCUMENT_ROOT; p.phys = path_phys; p.name = &path_phys[docroot_len]; p.info = path_info[0] ? path_info : NULL; return &p; } /* Make sure it is not a directory */ if (!(p.stat.st_mode & S_IFDIR)){ return NULL; } if (path_info[0]){ return NULL; } pathptr = path_phys + strlen(path_phys); /* ensure trailing slash */ if (pathptr[-1] != '/') { pathptr[0] = '/'; pathptr[1] = 0; pathptr++; } /* if requested url resolves to a directory and a trailing slash is missing in the request url, redirect the client to the same url with trailing slash appended */ if (!slash) { write_http_header(cl, 302, "Found"); ustream_printf(cl->us, "Content-Length: 0\r\n"); ustream_printf(cl->us, "Location: %s%s%s\r\n\r\n", &path_phys[docroot_len], p.query ? "?" : "", p.query ? p.query : ""); request_done(cl); p.redirected = 1; return &p; } /* Check if the folder contains an index file */ len = path_phys + sizeof(path_phys) - pathptr - 1; if(strlen(INDEX_FILE) <= len){ strcpy(pathptr, INDEX_FILE); if (!stat(path_phys, &s) && (s.st_mode & S_IFREG)) { memcpy(&p.stat, &s, sizeof(p.stat)); } else { /* Stop when strcpy is not needed */ *pathptr = 0; } } p.root = DOCUMENT_ROOT; p.phys = path_phys; p.name = &path_phys[docroot_len]; return p.phys ? &p : NULL; }
int main (int argc, char **argv) { /* master file descriptor list */ fd_set serv_fds; /* working structs */ struct addrinfo hints; struct sigaction sa; struct config conf; /* signal mask */ sigset_t ss; /* maximum file descriptor number */ int cur_fd, max_fd = 0; #ifdef HAVE_TLS int tls = 0; int keys = 0; #endif int bound = 0; int nofork = 0; /* args */ int opt; char bind[128]; char *port = NULL; #ifdef HAVE_LUA /* library handle */ void *lib; #endif FD_ZERO(&serv_fds); /* handle SIGPIPE, SIGINT, SIGTERM, SIGCHLD */ sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); sa.sa_handler = uh_sigchld; sigaction(SIGCHLD, &sa, NULL); sa.sa_handler = uh_sigterm; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); /* defer SIGCHLD */ sigemptyset(&ss); sigaddset(&ss, SIGCHLD); sigprocmask(SIG_BLOCK, &ss, NULL); /* prepare addrinfo hints */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; /* parse args */ memset(&conf, 0, sizeof(conf)); memset(bind, 0, sizeof(bind)); while( (opt = getopt(argc, argv, "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:A:")) > 0 ) { switch(opt) { /* [addr:]port */ case 'p': case 's': if( (port = strrchr(optarg, ':')) != NULL ) { if( (optarg[0] == '[') && (port > optarg) && (port[-1] == ']') ) memcpy(bind, optarg + 1, min(sizeof(bind), (int)(port - optarg) - 2)); else memcpy(bind, optarg, min(sizeof(bind), (int)(port - optarg))); port++; } else { port = optarg; } #ifdef HAVE_TLS if( opt == 's' ) { if( uh_inittls(&conf) ) { fprintf(stderr, "Notice: TLS support is disabled, " "ignoring '-s %s'\n", optarg ); continue; } tls = 1; } #endif /* bind sockets */ bound += uh_socket_bind( &serv_fds, &max_fd, bind[0] ? bind : NULL, port, &hints, (opt == 's'), &conf ); memset(bind, 0, sizeof(bind)); break; #ifdef HAVE_TLS /* certificate */ case 'C': if( !uh_inittls(&conf) ) { if( conf.tls_cert(conf.tls, optarg) < 1 ) { fprintf(stderr, "Error: Invalid certificate file given\n"); exit(1); } keys++; } break; /* key */ case 'K': if( !uh_inittls(&conf) ) { if( conf.tls_key(conf.tls, optarg) < 1 ) { fprintf(stderr, "Error: Invalid private key file given\n"); exit(1); } keys++; } break; #endif /* docroot */ case 'h': if( ! realpath(optarg, conf.docroot) ) { fprintf(stderr, "Error: Invalid directory %s: %s\n", optarg, strerror(errno)); exit(1); } break; /* error handler */ case 'E': if( (strlen(optarg) == 0) || (optarg[0] != '/') ) { fprintf(stderr, "Error: Invalid error handler: %s\n", optarg); exit(1); } conf.error_handler = optarg; break; /* index file */ case 'I': if( (strlen(optarg) == 0) || (optarg[0] == '/') ) { fprintf(stderr, "Error: Invalid index page: %s\n", optarg); exit(1); } conf.index_file = optarg; break; /* don't follow symlinks */ case 'S': conf.no_symlinks = 1; break; /* don't list directories */ case 'D': conf.no_dirlists = 1; break; case 'R': conf.rfc1918_filter = 1; break; #ifdef HAVE_CGI /* cgi prefix */ case 'x': conf.cgi_prefix = optarg; break; /* interpreter */ case 'i': if( (optarg[0] == '.') && (port = strchr(optarg, '=')) ) { *port++ = 0; uh_interpreter_add(optarg, port); } else { fprintf(stderr, "Error: Invalid interpreter: %s\n", optarg); exit(1); } break; #endif #ifdef HAVE_LUA /* lua prefix */ case 'l': conf.lua_prefix = optarg; break; /* lua handler */ case 'L': conf.lua_handler = optarg; break; #endif #if defined(HAVE_CGI) || defined(HAVE_LUA) /* script timeout */ case 't': conf.script_timeout = atoi(optarg); break; #endif /* network timeout */ case 'T': conf.network_timeout = atoi(optarg); break; /* tcp keep-alive */ case 'A': conf.tcp_keepalive = atoi(optarg); break; /* no fork */ case 'f': nofork = 1; break; /* urldecode */ case 'd': if( (port = malloc(strlen(optarg)+1)) != NULL ) { /* "decode" plus to space to retain compat */ for (opt = 0; optarg[opt]; opt++) if (optarg[opt] == '+') optarg[opt] = ' '; memset(port, 0, strlen(optarg)+1); uh_urldecode(port, strlen(optarg), optarg, strlen(optarg)); printf("%s", port); free(port); exit(0); } break; /* basic auth realm */ case 'r': conf.realm = optarg; break; /* md5 crypt */ /* case 'm': printf("%s\n", crypt(optarg, "$1$")); exit(0); break;*/ /* config file */ case 'c': conf.file = optarg; break; default: fprintf(stderr, "Usage: %s -p [addr:]port [-h docroot]\n" " -f Do not fork to background\n" " -c file Configuration file, default is '/etc/httpd.conf'\n" " -p [addr:]port Bind to specified address and port, multiple allowed\n" #ifdef HAVE_TLS " -s [addr:]port Like -p but provide HTTPS on this port\n" " -C file ASN.1 server certificate file\n" " -K file ASN.1 server private key file\n" #endif " -h directory Specify the document root, default is '.'\n" " -E string Use given virtual URL as 404 error handler\n" " -I string Use given filename as index page for directories\n" " -S Do not follow symbolic links outside of the docroot\n" " -D Do not allow directory listings, send 403 instead\n" " -R Enable RFC1918 filter\n" #ifdef HAVE_LUA " -l string URL prefix for Lua handler, default is '/lua'\n" " -L file Lua handler script, omit to disable Lua\n" #endif #ifdef HAVE_CGI " -x string URL prefix for CGI handler, default is '/cgi-bin'\n" " -i .ext=path Use interpreter at path for files with the given extension\n" #endif #if defined(HAVE_CGI) || defined(HAVE_LUA) " -t seconds CGI and Lua script timeout in seconds, default is 60\n" #endif " -T seconds Network timeout in seconds, default is 30\n" " -d string URL decode given string\n" " -r string Specify basic auth realm\n" " -m string MD5 crypt given string\n" "\n", argv[0] ); exit(1); } } #ifdef HAVE_TLS if( (tls == 1) && (keys < 2) ) { fprintf(stderr, "Error: Missing private key or certificate file\n"); exit(1); } #endif if( bound < 1 ) { fprintf(stderr, "Error: No sockets bound, unable to continue\n"); exit(1); } /* default docroot */ if( !conf.docroot[0] && !realpath(".", conf.docroot) ) { fprintf(stderr, "Error: Can not determine default document root: %s\n", strerror(errno)); exit(1); } /* default realm */ if( ! conf.realm ) conf.realm = "Protected Area"; /* config file */ uh_config_parse(&conf); /* default network timeout */ if( conf.network_timeout <= 0 ) conf.network_timeout = 30; #if defined(HAVE_CGI) || defined(HAVE_LUA) /* default script timeout */ if( conf.script_timeout <= 0 ) conf.script_timeout = 60; #endif #ifdef HAVE_CGI /* default cgi prefix */ if( ! conf.cgi_prefix ) conf.cgi_prefix = "/cgi-bin"; #endif #ifdef HAVE_LUA /* load Lua plugin */ if( ! (lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) ) { fprintf(stderr, "Notice: Unable to load Lua plugin - disabling Lua support! " "(Reason: %s)\n", dlerror() ); } else { /* resolve functions */ if( !(conf.lua_init = dlsym(lib, "uh_lua_init")) || !(conf.lua_close = dlsym(lib, "uh_lua_close")) || !(conf.lua_request = dlsym(lib, "uh_lua_request")) ) { fprintf(stderr, "Error: Failed to lookup required symbols " "in Lua plugin: %s\n", dlerror() ); exit(1); } /* init Lua runtime if handler is specified */ if( conf.lua_handler ) { /* default lua prefix */ if( ! conf.lua_prefix ) conf.lua_prefix = "/lua"; conf.lua_state = conf.lua_init(conf.lua_handler); } } #endif /* fork (if not disabled) */ if( ! nofork ) { switch( fork() ) { case -1: perror("fork()"); exit(1); case 0: /* daemon setup */ if( chdir("/") ) perror("chdir()"); if( (cur_fd = open("/dev/null", O_WRONLY)) > -1 ) dup2(cur_fd, 0); if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 ) dup2(cur_fd, 1); if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 ) dup2(cur_fd, 2); break; default: exit(0); } } /* server main loop */ uh_mainloop(&conf, serv_fds, max_fd); #ifdef HAVE_LUA /* destroy the Lua state */ if( conf.lua_state != NULL ) conf.lua_close(conf.lua_state); #endif return 0; }
int main(int argc, char **argv) { bool nofork = false; char *port; int opt, ch; int cur_fd; int bound = 0; #ifdef HAVE_TLS int n_tls = 0; const char *tls_key = NULL, *tls_crt = NULL; #endif BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX); uh_dispatch_add(&cgi_dispatch); init_defaults_pre(); signal(SIGPIPE, SIG_IGN); while ((ch = getopt(argc, argv, "afSDRXC:K:E:I:p:s:h:c:l:L:d:r:m:n:N:x:i:t:k:T:A:u:U:")) != -1) { switch(ch) { #ifdef HAVE_TLS case 'C': tls_crt = optarg; break; case 'K': tls_key = optarg; break; case 's': n_tls++; /* fall through */ #else case 'C': case 'K': case 's': fprintf(stderr, "uhttpd: TLS support not compiled, " "ignoring -%c\n", ch); break; #endif case 'p': optarg = strdup(optarg); bound += add_listener_arg(optarg, (ch == 's')); break; case 'h': if (!realpath(optarg, uh_buf)) { fprintf(stderr, "Error: Invalid directory %s: %s\n", optarg, strerror(errno)); exit(1); } conf.docroot = strdup(uh_buf); break; case 'E': if (optarg[0] != '/') { fprintf(stderr, "Error: Invalid error handler: %s\n", optarg); exit(1); } conf.error_handler = optarg; break; case 'I': if (optarg[0] == '/') { fprintf(stderr, "Error: Invalid index page: %s\n", optarg); exit(1); } uh_index_add(optarg); break; case 'S': conf.no_symlinks = 1; break; case 'D': conf.no_dirlists = 1; break; case 'R': conf.rfc1918_filter = 1; break; case 'n': conf.max_script_requests = atoi(optarg); break; case 'N': conf.max_connections = atoi(optarg); break; case 'x': fixup_prefix(optarg); conf.cgi_prefix = optarg; break; case 'i': optarg = strdup(optarg); port = strchr(optarg, '='); if (optarg[0] != '.' || !port) { fprintf(stderr, "Error: Invalid interpreter: %s\n", optarg); exit(1); } *port++ = 0; uh_interpreter_add(optarg, port); break; case 't': conf.script_timeout = atoi(optarg); break; case 'T': conf.network_timeout = atoi(optarg); break; case 'k': conf.http_keepalive = atoi(optarg); break; case 'A': conf.tcp_keepalive = atoi(optarg); break; case 'f': nofork = 1; break; case 'd': optarg = strdup(optarg); port = alloca(strlen(optarg) + 1); if (!port) return -1; /* "decode" plus to space to retain compat */ for (opt = 0; optarg[opt]; opt++) if (optarg[opt] == '+') optarg[opt] = ' '; /* opt now contains strlen(optarg) -- no need to re-scan */ if (uh_urldecode(port, opt, optarg, opt) < 0) { fprintf(stderr, "uhttpd: invalid encoding\n"); return -1; } printf("%s", port); return 0; break; /* basic auth realm */ case 'r': conf.realm = optarg; break; /* md5 crypt */ case 'm': printf("%s\n", crypt(optarg, "$1$")); return 0; break; /* config file */ case 'c': conf.file = optarg; break; #ifdef HAVE_LUA case 'l': conf.lua_prefix = optarg; break; case 'L': conf.lua_handler = optarg; break; #else case 'l': case 'L': fprintf(stderr, "uhttpd: Lua support not compiled, " "ignoring -%c\n", ch); break; #endif #ifdef HAVE_UBUS case 'a': conf.ubus_noauth = 1; break; case 'u': conf.ubus_prefix = optarg; break; case 'U': conf.ubus_socket = optarg; break; case 'X': conf.ubus_cors = 1; break; #else case 'a': case 'u': case 'U': case 'X': fprintf(stderr, "uhttpd: UBUS support not compiled, " "ignoring -%c\n", ch); break; #endif default: return usage(argv[0]); } } uh_config_parse(); if (!conf.docroot) { if (!realpath(".", uh_buf)) { fprintf(stderr, "Error: Unable to determine work dir\n"); return 1; } conf.docroot = strdup(uh_buf); } init_defaults_post(); if (!bound) { fprintf(stderr, "Error: No sockets bound, unable to continue\n"); return 1; } #ifdef HAVE_TLS if (n_tls) { if (!tls_crt || !tls_key) { fprintf(stderr, "Please specify a certificate and " "a key file to enable SSL support\n"); return 1; } if (uh_tls_init(tls_key, tls_crt)) return 1; } #endif #ifdef HAVE_LUA if (conf.lua_handler || conf.lua_prefix) { if (!conf.lua_handler || !conf.lua_prefix) { fprintf(stderr, "Need handler and prefix to enable Lua support\n"); return 1; } if (uh_plugin_init("uhttpd_lua.so")) return 1; } #endif #ifdef HAVE_UBUS if (conf.ubus_prefix && uh_plugin_init("uhttpd_ubus.so")) return 1; #endif /* fork (if not disabled) */ if (!nofork) { switch (fork()) { case -1: perror("fork()"); exit(1); case 0: /* daemon setup */ if (chdir("/")) perror("chdir()"); cur_fd = open("/dev/null", O_WRONLY); if (cur_fd > 0) { dup2(cur_fd, 0); dup2(cur_fd, 1); dup2(cur_fd, 2); } break; default: exit(0); } } return run_server(); }
int main (int argc, char **argv) { #ifdef HAVE_LUA /* Lua runtime */ lua_State *L = NULL; #endif /* master file descriptor list */ fd_set used_fds, serv_fds, read_fds; /* working structs */ struct addrinfo hints; struct http_request *req; struct path_info *pin; struct client *cl; struct sigaction sa; struct config conf; /* signal mask */ sigset_t ss; /* maximum file descriptor number */ int new_fd, cur_fd, max_fd = 0; int tls = 0; int keys = 0; int bound = 0; int nofork = 0; /* args */ int opt; char bind[128]; char *port = NULL; /* library handles */ void *tls_lib; void *lua_lib; /* clear the master and temp sets */ FD_ZERO(&used_fds); FD_ZERO(&serv_fds); FD_ZERO(&read_fds); /* handle SIGPIPE, SIGINT, SIGTERM, SIGCHLD */ sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); sa.sa_handler = uh_sigchld; sigaction(SIGCHLD, &sa, NULL); sa.sa_handler = uh_sigterm; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); /* defer SIGCHLD */ sigemptyset(&ss); sigaddset(&ss, SIGCHLD); sigprocmask(SIG_BLOCK, &ss, NULL); /* prepare addrinfo hints */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; /* parse args */ memset(&conf, 0, sizeof(conf)); memset(bind, 0, sizeof(bind)); #ifdef HAVE_TLS /* load TLS plugin */ if( ! (tls_lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) ) { fprintf(stderr, "Notice: Unable to load TLS plugin - disabling SSL support! " "(Reason: %s)\n", dlerror() ); } else { /* resolve functions */ if( !(conf.tls_init = dlsym(tls_lib, "uh_tls_ctx_init")) || !(conf.tls_cert = dlsym(tls_lib, "uh_tls_ctx_cert")) || !(conf.tls_key = dlsym(tls_lib, "uh_tls_ctx_key")) || !(conf.tls_free = dlsym(tls_lib, "uh_tls_ctx_free")) || !(conf.tls_accept = dlsym(tls_lib, "uh_tls_client_accept")) || !(conf.tls_close = dlsym(tls_lib, "uh_tls_client_close")) || !(conf.tls_recv = dlsym(tls_lib, "uh_tls_client_recv")) || !(conf.tls_send = dlsym(tls_lib, "uh_tls_client_send")) ) { fprintf(stderr, "Error: Failed to lookup required symbols " "in TLS plugin: %s\n", dlerror() ); exit(1); } /* init SSL context */ if( ! (conf.tls = conf.tls_init()) ) { fprintf(stderr, "Error: Failed to initalize SSL context\n"); exit(1); } } #endif while( (opt = getopt(argc, argv, "fC:K:p:s:h:c:l:L:d:r:m:x:t:")) > 0 ) { switch(opt) { /* [addr:]port */ case 'p': case 's': if( (port = strrchr(optarg, ':')) != NULL ) { if( (optarg[0] == '[') && (port > optarg) && (port[-1] == ']') ) memcpy(bind, optarg + 1, min(sizeof(bind), (int)(port - optarg) - 2)); else memcpy(bind, optarg, min(sizeof(bind), (int)(port - optarg))); port++; } else { port = optarg; } #ifdef HAVE_TLS if( opt == 's' ) { if( !conf.tls ) { fprintf(stderr, "Notice: TLS support is disabled, " "ignoring '-s %s'\n", optarg ); continue; } tls = 1; } #endif /* bind sockets */ bound += uh_socket_bind( &serv_fds, &max_fd, bind[0] ? bind : NULL, port, &hints, (opt == 's'), &conf ); break; #ifdef HAVE_TLS /* certificate */ case 'C': if( conf.tls ) { if( conf.tls_cert(conf.tls, optarg) < 1 ) { fprintf(stderr, "Error: Invalid certificate file given\n"); exit(1); } keys++; } break; /* key */ case 'K': if( conf.tls ) { if( conf.tls_key(conf.tls, optarg) < 1 ) { fprintf(stderr, "Error: Invalid private key file given\n"); exit(1); } keys++; } break; #endif /* docroot */ case 'h': if( ! realpath(optarg, conf.docroot) ) { fprintf(stderr, "Error: Invalid directory %s: %s\n", optarg, strerror(errno)); exit(1); } break; #ifdef HAVE_CGI /* cgi prefix */ case 'x': conf.cgi_prefix = optarg; break; #endif #ifdef HAVE_LUA /* lua prefix */ case 'l': conf.lua_prefix = optarg; break; /* lua handler */ case 'L': conf.lua_handler = optarg; break; #endif #if defined(HAVE_CGI) || defined(HAVE_LUA) /* script timeout */ case 't': conf.script_timeout = atoi(optarg); break; #endif /* no fork */ case 'f': nofork = 1; break; /* urldecode */ case 'd': if( (port = malloc(strlen(optarg)+1)) != NULL ) { memset(port, 0, strlen(optarg)+1); uh_urldecode(port, strlen(optarg), optarg, strlen(optarg)); printf("%s", port); free(port); exit(0); } break; /* basic auth realm */ case 'r': conf.realm = optarg; break; /* md5 crypt */ case 'm': printf("%s\n", crypt(optarg, "$1$")); exit(0); break; /* config file */ case 'c': conf.file = optarg; break; default: fprintf(stderr, "Usage: %s -p [addr:]port [-h docroot]\n" " -f Do not fork to background\n" " -c file Configuration file, default is '/etc/httpd.conf'\n" " -p [addr:]port Bind to specified address and port, multiple allowed\n" #ifdef HAVE_TLS " -s [addr:]port Like -p but provide HTTPS on this port\n" " -C file ASN.1 server certificate file\n" " -K file ASN.1 server private key file\n" #endif " -h directory Specify the document root, default is '.'\n" #ifdef HAVE_LUA " -l string URL prefix for Lua handler, default is '/lua'\n" " -L file Lua handler script, omit to disable Lua\n" #endif #ifdef HAVE_CGI " -x string URL prefix for CGI handler, default is '/cgi-bin'\n" #endif #if defined(HAVE_CGI) || defined(HAVE_LUA) " -t seconds CGI and Lua script timeout in seconds, default is 60\n" #endif " -d string URL decode given string\n" " -r string Specify basic auth realm\n" " -m string MD5 crypt given string\n" "\n", argv[0] ); exit(1); } } #ifdef HAVE_TLS if( (tls == 1) && (keys < 2) ) { fprintf(stderr, "Error: Missing private key or certificate file\n"); exit(1); } #endif if( bound < 1 ) { fprintf(stderr, "Error: No sockets bound, unable to continue\n"); exit(1); } /* default docroot */ if( !conf.docroot[0] && !realpath(".", conf.docroot) ) { fprintf(stderr, "Error: Can not determine default document root: %s\n", strerror(errno)); exit(1); } /* default realm */ if( ! conf.realm ) conf.realm = "Protected Area"; /* config file */ uh_config_parse(conf.file); #if defined(HAVE_CGI) || defined(HAVE_LUA) /* default script timeout */ if( conf.script_timeout <= 0 ) conf.script_timeout = 60; #endif #ifdef HAVE_CGI /* default cgi prefix */ if( ! conf.cgi_prefix ) conf.cgi_prefix = "/cgi-bin"; #endif #ifdef HAVE_LUA /* load Lua plugin */ if( ! (lua_lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) ) { fprintf(stderr, "Notice: Unable to load Lua plugin - disabling Lua support! " "(Reason: %s)\n", dlerror() ); } else { /* resolve functions */ if( !(conf.lua_init = dlsym(lua_lib, "uh_lua_init")) || !(conf.lua_close = dlsym(lua_lib, "uh_lua_close")) || !(conf.lua_request = dlsym(lua_lib, "uh_lua_request")) ) { fprintf(stderr, "Error: Failed to lookup required symbols " "in Lua plugin: %s\n", dlerror() ); exit(1); } /* init Lua runtime if handler is specified */ if( conf.lua_handler ) { /* default lua prefix */ if( ! conf.lua_prefix ) conf.lua_prefix = "/lua"; L = conf.lua_init(conf.lua_handler); } } #endif /* fork (if not disabled) */ if( ! nofork ) { switch( fork() ) { case -1: perror("fork()"); exit(1); case 0: /* daemon setup */ if( chdir("/") ) perror("chdir()"); if( (cur_fd = open("/dev/null", O_WRONLY)) > -1 ) dup2(cur_fd, 0); if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 ) dup2(cur_fd, 1); if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 ) dup2(cur_fd, 2); break; default: exit(0); } } /* backup server descriptor set */ used_fds = serv_fds; /* loop */ while(run) { /* create a working copy of the used fd set */ read_fds = used_fds; /* sleep until socket activity */ if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 ) { perror("select()"); exit(1); } /* run through the existing connections looking for data to be read */ for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ ) { /* is a socket managed by us */ if( FD_ISSET(cur_fd, &read_fds) ) { /* is one of our listen sockets */ if( FD_ISSET(cur_fd, &serv_fds) ) { /* handle new connections */ if( (new_fd = accept(cur_fd, NULL, 0)) != -1 ) { /* add to global client list */ if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL ) { #ifdef HAVE_TLS /* setup client tls context */ if( conf.tls ) conf.tls_accept(cl); #endif /* add client socket to global fdset */ FD_SET(new_fd, &used_fds); fd_cloexec(new_fd); max_fd = max(max_fd, new_fd); } /* insufficient resources */ else { fprintf(stderr, "uh_client_add(): Can not manage more than " "%i client sockets, connection dropped\n", UH_LIMIT_CLIENTS ); close(new_fd); } } } /* is a client socket */ else { if( ! (cl = uh_client_lookup(cur_fd)) ) { /* this should not happen! */ fprintf(stderr, "uh_client_lookup(): No entry for fd %i!\n", cur_fd); goto cleanup; } /* parse message header */ if( (req = uh_http_header_recv(cl)) != NULL ) { #ifdef HAVE_LUA /* Lua request? */ if( L && uh_path_match(conf.lua_prefix, req->url) ) { conf.lua_request(cl, req, L); } else #endif /* dispatch request */ if( (pin = uh_path_lookup(cl, req->url)) != NULL ) { /* auth ok? */ if( uh_auth_check(cl, req, pin) ) { #ifdef HAVE_CGI if( uh_path_match(conf.cgi_prefix, pin->name) ) { uh_cgi_request(cl, req, pin); } else #endif { uh_file_request(cl, req, pin); } } } /* 404 */ else { uh_http_sendhf(cl, 404, "Not Found", "No such file or directory"); } } /* 400 */ else { uh_http_sendhf(cl, 400, "Bad Request", "Malformed request received"); } #ifdef HAVE_TLS /* free client tls context */ if( conf.tls ) conf.tls_close(cl); #endif cleanup: /* close client socket */ close(cur_fd); FD_CLR(cur_fd, &used_fds); /* remove from global client list */ uh_client_remove(cur_fd); } } } } #ifdef HAVE_LUA /* destroy the Lua state */ if( L != NULL ) conf.lua_close(L); #endif return 0; }