/*
- * uhttpd - Tiny non-forking httpd - Main component
+ * uhttpd - Tiny single-threaded httpd - Main component
*
* Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
*
* limitations under the License.
*/
+#define _XOPEN_SOURCE 500 /* crypt() */
+
#include "uhttpd.h"
#include "uhttpd-utils.h"
#include "uhttpd-file.h"
run = 0;
}
+static void uh_config_parse(const char *path)
+{
+ FILE *c;
+ char line[512];
+ char *user = NULL;
+ char *pass = NULL;
+ char *eol = NULL;
+
+ if( (c = fopen(path ? path : "/etc/httpd.conf", "r")) != NULL )
+ {
+ memset(line, 0, sizeof(line));
+
+ while( fgets(line, sizeof(line) - 1, c) )
+ {
+ if( (line[0] == '/') && (strchr(line, ':') != NULL) )
+ {
+ if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+ !(pass = strchr(user, ':')) || (*pass++ = 0) ||
+ !(eol = strchr(pass, '\n')) || (*eol++ = 0) )
+ continue;
+
+ 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
+ );
+
+ break;
+ }
+ }
+ }
+
+ fclose(c);
+ }
+}
+
static int uh_socket_bind(
fd_set *serv_fds, int *max_fd, const char *host, const char *port,
struct addrinfo *hints, int do_tls, struct config *conf
static struct http_request * uh_http_header_recv(struct client *cl)
{
- char buffer[UH_LIMIT_MSGHEAD];
+ static char buffer[UH_LIMIT_MSGHEAD];
char *bufptr = &buffer[0];
char *idxptr = NULL;
return NULL;
}
+static int uh_path_match(const char *prefix, const char *url)
+{
+ if( (strstr(url, prefix) == url) &&
+ ((prefix[strlen(prefix)-1] == '/') ||
+ (strlen(url) == strlen(prefix)) ||
+ (url[strlen(prefix)] == '/'))
+ ) {
+ return 1;
+ }
+
+ return 0;
+}
+
int main (int argc, char **argv)
{
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, SIGCHILD */
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGCHLD, &sa, NULL);
memset(bind, 0, sizeof(bind));
#ifdef HAVE_TLS
- /* init SSL context */
- if( ! (conf.tls = uh_tls_ctx_init()) )
+ /* load TLS plugin */
+ if( ! (tls_lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) )
{
- fprintf(stderr, "Failed to initalize SSL context\n");
- exit(1);
+ 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:")) > 0 )
+ while( (opt = getopt(argc, argv, "fC:K:p:s:h:c:l:L:d:r:m:x:")) > 0 )
{
switch(opt)
{
}
if( opt == 's' )
+ {
+ if( !conf.tls )
+ {
+ fprintf(stderr,
+ "Notice: TLS support is disabled, "
+ "ignoring '-s %s'\n", optarg
+ );
+ continue;
+ }
+
tls = 1;
+ }
/* bind sockets */
bound += uh_socket_bind(
&serv_fds, &max_fd, bind[0] ? bind : NULL, port,
- &hints, tls, &conf
+ &hints, (opt == 's'), &conf
);
break;
#ifdef HAVE_TLS
/* certificate */
case 'C':
- if( SSL_CTX_use_certificate_file(conf.tls, optarg, SSL_FILETYPE_ASN1) < 1 )
+ if( conf.tls )
{
- fprintf(stderr, "Invalid certificate file given\n");
- exit(1);
+ if( conf.tls_cert(conf.tls, optarg) < 1 )
+ {
+ fprintf(stderr,
+ "Error: Invalid certificate file given\n");
+ exit(1);
+ }
+
+ keys++;
}
- keys++;
break;
/* key */
case 'K':
- if( SSL_CTX_use_PrivateKey_file(conf.tls, optarg, SSL_FILETYPE_ASN1) < 1 )
+ if( conf.tls )
{
- fprintf(stderr, "Invalid private key file given\n");
- exit(1);
+ if( conf.tls_key(conf.tls, optarg) < 1 )
+ {
+ fprintf(stderr,
+ "Error: Invalid private key file given\n");
+ exit(1);
+ }
+
+ keys++;
}
- keys++;
break;
#endif
case 'h':
if( ! realpath(optarg, conf.docroot) )
{
- fprintf(stderr, "Invalid directory %s: %s\n", optarg, strerror(errno));
+ fprintf(stderr, "Error: Invalid directory %s: %s\n",
+ optarg, strerror(errno));
exit(1);
}
break;
#ifdef HAVE_CGI
/* cgi prefix */
- case 'c':
+ case 'x':
conf.cgi_prefix = optarg;
break;
#endif
}
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"
- " -p Bind to specified address and port, multiple allowed\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 Like -p but provide HTTPS on this port\n"
- " -C ASN.1 server certificate file\n"
- " -K ASN.1 server private key file\n"
+ " -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 Specify the document root, default is '.'\n"
- " -f Do not fork to background\n"
+ " -h directory Specify the document root, default is '.'\n"
#ifdef HAVE_LUA
- " -l URL prefix for Lua handler, default is '/lua'\n"
- " -L Lua handler script, omit to disable Lua\n"
+ " -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
- " -c URL prefix for CGI handler, default is '/cgi-bin'\n"
+ " -x string URL prefix for CGI handler, default is '/cgi-bin'\n"
#endif
- " -d URL decode given string\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]
);
#ifdef HAVE_TLS
if( (tls == 1) && (keys < 2) )
{
- fprintf(stderr, "Missing private key or certificate file\n");
+ fprintf(stderr, "Error: Missing private key or certificate file\n");
exit(1);
}
#endif
if( bound < 1 )
{
- fprintf(stderr, "No sockets bound, unable to continue\n");
+ fprintf(stderr, "Error: No sockets bound, unable to continue\n");
exit(1);
}
/* default docroot */
if( !conf.docroot[0] && !realpath(".", conf.docroot) )
{
- fprintf(stderr, "Can not determine default document root: %s\n",
+ 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);
+
#ifdef HAVE_CGI
/* default cgi prefix */
if( ! conf.cgi_prefix )
#endif
#ifdef HAVE_LUA
- /* init Lua runtime if handler is specified */
- if( conf.lua_handler )
+ /* load Lua plugin */
+ if( ! (lua_lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) )
{
- /* default lua prefix */
- if( ! conf.lua_prefix )
- conf.lua_prefix = "/lua";
+ 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);
+ }
- L = uh_lua_init(conf.lua_handler);
+ /* 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
{
#ifdef HAVE_TLS
/* setup client tls context */
- uh_tls_client_accept(cl);
+ if( conf.tls )
+ conf.tls_accept(cl);
#endif
/* add client socket to global fdset */
/* 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 )
{
-#ifdef HAVE_CGI
- if( strstr(pin->name, conf.cgi_prefix) == pin->name )
+ /* auth ok? */
+ if( uh_auth_check(cl, req, pin) )
{
- uh_cgi_request(cl, req, pin);
- }
- else
+#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);
+ {
+ uh_file_request(cl, req, pin);
+ }
}
}
-#ifdef HAVE_LUA
- /* Lua request? */
- else if( strstr(req->url, conf.lua_prefix) == req->url )
- {
- uh_lua_request(cl, req, L);
- }
-#endif
+
/* 404 */
else
{
#ifdef HAVE_TLS
/* free client tls context */
- uh_tls_client_close(cl);
+ if( conf.tls )
+ conf.tls_close(cl);
#endif
cleanup:
#ifdef HAVE_LUA
/* destroy the Lua state */
if( L != NULL )
- lua_close(L);
+ conf.lua_close(L);
#endif
return 0;