diff --git a/Makefile b/Makefile index 1ebdcfc01..f4c2c86e8 100644 --- a/Makefile +++ b/Makefile @@ -34,8 +34,7 @@ RELEASE=axTLS-$(VERSION) # standard version target: $(MAKE) -C ssl -ifdef CONFIG_AWHTTPD - $(MAKE) -C httpd untar_web_server +ifdef CONFIG_AXHTTPD $(MAKE) -C httpd endif ifdef CONFIG_BINDINGS @@ -72,12 +71,12 @@ install: $(PREFIX) all chmod 755 $(PREFIX)/lib/libax* -@install -m 755 $(STAGE)/ax* $(PREFIX)/bin > /dev/null 2>&1 -@install -m 755 $(STAGE)/axtlsp.pm `perl -e 'use Config; print $$Config{installarchlib};'` > /dev/null 2>&1 - -@install -m 755 $(STAGE)/awhttpd* $(PREFIX)/bin > /dev/null 2>&1 + -@install -m 755 $(STAGE)/axhttpd* $(PREFIX)/bin > /dev/null 2>&1 installclean: -@rm $(PREFIX)/lib/libax* > /dev/null 2>&1 -@rm $(PREFIX)/bin/ax* > /dev/null 2>&1 - -@rm $(PREFIX)/bin/awhttpd* > /dev/null 2>&1 + -@rm $(PREFIX)/bin/axhttpd* > /dev/null 2>&1 -@rm `perl -e 'use Config; print $$Config{installarchlib};'`/axtlsp.pm > /dev/null 2>&1 test: diff --git a/README b/README index 2e4d492bb..db35888d7 100644 --- a/README +++ b/README @@ -9,7 +9,7 @@ This is a guide to get a small SSL web-server up and running quickly. ######################################################################## The axTLS project is an SSL client/server library using the TLSv1 protocol. It is designed to be small and fast, and is suited to embedded projects. A web -server is included (called Anti-Web). +server is included. The web server + SSL library is around 50-60kB and is configurable for features or size. @@ -32,8 +32,8 @@ the extracted directory and typing: Select your platform type, save the configuration, exit, and then type "make" again. -If all goes well, you should end up with an executable called "awhttpd" (or -awhttpd.exe) in the _stage directory. +If all goes well, you should end up with an executable called "axhttpd" (or +axhttpd.exe) in the _stage directory. To play with all the various axTLS options, type: @@ -47,7 +47,7 @@ Save the new configuration and rebuild. To run it, go to the _stage directory, and type (as superuser): -> awhttpd +> axhttpd And then point your browser at: @@ -61,9 +61,6 @@ http://127.0.0.1 to see the same page unencrypted. -See the README in the httpd directory from more configuration information on -Anti-Web. - ######################################################################## # The axssl utilities ######################################################################## diff --git a/config/Config.in b/config/Config.in index 14de5c8f2..de1808d8e 100644 --- a/config/Config.in +++ b/config/Config.in @@ -107,11 +107,11 @@ config CONFIG_EXTRA_LDFLAGS_OPTIONS endmenu source ssl/Config.in -config CONFIG_AWHTTPD +config CONFIG_AXHTTPD bool "Enable HTTP/HTTPS Web Server" default y help - Build the AWHTTPD web server + Build the AXHTTPD web server source httpd/Config.in source bindings/Config.in diff --git a/config/JMeter.jmx b/config/JMeter.jmx index 5070760b0..f62c03f0e 100755 --- a/config/JMeter.jmx +++ b/config/JMeter.jmx @@ -1,6 +1,6 @@ - + diff --git a/config/awhttpd.aip b/config/axhttpd.aip similarity index 97% rename from config/awhttpd.aip rename to config/axhttpd.aip index a460ecbf2..a1806c0c0 100755 --- a/config/awhttpd.aip +++ b/config/axhttpd.aip @@ -37,7 +37,7 @@ - + @@ -59,12 +59,12 @@ - + - + @@ -199,7 +199,7 @@ - + diff --git a/config/linuxconfig b/config/linuxconfig index 79a3591a3..44e5226ea 100644 --- a/config/linuxconfig +++ b/config/linuxconfig @@ -43,9 +43,8 @@ CONFIG_USE_DEV_URANDOM=y # CONFIG_WIN32_USE_CRYPTO_LIB is not set # CONFIG_PERFORMANCE_TESTING is not set # CONFIG_SSL_TEST is not set -# CONFIG_AWHTTPD is not set +# CONFIG_AXHTTPD is not set # CONFIG_HTTP_STATIC_BUILD is not set -# CONFIG_HTTP_HAS_SSL is not set CONFIG_HTTP_HTTPS_PORT=0 CONFIG_HTTP_SESSION_CACHE_SIZE=0 CONFIG_HTTP_WEBROOT="" diff --git a/config/win32config b/config/win32config index fe94084f5..ed7efe96f 100644 --- a/config/win32config +++ b/config/win32config @@ -47,13 +47,12 @@ CONFIG_SSL_MAX_CERTS=2 CONFIG_WIN32_USE_CRYPTO_LIB=y # CONFIG_PERFORMANCE_TESTING is not set # CONFIG_SSL_TEST is not set -CONFIG_AWHTTPD=y +CONFIG_AXHTTPD=y # # Awhttpd Configuration # # CONFIG_HTTP_STATIC_BUILD is not set -CONFIG_HTTP_HAS_SSL=y CONFIG_HTTP_HTTPS_PORT=443 CONFIG_HTTP_WEBROOT="www" CONFIG_HTTP_PORT=80 diff --git a/httpd/Config.in b/httpd/Config.in index 73e304cab..f720e4eea 100644 --- a/httpd/Config.in +++ b/httpd/Config.in @@ -3,26 +3,19 @@ # see scripts/config/Kconfig-language.txt # -menu "Awhttpd Configuration" -depends on CONFIG_AWHTTPD +menu "Axhttpd Configuration" +depends on CONFIG_AXHTTPD config CONFIG_HTTP_STATIC_BUILD bool "Static Build" default n help - Select y if you want awhttp to be a static build (i.e. don't use the + Select y if you want axhttp to be a static build (i.e. don't use the axtls shared library or dll). -config CONFIG_HTTP_HAS_SSL - bool "Use SSL" - default y - help - Build the HTTP server with SSL capability - config CONFIG_HTTP_HTTPS_PORT int "HTTPS port" default 443 - depends on CONFIG_HTTP_HAS_SSL help The port number of the HTTPS server. @@ -31,7 +24,6 @@ config CONFIG_HTTP_HTTPS_PORT config CONFIG_HTTP_SESSION_CACHE_SIZE int "SSL session cache size" default 5 - depends on CONFIG_HTTP_HAS_SSL help The size of the SSL session cache. @@ -44,7 +36,7 @@ config CONFIG_HTTP_WEBROOT default "../www" if !CONFIG_PLATFORM_WIN32 default "..\\www" if CONFIG_PLATFORM_WIN32 help - The location of the web root in relation to awhttpd. This is + The location of the web root in relation to axhttpd. This is the directory where index.html lives. config CONFIG_HTTP_PORT @@ -72,7 +64,7 @@ config CONFIG_HTTP_CGI_EXTENSION default ".php" depends on CONFIG_HTTP_HAS_CGI help - Tell awhhtp what file extension is used for CGI + Tell axhhtp what file extension is used for CGI config CONFIG_HTTP_DIRECTORIES bool "Enable Directory Listing" @@ -101,14 +93,14 @@ config CONFIG_HTTP_VERBOSE default y if CONFIG_SSL_FULL_MODE default n if !CONFIG_SSL_FULL_MODE help - Enable extra statements used when using awhttpd. + Enable extra statements used when using axhttpd. config CONFIG_HTTP_IS_DAEMON bool "Run as a daemon" default n depends on !CONFIG_PLATFORM_WIN32 help - Run awhttpd as a background process. + Run axhttpd as a background process. Does not work under Win32 diff --git a/httpd/Makefile b/httpd/Makefile index 22b8d0ff2..b676c8f9e 100644 --- a/httpd/Makefile +++ b/httpd/Makefile @@ -24,9 +24,9 @@ include ../config/makefile.conf ifndef CONFIG_PLATFORM_WIN32 ifdef CONFIG_PLATFORM_CYGWIN -TARGET=../$(STAGE)/awhttpd.exe +TARGET=../$(STAGE)/axhttpd.exe else -TARGET=../$(STAGE)/awhttpd +TARGET=../$(STAGE)/axhttpd endif ifdef CONFIG_HTTP_STATIC_BUILD @@ -38,7 +38,7 @@ endif CFLAGS += -I../ssl else # win32 build -TARGET=../$(STAGE)/awhttpd.exe +TARGET=../$(STAGE)/axhttpd.exe ifdef CONFIG_HTTP_STATIC_BUILD LIBS=../$(STAGE)/axtls.static.lib ..\\config\\axtls.res @@ -51,31 +51,16 @@ ifndef CONFIG_AWHTTPD web_server: else -untar_web_server: awhttpd/Makefile - -awhttpd/Makefile: - tar xvf awhttpd-3.0.7.tar - cat awhttpd.patch | patch -p0 - web_server : $(TARGET) OBJ= \ - cgi.o \ conn.o \ main.o \ net.o \ proc.o \ socket.o \ - errors.o \ misc.o \ - urldecode.o \ - mime_types.o \ - index.o \ - urlencode.o \ - permcheck.o - -%.o : awhttpd/%.c ../config/.config - $(CC) -c $(CFLAGS) $< + mime_types.o ifndef CONFIG_PLATFORM_WIN32 @@ -91,10 +76,6 @@ endif endif else # Win32 -OBJ:=$(OBJ:.o=.obj) -%.obj : awhttpd/%.c - $(CC) $(CFLAGS) $< - $(TARGET): $(OBJ) ifdef CONFIG_HTTP_NO_SSL $(LD) $(LDFLAGS) /out:$@ $(OBJ) @@ -106,5 +87,4 @@ endif # CONFIG_AWHTTPD clean:: -@rm -f $(TARGET)* - -@rm -fr awhttpd diff --git a/httpd/README b/httpd/README new file mode 100644 index 000000000..cb42bfd66 --- /dev/null +++ b/httpd/README @@ -0,0 +1,6 @@ + +axhttpd is a small embedded web server using the axTLS library. + +It is based quite closely on the web server written by Doug Currie (original +version is here: http://www.hcsw.org/awhttpd). + diff --git a/httpd/awhttpd-3.0.7.tar b/httpd/awhttpd-3.0.7.tar deleted file mode 100644 index 79105fbe1..000000000 Binary files a/httpd/awhttpd-3.0.7.tar and /dev/null differ diff --git a/httpd/awhttpd.patch b/httpd/awhttpd.patch deleted file mode 100644 index 59eb7eb81..000000000 --- a/httpd/awhttpd.patch +++ /dev/null @@ -1,2371 +0,0 @@ -diff -Naur awhttpd/aw3.h axTLS/httpd/awhttpd/aw3.h ---- awhttpd/aw3.h 2005-01-23 13:17:14.000000000 +1000 -+++ axTLS/httpd/awhttpd/aw3.h 2006-11-15 15:09:14.196258200 +1000 -@@ -7,17 +7,16 @@ - */ - - --#include --#include --#include --#include --#include -+#include "os_port.h" -+#include "ssl.h" - - - - #define BACKLOG 15 - #define VERSION "3.0.7" -+#ifdef CONFIG_HTTP_HAS_IPV6 - #define HAVE_IPV6 -+#endif - - #define MAXFILEPATH 1024 - #define MAXIPLEN 45 -@@ -26,6 +25,7 @@ - #define BLOCKSIZE 4096 - - #define INITIAL_CONNECTION_SLOTS 10 -+#define CONFIG_HTTP_DEFAULT_SSL_OPTIONS 0 - - #define STATE_WANT_TO_READ_HEAD 1 - #define STATE_WANT_TO_SEND_HEAD 2 -@@ -37,7 +37,6 @@ - #define TYPE_HEAD 1 - #define TYPE_POST 2 - -- - struct connstruct { - struct connstruct *next; - -@@ -46,29 +45,43 @@ - - int networkdesc; - int filedesc; -- DIR *dirp; - -- int timeout; -+#if defined(CONFIG_HTTP_DIRECTORIES) -+#ifdef WIN32 -+ HANDLE dirp; -+ WIN32_FIND_DATA file_data; -+#else -+ DIR *dirp; -+#endif -+#endif - -+ time_t timeout; - char ip[MAXIPLEN]; -- - char actualfile[MAXREQUESTLENGTH]; - char filereq[MAXREQUESTLENGTH]; -+#if defined(CONFIG_HTTP_HAS_CGI) - char cgiargs[MAXREQUESTLENGTH]; - char cgiscriptinfo[MAXREQUESTLENGTH]; - char cgipathinfo[MAXREQUESTLENGTH]; -+#endif - char virtualhostreq[MAXREQUESTLENGTH]; - - int numbytes; -- long offset; - char databuf[BLOCKSIZE]; - -+ unsigned char is_ssl; -+ unsigned char close_when_done; -+ unsigned char modified_since; - }; - - - struct serverstruct { - struct serverstruct *next; - int sd; -+ int is_ssl; -+#ifdef CONFIG_HTTP_HAS_SSL -+ SSLCTX *ssl_ctx; -+#endif - }; - - -@@ -96,28 +109,13 @@ - - // Conf global prototypes - --extern int usevirtualhosts; - extern char *webroot; - extern int allowdirectorylisting; - extern int allowcgi; - extern int permcheck; --extern int maxusers; --extern int usertimeout; --extern int initialslots; --extern char *quote; --extern int initialslots; -- --extern int numusers; -- -- --// Useful macros --#define istimedout(tp,ct) ((ct) > (tp)->timeout) --#define updatetimeout(tp,ct) ((tp)->timeout = (ct)+usertimeout) -- -- - - // conn.c prototypes --void addconnection(int sd, char *ip); -+void addconnection(int sd, char *ip, int is_ssl); - void removeconnection(struct connstruct *cn); - - -@@ -129,49 +127,47 @@ - void procsendhead(struct connstruct *cn); - void procreadfile(struct connstruct *cn); - void procsendfile(struct connstruct *cn); -+int special_write(struct connstruct *cn, const uint8_t *buf, size_t count); - - - // net.c prototypes - void addtoservers(int sd); --void selectloop(); -+void selectloop(void); - - - // socket.c prototypes --int pollsocket(int sd, long ustimeout); --void handlenewconnection(int listenfd); -+void handlenewconnection(int listenfd, int is_ssl); - int openlistener(int port); - int openlistener6(int port); - - - // errors.c prototypes --void send505(int sd, char *reason); - void send404(struct connstruct *cn); - void send301(struct connstruct *cn); - - - // misc.c prototypes --void nada(); --void die(); --void reaper(); -+void nada(int sigtype); -+void die(int sigtype); -+void reaper(int sigtype); - void stripcrlf(char *p); - char *my_strncpy(char *dest, const char *src, size_t n); - #ifndef __HAVE_ARCH_STRNLEN - size_t strnlen ( const char * str, size_t maxlen ); - #endif - int iscgi(char *fn); --int split(char *tp, char *sp[], int maxwords, char sc); --int confsplit(char *tp, char *sp[], int maxwords); -+void split(char *tp, char *sp[], int maxwords, char sc); - int sanitizefile(char *buf); - int sanitizehost(char *buf); - void buildactualfile(struct connstruct *cn); - int issockwriteable(int sd); - int isdir(char *name); --void status(); - int trycgi_withpathinfo(struct connstruct *cn); - - - // mime_types.c prototypes --char *getmimetype(char *fn); -+void mime_init(void); -+const char *getmimetype(const char *fn); - - - // urldecode.c prototypes -@@ -188,7 +184,6 @@ - - - // conf.c prototypes --void defaultconfvals(); - void procconf(char *filename); - - -@@ -202,4 +197,4 @@ - - - // main.c prototypes --void initlists(); -+void initlists(void); -diff -Naur awhttpd/cgi.c axTLS/httpd/awhttpd/cgi.c ---- awhttpd/cgi.c 2005-06-04 14:09:52.000000000 +1000 -+++ axTLS/httpd/awhttpd/cgi.c 2006-11-15 15:09:14.211883700 +1000 -@@ -7,93 +7,46 @@ - */ - - --#include - #include - #include --#include -+#include - - #include "aw3.h" - - - -+#if defined(CONFIG_HTTP_HAS_CGI) - void addcgiext(char *tp) { - - struct cgiextstruct *ex; - - ex = (struct cgiextstruct *) malloc(sizeof(struct cgiextstruct)); -- if (ex == NULL) { -- fprintf(stderr, "Serious memory error...\n"); -- exit(0); -- } -- - ex->ext = strdup(tp); -- if (ex->ext == NULL) { -- fprintf(stderr, "Serious memory error...\n"); -- exit(0); -- } -- - ex->next = cgiexts; - cgiexts = ex; -- -- return; -- --} -- -- -- --void gensysenv(struct connstruct *cn) { -- -- #ifndef LIMITEDCGI -- -- char buf[1024]; -- -- setenv("REMOTE_ADDR", cn->ip, 1); -- -- snprintf(buf, sizeof(buf), "%d", numusers); -- setenv("AW_NUMUSERS", buf, 1); -- -- setenv("AW_VERSION", VERSION, 1); -- -- setenv("AW_QUOTE", quote, 1); -- --/* Commented this out because (and this is ridiculous) PHP -- doesn't seem to work with this variable specified -- */ --/* -- snprintf ( buf, sizeof(buf), "Anti-Web V%s (%s)", VERSION, quote ); -- setenv("SERVER_SOFTWARE", buf, 1); --*/ -- -- setenv("SCRIPT_NAME", cn->cgiscriptinfo, 1); -- -- setenv("PATH_INFO", cn->cgipathinfo, 1); -- -- setenv("QUERY_STRING", cn->cgiargs, 1); -- -- return; -- -- #endif -- - } - -- -- - void proccgi(struct connstruct *cn, int has_pathinfo) { - -- int tpipe[2], fv; -- char *myargs[3]; -+ int tpipe[2]; -+ char *myargs[5]; - char buf[MAXREQUESTLENGTH]; -- -- snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: Anti-Web V%s (%s)\n%s", -- VERSION, -- quote, (cn->reqtype == TYPE_HEAD) ? "\n" : ""); -- write(cn->networkdesc, buf, strlen(buf)); -+#ifdef WIN32 -+ int tmp_stdout; -+#else -+ int fv; -+#endif -+ -+ snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: Anti-Web V%s\n%s", -+ VERSION, (cn->reqtype == TYPE_HEAD) ? "\n" : ""); -+ special_write(cn, buf, strlen(buf)); - - if (cn->reqtype == TYPE_HEAD) { - removeconnection(cn); - return; - } - -+#ifndef WIN32 - if (pipe(tpipe) == -1) { - removeconnection(cn); - return; -@@ -108,7 +61,8 @@ - return; - } - -- if (fv != 0) { -+ if (fv != 0) -+ { - // Close the write descriptor - close(tpipe[1]); - cn->filedesc = tpipe[0]; -@@ -132,19 +86,62 @@ - close(tpipe[1]); - - myargs[0] = cn->actualfile; -- myargs[1] = strdup(cn->cgiargs); -+ myargs[1] = cn->cgiargs; - myargs[2] = NULL; - -- if (!has_pathinfo) -- { -- my_strncpy(cn->cgipathinfo, "/", MAXREQUESTLENGTH); -- my_strncpy(cn->cgiscriptinfo, cn->filereq, MAXREQUESTLENGTH); -- } -- -- gensysenv(cn); -+ if (!has_pathinfo) { -+ my_strncpy(cn->cgipathinfo, "/", MAXREQUESTLENGTH); -+ my_strncpy(cn->cgiscriptinfo, cn->filereq, MAXREQUESTLENGTH); -+ } - - execv(cn->actualfile, myargs); -+#else /* WIN32 */ -+ if (_pipe(tpipe, 4096, O_BINARY| O_NOINHERIT) == -1) { -+ removeconnection(cn); -+ return; -+ } - -- exit(0); -+ myargs[0] = "sh"; -+ myargs[1] = "-c"; -+ myargs[2] = cn->actualfile; -+ myargs[3] = cn->cgiargs; -+ myargs[4] = NULL; -+ -+ /* convert all the forward slashes to back slashes */ -+ { -+ char *t = myargs[2]; -+ while ((t = strchr(t, '\\'))) -+ { -+ *t++ = '/'; -+ } -+ } -+ -+ tmp_stdout = _dup(_fileno(stdout)); -+ _dup2(tpipe[1], _fileno(stdout)); -+ close(tpipe[1]); -+ -+ /* change to suit execution method */ -+ if (spawnl(P_NOWAIT, "c:\\Program Files\\cygwin\\bin\\sh.exe", -+ myargs[0], myargs[1], myargs[2], myargs[3], myargs[4]) == -1) { -+ removeconnection(cn); -+ return; -+ } -+ -+ _dup2(tmp_stdout, _fileno(stdout)); -+ close(tmp_stdout); -+ cn->filedesc = tpipe[0]; -+ cn->state = STATE_WANT_TO_READ_FILE; -+ -+ for (;;) -+ { -+ procreadfile(cn); - -+ if (cn->filedesc == -1) -+ break; -+ -+ procsendfile(cn); -+ usleep(200000); /* don't know why this delay makes it work (yet) */ -+ } -+#endif - } -+#endif /* CONFIG_HTTP_HAS_CGI */ -diff -Naur awhttpd/conf.c axTLS/httpd/awhttpd/conf.c ---- awhttpd/conf.c 2005-06-04 14:09:52.000000000 +1000 -+++ axTLS/httpd/awhttpd/conf.c 1970-01-01 10:00:00.000000000 +1000 -@@ -1,265 +0,0 @@ --/* Anti-Web HTTPD */ --/* Hardcore Software */ --/* --This software is Copyright (C) 2001-2004 By Hardcore Software and --others. The software is distributed under the terms of the GNU General --Public License. See the file 'COPYING' for more details. --*/ -- -- --#include --#include --#include --#include --#include --#include --#include -- --#include "aw3.h" -- -- -- --// CONF GLOBALS: -- --int usevirtualhosts; --char *webroot; --int allowdirectorylisting; --int allowcgi; --int permcheck; --int maxusers; --int usertimeout; --int initialslots; --char *quote; -- --int numusers; -- -- -- --void defaultconfvals() { -- -- usevirtualhosts = 0; -- allowdirectorylisting = 0; -- allowcgi = 0; -- permcheck = 0; -- maxusers = 500; -- usertimeout = 5; -- initialslots = 10; -- quote = "Fear and loathing on the WWW"; -- -- // Not really conf stuff: -- numusers = 0; -- -- return; -- --} -- -- --void procconf(char *filename) { -- -- FILE *fp; -- char buf[MAXREQUESTLENGTH]; -- char *segs[10]; -- int tp, err=0, warn=0; -- -- usevirtualhosts = 1; -- -- fp = fopen(filename, "r"); -- -- if (fp == NULL) { -- fprintf(stderr, "ERROR: Unable to open conf file '%s'\n", filename); -- exit(1); -- } -- -- -- while (fgets(buf, sizeof(buf), fp) != NULL) { -- stripcrlf(buf); -- -- confsplit(buf, segs, 10); -- -- if (segs[0] == NULL) continue; -- -- if (segs[1] == NULL) { -- fprintf(stderr, "ERR: Unknown command in '%s': '%s'\n", filename, segs[0]); -- err++; -- continue; -- } -- -- -- if (strcasecmp(segs[0], "listen") == 0) { -- if (isdigit(*segs[1])) { -- if ((tp=openlistener(atoi(segs[1]))) == -1) { -- fprintf(stderr, "ERR: Couldn't bind to port %d (IPv4)\n", atoi(segs[1])); -- err++; -- continue; -- } -- -- addtoservers(tp); -- continue; -- } -- } -- -- if (strcasecmp(segs[0], "listen6") == 0) { -- #ifdef HAVE_IPV6 -- if (isdigit(*segs[1])) { -- if ((tp=openlistener6(atoi(segs[1]))) == -1) { -- fprintf(stderr, "ERR: Couldn't bind to port %d (IPv6)\n", atoi(segs[1])); -- err++; -- continue; -- } -- -- addtoservers(tp); -- continue; -- } -- #else -- fprintf(stderr, "ERR: AW was compiled without IPv6 support!\n"); -- err++; -- continue; -- #endif -- } -- -- if (strcasecmp(segs[0], "maxusers") == 0) { -- maxusers = tp = atoi(segs[1]); -- if (tp < 1) { -- fprintf(stderr, "ERR: Bad value for maxusers\n"); -- err++; -- } -- if (tp < 10 || tp > 10000) { -- fprintf(stderr, "WARN: Value for maxusers (%d) is not withing the recommended range\n", tp); -- warn++; -- } -- continue; -- } -- -- if (strcasecmp(segs[0], "usertimeout") == 0) { -- usertimeout = tp = atoi(segs[1]); -- if (tp < 1) { -- fprintf(stderr, "ERR: Bad value for usertimeout\n"); -- err++; -- } -- if (tp > 100) { -- fprintf(stderr, "WARN: Value for usertimeout (%d) is not withing the recommended range\n", tp); -- warn++; -- } -- continue; -- } -- -- if (strcasecmp(segs[0], "initialslots") == 0) { -- initialslots = tp = atoi(segs[1]); -- if (tp < 1) { -- fprintf(stderr, "ERR: Bad value for initialslots\n"); -- err++; -- } -- continue; -- } -- -- if (strcasecmp(segs[0], "directorylisting") == 0) { -- if (strcasecmp(segs[1], "on") == 0) allowdirectorylisting = 1; -- else if (strcasecmp(segs[1], "off") == 0) allowdirectorylisting = 0; -- else { -- fprintf(stderr, "ERR: Need on or off for directorylisting\n"); -- err++; -- } -- continue; -- } -- -- if (strcasecmp(segs[0], "cgi") == 0) { -- if (strcasecmp(segs[1], "on") == 0) allowcgi = 1; -- else if (strcasecmp(segs[1], "off") == 0) allowcgi = 0; -- else { -- fprintf(stderr, "ERR: Need on or off for cgi\n"); -- err++; -- } -- continue; -- } -- -- if (strcasecmp(segs[0], "cgiext") == 0) { -- if (*(segs[1]) != '.' && *(segs[1]+1) != '\0') { -- fprintf(stderr, "ERR: CGI extensions must start with a period and be at least 2 chars long\n"); -- err++; -- continue; -- } -- addcgiext(segs[1]); -- continue; -- } -- -- if (strcasecmp(segs[0], "addindex") == 0) { -- if (*(segs[1]) == '.') { -- fprintf(stderr, "ERR: Index files can't start with a dot\n"); -- err++; -- continue; -- } -- addindex(segs[1]); -- continue; -- } -- -- if (strcasecmp(segs[0], "permcheck") == 0) { -- if (strcasecmp(segs[1], "on") == 0) permcheck = 1; -- else if (strcasecmp(segs[1], "off") == 0) permcheck = 0; -- else { -- fprintf(stderr, "ERR: Need on or off for permcheck\n"); -- err++; -- } -- continue; -- } -- -- if (strcasecmp(segs[0], "dropto") == 0) { -- struct passwd *bl; -- -- if ((bl = getpwnam(segs[1])) == NULL) { -- fprintf(stderr, "ERR: Unable to look up user '%s' to drop privileges\n", segs[1]); -- err++; -- } else { -- if (setgid(bl->pw_gid) != 0) { -- fprintf(stderr, "WARN: Unable to drop GID to %d\n", bl->pw_gid); -- warn++; -- } -- if (setuid(bl->pw_uid) != 0) { -- fprintf(stderr, "WARN: Unable to drop UID to %d\n", bl->pw_uid); -- warn++; -- } -- } -- continue; -- } -- -- if (strcasecmp(segs[0], "quote") == 0) { -- quote = strdup(segs[1]); -- continue; -- } -- -- -- if (segs[2] == NULL) { -- fprintf(stderr, "ERR: Unknown command in '%s': '%s'\n", filename, segs[0]); -- err++; -- continue; -- } -- -- -- // Otherwise: -- -- fprintf(stderr, "ERR: Unknown command in '%s': '%s'\n", filename, segs[0]); -- err++; -- continue; -- -- } -- -- -- if (initialslots > maxusers) { -- fprintf(stderr, "ERR: initialslots is greater than maxusers!\n"); -- err++; -- } -- -- -- if (warn) { -- fprintf(stderr, "Alert! %d warnings!\n", warn); -- } -- -- if (err) { -- fprintf(stderr, "Unable to start: %d errors!\n", err); -- exit(1); -- } -- -- fclose(fp); -- -- return; -- --} -diff -Naur awhttpd/conn.c axTLS/httpd/awhttpd/conn.c ---- awhttpd/conn.c 2004-12-07 16:11:02.000000000 +1000 -+++ axTLS/httpd/awhttpd/conn.c 2006-11-15 15:09:14.243134700 +1000 -@@ -9,26 +9,16 @@ - - #include - #include --#include --#include - #include -- - #include "aw3.h" - - -- --void addconnection(int sd, char *ip) { -+void addconnection(int sd, char *ip, int is_ssl) { - struct connstruct *tp; - - // Get ourselves a connstruct - if (freeconns == NULL) { - tp = (struct connstruct *) malloc(sizeof(struct connstruct)); -- if (tp == NULL) { -- send505(sd, "Out of memory"); -- // removeconnection() should be used normally -- close(sd); -- return; -- } - } else { - tp = freeconns; - freeconns = tp->next; -@@ -39,12 +29,21 @@ - usedconns = tp; - - tp->networkdesc = sd; -+#ifdef CONFIG_HTTP_HAS_SSL -+ if (is_ssl) -+ ssl_server_new(servers->ssl_ctx, sd); -+#endif - tp->filedesc = -1; -+#if defined(CONFIG_HTTP_HAS_DIRECTORIES) - tp->dirp = NULL; -+#endif -+ tp->is_ssl = is_ssl; - - *(tp->actualfile) = '\0'; - *(tp->filereq) = '\0'; -+#if defined(CONFIG_HTTP_HAS_CGI) - *(tp->cgiargs) = '\0'; -+#endif - *(tp->virtualhostreq) = '\0'; - - tp->state = STATE_WANT_TO_READ_HEAD; -@@ -52,21 +51,16 @@ - - my_strncpy(tp->ip, ip, MAXIPLEN); - -- tp->offset = -1; -- -- numusers++; -- -- updatetimeout(tp, time(NULL)); -+ tp->close_when_done = 0; -+ tp->modified_since = 0; - -+ tp->timeout = time(NULL) + CONFIG_HTTP_TIMEOUT; - return; - - } - - - --// Remove cn from the used list --// FIXME: This O(N) operation could be avoided if we used --// doubly linked lists... - void removeconnection(struct connstruct *cn) { - - struct connstruct *tp; -@@ -95,12 +89,21 @@ - freeconns = cn; - - // Close it all down -- if (cn->networkdesc != -1) close(cn->networkdesc); -+ if (cn->networkdesc != -1) { -+#ifdef CONFIG_HTTP_HAS_SSL -+ if (cn->is_ssl) { -+ ssl_free(ssl_find(servers->ssl_ctx, cn->networkdesc)); -+ } -+#endif -+ SOCKET_CLOSE(cn->networkdesc); -+ } - if (cn->filedesc != -1) close(cn->filedesc); -- if (cn->dirp != NULL) closedir(cn->dirp); -- -- numusers--; -- -- return; -- -+#if defined(CONFIG_HTTP_HAS_DIRECTORIES) -+ if (cn->dirp != NULL) -+#ifdef WIN32 -+ FindClose(cn->dirp); -+#else -+ closedir(cn->dirp); -+#endif -+#endif - } -diff -Naur awhttpd/errors.c axTLS/httpd/awhttpd/errors.c ---- awhttpd/errors.c 2005-01-23 06:49:29.000000000 +1000 -+++ axTLS/httpd/awhttpd/errors.c 2006-11-15 15:09:14.258760200 +1000 -@@ -8,7 +8,6 @@ - - - #include --#include - #include - - #include "aw3.h" -@@ -20,7 +19,7 @@ - - snprintf(buf, sizeof(buf), "HTTP/1.1 301 Moved Permanently\nLocation: %s/\n\n\n\n301 Moved Permanently\n\n

Moved Permanently

\nThe document has moved here.

\n


\n\n", cn->filereq, cn->filereq); - -- write(cn->networkdesc, buf, strlen(buf)); -+ special_write(cn, buf, strlen(buf)); - - return; - -@@ -34,21 +33,7 @@ - - snprintf(buf, sizeof(buf), "HTTP/1.0 404 Not Found\nContent-Type: text/html\n\n\n404 Not Found

It ain't there my friend. (404 Not Found)

\n

Anti-Web HTTPD - Take back some simplicity.\n\n"); - -- write(cn->networkdesc, buf, strlen(buf)); -- -- return; -- --} -- -- -- --void send505(int sd, char *reason) { -- -- char buf[1024]; -- -- snprintf(buf, sizeof(buf), "HTTP/1.0 505 Server Error\nContent-Type: text/html\n\n\n505 Internal Server Error

Internal Server Error: %s

\n

Anti-Web HTTPD - Take back some simplicity.\n\n", reason); -- -- write(sd, buf, strlen(buf)); -+ special_write(cn, buf, strlen(buf)); - - return; - -diff -Naur awhttpd/index.c axTLS/httpd/awhttpd/index.c ---- awhttpd/index.c 2005-06-04 14:09:52.000000000 +1000 -+++ axTLS/httpd/awhttpd/index.c 2006-11-15 15:09:14.258760200 +1000 -@@ -11,7 +11,6 @@ - #include - #include - #include --#include - #include - - #include "aw3.h" -@@ -22,20 +21,9 @@ - struct indexstruct *ex; - - ex = (struct indexstruct *) malloc(sizeof(struct indexstruct)); -- if (ex == NULL) { -- fprintf(stderr, "Serious memory error...\n"); -- exit(1); -- } -- - ex->name = strdup(tp); -- if (ex->name == NULL) { -- fprintf(stderr, "Serious memory error...\n"); -- exit(1); -- } -- - ex->next = indexlist; - indexlist = ex; -- - return; - - } -@@ -52,7 +40,13 @@ - tp = indexlist; - - while(tp != NULL) { -- snprintf(tbuf, sizeof(tbuf), "%s%s", cn->actualfile, tp->name); -+ sprintf(tbuf, "%s%s%s", cn->actualfile, -+#ifdef WIN32 -+ "\\", -+#else -+ "/", -+#endif -+ tp->name); - - if (stat(tbuf, stp) != -1) { - my_strncpy(cn->actualfile, tbuf, MAXREQUESTLENGTH); -diff -Naur awhttpd/main.c axTLS/httpd/awhttpd/main.c ---- awhttpd/main.c 2005-06-04 14:09:52.000000000 +1000 -+++ axTLS/httpd/awhttpd/main.c 2006-11-21 16:30:37.093363800 +1000 -@@ -11,7 +11,6 @@ - #include - #include - #include --#include - #include - - #include "aw3.h" -@@ -21,10 +20,42 @@ - struct serverstruct *servers; - struct connstruct *usedconns; - struct connstruct *freeconns; -+#if defined(CONFIG_HTTP_HAS_CGI) - struct cgiextstruct *cgiexts; -+#endif - struct indexstruct *indexlist; - -+char *webroot = CONFIG_HTTP_WEBROOT; - -+/* clean up memory for valgrind */ -+static void sigint_cleanup(int sig) -+{ -+ struct serverstruct *sp; -+ struct connstruct *tp; -+ int i; -+ -+ while(servers != NULL) { -+#ifdef CONFIG_HTTP_HAS_SSL -+ if (servers->is_ssl) -+ ssl_ctx_free(servers->ssl_ctx); -+#endif -+ sp = servers->next; -+ free(servers); -+ servers = sp; -+ } -+ free(indexlist->name); -+ free(indexlist); -+ for(i=0; i< INITIAL_CONNECTION_SLOTS; i++) { -+ if (freeconns == NULL) -+ break; -+ -+ tp = freeconns->next; -+ free(freeconns); -+ freeconns = tp; -+ } -+ -+ exit(0); -+} - - void initlists() { - int i; -@@ -33,108 +64,118 @@ - servers = NULL; - usedconns = NULL; - freeconns = NULL; -+#if defined(CONFIG_HTTP_HAS_CGI) - cgiexts = NULL; -+#endif - indexlist = NULL; - - for(i=0; inext = tp; - } - } - - --void usage(char *cmline) { -- fprintf(stderr, "Anti-Web V%s (C) 2001-2004 by Hardcore Software and others\n\n", VERSION); -- -- fprintf(stderr, " AW has 2 valid command lines (see README for details)\n\n"); -- -- fprintf(stderr, " %s \n", cmline); -- fprintf(stderr, " The root of your HTML tree\n"); -- fprintf(stderr, " The port to use\n\n"); -- -- fprintf(stderr, " %s \n", cmline); -- fprintf(stderr, " /awhttpd.conf Conf file\n"); -- fprintf(stderr, " /default/ Default HTML root\n"); -- fprintf(stderr, " /example.com/ Zero or more virtual host directories\n"); -- -- exit(1); --} -- -- - int main(int argc, char *argv[]) { - -- char buf[MAXREQUESTLENGTH]; -- int pid, tp; -- -+ int tp; -+#if defined(CONFIG_HTTP_IS_DAEMON) -+ int pid; -+#endif -+ -+#ifdef WIN32 -+ WORD wVersionRequested = MAKEWORD(2,2); -+ WSADATA wsaData; -+ WSAStartup(wVersionRequested,&wsaData); -+#endif -+ -+ mime_init(); - initlists(); -- -- if (argc != 2 && argc != 3) usage(argv[0]); -- -- webroot = strdup(argv[1]); -- - tp = strlen(webroot); - if (webroot[tp-1] == '/') webroot[tp-1] = '\0'; - - if (isdir(webroot) == 0) { -+#ifdef CONFIG_HTTP_VERBOSE - fprintf(stderr, "'%s' is not a directory\n", webroot); -+#endif - exit(1); - } - -- defaultconfvals(); -- -- if (argc == 2) { -- snprintf(buf, sizeof(buf), "%s/awhttpd.conf", webroot); -- procconf(buf); -- } else { -- if ((tp=openlistener(atoi(argv[2]))) == -1) { -- fprintf(stderr, "ERR: Couldn't bind to port %d (IPv4)\n", atoi(argv[2])); -+ if ((tp=openlistener(CONFIG_HTTP_PORT)) == -1) { -+#ifdef CONFIG_HTTP_VERBOSE -+ fprintf(stderr, "ERR: Couldn't bind to port %d (IPv4)\n", -+ CONFIG_HTTP_PORT); -+#endif - exit(1); -- } -+ } - - addindex("index.html"); - addtoservers(tp); -- setgid(32767); -- setuid(32767); -- } - -- if (permcheck == 1) procpermcheck(webroot); -+#ifndef WIN32 -+ if (getuid() == 0) -+ { -+ setgid(32767); -+ setuid(32767); -+ } -+#endif -+ -+#ifdef CONFIG_HTTP_HAS_SSL -+ if ((tp=openlistener(CONFIG_HTTP_HTTPS_PORT)) == -1) { -+#ifdef CONFIG_HTTP_VERBOSE -+ fprintf(stderr, "ERR: Couldn't bind to port %d (IPv4)\n", -+ CONFIG_HTTP_HTTPS_PORT); -+#endif -+ exit(1); -+ } -+ -+ addtoservers(tp); -+ servers->ssl_ctx = ssl_ctx_new(CONFIG_HTTP_DEFAULT_SSL_OPTIONS, -+ CONFIG_HTTP_SESSION_CACHE_SIZE); -+ servers->is_ssl = 1; -+#endif /* CONFIG_HTTP_HAS_SSL */ -+ -+#if defined(CONFIG_HTTP_PERM_CHECK) -+ procpermcheck(webroot); -+#endif -+#if defined(CONFIG_HTTP_HAS_CGI) -+ addcgiext(CONFIG_HTTP_CGI_EXTENSION); -+#endif -+#if defined(CONFIG_HTTP_VERBOSE) -+ printf("awhttpd: listening on ports http:%d and https:%d\n", -+ CONFIG_HTTP_PORT, CONFIG_HTTP_HTTPS_PORT); -+ TTY_FLUSH(); -+#endif - -+#if defined(CONFIG_HTTP_IS_DAEMON) - pid = fork(); - - if(pid > 0) { -- status(); - exit(0); - } else if(pid == -1) { -+#ifdef CONFIG_HTTP_VERBOSE - fprintf(stderr,"Anti-Web: Sorry, fork failed... Tough dice.\n"); -+#endif - exit(1); - } - - setsid(); -+#endif - - /* SIGNALS */ -- signal(SIGINT, die); -- signal(SIGQUIT, die); -+ signal(SIGINT, sigint_cleanup); - signal(SIGTERM, die); -+#if defined(CONFIG_HTTP_HAS_CGI) -+#ifndef WIN32 - signal(SIGCHLD, reaper); -- -- #ifndef SOLARIS -- signal(SIGPIPE, nada); -- #endif -- -- #ifdef SOLARIS -- act.sa_handler = nada; -- sigemptyset(&act.sa_mask); -- act.sa_flags = SA_RESTART; -- -- sigaction(SIGPIPE,&act,NULL); -- #endif -- -+#endif -+#endif -+#ifndef WIN32 -+ signal(SIGQUIT, die); -+ signal(SIGPIPE, SIG_IGN); -+#endif -+ - selectloop(); - - return 0; -diff -Naur awhttpd/mime_types.c axTLS/httpd/awhttpd/mime_types.c ---- awhttpd/mime_types.c 2004-01-26 01:08:47.000000000 +1000 -+++ axTLS/httpd/awhttpd/mime_types.c 2006-11-15 15:09:14.305636700 +1000 -@@ -7,28 +7,21 @@ - */ - - --/*Code from mini_httpd - small HTTP server --** --** Copyright C 1999,2000 by Jef Poskanzer . --*/ -- --/* mini_httpd code adapted for Anti-Web by zas@norz.org */ --/* A couple TINY changes by Fractal */ -- --// Reformatted for aw3 -fractal -- --// FIXME: Ideally this code would use a binary search or a hash table... -- -- - #include -+#include -+#include -+#include -+#include "os_port.h" - - --char mime_default[] = "text/plain"; -+static const char mime_default[] = "text/plain"; - --struct { -- char *ext; -- char *type; --} mime_table[] = { -+typedef struct { -+ const char * const ext; -+ const char * const type; -+} mime_table_t; -+ -+static mime_table_t mime_table[] = { - - // Fundamentals - { ".html", "text/html" }, -@@ -160,21 +153,29 @@ - - }; - -+static int mime_cmp(const mime_table_t *t1, const mime_table_t *t2) -+{ -+ return strcasecmp(t1->ext, t2->ext); -+} - --char *getmimetype(char *name) { -- int namelen, extlen, i; -- -- namelen = strlen(name); -- -- for (i=0; i= namelen) continue; -+const char *getmimetype(const char *name) { -+ mime_table_t *mime_type; - -- if (strcasecmp(name+(namelen-extlen), mime_table[i].ext) == 0) -- return mime_table[i].type; -- } -+ if ((name = strrchr(name, '.')) == NULL) -+ return mime_default; - -- return mime_default; -+ mime_type = bsearch(&name, mime_table, -+ sizeof(mime_table)/sizeof(mime_table_t), -+ sizeof(mime_table_t), -+ (int (*)(const void *, const void *))mime_cmp); - -+ return mime_type == NULL ? mime_default : mime_type->type; - } -+ -diff -Naur awhttpd/misc.c axTLS/httpd/awhttpd/misc.c ---- awhttpd/misc.c 2005-01-23 12:59:09.000000000 +1000 -+++ axTLS/httpd/awhttpd/misc.c 2006-11-15 15:09:14.321262200 +1000 -@@ -7,33 +7,33 @@ - */ - - --#include - #include - #include - #include --#include -+#include - #include - #include --#include --#include --#include - - #include "aw3.h" - - - - --void nada() { } -+void nada(int sigtype) { } - - --void die() { -+void die(int sigtype) { - exit(0); - } - - --void reaper() { -+#if defined(CONFIG_HTTP_HAS_CGI) -+#ifndef WIN32 -+void reaper(int sigtype) { - wait3(NULL,WNOHANG,NULL); - } -+#endif -+#endif - - - void stripcrlf(char *p) { -@@ -76,116 +76,6 @@ - } - #endif - -- --int iscgi(char *fn) { -- -- struct cgiextstruct *tp; -- int fnlen, extlen; -- -- fnlen = strlen(fn); -- tp = cgiexts; -- -- while (tp != NULL) { -- extlen = strlen(tp->ext); -- -- if (strcasecmp(fn+(fnlen-extlen), tp->ext) == 0) -- return 1; -- -- tp = tp->next; -- } -- -- return 0; -- --} -- -- -- --int split(char *tp, char *sp[], int maxwords, char sc) { -- -- int i=0; -- -- while(1) { -- /* Skip leading whitespace */ -- while(*tp == sc) tp++; -- -- if (*tp == '\0') { -- sp[i] = NULL; -- break; -- } -- if (i==maxwords-2) { -- sp[maxwords-2] = NULL; -- break; -- } -- -- sp[i] = tp; -- -- while(*tp != sc && *tp != '\0') tp++; -- if (*tp == sc) *tp++ = '\0'; -- i++; -- -- } -- -- return i; -- --} -- -- -- --int confsplit(char *tp, char *sp[], int maxwords) { -- -- int i; -- -- // Skip comments -- i=0; -- while (tp[i] != '\0' && tp[i] != '#') i++; -- tp[i] = '\0'; -- -- i=0; -- while(1) { -- /* Skip leading whitespace */ -- while(*tp == ' ') tp++; -- -- if (*tp == '\0') { -- sp[i] = NULL; -- break; -- } -- if (i==maxwords-2) { -- sp[maxwords-2] = NULL; -- break; -- } -- -- if (*tp == '"') { -- tp++; -- -- if (*tp == '"') { -- tp++; -- continue; -- } -- -- sp[i] = tp; -- -- while(*tp != '"' && *tp != '\0') tp++; -- if (*tp == '"') *tp++ = '\0'; -- i++; -- -- } else { -- sp[i] = tp; -- -- while(*tp != ' ' && *tp != '\0') tp++; -- if (*tp == ' ') *tp++ = '\0'; -- i++; -- } -- -- } -- -- return i; -- --} -- -- -- -- -- - int sanitizefile(char *buf) { - - int len,i; -@@ -198,9 +88,6 @@ - // Check for "/." : In other words, don't send files starting with a . - // Notice, GOBBLES, that this includes ".." - if (buf[i] == '/' && buf[i+1] == '.') return 0; -- -- // Give people a "hidden prefix" for hiding private files in the HTML tree -- if (strncmp(buf+i, "/aw_", 4) == 0) return 0; - } - - return 1; -@@ -231,34 +118,33 @@ - - } - -- -- - void buildactualfile(struct connstruct *cn) { - -- char tpbuf[MAXREQUESTLENGTH]; -- -- if (usevirtualhosts) { -- if (*(cn->virtualhostreq) == '\0') -- my_strncpy(cn->virtualhostreq, "default", MAXREQUESTLENGTH); -- -- snprintf(tpbuf, sizeof(tpbuf), "%s/%s", webroot, cn->virtualhostreq); -- if (isdir(tpbuf) == 0) { -- my_strncpy(cn->virtualhostreq, "default", MAXREQUESTLENGTH); -- } -- } else { -- *(cn->virtualhostreq) = '\0'; -- } -- -- snprintf(cn->actualfile, MAXREQUESTLENGTH, "%s/%s%s", -+ snprintf(cn->actualfile, MAXREQUESTLENGTH, "%s%s", - webroot, -- cn->virtualhostreq, - cn->filereq); - -+ /* Add directory slash if not there */ -+ if (isdir(cn->actualfile) && -+ cn->actualfile[strlen(cn->actualfile)-1] != '/') -+ strcat(cn->actualfile, "/"); -+ -+#ifdef WIN32 -+ /* convert all the forward slashes to back slashes */ -+ { -+ char *t = cn->actualfile; -+ while ((t = strchr(t, '/'))) -+ { -+ *t++ = '\\'; -+ } -+ } -+#endif -+ - return; - - } - -- -+#if defined(CONFIG_HTTP_DIRECTORIES) - int issockwriteable(int sd) { - - fd_set wfds; -@@ -275,11 +161,11 @@ - return FD_ISSET(sd, &wfds); - - } -- -+#endif - - int isdir(char *tpbuf) { - -- static struct stat st; -+ struct stat st; - - if (stat(tpbuf, &st) == -1) return 0; - -@@ -288,25 +174,52 @@ - - } - -+#if defined(CONFIG_HTTP_HAS_CGI) -+int iscgi(char *fn) { - -+ struct cgiextstruct *tp; -+ int fnlen, extlen; - --// FIXME: Arg! This function is horrible! Rewrite it --void status() { -+ fnlen = strlen(fn); -+ tp = cgiexts; - -- int i; -+ while (tp != NULL) { -+ extlen = strlen(tp->ext); - -- fprintf(stdout," [*************************************************]\n"); -- fprintf(stdout," [ Anti-Web V%-6s by Hardcore Software ]\n",VERSION); -- fprintf(stdout," [*************************************************]\n"); -- fprintf(stdout," [ DIRECTORY {%s}",webroot); -- if(strlen(webroot)<35) -- for(i=1;i<=35-strlen(webroot);i++) fprintf(stdout," "); -- fprintf(stdout,"]\n"); -- fprintf(stdout," [*************************************************]\n"); -+ if (strcasecmp(fn+(fnlen-extlen), tp->ext) == 0) -+ return 1; -+ -+ tp = tp->next; -+ } -+ -+ return 0; - - } - -+void split(char *tp, char *sp[], int maxwords, char sc) { -+ -+ int i=0; -+ while(1) { -+ /* Skip leading whitespace */ -+ while(*tp == sc) tp++; -+ -+ if (*tp == '\0') { -+ sp[i] = NULL; -+ break; -+ } - -+ if (i==maxwords-2) { -+ sp[maxwords-2] = NULL; -+ break; -+ } -+ -+ sp[i] = tp; -+ -+ while(*tp != sc && *tp != '\0') tp++; -+ if (*tp == sc) *tp++ = '\0'; -+ i++; -+ } -+} - - /* This function was originally written by Nicolas Benoit - but I've rewritten some parts of it to work under -@@ -329,7 +242,7 @@ - while (fr_rs[i] != NULL) { - snprintf(tpfile, sizeof(tpfile), "%s/%s%s", webroot, cn->virtualhostreq, fr_str); - -- if (iscgi(tpfile) && access(tpfile, X_OK) == 0 && isdir(tpfile) == 0) { -+ if (iscgi(tpfile) && isdir(tpfile) == 0) { - /* We've found our CGI file! */ - my_strncpy(cn->actualfile, tpfile, MAXREQUESTLENGTH); - my_strncpy(cn->cgiscriptinfo, fr_str, MAXREQUESTLENGTH); -@@ -349,3 +262,4 @@ - *(cn->cgipathinfo) = '\0'; - return -1; - } -+#endif -diff -Naur awhttpd/net.c axTLS/httpd/awhttpd/net.c ---- awhttpd/net.c 2005-06-04 14:09:52.000000000 +1000 -+++ axTLS/httpd/awhttpd/net.c 2006-11-15 15:09:14.352513200 +1000 -@@ -8,9 +8,7 @@ - - - #include --#include - #include --#include - #include - #include - #include -@@ -23,17 +21,10 @@ - void addtoservers(int sd) { - struct serverstruct *tp; - -- tp = (struct serverstruct *) malloc(sizeof(struct serverstruct)); -- if (tp == NULL) { -- fprintf(stderr, "Serious memory error...\n"); -- exit(1); -- } -- -+ tp = (struct serverstruct *) calloc(1, sizeof(struct serverstruct)); - tp->next = servers; - tp->sd = sd; -- - servers = tp; -- - return; - } - -@@ -64,7 +55,7 @@ - currtime = time(NULL); - while(tp != NULL) { - -- if (istimedout(tp, currtime)) { -+ if (currtime > tp->timeout) { - to = tp; - tp = tp->next; - removeconnection(to); -@@ -87,14 +78,15 @@ - 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(4, &rfds, &wfds, NULL, NULL); - active = select(wnum > rnum ? wnum+1 : rnum+1, - rnum != -1 ? &rfds : NULL, - wnum != -1 ? &wfds : NULL, -@@ -104,7 +96,7 @@ - sp = servers; - while(active > 0 && sp != NULL) { - if (FD_ISSET(sp->sd, &rfds)) { -- handlenewconnection(sp->sd); -+ handlenewconnection(sp->sd, sp->is_ssl); - active--; - } - sp = sp->next; -@@ -112,41 +104,37 @@ - - // Handle the established sockets - tp = usedconns; -- currtime = time(NULL); - while(active > 0 && tp != NULL) { - to = tp; - tp = tp->next; - - if (to->state == STATE_WANT_TO_READ_HEAD) - if (FD_ISSET(to->networkdesc, &rfds)) { -- updatetimeout(to, currtime); - active--; - procreadhead(to); - } - if (to->state == STATE_WANT_TO_SEND_HEAD) - if (FD_ISSET(to->networkdesc, &wfds)) { -- updatetimeout(to, currtime); - active--; - procsendhead(to); - } - if (to->state == STATE_WANT_TO_READ_FILE) - if (FD_ISSET(to->filedesc, &rfds)) { -- updatetimeout(to, currtime); - active--; - procreadfile(to); - } - if (to->state == STATE_WANT_TO_SEND_FILE) - if (FD_ISSET(to->networkdesc, &wfds)) { -- updatetimeout(to, currtime); - active--; - procsendfile(to); - } -+#if defined(CONFIG_HTTP_DIRECTORIES) - if (to->state == STATE_DOING_DIR) - if (FD_ISSET(to->networkdesc, &wfds)) { -- updatetimeout(to, currtime); - active--; - procdodir(to); - } -+#endif - } - - -diff -Naur awhttpd/permcheck.c axTLS/httpd/awhttpd/permcheck.c ---- awhttpd/permcheck.c 2005-01-23 06:49:29.000000000 +1000 -+++ axTLS/httpd/awhttpd/permcheck.c 2006-11-15 15:09:14.368138700 +1000 -@@ -7,21 +7,23 @@ - */ - - --#include - #include -+#include -+#include - - #include "aw3.h" - -+#if defined(CONFIG_HTTP_PERM_CHECK) - void procpermcheck(char *pathtocheck) { -- -+ char thepath[MAXREQUESTLENGTH]; -+#ifndef WIN32 - DIR *tpdir; - struct dirent *dp; -- char thepath[MAXREQUESTLENGTH]; - - tpdir=opendir(pathtocheck); - - if (tpdir==NULL) { -- printf("WARNING: UID (%d) is unable to read %s\n", getuid(), pathtocheck); -+ printf("WARNING: UID (%d) is unable to read %s\n", (int)getuid(), pathtocheck); - return; - } - -@@ -38,14 +40,56 @@ - } - - if (access(thepath, R_OK) != 0) -- printf("WARNING: UID (%d) is unable to read %s\n", getuid(), thepath); -+ printf("WARNING: UID (%d) is unable to read %s\n", (int)getuid(), thepath); - if (access(thepath, W_OK) == 0) -- printf("SECURITY: UID (%d) is ABLE TO WRITE TO %s\n", getuid(), thepath); -+ printf("SECURITY: UID (%d) is ABLE TO WRITE TO %s\n", (int)getuid(), thepath); - - } - - closedir(tpdir); -+#else /* Win32 */ -+ HANDLE tpdir; -+ WIN32_FIND_DATA file_data; -+ struct stat st; -+ char buf2[1024]; -+ -+ strcpy(buf2, pathtocheck); -+ strcat(buf2, "\\*"); -+ tpdir = FindFirstFile(buf2, &file_data); -+ -+ if (tpdir == INVALID_HANDLE_VALUE) { -+ printf("WARNING: unable to read %s\n", buf2); -+ TTY_FLUSH(); -+ return; -+ } -+ -+ while (FindNextFile(tpdir, &file_data)) { -+ -+ if (strcmp(file_data.cFileName, "..")==0) continue; -+ if (strcmp(file_data.cFileName, ".")==0) continue; -+ -+ snprintf(thepath, sizeof(thepath), "%s\\%s", -+ pathtocheck, file_data.cFileName); - -- return; -+ if (isdir(thepath)) { -+ procpermcheck(thepath); -+ continue; -+ } -+ -+ if (stat(thepath, &st) >= 0) { -+ if ((st.st_mode & _S_IREAD) == 0) { -+ printf("WARNING: unable to read %s\n", thepath); -+ TTY_FLUSH(); -+ } -+ -+ if (st.st_mode & _S_IWRITE) { -+ printf("SECURITY: ABLE TO WRITE TO %s\n", thepath); -+ TTY_FLUSH(); -+ } -+ } -+ } - -+ FindClose(tpdir); -+#endif - } -+#endif /* CONFIG_HTTP_PERM_CHECK */ -diff -Naur awhttpd/proc.c axTLS/httpd/awhttpd/proc.c ---- awhttpd/proc.c 2005-01-23 10:59:41.000000000 +1000 -+++ axTLS/httpd/awhttpd/proc.c 2006-11-15 15:09:14.399389700 +1000 -@@ -13,91 +13,92 @@ - #include - #include - #include --#include - #include - #include - - #include "aw3.h" - -- -- -+static int special_read(struct connstruct *cn, void *buf, size_t count); - - // Returns 1 if elems should continue being read, 0 otherwise - int procheadelem(struct connstruct *cn, char *buf) { - -- char *words[10]; -- -- split(buf, words, 10, ' '); -+ char *delim, *value; -+#if defined(CONFIG_HTTP_HAS_CGI) -+ char *cgi_delim; -+#endif - -- if (words[0] == NULL) return 0; -- -- if (strcmp(words[0], "GET")==0 || -- strcmp(words[0], "HEAD")==0 || -- strcmp(words[0], "POST")==0) { -- char *segs[4]; -- -- if (*words[0] == 'H') cn->reqtype = TYPE_HEAD; -- else if (*words[0] == 'P') cn->reqtype = TYPE_POST; -+ if ((delim = strchr(buf, ' ')) == NULL) -+ return 0; - -- split(words[1], segs, 4, '?'); -+ *delim = 0; -+ value = delim+1; - -- if (segs[0] == NULL) return 0; -+ if (strcmp(buf, "GET")==0 || -+ strcmp(buf, "HEAD")==0 || -+ strcmp(buf, "POST")==0) -+ { -+ if (buf[0] == 'H') -+ cn->reqtype = TYPE_HEAD; -+ else if (buf[0] == 'P') -+ cn->reqtype = TYPE_POST; -+ -+ if ((delim = strchr(value, ' ')) == NULL) /* expect HTTP type */ -+ return 0; -+ *delim = 0; - -- urldecode(segs[0]); -+ urldecode(value); - -- if (sanitizefile(segs[0]) == 0) { -+ if (sanitizefile(value) == 0) { - send404(cn); - removeconnection(cn); - return 0; - } - -- my_strncpy(cn->filereq, segs[0], MAXREQUESTLENGTH); -- -- if (segs[1] != NULL) my_strncpy(cn->cgiargs, segs[1], MAXREQUESTLENGTH); -- -- } else if (strcmp(words[0], "Host:")==0) { -+ my_strncpy(cn->filereq, value, MAXREQUESTLENGTH); -+#if defined(CONFIG_HTTP_HAS_CGI) -+ if ((cgi_delim = strchr(value, '?'))) -+ { -+ *cgi_delim = 0; -+ my_strncpy(cn->cgiargs, value+1, MAXREQUESTLENGTH); -+ } -+#endif - -- if (words[1] == NULL) return 0; -+ } else if (strcmp(buf, "Host:")==0) { - -- if (sanitizehost(words[1]) == 0) { -+ if (sanitizehost(value) == 0) { - send404(cn); - removeconnection(cn); - return 0; - } - -- my_strncpy(cn->virtualhostreq, words[1], MAXREQUESTLENGTH); -- } else if (strcmp(words[0], "Range:")==0) { -- -- cn->offset = -1; -- -- if (strchr(words[1], '-') == NULL) return 1; -- -- if (strchr(words[1], '=') != NULL) { -- while(*words[1] != '=') words[1]++; -- words[1]++; -- } -- -- if (isdigit(*words[1]) == 0) return 1; -- -- cn->offset = atoi(words[1]); -- -+ my_strncpy(cn->virtualhostreq, value, MAXREQUESTLENGTH); -+ } else if (strcmp(buf, "Connection:")==0 && -+ strcmp(value, "close")==0) { -+ cn->close_when_done = 1; -+ } else if (strcmp(buf, "If-Modified-Since:") ==0 ) { -+ /* TODO: parse this date properly with getdate() or similar */ -+ cn->modified_since = 1; - } - - return 1; - --} -- - -+} - -+#if defined(CONFIG_HTTP_DIRECTORIES) - void procdirlisting(struct connstruct *cn) { - - char buf[MAXREQUESTLENGTH]; -+ char actualfile[1024]; - -+#ifndef CONFIG_HTTP_DIRECTORIES - if (allowdirectorylisting == 0) { - send404(cn); - removeconnection(cn); - return; - } -+#endif - - if (cn->reqtype == TYPE_HEAD) { - snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nContent-Type: text/html\n\n"); -@@ -107,7 +108,17 @@ - return; - } - -- cn->dirp = opendir(cn->actualfile); -+ strcpy(actualfile, cn->actualfile); -+#ifdef WIN32 -+ strcat(actualfile, "*"); -+ cn->dirp = FindFirstFile(actualfile, &cn->file_data); -+ if (cn->dirp == INVALID_HANDLE_VALUE) { -+ send404(cn); -+ removeconnection(cn); -+ return; -+ } -+#else -+ cn->dirp = opendir(actualfile); - if (cn->dirp == NULL) { - send404(cn); - removeconnection(cn); -@@ -116,12 +127,10 @@ - - // Get rid of the "." - readdir(cn->dirp); -+#endif - -- // If the browser doesn't specify a virtual host, the client will -- // see "http://default/thedir/" instead of "http://thehost.com/thedir/" -- // Consider this punishment for using such an old browser. -- snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nContent-Type: text/html\n\n\nDirectory Listing\n

Directory listing of http://%s%s


\n", cn->virtualhostreq, cn->filereq); -- write(cn->networkdesc, buf, strlen(buf)); -+ snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nContent-Type: text/html\n\n\nDirectory Listing\n

Directory listing of %s://%s%s


\n", cn->is_ssl ? "https" : "http", cn->virtualhostreq, cn->filereq); -+ special_write(cn, buf, strlen(buf)); - - cn->state = STATE_DOING_DIR; - -@@ -134,36 +143,48 @@ - - void procdodir(struct connstruct *cn) { - -+#ifndef WIN32 - struct dirent *dp; -+#endif - char buf[MAXREQUESTLENGTH]; -- char encbuf[sizeof(dp->d_name)*3+1]; -+ char encbuf[1024]; - int putslash; -+ char *file; - - do { - -- if ((dp = readdir(cn->dirp)) == NULL) { -- snprintf(buf, sizeof(buf), "

End of Anti-Web directory listing.\n"); -- write(cn->networkdesc, buf, strlen(buf)); -+#ifdef WIN32 -+ if (!FindNextFile(cn->dirp, &cn->file_data)) { -+#else -+ if ((dp = readdir(cn->dirp)) == NULL) { -+#endif -+ snprintf(buf, sizeof(buf), "\n"); -+ special_write(cn, buf, strlen(buf)); - removeconnection(cn); - return; - } - -+#ifdef WIN32 -+ file = cn->file_data.cFileName; -+#else -+ file = dp->d_name; -+#endif - if (cn->filereq[0] == '/' && cn->filereq[1] == '\0' && -- strcmp(dp->d_name, "..") == 0) continue; -+ strcmp(file, "..") == 0) continue; - -- snprintf(buf, sizeof(buf), "%s%s", cn->actualfile, dp->d_name); -+ snprintf(buf, sizeof(buf), "%s%s", cn->actualfile, file); - putslash = isdir(buf); - -- urlencode(dp->d_name, encbuf); -+ urlencode(file, encbuf); - snprintf(buf, sizeof(buf), "%s%s
\n", -- encbuf, putslash ? "/" : "", dp->d_name, putslash ? "/" : ""); -- write(cn->networkdesc, buf, strlen(buf)); -+ encbuf, putslash ? "/" : "", file, putslash ? "/" : ""); -+ special_write(cn, buf, strlen(buf)); - - } while (issockwriteable(cn->networkdesc)); - - return; - } -- -+#endif - - - -@@ -172,9 +193,10 @@ - char buf[MAXREQUESTLENGTH*4], *tp, *next; - int rv; - -- rv = read(cn->networkdesc, buf, sizeof(buf)-1); -- if (rv == 0 || rv == -1) { -- removeconnection(cn); -+ rv = special_read(cn, buf, sizeof(buf)-1); -+ if (rv <= 0) { -+ if (rv < 0) -+ removeconnection(cn); - return; - } - -@@ -217,36 +239,85 @@ - void procsendhead(struct connstruct *cn) { - - char buf[1024]; -+ char actualfile[1024]; - struct stat stbuf; -+ time_t now = cn->timeout - CONFIG_HTTP_TIMEOUT; -+ char date[32]; -+ strcpy(date, ctime(&now)); -+ -+ strcpy(actualfile, cn->actualfile); -+ -+#ifdef WIN32 -+ /* stat() under win32 can't deal with trail slash */ -+ if (actualfile[strlen(actualfile)-1] == '\\') -+ actualfile[strlen(actualfile)-1] = 0; -+#endif - -- if (stat(cn->actualfile, &stbuf) == -1) { -- if (allowcgi != 0) { -+ if (stat(actualfile, &stbuf) == -1) { -+#if defined(CONFIG_HTTP_HAS_CGI) - if (trycgi_withpathinfo(cn) == 0) { // We Try To Find A CGI - proccgi(cn,1); - return; - } -- } -+#endif - - send404(cn); - removeconnection(cn); - return; - } - -+#if defined(CONFIG_HTTP_HAS_CGI) - if (iscgi(cn->actualfile)) { -+#ifndef WIN32 - // Set up CGI script -- if (allowcgi == 0 || -- access(cn->actualfile, X_OK) != 0 || -- isdir(cn->actualfile)) { -+ if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile)) { -+ send404(cn); -+ removeconnection(cn); -+ return; -+ } -+#endif -+ -+ proccgi(cn,0); -+ return; -+ } -+#endif -+ -+ if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { -+ if (cn->filereq[strlen(cn->filereq)-1] != '/') { -+ send301(cn); -+ removeconnection(cn); -+ return; -+ } -+ -+ // Check to see if this dir has an index file -+ if (procindex(cn, &stbuf) == 0) { -+#if defined(CONFIG_HTTP_DIRECTORIES) -+ // If not, we do a directory listing of it -+ procdirlisting(cn); -+#else - send404(cn); - removeconnection(cn); -+#endif - return; - } - -- proccgi(cn,0); -- return; -- } -+#if defined(CONFIG_HTTP_HAS_CGI) -+ // If the index is a CGI file, handle it like any other CGI -+ if (iscgi(cn->actualfile)) { -+ // Set up CGI script -+ if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile)) { -+ send404(cn); -+ removeconnection(cn); -+ return; -+ } - -+ proccgi(cn,0); -+ return; -+ } -+#endif -+ // If the index isn't a CGI, we continue on with the index file - -+ } - if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { - if (cn->filereq[strlen(cn->filereq)-1] != '/') { - send301(cn); -@@ -256,17 +327,18 @@ - - // Check to see if this dir has an index file - if (procindex(cn, &stbuf) == 0) { -+#if defined(CONFIG_HTTP_DIRECTORIES) - // If not, we do a directory listing of it - procdirlisting(cn); -+#endif - return; - } - -+#if defined(CONFIG_HTTP_HAS_CGI) - // If the index is a CGI file, handle it like any other CGI - if (iscgi(cn->actualfile)) { - // Set up CGI script -- if (allowcgi == 0 || -- access(cn->actualfile, X_OK) != 0 || -- isdir(cn->actualfile)) { -+ if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile)) { - send404(cn); - removeconnection(cn); - return; -@@ -275,50 +347,68 @@ - proccgi(cn,0); - return; - } -+#endif - // If the index isn't a CGI, we continue on with the index file - - } - -- if (cn->offset == -1 || cn->offset >= stbuf.st_size) { -- cn->offset = -1; -+ if (cn->modified_since) { -+ snprintf(buf, sizeof(buf), "HTTP/1.1 304 Not Modified\nServer: Anti-Web V%s\nDate: %s\n", VERSION, date); -+ special_write(cn, buf, strlen(buf)); -+ cn->modified_since = 0; -+ cn->state = STATE_WANT_TO_READ_HEAD; -+ return; -+ } -+ else { -+#ifdef CONFIG_HTTP_VERBOSE -+ printf("awhttpd: %s send %s\n", -+ cn->is_ssl ? "https" : "http", cn->actualfile); -+ TTY_FLUSH(); -+#endif - -- snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: Anti-Web V%s (%s)\nContent-Type: %s\nContent-Length: %ld\nLast-Modified: %s\n", -- VERSION, -- quote, -- getmimetype(cn->actualfile), -- (long) stbuf.st_size, -- ctime(&(stbuf.st_mtime))); // ctime() has a \n on the end -- } else { -- snprintf(buf, sizeof(buf), "HTTP/1.1 206 OK\nServer: Anti-Web V%s (%s)\nContent-Type: %s\nContent-Range: %ld-%ld/%ld\nContent-Length: %ld\nLast-Modified: %s\n", -+ snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: Anti-Web V%s\nContent-Type: %s\nContent-Length: %ld\nDate: %sLast-Modified: %s\n", - VERSION, -- quote, - getmimetype(cn->actualfile), -- cn->offset, -- (long) stbuf.st_size-1, - (long) stbuf.st_size, -- (long) stbuf.st_size - cn->offset, -+ date, - ctime(&(stbuf.st_mtime))); // ctime() has a \n on the end - } - -- write(cn->networkdesc, buf, strlen(buf)); -+ special_write(cn, buf, strlen(buf)); - - if (cn->reqtype == TYPE_HEAD) { - removeconnection(cn); - return; - } else { -+ int flags = O_RDONLY; -+#if defined(WIN32) || defined(CYGWIN) -+ flags |= O_BINARY; -+#endif - -- cn->filedesc = open(cn->actualfile, O_RDONLY); -+ cn->filedesc = open(cn->actualfile, flags); - if (cn->filedesc == -1) { - send404(cn); - removeconnection(cn); - return; - } - -- if (cn->offset != -1) { -- lseek(cn->filedesc, cn->offset, SEEK_SET); -+#ifdef WIN32 -+ for (;;) -+ { -+ procreadfile(cn); -+ if (cn->filedesc == -1) -+ { -+ break; -+ } -+ -+ do -+ { -+ procsendfile(cn); -+ } while (cn->state != STATE_WANT_TO_READ_FILE); - } -- -+#else - cn->state = STATE_WANT_TO_READ_FILE; -+#endif - return; - } - -@@ -328,13 +418,18 @@ - - void procreadfile(struct connstruct *cn) { - -- int rv; -- -- rv = read(cn->filedesc, cn->databuf, BLOCKSIZE); -+ int rv = read(cn->filedesc, cn->databuf, BLOCKSIZE); - - if (rv == 0 || rv == -1) { -- removeconnection(cn); -- return; -+ close(cn->filedesc); -+ cn->filedesc = -1; -+ if (cn->close_when_done) /* close immediately */ -+ removeconnection(cn); -+ else { /* keep socket open - HTTP 1.1 */ -+ cn->state = STATE_WANT_TO_READ_HEAD; -+ cn->numbytes = 0; -+ } -+ return; - } - - cn->numbytes = rv; -@@ -347,11 +442,9 @@ - - void procsendfile(struct connstruct *cn) { - -- int rv; -- -- rv = write(cn->networkdesc, cn->databuf, cn->numbytes); -+ int rv = special_write(cn, cn->databuf, cn->numbytes); - -- if (rv == -1) -+ if (rv < 0) - removeconnection(cn); - else if (rv == cn->numbytes) - cn->state = STATE_WANT_TO_READ_FILE; -@@ -361,7 +454,47 @@ - memmove(cn->databuf, cn->databuf + rv, cn->numbytes - rv); - cn->numbytes -= rv; - } -+} - -- return; -+int special_write(struct connstruct *cn, -+ const uint8_t *buf, size_t count) -+{ -+ int res; -+ -+#ifdef CONFIG_HTTP_HAS_SSL -+ if (cn->is_ssl) -+ { -+ SSL *ssl = ssl_find(servers->ssl_ctx, cn->networkdesc); -+ if (ssl) -+ { -+ res = ssl_write(ssl, (unsigned char *)buf, count); -+ } -+ else -+ return -1; -+ } -+ else -+#endif -+ res = SOCKET_WRITE(cn->networkdesc, buf, count); -+ -+ return res; -+} -+ -+static int special_read(struct connstruct *cn, void *buf, size_t count) -+{ -+ int res; -+ -+#ifdef CONFIG_HTTP_HAS_SSL -+ if (cn->is_ssl) -+ { -+ SSL *ssl = ssl_find(servers->ssl_ctx, cn->networkdesc); -+ unsigned char *read_buf; -+ -+ if ((res = ssl_read(ssl, &read_buf)) > SSL_OK) -+ memcpy(buf, read_buf, res > (int)count ? count : res); -+ } -+ else -+#endif -+ res = SOCKET_READ(cn->networkdesc, buf, count); - -+ return res; - } -diff -Naur awhttpd/socket.c axTLS/httpd/awhttpd/socket.c ---- awhttpd/socket.c 2004-04-25 13:03:05.000000000 +1000 -+++ axTLS/httpd/awhttpd/socket.c 2006-11-15 15:09:14.415015200 +1000 -@@ -8,61 +8,17 @@ - - - #include --#include --#include - #include --#include --#include --#include --#include --#include --#include - #include - - -- - #include "aw3.h" - - - --int checkmaxusers(int sd) { -- -- if (maxusers <= 0) return 1; -- -- if (numusers >= maxusers) { -- send505(sd, "Maximum user limit reached"); -- // removeconnection() should be used normally -- close(sd); -- -- return 0; -- } -- -- return 1; --} -- -- --int pollsocket(int sd, long ustimeout) { -- -- fd_set rfds; -- struct timeval tv; -- -- tv.tv_sec = 0; -- tv.tv_usec = ustimeout; -- -- FD_ZERO(&rfds); -- FD_SET(sd, &rfds); -- -- select(FD_SETSIZE, &rfds, NULL, NULL, (ustimeout >= 0) ? &tv : NULL); -- -- return FD_ISSET(sd, &rfds); -- --} -- -- -- - #ifdef HAVE_IPV6 - --void handlenewconnection(int listenfd) { -+void handlenewconnection(int listenfd, int is_ssl) { - - struct sockaddr_in6 their_addr; - int tp = sizeof(their_addr); -@@ -82,7 +38,7 @@ - *ipbuf = '\0'; - } - -- if (checkmaxusers(connfd)) addconnection(connfd, ipbuf); -+ addconnection(connfd, ipbuf, is_ssl); - - return; - -@@ -90,19 +46,17 @@ - - #else - --void handlenewconnection(int listenfd) { -+void handlenewconnection(int listenfd, int is_ssl) { - - struct sockaddr_in their_addr; - int tp = sizeof(struct sockaddr_in); - int connfd; -- char ipbuf[100]; - - connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp); - - if (connfd == -1) return; - -- if (checkmaxusers(connfd)) -- addconnection(connfd, inet_ntoa(their_addr.sin_addr)); -+ addconnection(connfd, inet_ntoa(their_addr.sin_addr), is_ssl); - - return; - } -@@ -113,8 +67,12 @@ - - - int openlistener(int port) { -- -- int tp=0,sd; -+ int sd; -+#ifdef WIN32 -+ char tp=1; -+#else -+ int tp=1; -+#endif - struct sockaddr_in my_addr; - - if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1; -@@ -125,7 +83,7 @@ - setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &tp, sizeof(tp)); - - my_addr.sin_family = AF_INET; // host byte order -- my_addr.sin_port = htons(port); // short, network byte order -+ my_addr.sin_port = htons((short)port); // short, network byte order - my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP - - memset(&(my_addr.sin_zero), 0, 8); // zero the rest of the struct -diff -Naur awhttpd/urlencode.c axTLS/httpd/awhttpd/urlencode.c ---- awhttpd/urlencode.c 2004-05-14 10:53:47.000000000 +1000 -+++ axTLS/httpd/awhttpd/urlencode.c 2006-11-15 15:09:14.430640700 +1000 -@@ -13,7 +13,7 @@ - - #include - #include -- -+#include - #include "aw3.h" - - -@@ -37,7 +37,7 @@ - (*p > 'Z' && *p < '_') || - (*p > '_' && *p < 'a') || - (*p > 'z' && *p < 0xA1)) { -- sprintf(tp, "%%%02X", *p); -+ sprintf((char *)tp, "%%%02X", *p); - tp += 3; - } else { - *tp = *p; diff --git a/httpd/axhttp.h b/httpd/axhttp.h new file mode 100644 index 000000000..7af635a71 --- /dev/null +++ b/httpd/axhttp.h @@ -0,0 +1,164 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "os_port.h" +#include "ssl.h" + +#define BACKLOG 15 +#define VERSION "3.0.7" +#ifdef CONFIG_HTTP_HAS_IPV6 +#define HAVE_IPV6 +#endif + +#define MAXFILEPATH 1024 +#define MAXIPLEN 45 +#define MAXREQUESTLENGTH 1024 +#define MAXCGIARGS 100 +#define BLOCKSIZE 4096 + +#define INITIAL_CONNECTION_SLOTS 10 +#define CONFIG_HTTP_DEFAULT_SSL_OPTIONS 0 + +#define STATE_WANT_TO_READ_HEAD 1 +#define STATE_WANT_TO_SEND_HEAD 2 +#define STATE_WANT_TO_READ_FILE 3 +#define STATE_WANT_TO_SEND_FILE 4 +#define STATE_DOING_DIR 5 + +#define TYPE_GET 0 +#define TYPE_HEAD 1 +#define TYPE_POST 2 + +struct connstruct +{ + struct connstruct *next; + + int state; + int reqtype; + + int networkdesc; + int filedesc; + +#if defined(CONFIG_HTTP_DIRECTORIES) +#ifdef WIN32 + HANDLE dirp; + WIN32_FIND_DATA file_data; +#else + DIR *dirp; +#endif +#endif + + time_t timeout; + char ip[MAXIPLEN]; + char actualfile[MAXREQUESTLENGTH]; + char filereq[MAXREQUESTLENGTH]; +#if defined(CONFIG_HTTP_HAS_CGI) + char cgiargs[MAXREQUESTLENGTH]; + char cgiscriptinfo[MAXREQUESTLENGTH]; + char cgipathinfo[MAXREQUESTLENGTH]; +#endif + char virtualhostreq[MAXREQUESTLENGTH]; + + int numbytes; + char databuf[BLOCKSIZE]; + + unsigned char is_ssl; + unsigned char close_when_done; + unsigned char modified_since; +}; + +struct serverstruct +{ + struct serverstruct *next; + int sd; + int is_ssl; + SSLCTX *ssl_ctx; +}; + +struct cgiextstruct +{ + struct cgiextstruct *next; + char *ext; +}; + +struct indexstruct +{ + struct indexstruct *next; + char *name; +}; + +// Global prototypes +extern struct serverstruct *servers; +extern struct connstruct *usedconns; +extern struct connstruct *freeconns; +extern struct cgiextstruct *cgiexts; +extern struct indexstruct *indexlist; + +// Conf global prototypes +extern char *webroot; +extern int allowdirectorylisting; +extern int allowcgi; +extern int permcheck; + +// conn.c prototypes +void addconnection(int sd, char *ip, int is_ssl); +void removeconnection(struct connstruct *cn); + +// proc.c prototypes +int procheadelem(struct connstruct *cn, char *buf); +void procdirlisting(struct connstruct *cn); +void procdodir(struct connstruct *cn); +void procreadhead(struct connstruct *cn); +void procsendhead(struct connstruct *cn); +void procreadfile(struct connstruct *cn); +void procsendfile(struct connstruct *cn); +int special_write(struct connstruct *cn, const uint8_t *buf, size_t count); + +// net.c prototypes +void addtoservers(int sd); +void selectloop(void); + +// socket.c prototypes +void handlenewconnection(int listenfd, int is_ssl); +int openlistener(int port); +int openlistener6(int port); + +// misc.c prototypes +void nada(int sigtype); +void die(int sigtype); +void reaper(int sigtype); +void stripcrlf(char *p); +char *my_strncpy(char *dest, const char *src, size_t n); +#ifndef __HAVE_ARCH_STRNLEN +size_t strnlen ( const char * str, size_t maxlen ); +#endif +int iscgi(char *fn); +void split(char *tp, char *sp[], int maxwords, char sc); +int sanitizefile(char *buf); +int sanitizehost(char *buf); +void buildactualfile(struct connstruct *cn); +int issockwriteable(int sd); +int isdir(char *name); +int trycgi_withpathinfo(struct connstruct *cn); + +// mime_types.c prototypes +void mime_init(void); +const char *getmimetype(const char *fn); + +// main.c prototypes +void initlists(void); diff --git a/httpd/conn.c b/httpd/conn.c new file mode 100644 index 000000000..a36271709 --- /dev/null +++ b/httpd/conn.c @@ -0,0 +1,121 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "axhttp.h" + +void addconnection(int sd, char *ip, int is_ssl) +{ + struct connstruct *tp; + + // Get ourselves a connstruct + if (freeconns == NULL) + { + tp = (struct connstruct *) malloc(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) + ssl_server_new(servers->ssl_ctx, sd); + tp->filedesc = -1; +#if defined(CONFIG_HTTP_HAS_DIRECTORIES) + tp->dirp = NULL; +#endif + tp->is_ssl = is_ssl; + + *(tp->actualfile) = '\0'; + *(tp->filereq) = '\0'; +#if defined(CONFIG_HTTP_HAS_CGI) + *(tp->cgiargs) = '\0'; +#endif + *(tp->virtualhostreq) = '\0'; + + tp->state = STATE_WANT_TO_READ_HEAD; + tp->reqtype = TYPE_GET; + my_strncpy(tp->ip, ip, MAXIPLEN); + tp->close_when_done = 0; + tp->modified_since = 0; + tp->timeout = time(NULL) + CONFIG_HTTP_TIMEOUT; +} + +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(ssl_find(servers->ssl_ctx, cn->networkdesc)); + } + + 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 +} diff --git a/httpd/main.c b/httpd/main.c new file mode 100644 index 000000000..d3e5e1aed --- /dev/null +++ b/httpd/main.c @@ -0,0 +1,323 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "axhttp.h" + +// GLOBALS +struct serverstruct *servers; +struct connstruct *usedconns; +struct connstruct *freeconns; +#if defined(CONFIG_HTTP_HAS_CGI) +struct cgiextstruct *cgiexts; +#endif +struct indexstruct *indexlist; + +char *webroot = CONFIG_HTTP_WEBROOT; +static void addindex(char *tp); +#if defined(CONFIG_HTTP_PERM_CHECK) +static void procpermcheck(char *pathtocheck); +#endif +#if defined(CONFIG_HTTP_HAS_CGI) +static void addcgiext(char *tp); +#endif + +/* clean up memory for valgrind */ +static void sigint_cleanup(int sig) +{ + struct serverstruct *sp; + struct connstruct *tp; + int i; + + while(servers != NULL) + { + if (servers->is_ssl) + ssl_ctx_free(servers->ssl_ctx); + + sp = servers->next; + free(servers); + servers = sp; + } + + free(indexlist->name); + free(indexlist); + + for (i = 0; i < INITIAL_CONNECTION_SLOTS; i++) + { + if (freeconns == NULL) + break; + + tp = freeconns->next; + free(freeconns); + freeconns = tp; + } + + exit(0); +} + +void initlists() +{ + int i; + struct connstruct *tp; + + servers = NULL; + usedconns = NULL; + freeconns = NULL; +#if defined(CONFIG_HTTP_HAS_CGI) + cgiexts = NULL; +#endif + indexlist = NULL; + + for (i=0; inext = tp; + } +} + +int main(int argc, char *argv[]) +{ + int tp; +#if defined(CONFIG_HTTP_IS_DAEMON) + int pid; +#endif + +#ifdef WIN32 + WORD wVersionRequested = MAKEWORD(2,2); + WSADATA wsaData; + WSAStartup(wVersionRequested,&wsaData); +#endif + + mime_init(); + initlists(); + tp = strlen(webroot); + + if (webroot[tp-1] == '/') + webroot[tp-1] = '\0'; + + if (isdir(webroot) == 0) + { +#ifdef CONFIG_HTTP_VERBOSE + fprintf(stderr, "'%s' is not a directory\n", webroot); +#endif + exit(1); + } + + if ((tp=openlistener(CONFIG_HTTP_PORT)) == -1) + { +#ifdef CONFIG_HTTP_VERBOSE + fprintf(stderr, "ERR: Couldn't bind to port %d (IPv4)\n", + CONFIG_HTTP_PORT); +#endif + exit(1); + } + + addindex("index.html"); + addtoservers(tp); + +#ifndef WIN32 + if (getuid() == 0) + { + setgid(32767); + setuid(32767); + } +#endif + + if ((tp=openlistener(CONFIG_HTTP_HTTPS_PORT)) == -1) + { +#ifdef CONFIG_HTTP_VERBOSE + fprintf(stderr, "ERR: Couldn't bind to port %d (IPv4)\n", + CONFIG_HTTP_HTTPS_PORT); +#endif + exit(1); + } + + addtoservers(tp); + servers->ssl_ctx = ssl_ctx_new(CONFIG_HTTP_DEFAULT_SSL_OPTIONS, + CONFIG_HTTP_SESSION_CACHE_SIZE); + servers->is_ssl = 1; + +#if defined(CONFIG_HTTP_PERM_CHECK) + procpermcheck(webroot); +#endif +#if defined(CONFIG_HTTP_HAS_CGI) + addcgiext(CONFIG_HTTP_CGI_EXTENSION); +#endif +#if defined(CONFIG_HTTP_VERBOSE) + printf("axhttpd: listening on ports http:%d and https:%d\n", + CONFIG_HTTP_PORT, CONFIG_HTTP_HTTPS_PORT); + TTY_FLUSH(); +#endif + +#if defined(CONFIG_HTTP_IS_DAEMON) + pid = fork(); + + if (pid > 0) + { + exit(0); + } + else if(pid == -1) + { +#ifdef CONFIG_HTTP_VERBOSE + fprintf(stderr,"axhttpd: Sorry, fork failed... Tough dice.\n"); +#endif + exit(1); + } + + setsid(); +#endif + + /* SIGNALS */ + signal(SIGINT, sigint_cleanup); + signal(SIGTERM, die); +#if defined(CONFIG_HTTP_HAS_CGI) +#ifndef WIN32 + signal(SIGCHLD, reaper); +#endif +#endif +#ifndef WIN32 + signal(SIGQUIT, die); + signal(SIGPIPE, SIG_IGN); +#endif + + selectloop(); + return 0; +} + +static void addindex(char *tp) +{ + struct indexstruct *ex = (struct indexstruct *) + malloc(sizeof(struct indexstruct)); + ex->name = strdup(tp); + ex->next = indexlist; + indexlist = ex; +} + +#if defined(CONFIG_HTTP_PERM_CHECK) +static void procpermcheck(char *pathtocheck) +{ + char thepath[MAXREQUESTLENGTH]; +#ifndef WIN32 + DIR *tpdir; + struct dirent *dp; + + tpdir = opendir(pathtocheck); + + if (tpdir == NULL) + { + printf("WARNING: UID (%d) is unable to read %s\n", + (int)getuid(), pathtocheck); + return; + } + + while ((dp=readdir(tpdir))) + { + if (strcmp(dp->d_name, "..")==0) + continue; + + if (strcmp(dp->d_name, ".")==0) + continue; + + snprintf(thepath, sizeof(thepath), "%s/%s", pathtocheck, dp->d_name); + + if (isdir(thepath)) + { + procpermcheck(thepath); + continue; + } + + if (access(thepath, R_OK) != 0) + printf("WARNING: UID (%d) is unable to read %s\n", + (int)getuid(), thepath); + if (access(thepath, W_OK) == 0) + printf("SECURITY: UID (%d) is ABLE TO WRITE TO %s\n", + (int)getuid(), thepath); + } + + closedir(tpdir); +#else /* Win32 */ + HANDLE tpdir; + WIN32_FIND_DATA file_data; + struct stat st; + char buf2[1024]; + + strcpy(buf2, pathtocheck); + strcat(buf2, "\\*"); + tpdir = FindFirstFile(buf2, &file_data); + + if (tpdir == INVALID_HANDLE_VALUE) + { + printf("WARNING: unable to read %s\n", buf2); + TTY_FLUSH(); + return; + } + + while (FindNextFile(tpdir, &file_data)) + { + if (strcmp(file_data.cFileName, "..") == 0) + continue; + + if (strcmp(file_data.cFileName, ".") == 0) + continue; + + snprintf(thepath, sizeof(thepath), "%s\\%s", + pathtocheck, file_data.cFileName); + + if (isdir(thepath)) + { + procpermcheck(thepath); + continue; + } + + if (stat(thepath, &st) >= 0) + { + if ((st.st_mode & _S_IREAD) == 0) + { + printf("WARNING: unable to read %s\n", thepath); + TTY_FLUSH(); + } + + if (st.st_mode & _S_IWRITE) + { + printf("SECURITY: ABLE TO WRITE TO %s\n", thepath); + TTY_FLUSH(); + } + } + } + + FindClose(tpdir); +#endif +} +#endif /* CONFIG_HTTP_PERM_CHECK */ + +#if defined(CONFIG_HTTP_HAS_CGI) +static void addcgiext(char *tp) +{ + struct cgiextstruct *ex = (struct cgiextstruct *) + malloc(sizeof(struct cgiextstruct)); + ex->ext = strdup(tp); + ex->next = cgiexts; + cgiexts = ex; +} +#endif + diff --git a/httpd/mime_types.c b/httpd/mime_types.c new file mode 100644 index 000000000..5bc959be4 --- /dev/null +++ b/httpd/mime_types.c @@ -0,0 +1,190 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "os_port.h" + +static const char mime_default[] = "text/plain"; + +typedef struct +{ + const char * const ext; + const char * const type; +} mime_table_t; + +static mime_table_t mime_table[] = +{ + // Fundamentals + { ".html", "text/html" }, + { ".htm", "text/html" }, + { ".txt", "text/plain" }, + + // Others + { ".rtx", "text/richtext" }, + { ".etx", "text/x-setext" }, + { ".tsv", "text/tab-separated-values" }, + { ".css", "text/css" }, + { ".xml", "text/xml" }, + { ".dtd", "text/xml" }, + { ".gif", "image/gif" }, + { ".jpg", "image/jpeg" }, + { ".jpeg", "image/jpeg" }, + { ".jpe", "image/jpeg" }, + { ".jfif", "image/jpeg" }, + { ".tif", "image/tiff" }, + { ".tiff", "image/tiff" }, + { ".pbm", "image/x-portable-bitmap" }, + { ".pgm", "image/x-portable-graymap" }, + { ".ppm", "image/x-portable-pixmap" }, + { ".pnm", "image/x-portable-anymap" }, + { ".xbm", "image/x-xbitmap" }, + { ".xpm", "image/x-xpixmap" }, + { ".xwd", "image/x-xwindowdump" }, + { ".ief", "image/ief" }, + { ".png", "image/png" }, + { ".au", "audio/basic" }, + { ".snd", "audio/basic" }, + { ".aif", "audio/x-aiff" }, + { ".aiff", "audio/x-aiff" }, + { ".aifc", "audio/x-aiff" }, + { ".ra", "audio/x-pn-realaudio" }, + { ".ram", "audio/x-pn-realaudio" }, + { ".rm", "audio/x-pn-realaudio" }, + { ".rpm", "audio/x-pn-realaudio-plugin" }, + { ".wav", "audio/wav" }, + { ".mid", "audio/midi" }, + { ".midi", "audio/midi" }, + { ".kar", "audio/midi" }, + { ".mpga", "audio/mpeg" }, + { ".mp2", "audio/mpeg" }, + { ".mp3", "audio/mpeg" }, + { ".mpeg", "video/mpeg" }, + { ".mpg", "video/mpeg" }, + { ".mpe", "video/mpeg" }, + { ".qt", "video/quicktime" }, + { ".mov", "video/quicktime" }, + { ".avi", "video/x-msvideo" }, + { ".movie", "video/x-sgi-movie" }, + { ".mv", "video/x-sgi-movie" }, + { ".vx", "video/x-rad-screenplay" }, + { ".a", "application/octet-stream" }, + { ".bin", "application/octet-stream" }, + { ".exe", "application/octet-stream" }, + { ".dump", "application/octet-stream" }, + { ".o", "application/octet-stream" }, + { ".class", "application/java" }, + { ".js", "application/x-javascript" }, + { ".ai", "application/postscript" }, + { ".eps", "application/postscript" }, + { ".ps", "application/postscript" }, + { ".dir", "application/x-director" }, + { ".dcr", "application/x-director" }, + { ".dxr", "application/x-director" }, + { ".fgd", "application/x-director" }, + { ".aam", "application/x-authorware-map" }, + { ".aas", "application/x-authorware-seg" }, + { ".aab", "application/x-authorware-bin" }, + { ".fh4", "image/x-freehand" }, + { ".fh7", "image/x-freehand" }, + { ".fh5", "image/x-freehand" }, + { ".fhc", "image/x-freehand" }, + { ".fh", "image/x-freehand" }, + { ".spl", "application/futuresplash" }, + { ".swf", "application/x-shockwave-flash" }, + { ".dvi", "application/x-dvi" }, + { ".gtar", "application/x-gtar" }, + { ".hdf", "application/x-hdf" }, + { ".hqx", "application/mac-binhex40" }, + { ".iv", "application/x-inventor" }, + { ".latex", "application/x-latex" }, + { ".man", "application/x-troff-man" }, + { ".me", "application/x-troff-me" }, + { ".mif", "application/x-mif" }, + { ".ms", "application/x-troff-ms" }, + { ".oda", "application/oda" }, + { ".pdf", "application/pdf" }, + { ".rtf", "application/rtf" }, + { ".bcpio", "application/x-bcpio" }, + { ".cpio", "application/x-cpio" }, + { ".sv4cpio", "application/x-sv4cpio" }, + { ".sv4crc", "application/x-sv4crc" }, + { ".sh", "application/x-shar" }, + { ".shar", "application/x-shar" }, + { ".sit", "application/x-stuffit" }, + { ".tar", "application/x-tar" }, + { ".tex", "application/x-tex" }, + { ".texi", "application/x-texinfo" }, + { ".texinfo", "application/x-texinfo" }, + { ".tr", "application/x-troff" }, + { ".roff", "application/x-troff" }, + { ".man", "application/x-troff-man" }, + { ".me", "application/x-troff-me" }, + { ".ms", "application/x-troff-ms" }, + { ".zip", "application/x-zip-compressed" }, + { ".tsp", "application/dsptype" }, + { ".wsrc", "application/x-wais-source" }, + { ".ustar", "application/x-ustar" }, + { ".cdf", "application/x-netcdf" }, + { ".nc", "application/x-netcdf" }, + { ".doc", "application/msword" }, + { ".ppt", "application/powerpoint" }, + { ".wrl", "model/vrml" }, + { ".vrml", "model/vrml" }, + { ".mime", "message/rfc822" }, + { ".pac", "application/x-ns-proxy-autoconfig" }, + { ".wml", "text/vnd.wap.wml" }, + { ".wmlc", "application/vnd.wap.wmlc" }, + { ".wmls", "text/vnd.wap.wmlscript" }, + { ".wmlsc", "application/vnd.wap.wmlscriptc" }, + { ".wbmp", "image/vnd.wap.wbmp" }, + { ".tgz", "application/x-gzip" }, + { ".tar.gz", "application/x-gzip" }, + { ".bz2", "application/x-bzip2" }, + { ".zip", "application/zip" } +}; + +static int mime_cmp(const mime_table_t *t1, const mime_table_t *t2) +{ + return strcasecmp(t1->ext, t2->ext); +} + +void mime_init(void) +{ + qsort(mime_table, sizeof(mime_table)/sizeof(mime_table_t), + sizeof(mime_table_t), + (int (*)(const void *, const void *))mime_cmp); +} + +const char *getmimetype(const char *name) +{ + mime_table_t *mime_type; + + if ((name = strrchr(name, '.')) == NULL) + return mime_default; + + mime_type = bsearch(&name, mime_table, + sizeof(mime_table)/sizeof(mime_table_t), + sizeof(mime_table_t), + (int (*)(const void *, const void *))mime_cmp); + + return mime_type == NULL ? mime_default : mime_type->type; +} + diff --git a/httpd/misc.c b/httpd/misc.c new file mode 100644 index 000000000..bca059b5b --- /dev/null +++ b/httpd/misc.c @@ -0,0 +1,268 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "axhttp.h" + +void nada(int sigtype) { } + +void die(int sigtype) +{ + exit(0); +} + +#if defined(CONFIG_HTTP_HAS_CGI) +#ifndef WIN32 +void reaper(int sigtype) +{ + wait3(NULL,WNOHANG,NULL); +} +#endif +#endif + +void stripcrlf(char *p) +{ + while (p && *p) + { + if (*p=='\n' || *p=='\r') + { + *p='\0'; + return; + } + p++; + } +} + +/* Wrapper function for strncpy() that guarantees + a null-terminated string. This is to avoid any possible + issues due to strncpy()'s behaviour. Thanks to + Werner Almesberger for pointing out this potential + issue. Needless to say, make sure sizeof(dest) > 0 + and sizeof(dest) >= n. + */ +char *my_strncpy(char *dest, const char *src, size_t n) +{ + strncpy(dest, src, n); + dest[n-1] = '\0'; + return dest; +} + +/* strnlen is a GNU Extension */ +#ifndef __HAVE_ARCH_STRNLEN +size_t strnlen (const char * str, size_t maxlen) +{ + const char *p; + + for (p=str; maxlen-- && *p!='\0'; ++p); + return (p - str); +} +#endif + +int sanitizefile(char *buf) +{ + int len, i; + + // Don't accept anything not starting with a / + if (*buf != '/') + return 0; + + len = strlen(buf); + for (i = 0; i < len; i++) + { + // Check for "/." : In other words, don't send files starting with a . + // Notice, GOBBLES, that this includes ".." + if (buf[i] == '/' && buf[i+1] == '.') + return 0; + } + + return 1; +} + +int sanitizehost(char *buf) +{ + while(*buf != '\0') + { + // Handle the port + if (*buf == ':') + { + *buf = '\0'; + return 1; + } + + // Enforce some basic URL rules... + if (isalnum(*buf)==0 && *buf != '-' && *buf != '.') return 0; + if (*buf == '.' && *(buf+1) == '.') return 0; + if (*buf == '.' && *(buf+1) == '-') return 0; + if (*buf == '-' && *(buf+1) == '.') return 0; + buf++; + } + + return 1; +} + +void buildactualfile(struct connstruct *cn) +{ + snprintf(cn->actualfile, MAXREQUESTLENGTH, "%s%s", + webroot, + cn->filereq); + + /* Add directory slash if not there */ + if (isdir(cn->actualfile) && + cn->actualfile[strlen(cn->actualfile)-1] != '/') + strcat(cn->actualfile, "/"); + +#ifdef WIN32 + /* convert all the forward slashes to back slashes */ + { + char *t = cn->actualfile; + while ((t = strchr(t, '/'))) + { + *t++ = '\\'; + } + } +#endif +} + +#if defined(CONFIG_HTTP_DIRECTORIES) +int issockwriteable(int sd) +{ + fd_set wfds; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 0; + + FD_ZERO(&wfds); + FD_SET(sd, &wfds); + + select(FD_SETSIZE, NULL, &wfds, NULL, &tv); + + return FD_ISSET(sd, &wfds); +} +#endif + +int isdir(char *tpbuf) +{ + struct stat st; + + if (stat(tpbuf, &st) == -1) + return 0; + + if ((st.st_mode & S_IFMT) == S_IFDIR) + return 1; + + return 0; +} + +#if defined(CONFIG_HTTP_HAS_CGI) +int iscgi(char *fn) +{ + struct cgiextstruct *tp; + int fnlen, extlen; + + fnlen = strlen(fn); + tp = cgiexts; + + while (tp != NULL) + { + extlen = strlen(tp->ext); + + if (strcasecmp(fn+(fnlen-extlen), tp->ext) == 0) + return 1; + + tp = tp->next; + } + + return 0; +} + +void split(char *tp, char *sp[], int maxwords, char sc) +{ + int i = 0; + + while(1) + { + /* Skip leading whitespace */ + while(*tp == sc) tp++; + + if (*tp == '\0') + { + sp[i] = NULL; + break; + } + + if (i==maxwords-2) + { + sp[maxwords-2] = NULL; + break; + } + + sp[i] = tp; + + while(*tp != sc && *tp != '\0') + tp++; + + if (*tp == sc) + *tp++ = '\0'; + + i++; + } +} + +int trycgi_withpathinfo(struct connstruct *cn) +{ + char tpfile[MAXREQUESTLENGTH]; + char fr_str[MAXREQUESTLENGTH]; + char *fr_rs[MAXCGIARGS]; // filereq splitted + int i = 0, offset; + + my_strncpy(fr_str, cn->filereq, MAXREQUESTLENGTH); + split(fr_str, fr_rs, MAXCGIARGS, '/'); + + while (fr_rs[i] != NULL) + { + snprintf(tpfile, sizeof(tpfile), "%s/%s%s", + webroot, cn->virtualhostreq, fr_str); + + if (iscgi(tpfile) && isdir(tpfile) == 0) + { + /* We've found our CGI file! */ + my_strncpy(cn->actualfile, tpfile, MAXREQUESTLENGTH); + my_strncpy(cn->cgiscriptinfo, fr_str, MAXREQUESTLENGTH); + + offset = (fr_rs[i] + strlen(fr_rs[i])) - fr_str; + my_strncpy(cn->cgipathinfo, cn->filereq+offset, MAXREQUESTLENGTH); + + return 0; + } + + *(fr_rs[i]+strlen(fr_rs[i])) = '/'; + i++; + } + + /* Couldn't find any CGIs :( */ + *(cn->cgiscriptinfo) = '\0'; + *(cn->cgipathinfo) = '\0'; + return -1; +} +#endif diff --git a/httpd/net.c b/httpd/net.c new file mode 100644 index 000000000..dd325a61d --- /dev/null +++ b/httpd/net.c @@ -0,0 +1,176 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "axhttp.h" + +void addtoservers(int sd) +{ + struct serverstruct *tp = (struct serverstruct *) + calloc(1, sizeof(struct serverstruct)); + tp->next = servers; + tp->sd = sd; + servers = tp; +} + +void selectloop() +{ + fd_set rfds, wfds; + struct connstruct *tp, *to; + struct serverstruct *sp; + int rnum, wnum, active; + int currtime; + + while (1) + { + // MAIN SELECT LOOP + FD_ZERO(&rfds); + FD_ZERO(&wfds); + rnum = wnum = -1; + + // Add the listening sockets + sp = servers; + while (sp != NULL) + { + 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) + { + 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); + + // Handle the listening sockets + 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) + if (FD_ISSET(to->networkdesc, &rfds)) + { + active--; + procreadhead(to); + } + + if (to->state == STATE_WANT_TO_SEND_HEAD) + if (FD_ISSET(to->networkdesc, &wfds)) + { + active--; + procsendhead(to); + } + + if (to->state == STATE_WANT_TO_READ_FILE) + if (FD_ISSET(to->filedesc, &rfds)) + { + active--; + procreadfile(to); + } + + if (to->state == STATE_WANT_TO_SEND_FILE) + if (FD_ISSET(to->networkdesc, &wfds)) + { + active--; + procsendfile(to); + } + +#if defined(CONFIG_HTTP_DIRECTORIES) + if (to->state == STATE_DOING_DIR) + if (FD_ISSET(to->networkdesc, &wfds)) + { + active--; + procdodir(to); + } +#endif + } + } // MAIN SELECT LOOP +} diff --git a/httpd/proc.c b/httpd/proc.c new file mode 100644 index 000000000..3eb164f00 --- /dev/null +++ b/httpd/proc.c @@ -0,0 +1,780 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "axhttp.h" + +static int special_read(struct connstruct *cn, void *buf, size_t count); +static void send301(struct connstruct *cn); +static void send404(struct connstruct *cn); +static int procindex(struct connstruct *cn, struct stat *stp); +static int hexit(char c); +static void urlencode(unsigned char *s, unsigned char *t); +static void urldecode(char *buf); + +#if defined(CONFIG_HTTP_HAS_CGI) +static void proccgi(struct connstruct *cn, int has_pathinfo); +#endif + +// Returns 1 if elems should continue being read, 0 otherwise +int procheadelem(struct connstruct *cn, char *buf) +{ + char *delim, *value; +#if defined(CONFIG_HTTP_HAS_CGI) + char *cgi_delim; +#endif + + if ((delim = strchr(buf, ' ')) == NULL) + return 0; + + *delim = 0; + value = delim+1; + + if (strcmp(buf, "GET")==0 || + strcmp(buf, "HEAD")==0 || + strcmp(buf, "POST")==0) + { + if (buf[0] == 'H') + cn->reqtype = TYPE_HEAD; + else if (buf[0] == 'P') + cn->reqtype = TYPE_POST; + + if ((delim = strchr(value, ' ')) == NULL) /* expect HTTP type */ + return 0; + *delim = 0; + + urldecode(value); + + if (sanitizefile(value) == 0) + { + send404(cn); + removeconnection(cn); + return 0; + } + + my_strncpy(cn->filereq, value, MAXREQUESTLENGTH); +#if defined(CONFIG_HTTP_HAS_CGI) + if ((cgi_delim = strchr(value, '?'))) + { + *cgi_delim = 0; + my_strncpy(cn->cgiargs, value+1, MAXREQUESTLENGTH); + } +#endif + + } + else if (strcmp(buf, "Host:")==0) + { + if (sanitizehost(value) == 0) + { + send404(cn); + removeconnection(cn); + return 0; + } + + my_strncpy(cn->virtualhostreq, value, MAXREQUESTLENGTH); + } + else if (strcmp(buf, "Connection:")==0 && + strcmp(value, "close")==0) { + cn->close_when_done = 1; + } + else if (strcmp(buf, "If-Modified-Since:") ==0 ) + { + /* TODO: parse this date properly with getdate() or similar */ + cn->modified_since = 1; + } + + return 1; +} + +#if defined(CONFIG_HTTP_DIRECTORIES) +void procdirlisting(struct connstruct *cn) +{ + char buf[MAXREQUESTLENGTH]; + char actualfile[1024]; + +#ifndef CONFIG_HTTP_DIRECTORIES + if (allowdirectorylisting == 0) + { + send404(cn); + removeconnection(cn); + return; + } +#endif + + if (cn->reqtype == TYPE_HEAD) + { + snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nContent-Type: text/html\n\n"); + write(cn->networkdesc, buf, strlen(buf)); + + removeconnection(cn); + return; + } + + strcpy(actualfile, cn->actualfile); +#ifdef WIN32 + strcat(actualfile, "*"); + cn->dirp = FindFirstFile(actualfile, &cn->file_data); + if (cn->dirp == INVALID_HANDLE_VALUE) + { + send404(cn); + removeconnection(cn); + return; + } +#else + + cn->dirp = opendir(actualfile); + + if (cn->dirp == NULL) + { + send404(cn); + removeconnection(cn); + return; + } + + // Get rid of the "." + readdir(cn->dirp); +#endif + + sprintf(buf, "HTTP/1.1 200 OK\nContent-Type: text/html\n\n\nDirectory Listing\n

Directory listing of %s://%s%s


\n", cn->is_ssl ? "https" : "http", cn->virtualhostreq, cn->filereq); + special_write(cn, buf, strlen(buf)); + cn->state = STATE_DOING_DIR; +} + +void procdodir(struct connstruct *cn) +{ +#ifndef WIN32 + struct dirent *dp; +#endif + char buf[MAXREQUESTLENGTH]; + char encbuf[1024]; + int putslash; + char *file; + + do + { +#ifdef WIN32 + if (!FindNextFile(cn->dirp, &cn->file_data)) +#else + if ((dp = readdir(cn->dirp)) == NULL) +#endif + { + snprintf(buf, sizeof(buf), "\n"); + special_write(cn, buf, strlen(buf)); + removeconnection(cn); + return; + } + +#ifdef WIN32 + file = cn->file_data.cFileName; +#else + file = dp->d_name; +#endif + + if (cn->filereq[0] == '/' && cn->filereq[1] == '\0' && + strcmp(file, "..") == 0) continue; + + snprintf(buf, sizeof(buf), "%s%s", cn->actualfile, file); + putslash = isdir(buf); + + urlencode(file, encbuf); + snprintf(buf, sizeof(buf), "%s%s
\n", + encbuf, putslash ? "/" : "", file, putslash ? "/" : ""); + special_write(cn, buf, strlen(buf)); + + } + while (issockwriteable(cn->networkdesc)); +} +#endif + +void procreadhead(struct connstruct *cn) +{ + char buf[MAXREQUESTLENGTH*4], *tp, *next; + int rv; + + rv = special_read(cn, buf, sizeof(buf)-1); + if (rv <= 0) { + if (rv < 0) + removeconnection(cn); + return; + } + + buf[rv] = '\0'; + + next = tp = buf; + + // Split up lines and send to procheadelem() + while(*next != '\0') + { + // If we have a blank line, advance to next stage! + if (*next == '\r' || *next == '\n') + { + buildactualfile(cn); + + cn->state = STATE_WANT_TO_SEND_HEAD; + return; + } + + while(*next != '\r' && *next != '\n' && *next != '\0') + next++; + + if (*next == '\r') + { + *next = '\0'; + next+=2; + } + else if (*next == '\n') + *next++ = '\0'; + + if (procheadelem(cn, tp) == 0) + return; + + tp = next; + } +} + +/* In this function we assume that the file has been checked for + * maliciousness (".."s, etc) and has been decoded + */ +void procsendhead(struct connstruct *cn) +{ + char buf[1024]; + char actualfile[1024]; + struct stat stbuf; + time_t now = cn->timeout - CONFIG_HTTP_TIMEOUT; + char date[32]; + strcpy(date, ctime(&now)); + + strcpy(actualfile, cn->actualfile); + +#ifdef WIN32 + /* stat() under win32 can't deal with trail slash */ + if (actualfile[strlen(actualfile)-1] == '\\') + actualfile[strlen(actualfile)-1] = 0; +#endif + + if (stat(actualfile, &stbuf) == -1) + { +#if defined(CONFIG_HTTP_HAS_CGI) + if (trycgi_withpathinfo(cn) == 0) + { // We Try To Find A CGI + proccgi(cn,1); + return; + } +#endif + + send404(cn); + removeconnection(cn); + return; + } + +#if defined(CONFIG_HTTP_HAS_CGI) + if (iscgi(cn->actualfile)) + { +#ifndef WIN32 + // Set up CGI script + if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile)) + { + send404(cn); + removeconnection(cn); + return; + } +#endif + + proccgi(cn,0); + return; + } +#endif + + if ((stbuf.st_mode & S_IFMT) == S_IFDIR) + { + if (cn->filereq[strlen(cn->filereq)-1] != '/') + { + send301(cn); + removeconnection(cn); + return; + } + + // Check to see if this dir has an index file + if (procindex(cn, &stbuf) == 0) + { +#if defined(CONFIG_HTTP_DIRECTORIES) + // If not, we do a directory listing of it + procdirlisting(cn); +#else + send404(cn); + removeconnection(cn); +#endif + return; + } + +#if defined(CONFIG_HTTP_HAS_CGI) + // If the index is a CGI file, handle it like any other CGI + if (iscgi(cn->actualfile)) + { + // Set up CGI script + if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile)) + { + send404(cn); + removeconnection(cn); + return; + } + + proccgi(cn,0); + return; + } +#endif + // If the index isn't a CGI, we continue on with the index file + } + + if ((stbuf.st_mode & S_IFMT) == S_IFDIR) + { + if (cn->filereq[strlen(cn->filereq)-1] != '/') + { + send301(cn); + removeconnection(cn); + return; + } + + // Check to see if this dir has an index file + if (procindex(cn, &stbuf) == 0) + { +#if defined(CONFIG_HTTP_DIRECTORIES) + // If not, we do a directory listing of it + procdirlisting(cn); +#endif + return; + } + +#if defined(CONFIG_HTTP_HAS_CGI) + // If the index is a CGI file, handle it like any other CGI + if (iscgi(cn->actualfile)) + { + // Set up CGI script + if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile)) + { + send404(cn); + removeconnection(cn); + return; + } + + proccgi(cn,0); + return; + } +#endif + // If the index isn't a CGI, we continue on with the index file + } + + if (cn->modified_since) + { + snprintf(buf, sizeof(buf), "HTTP/1.1 304 Not Modified\nServer: axhttpd V%s\nDate: %s\n", VERSION, date); + special_write(cn, buf, strlen(buf)); + cn->modified_since = 0; + cn->state = STATE_WANT_TO_READ_HEAD; + return; + } + else + { +#ifdef CONFIG_HTTP_VERBOSE + printf("axhttpd: %s send %s\n", + cn->is_ssl ? "https" : "http", cn->actualfile); + TTY_FLUSH(); +#endif + + snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: axhttpd V%s\nContent-Type: %s\nContent-Length: %ld\nDate: %sLast-Modified: %s\n", + VERSION, + getmimetype(cn->actualfile), + (long) stbuf.st_size, + date, + ctime(&(stbuf.st_mtime))); // ctime() has a \n on the end + } + + special_write(cn, buf, strlen(buf)); + + if (cn->reqtype == TYPE_HEAD) + { + removeconnection(cn); + return; + } + else + { + int flags = O_RDONLY; +#if defined(WIN32) || defined(CYGWIN) + flags |= O_BINARY; +#endif + + cn->filedesc = open(cn->actualfile, flags); + if (cn->filedesc == -1) + { + send404(cn); + removeconnection(cn); + return; + } + +#ifdef WIN32 + for (;;) + { + procreadfile(cn); + if (cn->filedesc == -1) + break; + + do + { + procsendfile(cn); + } while (cn->state != STATE_WANT_TO_READ_FILE); + } +#else + cn->state = STATE_WANT_TO_READ_FILE; +#endif + return; + } +} + +void procreadfile(struct connstruct *cn) +{ + int rv = read(cn->filedesc, cn->databuf, BLOCKSIZE); + + if (rv == 0 || rv == -1) + { + close(cn->filedesc); + cn->filedesc = -1; + if (cn->close_when_done) /* close immediately */ + removeconnection(cn); + else + { /* keep socket open - HTTP 1.1 */ + cn->state = STATE_WANT_TO_READ_HEAD; + cn->numbytes = 0; + } + + return; + } + + cn->numbytes = rv; + cn->state = STATE_WANT_TO_SEND_FILE; +} + +void procsendfile(struct connstruct *cn) +{ + int rv = special_write(cn, cn->databuf, cn->numbytes); + + if (rv < 0) + removeconnection(cn); + else if (rv == cn->numbytes) + cn->state = STATE_WANT_TO_READ_FILE; + else if (rv == 0) + { /* Do nothing */ } + else + { + memmove(cn->databuf, cn->databuf + rv, cn->numbytes - rv); + cn->numbytes -= rv; + } +} + +int special_write(struct connstruct *cn, const uint8_t *buf, size_t count) +{ + int res; + + if (cn->is_ssl) + { + SSL *ssl = ssl_find(servers->ssl_ctx, cn->networkdesc); + if (ssl) + { + res = ssl_write(ssl, (unsigned char *)buf, count); + } + else + return -1; + } + else + res = SOCKET_WRITE(cn->networkdesc, buf, count); + + return res; +} + +static int special_read(struct connstruct *cn, void *buf, size_t count) +{ + int res; + + if (cn->is_ssl) + { + SSL *ssl = ssl_find(servers->ssl_ctx, cn->networkdesc); + unsigned char *read_buf; + + if ((res = ssl_read(ssl, &read_buf)) > SSL_OK) + memcpy(buf, read_buf, res > (int)count ? count : res); + } + else + res = SOCKET_READ(cn->networkdesc, buf, count); + + return res; +} + +static void send301(struct connstruct *cn) +{ + char buf[2048]; + sprintf(buf, "HTTP/1.1 301 Moved Permanently\nLocation: %s/\n\n\n\n301 Moved Permanently\n\n

Moved Permanently

\nThe document has moved here.

\n


\n\n", cn->filereq, cn->filereq); + special_write(cn, buf, strlen(buf)); +} + +static void send404(struct connstruct *cn) +{ + char buf[1024]; + sprintf(buf, "HTTP/1.0 404 Not Found\nContent-Type: text/html\n\n\n404 Not Found

It ain't there my friend. (404 Not Found)

\n\n"); + special_write(cn, buf, strlen(buf)); +} + +// Returns 0 if no index was found and doesn't modify cn->actualfile +// Returns 1 if an index was found and puts the index in cn->actualfile +// and puts its stat info into stp +static int procindex(struct connstruct *cn, struct stat *stp) +{ + char tbuf[MAXREQUESTLENGTH]; + struct indexstruct *tp; + + tp = indexlist; + + while(tp != NULL) { + sprintf(tbuf, "%s%s%s", cn->actualfile, +#ifdef WIN32 + "\\", +#else + "/", +#endif + tp->name); + + if (stat(tbuf, stp) != -1) + { + my_strncpy(cn->actualfile, tbuf, MAXREQUESTLENGTH); + return 1; + } + + tp = tp->next; + } + + return 0; +} + +#if defined(CONFIG_HTTP_HAS_CGI) +static void proccgi(struct connstruct *cn, int has_pathinfo) +{ + int tpipe[2]; + char *myargs[5]; + char buf[MAXREQUESTLENGTH]; +#ifdef WIN32 + int tmp_stdout; +#else + int fv; +#endif + + snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\nServer: axhttpd V%s\n%s", + VERSION, (cn->reqtype == TYPE_HEAD) ? "\n" : ""); + special_write(cn, buf, strlen(buf)); + + if (cn->reqtype == TYPE_HEAD) + { + removeconnection(cn); + return; + } + +#ifndef WIN32 + if (pipe(tpipe) == -1) + { + removeconnection(cn); + return; + } + + fv = fork(); + + if (fv == -1) + { + close(tpipe[0]); + close(tpipe[1]); + removeconnection(cn); + return; + } + + if (fv != 0) + { + // Close the write descriptor + close(tpipe[1]); + cn->filedesc = tpipe[0]; + cn->state = STATE_WANT_TO_READ_FILE; + return; + } + + // The problem child... + + // Our stdout/stderr goes to the socket + dup2(tpipe[1], 1); + dup2(tpipe[1], 2); + + // If it was a POST request, send the socket data to our stdin + if (cn->reqtype == TYPE_POST) + dup2(cn->networkdesc, 0); + else // Otherwise we can shutdown the read side of the sock + shutdown(cn->networkdesc, 0); + + close(tpipe[0]); + close(tpipe[1]); + + myargs[0] = cn->actualfile; + myargs[1] = cn->cgiargs; + myargs[2] = NULL; + + if (!has_pathinfo) + { + my_strncpy(cn->cgipathinfo, "/", MAXREQUESTLENGTH); + my_strncpy(cn->cgiscriptinfo, cn->filereq, MAXREQUESTLENGTH); + } + + execv(cn->actualfile, myargs); +#else /* WIN32 */ + if (_pipe(tpipe, 4096, O_BINARY| O_NOINHERIT) == -1) + { + removeconnection(cn); + return; + } + + myargs[0] = "sh"; + myargs[1] = "-c"; + myargs[2] = cn->actualfile; + myargs[3] = cn->cgiargs; + myargs[4] = NULL; + + /* convert all the forward slashes to back slashes */ + { + char *t = myargs[2]; + while ((t = strchr(t, '\\'))) + { + *t++ = '/'; + } + } + + tmp_stdout = _dup(_fileno(stdout)); + _dup2(tpipe[1], _fileno(stdout)); + close(tpipe[1]); + + /* change to suit execution method */ + if (spawnl(P_NOWAIT, "c:\\Program Files\\cygwin\\bin\\sh.exe", + myargs[0], myargs[1], myargs[2], myargs[3], myargs[4]) == -1) + { + removeconnection(cn); + return; + } + + _dup2(tmp_stdout, _fileno(stdout)); + close(tmp_stdout); + cn->filedesc = tpipe[0]; + cn->state = STATE_WANT_TO_READ_FILE; + + for (;;) + { + procreadfile(cn); + + if (cn->filedesc == -1) + break; + + procsendfile(cn); + usleep(200000); /* don't know why this delay makes it work (yet) */ + } +#endif +} +#endif /* CONFIG_HTTP_HAS_CGI */ + +/* Encode funny chars -> %xx in newly allocated storage */ +/* (preserves '/' !) */ +static void urlencode(unsigned char *s, unsigned char *t) +{ + uint8_t *p, *tp; + + tp =t ; + + for (p=s; *p; p++) + { + if ((*p > 0x00 && *p < ',') || + (*p > '9' && *p < 'A') || + (*p > 'Z' && *p < '_') || + (*p > '_' && *p < 'a') || + (*p > 'z' && *p < 0xA1)) { + sprintf((char *)tp, "%%%02X", *p); + tp += 3; + } + else + { + *tp = *p; + tp++; + } + } + + *tp='\0'; +} + +/* Decode string %xx -> char (in place) */ +static void urldecode(char *buf) +{ + int v; + char *p, *s, *w; + + w = p = buf; + + while (*p) + { + v = 0; + + if (*p=='%') + { + s = p; + s++; + + if (isxdigit((int) s[0]) && isxdigit((int) s[1])) + { + v = hexit(s[0])*16+hexit(s[1]); + if (v) + { /* do not decode %00 to null char */ + *w=(char)v; + p=&s[1]; + } + } + + } + + if (!v) + *w=*p; + p++; w++; + } + + *w='\0'; +} + +static int hexit(char c) +{ + if ( c >= '0' && c <= '9' ) + return c - '0'; + if ( c >= 'a' && c <= 'f' ) + return c - 'a' + 10; + if ( c >= 'A' && c <= 'F' ) + return c - 'A' + 10; + + return 0; +} + diff --git a/httpd/socket.c b/httpd/socket.c new file mode 100644 index 000000000..e7faa78e8 --- /dev/null +++ b/httpd/socket.c @@ -0,0 +1,129 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "axhttp.h" + +#ifdef HAVE_IPV6 + +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 (connfd == -1) + return; + + 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 +void handlenewconnection(int listenfd, int is_ssl) +{ + struct sockaddr_in their_addr; + int tp = sizeof(struct sockaddr_in); + int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp); + + if (connfd == -1) + return; + + addconnection(connfd, inet_ntoa(their_addr.sin_addr), is_ssl); +} +#endif + +int openlistener(int port) +{ + int sd; +#ifdef WIN32 + char tp=1; +#else + int tp=1; +#endif + struct sockaddr_in my_addr; + + if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + return -1; + + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &tp, sizeof(tp)); + my_addr.sin_family = AF_INET; // host byte order + my_addr.sin_port = htons((short)port); // short, network byte order + my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP + memset(&(my_addr.sin_zero), 0, 8); // zero the rest of the struct + + if (bind(sd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) + { + close(sd); + return -1; + } + + if (listen(sd, BACKLOG) == -1) + { + close(sd); + return -1; + } + + return sd; +} + +#ifdef HAVE_IPV6 +int openlistener6(int port) +{ + int sd,tp; + struct sockaddr_in6 my_addr; + + if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) + return -1; + + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &tp, sizeof(tp)); + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin6_family = AF_INET6; + my_addr.sin6_port = htons(port); + + if (bind(sd, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1) + { + close(sd); + return -1; + } + + if (listen(sd, BACKLOG) == -1) + { + close(sd); + return -1; + } + + return sd; +} +#endif