X-Git-Url: https://git.archive.openwrt.org/?a=blobdiff_plain;ds=inline;f=contrib%2Fpackage%2Fuhttpd%2Fsrc%2Fuhttpd.c;h=a7db794a5bcf64b3a8e9a7c18731eaa86dc0ea5e;hb=af4611d4e02c8a5b0c72cae08c1d944528d63787;hp=1f66f785f05f9efd98705dbac1f825fadae7bda9;hpb=3403abe05fc62efda046fe65c0b43ce785ab1f4d;p=project%2Fluci.git diff --git a/contrib/package/uhttpd/src/uhttpd.c b/contrib/package/uhttpd/src/uhttpd.c index 1f66f785f..a7db794a5 100644 --- a/contrib/package/uhttpd/src/uhttpd.c +++ b/contrib/package/uhttpd/src/uhttpd.c @@ -1,3 +1,23 @@ +/* + * uhttpd - Tiny single-threaded httpd - Main component + * + * Copyright (C) 2010 Jo-Philipp Wich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _XOPEN_SOURCE 500 /* crypt() */ + #include "uhttpd.h" #include "uhttpd-utils.h" #include "uhttpd-file.h" @@ -22,6 +42,43 @@ static void uh_sigterm(int sig) 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 @@ -104,7 +161,7 @@ static int uh_socket_bind( continue; error: - if( sock > 0 ) + if( sock > 0 ) close(sock); } @@ -250,7 +307,7 @@ static struct http_request * uh_http_header_parse(struct client *cl, char *buffe 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; @@ -315,32 +372,17 @@ static struct http_request * uh_http_header_recv(struct client *cl) return NULL; } -static int uh_docroot_resolve(const char *path, char *buf) +static int uh_path_match(const char *prefix, const char *url) { - char curpath[PATH_MAX]; - - if( ! getcwd(curpath, sizeof(curpath)) ) - { - perror("getcwd()"); - return 0; - } - - if( chdir(path) || !getcwd(buf, PATH_MAX) ) - { - return 0; - } - else - { - buf[strlen(buf)] = '/'; - } - - if( chdir(curpath) ) - { - perror("chdir()"); - return 0; + if( (strstr(url, prefix) == url) && + ((prefix[strlen(prefix)-1] == '/') || + (strlen(url) == strlen(prefix)) || + (url[strlen(prefix)] == '/')) + ) { + return 1; } - return 1; + return 0; } @@ -357,12 +399,15 @@ int main (int argc, char **argv) /* working structs */ struct addrinfo hints; struct http_request *req; + struct path_info *pin; struct client *cl; struct sigaction sa; struct config conf; /* maximum file descriptor number */ int new_fd, cur_fd, max_fd = 0; + + int tls = 0; int keys = 0; int bound = 0; int nofork = 0; @@ -372,12 +417,19 @@ 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); @@ -397,15 +449,43 @@ int main (int argc, char **argv) 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) { @@ -428,10 +508,24 @@ int main (int argc, char **argv) port = optarg; } + 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, - (opt == 's') ? 1 : 0, &conf + &serv_fds, &max_fd, bind[0] ? bind : NULL, port, + &hints, (opt == 's'), &conf ); break; @@ -439,39 +533,50 @@ int main (int argc, char **argv) #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 /* docroot */ case 'h': - if( ! uh_docroot_resolve(optarg, conf.docroot) ) + if( ! realpath(optarg, conf.docroot) ) { - fprintf(stderr, "Invalid directory: %s\n", optarg); + 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 @@ -505,25 +610,44 @@ int main (int argc, char **argv) } 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, default is './lua/handler.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] ); @@ -532,26 +656,34 @@ int main (int argc, char **argv) } #ifdef HAVE_TLS - if( keys < 2 ) + 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] && !uh_docroot_resolve(".", conf.docroot) ) + if( !conf.docroot[0] && !realpath(".", conf.docroot) ) { - fprintf(stderr, "Can not determine default document root\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 ) @@ -559,14 +691,37 @@ int main (int argc, char **argv) #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); + } + + /* init Lua runtime if handler is specified */ + if( conf.lua_handler ) + { + /* default lua prefix */ + if( ! conf.lua_prefix ) + conf.lua_prefix = "/lua"; - L = uh_lua_init(conf.lua_handler); + L = conf.lua_init(conf.lua_handler); + } } #endif @@ -621,7 +776,7 @@ int main (int argc, char **argv) { /* 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) ) { @@ -633,12 +788,13 @@ int main (int argc, char **argv) { #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 */ FD_SET(new_fd, &used_fds); - max_fd = max(max_fd, new_fd); + max_fd = max(max_fd, new_fd); } /* insufficient resources */ @@ -668,35 +824,55 @@ int main (int argc, char **argv) goto cleanup; } - /* parse message header and dispatch request */ + /* parse message header */ if( (req = uh_http_header_recv(cl)) != NULL ) { -#ifdef HAVE_CGI - if( strstr(req->url, conf.cgi_prefix) == req->url ) +#ifdef HAVE_LUA + /* Lua request? */ + if( L && uh_path_match(conf.lua_prefix, req->url) ) { - uh_cgi_request(cl, req); + conf.lua_request(cl, req, L); } else #endif - -#ifdef HAVE_LUA - if( (L != NULL) && - (strstr(req->url, conf.lua_prefix) == req->url) - ) { - uh_lua_request(cl, req, L); - } - else + /* 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_file_request(cl, req); + 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 */ - uh_tls_client_close(cl); + if( conf.tls ) + conf.tls_close(cl); #endif cleanup: @@ -715,7 +891,7 @@ int main (int argc, char **argv) #ifdef HAVE_LUA /* destroy the Lua state */ if( L != NULL ) - lua_close(L); + conf.lua_close(L); #endif return 0;