run = 0;
}
-static void uh_config_parse(const char *path)
+static void uh_sigchld(int sig)
+{
+ while( waitpid(-1, NULL, WNOHANG) > 0 ) { }
+}
+
+static void uh_config_parse(struct config *conf)
{
FILE *c;
char line[512];
char *pass = NULL;
char *eol = NULL;
- if( (c = fopen(path ? path : "/etc/httpd.conf", "r")) != NULL )
+ const char *path = conf->file ? conf->file : "/etc/httpd.conf";
+
+
+ if( (c = fopen(path, "r")) != NULL )
{
memset(line, 0, sizeof(line));
if( !uh_auth_add(line, user, pass) )
{
fprintf(stderr,
- "Can not manage more than %i basic auth realms, "
- "will skip the rest\n", UH_LIMIT_AUTHREALMS
+ "Notice: No password set for user %s, ignoring "
+ "authentication on %s\n", user, line
);
+ }
+ }
+ else if( !strncmp(line, "I:", 2) )
+ {
+ if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+ !(eol = strchr(user, '\n')) || (*eol++ = 0) )
+ continue;
- break;
- }
+ conf->index_file = strdup(user);
+ }
+ else if( !strncmp(line, "E404:", 5) )
+ {
+ if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+ !(eol = strchr(user, '\n')) || (*eol++ = 0) )
+ continue;
+
+ conf->error_handler = strdup(user);
}
}
/* add socket to server fd set */
FD_SET(sock, serv_fds);
+ fd_cloexec(sock);
*max_fd = max(*max_fd, sock);
bound++;
}
/* valid enough */
+ req.redirect_status = 200;
return &req;
}
struct sigaction sa;
struct config conf;
+ /* signal mask */
+ sigset_t ss;
+
/* maximum file descriptor number */
int new_fd, cur_fd, max_fd = 0;
+#ifdef HAVE_TLS
int tls = 0;
int keys = 0;
+#endif
+
int bound = 0;
int nofork = 0;
/* args */
- char opt;
+ int opt;
char bind[128];
char *port = NULL;
- /* library handles */
- void *tls_lib;
- void *lua_lib;
+#if defined(HAVE_TLS) || defined(HAVE_LUA)
+ /* library handle */
+ void *lib;
+#endif
/* clear the master and temp sets */
FD_ZERO(&used_fds);
FD_ZERO(&serv_fds);
FD_ZERO(&read_fds);
- /* handle SIGPIPE, SIGCHILD */
+ /* 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;
#ifdef HAVE_TLS
/* load TLS plugin */
- if( ! (tls_lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) )
+ if( ! (lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) )
{
fprintf(stderr,
"Notice: Unable to load TLS plugin - disabling SSL support! "
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"))
+ if( !(conf.tls_init = dlsym(lib, "uh_tls_ctx_init")) ||
+ !(conf.tls_cert = dlsym(lib, "uh_tls_ctx_cert")) ||
+ !(conf.tls_key = dlsym(lib, "uh_tls_ctx_key")) ||
+ !(conf.tls_free = dlsym(lib, "uh_tls_ctx_free")) ||
+ !(conf.tls_accept = dlsym(lib, "uh_tls_client_accept")) ||
+ !(conf.tls_close = dlsym(lib, "uh_tls_client_close")) ||
+ !(conf.tls_recv = dlsym(lib, "uh_tls_client_recv")) ||
+ !(conf.tls_send = dlsym(lib, "uh_tls_client_send"))
) {
fprintf(stderr,
"Error: Failed to lookup required symbols "
}
#endif
- while( (opt = getopt(argc, argv, "fC:K:p:s:h:c:l:L:d:r:m:x:")) > 0 )
- {
+ while( (opt = getopt(argc, argv,
+ "fSDC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+ ) {
switch(opt)
{
/* [addr:]port */
port = optarg;
}
+#ifdef HAVE_TLS
if( opt == 's' )
{
if( !conf.tls )
tls = 1;
}
+#endif
/* bind sockets */
bound += uh_socket_bind(
&hints, (opt == 's'), &conf
);
+ memset(bind, 0, sizeof(bind));
break;
#ifdef HAVE_TLS
}
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;
+
#ifdef HAVE_CGI
/* cgi prefix */
case 'x':
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;
+
/* no fork */
case 'f':
nofork = 1;
" -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"
#ifdef HAVE_LUA
" -l string URL prefix for Lua handler, default is '/lua'\n"
" -L file Lua handler script, omit to disable Lua\n"
#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
+ " -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"
conf.realm = "Protected Area";
/* config file */
- uh_config_parse(conf.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 */
#ifdef HAVE_LUA
/* load Lua plugin */
- if( ! (lua_lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) )
+ if( ! (lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) )
{
fprintf(stderr,
"Notice: Unable to load Lua plugin - disabling Lua support! "
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"))
+ 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 "
/* add client socket to global fdset */
FD_SET(new_fd, &used_fds);
+ fd_cloexec(new_fd);
max_fd = max(max_fd, new_fd);
}
/* 404 */
else
{
- uh_http_sendhf(cl, 404, "Not Found",
- "No such file or directory");
+ /* Try to invoke an error handler */
+ pin = uh_path_lookup(cl, conf.error_handler);
+
+ if( pin && uh_auth_check(cl, req, pin) )
+ {
+ req->redirect_status = 404;
+
+#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);
+ }
+ }
+ else
+ {
+ uh_http_sendhf(cl, 404, "Not Found",
+ "No such file or directory");
+ }
}
}