diff options
-rw-r--r-- | package/uhttpd/Makefile | 44 | ||||
-rwxr-xr-x | package/uhttpd/files/uhttpd.init | 8 | ||||
-rw-r--r-- | package/uhttpd/src/Makefile | 89 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-cgi.c | 556 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-cgi.h | 43 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-file.c | 438 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-file.h | 36 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-lua.c | 579 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-lua.h | 44 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-mimetypes.h | 86 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-tls.c | 170 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-tls.h | 36 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-ubus.c | 957 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-ubus.h | 70 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-utils.c | 1081 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd-utils.h | 140 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd.c | 1288 | ||||
-rw-r--r-- | package/uhttpd/src/uhttpd.h | 214 |
18 files changed, 29 insertions, 5850 deletions
diff --git a/package/uhttpd/Makefile b/package/uhttpd/Makefile index 245426b4e..64ea5f202 100644 --- a/package/uhttpd/Makefile +++ b/package/uhttpd/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> +# Copyright (C) 2010-2012 Jo-Philipp Wich <jow@openwrt.org> # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. @@ -8,9 +8,16 @@ include $(TOPDIR)/rules.mk PKG_NAME:=uhttpd -PKG_RELEASE:=40 +PKG_VERSION:=2012-10-30 +PKG_RELEASE=$(PKG_SOURCE_VERSION) + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=git://nbd.name/uhttpd.git +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) +PKG_SOURCE_VERSION:=e57bf6d8bfa465a50eea2c30269acdfe751a46fd +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz +PKG_MAINTAINER:=Jo-Philipp Wich <jow@openwrt.org> -PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) PKG_CONFIG_DEPENDS := \ CONFIG_PACKAGE_uhttpd_debug \ CONFIG_PACKAGE_uhttpd-mod-lua \ @@ -20,13 +27,14 @@ PKG_CONFIG_DEPENDS := \ CONFIG_PACKAGE_uhttpd-mod-ubus include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + define Package/uhttpd/default SECTION:=net CATEGORY:=Network SUBMENU:=Web Servers/Proxies TITLE:=uHTTPd - tiny, single threaded HTTP server - MAINTAINER:=Jo-Philipp Wich <xm@subsignal.org> endef define Package/uhttpd @@ -71,19 +79,18 @@ define Package/uhttpd-mod-tls/config endchoice endef -UHTTPD_TLS:= +UHTTPD_TLS:=none TLS_CFLAGS:= TLS_LDFLAGS:= ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_cyassl),) UHTTPD_TLS:=cyassl - TLS_CFLAGS:=-I$(STAGING_DIR)/usr/include/cyassl -DTLS_IS_CYASSL + TLS_CFLAGS:=-I$(STAGING_DIR)/usr/include/cyassl TLS_LDFLAGS:=-lcyassl -lm endif ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_openssl),) UHTTPD_TLS:=openssl - TLS_CFLAGS:=-DTLS_IS_OPENSSL TLS_LDFLAGS:=-lssl endif @@ -111,21 +118,16 @@ define Package/uhttpd-mod-ubus/description endef -TARGET_CFLAGS += $(TLS_CFLAGS) $(if $(CONFIG_PACKAGE_uhttpd_debug),-DDEBUG) -ggdb3 -TARGET_LDFLAGS += -lubox -Wl,-rpath-link=$(STAGING_DIR)/usr/lib -MAKE_VARS += \ - FPIC="$(FPIC)" \ - LUA_SUPPORT="$(if $(CONFIG_PACKAGE_uhttpd-mod-lua),1)" \ - TLS_SUPPORT="$(if $(CONFIG_PACKAGE_uhttpd-mod-tls),1)" \ - UBUS_SUPPORT="$(if $(CONFIG_PACKAGE_uhttpd-mod-ubus),1)" \ - UHTTPD_TLS="$(UHTTPD_TLS)" \ - TLS_CFLAGS="$(TLS_CFLAGS)" \ - TLS_LDFLAGS="$(TLS_LDFLAGS)" +TARGET_LDFLAGS += -lubox -lcrypt + +CMAKE_OPTIONS += \ + -DDEBUG=$(if $(CONFIG_PACKAGE_uhttpd_debug),ON,OFF) \ + -DLUA_SUPPORT=$(if $(CONFIG_PACKAGE_uhttpd-mod-lua),ON,OFF) \ + -DUBUS_SUPPORT=$(if $(CONFIG_PACKAGE_uhttpd-mod-ubus),ON,OFF) \ + -DTLS_SUPPORT=$(UHTTPD_TLS) \ + -DTLS_CFLAGS="$(TLS_CFLAGS)" \ + -DTLS_LDFLAGS="$(TLS_LDFLAGS)" \ -define Build/Prepare - mkdir -p $(PKG_BUILD_DIR) - $(CP) ./src/* $(PKG_BUILD_DIR)/ -endef define Package/uhttpd/conffiles /etc/config/uhttpd diff --git a/package/uhttpd/files/uhttpd.init b/package/uhttpd/files/uhttpd.init index 379a9f5b5..ad46d2c9b 100755 --- a/package/uhttpd/files/uhttpd.init +++ b/package/uhttpd/files/uhttpd.init @@ -59,7 +59,7 @@ start_instance() local cfg="$1" local realm="$(uci_get system.@system[0].hostname)" - local listen http https interpreter path + local listen http https interpreter indexes path append_arg "$cfg" home "-h" append_arg "$cfg" realm "-r" "${realm:-OpenWrt}" @@ -71,7 +71,6 @@ start_instance() append_arg "$cfg" network_timeout "-T" append_arg "$cfg" tcp_keepalive "-A" append_arg "$cfg" error_page "-E" - append_arg "$cfg" index_page "-I" append_arg "$cfg" max_requests "-n" 3 append_bool "$cfg" no_symlinks "-S" 0 @@ -88,6 +87,11 @@ start_instance() append UHTTPD_ARGS "-i $path" done + config_get indexes "$cfg" index_page + for path in $indexes; do + append UHTTPD_ARGS "-I $path" + done + config_get https "$cfg" listen_https config_get UHTTPD_KEY "$cfg" key /etc/uhttpd.key config_get UHTTPD_CERT "$cfg" cert /etc/uhttpd.crt diff --git a/package/uhttpd/src/Makefile b/package/uhttpd/src/Makefile deleted file mode 100644 index 98226ed20..000000000 --- a/package/uhttpd/src/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -CGI_SUPPORT ?= 1 -LUA_SUPPORT ?= 1 -TLS_SUPPORT ?= 1 -UHTTPD_TLS ?= cyassl - -CFLAGS ?= -I./lua-5.1.4/src $(TLS_CFLAGS) -O0 -ggdb3 -LDFLAGS ?= -L./lua-5.1.4/src - -CFLAGS += -Wall --std=gnu99 - -ifeq ($(UHTTPD_TLS),openssl) - TLS_LDFLAGS ?= -L./openssl-0.9.8m -lssl - TLS_CFLAGS ?= -I./openssl-0.9.8m/include -DTLS_IS_OPENSSL -else - TLS_LDFLAGS ?= -L./cyassl-1.4.0/src/.libs -lcyassl - TLS_CFLAGS ?= -I./cyassl-1.4.0/include -DTLS_IS_CYASSL -endif - -OBJ := uhttpd.o uhttpd-file.o uhttpd-utils.o -LIB := -Wl,--export-dynamic -lcrypt -ldl - -TLSLIB := -LUALIB := - -HAVE_SHADOW=$(shell echo 'int main(void){ return !getspnam("root"); }' | \ - $(CC) -include shadow.h -xc -o/dev/null - 2>/dev/null && echo yes) - -ifeq ($(HAVE_SHADOW),yes) - CFLAGS += -DHAVE_SHADOW -endif - -ifeq ($(TLS_SUPPORT),1) - CFLAGS += -DHAVE_TLS -endif - -ifeq ($(CGI_SUPPORT),1) - CFLAGS += -DHAVE_CGI -endif - -ifeq ($(LUA_SUPPORT),1) - CFLAGS += -DHAVE_LUA -endif - -ifeq ($(UBUS_SUPPORT),1) - CFLAGS += -DHAVE_UBUS -endif - - -world: compile - -ifeq ($(CGI_SUPPORT),1) - OBJ += uhttpd-cgi.o -endif - -ifeq ($(LUA_SUPPORT),1) - LUALIB := uhttpd_lua.so - - $(LUALIB): uhttpd-lua.c - $(CC) $(CFLAGS) $(LDFLAGS) $(FPIC) \ - -shared -lm -llua -ldl \ - -o $(LUALIB) uhttpd-lua.c -endif - -ifeq ($(TLS_SUPPORT),1) - TLSLIB := uhttpd_tls.so - - $(TLSLIB): uhttpd-tls.c - $(CC) $(CFLAGS) $(LDFLAGS) $(FPIC) \ - -shared $(TLS_LDFLAGS) \ - -o $(TLSLIB) uhttpd-tls.c -endif - -ifeq ($(UBUS_SUPPORT),1) - UBUSLIB := uhttpd_ubus.so - - $(UBUSLIB): uhttpd-ubus.c - $(CC) $(CFLAGS) $(LDFLAGS) $(FPIC) \ - -shared -lubus -ljson -lblobmsg_json \ - -o $(UBUSLIB) uhttpd-ubus.c -endif - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -compile: $(OBJ) $(TLSLIB) $(LUALIB) $(UBUSLIB) - $(CC) -o uhttpd $(LDFLAGS) $(OBJ) $(LIB) - -clean: - rm -f *.o *.so uhttpd diff --git a/package/uhttpd/src/uhttpd-cgi.c b/package/uhttpd/src/uhttpd-cgi.c deleted file mode 100644 index 69af90db4..000000000 --- a/package/uhttpd/src/uhttpd-cgi.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - CGI handler - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#include "uhttpd.h" -#include "uhttpd-utils.h" -#include "uhttpd-cgi.h" - - -static bool -uh_cgi_header_parse(struct http_response *res, char *buf, int len, int *off) -{ - char *bufptr = NULL; - char *hdrname = NULL; - int hdrcount = 0; - int pos = 0; - - if (((bufptr = strfind(buf, len, "\r\n\r\n", 4)) != NULL) || - ((bufptr = strfind(buf, len, "\n\n", 2)) != NULL)) - { - *off = (int)(bufptr - buf) + ((bufptr[0] == '\r') ? 4 : 2); - - memset(res, 0, sizeof(*res)); - - res->statuscode = 200; - res->statusmsg = "OK"; - - bufptr = &buf[0]; - - for (pos = 0; pos < *off; pos++) - { - if (!hdrname && (buf[pos] == ':')) - { - buf[pos++] = 0; - - if ((pos < len) && (buf[pos] == ' ')) - pos++; - - if (pos < len) - { - hdrname = bufptr; - bufptr = &buf[pos]; - } - } - - else if ((buf[pos] == '\r') || (buf[pos] == '\n')) - { - if (! hdrname) - break; - - buf[pos++] = 0; - - if ((pos < len) && (buf[pos] == '\n')) - pos++; - - if (pos <= len) - { - if ((hdrcount+1) < array_size(res->headers)) - { - if (!strcasecmp(hdrname, "Status")) - { - res->statuscode = atoi(bufptr); - - if (res->statuscode < 100) - res->statuscode = 200; - - if (((bufptr = strchr(bufptr, ' ')) != NULL) && - (&bufptr[1] != 0)) - { - res->statusmsg = &bufptr[1]; - } - - D("CGI: HTTP/1.x %03d %s\n", - res->statuscode, res->statusmsg); - } - else - { - D("CGI: HTTP: %s: %s\n", hdrname, bufptr); - - res->headers[hdrcount++] = hdrname; - res->headers[hdrcount++] = bufptr; - } - - bufptr = &buf[pos]; - hdrname = NULL; - } - else - { - return false; - } - } - } - } - - return true; - } - - return false; -} - -static char * uh_cgi_header_lookup(struct http_response *res, - const char *hdrname) -{ - int i; - - foreach_header(i, res->headers) - { - if (!strcasecmp(res->headers[i], hdrname)) - return res->headers[i+1]; - } - - return NULL; -} - -static void uh_cgi_shutdown(struct uh_cgi_state *state) -{ - free(state); -} - -static bool uh_cgi_socket_cb(struct client *cl) -{ - int i, len, blen, hdroff; - char buf[UH_LIMIT_MSGHEAD]; - - struct uh_cgi_state *state = (struct uh_cgi_state *)cl->priv; - struct http_response *res = &cl->response; - struct http_request *req = &cl->request; - - /* there is unread post data waiting */ - while (state->content_length > 0) - { - /* remaining data in http head buffer ... */ - if (cl->httpbuf.len > 0) - { - len = min(state->content_length, cl->httpbuf.len); - - D("CGI: Child(%d) feed %d HTTP buffer bytes\n", cl->proc.pid, len); - - memcpy(buf, cl->httpbuf.ptr, len); - - cl->httpbuf.len -= len; - cl->httpbuf.ptr +=len; - } - - /* read it from socket ... */ - else - { - len = uh_tcp_recv(cl, buf, - min(state->content_length, sizeof(buf))); - - if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) - break; - - D("CGI: Child(%d) feed %d/%d TCP socket bytes\n", - cl->proc.pid, len, min(state->content_length, sizeof(buf))); - } - - if (len) - state->content_length -= len; - else - state->content_length = 0; - - /* ... write to CGI process */ - len = uh_raw_send(cl->wpipe.fd, buf, len, - cl->server->conf->script_timeout); - - /* explicit EOF notification for the child */ - if (state->content_length <= 0) - uh_ufd_remove(&cl->wpipe); - } - - /* try to read data from child */ - while ((len = uh_raw_recv(cl->rpipe.fd, buf, state->header_sent - ? sizeof(buf) : state->httpbuf.len, -1)) > 0) - { - /* we have not pushed out headers yet, parse input */ - if (!state->header_sent) - { - /* try to parse header ... */ - memcpy(state->httpbuf.ptr, buf, len); - state->httpbuf.len -= len; - state->httpbuf.ptr += len; - - blen = state->httpbuf.ptr - state->httpbuf.buf; - - if (uh_cgi_header_parse(res, state->httpbuf.buf, blen, &hdroff)) - { - /* write status */ - ensure_out(uh_http_sendf(cl, NULL, - "%s %03d %s\r\n" - "Connection: close\r\n", - http_versions[req->version], - res->statuscode, res->statusmsg)); - - /* add Content-Type if no Location or Content-Type */ - if (!uh_cgi_header_lookup(res, "Location") && - !uh_cgi_header_lookup(res, "Content-Type")) - { - ensure_out(uh_http_send(cl, NULL, - "Content-Type: text/plain\r\n", -1)); - } - - /* if request was HTTP 1.1 we'll respond chunked */ - if ((req->version > UH_HTTP_VER_1_0) && - !uh_cgi_header_lookup(res, "Transfer-Encoding")) - { - ensure_out(uh_http_send(cl, NULL, - "Transfer-Encoding: chunked\r\n", -1)); - } - - /* write headers from CGI program */ - foreach_header(i, res->headers) - { - ensure_out(uh_http_sendf(cl, NULL, "%s: %s\r\n", - res->headers[i], res->headers[i+1])); - } - - /* terminate header */ - ensure_out(uh_http_send(cl, NULL, "\r\n", -1)); - - state->header_sent = true; - - /* push out remaining head buffer */ - if (hdroff < blen) - { - D("CGI: Child(%d) relaying %d rest bytes\n", - cl->proc.pid, blen - hdroff); - - ensure_out(uh_http_send(cl, req, - state->httpbuf.buf + hdroff, - blen - hdroff)); - } - } - - /* ... failed and head buffer exceeded */ - else if (!state->httpbuf.len) - { - /* I would do this ... - * - * uh_cgi_error_500(cl, req, - * "The CGI program generated an " - * "invalid response:\n\n"); - * - * ... but in order to stay as compatible as possible, - * treat whatever we got as text/plain response and - * build the required headers here. - */ - - ensure_out(uh_http_sendf(cl, NULL, - "%s 200 OK\r\n" - "Content-Type: text/plain\r\n" - "%s\r\n", - http_versions[req->version], - (req->version > UH_HTTP_VER_1_0) - ? "Transfer-Encoding: chunked\r\n" : "" - )); - - state->header_sent = true; - - D("CGI: Child(%d) relaying %d invalid bytes\n", - cl->proc.pid, len); - - ensure_out(uh_http_send(cl, req, buf, len)); - } - } - else - { - /* headers complete, pass through buffer to socket */ - D("CGI: Child(%d) relaying %d normal bytes\n", cl->proc.pid, len); - ensure_out(uh_http_send(cl, req, buf, len)); - } - } - - /* got EOF or read error from child */ - if ((len == 0) || - ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1))) - { - D("CGI: Child(%d) presumed dead [%s]\n", cl->proc.pid, strerror(errno)); - - goto out; - } - - return true; - -out: - if (!state->header_sent) - { - if (cl->timeout.pending) - uh_http_sendhf(cl, 502, "Bad Gateway", - "The CGI process did not produce any response\n"); - else - uh_http_sendhf(cl, 504, "Gateway Timeout", - "The CGI process took too long to produce a " - "response\n"); - } - else - { - uh_http_send(cl, req, "", 0); - } - - uh_cgi_shutdown(state); - return false; -} - -bool uh_cgi_request(struct client *cl, struct path_info *pi, - struct interpreter *ip) -{ - int i; - - int rfd[2] = { 0, 0 }; - int wfd[2] = { 0, 0 }; - - pid_t child; - - struct uh_cgi_state *state; - struct http_request *req = &cl->request; - - /* allocate state */ - if (!(state = malloc(sizeof(*state)))) - { - uh_http_sendhf(cl, 500, "Internal Server Error", "Out of memory"); - return false; - } - - /* spawn pipes for me->child, child->me */ - if ((pipe(rfd) < 0) || (pipe(wfd) < 0)) - { - if (rfd[0] > 0) close(rfd[0]); - if (rfd[1] > 0) close(rfd[1]); - if (wfd[0] > 0) close(wfd[0]); - if (wfd[1] > 0) close(wfd[1]); - - uh_http_sendhf(cl, 500, "Internal Server Error", - "Failed to create pipe: %s\n", strerror(errno)); - - return false; - } - - /* fork off child process */ - switch ((child = fork())) - { - /* oops */ - case -1: - uh_http_sendhf(cl, 500, "Internal Server Error", - "Failed to fork child: %s\n", strerror(errno)); - - return false; - - /* exec child */ - case 0: -#ifdef DEBUG - sleep(atoi(getenv("UHTTPD_SLEEP_ON_FORK") ?: "0")); -#endif - - /* do not leak parent epoll descriptor */ - uloop_done(); - - /* close loose pipe ends */ - close(rfd[0]); - close(wfd[1]); - - /* patch stdout and stdin to pipes */ - dup2(rfd[1], 1); - dup2(wfd[0], 0); - - /* avoid leaking our pipe into child-child processes */ - fd_cloexec(rfd[1]); - fd_cloexec(wfd[0]); - - /* check for regular, world-executable file _or_ interpreter */ - if (((pi->stat.st_mode & S_IFREG) && - (pi->stat.st_mode & S_IXOTH)) || (ip != NULL)) - { - /* build environment */ - clearenv(); - - /* common information */ - setenv("GATEWAY_INTERFACE", "CGI/1.1", 1); - setenv("SERVER_SOFTWARE", "uHTTPd", 1); - setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1); - -#ifdef HAVE_TLS - /* https? */ - if (cl->tls) - setenv("HTTPS", "on", 1); -#endif - - /* addresses */ - setenv("SERVER_NAME", sa_straddr(&cl->servaddr), 1); - setenv("SERVER_ADDR", sa_straddr(&cl->servaddr), 1); - setenv("SERVER_PORT", sa_strport(&cl->servaddr), 1); - setenv("REMOTE_HOST", sa_straddr(&cl->peeraddr), 1); - setenv("REMOTE_ADDR", sa_straddr(&cl->peeraddr), 1); - setenv("REMOTE_PORT", sa_strport(&cl->peeraddr), 1); - - /* path information */ - setenv("SCRIPT_NAME", pi->name, 1); - setenv("SCRIPT_FILENAME", pi->phys, 1); - setenv("DOCUMENT_ROOT", pi->root, 1); - setenv("QUERY_STRING", pi->query ? pi->query : "", 1); - - if (pi->info) - setenv("PATH_INFO", pi->info, 1); - - /* REDIRECT_STATUS, php-cgi wants it */ - switch (req->redirect_status) - { - case 404: - setenv("REDIRECT_STATUS", "404", 1); - break; - - default: - setenv("REDIRECT_STATUS", "200", 1); - break; - } - - /* http version */ - setenv("SERVER_PROTOCOL", http_versions[req->version], 1); - - /* request method */ - setenv("REQUEST_METHOD", http_methods[req->method], 1); - - /* request url */ - setenv("REQUEST_URI", req->url, 1); - - /* remote user */ - if (req->realm) - setenv("REMOTE_USER", req->realm->user, 1); - - /* request message headers */ - foreach_header(i, req->headers) - { - if (!strcasecmp(req->headers[i], "Accept")) - setenv("HTTP_ACCEPT", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Accept-Charset")) - setenv("HTTP_ACCEPT_CHARSET", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Accept-Encoding")) - setenv("HTTP_ACCEPT_ENCODING", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Accept-Language")) - setenv("HTTP_ACCEPT_LANGUAGE", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Authorization")) - setenv("HTTP_AUTHORIZATION", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Connection")) - setenv("HTTP_CONNECTION", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Cookie")) - setenv("HTTP_COOKIE", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Host")) - setenv("HTTP_HOST", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Referer")) - setenv("HTTP_REFERER", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "User-Agent")) - setenv("HTTP_USER_AGENT", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Content-Type")) - setenv("CONTENT_TYPE", req->headers[i+1], 1); - - else if (!strcasecmp(req->headers[i], "Content-Length")) - setenv("CONTENT_LENGTH", req->headers[i+1], 1); - } - - - /* execute child code ... */ - if (chdir(pi->root)) - perror("chdir()"); - - if (ip != NULL) - execl(ip->path, ip->path, pi->phys, NULL); - else - execl(pi->phys, pi->phys, NULL); - - /* in case it fails ... */ - printf("Status: 500 Internal Server Error\r\n\r\n" - "Unable to launch the requested CGI program:\n" - " %s: %s\n", ip ? ip->path : pi->phys, strerror(errno)); - } - - /* 403 */ - else - { - printf("Status: 403 Forbidden\r\n\r\n" - "Access to this resource is forbidden\n"); - } - - close(wfd[0]); - close(rfd[1]); - exit(0); - - break; - - /* parent; handle I/O relaying */ - default: - memset(state, 0, sizeof(*state)); - - cl->rpipe.fd = rfd[0]; - cl->wpipe.fd = wfd[1]; - cl->proc.pid = child; - - /* make pipe non-blocking */ - fd_nonblock(cl->rpipe.fd); - fd_nonblock(cl->wpipe.fd); - - /* close unneeded pipe ends */ - close(rfd[1]); - close(wfd[0]); - - D("CGI: Child(%d) created: rfd(%d) wfd(%d)\n", child, rfd[0], wfd[1]); - - state->httpbuf.ptr = state->httpbuf.buf; - state->httpbuf.len = sizeof(state->httpbuf.buf); - - state->content_length = cl->httpbuf.len; - - /* find content length */ - if (req->method == UH_HTTP_MSG_POST) - { - foreach_header(i, req->headers) - { - if (!strcasecmp(req->headers[i], "Content-Length")) - { - state->content_length = atoi(req->headers[i+1]); - break; - } - } - } - - cl->cb = uh_cgi_socket_cb; - cl->priv = state; - - break; - } - - return true; -} diff --git a/package/uhttpd/src/uhttpd-cgi.h b/package/uhttpd/src/uhttpd-cgi.h deleted file mode 100644 index c7094da42..000000000 --- a/package/uhttpd/src/uhttpd-cgi.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - CGI header - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_CGI_ - -#include <errno.h> -#include <unistd.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <linux/limits.h> - -#include <time.h> - - -struct uh_cgi_state { - struct { - char buf[UH_LIMIT_MSGHEAD]; - char *ptr; - int len; - } httpbuf; - int content_length; - bool header_sent; -}; - -bool uh_cgi_request(struct client *cl, struct path_info *pi, - struct interpreter *ip); - -#endif diff --git a/package/uhttpd/src/uhttpd-file.c b/package/uhttpd/src/uhttpd-file.c deleted file mode 100644 index 0bafc2b38..000000000 --- a/package/uhttpd/src/uhttpd-file.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Static file handler - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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 /* strptime() */ -#define _BSD_SOURCE /* scandir(), timegm() */ - -#include "uhttpd.h" -#include "uhttpd-utils.h" -#include "uhttpd-file.h" - -#include "uhttpd-mimetypes.h" - - -static const char * uh_file_mime_lookup(const char *path) -{ - struct mimetype *m = &uh_mime_types[0]; - const char *e; - - while (m->extn) - { - e = &path[strlen(path)-1]; - - while (e >= path) - { - if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn)) - return m->mime; - - e--; - } - - m++; - } - - return "application/octet-stream"; -} - -static const char * uh_file_mktag(struct stat *s) -{ - static char tag[128]; - - snprintf(tag, sizeof(tag), "\"%x-%x-%x\"", - (unsigned int) s->st_ino, - (unsigned int) s->st_size, - (unsigned int) s->st_mtime); - - return tag; -} - -static time_t uh_file_date2unix(const char *date) -{ - struct tm t; - - memset(&t, 0, sizeof(t)); - - if (strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) != NULL) - return timegm(&t); - - return 0; -} - -static char * uh_file_unix2date(time_t ts) -{ - static char str[128]; - struct tm *t = gmtime(&ts); - - strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S GMT", t); - - return str; -} - -static char * uh_file_header_lookup(struct client *cl, const char *name) -{ - int i; - - foreach_header(i, cl->request.headers) - { - if (!strcasecmp(cl->request.headers[i], name)) - return cl->request.headers[i+1]; - } - - return NULL; -} - - -static int uh_file_response_ok_hdrs(struct client *cl, struct stat *s) -{ - ensure_ret(uh_http_sendf(cl, NULL, "Connection: close\r\n")); - - if (s) - { - ensure_ret(uh_http_sendf(cl, NULL, "ETag: %s\r\n", uh_file_mktag(s))); - ensure_ret(uh_http_sendf(cl, NULL, "Last-Modified: %s\r\n", - uh_file_unix2date(s->st_mtime))); - } - - return uh_http_sendf(cl, NULL, "Date: %s\r\n", uh_file_unix2date(time(NULL))); -} - -static int uh_file_response_200(struct client *cl, struct stat *s) -{ - ensure_ret(uh_http_sendf(cl, NULL, "%s 200 OK\r\n", - http_versions[cl->request.version])); - - return uh_file_response_ok_hdrs(cl, s); -} - -static int uh_file_response_304(struct client *cl, struct stat *s) -{ - ensure_ret(uh_http_sendf(cl, NULL, "%s 304 Not Modified\r\n", - http_versions[cl->request.version])); - - return uh_file_response_ok_hdrs(cl, s); -} - -static int uh_file_response_412(struct client *cl) -{ - return uh_http_sendf(cl, NULL, - "%s 412 Precondition Failed\r\n" - "Connection: close\r\n", - http_versions[cl->request.version]); -} - -static int uh_file_if_match(struct client *cl, struct stat *s, int *ok) -{ - const char *tag = uh_file_mktag(s); - char *hdr = uh_file_header_lookup(cl, "If-Match"); - char *p; - int i; - - if (hdr) - { - p = &hdr[0]; - - for (i = 0; i < strlen(hdr); i++) - { - if ((hdr[i] == ' ') || (hdr[i] == ',')) - { - hdr[i++] = 0; - p = &hdr[i]; - } - else if (!strcmp(p, "*") || !strcmp(p, tag)) - { - *ok = 1; - return *ok; - } - } - - *ok = 0; - ensure_ret(uh_file_response_412(cl)); - return *ok; - } - - *ok = 1; - return *ok; -} - -static int uh_file_if_modified_since(struct client *cl, struct stat *s, int *ok) -{ - char *hdr = uh_file_header_lookup(cl, "If-Modified-Since"); - *ok = 1; - - if (hdr) - { - if (uh_file_date2unix(hdr) >= s->st_mtime) - { - *ok = 0; - ensure_ret(uh_file_response_304(cl, s)); - } - } - - return *ok; -} - -static int uh_file_if_none_match(struct client *cl, struct stat *s, int *ok) -{ - const char *tag = uh_file_mktag(s); - char *hdr = uh_file_header_lookup(cl, "If-None-Match"); - char *p; - int i; - *ok = 1; - - if (hdr) - { - p = &hdr[0]; - - for (i = 0; i < strlen(hdr); i++) - { - if ((hdr[i] == ' ') || (hdr[i] == ',')) - { - hdr[i++] = 0; - p = &hdr[i]; - } - else if (!strcmp(p, "*") || !strcmp(p, tag)) - { - *ok = 0; - - if ((cl->request.method == UH_HTTP_MSG_GET) || - (cl->request.method == UH_HTTP_MSG_HEAD)) - { - ensure_ret(uh_file_response_304(cl, s)); - } - else - { - ensure_ret(uh_file_response_412(cl)); - } - - break; - } - } - } - - return *ok; -} - -static int uh_file_if_range(struct client *cl, struct stat *s, int *ok) -{ - char *hdr = uh_file_header_lookup(cl, "If-Range"); - *ok = 1; - - if (hdr) - { - *ok = 0; - ensure_ret(uh_file_response_412(cl)); - } - - return *ok; -} - -static int uh_file_if_unmodified_since(struct client *cl, struct stat *s, - int *ok) -{ - char *hdr = uh_file_header_lookup(cl, "If-Unmodified-Since"); - *ok = 1; - - if (hdr) - { - if (uh_file_date2unix(hdr) <= s->st_mtime) - { - *ok = 0; - ensure_ret(uh_file_response_412(cl)); - } - } - - return *ok; -} - - -static int uh_file_scandir_filter_dir(const struct dirent *e) -{ - return strcmp(e->d_name, ".") ? 1 : 0; -} - -static void uh_file_dirlist(struct client *cl, struct path_info *pi) -{ - int i; - int count = 0; - char filename[PATH_MAX]; - char *pathptr; - struct dirent **files = NULL; - struct stat s; - - ensure_out(uh_http_sendf(cl, &cl->request, - "<html><head><title>Index of %s</title></head>" - "<body><h1>Index of %s</h1><hr /><ol>", - pi->name, pi->name)); - - if ((count = scandir(pi->phys, &files, uh_file_scandir_filter_dir, - alphasort)) > 0) - { - memset(filename, 0, sizeof(filename)); - memcpy(filename, pi->phys, sizeof(filename)); - pathptr = &filename[strlen(filename)]; - - /* list subdirs */ - for (i = 0; i < count; i++) - { - strncat(filename, files[i]->d_name, - sizeof(filename) - strlen(files[i]->d_name)); - - if (!stat(filename, &s) && - (s.st_mode & S_IFDIR) && (s.st_mode & S_IXOTH)) - { - ensure_out(uh_http_sendf(cl, &cl->request, - "<li><strong><a href='%s%s'>%s</a>/" - "</strong><br /><small>modified: %s" - "<br />directory - %.02f kbyte<br />" - "<br /></small></li>", - pi->name, files[i]->d_name, - files[i]->d_name, - uh_file_unix2date(s.st_mtime), - s.st_size / 1024.0)); - } - - *pathptr = 0; - } - - /* list files */ - for (i = 0; i < count; i++) - { - strncat(filename, files[i]->d_name, - sizeof(filename) - strlen(files[i]->d_name)); - - if (!stat(filename, &s) && - !(s.st_mode & S_IFDIR) && (s.st_mode & S_IROTH)) - { - ensure_out(uh_http_sendf(cl, &cl->request, - "<li><strong><a href='%s%s'>%s</a>" - "</strong><br /><small>modified: %s" - "<br />%s - %.02f kbyte<br />" - "<br /></small></li>", - pi->name, files[i]->d_name, - files[i]->d_name, - uh_file_unix2date(s.st_mtime), - uh_file_mime_lookup(filename), - s.st_size / 1024.0)); - } - - *pathptr = 0; - } - } - - ensure_out(uh_http_sendf(cl, &cl->request, "</ol><hr /></body></html>")); - ensure_out(uh_http_sendf(cl, &cl->request, "")); - -out: - if (files) - { - for (i = 0; i < count; i++) - free(files[i]); - - free(files); - } -} - - -bool uh_file_request(struct client *cl, struct path_info *pi) -{ - int rlen; - int ok = 1; - int fd = -1; - char buf[UH_LIMIT_MSGHEAD]; - - /* we have a file */ - if ((pi->stat.st_mode & S_IFREG) && ((fd = open(pi->phys, O_RDONLY)) > 0)) - { - /* test preconditions */ - if (ok) ensure_out(uh_file_if_modified_since(cl, &pi->stat, &ok)); - if (ok) ensure_out(uh_file_if_match(cl, &pi->stat, &ok)); - if (ok) ensure_out(uh_file_if_range(cl, &pi->stat, &ok)); - if (ok) ensure_out(uh_file_if_unmodified_since(cl, &pi->stat, &ok)); - if (ok) ensure_out(uh_file_if_none_match(cl, &pi->stat, &ok)); - - if (ok > 0) - { - /* write status */ - ensure_out(uh_file_response_200(cl, &pi->stat)); - - ensure_out(uh_http_sendf(cl, NULL, "Content-Type: %s\r\n", - uh_file_mime_lookup(pi->name))); - - ensure_out(uh_http_sendf(cl, NULL, "Content-Length: %i\r\n", - pi->stat.st_size)); - - /* if request was HTTP 1.1 we'll respond chunked */ - if ((cl->request.version > 1.0) && - (cl->request.method != UH_HTTP_MSG_HEAD)) - { - ensure_out(uh_http_send(cl, NULL, - "Transfer-Encoding: chunked\r\n", -1)); - } - - /* close header */ - ensure_out(uh_http_send(cl, NULL, "\r\n", -1)); - - /* send body */ - if (cl->request.method != UH_HTTP_MSG_HEAD) - { - /* pump file data */ - while ((rlen = read(fd, buf, sizeof(buf))) > 0) - ensure_out(uh_http_send(cl, &cl->request, buf, rlen)); - - /* send trailer in chunked mode */ - ensure_out(uh_http_send(cl, &cl->request, "", 0)); - } - } - - /* one of the preconditions failed, terminate opened header and exit */ - else - { - ensure_out(uh_http_send(cl, NULL, "\r\n", -1)); - } - } - - /* directory */ - else if ((pi->stat.st_mode & S_IFDIR) && !cl->server->conf->no_dirlists) - { - /* write status */ - ensure_out(uh_file_response_200(cl, NULL)); - - if (cl->request.version > 1.0) - ensure_out(uh_http_send(cl, NULL, - "Transfer-Encoding: chunked\r\n", -1)); - - ensure_out(uh_http_send(cl, NULL, - "Content-Type: text/html\r\n\r\n", -1)); - - /* content */ - uh_file_dirlist(cl, pi); - } - - /* 403 */ - else - { - ensure_out(uh_http_sendhf(cl, 403, "Forbidden", - "Access to this resource is forbidden")); - } - -out: - if (fd > -1) - close(fd); - - return false; -} diff --git a/package/uhttpd/src/uhttpd-file.h b/package/uhttpd/src/uhttpd-file.h deleted file mode 100644 index 08dbe2cf9..000000000 --- a/package/uhttpd/src/uhttpd-file.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Static file header - * - * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_FILE_ - -#include <fcntl.h> -#include <time.h> -#include <strings.h> -#include <dirent.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <linux/limits.h> - -struct mimetype { - const char *extn; - const char *mime; -}; - -bool uh_file_request(struct client *cl, struct path_info *pi); - -#endif diff --git a/package/uhttpd/src/uhttpd-lua.c b/package/uhttpd/src/uhttpd-lua.c deleted file mode 100644 index e11393739..000000000 --- a/package/uhttpd/src/uhttpd-lua.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Lua handler - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#include "uhttpd.h" -#include "uhttpd-utils.h" -#include "uhttpd-lua.h" - - -static int uh_lua_recv(lua_State *L) -{ - size_t length; - - char buffer[UH_LIMIT_MSGHEAD]; - - int to = 1; - int fd = fileno(stdin); - int rlen = 0; - - length = luaL_checknumber(L, 1); - - if ((length > 0) && (length <= sizeof(buffer))) - { - /* receive data */ - rlen = uh_raw_recv(fd, buffer, length, to); - - /* data read */ - if (rlen > 0) - { - lua_pushnumber(L, rlen); - lua_pushlstring(L, buffer, rlen); - return 2; - } - - /* eof */ - else if (rlen == 0) - { - lua_pushnumber(L, 0); - return 1; - } - - /* no, timeout and actually no data */ - else - { - lua_pushnumber(L, -1); - return 1; - } - } - - /* parameter error */ - lua_pushnumber(L, -2); - return 1; -} - -static int uh_lua_send_common(lua_State *L, bool chunked) -{ - size_t length; - - char chunk[16]; - const char *buffer; - - int rv; - int to = 1; - int fd = fileno(stdout); - int slen = 0; - - buffer = luaL_checklstring(L, 1, &length); - - if (chunked) - { - if (length > 0) - { - snprintf(chunk, sizeof(chunk), "%X\r\n", length); - - ensure_out(rv = uh_raw_send(fd, chunk, strlen(chunk), to)); - slen += rv; - - ensure_out(rv = uh_raw_send(fd, buffer, length, to)); - slen += rv; - - ensure_out(rv = uh_raw_send(fd, "\r\n", 2, to)); - slen += rv; - } - else - { - slen = uh_raw_send(fd, "0\r\n\r\n", 5, to); - } - } - else - { - slen = uh_raw_send(fd, buffer, length, to); - } - -out: - lua_pushnumber(L, slen); - return 1; -} - -static int uh_lua_send(lua_State *L) -{ - return uh_lua_send_common(L, false); -} - -static int uh_lua_sendc(lua_State *L) -{ - return uh_lua_send_common(L, true); -} - -static int uh_lua_str2str(lua_State *L, int (*xlate_func) (char *, int, const char *, int)) -{ - size_t inlen; - int outlen; - const char *inbuf; - char outbuf[UH_LIMIT_MSGHEAD]; - - inbuf = luaL_checklstring(L, 1, &inlen); - outlen = (* xlate_func)(outbuf, sizeof(outbuf), inbuf, inlen); - if (outlen < 0) - luaL_error(L, "%s on URL-encode codec", - (outlen==-1) ? "buffer overflow" : "malformed string"); - - lua_pushlstring(L, outbuf, outlen); - return 1; -} - -static int uh_lua_urldecode(lua_State *L) -{ - return uh_lua_str2str( L, uh_urldecode ); -} - - -static int uh_lua_urlencode(lua_State *L) -{ - return uh_lua_str2str( L, uh_urlencode ); -} - - -lua_State * uh_lua_init(const struct config *conf) -{ - lua_State *L = lua_open(); - const char *err_str = NULL; - - /* Load standard libaries */ - luaL_openlibs(L); - - /* build uhttpd api table */ - lua_newtable(L); - - /* register global send and receive functions */ - lua_pushcfunction(L, uh_lua_recv); - lua_setfield(L, -2, "recv"); - - lua_pushcfunction(L, uh_lua_send); - lua_setfield(L, -2, "send"); - - lua_pushcfunction(L, uh_lua_sendc); - lua_setfield(L, -2, "sendc"); - - lua_pushcfunction(L, uh_lua_urldecode); - lua_setfield(L, -2, "urldecode"); - - lua_pushcfunction(L, uh_lua_urlencode); - lua_setfield(L, -2, "urlencode"); - - /* Pass the document-root to the Lua handler by placing it in - ** uhttpd.docroot. It could alternatively be placed in env.DOCUMENT_ROOT - ** which would more closely resemble the CGI protocol; but would mean that - ** it is not available at the time when the handler-chunk is loaded but - ** rather not until the handler is called, without any code savings. */ - lua_pushstring(L, conf->docroot); - lua_setfield(L, -2, "docroot"); - - /* _G.uhttpd = { ... } */ - lua_setfield(L, LUA_GLOBALSINDEX, "uhttpd"); - - - /* load Lua handler */ - switch (luaL_loadfile(L, conf->lua_handler)) - { - case LUA_ERRSYNTAX: - fprintf(stderr, - "Lua handler contains syntax errors, unable to continue\n"); - exit(1); - - case LUA_ERRMEM: - fprintf(stderr, - "Lua handler ran out of memory, unable to continue\n"); - exit(1); - - case LUA_ERRFILE: - fprintf(stderr, - "Lua cannot open the handler script, unable to continue\n"); - exit(1); - - default: - /* compile Lua handler */ - switch (lua_pcall(L, 0, 0, 0)) - { - case LUA_ERRRUN: - err_str = luaL_checkstring(L, -1); - fprintf(stderr, - "Lua handler had runtime error, " - "unable to continue\n" - "Error: %s\n", err_str); - exit(1); - - case LUA_ERRMEM: - err_str = luaL_checkstring(L, -1); - fprintf(stderr, - "Lua handler ran out of memory, " - "unable to continue\n" - "Error: %s\n", err_str); - exit(1); - - default: - /* test handler function */ - lua_getglobal(L, UH_LUA_CALLBACK); - - if (! lua_isfunction(L, -1)) - { - fprintf(stderr, - "Lua handler provides no "UH_LUA_CALLBACK"(), " - "unable to continue\n"); - exit(1); - } - - lua_pop(L, 1); - break; - } - - break; - } - - return L; -} - -static void uh_lua_shutdown(struct uh_lua_state *state) -{ - free(state); -} - -static bool uh_lua_socket_cb(struct client *cl) -{ - int len; - char buf[UH_LIMIT_MSGHEAD]; - - struct uh_lua_state *state = (struct uh_lua_state *)cl->priv; - - /* there is unread post data waiting */ - while (state->content_length > 0) - { - /* remaining data in http head buffer ... */ - if (cl->httpbuf.len > 0) - { - len = min(state->content_length, cl->httpbuf.len); - - D("Lua: Child(%d) feed %d HTTP buffer bytes\n", cl->proc.pid, len); - - memcpy(buf, cl->httpbuf.ptr, len); - - cl->httpbuf.len -= len; - cl->httpbuf.ptr += len; - } - - /* read it from socket ... */ - else - { - len = uh_tcp_recv(cl, buf, min(state->content_length, sizeof(buf))); - - if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) - break; - - D("Lua: Child(%d) feed %d/%d TCP socket bytes\n", - cl->proc.pid, len, min(state->content_length, sizeof(buf))); - } - - if (len) - state->content_length -= len; - else - state->content_length = 0; - - /* ... write to Lua process */ - len = uh_raw_send(cl->wpipe.fd, buf, len, - cl->server->conf->script_timeout); - - /* explicit EOF notification for the child */ - if (state->content_length <= 0) - uh_ufd_remove(&cl->wpipe); - } - - /* try to read data from child */ - while ((len = uh_raw_recv(cl->rpipe.fd, buf, sizeof(buf), -1)) > 0) - { - /* pass through buffer to socket */ - D("Lua: Child(%d) relaying %d normal bytes\n", cl->proc.pid, len); - ensure_out(uh_tcp_send(cl, buf, len)); - state->data_sent = true; - } - - /* got EOF or read error from child */ - if ((len == 0) || - ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1))) - { - D("Lua: Child(%d) presumed dead [%s]\n", - cl->proc.pid, strerror(errno)); - - goto out; - } - - return true; - -out: - if (!state->data_sent) - { - if (cl->timeout.pending) - uh_http_sendhf(cl, 502, "Bad Gateway", - "The Lua process did not produce any response\n"); - else - uh_http_sendhf(cl, 504, "Gateway Timeout", - "The Lua process took too long to produce a " - "response\n"); - } - - uh_lua_shutdown(state); - return false; -} - -bool uh_lua_request(struct client *cl, lua_State *L) -{ - int i; - char *query_string; - const char *prefix = cl->server->conf->lua_prefix; - const char *err_str = NULL; - - int rfd[2] = { 0, 0 }; - int wfd[2] = { 0, 0 }; - - pid_t child; - - struct uh_lua_state *state; - struct http_request *req = &cl->request; - - int content_length = cl->httpbuf.len; - - - /* allocate state */ - if (!(state = malloc(sizeof(*state)))) - { - uh_client_error(cl, 500, "Internal Server Error", "Out of memory"); - return false; - } - - /* spawn pipes for me->child, child->me */ - if ((pipe(rfd) < 0) || (pipe(wfd) < 0)) - { - if (rfd[0] > 0) close(rfd[0]); - if (rfd[1] > 0) close(rfd[1]); - if (wfd[0] > 0) close(wfd[0]); - if (wfd[1] > 0) close(wfd[1]); - - uh_client_error(cl, 500, "Internal Server Error", - "Failed to create pipe: %s", strerror(errno)); - - return false; - } - - - switch ((child = fork())) - { - case -1: - uh_client_error(cl, 500, "Internal Server Error", - "Failed to fork child: %s", strerror(errno)); - - return false; - - case 0: -#ifdef DEBUG - sleep(atoi(getenv("UHTTPD_SLEEP_ON_FORK") ?: "0")); -#endif - - /* do not leak parent epoll descriptor */ - uloop_done(); - - /* close loose pipe ends */ - close(rfd[0]); - close(wfd[1]); - - /* patch stdout and stdin to pipes */ - dup2(rfd[1], 1); - dup2(wfd[0], 0); - - /* avoid leaking our pipe into child-child processes */ - fd_cloexec(rfd[1]); - fd_cloexec(wfd[0]); - - /* put handler callback on stack */ - lua_getglobal(L, UH_LUA_CALLBACK); - - /* build env table */ - lua_newtable(L); - - /* request method */ - lua_pushstring(L, http_methods[req->method]); - lua_setfield(L, -2, "REQUEST_METHOD"); - - /* request url */ - lua_pushstring(L, req->url); - lua_setfield(L, -2, "REQUEST_URI"); - - /* script name */ - lua_pushstring(L, cl->server->conf->lua_prefix); - lua_setfield(L, -2, "SCRIPT_NAME"); - - /* query string, path info */ - if ((query_string = strchr(req->url, '?')) != NULL) - { - lua_pushstring(L, query_string + 1); - lua_setfield(L, -2, "QUERY_STRING"); - - if ((int)(query_string - req->url) > strlen(prefix)) - { - lua_pushlstring(L, - &req->url[strlen(prefix)], - (int)(query_string - req->url) - strlen(prefix) - ); - - lua_setfield(L, -2, "PATH_INFO"); - } - } - else if (strlen(req->url) > strlen(prefix)) - { - lua_pushstring(L, &req->url[strlen(prefix)]); - lua_setfield(L, -2, "PATH_INFO"); - } - - /* http protcol version */ - lua_pushnumber(L, 0.9 + (req->version / 10.0)); - lua_setfield(L, -2, "HTTP_VERSION"); - - lua_pushstring(L, http_versions[req->version]); - lua_setfield(L, -2, "SERVER_PROTOCOL"); - - - /* address information */ - lua_pushstring(L, sa_straddr(&cl->peeraddr)); - lua_setfield(L, -2, "REMOTE_ADDR"); - - lua_pushinteger(L, sa_port(&cl->peeraddr)); - lua_setfield(L, -2, "REMOTE_PORT"); - - lua_pushstring(L, sa_straddr(&cl->servaddr)); - lua_setfield(L, -2, "SERVER_ADDR"); - - lua_pushinteger(L, sa_port(&cl->servaddr)); - lua_setfield(L, -2, "SERVER_PORT"); - - /* essential env vars */ - foreach_header(i, req->headers) - { - if (!strcasecmp(req->headers[i], "Content-Length")) - { - content_length = atoi(req->headers[i+1]); - } - else if (!strcasecmp(req->headers[i], "Content-Type")) - { - lua_pushstring(L, req->headers[i+1]); - lua_setfield(L, -2, "CONTENT_TYPE"); - } - } - - lua_pushnumber(L, content_length); - lua_setfield(L, -2, "CONTENT_LENGTH"); - - /* misc. headers */ - lua_newtable(L); - - foreach_header(i, req->headers) - { - if( strcasecmp(req->headers[i], "Content-Length") && - strcasecmp(req->headers[i], "Content-Type")) - { - lua_pushstring(L, req->headers[i+1]); - lua_setfield(L, -2, req->headers[i]); - } - } - - lua_setfield(L, -2, "headers"); - - - /* call */ - switch (lua_pcall(L, 1, 0, 0)) - { - case LUA_ERRMEM: - case LUA_ERRRUN: - err_str = luaL_checkstring(L, -1); - - if (! err_str) - err_str = "Unknown error"; - - printf("%s 500 Internal Server Error\r\n" - "Connection: close\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: %i\r\n\r\n" - "Lua raised a runtime error:\n %s\n", - http_versions[req->version], - 31 + strlen(err_str), err_str); - - break; - - default: - break; - } - - close(wfd[0]); - close(rfd[1]); - exit(0); - - break; - - /* parent; handle I/O relaying */ - default: - memset(state, 0, sizeof(*state)); - - cl->rpipe.fd = rfd[0]; - cl->wpipe.fd = wfd[1]; - cl->proc.pid = child; - - /* make pipe non-blocking */ - fd_nonblock(cl->rpipe.fd); - fd_nonblock(cl->wpipe.fd); - - /* close unneeded pipe ends */ - close(rfd[1]); - close(wfd[0]); - - D("Lua: Child(%d) created: rfd(%d) wfd(%d)\n", child, rfd[0], wfd[1]); - - state->content_length = cl->httpbuf.len; - - /* find content length */ - if (req->method == UH_HTTP_MSG_POST) - { - foreach_header(i, req->headers) - { - if (!strcasecmp(req->headers[i], "Content-Length")) - { - state->content_length = atoi(req->headers[i+1]); - break; - } - } - } - - cl->cb = uh_lua_socket_cb; - cl->priv = state; - - break; - } - - return true; -} - -void uh_lua_close(lua_State *L) -{ - lua_close(L); -} diff --git a/package/uhttpd/src/uhttpd-lua.h b/package/uhttpd/src/uhttpd-lua.h deleted file mode 100644 index 780d845d8..000000000 --- a/package/uhttpd/src/uhttpd-lua.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Lua header - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_LUA_ - -#include <math.h> /* floor() */ -#include <errno.h> - -#include <lua.h> -#include <lauxlib.h> -#include <lualib.h> - -#define UH_LUA_CALLBACK "handle_request" - -#define UH_LUA_ERR_TIMEOUT -1 -#define UH_LUA_ERR_TOOBIG -2 -#define UH_LUA_ERR_PARAM -3 - - -struct uh_lua_state { - int content_length; - bool data_sent; -}; - -lua_State * uh_lua_init(const struct config *conf); -bool uh_lua_request(struct client *cl, lua_State *L); -void uh_lua_close(lua_State *L); - -#endif diff --git a/package/uhttpd/src/uhttpd-mimetypes.h b/package/uhttpd/src/uhttpd-mimetypes.h deleted file mode 100644 index 21717c000..000000000 --- a/package/uhttpd/src/uhttpd-mimetypes.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - MIME type definitions - * - * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_MIMETYPES_ - -static struct mimetype uh_mime_types[] = { - - { "txt", "text/plain" }, - { "log", "text/plain" }, - { "js", "text/javascript" }, - { "css", "text/css" }, - { "htm", "text/html" }, - { "html", "text/html" }, - { "diff", "text/x-patch" }, - { "patch", "text/x-patch" }, - { "c", "text/x-csrc" }, - { "h", "text/x-chdr" }, - { "o", "text/x-object" }, - { "ko", "text/x-object" }, - - { "bmp", "image/bmp" }, - { "gif", "image/gif" }, - { "png", "image/png" }, - { "jpg", "image/jpeg" }, - { "jpeg", "image/jpeg" }, - { "svg", "image/svg+xml" }, - - { "zip", "application/zip" }, - { "pdf", "application/pdf" }, - { "xml", "application/xml" }, - { "xsl", "application/xml" }, - { "doc", "application/msword" }, - { "ppt", "application/vnd.ms-powerpoint" }, - { "xls", "application/vnd.ms-excel" }, - { "odt", "application/vnd.oasis.opendocument.text" }, - { "odp", "application/vnd.oasis.opendocument.presentation" }, - { "pl", "application/x-perl" }, - { "sh", "application/x-shellscript" }, - { "php", "application/x-php" }, - { "deb", "application/x-deb" }, - { "iso", "application/x-cd-image" }, - { "tar.gz", "application/x-compressed-tar" }, - { "tgz", "application/x-compressed-tar" }, - { "gz", "application/x-gzip" }, - { "tar.bz2", "application/x-bzip-compressed-tar" }, - { "tbz", "application/x-bzip-compressed-tar" }, - { "bz2", "application/x-bzip" }, - { "tar", "application/x-tar" }, - { "rar", "application/x-rar-compressed" }, - - { "mp3", "audio/mpeg" }, - { "ogg", "audio/x-vorbis+ogg" }, - { "wav", "audio/x-wav" }, - - { "mpg", "video/mpeg" }, - { "mpeg", "video/mpeg" }, - { "avi", "video/x-msvideo" }, - - { "README", "text/plain" }, - { "log", "text/plain" }, - { "cfg", "text/plain" }, - { "conf", "text/plain" }, - - { "pac", "application/x-ns-proxy-autoconfig" }, - { "wpad.dat", "application/x-ns-proxy-autoconfig" }, - - { NULL, NULL } -}; - -#endif - diff --git a/package/uhttpd/src/uhttpd-tls.c b/package/uhttpd/src/uhttpd-tls.c deleted file mode 100644 index 9c6eb81db..000000000 --- a/package/uhttpd/src/uhttpd-tls.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - TLS helper - * - * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#include "uhttpd.h" -#include "uhttpd-tls.h" -#include "uhttpd-utils.h" - -#include <syslog.h> -#define dbg(...) syslog(LOG_INFO, __VA_ARGS__) - -SSL_CTX * uh_tls_ctx_init(void) -{ - SSL_CTX *c; - - SSL_load_error_strings(); - SSL_library_init(); - -#if TLS_IS_OPENSSL - if ((c = SSL_CTX_new(SSLv23_server_method())) != NULL) -#else - if ((c = SSL_CTX_new(TLSv1_server_method())) != NULL) -#endif - SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL); - - return c; -} - -int uh_tls_ctx_cert(SSL_CTX *c, const char *file) -{ - int rv; - - if( (rv = SSL_CTX_use_certificate_file(c, file, SSL_FILETYPE_PEM)) < 1 ) - rv = SSL_CTX_use_certificate_file(c, file, SSL_FILETYPE_ASN1); - - return rv; -} - -int uh_tls_ctx_key(SSL_CTX *c, const char *file) -{ - int rv; - - if( (rv = SSL_CTX_use_PrivateKey_file(c, file, SSL_FILETYPE_PEM)) < 1 ) - rv = SSL_CTX_use_PrivateKey_file(c, file, SSL_FILETYPE_ASN1); - - return rv; -} - -void uh_tls_ctx_free(struct listener *l) -{ - SSL_CTX_free(l->tls); -} - - -int uh_tls_client_accept(struct client *c) -{ - int rv, err; - int fd = c->fd.fd; - - if (!c->server || !c->server->tls) - { - c->tls = NULL; - return 1; - } - - if ((c->tls = SSL_new(c->server->tls))) - { - if ((rv = SSL_set_fd(c->tls, fd)) < 1) - { - SSL_free(c->tls); - c->tls = NULL; - } - else - { - while (true) - { - rv = SSL_accept(c->tls); - err = SSL_get_error(c->tls, rv); - - if ((rv != 1) && - (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)) - { - if (uh_socket_wait(fd, c->server->conf->network_timeout, - (err == SSL_ERROR_WANT_WRITE))) - { - D("TLS: accept(%d) = retry\n", fd); - continue; - } - - D("TLS: accept(%d) = timeout\n", fd); - } - else if (rv == 1) - { - D("TLS: accept(%d) = %p\n", fd, c->tls); - return 1; - } - -#ifdef TLS_IS_OPENSSL - D("TLS: accept(%d) = failed: %s\n", - fd, ERR_error_string(ERR_get_error(), NULL)); -#endif - - SSL_free(c->tls); - c->tls = NULL; - break; - } - } - } - - return 0; -} - -int uh_tls_client_recv(struct client *c, char *buf, int len) -{ - int rv = SSL_read(c->tls, buf, len); - int err = SSL_get_error(c->tls, 0); - - if ((rv == -1) && (err == SSL_ERROR_WANT_READ)) - { - D("TLS: recv(%d, %d) = retry\n", c->fd.fd, len); - errno = EAGAIN; - return -1; - } - - D("TLS: recv(%d, %d) = %d\n", c->fd.fd, len, rv); - return rv; -} - -int uh_tls_client_send(struct client *c, const char *buf, int len) -{ - int rv = SSL_write(c->tls, buf, len); - int err = SSL_get_error(c->tls, 0); - - if ((rv == -1) && (err == SSL_ERROR_WANT_WRITE)) - { - D("TLS: send(%d, %d) = retry\n", c->fd.fd, len); - errno = EAGAIN; - return -1; - } - - D("TLS: send(%d, %d) = %d\n", c->fd.fd, len, rv); - return rv; -} - -void uh_tls_client_close(struct client *c) -{ - if (c->tls) - { - D("TLS: close(%d)\n", c->fd.fd); - - SSL_shutdown(c->tls); - SSL_free(c->tls); - - c->tls = NULL; - } -} diff --git a/package/uhttpd/src/uhttpd-tls.h b/package/uhttpd/src/uhttpd-tls.h deleted file mode 100644 index 8644c2ac5..000000000 --- a/package/uhttpd/src/uhttpd-tls.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - TLS header - * - * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_TLS_ - -#include <openssl/ssl.h> -#ifdef TLS_IS_OPENSSL -#include <openssl/err.h> -#endif - -SSL_CTX * uh_tls_ctx_init(); -int uh_tls_ctx_cert(SSL_CTX *c, const char *file); -int uh_tls_ctx_key(SSL_CTX *c, const char *file); -void uh_tls_ctx_free(struct listener *l); - -int uh_tls_client_accept(struct client *c); -int uh_tls_client_recv(struct client *c, char *buf, int len); -int uh_tls_client_send(struct client *c, const char *buf, int len); -void uh_tls_client_close(struct client *c); - -#endif diff --git a/package/uhttpd/src/uhttpd-ubus.c b/package/uhttpd/src/uhttpd-ubus.c deleted file mode 100644 index 20781629b..000000000 --- a/package/uhttpd/src/uhttpd-ubus.c +++ /dev/null @@ -1,957 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - ubus handler - * - * Copyright (C) 2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#include "uhttpd.h" -#include "uhttpd-utils.h" -#include "uhttpd-ubus.h" - - -enum { - UH_UBUS_SN_TIMEOUT, - __UH_UBUS_SN_MAX, -}; - -static const struct blobmsg_policy new_policy[__UH_UBUS_SN_MAX] = { - [UH_UBUS_SN_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 }, -}; - - -enum { - UH_UBUS_SI_SID, - __UH_UBUS_SI_MAX, -}; - -static const struct blobmsg_policy sid_policy[__UH_UBUS_SI_MAX] = { - [UH_UBUS_SI_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING }, -}; - - -enum { - UH_UBUS_SS_SID, - UH_UBUS_SS_VALUES, - __UH_UBUS_SS_MAX, -}; - -static const struct blobmsg_policy set_policy[__UH_UBUS_SS_MAX] = { - [UH_UBUS_SS_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING }, - [UH_UBUS_SS_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE }, -}; - - -enum { - UH_UBUS_SG_SID, - UH_UBUS_SG_KEYS, - __UH_UBUS_SG_MAX, -}; - -static const struct blobmsg_policy get_policy[__UH_UBUS_SG_MAX] = { - [UH_UBUS_SG_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING }, - [UH_UBUS_SG_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY }, -}; - - -enum { - UH_UBUS_SA_SID, - UH_UBUS_SA_OBJECTS, - __UH_UBUS_SA_MAX, -}; - -static const struct blobmsg_policy acl_policy[__UH_UBUS_SA_MAX] = { - [UH_UBUS_SA_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING }, - [UH_UBUS_SA_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY }, -}; - - -static bool -uh_ubus_strmatch(const char *str, const char *pat) -{ - while (*pat) - { - if (*pat == '?') - { - if (!*str) - return false; - - str++; - pat++; - } - else if (*pat == '*') - { - if (uh_ubus_strmatch(str, pat+1)) - return true; - - if (*str && uh_ubus_strmatch(str+1, pat)) - return true; - - return false; - } - else if (*str++ != *pat++) - { - return false; - } - } - - return (!*str && !*pat); -} - -static int -uh_ubus_avlcmp(const void *k1, const void *k2, void *ptr) -{ - return strcmp((char *)k1, (char *)k2); -} - -static void -uh_ubus_random(char *dest) -{ - int i; - unsigned char buf[16] = { 0 }; - FILE *f; - - if ((f = fopen("/dev/urandom", "r")) != NULL) - { - fread(buf, 1, sizeof(buf), f); - fclose(f); - } - - for (i = 0; i < sizeof(buf); i++) - sprintf(dest + (i<<1), "%02x", buf[i]); -} - -static void -uh_ubus_session_dump_data(struct uh_ubus_session *ses, struct blob_buf *b) -{ - struct uh_ubus_session_data *d; - - avl_for_each_element(&ses->data, d, avl) - { - blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr), - blobmsg_data(d->attr), blobmsg_data_len(d->attr)); - } -} - -static void -uh_ubus_session_dump_acls(struct uh_ubus_session *ses, struct blob_buf *b) -{ - struct uh_ubus_session_acl *acl; - const char *lastobj = NULL; - void *c = NULL; - - avl_for_each_element(&ses->acls, acl, avl) - { - if (!lastobj || strcmp(acl->object, lastobj)) - { - if (c) blobmsg_close_array(b, c); - c = blobmsg_open_array(b, acl->object); - } - - blobmsg_add_string(b, NULL, acl->function); - lastobj = acl->object; - } - - if (c) blobmsg_close_array(b, c); -} - -static void -uh_ubus_session_dump(struct uh_ubus_session *ses, - struct ubus_context *ctx, - struct ubus_request_data *req) -{ - void *c; - struct blob_buf b; - - memset(&b, 0, sizeof(b)); - blob_buf_init(&b, 0); - - blobmsg_add_string(&b, "sid", ses->id); - blobmsg_add_u32(&b, "timeout", ses->timeout); - blobmsg_add_u32(&b, "touched", ses->touched.tv_sec); - - c = blobmsg_open_table(&b, "acls"); - uh_ubus_session_dump_acls(ses, &b); - blobmsg_close_table(&b, c); - - c = blobmsg_open_table(&b, "data"); - uh_ubus_session_dump_data(ses, &b); - blobmsg_close_table(&b, c); - - ubus_send_reply(ctx, req, b.head); - blob_buf_free(&b); -} - -static struct uh_ubus_session * -uh_ubus_session_create(struct uh_ubus_state *state, int timeout) -{ - struct uh_ubus_session *ses; - - ses = malloc(sizeof(*ses)); - - /* failed to allocate memory... */ - if (!ses) - return NULL; - - memset(ses, 0, sizeof(*ses)); - - uh_ubus_random(ses->id); - - ses->timeout = timeout; - ses->avl.key = ses->id; - - avl_insert(&state->sessions, &ses->avl); - avl_init(&ses->acls, uh_ubus_avlcmp, true, NULL); - avl_init(&ses->data, uh_ubus_avlcmp, false, NULL); - clock_gettime(CLOCK_MONOTONIC, &ses->touched); - - return ses; -} - - -static struct uh_ubus_session * -uh_ubus_session_get(struct uh_ubus_state *state, const char *id) -{ - struct uh_ubus_session *ses; - - ses = avl_find_element(&state->sessions, id, ses, avl); - - if (ses) - clock_gettime(CLOCK_MONOTONIC, &ses->touched); - - return ses; -} - -static void -uh_ubus_session_destroy(struct uh_ubus_state *state, - struct uh_ubus_session *ses) -{ - struct uh_ubus_session_acl *acl, *nacl; - struct uh_ubus_session_data *data, *ndata; - - avl_remove_all_elements(&ses->acls, acl, avl, nacl) - free(acl); - - avl_remove_all_elements(&ses->data, data, avl, ndata) - free(data); - - avl_delete(&state->sessions, &ses->avl); - free(ses); -} - -static void -uh_ubus_session_cleanup(struct uh_ubus_state *state) -{ - struct timespec now; - struct uh_ubus_session *ses, *nses; - - clock_gettime(CLOCK_MONOTONIC, &now); - - avl_for_each_element_safe(&state->sessions, ses, avl, nses) - { - if ((now.tv_sec - ses->touched.tv_sec) >= ses->timeout) - uh_ubus_session_destroy(state, ses); - } -} - - -static int -uh_ubus_handle_create(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct blob_attr *tb[__UH_UBUS_SN_MAX]; - - int timeout = state->timeout; - - blobmsg_parse(new_policy, __UH_UBUS_SN_MAX, tb, blob_data(msg), blob_len(msg)); - - /* TODO: make this a uloop timeout */ - uh_ubus_session_cleanup(state); - - if (tb[UH_UBUS_SN_TIMEOUT]) - timeout = *(uint32_t *)blobmsg_data(tb[UH_UBUS_SN_TIMEOUT]); - - ses = uh_ubus_session_create(state, timeout); - - if (ses) - uh_ubus_session_dump(ses, ctx, req); - - return 0; -} - -static int -uh_ubus_handle_list(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct blob_attr *tb[__UH_UBUS_SI_MAX]; - - blobmsg_parse(sid_policy, __UH_UBUS_SI_MAX, tb, blob_data(msg), blob_len(msg)); - - /* TODO: make this a uloop timeout */ - uh_ubus_session_cleanup(state); - - if (!tb[UH_UBUS_SI_SID]) - { - avl_for_each_element(&state->sessions, ses, avl) - uh_ubus_session_dump(ses, ctx, req); - } - else - { - ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SI_SID])); - - if (!ses) - return UBUS_STATUS_NOT_FOUND; - - uh_ubus_session_dump(ses, ctx, req); - } - - return 0; -} - - -static int -uh_ubus_session_grant(struct uh_ubus_session *ses, struct ubus_context *ctx, - const char *object, const char *function) -{ - struct uh_ubus_session_acl *acl, *nacl; - - acl = avl_find_element(&ses->acls, object, acl, avl); - - if (acl) - { - avl_for_element_to_last(&ses->acls, acl, acl, avl) - { - if (!strcmp(acl->function, function)) - return 1; - } - } - - nacl = malloc(sizeof(*nacl) + strlen(object) + strlen(function) + 2); - - if (nacl) - { - memset(nacl, 0, sizeof(*nacl)); - nacl->function = nacl->object + 1; - nacl->function += sprintf(nacl->object, "%s", object); - sprintf(nacl->function, "%s", function); - - nacl->avl.key = nacl->object; - avl_insert(&ses->acls, &nacl->avl); - } - - return 0; -} - -static int -uh_ubus_session_revoke(struct uh_ubus_session *ses, struct ubus_context *ctx, - const char *object, const char *function) -{ - struct uh_ubus_session_acl *acl, *nacl; - - if (!object && !function) - { - avl_remove_all_elements(&ses->acls, acl, avl, nacl) - free(acl); - } - else - { - avl_for_each_element_safe(&ses->acls, acl, avl, nacl) - { - if (uh_ubus_strmatch(acl->object, object) && - uh_ubus_strmatch(acl->function, function)) - { - avl_delete(&ses->acls, &acl->avl); - free(acl); - } - } - } - - return 0; -} - - -static int -uh_ubus_handle_grant(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct blob_attr *tb[__UH_UBUS_SA_MAX]; - struct blob_attr *attr, *sattr; - const char *object, *function; - int rem1, rem2; - - blobmsg_parse(acl_policy, __UH_UBUS_SA_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[UH_UBUS_SA_SID] || !tb[UH_UBUS_SA_OBJECTS]) - return UBUS_STATUS_INVALID_ARGUMENT; - - ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SA_SID])); - - if (!ses) - return UBUS_STATUS_NOT_FOUND; - - blobmsg_for_each_attr(attr, tb[UH_UBUS_SA_OBJECTS], rem1) - { - if (blob_id(attr) != BLOBMSG_TYPE_ARRAY) - continue; - - object = NULL; - function = NULL; - - blobmsg_for_each_attr(sattr, attr, rem2) - { - if (blob_id(sattr) != BLOBMSG_TYPE_STRING) - continue; - - if (!object) - object = blobmsg_data(sattr); - else if (!function) - function = blobmsg_data(sattr); - else - break; - } - - if (object && function) - uh_ubus_session_grant(ses, ctx, object, function); - } - - return 0; -} - -static int -uh_ubus_handle_revoke(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct blob_attr *tb[__UH_UBUS_SA_MAX]; - struct blob_attr *attr, *sattr; - const char *object, *function; - int rem1, rem2; - - blobmsg_parse(acl_policy, __UH_UBUS_SA_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[UH_UBUS_SA_SID]) - return UBUS_STATUS_INVALID_ARGUMENT; - - ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SA_SID])); - - if (!ses) - return UBUS_STATUS_NOT_FOUND; - - if (!tb[UH_UBUS_SA_OBJECTS]) - { - uh_ubus_session_revoke(ses, ctx, NULL, NULL); - } - else - { - blobmsg_for_each_attr(attr, tb[UH_UBUS_SA_OBJECTS], rem1) - { - if (blob_id(attr) != BLOBMSG_TYPE_ARRAY) - continue; - - object = NULL; - function = NULL; - - blobmsg_for_each_attr(sattr, attr, rem2) - { - if (blob_id(sattr) != BLOBMSG_TYPE_STRING) - continue; - - if (!object) - object = blobmsg_data(sattr); - else if (!function) - function = blobmsg_data(sattr); - else - break; - } - - if (object && function) - uh_ubus_session_revoke(ses, ctx, object, function); - } - } - - return 0; -} - -static int -uh_ubus_handle_set(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct uh_ubus_session_data *data; - struct blob_attr *tb[__UH_UBUS_SA_MAX]; - struct blob_attr *attr; - int rem; - - blobmsg_parse(set_policy, __UH_UBUS_SS_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[UH_UBUS_SS_SID] || !tb[UH_UBUS_SS_VALUES]) - return UBUS_STATUS_INVALID_ARGUMENT; - - ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SS_SID])); - - if (!ses) - return UBUS_STATUS_NOT_FOUND; - - blobmsg_for_each_attr(attr, tb[UH_UBUS_SS_VALUES], rem) - { - if (!blobmsg_name(attr)[0]) - continue; - - data = avl_find_element(&ses->data, blobmsg_name(attr), data, avl); - - if (data) - { - avl_delete(&ses->data, &data->avl); - free(data); - } - - data = malloc(sizeof(*data) + blob_pad_len(attr)); - - if (!data) - break; - - memset(data, 0, sizeof(*data) + blob_pad_len(attr)); - memcpy(data->attr, attr, blob_pad_len(attr)); - - data->avl.key = blobmsg_name(data->attr); - avl_insert(&ses->data, &data->avl); - } - - return 0; -} - -static int -uh_ubus_handle_get(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct uh_ubus_session_data *data; - struct blob_attr *tb[__UH_UBUS_SA_MAX]; - struct blob_attr *attr; - struct blob_buf b; - void *c; - int rem; - - blobmsg_parse(get_policy, __UH_UBUS_SG_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[UH_UBUS_SG_SID]) - return UBUS_STATUS_INVALID_ARGUMENT; - - ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SG_SID])); - - if (!ses) - return UBUS_STATUS_NOT_FOUND; - - memset(&b, 0, sizeof(b)); - blob_buf_init(&b, 0); - c = blobmsg_open_table(&b, "values"); - - if (!tb[UH_UBUS_SG_KEYS]) - { - uh_ubus_session_dump_data(ses, &b); - } - else - { - blobmsg_for_each_attr(attr, tb[UH_UBUS_SG_KEYS], rem) - { - if (blob_id(attr) != BLOBMSG_TYPE_STRING) - continue; - - data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl); - - if (!data) - continue; - - blobmsg_add_field(&b, blobmsg_type(data->attr), - blobmsg_name(data->attr), - blobmsg_data(data->attr), - blobmsg_data_len(data->attr)); - } - } - - blobmsg_close_table(&b, c); - ubus_send_reply(ctx, req, b.head); - blob_buf_free(&b); - - return 0; -} - -static int -uh_ubus_handle_unset(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct uh_ubus_session_data *data, *ndata; - struct blob_attr *tb[__UH_UBUS_SA_MAX]; - struct blob_attr *attr; - int rem; - - blobmsg_parse(get_policy, __UH_UBUS_SG_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[UH_UBUS_SG_SID]) - return UBUS_STATUS_INVALID_ARGUMENT; - - ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SG_SID])); - - if (!ses) - return UBUS_STATUS_NOT_FOUND; - - if (!tb[UH_UBUS_SG_KEYS]) - { - avl_remove_all_elements(&ses->data, data, avl, ndata) - free(data); - } - else - { - blobmsg_for_each_attr(attr, tb[UH_UBUS_SG_KEYS], rem) - { - if (blob_id(attr) != BLOBMSG_TYPE_STRING) - continue; - - data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl); - - if (!data) - continue; - - avl_delete(&ses->data, &data->avl); - free(data); - } - } - - return 0; -} - -static int -uh_ubus_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus); - struct uh_ubus_session *ses; - struct blob_attr *tb[__UH_UBUS_SA_MAX]; - - blobmsg_parse(sid_policy, __UH_UBUS_SI_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[UH_UBUS_SI_SID]) - return UBUS_STATUS_INVALID_ARGUMENT; - - ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SI_SID])); - - if (!ses) - return UBUS_STATUS_NOT_FOUND; - - uh_ubus_session_destroy(state, ses); - - return 0; -} - - -struct uh_ubus_state * -uh_ubus_init(const struct config *conf) -{ - int rv; - struct uh_ubus_state *state; - struct ubus_object *session_object; - - static struct ubus_method session_methods[] = { - UBUS_METHOD("create", uh_ubus_handle_create, new_policy), - UBUS_METHOD("list", uh_ubus_handle_list, sid_policy), - UBUS_METHOD("grant", uh_ubus_handle_grant, acl_policy), - UBUS_METHOD("revoke", uh_ubus_handle_revoke, acl_policy), - UBUS_METHOD("set", uh_ubus_handle_set, set_policy), - UBUS_METHOD("get", uh_ubus_handle_get, get_policy), - UBUS_METHOD("unset", uh_ubus_handle_unset, get_policy), - UBUS_METHOD("destroy", uh_ubus_handle_destroy, sid_policy), - }; - - static struct ubus_object_type session_type = - UBUS_OBJECT_TYPE("uhttpd", session_methods); - - state = malloc(sizeof(*state)); - - if (!state) - { - fprintf(stderr, "Unable to allocate memory for ubus state\n"); - exit(1); - } - - memset(state, 0, sizeof(*state)); - state->ctx = ubus_connect(conf->ubus_socket); - state->timeout = conf->script_timeout; - - if (!state->ctx) - { - fprintf(stderr, "Unable to connect to ubus socket\n"); - exit(1); - } - - ubus_add_uloop(state->ctx); - - session_object = &state->ubus; - session_object->name = "session"; - session_object->type = &session_type; - session_object->methods = session_methods; - session_object->n_methods = ARRAY_SIZE(session_methods); - - rv = ubus_add_object(state->ctx, &state->ubus); - - if (rv) - { - fprintf(stderr, "Unable to publish ubus object: %s\n", - ubus_strerror(rv)); - exit(1); - } - - blob_buf_init(&state->buf, 0); - avl_init(&state->sessions, uh_ubus_avlcmp, false, NULL); - - return state; -} - - -static bool -uh_ubus_request_parse_url(struct client *cl, char **sid, char **obj, char **fun) -{ - char *url = cl->request.url + strlen(cl->server->conf->ubus_prefix); - - for (; url && *url == '/'; *url++ = 0); - *sid = url; - - for (url = url ? strchr(url, '/') : NULL; url && *url == '/'; *url++ = 0); - *obj = url; - - for (url = url ? strchr(url, '/') : NULL; url && *url == '/'; *url++ = 0); - *fun = url; - - for (url = url ? strchr(url, '/') : NULL; url && *url == '/'; *url++ = 0); - return (*sid && *obj && *fun); -} - -static bool -uh_ubus_request_parse_post(struct client *cl, int len, struct blob_buf *b) -{ - int rlen; - bool rv = false; - char buf[UH_LIMIT_MSGHEAD]; - - struct json_object *obj = NULL; - struct json_tokener *tok = NULL; - - if (!len) - return NULL; - - memset(b, 0, sizeof(*b)); - blob_buf_init(b, 0); - - tok = json_tokener_new(); - - while (len > 0) - { - /* remaining data in http head buffer ... */ - if (cl->httpbuf.len > 0) - { - rlen = min(len, cl->httpbuf.len); - - D("ubus: feed %d HTTP buffer bytes\n", rlen); - - memcpy(buf, cl->httpbuf.ptr, rlen); - - cl->httpbuf.len -= rlen; - cl->httpbuf.ptr += rlen; - } - - /* read it from socket ... */ - else - { - ensure_out(rlen = uh_tcp_recv(cl, buf, min(len, sizeof(buf)))); - - if ((rlen < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) - break; - - D("ubus: feed %d/%d TCP socket bytes\n", - rlen, min(len, sizeof(buf))); - } - - obj = json_tokener_parse_ex(tok, buf, rlen); - len -= rlen; - - if (tok->err != json_tokener_continue && !is_error(obj)) - break; - } - -out: - if (!is_error(obj)) - { - if (json_object_get_type(obj) == json_type_object) - { - rv = true; - json_object_object_foreach(obj, key, val) - { - if (!blobmsg_add_json_element(b, key, val)) - { - rv = false; - break; - } - } - } - - json_object_put(obj); - } - - json_tokener_free(tok); - - if (!rv) - blob_buf_free(b); - - return rv; -} - -static void -uh_ubus_request_cb(struct ubus_request *req, int type, struct blob_attr *msg) -{ - int len; - char *str; - struct client *cl = (struct client *)req->priv; - - if (!msg) - { - uh_http_sendhf(cl, 204, "No content", "Function did not return data\n"); - return; - } - - str = blobmsg_format_json_indent(msg, true, 0); - len = strlen(str); - - ensure_out(uh_http_sendf(cl, NULL, "HTTP/1.0 200 OK\r\n")); - ensure_out(uh_http_sendf(cl, NULL, "Content-Type: application/json\r\n")); - ensure_out(uh_http_sendf(cl, NULL, "Content-Length: %i\r\n\r\n", len)); - ensure_out(uh_http_send(cl, NULL, str, len)); - -out: - free(str); -} - -bool -uh_ubus_request(struct client *cl, struct uh_ubus_state *state) -{ - int i, len = 0; - bool access = false; - char *sid, *obj, *fun; - - struct blob_buf buf; - struct uh_ubus_session *ses; - struct uh_ubus_session_acl *acl; - - uint32_t obj_id; - - - memset(&buf, 0, sizeof(buf)); - blob_buf_init(&buf, 0); - - if (!uh_ubus_request_parse_url(cl, &sid, &obj, &fun)) - { - uh_http_sendhf(cl, 400, "Bad Request", "Invalid Request\n"); - goto out; - } - - if (!(ses = uh_ubus_session_get(state, sid))) - { - uh_http_sendhf(cl, 404, "Not Found", "No such session\n"); - goto out; - } - - avl_for_each_element(&ses->acls, acl, avl) - { - if (uh_ubus_strmatch(obj, acl->object) && - uh_ubus_strmatch(fun, acl->function)) - { - access = true; - break; - } - } - - if (!access) - { - uh_http_sendhf(cl, 403, "Denied", "Access to object denied\n"); - goto out; - } - - /* find content length */ - if (cl->request.method == UH_HTTP_MSG_POST) - { - foreach_header(i, cl->request.headers) - { - if (!strcasecmp(cl->request.headers[i], "Content-Length")) - { - len = atoi(cl->request.headers[i+1]); - break; - } - } - } - - if (len > UH_UBUS_MAX_POST_SIZE) - { - uh_http_sendhf(cl, 413, "Too Large", "Message too big\n"); - goto out; - } - - if (len && !uh_ubus_request_parse_post(cl, len, &buf)) - { - uh_http_sendhf(cl, 400, "Bad Request", "Invalid JSON data\n"); - goto out; - } - - if (ubus_lookup_id(state->ctx, obj, &obj_id)) - { - uh_http_sendhf(cl, 500, "Internal Error", "Unable to lookup object\n"); - goto out; - } - - if (ubus_invoke(state->ctx, obj_id, fun, buf.head, - uh_ubus_request_cb, cl, state->timeout * 1000)) - { - uh_http_sendhf(cl, 500, "Internal Error", "Unable to invoke function\n"); - goto out; - } - -out: - blob_buf_free(&buf); - return false; -} - -void -uh_ubus_close(struct uh_ubus_state *state) -{ - if (state->ctx) - ubus_free(state->ctx); - - free(state); -} diff --git a/package/uhttpd/src/uhttpd-ubus.h b/package/uhttpd/src/uhttpd-ubus.h deleted file mode 100644 index 777ce27fd..000000000 --- a/package/uhttpd/src/uhttpd-ubus.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - ubus header - * - * Copyright (C) 2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_UBUS_ - -#include <time.h> - -#include <libubus.h> -#include <libubox/avl.h> -#include <libubox/blobmsg_json.h> -#include <json/json.h> - - -#define UH_UBUS_MAX_POST_SIZE 4096 - - -struct uh_ubus_state { - struct ubus_context *ctx; - struct ubus_object ubus; - struct blob_buf buf; - struct avl_tree sessions; - int timeout; -}; - -struct uh_ubus_request_data { - const char *sid; - const char *object; - const char *function; -}; - -struct uh_ubus_session { - char id[33]; - int timeout; - struct avl_node avl; - struct avl_tree data; - struct avl_tree acls; - struct timespec touched; -}; - -struct uh_ubus_session_data { - struct avl_node avl; - struct blob_attr attr[]; -}; - -struct uh_ubus_session_acl { - struct avl_node avl; - char *function; - char object[]; -}; - -struct uh_ubus_state * uh_ubus_init(const struct config *conf); -bool uh_ubus_request(struct client *cl, struct uh_ubus_state *state); -void uh_ubus_close(struct uh_ubus_state *state); - -#endif diff --git a/package/uhttpd/src/uhttpd-utils.c b/package/uhttpd/src/uhttpd-utils.c deleted file mode 100644 index c8d3bb40f..000000000 --- a/package/uhttpd/src/uhttpd-utils.c +++ /dev/null @@ -1,1081 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Utility functions - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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() */ -#define _BSD_SOURCE /* strcasecmp(), strncasecmp() */ - -#include "uhttpd.h" -#include "uhttpd-utils.h" - -#ifdef HAVE_TLS -#include "uhttpd-tls.h" -#endif - - -static char *uh_index_files[] = { - "index.html", - "index.htm", - "default.html", - "default.htm" -}; - - -const char * sa_straddr(void *sa) -{ - static char str[INET6_ADDRSTRLEN]; - struct sockaddr_in *v4 = (struct sockaddr_in *)sa; - struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)sa; - - if (v4->sin_family == AF_INET) - return inet_ntop(AF_INET, &(v4->sin_addr), str, sizeof(str)); - else - return inet_ntop(AF_INET6, &(v6->sin6_addr), str, sizeof(str)); -} - -const char * sa_strport(void *sa) -{ - static char str[6]; - snprintf(str, sizeof(str), "%i", sa_port(sa)); - return str; -} - -int sa_port(void *sa) -{ - return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); -} - -int sa_rfc1918(void *sa) -{ - struct sockaddr_in *v4 = (struct sockaddr_in *)sa; - unsigned long a = htonl(v4->sin_addr.s_addr); - - if (v4->sin_family == AF_INET) - { - return ((a >= 0x0A000000) && (a <= 0x0AFFFFFF)) || - ((a >= 0xAC100000) && (a <= 0xAC1FFFFF)) || - ((a >= 0xC0A80000) && (a <= 0xC0A8FFFF)); - } - - return 0; -} - -/* Simple strstr() like function that takes len arguments for both haystack and needle. */ -char *strfind(char *haystack, int hslen, const char *needle, int ndlen) -{ - int match = 0; - int i, j; - - for (i = 0; i < hslen; i++) - { - if (haystack[i] == needle[0]) - { - match = ((ndlen == 1) || ((i + ndlen) <= hslen)); - - for (j = 1; (j < ndlen) && ((i + j) < hslen); j++) - { - if (haystack[i+j] != needle[j]) - { - match = 0; - break; - } - } - - if (match) - return &haystack[i]; - } - } - - return NULL; -} - -bool uh_socket_wait(int fd, int sec, bool write) -{ - int rv; - struct timeval timeout; - - fd_set fds; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - timeout.tv_sec = sec; - timeout.tv_usec = 0; - - while (((rv = select(fd+1, write ? NULL : &fds, write ? &fds : NULL, - NULL, &timeout)) < 0) && (errno == EINTR)) - { - D("IO: FD(%d) select interrupted: %s\n", - fd, strerror(errno)); - - continue; - } - - if (rv <= 0) - { - D("IO: FD(%d) appears dead (rv=%d)\n", fd, rv); - return false; - } - - return true; -} - -static int __uh_raw_send(struct client *cl, const char *buf, int len, int sec, - int (*wfn) (struct client *, const char *, int)) -{ - ssize_t rv; - int fd = cl->fd.fd; - - while (true) - { - if ((rv = wfn(cl, buf, len)) < 0) - { - if (errno == EINTR) - { - D("IO: FD(%d) interrupted\n", cl->fd.fd); - continue; - } - else if ((sec > 0) && (errno == EAGAIN || errno == EWOULDBLOCK)) - { - if (!uh_socket_wait(fd, sec, true)) - return -1; - } - else - { - D("IO: FD(%d) write error: %s\n", fd, strerror(errno)); - return -1; - } - } - /* - * It is not entirely clear whether rv = 0 on nonblocking sockets - * is an error. In real world fuzzing tests, not handling it as close - * led to tight infinite loops in this send procedure, so treat it as - * closed and break out. - */ - else if (rv == 0) - { - D("IO: FD(%d) appears closed\n", fd); - return 0; - } - else if (rv < len) - { - D("IO: FD(%d) short write %d/%d bytes\n", fd, rv, len); - len -= rv; - buf += rv; - continue; - } - else - { - D("IO: FD(%d) sent %d/%d bytes\n", fd, rv, len); - return rv; - } - } -} - -int uh_tcp_send_lowlevel(struct client *cl, const char *buf, int len) -{ - return write(cl->fd.fd, buf, len); -} - -int uh_raw_send(int fd, const char *buf, int len, int sec) -{ - struct client_light cl = { .fd = { .fd = fd } }; - return __uh_raw_send((struct client *)&cl, buf, len, sec, - uh_tcp_send_lowlevel); -} - -int uh_tcp_send(struct client *cl, const char *buf, int len) -{ - int seconds = cl->server->conf->network_timeout; -#ifdef HAVE_TLS - if (cl->tls) - return __uh_raw_send(cl, buf, len, seconds, - cl->server->conf->tls_send); -#endif - return __uh_raw_send(cl, buf, len, seconds, uh_tcp_send_lowlevel); -} - -static int __uh_raw_recv(struct client *cl, char *buf, int len, int sec, - int (*rfn) (struct client *, char *, int)) -{ - ssize_t rv; - int fd = cl->fd.fd; - - while (true) - { - if ((rv = rfn(cl, buf, len)) < 0) - { - if (errno == EINTR) - { - continue; - } - else if ((sec > 0) && (errno == EAGAIN || errno == EWOULDBLOCK)) - { - if (!uh_socket_wait(fd, sec, false)) - return -1; - } - else - { - D("IO: FD(%d) read error: %s\n", fd, strerror(errno)); - return -1; - } - } - else if (rv == 0) - { - D("IO: FD(%d) appears closed\n", fd); - return 0; - } - else - { - D("IO: FD(%d) read %d bytes\n", fd, rv); - return rv; - } - } -} - -int uh_tcp_recv_lowlevel(struct client *cl, char *buf, int len) -{ - return read(cl->fd.fd, buf, len); -} - -int uh_raw_recv(int fd, char *buf, int len, int sec) -{ - struct client_light cl = { .fd = { .fd = fd } }; - return __uh_raw_recv((struct client *)&cl, buf, len, sec, - uh_tcp_recv_lowlevel); -} - -int uh_tcp_recv(struct client *cl, char *buf, int len) -{ - int seconds = cl->server->conf->network_timeout; -#ifdef HAVE_TLS - if (cl->tls) - return __uh_raw_recv(cl, buf, len, seconds, - cl->server->conf->tls_recv); -#endif - return __uh_raw_recv(cl, buf, len, seconds, uh_tcp_recv_lowlevel); -} - - -int uh_http_sendhf(struct client *cl, int code, const char *summary, - const char *fmt, ...) -{ - va_list ap; - - char buffer[UH_LIMIT_MSGHEAD]; - int len; - - len = snprintf(buffer, sizeof(buffer), - "HTTP/1.1 %03i %s\r\n" - "Connection: close\r\n" - "Content-Type: text/plain\r\n" - "Transfer-Encoding: chunked\r\n\r\n", - code, summary - ); - - ensure_ret(uh_tcp_send(cl, buffer, len)); - - va_start(ap, fmt); - len = vsnprintf(buffer, sizeof(buffer), fmt, ap); - va_end(ap); - - ensure_ret(uh_http_sendc(cl, buffer, len)); - ensure_ret(uh_http_sendc(cl, NULL, 0)); - - return 0; -} - - -int uh_http_sendc(struct client *cl, const char *data, int len) -{ - char chunk[8]; - int clen; - - if (len == -1) - len = strlen(data); - - if (len > 0) - { - clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len); - ensure_ret(uh_tcp_send(cl, chunk, clen)); - ensure_ret(uh_tcp_send(cl, data, len)); - ensure_ret(uh_tcp_send(cl, "\r\n", 2)); - } - else - { - ensure_ret(uh_tcp_send(cl, "0\r\n\r\n", 5)); - } - - return 0; -} - -int uh_http_sendf(struct client *cl, struct http_request *req, - const char *fmt, ...) -{ - va_list ap; - char buffer[UH_LIMIT_MSGHEAD]; - int len; - - va_start(ap, fmt); - len = vsnprintf(buffer, sizeof(buffer), fmt, ap); - va_end(ap); - - if ((req != NULL) && (req->version > UH_HTTP_VER_1_0)) - ensure_ret(uh_http_sendc(cl, buffer, len)); - else if (len > 0) - ensure_ret(uh_tcp_send(cl, buffer, len)); - - return 0; -} - -int uh_http_send(struct client *cl, struct http_request *req, - const char *buf, int len) -{ - if (len < 0) - len = strlen(buf); - - if ((req != NULL) && (req->version > UH_HTTP_VER_1_0)) - ensure_ret(uh_http_sendc(cl, buf, len)); - else if (len > 0) - ensure_ret(uh_tcp_send(cl, buf, len)); - - return 0; -} - - -/* blen is the size of buf; slen is the length of src. The input-string need -** not be, and the output string will not be, null-terminated. Returns the -** length of the decoded string, -1 on buffer overflow, -2 on malformed string. */ -int uh_urldecode(char *buf, int blen, const char *src, int slen) -{ - int i; - int len = 0; - -#define hex(x) \ - (((x) <= '9') ? ((x) - '0') : \ - (((x) <= 'F') ? ((x) - 'A' + 10) : \ - ((x) - 'a' + 10))) - - for (i = 0; (i < slen) && (len < blen); i++) - { - if (src[i] == '%') - { - if (((i+2) < slen) && isxdigit(src[i+1]) && isxdigit(src[i+2])) - { - buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2])); - i += 2; - } - else - { - /* Encoding error: it's hard to think of a - ** scenario in which returning an incorrect - ** 'decoding' of the malformed string is - ** preferable to signaling an error condition. */ - #if 0 /* WORSE_IS_BETTER */ - buf[len++] = '%'; - #else - return -2; - #endif - } - } - else - { - buf[len++] = src[i]; - } - } - - return (i == slen) ? len : -1; -} - -/* blen is the size of buf; slen is the length of src. The input-string need -** not be, and the output string will not be, null-terminated. Returns the -** length of the encoded string, or -1 on error (buffer overflow) */ -int uh_urlencode(char *buf, int blen, const char *src, int slen) -{ - int i; - int len = 0; - const char hex[] = "0123456789abcdef"; - - for (i = 0; (i < slen) && (len < blen); i++) - { - if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') || - (src[i] == '.') || (src[i] == '~') ) - { - buf[len++] = src[i]; - } - else if ((len+3) <= blen) - { - buf[len++] = '%'; - buf[len++] = hex[(src[i] >> 4) & 15]; - buf[len++] = hex[ src[i] & 15]; - } - else - { - len = -1; - break; - } - } - - return (i == slen) ? len : -1; -} - -int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen) -{ - int i = 0; - int len = 0; - - unsigned int cin = 0; - unsigned int cout = 0; - - - for (i = 0; (i <= slen) && (src[i] != 0); i++) - { - cin = src[i]; - - if ((cin >= '0') && (cin <= '9')) - cin = cin - '0' + 52; - else if ((cin >= 'A') && (cin <= 'Z')) - cin = cin - 'A'; - else if ((cin >= 'a') && (cin <= 'z')) - cin = cin - 'a' + 26; - else if (cin == '+') - cin = 62; - else if (cin == '/') - cin = 63; - else if (cin == '=') - cin = 0; - else - continue; - - cout = (cout << 6) | cin; - - if ((i % 4) == 3) - { - if ((len + 3) < blen) - { - buf[len++] = (char)(cout >> 16); - buf[len++] = (char)(cout >> 8); - buf[len++] = (char)(cout); - } - else - { - break; - } - } - } - - buf[len++] = 0; - return len; -} - -static char * canonpath(const char *path, char *path_resolved) -{ - char path_copy[PATH_MAX]; - char *path_cpy = path_copy; - char *path_res = path_resolved; - - struct stat s; - - - /* relative -> absolute */ - if (*path != '/') - { - getcwd(path_copy, PATH_MAX); - strncat(path_copy, "/", PATH_MAX - strlen(path_copy)); - strncat(path_copy, path, PATH_MAX - strlen(path_copy)); - } - else - { - strncpy(path_copy, path, PATH_MAX); - } - - /* normalize */ - while ((*path_cpy != '\0') && (path_cpy < (path_copy + PATH_MAX - 2))) - { - if (*path_cpy == '/') - { - /* skip repeating / */ - if (path_cpy[1] == '/') - { - path_cpy++; - continue; - } - - /* /./ or /../ */ - else if (path_cpy[1] == '.') - { - /* skip /./ */ - if ((path_cpy[2] == '/') || (path_cpy[2] == '\0')) - { - path_cpy += 2; - continue; - } - - /* collapse /x/../ */ - else if ((path_cpy[2] == '.') && - ((path_cpy[3] == '/') || (path_cpy[3] == '\0'))) - { - while ((path_res > path_resolved) && (*--path_res != '/')) - ; - - path_cpy += 3; - continue; - } - } - } - - *path_res++ = *path_cpy++; - } - - /* remove trailing slash if not root / */ - if ((path_res > (path_resolved+1)) && (path_res[-1] == '/')) - path_res--; - else if (path_res == path_resolved) - *path_res++ = '/'; - - *path_res = '\0'; - - /* test access */ - if (!stat(path_resolved, &s) && (s.st_mode & S_IROTH)) - return path_resolved; - - return NULL; -} - -/* Returns NULL on error. -** NB: improperly encoded URL should give client 400 [Bad Syntax]; returning -** NULL here causes 404 [Not Found], but that's not too unreasonable. */ -struct path_info * uh_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; - - char buffer[UH_LIMIT_MSGHEAD]; - char *docroot = cl->server->conf->docroot; - char *pathptr = NULL; - - int slash = 0; - int no_sym = cl->server->conf->no_symlinks; - int i = 0; - struct stat s; - - /* back out early if url is undefined */ - if (url == NULL) - return NULL; - - memset(path_phys, 0, sizeof(path_phys)); - memset(path_info, 0, sizeof(path_info)); - memset(buffer, 0, sizeof(buffer)); - memset(&p, 0, sizeof(p)); - - /* copy docroot */ - memcpy(buffer, docroot, - min(strlen(docroot), sizeof(buffer) - 1)); - - /* separate query string from url */ - if ((pathptr = strchr(url, '?')) != NULL) - { - p.query = pathptr[1] ? pathptr + 1 : NULL; - - /* urldecode component w/o query */ - if (pathptr > url) - { - if (uh_urldecode(&buffer[strlen(docroot)], - sizeof(buffer) - strlen(docroot) - 1, - url, pathptr - url ) < 0) - { - return NULL; /* bad URL */ - } - } - } - - /* no query string, decode all of url */ - else - { - if (uh_urldecode(&buffer[strlen(docroot)], - sizeof(buffer) - strlen(docroot) - 1, - url, strlen(url) ) < 0) - { - return NULL; /* bad URL */ - } - } - - /* create canon path */ - for (i = strlen(buffer), slash = (buffer[max(0, i-1)] == '/'); i >= 0; i--) - { - if ((buffer[i] == 0) || (buffer[i] == '/')) - { - memset(path_info, 0, sizeof(path_info)); - memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1)); - - if (no_sym ? realpath(path_info, path_phys) - : canonpath(path_info, path_phys)) - { - memset(path_info, 0, sizeof(path_info)); - memcpy(path_info, &buffer[i], - min(strlen(buffer) - i, sizeof(path_info) - 1)); - - break; - } - } - } - - /* check whether found path is within docroot */ - if (strncmp(path_phys, docroot, strlen(docroot)) || - ((path_phys[strlen(docroot)] != 0) && - (path_phys[strlen(docroot)] != '/'))) - { - return NULL; - } - - /* test current path */ - if (!stat(path_phys, &p.stat)) - { - /* is a regular file */ - if (p.stat.st_mode & S_IFREG) - { - p.root = docroot; - p.phys = path_phys; - p.name = &path_phys[strlen(docroot)]; - p.info = path_info[0] ? path_info : NULL; - } - - /* is a directory */ - else if ((p.stat.st_mode & S_IFDIR) && !strlen(path_info)) - { - /* ensure trailing slash */ - if (path_phys[strlen(path_phys)-1] != '/') - path_phys[strlen(path_phys)] = '/'; - - /* try to locate index file */ - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, path_phys, sizeof(buffer)); - pathptr = &buffer[strlen(buffer)]; - - /* 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) - { - uh_http_sendf(cl, NULL, - "HTTP/1.1 302 Found\r\n" - "Location: %s%s%s\r\n" - "Connection: close\r\n\r\n", - &path_phys[strlen(docroot)], - p.query ? "?" : "", - p.query ? p.query : "" - ); - - p.redirected = 1; - } - else if (cl->server->conf->index_file) - { - strncat(buffer, cl->server->conf->index_file, sizeof(buffer)); - - if (!stat(buffer, &s) && (s.st_mode & S_IFREG)) - { - memcpy(path_phys, buffer, sizeof(path_phys)); - memcpy(&p.stat, &s, sizeof(p.stat)); - } - } - else - { - for (i = 0; i < array_size(uh_index_files); i++) - { - strncat(buffer, uh_index_files[i], sizeof(buffer)); - - if (!stat(buffer, &s) && (s.st_mode & S_IFREG)) - { - memcpy(path_phys, buffer, sizeof(path_phys)); - memcpy(&p.stat, &s, sizeof(p.stat)); - break; - } - - *pathptr = 0; - } - } - - p.root = docroot; - p.phys = path_phys; - p.name = &path_phys[strlen(docroot)]; - } - } - - return p.phys ? &p : NULL; -} - - -static struct auth_realm *uh_realms = NULL; - -struct auth_realm * uh_auth_add(char *path, char *user, char *pass) -{ - struct auth_realm *new = NULL; - struct passwd *pwd; - -#ifdef HAVE_SHADOW - struct spwd *spwd; -#endif - - if((new = (struct auth_realm *)malloc(sizeof(struct auth_realm))) != NULL) - { - memset(new, 0, sizeof(struct auth_realm)); - - memcpy(new->path, path, - min(strlen(path), sizeof(new->path) - 1)); - - memcpy(new->user, user, - min(strlen(user), sizeof(new->user) - 1)); - - /* given password refers to a passwd entry */ - if ((strlen(pass) > 3) && !strncmp(pass, "$p$", 3)) - { -#ifdef HAVE_SHADOW - /* try to resolve shadow entry */ - if (((spwd = getspnam(&pass[3])) != NULL) && spwd->sp_pwdp) - { - memcpy(new->pass, spwd->sp_pwdp, - min(strlen(spwd->sp_pwdp), sizeof(new->pass) - 1)); - } - - else -#endif - - /* try to resolve passwd entry */ - if (((pwd = getpwnam(&pass[3])) != NULL) && pwd->pw_passwd && - (pwd->pw_passwd[0] != '!') && (pwd->pw_passwd[0] != 0)) - { - memcpy(new->pass, pwd->pw_passwd, - min(strlen(pwd->pw_passwd), sizeof(new->pass) - 1)); - } - } - - /* ordinary pwd */ - else - { - memcpy(new->pass, pass, - min(strlen(pass), sizeof(new->pass) - 1)); - } - - if (new->pass[0]) - { - new->next = uh_realms; - uh_realms = new; - - return new; - } - - free(new); - } - - return NULL; -} - -int uh_auth_check(struct client *cl, struct http_request *req, - struct path_info *pi) -{ - int i, plen, rlen, protected; - char buffer[UH_LIMIT_MSGHEAD]; - char *user = NULL; - char *pass = NULL; - - struct auth_realm *realm = NULL; - - plen = strlen(pi->name); - protected = 0; - - /* check whether at least one realm covers the requested url */ - for (realm = uh_realms; realm; realm = realm->next) - { - rlen = strlen(realm->path); - - if ((plen >= rlen) && !strncasecmp(pi->name, realm->path, rlen)) - { - req->realm = realm; - protected = 1; - break; - } - } - - /* requested resource is covered by a realm */ - if (protected) - { - /* try to get client auth info */ - foreach_header(i, req->headers) - { - if (!strcasecmp(req->headers[i], "Authorization") && - (strlen(req->headers[i+1]) > 6) && - !strncasecmp(req->headers[i+1], "Basic ", 6)) - { - memset(buffer, 0, sizeof(buffer)); - uh_b64decode(buffer, sizeof(buffer) - 1, - (unsigned char *) &req->headers[i+1][6], - strlen(req->headers[i+1]) - 6); - - if ((pass = strchr(buffer, ':')) != NULL) - { - user = buffer; - *pass++ = 0; - } - - break; - } - } - - /* have client auth */ - if (user && pass) - { - /* find matching realm */ - for (realm = uh_realms; realm; realm = realm->next) - { - rlen = strlen(realm->path); - - if ((plen >= rlen) && - !strncasecmp(pi->name, realm->path, rlen) && - !strcmp(user, realm->user)) - { - req->realm = realm; - break; - } - } - - /* found a realm matching the username */ - if (realm) - { - /* check user pass */ - if (!strcmp(pass, realm->pass) || - !strcmp(crypt(pass, realm->pass), realm->pass)) - return 1; - } - } - - /* 401 */ - uh_http_sendf(cl, NULL, - "%s 401 Authorization Required\r\n" - "WWW-Authenticate: Basic realm=\"%s\"\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: 23\r\n\r\n" - "Authorization Required\n", - http_versions[req->version], - cl->server->conf->realm); - - return 0; - } - - return 1; -} - - -static struct listener *uh_listeners = NULL; -static struct client *uh_clients = NULL; - -struct listener * uh_listener_add(int sock, struct config *conf) -{ - struct listener *new = NULL; - socklen_t sl; - - if ((new = (struct listener *)malloc(sizeof(struct listener))) != NULL) - { - memset(new, 0, sizeof(struct listener)); - - new->fd.fd = sock; - new->conf = conf; - - - /* get local endpoint addr */ - sl = sizeof(struct sockaddr_in6); - memset(&(new->addr), 0, sl); - getsockname(sock, (struct sockaddr *) &(new->addr), &sl); - - new->next = uh_listeners; - uh_listeners = new; - - return new; - } - - return NULL; -} - -struct listener * uh_listener_lookup(int sock) -{ - struct listener *cur = NULL; - - for (cur = uh_listeners; cur; cur = cur->next) - if (cur->fd.fd == sock) - return cur; - - return NULL; -} - - -struct client * uh_client_add(int sock, struct listener *serv, - struct sockaddr_in6 *peer) -{ - struct client *new = NULL; - socklen_t sl; - - if ((new = (struct client *)malloc(sizeof(struct client))) != NULL) - { - memset(new, 0, sizeof(struct client)); - memcpy(&new->peeraddr, peer, sizeof(new->peeraddr)); - - new->fd.fd = sock; - new->server = serv; - - new->rpipe.fd = -1; - new->wpipe.fd = -1; - - /* get local endpoint addr */ - sl = sizeof(struct sockaddr_in6); - getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl); - - new->next = uh_clients; - uh_clients = new; - - serv->n_clients++; - - D("IO: Client(%d) allocated\n", new->fd.fd); - } - - return new; -} - -struct client * uh_client_lookup(int sock) -{ - struct client *cur = NULL; - - for (cur = uh_clients; cur; cur = cur->next) - if (cur->fd.fd == sock) - return cur; - - return NULL; -} - -void uh_client_shutdown(struct client *cl) -{ -#ifdef HAVE_TLS - /* free client tls context */ - if (cl->server && cl->server->conf->tls) - cl->server->conf->tls_close(cl); -#endif - - /* remove from global client list */ - uh_client_remove(cl); -} - -void uh_client_remove(struct client *cl) -{ - struct client *cur = NULL; - struct client *prv = NULL; - - for (cur = uh_clients; cur; prv = cur, cur = cur->next) - { - if (cur == cl) - { - if (prv) - prv->next = cur->next; - else - uh_clients = cur->next; - - if (cur->timeout.pending) - uloop_timeout_cancel(&cur->timeout); - - if (cur->proc.pid) - uloop_process_delete(&cur->proc); - - D("IO: Client(%d) freeing\n", cur->fd.fd); - - uh_ufd_remove(&cur->rpipe); - uh_ufd_remove(&cur->wpipe); - uh_ufd_remove(&cur->fd); - - cur->server->n_clients--; - - free(cur); - break; - } - } -} - - -void uh_ufd_add(struct uloop_fd *u, uloop_fd_handler h, unsigned int ev) -{ - if (h != NULL) - { - u->cb = h; - uloop_fd_add(u, ev); - D("IO: FD(%d) added to uloop\n", u->fd); - } -} - -void uh_ufd_remove(struct uloop_fd *u) -{ - if (u->cb != NULL) - { - uloop_fd_delete(u); - D("IO: FD(%d) removed from uloop\n", u->fd); - u->cb = NULL; - } - - if (u->fd > -1) - { - close(u->fd); - D("IO: FD(%d) closed\n", u->fd); - u->fd = -1; - } -} - - -#ifdef HAVE_CGI -static struct interpreter *uh_interpreters = NULL; - -struct interpreter * uh_interpreter_add(const char *extn, const char *path) -{ - struct interpreter *new = NULL; - - if ((new = (struct interpreter *)malloc(sizeof(struct interpreter))) != NULL) - { - memset(new, 0, sizeof(struct interpreter)); - - memcpy(new->extn, extn, min(strlen(extn), sizeof(new->extn)-1)); - memcpy(new->path, path, min(strlen(path), sizeof(new->path)-1)); - - new->next = uh_interpreters; - uh_interpreters = new; - - return new; - } - - return NULL; -} - -struct interpreter * uh_interpreter_lookup(const char *path) -{ - struct interpreter *cur = NULL; - const char *e; - - for (cur = uh_interpreters; cur; cur = cur->next) - { - e = &path[max(strlen(path) - strlen(cur->extn), 0)]; - - if (!strcmp(e, cur->extn)) - return cur; - } - - return NULL; -} -#endif diff --git a/package/uhttpd/src/uhttpd-utils.h b/package/uhttpd/src/uhttpd-utils.h deleted file mode 100644 index 9de919194..000000000 --- a/package/uhttpd/src/uhttpd-utils.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Utility header - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_UTILS_ - -#include <stdarg.h> -#include <fcntl.h> -#include <pwd.h> -#include <sys/stat.h> - -#include <libubox/uloop.h> - - -#ifdef HAVE_SHADOW -#include <shadow.h> -#endif - -#define min(x, y) (((x) < (y)) ? (x) : (y)) -#define max(x, y) (((x) > (y)) ? (x) : (y)) - -#define array_size(x) \ - (sizeof(x) / sizeof(x[0])) - -#define foreach_header(i, h) \ - for( i = 0; (i + 1) < (sizeof(h) / sizeof(h[0])) && h[i]; i += 2 ) - -#define fd_cloexec(fd) \ - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) - -#define fd_nonblock(fd) \ - fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) - -#define ensure_out(x) \ - do { if((x) < 0) goto out; } while(0) - -#define ensure_ret(x) \ - do { if((x) < 0) return -1; } while(0) - - -struct path_info { - char *root; - char *phys; - char *name; - char *info; - char *query; - int redirected; - struct stat stat; -}; - - -const char * sa_straddr(void *sa); -const char * sa_strport(void *sa); -int sa_port(void *sa); -int sa_rfc1918(void *sa); - -char *strfind(char *haystack, int hslen, const char *needle, int ndlen); - -bool uh_socket_wait(int fd, int sec, bool write); - -int uh_raw_send(int fd, const char *buf, int len, int seconds); -int uh_raw_recv(int fd, char *buf, int len, int seconds); -int uh_tcp_send(struct client *cl, const char *buf, int len); -int uh_tcp_send_lowlevel(struct client *cl, const char *buf, int len); -int uh_tcp_recv(struct client *cl, char *buf, int len); -int uh_tcp_recv_lowlevel(struct client *cl, char *buf, int len); - -int uh_http_sendhf(struct client *cl, int code, const char *summary, - const char *fmt, ...); - -#define uh_http_response(cl, code, message) \ - uh_http_sendhf(cl, code, message, message) - -int uh_http_sendc(struct client *cl, const char *data, int len); - -int uh_http_sendf( - struct client *cl, struct http_request *req, - const char *fmt, ... -); - -int uh_http_send( - struct client *cl, struct http_request *req, - const char *buf, int len -); - - -int uh_urldecode(char *buf, int blen, const char *src, int slen); -int uh_urlencode(char *buf, int blen, const char *src, int slen); -int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen); - - -struct auth_realm * uh_auth_add(char *path, char *user, char *pass); - -int uh_auth_check( - struct client *cl, struct http_request *req, struct path_info *pi -); - - -struct path_info * uh_path_lookup(struct client *cl, const char *url); - -struct listener * uh_listener_add(int sock, struct config *conf); -struct listener * uh_listener_lookup(int sock); - -struct client * uh_client_add(int sock, struct listener *serv, - struct sockaddr_in6 *peer); - -struct client * uh_client_lookup(int sock); - -#define uh_client_error(cl, code, status, ...) do { \ - uh_http_sendhf(cl, code, status, __VA_ARGS__); \ - uh_client_shutdown(cl); \ -} while(0) - -void uh_client_shutdown(struct client *cl); -void uh_client_remove(struct client *cl); - -void uh_ufd_add(struct uloop_fd *u, uloop_fd_handler h, unsigned int ev); -void uh_ufd_remove(struct uloop_fd *u); - - -#ifdef HAVE_CGI -struct interpreter * uh_interpreter_add(const char *extn, const char *path); -struct interpreter * uh_interpreter_lookup(const char *path); -#endif - -#endif diff --git a/package/uhttpd/src/uhttpd.c b/package/uhttpd/src/uhttpd.c deleted file mode 100644 index 1efcbf0f5..000000000 --- a/package/uhttpd/src/uhttpd.c +++ /dev/null @@ -1,1288 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Main component - * - * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org> - * - * 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" - -#ifdef HAVE_CGI -#include "uhttpd-cgi.h" -#endif - -#ifdef HAVE_LUA -#include "uhttpd-lua.h" -#endif - -#ifdef HAVE_TLS -#include "uhttpd-tls.h" -#endif - - -const char * http_methods[] = { "GET", "POST", "HEAD", }; -const char * http_versions[] = { "HTTP/0.9", "HTTP/1.0", "HTTP/1.1", }; - -static int run = 1; - -static void uh_sigterm(int sig) -{ - run = 0; -} - -static void uh_config_parse(struct config *conf) -{ - FILE *c; - char line[512]; - char *col1 = NULL; - char *col2 = NULL; - char *eol = NULL; - - const char *path = conf->file ? conf->file : "/etc/httpd.conf"; - - - if ((c = fopen(path, "r")) != NULL) - { - memset(line, 0, sizeof(line)); - - while (fgets(line, sizeof(line) - 1, c)) - { - if ((line[0] == '/') && (strchr(line, ':') != NULL)) - { - if (!(col1 = strchr(line, ':')) || (*col1++ = 0) || - !(col2 = strchr(col1, ':')) || (*col2++ = 0) || - !(eol = strchr(col2, '\n')) || (*eol++ = 0)) - { - continue; - } - - if (!uh_auth_add(line, col1, col2)) - { - fprintf(stderr, - "Notice: No password set for user %s, ignoring " - "authentication on %s\n", col1, line - ); - } - } - else if (!strncmp(line, "I:", 2)) - { - if (!(col1 = strchr(line, ':')) || (*col1++ = 0) || - !(eol = strchr(col1, '\n')) || (*eol++ = 0)) - { - continue; - } - - conf->index_file = strdup(col1); - } - else if (!strncmp(line, "E404:", 5)) - { - if (!(col1 = strchr(line, ':')) || (*col1++ = 0) || - !(eol = strchr(col1, '\n')) || (*eol++ = 0)) - { - continue; - } - - conf->error_handler = strdup(col1); - } -#ifdef HAVE_CGI - else if ((line[0] == '*') && (strchr(line, ':') != NULL)) - { - if (!(col1 = strchr(line, '*')) || (*col1++ = 0) || - !(col2 = strchr(col1, ':')) || (*col2++ = 0) || - !(eol = strchr(col2, '\n')) || (*eol++ = 0)) - { - continue; - } - - if (!uh_interpreter_add(col1, col2)) - { - fprintf(stderr, - "Unable to add interpreter %s for extension %s: " - "Out of memory\n", col2, col1 - ); - } - } -#endif - } - - fclose(c); - } -} - -static void uh_listener_cb(struct uloop_fd *u, unsigned int events); - -static int uh_socket_bind(const char *host, const char *port, - struct addrinfo *hints, int do_tls, - struct config *conf) -{ - int sock = -1; - int yes = 1; - int status; - int bound = 0; - - int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt; - - struct listener *l = NULL; - struct addrinfo *addrs = NULL, *p = NULL; - - if ((status = getaddrinfo(host, port, hints, &addrs)) != 0) - { - fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status)); - } - - /* try to bind a new socket to each found address */ - for (p = addrs; p; p = p->ai_next) - { - /* get the socket */ - if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) - { - perror("socket()"); - goto error; - } - - /* "address already in use" */ - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) - { - perror("setsockopt()"); - goto error; - } - - /* TCP keep-alive */ - if (conf->tcp_keepalive > 0) - { - tcp_ka_idl = 1; - tcp_ka_cnt = 3; - tcp_ka_int = conf->tcp_keepalive; - - if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) || - setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &tcp_ka_idl, sizeof(tcp_ka_idl)) || - setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int)) || - setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &tcp_ka_cnt, sizeof(tcp_ka_cnt))) - { - fprintf(stderr, "Notice: Unable to enable TCP keep-alive: %s\n", - strerror(errno)); - } - } - - /* required to get parallel v4 + v6 working */ - if (p->ai_family == AF_INET6) - { - if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1) - { - perror("setsockopt()"); - goto error; - } - } - - /* bind */ - if (bind(sock, p->ai_addr, p->ai_addrlen) == -1) - { - perror("bind()"); - goto error; - } - - /* listen */ - if (listen(sock, UH_LIMIT_CLIENTS) == -1) - { - perror("listen()"); - goto error; - } - - /* add listener to global list */ - if (!(l = uh_listener_add(sock, conf))) - { - fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n"); - goto error; - } - -#ifdef HAVE_TLS - /* init TLS */ - l->tls = do_tls ? conf->tls : NULL; -#endif - - /* add socket to uloop */ - fd_cloexec(sock); - uh_ufd_add(&l->fd, uh_listener_cb, ULOOP_READ); - - bound++; - continue; - - error: - if (sock > 0) - close(sock); - } - - freeaddrinfo(addrs); - - return bound; -} - -static struct http_request * uh_http_header_parse(struct client *cl, - char *buffer, int buflen) -{ - char *method = buffer; - char *path = NULL; - char *version = NULL; - - char *headers = NULL; - char *hdrname = NULL; - char *hdrdata = NULL; - - int i; - int hdrcount = 0; - - struct http_request *req = &cl->request; - - - /* terminate initial header line */ - if ((headers = strfind(buffer, buflen, "\r\n", 2)) != NULL) - { - buffer[buflen-1] = 0; - - *headers++ = 0; - *headers++ = 0; - - /* find request path */ - if ((path = strchr(buffer, ' ')) != NULL) - *path++ = 0; - - /* find http version */ - if ((path != NULL) && ((version = strchr(path, ' ')) != NULL)) - *version++ = 0; - - - /* check method */ - if (method && !strcmp(method, "GET")) - req->method = UH_HTTP_MSG_GET; - else if (method && !strcmp(method, "POST")) - req->method = UH_HTTP_MSG_POST; - else if (method && !strcmp(method, "HEAD")) - req->method = UH_HTTP_MSG_HEAD; - else - { - /* invalid method */ - uh_http_response(cl, 405, "Method Not Allowed"); - return NULL; - } - - /* check path */ - if (!path || !strlen(path)) - { - /* malformed request */ - uh_http_response(cl, 400, "Bad Request"); - return NULL; - } - else - { - req->url = path; - } - - /* check version */ - if (version && !strcmp(version, "HTTP/0.9")) - req->version = UH_HTTP_VER_0_9; - else if (version && !strcmp(version, "HTTP/1.0")) - req->version = UH_HTTP_VER_1_0; - else if (version && !strcmp(version, "HTTP/1.1")) - req->version = UH_HTTP_VER_1_1; - else - { - /* unsupported version */ - uh_http_response(cl, 400, "Bad Request"); - return NULL; - } - - D("SRV: %s %s %s\n", - http_methods[req->method], req->url, http_versions[req->version]); - - /* process header fields */ - for (i = (int)(headers - buffer); i < buflen; i++) - { - /* found eol and have name + value, push out header tuple */ - if (hdrname && hdrdata && (buffer[i] == '\r' || buffer[i] == '\n')) - { - buffer[i] = 0; - - /* store */ - if ((hdrcount + 1) < array_size(req->headers)) - { - D("SRV: HTTP: %s: %s\n", hdrname, hdrdata); - - req->headers[hdrcount++] = hdrname; - req->headers[hdrcount++] = hdrdata; - - hdrname = hdrdata = NULL; - } - - /* too large */ - else - { - D("SRV: HTTP: header too big (too many headers)\n"); - uh_http_response(cl, 413, "Request Entity Too Large"); - return NULL; - } - } - - /* have name but no value and found a colon, start of value */ - else if (hdrname && !hdrdata && - ((i+1) < buflen) && (buffer[i] == ':')) - { - buffer[i] = 0; - hdrdata = &buffer[i+1]; - - while ((hdrdata + 1) < (buffer + buflen) && *hdrdata == ' ') - hdrdata++; - } - - /* have no name and found [A-Za-z], start of name */ - else if (!hdrname && isalpha(buffer[i])) - { - hdrname = &buffer[i]; - } - } - - /* valid enough */ - req->redirect_status = 200; - return req; - } - - /* Malformed request */ - uh_http_response(cl, 400, "Bad Request"); - return NULL; -} - - -static struct http_request * uh_http_header_recv(struct client *cl) -{ - char *bufptr = cl->httpbuf.buf; - char *idxptr = NULL; - - ssize_t blen = sizeof(cl->httpbuf.buf)-1; - ssize_t rlen = 0; - - memset(bufptr, 0, sizeof(cl->httpbuf.buf)); - - while (blen > 0) - { - /* receive data */ - ensure_out(rlen = uh_tcp_recv(cl, bufptr, blen)); - D("SRV: Client(%d) peek(%d) = %d\n", cl->fd.fd, blen, rlen); - - if (rlen <= 0) - { - D("SRV: Client(%d) dead [%s]\n", cl->fd.fd, strerror(errno)); - return NULL; - } - - blen -= rlen; - bufptr += rlen; - - if ((idxptr = strfind(cl->httpbuf.buf, sizeof(cl->httpbuf.buf), - "\r\n\r\n", 4))) - { - /* header read complete ... */ - cl->httpbuf.ptr = idxptr + 4; - cl->httpbuf.len = bufptr - cl->httpbuf.ptr; - - return uh_http_header_parse(cl, cl->httpbuf.buf, - (cl->httpbuf.ptr - cl->httpbuf.buf)); - } - } - - /* request entity too large */ - D("SRV: HTTP: header too big (buffer exceeded)\n"); - uh_http_response(cl, 413, "Request Entity Too Large"); - -out: - return NULL; -} - -#if defined(HAVE_LUA) || defined(HAVE_CGI) -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; -} -#endif - -static bool uh_dispatch_request(struct client *cl, struct http_request *req) -{ - struct path_info *pin; - struct interpreter *ipr = NULL; - struct config *conf = cl->server->conf; - -#ifdef HAVE_LUA - /* Lua request? */ - if (conf->lua_state && - uh_path_match(conf->lua_prefix, req->url)) - { - return conf->lua_request(cl, conf->lua_state); - } - else -#endif - -#ifdef HAVE_UBUS - /* ubus request? */ - if (conf->ubus_state && - uh_path_match(conf->ubus_prefix, req->url)) - { - return conf->ubus_request(cl, conf->ubus_state); - } - else -#endif - - /* dispatch request */ - if ((pin = uh_path_lookup(cl, req->url)) != NULL) - { - /* auth ok? */ - if (!pin->redirected && uh_auth_check(cl, req, pin)) - { -#ifdef HAVE_CGI - if (uh_path_match(conf->cgi_prefix, pin->name) || - (ipr = uh_interpreter_lookup(pin->phys)) != NULL) - { - return uh_cgi_request(cl, pin, ipr); - } -#endif - return uh_file_request(cl, pin); - } - } - - /* 404 - pass 1 */ - else - { - /* Try to invoke an error handler */ - if ((pin = uh_path_lookup(cl, conf->error_handler)) != NULL) - { - /* auth ok? */ - if (uh_auth_check(cl, req, pin)) - { - req->redirect_status = 404; -#ifdef HAVE_CGI - if (uh_path_match(conf->cgi_prefix, pin->name) || - (ipr = uh_interpreter_lookup(pin->phys)) != NULL) - { - return uh_cgi_request(cl, pin, ipr); - } -#endif - return uh_file_request(cl, pin); - } - } - - /* 404 - pass 2 */ - else - { - uh_http_sendhf(cl, 404, "Not Found", "No such file or directory"); - } - } - - return false; -} - -static void uh_socket_cb(struct uloop_fd *u, unsigned int events); - -static void uh_listener_cb(struct uloop_fd *u, unsigned int events) -{ - int new_fd; - struct listener *serv; - struct client *cl; - struct config *conf; - - struct sockaddr_in6 sa; - socklen_t sl = sizeof(sa); - - serv = container_of(u, struct listener, fd); - conf = serv->conf; - - /* defer client if maximum number of requests is exceeded */ - if (serv->n_clients >= conf->max_requests) - return; - - /* handle new connections */ - if ((new_fd = accept(u->fd, (struct sockaddr *)&sa, &sl)) != -1) - { - D("SRV: Server(%d) accept => Client(%d)\n", u->fd, new_fd); - - /* add to global client list */ - if ((cl = uh_client_add(new_fd, serv, &sa)) != NULL) - { - /* add client socket to global fdset */ - uh_ufd_add(&cl->fd, uh_socket_cb, ULOOP_READ); - fd_cloexec(cl->fd.fd); - -#ifdef HAVE_TLS - /* setup client tls context */ - if (conf->tls) - { - if (conf->tls_accept(cl) < 1) - { - D("SRV: Client(%d) SSL handshake failed, drop\n", new_fd); - - /* remove from global client list */ - uh_client_remove(cl); - return; - } - } -#endif - } - - /* insufficient resources */ - else - { - fprintf(stderr, "uh_client_add(): Cannot allocate memory\n"); - close(new_fd); - } - } -} - -static void uh_client_cb(struct client *cl, unsigned int events); - -static void uh_rpipe_cb(struct uloop_fd *u, unsigned int events) -{ - struct client *cl = container_of(u, struct client, rpipe); - - D("SRV: Client(%d) rpipe readable\n", cl->fd.fd); - - uh_client_cb(cl, ULOOP_WRITE); -} - -static void uh_socket_cb(struct uloop_fd *u, unsigned int events) -{ - struct client *cl = container_of(u, struct client, fd); - - D("SRV: Client(%d) socket readable\n", cl->fd.fd); - - uh_client_cb(cl, ULOOP_READ); -} - -static void uh_child_cb(struct uloop_process *p, int rv) -{ - struct client *cl = container_of(p, struct client, proc); - - D("SRV: Client(%d) child(%d) dead\n", cl->fd.fd, cl->proc.pid); - - uh_client_cb(cl, ULOOP_READ | ULOOP_WRITE); -} - -static void uh_kill9_cb(struct uloop_timeout *t) -{ - struct client *cl = container_of(t, struct client, timeout); - - if (!kill(cl->proc.pid, 0)) - { - D("SRV: Client(%d) child(%d) kill(SIGKILL)...\n", - cl->fd.fd, cl->proc.pid); - - kill(cl->proc.pid, SIGKILL); - } -} - -static void uh_timeout_cb(struct uloop_timeout *t) -{ - struct client *cl = container_of(t, struct client, timeout); - - D("SRV: Client(%d) child(%d) timed out\n", cl->fd.fd, cl->proc.pid); - - if (!kill(cl->proc.pid, 0)) - { - D("SRV: Client(%d) child(%d) kill(SIGTERM)...\n", - cl->fd.fd, cl->proc.pid); - - kill(cl->proc.pid, SIGTERM); - - cl->timeout.cb = uh_kill9_cb; - uloop_timeout_set(&cl->timeout, 1000); - } -} - -static void uh_client_cb(struct client *cl, unsigned int events) -{ - int i; - struct config *conf; - struct http_request *req; - - conf = cl->server->conf; - - D("SRV: Client(%d) enter callback\n", cl->fd.fd); - - /* undispatched yet */ - if (!cl->dispatched) - { - /* we have no headers yet and this was a write event, ignore... */ - if (!(events & ULOOP_READ)) - { - D("SRV: Client(%d) ignoring write event before headers\n", cl->fd.fd); - return; - } - - /* attempt to receive and parse headers */ - if (!(req = uh_http_header_recv(cl))) - { - D("SRV: Client(%d) failed to receive header\n", cl->fd.fd); - uh_client_shutdown(cl); - return; - } - - /* process expect headers */ - foreach_header(i, req->headers) - { - if (strcasecmp(req->headers[i], "Expect")) - continue; - - if (strcasecmp(req->headers[i+1], "100-continue")) - { - D("SRV: Client(%d) unknown expect header (%s)\n", - cl->fd.fd, req->headers[i+1]); - - uh_http_response(cl, 417, "Precondition Failed"); - uh_client_shutdown(cl); - return; - } - else - { - D("SRV: Client(%d) sending HTTP/1.1 100 Continue\n", cl->fd.fd); - - uh_http_sendf(cl, NULL, "HTTP/1.1 100 Continue\r\n\r\n"); - cl->httpbuf.len = 0; /* client will re-send the body */ - break; - } - } - - /* RFC1918 filtering */ - if (conf->rfc1918_filter && - sa_rfc1918(&cl->peeraddr) && !sa_rfc1918(&cl->servaddr)) - { - uh_http_sendhf(cl, 403, "Forbidden", - "Rejected request from RFC1918 IP " - "to public server address"); - - uh_client_shutdown(cl); - return; - } - - /* dispatch request */ - if (!uh_dispatch_request(cl, req)) - { - D("SRV: Client(%d) failed to dispach request\n", cl->fd.fd); - uh_client_shutdown(cl); - return; - } - - /* request handler spawned a pipe, register handler */ - if (cl->rpipe.fd > -1) - { - D("SRV: Client(%d) pipe(%d) spawned\n", cl->fd.fd, cl->rpipe.fd); - - uh_ufd_add(&cl->rpipe, uh_rpipe_cb, ULOOP_READ); - } - - /* request handler spawned a child, register handler */ - if (cl->proc.pid) - { - D("SRV: Client(%d) child(%d) spawned\n", cl->fd.fd, cl->proc.pid); - - cl->proc.cb = uh_child_cb; - uloop_process_add(&cl->proc); - - cl->timeout.cb = uh_timeout_cb; - uloop_timeout_set(&cl->timeout, conf->script_timeout * 1000); - } - - /* header processing complete */ - D("SRV: Client(%d) dispatched\n", cl->fd.fd); - cl->dispatched = true; - } - - if (!cl->cb(cl)) - { - D("SRV: Client(%d) response callback signalized EOF\n", cl->fd.fd); - uh_client_shutdown(cl); - return; - } -} - -#ifdef HAVE_TLS -static inline int uh_inittls(struct config *conf) -{ - /* library handle */ - void *lib; - - /* already loaded */ - if (conf->tls != NULL) - return 0; - - /* load TLS plugin */ - if (!(lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL))) - { - fprintf(stderr, - "Notice: Unable to load TLS plugin - disabling SSL support! " - "(Reason: %s)\n", dlerror() - ); - - return 1; - } - else - { - /* resolve functions */ - 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 " - "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); - } - } - - return 0; -} -#endif - -int main (int argc, char **argv) -{ - /* working structs */ - struct addrinfo hints; - struct sigaction sa; - struct config conf; - - /* maximum file descriptor number */ - int cur_fd = 0; - -#ifdef HAVE_TLS - int tls = 0; - int keys = 0; -#endif - - int bound = 0; - int nofork = 0; - - /* args */ - int opt; - char addr[128]; - char *port = NULL; - -#if defined(HAVE_LUA) || defined(HAVE_TLS) || defined(HAVE_UBUS) - /* library handle */ - void *lib; -#endif - - /* handle SIGPIPE, SIGINT, SIGTERM */ - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); - - sa.sa_handler = uh_sigterm; - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, 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)); - - uloop_init(); - - while ((opt = getopt(argc, argv, - "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:n:x:i:t:T:A:u:U:")) > 0) - { - switch(opt) - { - /* [addr:]port */ - case 'p': - case 's': - memset(addr, 0, sizeof(addr)); - - if ((port = strrchr(optarg, ':')) != NULL) - { - if ((optarg[0] == '[') && (port > optarg) && (port[-1] == ']')) - memcpy(addr, optarg + 1, - min(sizeof(addr), (int)(port - optarg) - 2)); - else - memcpy(addr, optarg, - min(sizeof(addr), (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(addr[0] ? addr : NULL, port, &hints, - (opt == 's'), &conf); - 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; -#else - case 'C': - case 'K': - fprintf(stderr, - "Notice: TLS support not compiled, ignoring -%c\n", - opt); - 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; - - case 'n': - conf.max_requests = atoi(optarg); - 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; -#else - case 'x': - case 'i': - fprintf(stderr, - "Notice: CGI support not compiled, ignoring -%c\n", - opt); - break; -#endif - -#ifdef HAVE_LUA - /* lua prefix */ - case 'l': - conf.lua_prefix = optarg; - break; - - /* lua handler */ - case 'L': - conf.lua_handler = optarg; - break; -#else - case 'l': - case 'L': - fprintf(stderr, - "Notice: Lua support not compiled, ignoring -%c\n", - opt); - break; -#endif - -#ifdef HAVE_UBUS - /* ubus prefix */ - case 'u': - conf.ubus_prefix = optarg; - break; - - /* ubus socket */ - case 'U': - conf.ubus_socket = optarg; - break; -#else - case 'u': - case 'U': - fprintf(stderr, - "Notice: UBUS support not compiled, ignoring -%c\n", - opt); - 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] = ' '; - /* opt now contains strlen(optarg) -- no need to re-scan */ - memset(port, 0, opt+1); - if (uh_urldecode(port, opt, optarg, opt) < 0) - fprintf(stderr, "uhttpd: invalid encoding\n"); - - 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" - " -n count Maximum allowed number of concurrent requests\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_UBUS - " -u string URL prefix for HTTP/JSON handler\n" - " -U file Override ubus socket path\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) || defined(HAVE_UBUS) - " -t seconds CGI, Lua and UBUS 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 max requests */ - if (conf.max_requests <= 0) - conf.max_requests = 3; - - /* default network timeout */ - if (conf.network_timeout <= 0) - conf.network_timeout = 30; - -#if defined(HAVE_CGI) || defined(HAVE_LUA) || defined(HAVE_UBUS) - /* 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); - } - } -#endif - -#ifdef HAVE_UBUS - /* load ubus plugin */ - if (!(lib = dlopen("uhttpd_ubus.so", RTLD_LAZY | RTLD_GLOBAL))) - { - fprintf(stderr, - "Notice: Unable to load ubus plugin - disabling ubus support! " - "(Reason: %s)\n", dlerror()); - } - else if (conf.ubus_prefix) - { - /* resolve functions */ - if (!(conf.ubus_init = dlsym(lib, "uh_ubus_init")) || - !(conf.ubus_close = dlsym(lib, "uh_ubus_close")) || - !(conf.ubus_request = dlsym(lib, "uh_ubus_request"))) - { - fprintf(stderr, - "Error: Failed to lookup required symbols " - "in ubus plugin: %s\n", dlerror() - ); - exit(1); - } - - /* initialize ubus */ - conf.ubus_state = conf.ubus_init(&conf); - } -#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 */ - uloop_run(); - -#ifdef HAVE_LUA - /* destroy the Lua state */ - if (conf.lua_state != NULL) - conf.lua_close(conf.lua_state); -#endif - -#ifdef HAVE_UBUS - /* destroy the ubus state */ - if (conf.ubus_state != NULL) - conf.ubus_close(conf.ubus_state); -#endif - - return 0; -} diff --git a/package/uhttpd/src/uhttpd.h b/package/uhttpd/src/uhttpd.h deleted file mode 100644 index f6982db32..000000000 --- a/package/uhttpd/src/uhttpd.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Main header - * - * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org> - * - * 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. - */ - -#ifndef _UHTTPD_ - -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/wait.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <arpa/inet.h> -#include <linux/limits.h> -#include <netdb.h> -#include <ctype.h> -#include <errno.h> -#include <dlfcn.h> - -#include <libubox/list.h> -#include <libubox/uloop.h> - - -#ifdef HAVE_LUA -#include <lua.h> -#endif - -#ifdef HAVE_TLS -#include <openssl/ssl.h> -#endif - -/* uClibc... */ -#ifndef SOL_TCP -#define SOL_TCP 6 -#endif - -#ifdef DEBUG -#define D(...) fprintf(stderr, __VA_ARGS__) -#else -#define D(...) -#endif - - -#define UH_LIMIT_MSGHEAD 4096 -#define UH_LIMIT_HEADERS 64 -#define UH_LIMIT_CLIENTS 64 - - -struct listener; -struct client; -struct interpreter; -struct http_request; -struct uh_ubus_state; - -struct config { - char docroot[PATH_MAX]; - char *realm; - char *file; - char *index_file; - char *error_handler; - int no_symlinks; - int no_dirlists; - int network_timeout; - int rfc1918_filter; - int tcp_keepalive; - int max_requests; -#ifdef HAVE_CGI - char *cgi_prefix; -#endif -#ifdef HAVE_LUA - char *lua_prefix; - char *lua_handler; - lua_State *lua_state; - lua_State * (*lua_init) (const struct config *conf); - void (*lua_close) (lua_State *L); - bool (*lua_request) (struct client *cl, lua_State *L); -#endif -#ifdef HAVE_UBUS - char *ubus_prefix; - char *ubus_socket; - void *ubus_state; - struct uh_ubus_state * (*ubus_init) (const struct config *conf); - void (*ubus_close) (struct uh_ubus_state *state); - bool (*ubus_request) (struct client *cl, struct uh_ubus_state *state); -#endif -#if defined(HAVE_CGI) || defined(HAVE_LUA) || defined(HAVE_UBUS) - int script_timeout; -#endif -#ifdef HAVE_TLS - char *cert; - char *key; - SSL_CTX *tls; - SSL_CTX * (*tls_init) (void); - int (*tls_cert) (SSL_CTX *c, const char *file); - int (*tls_key) (SSL_CTX *c, const char *file); - void (*tls_free) (struct listener *l); - int (*tls_accept) (struct client *c); - void (*tls_close) (struct client *c); - int (*tls_recv) (struct client *c, char *buf, int len); - int (*tls_send) (struct client *c, const char *buf, int len); -#endif -}; - -enum http_method { - UH_HTTP_MSG_GET, - UH_HTTP_MSG_POST, - UH_HTTP_MSG_HEAD, -}; - -extern const char *http_methods[]; - -enum http_version { - UH_HTTP_VER_0_9, - UH_HTTP_VER_1_0, - UH_HTTP_VER_1_1, -}; - -extern const char *http_versions[]; - -struct http_request { - enum http_method method; - enum http_version version; - int redirect_status; - char *url; - char *headers[UH_LIMIT_HEADERS]; - struct auth_realm *realm; -}; - -struct http_response { - int statuscode; - char *statusmsg; - char *headers[UH_LIMIT_HEADERS]; -}; - -struct listener { - struct uloop_fd fd; - int socket; - int n_clients; - struct sockaddr_in6 addr; - struct config *conf; -#ifdef HAVE_TLS - SSL_CTX *tls; -#endif - struct listener *next; -}; - -struct client { -#ifdef HAVE_TLS - SSL *tls; -#endif - struct uloop_fd fd; - struct uloop_fd rpipe; - struct uloop_fd wpipe; - struct uloop_process proc; - struct uloop_timeout timeout; - bool (*cb)(struct client *); - void *priv; - bool dispatched; - struct { - char buf[UH_LIMIT_MSGHEAD]; - char *ptr; - int len; - } httpbuf; - struct listener *server; - struct http_request request; - struct http_response response; - struct sockaddr_in6 servaddr; - struct sockaddr_in6 peeraddr; - struct client *next; -}; - -struct client_light { -#ifdef HAVE_TLS - SSL *tls; -#endif - struct uloop_fd fd; -}; - -struct auth_realm { - char path[PATH_MAX]; - char user[32]; - char pass[128]; - struct auth_realm *next; -}; - -#ifdef HAVE_CGI -struct interpreter { - char path[PATH_MAX]; - char extn[32]; - struct interpreter *next; -}; -#endif - -#endif |