Rework LuCI build system
[project/luci.git] / libs / luci-lib-nixio / axTLS / httpd / axhttpd.c
diff --git a/libs/luci-lib-nixio/axTLS/httpd/axhttpd.c b/libs/luci-lib-nixio/axTLS/httpd/axhttpd.c
new file mode 100644 (file)
index 0000000..35d36f6
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2007, Cameron Rich
+ * 
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the axTLS project nor the names of its contributors 
+ *   may be used to endorse or promote products derived from this software 
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include "axhttp.h"
+
+struct serverstruct *servers;
+struct connstruct *usedconns;
+struct connstruct *freeconns;
+const char * const server_version = "axhttpd/"AXTLS_VERSION;
+
+static void addtoservers(int sd);
+static int openlistener(int port);
+static void handlenewconnection(int listenfd, int is_ssl);
+static void addconnection(int sd, char *ip, int is_ssl);
+static void ax_chdir(void);
+
+#if defined(CONFIG_HTTP_HAS_CGI)
+struct cgiextstruct *cgiexts;
+static void addcgiext(const char *tp);
+
+#if !defined(WIN32)
+static void reaper(int sigtype) 
+{
+    wait3(NULL, WNOHANG, NULL);
+}
+#endif
+#endif
+
+#ifdef CONFIG_HTTP_VERBOSE  /* should really be in debug mode or something */
+/* clean up memory for valgrind */
+static void sigint_cleanup(int sig)
+{
+    struct serverstruct *sp;
+    struct connstruct *tp;
+
+    while (servers != NULL) 
+    {
+        if (servers->is_ssl)
+            ssl_ctx_free(servers->ssl_ctx);
+
+        sp = servers->next;
+        free(servers);
+        servers = sp;
+    }
+
+    while (freeconns != NULL)
+    {
+        tp = freeconns->next;
+        free(freeconns);
+        freeconns = tp;
+    }
+
+    while (usedconns != NULL)
+    {
+        tp = usedconns->next;
+        free(usedconns);
+        usedconns = tp;
+    }
+
+#if defined(CONFIG_HTTP_HAS_CGI)
+    while (cgiexts)
+    {
+        struct cgiextstruct *cp = cgiexts->next;
+        if (cp == NULL) /* last entry */
+            free(cgiexts->ext);
+        free(cgiexts);
+        cgiexts = cp;
+    }
+#endif
+
+    exit(0);
+}
+
+static void die(int sigtype) 
+{
+    exit(0);
+}
+#endif
+
+int main(int argc, char *argv[]) 
+{
+    fd_set rfds, wfds;
+    struct connstruct *tp, *to;
+    struct serverstruct *sp;
+    int rnum, wnum, active;
+    int i;
+    time_t currtime;
+
+#ifdef WIN32
+    WORD wVersionRequested = MAKEWORD(2, 2);
+    WSADATA wsaData;
+    WSAStartup(wVersionRequested,&wsaData);
+#else
+    signal(SIGPIPE, SIG_IGN);
+#if defined(CONFIG_HTTP_HAS_CGI)
+    signal(SIGCHLD, reaper);
+#endif
+#ifdef CONFIG_HTTP_VERBOSE
+    signal(SIGQUIT, die);
+#endif
+#endif
+
+#ifdef CONFIG_HTTP_VERBOSE
+    signal(SIGTERM, die);
+    signal(SIGINT, sigint_cleanup);
+#endif
+    tdate_init();
+
+    for (i = 0; i < INITIAL_CONNECTION_SLOTS; i++) 
+    {
+        tp = freeconns;
+        freeconns = (struct connstruct *)calloc(1, sizeof(struct connstruct));
+        freeconns->next = tp;
+    }
+
+    if ((active = openlistener(CONFIG_HTTP_PORT)) == -1) 
+    {
+#ifdef CONFIG_HTTP_VERBOSE
+        fprintf(stderr, "ERR: Couldn't bind to port %d\n",
+                CONFIG_HTTP_PORT);
+#endif
+        exit(1);
+    }
+
+    addtoservers(active);
+
+    if ((active = openlistener(CONFIG_HTTP_HTTPS_PORT)) == -1) 
+    {
+#ifdef CONFIG_HTTP_VERBOSE
+        fprintf(stderr, "ERR: Couldn't bind to port %d\n", 
+                CONFIG_HTTP_HTTPS_PORT);
+#endif
+        exit(1);
+    }
+
+    addtoservers(active);
+    servers->ssl_ctx = ssl_ctx_new(CONFIG_HTTP_DEFAULT_SSL_OPTIONS, 
+                                CONFIG_HTTP_SESSION_CACHE_SIZE);
+    servers->is_ssl = 1;
+
+#if defined(CONFIG_HTTP_HAS_CGI)
+    addcgiext(CONFIG_HTTP_CGI_EXTENSIONS);
+#endif
+
+#if defined(CONFIG_HTTP_VERBOSE)
+#if defined(CONFIG_HTTP_HAS_CGI)
+    printf("addcgiext %s\n", CONFIG_HTTP_CGI_EXTENSIONS); 
+#endif
+    printf("%s: listening on ports %d (http) and %d (https)\n", 
+            server_version, CONFIG_HTTP_PORT, CONFIG_HTTP_HTTPS_PORT);
+    TTY_FLUSH();
+#endif
+
+    ax_chdir();
+
+#ifdef CONFIG_HTTP_ENABLE_DIFFERENT_USER
+    {
+        struct passwd *pd = getpwnam(CONFIG_HTTP_USER);
+
+        if (pd != NULL)
+        {
+            int res = setuid(pd->pw_uid);
+            res |= setgid(pd->pw_gid);
+
+#if defined(CONFIG_HTTP_VERBOSE)
+            if (res == 0)
+            {
+                printf("change to '%s' successful\n", CONFIG_HTTP_USER); 
+                TTY_FLUSH();
+            }
+#endif
+        }
+
+    }
+#endif
+
+
+#ifndef WIN32 
+#ifdef CONFIG_HTTP_IS_DAEMON
+    if (fork() > 0)  /* parent will die */
+        exit(0);
+
+    setsid();
+#endif
+#endif
+
+    /* main loop */
+    while (1)
+    {
+        FD_ZERO(&rfds);
+        FD_ZERO(&wfds);
+        rnum = wnum = -1;
+        sp = servers;
+
+        while (sp != NULL)  /* read each server port */
+        {
+            FD_SET(sp->sd, &rfds);
+
+            if (sp->sd > rnum) 
+                rnum = sp->sd;
+            sp = sp->next;
+        }
+
+        /* Add the established sockets */
+        tp = usedconns;
+        currtime = time(NULL);
+
+        while (tp != NULL) 
+        {
+            if (currtime > tp->timeout)     /* timed out? Kill it. */
+            {
+                to = tp;
+                tp = tp->next;
+                removeconnection(to);
+                continue;
+            }
+
+            if (tp->state == STATE_WANT_TO_READ_HEAD) 
+            {
+                FD_SET(tp->networkdesc, &rfds);
+                if (tp->networkdesc > rnum) 
+                    rnum = tp->networkdesc;
+            }
+
+            if (tp->state == STATE_WANT_TO_SEND_HEAD) 
+            {
+                FD_SET(tp->networkdesc, &wfds);
+                if (tp->networkdesc > wnum) 
+                    wnum = tp->networkdesc;
+            }
+
+            if (tp->state == STATE_WANT_TO_READ_FILE) 
+            {
+                FD_SET(tp->filedesc, &rfds);
+                if (tp->filedesc > rnum) 
+                    rnum = tp->filedesc;
+            }
+
+            if (tp->state == STATE_WANT_TO_SEND_FILE) 
+            {
+                FD_SET(tp->networkdesc, &wfds);
+                if (tp->networkdesc > wnum) 
+                    wnum = tp->networkdesc;
+            }
+
+#if defined(CONFIG_HTTP_DIRECTORIES)
+            if (tp->state == STATE_DOING_DIR) 
+            {
+                FD_SET(tp->networkdesc, &wfds);
+                if (tp->networkdesc > wnum) 
+                    wnum = tp->networkdesc;
+            }
+#endif
+            tp = tp->next;
+        }
+
+        active = select(wnum > rnum ? wnum+1 : rnum+1,
+                rnum != -1 ? &rfds : NULL, 
+                wnum != -1 ? &wfds : NULL,
+                NULL, NULL);
+
+        /* New connection? */
+        sp = servers;
+        while (active > 0 && sp != NULL) 
+        {
+            if (FD_ISSET(sp->sd, &rfds)) 
+            {
+                handlenewconnection(sp->sd, sp->is_ssl);
+                active--;
+            }
+
+            sp = sp->next;
+        }
+
+        /* Handle the established sockets */
+        tp = usedconns;
+
+        while (active > 0 && tp != NULL) 
+        {
+            to = tp;
+            tp = tp->next;
+
+            if (to->state == STATE_WANT_TO_READ_HEAD &&
+                        FD_ISSET(to->networkdesc, &rfds)) 
+            {
+                active--;
+#if defined(CONFIG_HTTP_HAS_CGI)
+                if (to->post_state)
+                    read_post_data(to);
+                else
+#endif
+                    procreadhead(to);
+            } 
+
+            if (to->state == STATE_WANT_TO_SEND_HEAD &&
+                        FD_ISSET(to->networkdesc, &wfds)) 
+            {
+                active--;
+                procsendhead(to);
+            } 
+
+            if (to->state == STATE_WANT_TO_READ_FILE && 
+                        FD_ISSET(to->filedesc, &rfds)) 
+            {
+                active--;
+                procreadfile(to);
+            } 
+
+            if (to->state == STATE_WANT_TO_SEND_FILE && 
+                        FD_ISSET(to->networkdesc, &wfds)) 
+            {
+                active--;
+                procsendfile(to);
+            }
+
+#if defined(CONFIG_HTTP_DIRECTORIES)
+            if (to->state == STATE_DOING_DIR &&
+                        FD_ISSET(to->networkdesc, &wfds)) 
+            {
+                active--;
+                procdodir(to);
+            }
+#endif
+        }
+    }
+
+    return 0;
+}
+
+#if defined(CONFIG_HTTP_HAS_CGI)
+static void addcgiext(const char *cgi_exts)
+{
+    char *cp = strdup(cgi_exts);
+
+    /* extenstions are comma separated */
+    do 
+    {
+        struct cgiextstruct *ex = (struct cgiextstruct *)
+                            malloc(sizeof(struct cgiextstruct));
+        ex->ext = cp;
+        ex->next = cgiexts;
+        cgiexts = ex;
+        if ((cp = strchr(cp, ',')) != NULL)
+            *cp++ = 0;
+    } while (cp != NULL);
+}
+#endif
+
+static void addtoservers(int sd) 
+{
+    struct serverstruct *tp = (struct serverstruct *)
+                            calloc(1, sizeof(struct serverstruct));
+    tp->next = servers;
+    tp->sd = sd;
+    servers = tp;
+}
+
+#ifdef HAVE_IPV6
+static void handlenewconnection(int listenfd, int is_ssl) 
+{
+    struct sockaddr_in6 their_addr;
+    int tp = sizeof(their_addr);
+    char ipbuf[100];
+    int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp);
+
+    if (tp == sizeof(struct sockaddr_in6)) 
+        inet_ntop(AF_INET6, &their_addr.sin6_addr, ipbuf, sizeof(ipbuf));
+    else if (tp == sizeof(struct sockaddr_in)) 
+        inet_ntop(AF_INET, &(((struct sockaddr_in *)&their_addr)->sin_addr),
+                ipbuf, sizeof(ipbuf));
+    else 
+        *ipbuf = '\0';
+
+    addconnection(connfd, ipbuf, is_ssl);
+}
+
+#else
+static void handlenewconnection(int listenfd, int is_ssl) 
+{
+    struct sockaddr_in their_addr;
+    socklen_t tp = sizeof(struct sockaddr_in);
+    int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp);
+    addconnection(connfd, inet_ntoa(their_addr.sin_addr), is_ssl);
+}
+#endif
+
+static int openlistener(int port) 
+{
+    int sd;
+#ifdef WIN32
+    char tp = 1;
+#else
+    int tp = 1;
+#endif
+#ifndef HAVE_IPV6
+    struct sockaddr_in my_addr;
+
+    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
+        return -1;
+
+    memset(&my_addr, 0, sizeof(my_addr));
+    my_addr.sin_family = AF_INET;
+    my_addr.sin_port = htons((short)port);
+    my_addr.sin_addr.s_addr = INADDR_ANY;
+#else
+    struct sockaddr_in6 my_addr;
+
+    if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) 
+        return -1;
+
+    memset(&my_addr, 0, sizeof(my_addr));
+    my_addr.sin6_family = AF_INET6;
+    my_addr.sin6_port = htons(port);
+    my_addr.sin6_addr.s_addr = INADDR_ANY;
+#endif
+
+    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &tp, sizeof(tp));
+    if (bind(sd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
+    {
+        close(sd);
+        return -1;
+    }
+
+    listen(sd, BACKLOG);
+    return sd;
+}
+
+/* Wrapper function for strncpy() that guarantees
+   a null-terminated string. This is to avoid any possible
+   issues due to strncpy()'s behaviour.
+ */
+char *my_strncpy(char *dest, const char *src, size_t n) 
+{
+    strncpy(dest, src, n);
+    dest[n-1] = '\0';
+    return dest;
+}
+
+int isdir(const char *tpbuf) 
+{
+    struct stat st;
+    char path[MAXREQUESTLENGTH];
+    strcpy(path, tpbuf);
+
+#ifdef WIN32        /* win32 stat() can't handle trailing '\' */
+    if (path[strlen(path)-1] == '\\')
+        path[strlen(path)-1] = 0;
+#endif
+
+    if (stat(path, &st) == -1) 
+        return 0;
+
+    if ((st.st_mode & S_IFMT) == S_IFDIR) 
+        return 1;
+
+    return 0;
+}
+
+static void addconnection(int sd, char *ip, int is_ssl) 
+{
+    struct connstruct *tp;
+
+    /* Get ourselves a connstruct */
+    if (freeconns == NULL) 
+        tp = (struct connstruct *)calloc(1, sizeof(struct connstruct));
+    else 
+    {
+        tp = freeconns;
+        freeconns = tp->next;
+    }
+
+    /* Attach it to the used list */
+    tp->next = usedconns;
+    usedconns = tp;
+    tp->networkdesc = sd;
+
+    if (is_ssl)
+        tp->ssl = ssl_server_new(servers->ssl_ctx, sd);
+
+    tp->is_ssl = is_ssl;
+    tp->filedesc = -1;
+#if defined(CONFIG_HTTP_HAS_DIRECTORIES)
+    tp->dirp = NULL;
+#endif
+    *tp->actualfile = '\0';
+    *tp->filereq = '\0';
+    tp->state = STATE_WANT_TO_READ_HEAD;
+    tp->reqtype = TYPE_GET;
+    tp->close_when_done = 0;
+    tp->timeout = time(NULL) + CONFIG_HTTP_TIMEOUT;
+#if defined(CONFIG_HTTP_HAS_CGI)
+    strcpy(tp->remote_addr, ip);
+#endif
+}
+
+void removeconnection(struct connstruct *cn) 
+{
+    struct connstruct *tp;
+    int shouldret = 0;
+
+    tp = usedconns;
+
+    if (tp == NULL || cn == NULL) 
+        shouldret = 1;
+    else if (tp == cn) 
+        usedconns = tp->next;
+    else 
+    {
+        while (tp != NULL) 
+        {
+            if (tp->next == cn) 
+            {
+                tp->next = (tp->next)->next;
+                shouldret = 0;
+                break;
+            }
+
+            tp = tp->next;
+            shouldret = 1;
+        }
+    }
+
+    if (shouldret) 
+        return;
+
+    /* If we did, add it to the free list */
+    cn->next = freeconns;
+    freeconns = cn;
+
+    /* Close it all down */
+    if (cn->networkdesc != -1) 
+    {
+        if (cn->is_ssl) 
+        {
+            ssl_free(cn->ssl);
+            cn->ssl = NULL;
+        }
+
+        SOCKET_CLOSE(cn->networkdesc);
+    }
+
+    if (cn->filedesc != -1) 
+        close(cn->filedesc);
+
+#if defined(CONFIG_HTTP_HAS_DIRECTORIES)
+    if (cn->dirp != NULL) 
+#ifdef WIN32
+        FindClose(cn->dirp);
+#else
+        closedir(cn->dirp);
+#endif
+#endif
+}
+
+/*
+ * Change directories one way or the other.
+ */
+static void ax_chdir(void)
+{
+    static char *webroot = CONFIG_HTTP_WEBROOT;
+
+    if (chdir(webroot))
+    {
+#ifdef CONFIG_HTTP_VERBOSE
+        fprintf(stderr, "'%s' is not a directory\n", webroot);
+#endif
+        exit(1);
+    }
+}
+