merge r22630, r22692, r22805
authorJo-Philipp Wich <jow@openwrt.org>
Mon, 6 Sep 2010 20:49:31 +0000 (20:49 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Mon, 6 Sep 2010 20:49:31 +0000 (20:49 +0000)
SVN-Revision: 22962

package/uhttpd/Makefile
package/uhttpd/files/uhttpd.config
package/uhttpd/files/uhttpd.init
package/uhttpd/src/uhttpd-cgi.c
package/uhttpd/src/uhttpd-cgi.h
package/uhttpd/src/uhttpd-file.c
package/uhttpd/src/uhttpd-tls.c
package/uhttpd/src/uhttpd-utils.c
package/uhttpd/src/uhttpd-utils.h
package/uhttpd/src/uhttpd.c
package/uhttpd/src/uhttpd.h

index cb85ad88c0e40478ce14c5bafffc2987ff73731f..086c294580f1672509fcd3201576c082ac647a8f 100644 (file)
@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=uhttpd
-PKG_RELEASE:=14
+PKG_RELEASE:=17
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 PKG_BUILD_DEPENDS := libcyassl liblua
index 534e8f8b29da31f1e33df1a2975250fbccd25742..a29910a65f38b3d1409dfb40bd2aedad788bfcf7 100644 (file)
@@ -27,6 +27,13 @@ config uhttpd main
        # Default is /cgi-bin
        option cgi_prefix       /cgi-bin
 
+       # List of extension->interpreter mappings.
+       # Files with an associated interpreter can
+       # be called outside of the CGI prefix and do
+       # not need to be executable.
+#      list interpreter        ".php=/usr/bin/php-cgi"
+#      list interpreter        ".cgi=/usr/bin/perl"
+
        # Lua url prefix and handler script.
        # Lua support is disabled if no prefix given.
 #      option lua_prefix       /luci
index 8221d859bfc43a33f8bd9abdd0cf6f48d6c7fc4b..f8f1754e9065b7587b961fb4345a45bda2d8079b 100755 (executable)
@@ -56,7 +56,7 @@ start_instance()
 
        local cfg="$1"
        local realm="$(uci_get system.@system[0].hostname)"
-       local listen http https
+       local listen http https interpreter path
 
        append_arg "$cfg" home "-h"
        append_arg "$cfg" realm "-r" "${realm:-OpenWrt}"
@@ -78,6 +78,11 @@ start_instance()
                append UHTTPD_ARGS "-p $listen"
        done
 
+       config_get interpreter "$cfg" interpreter
+       for path in $interpreter; 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
index 08612491624d0c8c1303152b6e7b4732153caa3c..8c17251b317b56de857e0854b69167b5ec3f15fc 100644 (file)
@@ -135,9 +135,11 @@ static int uh_cgi_error_500(struct client *cl, struct http_request *req, const c
 }
 
 
-void uh_cgi_request(struct client *cl, struct http_request *req, struct path_info *pi)
-{
-       int i, hdroff, bufoff;
+void uh_cgi_request(
+       struct client *cl, struct http_request *req,
+       struct path_info *pi, struct interpreter *ip
+) {
+       int i, hdroff, bufoff, rv;
        int hdrlen = 0;
        int buflen = 0;
        int fd_max = 0;
@@ -199,9 +201,9 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                        dup2(rfd[1], 1);
                        dup2(wfd[0], 0);
 
-                       /* check for regular, world-executable file */
-                       if( (pi->stat.st_mode & S_IFREG) &&
-                           (pi->stat.st_mode & S_IXOTH)
+                       /* 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();
@@ -320,14 +322,17 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                if( chdir(pi->root) )
                                        perror("chdir()");
 
-                               execl(pi->phys, pi->phys, NULL);
+                               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",
-                                               pi->phys, strerror(errno)
+                                               ip ? ip->path : pi->phys, strerror(errno)
                                );
                        }
 
@@ -371,12 +376,6 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
 
                        memset(hdr, 0, sizeof(hdr));
 
-                       timeout.tv_sec = cl->server->conf->script_timeout;
-                       timeout.tv_usec = 0;
-
-#define ensure(x) \
-       do { if( x < 0 ) goto out; } while(0)
-
                        /* I/O loop, watch our pipe ends and dispatch child reads/writes from/to socket */
                        while( 1 )
                        {
@@ -386,11 +385,21 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                FD_SET(rfd[0], &reader);
                                FD_SET(wfd[1], &writer);
 
+                               timeout.tv_sec = (header_sent < 1) ? cl->server->conf->script_timeout : 3;
+                               timeout.tv_usec = 0;
+
+                               ensure_out(rv = select_intr(fd_max, &reader,
+                                       (content_length > -1) ? &writer : NULL, NULL, &timeout));
+
+                               /* timeout */
+                               if( rv == 0 )
+                               {
+                                       ensure_out(kill(child, 0));
+                               }
+
                                /* wait until we can read or write or both */
-                               if( select_intr(fd_max, &reader,
-                                       (content_length > -1) ? &writer : NULL, NULL,
-                                       (header_sent < 1) ? &timeout : NULL) > 0
-                               ) {
+                               else if( rv > 0 )
+                               {
                                        /* ready to write to cgi program */
                                        if( FD_ISSET(wfd[1], &writer) )
                                        {
@@ -398,7 +407,10 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                                if( content_length > 0 )
                                                {
                                                        /* read it from socket ... */
-                                                       if( (buflen = uh_tcp_recv(cl, buf, min(content_length, sizeof(buf)))) > 0 )
+                                                       ensure_out(buflen = uh_tcp_recv(cl, buf,
+                                                               min(content_length, sizeof(buf))));
+
+                                                       if( buflen > 0 )
                                                        {
                                                                /* ... and write it to child's stdin */
                                                                if( write(wfd[1], buf, buflen) < 0 )
@@ -451,7 +463,7 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                                                if( (res = uh_cgi_header_parse(hdr, hdrlen, &hdroff)) != NULL )
                                                                {
                                                                        /* write status */
-                                                                       ensure(uh_http_sendf(cl, NULL,
+                                                                       ensure_out(uh_http_sendf(cl, NULL,
                                                                                "HTTP/%.1f %03d %s\r\n"
                                                                                "Connection: close\r\n",
                                                                                req->version, res->statuscode,
@@ -461,7 +473,7 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                                                        if( !uh_cgi_header_lookup(res, "Location") &&
                                                                            !uh_cgi_header_lookup(res, "Content-Type")
                                                                        ) {
-                                                                               ensure(uh_http_send(cl, NULL,
+                                                                               ensure_out(uh_http_send(cl, NULL,
                                                                                        "Content-Type: text/plain\r\n", -1));
                                                                        }
 
@@ -469,32 +481,32 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                                                        if( (req->version > 1.0) &&
                                                                            !uh_cgi_header_lookup(res, "Transfer-Encoding")
                                                                        ) {
-                                                                               ensure(uh_http_send(cl, NULL,
+                                                                               ensure_out(uh_http_send(cl, NULL,
                                                                                        "Transfer-Encoding: chunked\r\n", -1));
                                                                        }
 
                                                                        /* write headers from CGI program */
                                                                        foreach_header(i, res->headers)
                                                                        {
-                                                                               ensure(uh_http_sendf(cl, NULL, "%s: %s\r\n",
+                                                                               ensure_out(uh_http_sendf(cl, NULL, "%s: %s\r\n",
                                                                                        res->headers[i], res->headers[i+1]));
                                                                        }
 
                                                                        /* terminate header */
-                                                                       ensure(uh_http_send(cl, NULL, "\r\n", -1));
+                                                                       ensure_out(uh_http_send(cl, NULL, "\r\n", -1));
 
                                                                        /* push out remaining head buffer */
                                                                        if( hdroff < hdrlen )
-                                                                               ensure(uh_http_send(cl, req, &hdr[hdroff], hdrlen - hdroff));
+                                                                               ensure_out(uh_http_send(cl, req, &hdr[hdroff], hdrlen - hdroff));
                                                                }
 
                                                                /* ... failed and head buffer exceeded */
                                                                else if( hdrlen >= sizeof(hdr) )
                                                                {
-                                                                       ensure(uh_cgi_error_500(cl, req,
+                                                                       ensure_out(uh_cgi_error_500(cl, req,
                                                                                "The CGI program generated an invalid response:\n\n"));
 
-                                                                       ensure(uh_http_send(cl, req, hdr, hdrlen));
+                                                                       ensure_out(uh_http_send(cl, req, hdr, hdrlen));
                                                                }
 
                                                                /* ... failed but free buffer space, try again */
@@ -505,7 +517,7 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
 
                                                                /* push out remaining read buffer */
                                                                if( bufoff < buflen )
-                                                                       ensure(uh_http_send(cl, req, &buf[bufoff], buflen - bufoff));
+                                                                       ensure_out(uh_http_send(cl, req, &buf[bufoff], buflen - bufoff));
 
                                                                header_sent = 1;
                                                                continue;
@@ -513,7 +525,7 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
 
 
                                                        /* headers complete, pass through buffer to socket */
-                                                       ensure(uh_http_send(cl, req, buf, buflen));
+                                                       ensure_out(uh_http_send(cl, req, buf, buflen));
                                                }
 
                                                /* looks like eof from child */
@@ -533,7 +545,7 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                                                 * build the required headers here.
                                                                 */
 
-                                                               ensure(uh_http_sendf(cl, NULL,
+                                                               ensure_out(uh_http_sendf(cl, NULL,
                                                                        "HTTP/%.1f 200 OK\r\n"
                                                                        "Content-Type: text/plain\r\n"
                                                                        "%s\r\n",
@@ -541,11 +553,11 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                                                                        ? "Transfer-Encoding: chunked\r\n" : ""
                                                                ));
 
-                                                               ensure(uh_http_send(cl, req, hdr, hdrlen));
+                                                               ensure_out(uh_http_send(cl, req, hdr, hdrlen));
                                                        }
 
                                                        /* send final chunk if we're in chunked transfer mode */
-                                                       ensure(uh_http_send(cl, req, "", 0));
+                                                       ensure_out(uh_http_send(cl, req, "", 0));
                                                        break;
                                                }
                                        }
@@ -556,13 +568,13 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
                                {
                                        if( (errno != EINTR) && ! header_sent )
                                        {
-                                               ensure(uh_http_sendhf(cl, 504, "Gateway Timeout",
+                                               ensure_out(uh_http_sendhf(cl, 504, "Gateway Timeout",
                                                        "The CGI script took too long to produce "
                                                        "a response"));
                                        }
 
                                        /* send final chunk if we're in chunked transfer mode */
-                                       ensure(uh_http_send(cl, req, "", 0));
+                                       ensure_out(uh_http_send(cl, req, "", 0));
 
                                        break;
                                }
index c90557d8fd5a6f7bf4720fc93acb32c58272ec19..cb84dae0c68b3496ad4b69df27eae81c2e76e244 100644 (file)
@@ -25,7 +25,8 @@
 #include <linux/limits.h>
 
 void uh_cgi_request(
-       struct client *cl, struct http_request *req, struct path_info *pi
+       struct client *cl, struct http_request *req,
+       struct path_info *pi, struct interpreter *ip
 );
 
 #endif
index ef9a77b6cce4f4f5ed21bbe23842a03880758341..fda86d72631031c7f1884e58590a99a0df3c0c63 100644 (file)
@@ -97,10 +97,8 @@ static char * uh_file_header_lookup(struct http_request *req, const char *name)
        return NULL;
 }
 
-#define ensure_ret(x) \
-       do { if( x < 0 ) return; } while(0)
 
-static void uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s)
 {
        ensure_ret(uh_http_sendf(cl, NULL, "Connection: close\r\n"));
 
@@ -110,29 +108,29 @@ static void uh_file_response_ok_hdrs(struct client *cl, struct http_request *req
                ensure_ret(uh_http_sendf(cl, NULL, "Last-Modified: %s\r\n", uh_file_unix2date(s->st_mtime)));
        }
 
-       ensure_ret(uh_http_sendf(cl, NULL, "Date: %s\r\n", uh_file_unix2date(time(NULL))));
+       return uh_http_sendf(cl, NULL, "Date: %s\r\n", uh_file_unix2date(time(NULL)));
 }
 
-static void uh_file_response_200(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_200(struct client *cl, struct http_request *req, struct stat *s)
 {
        ensure_ret(uh_http_sendf(cl, NULL, "HTTP/%.1f 200 OK\r\n", req->version));
-       uh_file_response_ok_hdrs(cl, req, s);
+       return uh_file_response_ok_hdrs(cl, req, s);
 }
 
-static void uh_file_response_304(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_304(struct client *cl, struct http_request *req, struct stat *s)
 {
        ensure_ret(uh_http_sendf(cl, NULL, "HTTP/%.1f 304 Not Modified\r\n", req->version));
-       uh_file_response_ok_hdrs(cl, req, s);
+       return uh_file_response_ok_hdrs(cl, req, s);
 }
 
-static void uh_file_response_412(struct client *cl, struct http_request *req)
+static int uh_file_response_412(struct client *cl, struct http_request *req)
 {
-       ensure_ret(uh_http_sendf(cl, NULL,
+       return uh_http_sendf(cl, NULL,
                "HTTP/%.1f 412 Precondition Failed\r\n"
-               "Connection: close\r\n", req->version));
+               "Connection: close\r\n", req->version);
 }
 
-static int uh_file_if_match(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_match(struct client *cl, struct http_request *req, struct stat *s, int *ok)
 {
        const char *tag = uh_file_mktag(s);
        char *hdr = uh_file_header_lookup(req, "If-Match");
@@ -152,43 +150,44 @@ static int uh_file_if_match(struct client *cl, struct http_request *req, struct
                        }
                        else if( !strcmp(p, "*") || !strcmp(p, tag) )
                        {
-                               return 1;
+                               *ok = 1;
+                               return *ok;
                        }
                }
 
-               uh_file_response_412(cl, req);
-               return 0;
+               *ok = 0;
+               ensure_ret(uh_file_response_412(cl, req));
+               return *ok;
        }
 
-       return 1;
+       *ok = 1;
+       return *ok;
 }
 
-static int uh_file_if_modified_since(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_modified_since(struct client *cl, struct http_request *req, struct stat *s, int *ok)
 {
        char *hdr = uh_file_header_lookup(req, "If-Modified-Since");
+       *ok = 1;
 
        if( hdr )
        {
-               if( uh_file_date2unix(hdr) < s->st_mtime )
+               if( uh_file_date2unix(hdr) >= s->st_mtime )
                {
-                       return 1;
-               }
-               else
-               {
-                       uh_file_response_304(cl, req, s);
-                       return 0;
+                       *ok = 0;
+                       ensure_ret(uh_file_response_304(cl, req, s));
                }
        }
 
-       return 1;
+       return *ok;
 }
 
-static int uh_file_if_none_match(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_none_match(struct client *cl, struct http_request *req, struct stat *s, int *ok)
 {
        const char *tag = uh_file_mktag(s);
        char *hdr = uh_file_header_lookup(req, "If-None-Match");
        char *p;
        int i;
+       *ok = 1;
 
        if( hdr )
        {
@@ -203,53 +202,54 @@ static int uh_file_if_none_match(struct client *cl, struct http_request *req, st
                        }
                        else if( !strcmp(p, "*") || !strcmp(p, tag) )
                        {
+                               *ok = 0;
+
                                if( (req->method == UH_HTTP_MSG_GET) ||
                                    (req->method == UH_HTTP_MSG_HEAD) )
-                                       uh_file_response_304(cl, req, s);
+                                       ensure_ret(uh_file_response_304(cl, req, s));
                                else
-                                       uh_file_response_412(cl, req);
+                                       ensure_ret(uh_file_response_412(cl, req));
 
-                               return 0;
+                               break;
                        }
                }
        }
 
-       return 1;
+       return *ok;
 }
 
-static int uh_file_if_range(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_range(struct client *cl, struct http_request *req, struct stat *s, int *ok)
 {
        char *hdr = uh_file_header_lookup(req, "If-Range");
+       *ok = 1;
 
        if( hdr )
        {
-               uh_file_response_412(cl, req);
-               return 0;
+               *ok = 0;
+               ensure_ret(uh_file_response_412(cl, req));
        }
 
-       return 1;
+       return *ok;
 }
 
-static int uh_file_if_unmodified_since(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_unmodified_since(struct client *cl, struct http_request *req, struct stat *s, int *ok)
 {
        char *hdr = uh_file_header_lookup(req, "If-Unmodified-Since");
+       *ok = 1;
 
        if( hdr )
        {
                if( uh_file_date2unix(hdr) <= s->st_mtime )
                {
-                       uh_file_response_412(cl, req);
-                       return 0;
+                       *ok = 0;
+                       ensure_ret(uh_file_response_412(cl, req));
                }
        }
 
-       return 1;
+       return *ok;
 }
 
 
-#define ensure_out(x) \
-       do { if( x < 0 ) goto out; } while(0)
-
 static int uh_file_scandir_filter_dir(const struct dirent *e)
 {
        return strcmp(e->d_name, ".") ? 1 : 0;
@@ -335,6 +335,7 @@ out:
 void uh_file_request(struct client *cl, struct http_request *req, struct path_info *pi)
 {
        int rlen;
+       int ok = 1;
        int fd = -1;
        char buf[UH_LIMIT_MSGHEAD];
 
@@ -342,15 +343,16 @@ void uh_file_request(struct client *cl, struct http_request *req, struct path_in
        if( (pi->stat.st_mode & S_IFREG) && ((fd = open(pi->phys, O_RDONLY)) > 0) )
        {
                /* test preconditions */
-               if(
-                       uh_file_if_modified_since(cl, req, &pi->stat)   &&
-                       uh_file_if_match(cl, req, &pi->stat)            &&
-                       uh_file_if_range(cl, req, &pi->stat)            &&
-                       uh_file_if_unmodified_since(cl, req, &pi->stat) &&
-                       uh_file_if_none_match(cl, req, &pi->stat)
-               ) {
+               if(ok) ensure_out(uh_file_if_modified_since(cl, req, &pi->stat, &ok));
+               if(ok) ensure_out(uh_file_if_match(cl, req, &pi->stat, &ok));
+               if(ok) ensure_out(uh_file_if_range(cl, req, &pi->stat, &ok));
+               if(ok) ensure_out(uh_file_if_unmodified_since(cl, req, &pi->stat, &ok));
+               if(ok) ensure_out(uh_file_if_none_match(cl, req, &pi->stat, &ok));
+
+               if( ok > 0 )
+               {
                        /* write status */
-                       uh_file_response_200(cl, req, &pi->stat);
+                       ensure_out(uh_file_response_200(cl, req, &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));
@@ -385,7 +387,7 @@ void uh_file_request(struct client *cl, struct http_request *req, struct path_in
        else if( (pi->stat.st_mode & S_IFDIR) && !cl->server->conf->no_dirlists )
        {
                /* write status */
-               uh_file_response_200(cl, req, NULL);
+               ensure_out(uh_file_response_200(cl, req, NULL));
 
                if( req->version > 1.0 )
                        ensure_out(uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1));
index 26143ddf7108c0c9f084a6036a1fab18b6bdbd3c..008f8e0df6a7e873a96ec0a3182cad60243155a5 100644 (file)
@@ -70,12 +70,14 @@ void uh_tls_client_accept(struct client *c)
 
 int uh_tls_client_recv(struct client *c, void *buf, int len)
 {
-       return SSL_read(c->tls, buf, len);
+       int rv = SSL_read(c->tls, buf, len);
+       return (rv > 0) ? rv : -1;
 }
 
 int uh_tls_client_send(struct client *c, void *buf, int len)
 {
-       return SSL_write(c->tls, buf, len);
+       int rv = SSL_write(c->tls, buf, len);
+       return (rv > 0) ? rv : -1;
 }
 
 void uh_tls_client_close(struct client *c)
index 4a1423c715d0db9e9b6f7d1ff44f34250574b336..3821eb54cb18df8328d79d583d80d3a0bd7dc4bd 100644 (file)
@@ -112,6 +112,7 @@ int select_intr(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
        /* unblock SIGCHLD */
        sigemptyset(&ssn);
        sigaddset(&ssn, SIGCHLD);
+       sigaddset(&ssn, SIGPIPE);
        sigprocmask(SIG_UNBLOCK, &ssn, &sso);
 
        rv = select(n, r, w, e, t);
@@ -193,8 +194,6 @@ int uh_tcp_recv(struct client *cl, char *buf, int len)
        return sz;
 }
 
-#define ensure(x) \
-       do { if( x < 0 ) return -1; } while(0)
 
 int uh_http_sendhf(struct client *cl, int code, const char *summary, const char *fmt, ...)
 {
@@ -211,14 +210,14 @@ int uh_http_sendhf(struct client *cl, int code, const char *summary, const char
                        code, summary
        );
 
-       ensure(uh_tcp_send(cl, buffer, len));
+       ensure_ret(uh_tcp_send(cl, buffer, len));
 
        va_start(ap, fmt);
        len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
        va_end(ap);
 
-       ensure(uh_http_sendc(cl, buffer, len));
-       ensure(uh_http_sendc(cl, NULL, 0));
+       ensure_ret(uh_http_sendc(cl, buffer, len));
+       ensure_ret(uh_http_sendc(cl, NULL, 0));
 
        return 0;
 }
@@ -235,13 +234,13 @@ int uh_http_sendc(struct client *cl, const char *data, int len)
        if( len > 0 )
        {
                clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len);
-               ensure(uh_tcp_send(cl, chunk, clen));
-               ensure(uh_tcp_send(cl, data, len));
-               ensure(uh_tcp_send(cl, "\r\n", 2));
+               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(uh_tcp_send(cl, "0\r\n\r\n", 5));
+               ensure_ret(uh_tcp_send(cl, "0\r\n\r\n", 5));
        }
 
        return 0;
@@ -259,9 +258,9 @@ int uh_http_sendf(
        va_end(ap);
 
        if( (req != NULL) && (req->version > 1.0) )
-               ensure(uh_http_sendc(cl, buffer, len));
+               ensure_ret(uh_http_sendc(cl, buffer, len));
        else if( len > 0 )
-               ensure(uh_tcp_send(cl, buffer, len));
+               ensure_ret(uh_tcp_send(cl, buffer, len));
 
        return 0;
 }
@@ -273,9 +272,9 @@ int uh_http_send(
                len = strlen(buf);
 
        if( (req != NULL) && (req->version > 1.0) )
-               ensure(uh_http_sendc(cl, buf, len));
+               ensure_ret(uh_http_sendc(cl, buf, len));
        else if( len > 0 )
-               ensure(uh_tcp_send(cl, buf, len));
+               ensure_ret(uh_tcp_send(cl, buf, len));
 
        return 0;
 }
@@ -605,8 +604,7 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
 }
 
 
-static char uh_realms[UH_LIMIT_AUTHREALMS * sizeof(struct auth_realm)] = { 0 };
-static int uh_realm_count = 0;
+static struct auth_realm *uh_realms = NULL;
 
 struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
 {
@@ -614,11 +612,8 @@ struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
        struct passwd *pwd;
        struct spwd *spwd;
 
-       if( uh_realm_count < UH_LIMIT_AUTHREALMS )
+       if((new = (struct auth_realm *)malloc(sizeof(struct auth_realm))) != NULL)
        {
-               new = (struct auth_realm *)
-                       &uh_realms[uh_realm_count * sizeof(struct auth_realm)];
-
                memset(new, 0, sizeof(struct auth_realm));
 
                memcpy(new->path, path,
@@ -643,7 +638,7 @@ struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
                        ) {
                                memcpy(new->pass, pwd->pw_passwd,
                                        min(strlen(pwd->pw_passwd), sizeof(new->pass) - 1));
-                       }                       
+                       }
                }
 
                /* ordinary pwd */
@@ -655,9 +650,13 @@ struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
 
                if( new->pass[0] )
                {
-                       uh_realm_count++;
+                       new->next = uh_realms;
+                       uh_realms = new;
+
                        return new;
                }
+
+               free(new);
        }
 
        return NULL;
@@ -677,11 +676,8 @@ int uh_auth_check(
        protected = 0;
 
        /* check whether at least one realm covers the requested url */
-       for( i = 0; i < uh_realm_count; i++ )
+       for( realm = uh_realms; realm; realm = realm->next )
        {
-               realm = (struct auth_realm *)
-                       &uh_realms[i * sizeof(struct auth_realm)];
-
                rlen = strlen(realm->path);
 
                if( (plen >= rlen) && !strncasecmp(pi->name, realm->path, rlen) )
@@ -721,11 +717,8 @@ int uh_auth_check(
                if( user && pass )
                {
                        /* find matching realm */
-                       for( i = 0, realm = NULL; i < uh_realm_count; i++ )
+                       for( realm = uh_realms; realm; realm = realm->next )
                        {
-                               realm = (struct auth_realm *)
-                                       &uh_realms[i * sizeof(struct auth_realm)];
-
                                rlen = strlen(realm->path);
 
                                if( (plen >= rlen) &&
@@ -735,8 +728,6 @@ int uh_auth_check(
                                        req->realm = realm;
                                        break;
                                }
-
-                               realm = NULL;
                        }
 
                        /* found a realm matching the username */
@@ -769,22 +760,17 @@ int uh_auth_check(
 }
 
 
-static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
-static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
-
-static int uh_listener_count = 0;
-static int uh_client_count = 0;
-
+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( uh_listener_count < UH_LIMIT_LISTENERS )
+       if( (new = (struct listener *)malloc(sizeof(struct listener))) != NULL )
        {
-               new = (struct listener *)
-                       &uh_listeners[uh_listener_count * sizeof(struct listener)];
+               memset(new, 0, sizeof(struct listener));
 
                new->socket = sock;
                new->conf   = conf;
@@ -794,24 +780,22 @@ struct listener * uh_listener_add(int sock, struct config *conf)
                memset(&(new->addr), 0, sl);
                getsockname(sock, (struct sockaddr *) &(new->addr), &sl);
 
-               uh_listener_count++;
+               new->next = uh_listeners;
+               uh_listeners = new;
+
+               return new;
        }
 
-       return new;
+       return NULL;
 }
 
 struct listener * uh_listener_lookup(int sock)
 {
        struct listener *cur = NULL;
-       int i;
-
-       for( i = 0; i < uh_listener_count; i++ )
-       {
-               cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
 
+       for( cur = uh_listeners; cur; cur = cur->next )
                if( cur->socket == sock )
                        return cur;
-       }
 
        return NULL;
 }
@@ -822,10 +806,9 @@ struct client * uh_client_add(int sock, struct listener *serv)
        struct client *new = NULL;
        socklen_t sl;
 
-       if( uh_client_count < UH_LIMIT_CLIENTS )
+       if( (new = (struct client *)malloc(sizeof(struct client))) != NULL )
        {
-               new = (struct client *)
-                       &uh_clients[uh_client_count * sizeof(struct client)];
+               memset(new, 0, sizeof(struct client));
 
                new->socket = sock;
                new->server = serv;
@@ -840,7 +823,8 @@ struct client * uh_client_add(int sock, struct listener *serv)
                memset(&(new->servaddr), 0, sl);
                getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl);
 
-               uh_client_count++;
+               new->next = uh_clients;
+               uh_clients = new;
        }
 
        return new;
@@ -849,30 +833,72 @@ struct client * uh_client_add(int sock, struct listener *serv)
 struct client * uh_client_lookup(int sock)
 {
        struct client *cur = NULL;
-       int i;
-
-       for( i = 0; i < uh_client_count; i++ )
-       {
-               cur = (struct client *) &uh_clients[i * sizeof(struct client)];
 
+       for( cur = uh_clients; cur; cur = cur->next )
                if( cur->socket == sock )
                        return cur;
-       }
 
        return NULL;
 }
 
 void uh_client_remove(int sock)
 {
-       struct client *del = uh_client_lookup(sock);
+       struct client *cur = NULL;
+       struct client *prv = NULL;
+
+       for( cur = uh_clients; cur; prv = cur, cur = cur->next )
+       {
+               if( cur->socket == sock )
+               {
+                       if( prv )
+                               prv->next = cur->next;
+                       else
+                               uh_clients = cur->next;
+
+                       free(cur);
+                       break;
+               }
+       }
+}
+
+
+#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( del )
+       if( (new = (struct interpreter *)
+                       malloc(sizeof(struct interpreter))) != NULL )
        {
-               memmove(del, del + 1,
-                       sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));
+               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));
 
-               uh_client_count--;
+               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
index 1b18265417910ad402e125dd5e924a4c54aac0ea..3514ce1caca730337caf04a926de0f212b5ac01a 100644 (file)
 #define fd_cloexec(fd) \
        fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)
 
+#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;
@@ -101,4 +108,9 @@ struct client * uh_client_add(int sock, struct listener *serv);
 struct client * uh_client_lookup(int sock);
 void uh_client_remove(int sock);
 
+#ifdef HAVE_CGI
+struct interpreter * uh_interpreter_add(const char *extn, const char *path);
+struct interpreter * uh_interpreter_lookup(const char *path);
+#endif
+
 #endif
index 764ff7d5703b332ddfdbc4bd2e9f5eb63265dcc7..6f5e616345121205a0a4fb4a93daaaab33108854 100644 (file)
@@ -51,8 +51,8 @@ static void uh_config_parse(struct config *conf)
 {
        FILE *c;
        char line[512];
-       char *user = NULL;
-       char *pass = NULL;
+       char *col1 = NULL;
+       char *col2 = NULL;
        char *eol  = NULL;
 
        const char *path = conf->file ? conf->file : "/etc/httpd.conf";
@@ -66,35 +66,52 @@ static void uh_config_parse(struct config *conf)
                {
                        if( (line[0] == '/') && (strchr(line, ':') != NULL) )
                        {
-                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-                                   !(pass = strchr(user, ':')) || (*pass++ = 0) ||
-                                       !(eol = strchr(pass, '\n')) || (*eol++  = 0) )
+                               if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+                                   !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
+                                       !(eol = strchr(col2, '\n')) || (*eol++  = 0) )
                                                continue;
 
-                               if( !uh_auth_add(line, user, pass) )
+                               if( !uh_auth_add(line, col1, col2) )
                                {
                                        fprintf(stderr,
                                                "Notice: No password set for user %s, ignoring "
-                                               "authentication on %s\n", user, line
+                                               "authentication on %s\n", col1, line
                                        );
                                }
                        }
                        else if( !strncmp(line, "I:", 2) )
                        {
-                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-                                   !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+                               if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+                                   !(eol = strchr(col1, '\n')) || (*eol++  = 0) )
                                        continue;
 
-                               conf->index_file = strdup(user);
+                               conf->index_file = strdup(col1);
                        }
                        else if( !strncmp(line, "E404:", 5) )
                        {
-                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
-                                   !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+                               if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+                                   !(eol = strchr(col1, '\n')) || (*eol++  = 0) )
                                                continue;
 
-                               conf->error_handler = strdup(user);
+                               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);
@@ -110,6 +127,10 @@ static int uh_socket_bind(
        int status;
        int bound = 0;
 
+       int tcp_ka_idl = 1;
+       int tcp_ka_int = 1;
+       int tcp_ka_cnt = 3;
+
        struct listener *l = NULL;
        struct addrinfo *addrs = NULL, *p = NULL;
 
@@ -129,12 +150,22 @@ static int uh_socket_bind(
                }
 
                /* "address already in use" */
-               if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1 )
+               if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) )
                {
                        perror("setsockopt()");
                        goto error;
                }
 
+               /* TCP keep-alive */
+               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 )
                {
@@ -162,11 +193,7 @@ static int uh_socket_bind(
                /* add listener to global list */
                if( ! (l = uh_listener_add(sock, conf)) )
                {
-                       fprintf(stderr,
-                               "uh_listener_add(): Can not create more than "
-                               "%i listen sockets\n", UH_LIMIT_LISTENERS
-                       );
-
+                       fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n");
                        goto error;
                }
 
@@ -343,7 +370,6 @@ static struct http_request * uh_http_header_recv(struct client *cl)
        ssize_t blen = sizeof(buffer)-1;
        ssize_t rlen = 0;
 
-
        memset(buffer, 0, sizeof(buffer));
 
        while( blen > 0 )
@@ -359,44 +385,41 @@ static struct http_request * uh_http_header_recv(struct client *cl)
                if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0 )
                {
                        /* receive data */
-                       rlen = uh_tcp_peek(cl, bufptr, blen);
+                       ensure_out(rlen = uh_tcp_peek(cl, bufptr, blen));
 
-                       if( rlen > 0 )
+                       if( (idxptr = strfind(buffer, sizeof(buffer), "\r\n\r\n", 4)) )
                        {
-                               if( (idxptr = strfind(buffer, sizeof(buffer), "\r\n\r\n", 4)) )
-                               {
-                                       blen -= uh_tcp_recv(cl, bufptr, (int)(idxptr - bufptr) + 4);
+                               ensure_out(rlen = uh_tcp_recv(cl, bufptr,
+                                       (int)(idxptr - bufptr) + 4));
 
-                                       /* header read complete ... */
-                                       return uh_http_header_parse(cl, buffer, sizeof(buffer) - blen - 1);
-                               }
-                               else
-                               {
-                                       rlen = uh_tcp_recv(cl, bufptr, rlen);
-                                       blen -= rlen;
-                                       bufptr += rlen;
-                               }
+                               /* header read complete ... */
+                               blen -= rlen;
+                               return uh_http_header_parse(cl, buffer,
+                                       sizeof(buffer) - blen - 1);
                        }
                        else
                        {
-                               /* invalid request (unexpected eof/timeout) */
-                               uh_http_response(cl, 408, "Request Timeout");
-                               return NULL;
+                               ensure_out(rlen = uh_tcp_recv(cl, bufptr, rlen));
+
+                               blen -= rlen;
+                               bufptr += rlen;
                        }
                }
                else
                {
                        /* invalid request (unexpected eof/timeout) */
-                       uh_http_response(cl, 408, "Request Timeout");
                        return NULL;
                }
        }
 
        /* request entity too large */
        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) &&
@@ -409,23 +432,193 @@ static int uh_path_match(const char *prefix, const char *url)
 
        return 0;
 }
+#endif
 
+static void uh_dispatch_request(
+       struct client *cl, struct http_request *req, struct path_info *pin
+) {
+#ifdef HAVE_CGI
+       struct interpreter *ipr = NULL;
 
-int main (int argc, char **argv)
+       if( uh_path_match(cl->server->conf->cgi_prefix, pin->name) ||
+               (ipr = uh_interpreter_lookup(pin->phys)) )
+       {
+               uh_cgi_request(cl, req, pin, ipr);
+       }
+       else
+#endif
+       {
+               uh_file_request(cl, req, pin);
+       }
+}
+
+static void uh_mainloop(struct config *conf, fd_set serv_fds, int max_fd)
 {
+       /* master file descriptor list */
+       fd_set used_fds, read_fds;
+
+       /* working structs */
+       struct http_request *req;
+       struct path_info *pin;
+       struct client *cl;
+
+       /* maximum file descriptor number */
+       int new_fd, cur_fd = 0;
+
+       /* clear the master and temp sets */
+       FD_ZERO(&used_fds);
+       FD_ZERO(&read_fds);
+
+       /* backup server descriptor set */
+       used_fds = serv_fds;
+
+       /* loop */
+       while(run)
+       {
+               /* create a working copy of the used fd set */
+               read_fds = used_fds;
+
+               /* sleep until socket activity */
+               if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
+               {
+                       perror("select()");
+                       exit(1);
+               }
+
+               /* run through the existing connections looking for data to be read */
+               for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
+               {
+                       /* is a socket managed by us */
+                       if( FD_ISSET(cur_fd, &read_fds) )
+                       {
+                               /* is one of our listen sockets */
+                               if( FD_ISSET(cur_fd, &serv_fds) )
+                               {
+                                       /* handle new connections */
+                                       if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
+                                       {
+                                               /* add to global client list */
+                                               if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
+                                               {
+#ifdef HAVE_TLS
+                                                       /* setup client tls context */
+                                                       if( conf->tls )
+                                                               conf->tls_accept(cl);
+#endif
+
+                                                       /* add client socket to global fdset */
+                                                       FD_SET(new_fd, &used_fds);
+                                                       fd_cloexec(new_fd);
+                                                       max_fd = max(max_fd, new_fd);
+                                               }
+
+                                               /* insufficient resources */
+                                               else
+                                               {
+                                                       fprintf(stderr,
+                                                               "uh_client_add(): Cannot allocate memory\n");
+
+                                                       close(new_fd);
+                                               }
+                                       }
+                               }
+
+                               /* is a client socket */
+                               else
+                               {
+                                       if( ! (cl = uh_client_lookup(cur_fd)) )
+                                       {
+                                               /* this should not happen! */
+                                               fprintf(stderr,
+                                                       "uh_client_lookup(): No entry for fd %i!\n",
+                                                       cur_fd);
+
+                                               goto cleanup;
+                                       }
+
+                                       /* parse message header */
+                                       if( (req = uh_http_header_recv(cl)) != NULL )
+                                       {
+                                               /* RFC1918 filtering required? */
+                                               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");
+                                               }
+                                               else
 #ifdef HAVE_LUA
-       /* Lua runtime */
-       lua_State *L = NULL;
+                                               /* Lua request? */
+                                               if( conf->lua_state &&
+                                                   uh_path_match(conf->lua_prefix, req->url) )
+                                               {
+                                                       conf->lua_request(cl, req, conf->lua_state);
+                                               }
+                                               else
 #endif
+                                               /* dispatch request */
+                                               if( (pin = uh_path_lookup(cl, req->url)) != NULL )
+                                               {
+                                                       /* auth ok? */
+                                                       if( uh_auth_check(cl, req, pin) )
+                                                               uh_dispatch_request(cl, req, pin);
+                                               }
+
+                                               /* 404 */
+                                               else
+                                               {
+                                                       /* Try to invoke an error handler */
+                                                       pin = uh_path_lookup(cl, conf->error_handler);
 
+                                                       if( pin && uh_auth_check(cl, req, pin) )
+                                                       {
+                                                               req->redirect_status = 404;
+                                                               uh_dispatch_request(cl, req, pin);
+                                                       }
+                                                       else
+                                                       {
+                                                               uh_http_sendhf(cl, 404, "Not Found",
+                                                                       "No such file or directory");
+                                                       }
+                                               }
+                                       }
+
+#ifdef HAVE_TLS
+                                       /* free client tls context */
+                                       if( conf->tls )
+                                               conf->tls_close(cl);
+#endif
+
+                                       cleanup:
+
+                                       /* close client socket */
+                                       close(cur_fd);
+                                       FD_CLR(cur_fd, &used_fds);
+
+                                       /* remove from global client list */
+                                       uh_client_remove(cur_fd);
+                               }
+                       }
+               }
+       }
+
+#ifdef HAVE_LUA
+       /* destroy the Lua state */
+       if( conf->lua_state != NULL )
+               conf->lua_close(conf->lua_state);
+#endif
+}
+
+
+int main (int argc, char **argv)
+{
        /* master file descriptor list */
        fd_set used_fds, serv_fds, read_fds;
 
        /* working structs */
        struct addrinfo hints;
-       struct http_request *req;
-       struct path_info *pin;
-       struct client *cl;
        struct sigaction sa;
        struct config conf;
 
@@ -433,7 +626,7 @@ int main (int argc, char **argv)
        sigset_t ss;
 
        /* maximum file descriptor number */
-       int new_fd, cur_fd, max_fd = 0;
+       int cur_fd, max_fd = 0;
 
 #ifdef HAVE_TLS
        int tls = 0;
@@ -525,7 +718,7 @@ int main (int argc, char **argv)
 #endif
 
        while( (opt = getopt(argc, argv,
-               "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+               "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:")) > 0
        ) {
                switch(opt)
                {
@@ -658,6 +851,21 @@ int main (int argc, char **argv)
                        case 'x':
                                conf.cgi_prefix = optarg;
                                break;
+
+                       /* interpreter */
+                       case 'i':
+                               if( (optarg[0] == '.') && (port = strchr(optarg, '=')) )
+                               {
+                                       *port++ = 0;
+                                       uh_interpreter_add(optarg, port);
+                               }
+                               else
+                               {
+                                       fprintf(stderr, "Error: Invalid interpreter: %s\n",
+                                               optarg);
+                                       exit(1);
+                               }
+                               break;
 #endif
 
 #ifdef HAVE_LUA
@@ -740,6 +948,7 @@ int main (int argc, char **argv)
 #endif
 #ifdef HAVE_CGI
                                        "       -x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
+                                       "       -i .ext=path    Use interpreter at path for files with the given extension\n"
 #endif
 #if defined(HAVE_CGI) || defined(HAVE_LUA)
                                        "       -t seconds      CGI and Lua script timeout in seconds, default is 60\n"
@@ -830,7 +1039,7 @@ int main (int argc, char **argv)
                        if( ! conf.lua_prefix )
                                conf.lua_prefix = "/lua";
 
-                       L = conf.lua_init(conf.lua_handler);
+                       conf.lua_state = conf.lua_init(conf.lua_handler);
                }
        }
 #endif
@@ -865,166 +1074,13 @@ int main (int argc, char **argv)
                }
        }
 
-       /* backup server descriptor set */
-       used_fds = serv_fds;
-
-       /* loop */
-       while(run)
-       {
-               /* create a working copy of the used fd set */
-               read_fds = used_fds;
-
-               /* sleep until socket activity */
-               if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
-               {
-                       perror("select()");
-                       exit(1);
-               }
-
-               /* run through the existing connections looking for data to be read */
-               for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
-               {
-                       /* is a socket managed by us */
-                       if( FD_ISSET(cur_fd, &read_fds) )
-                       {
-                               /* is one of our listen sockets */
-                               if( FD_ISSET(cur_fd, &serv_fds) )
-                               {
-                                       /* handle new connections */
-                                       if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
-                                       {
-                                               /* add to global client list */
-                                               if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
-                                               {
-#ifdef HAVE_TLS
-                                                       /* setup client tls context */
-                                                       if( conf.tls )
-                                                               conf.tls_accept(cl);
-#endif
-
-                                                       /* add client socket to global fdset */
-                                                       FD_SET(new_fd, &used_fds);
-                                                       fd_cloexec(new_fd);
-                                                       max_fd = max(max_fd, new_fd);
-                                               }
-
-                                               /* insufficient resources */
-                                               else
-                                               {
-                                                       fprintf(stderr,
-                                                               "uh_client_add(): Can not manage more than "
-                                                               "%i client sockets, connection dropped\n",
-                                                               UH_LIMIT_CLIENTS
-                                                       );
-
-                                                       close(new_fd);
-                                               }
-                                       }
-                               }
-
-                               /* is a client socket */
-                               else
-                               {
-                                       if( ! (cl = uh_client_lookup(cur_fd)) )
-                                       {
-                                               /* this should not happen! */
-                                               fprintf(stderr,
-                                                       "uh_client_lookup(): No entry for fd %i!\n",
-                                                       cur_fd);
-
-                                               goto cleanup;
-                                       }
-
-                                       /* parse message header */
-                                       if( (req = uh_http_header_recv(cl)) != NULL )
-                                       {
-                                               /* RFC1918 filtering required? */
-                                               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");
-                                               }
-                                               else
-#ifdef HAVE_LUA
-                                               /* Lua request? */
-                                               if( L && uh_path_match(conf.lua_prefix, req->url) )
-                                               {
-                                                       conf.lua_request(cl, req, L);
-                                               }
-                                               else
-#endif
-                                               /* dispatch request */
-                                               if( (pin = uh_path_lookup(cl, req->url)) != NULL )
-                                               {
-                                                       /* auth ok? */
-                                                       if( uh_auth_check(cl, req, pin) )
-                                                       {
-#ifdef HAVE_CGI
-                                                               if( uh_path_match(conf.cgi_prefix, pin->name) )
-                                                               {
-                                                                       uh_cgi_request(cl, req, pin);
-                                                               }
-                                                               else
-#endif
-                                                               {
-                                                                       uh_file_request(cl, req, pin);
-                                                               }
-                                                       }
-                                               }
-
-                                               /* 404 */
-                                               else
-                                               {
-                                                       /* Try to invoke an error handler */
-                                                       pin = uh_path_lookup(cl, conf.error_handler);
-
-                                                       if( pin && uh_auth_check(cl, req, pin) )
-                                                       {
-                                                               req->redirect_status = 404;
-
-#ifdef HAVE_CGI
-                                                               if( uh_path_match(conf.cgi_prefix, pin->name) )
-                                                               {
-                                                                       uh_cgi_request(cl, req, pin);
-                                                               }
-                                                               else
-#endif
-                                                               {
-                                                                       uh_file_request(cl, req, pin);
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               uh_http_sendhf(cl, 404, "Not Found",
-                                                                       "No such file or directory");
-                                                       }
-                                               }
-                                       }
-
-#ifdef HAVE_TLS
-                                       /* free client tls context */
-                                       if( conf.tls )
-                                               conf.tls_close(cl);
-#endif
-
-                                       cleanup:
-
-                                       /* close client socket */
-                                       close(cur_fd);
-                                       FD_CLR(cur_fd, &used_fds);
-
-                                       /* remove from global client list */
-                                       uh_client_remove(cur_fd);
-                               }
-                       }
-               }
-       }
+       /* server main loop */
+       uh_mainloop(&conf, serv_fds, max_fd);
 
 #ifdef HAVE_LUA
        /* destroy the Lua state */
-       if( L != NULL )
-               conf.lua_close(L);
+       if( conf.lua_state != NULL )
+               conf.lua_close(conf.lua_state);
 #endif
 
        return 0;
index fd2176ebdd9c7a3fc5e4470698e8c6bb2c352b4f..6747b905ff459f7a69ccbc9ec187904b8c226a74 100644 (file)
@@ -28,6 +28,7 @@
 #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 <openssl/ssl.h>
 #endif
 
+/* uClibc... */
+#ifndef SOL_TCP
+#define SOL_TCP        6
+#endif
+
 
 #define UH_LIMIT_MSGHEAD       4096
 #define UH_LIMIT_HEADERS       64
 
-#define UH_LIMIT_LISTENERS     16
 #define UH_LIMIT_CLIENTS       64
-#define UH_LIMIT_AUTHREALMS    8
 
 #define UH_HTTP_MSG_GET                0
 #define UH_HTTP_MSG_HEAD       1
@@ -58,6 +62,7 @@
 
 struct listener;
 struct client;
+struct interpreter;
 struct http_request;
 
 struct config {
@@ -76,6 +81,7 @@ struct config {
 #ifdef HAVE_LUA
        char *lua_prefix;
        char *lua_handler;
+       lua_State *lua_state;
        lua_State * (*lua_init) (const char *handler);
        void (*lua_close) (lua_State *L);
        void (*lua_request) (struct client *cl, struct http_request *req, lua_State *L);
@@ -105,6 +111,7 @@ struct listener {
 #ifdef HAVE_TLS
        SSL_CTX *tls;
 #endif
+       struct listener *next;
 };
 
 struct client {
@@ -117,12 +124,14 @@ struct client {
 #ifdef HAVE_TLS
        SSL *tls;
 #endif
+       struct client *next;
 };
 
 struct auth_realm {
        char path[PATH_MAX];
        char user[32];
        char pass[128];
+       struct auth_realm *next;
 };
 
 struct http_request {
@@ -140,5 +149,13 @@ struct http_response {
        char *headers[UH_LIMIT_HEADERS];
 };
 
+#ifdef HAVE_CGI
+struct interpreter {
+       char path[PATH_MAX];
+       char extn[32];
+       struct interpreter *next;
+};
+#endif
+
 #endif