Module Name:    src
Committed By:   mrg
Date:           Sat Apr 18 07:28:24 UTC 2009

Modified Files:
        src/libexec/httpd: auth-bozo.c bozohttpd.8 bozohttpd.c bozohttpd.h
            cgi-bozo.c content-bozo.c daemon-bozo.c dir-index-bozo.c ssl-bozo.c
            tilde-luzah-bozo.c

Log Message:
merge bozohttpd 20090417


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/libexec/httpd/auth-bozo.c \
    src/libexec/httpd/daemon-bozo.c src/libexec/httpd/ssl-bozo.c \
    src/libexec/httpd/tilde-luzah-bozo.c
cvs rdiff -u -r1.10 -r1.11 src/libexec/httpd/bozohttpd.8
cvs rdiff -u -r1.11 -r1.12 src/libexec/httpd/bozohttpd.c \
    src/libexec/httpd/cgi-bozo.c
cvs rdiff -u -r1.7 -r1.8 src/libexec/httpd/bozohttpd.h
cvs rdiff -u -r1.3 -r1.4 src/libexec/httpd/content-bozo.c
cvs rdiff -u -r1.5 -r1.6 src/libexec/httpd/dir-index-bozo.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/libexec/httpd/auth-bozo.c
diff -u src/libexec/httpd/auth-bozo.c:1.4 src/libexec/httpd/auth-bozo.c:1.5
--- src/libexec/httpd/auth-bozo.c:1.4	Mon Mar  3 22:15:08 2008
+++ src/libexec/httpd/auth-bozo.c	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: auth-bozo.c,v 1.4 2008/03/03 22:15:08 mrg Exp $	*/
+/*	$NetBSD: auth-bozo.c,v 1.5 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: auth-bozo.c,v 1.8 2008/03/03 03:36:11 mrg Exp $	*/
+/*	$eterna: auth-bozo.c,v 1.12 2009/04/17 22:52:19 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -53,7 +51,7 @@
 /*
  * Check if HTTP authentication is required
  */
-void
+int
 auth_check(http_req *request, const char *file)
 {
 	struct stat sb;
@@ -69,9 +67,10 @@
 	else {
 		*basename++ = '\0';
 			/* ensure basename(file) != AUTH_FILE */
-		check_special_files(request, basename);
+		if (check_special_files(request, basename))
+			return 1;
 	}
-	request->hr_authrealm = dir;
+	request->hr_authrealm = bozostrdup(dir);
 
 	snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE);
 	if (stat(authfile, &sb) < 0) {
@@ -81,7 +80,8 @@
 		return;
 	}
 	if ((fp = fopen(authfile, "r")) == NULL)
-		http_error(403, request, "no permission to open authfile");
+		return http_error(403, request, "no permission to open "
+						"authfile");
 	debug((DEBUG_NORMAL,
 	    "auth_check realm `%s' dir `%s' authfile `%s' open",
 	    dir, file, authfile));
@@ -106,7 +106,21 @@
 		}
 	}
 	fclose(fp);
-	http_error(401, request, "bad auth");
+	return http_error(401, request, "bad auth");
+}
+
+void
+auth_cleanup(http_req *request)
+{
+
+	if (request == NULL)
+		return;
+	if (request->hr_authuser)
+		free(request->hr_authuser);
+	if (request->hr_authpass)
+		free(request->hr_authpass);
+	if (request->hr_authrealm)
+		free(request->hr_authrealm);
 }
 
 int
@@ -124,7 +138,7 @@
 			authbuf[alen] = '\0';
 		if (alen == -1 ||
 		    (pass = strchr(authbuf, ':')) == NULL)
-			http_error(400, request,
+			return http_error(400, request,
 			    "bad authorization field");
 		*pass++ = '\0';
 		request->hr_authuser = bozostrdup(authbuf);
@@ -138,11 +152,12 @@
 	return 0;
 }
 
-void
+int
 auth_check_special_files(http_req *request, const char *name)
 {
 	if (strcmp(name, AUTH_FILE) == 0)
-		http_error(403, request, "no permission to open authfile");
+		return http_error(403, request, "no permission to open authfile");
+	return 0;
 }
 
 void
@@ -160,7 +175,7 @@
 {
 	if (request->hr_authuser && *request->hr_authuser) {
 		spsetenv("AUTH_TYPE", "Basic", (*curenvpp)++);
-		spsetenv("REMOTEUSER", request->hr_authuser, (*curenvpp)++);
+		spsetenv("REMOTE_USER", request->hr_authuser, (*curenvpp)++);
 	}
 }
 
Index: src/libexec/httpd/daemon-bozo.c
diff -u src/libexec/httpd/daemon-bozo.c:1.4 src/libexec/httpd/daemon-bozo.c:1.5
--- src/libexec/httpd/daemon-bozo.c:1.4	Fri May  2 19:14:03 2008
+++ src/libexec/httpd/daemon-bozo.c	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: daemon-bozo.c,v 1.4 2008/05/02 19:14:03 degroote Exp $	*/
+/*	$NetBSD: daemon-bozo.c,v 1.5 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: daemon-bozo.c,v 1.9 2008/03/03 03:36:11 mrg Exp $	*/
+/*	$eterna: daemon-bozo.c,v 1.13 2009/04/17 22:52:20 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -116,7 +114,7 @@
 
 /*
  * the parent never returns from this function, only children that
- * are ready to run...
+ * are ready to run... XXXMRG - still true in fork-lesser bozo?
  */
 void
 daemon_fork()
Index: src/libexec/httpd/ssl-bozo.c
diff -u src/libexec/httpd/ssl-bozo.c:1.4 src/libexec/httpd/ssl-bozo.c:1.5
--- src/libexec/httpd/ssl-bozo.c:1.4	Sat May 10 19:25:20 2008
+++ src/libexec/httpd/ssl-bozo.c	Sat Apr 18 07:28:24 2009
@@ -1,6 +1,6 @@
-/*	$NetBSD: ssl-bozo.c,v 1.4 2008/05/10 19:25:20 mlelstv Exp $	*/
+/*	$NetBSD: ssl-bozo.c,v 1.5 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: ssl-bozo.c,v 1.7 2008/03/03 03:36:12 mrg Exp $	*/
+/*	$eterna: ssl-bozo.c,v 1.9 2008/11/06 05:08:11 mrg Exp $	*/
 
 /*
  * Copyright (c) 1997-2008 Matthew R. Green
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Index: src/libexec/httpd/tilde-luzah-bozo.c
diff -u src/libexec/httpd/tilde-luzah-bozo.c:1.4 src/libexec/httpd/tilde-luzah-bozo.c:1.5
--- src/libexec/httpd/tilde-luzah-bozo.c:1.4	Wed Feb  4 22:55:58 2009
+++ src/libexec/httpd/tilde-luzah-bozo.c	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: tilde-luzah-bozo.c,v 1.4 2009/02/04 22:55:58 tls Exp $	*/
+/*	$NetBSD: tilde-luzah-bozo.c,v 1.5 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: tilde-luzah-bozo.c,v 1.5 2008/03/03 03:36:12 mrg Exp $	*/
+/*	$eterna: tilde-luzah-bozo.c,v 1.10 2009/04/18 05:36:04 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -40,6 +38,7 @@
 
 #include <errno.h>
 #include <pwd.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -56,11 +55,13 @@
  * user_transform does this:
  *	- chdir's /~user/public_html
  *	- returns the rest of the file, index.html appended if required
+ *	- returned malloced file to serve in request->hr_file,
+ *        ala transform_request().
  *
  * transform_request() is supposed to check that we have user support
  * enabled.
  */
-char *
+int
 user_transform(request, isindex)
 	http_req *request;
 	int *isindex;
@@ -81,19 +82,23 @@
 	/* fix this up immediately */
 	if (s)
 		s[-1] = '/';
-	if (pw == NULL)
-		http_error(404, request, "no such user");
+	if (pw == NULL) {
+		(void)http_error(404, request, "no such user");
+		return 0;
+	}
 
 	debug((DEBUG_OBESE, "user %s home dir %s uid %d gid %d", pw->pw_name,
 	    pw->pw_dir, pw->pw_uid, pw->pw_gid));
 
 	if (chdir(pw->pw_dir) < 0) {
 		warning("chdir1 error: %s: %s", pw->pw_dir, strerror(errno));
-		http_error(403, request, "can't chdir to homedir");
+		(void)http_error(403, request, "can't chdir to homedir");
+		return 0;
 	}
 	if (chdir(public_html) < 0) {
 		warning("chdir2 error: %s: %s", public_html, strerror(errno));
-		http_error(403, request, "can't chdir to public_html");
+		(void)http_error(403, request, "can't chdir to public_html");
+		return 0;
 	}
 	if (s == NULL || *s == '\0') {
 		file = bozostrdup(index_html);
@@ -107,13 +112,22 @@
 
 	/* see transform_request() */
 	if (*file == '/' || strcmp(file, "..") == 0 ||
-	    strstr(file, "/..") || strstr(file, "../"))
-		http_error(403, request, "illegal request");
+	    strstr(file, "/..") || strstr(file, "../")) {
+		(void)http_error(403, request, "illegal request");
+		free(file);
+		return 0;
+	}
+
+	if (auth_check(request, file)) {
+		free(file);
+		return 0;
+	}
 
-	auth_check(request, file);
+	free(request->hr_file);
+	request->hr_file = file;
 
 	debug((DEBUG_FAT, "transform_user returning %s under %s", file,
 	    pw->pw_dir));
-	return (file);
+	return 1;
 }
 #endif /* NO_USER_SUPPORT */

Index: src/libexec/httpd/bozohttpd.8
diff -u src/libexec/httpd/bozohttpd.8:1.10 src/libexec/httpd/bozohttpd.8:1.11
--- src/libexec/httpd/bozohttpd.8:1.10	Thu Apr 16 21:26:57 2009
+++ src/libexec/httpd/bozohttpd.8	Sat Apr 18 07:28:24 2009
@@ -1,8 +1,8 @@
-.\"	$NetBSD: bozohttpd.8,v 1.10 2009/04/16 21:26:57 snj Exp $
+.\"	$NetBSD: bozohttpd.8,v 1.11 2009/04/18 07:28:24 mrg Exp $
 .\"
-.\"	$eterna: bozohttpd.8,v 1.78 2008/03/03 03:36:11 mrg Exp $
+.\"	$eterna: bozohttpd.8,v 1.84 2009/04/18 01:48:18 mrg Exp $
 .\"
-.\" Copyright (c) 1997-2008 Matthew R. Green
+.\" Copyright (c) 1997-2009 Matthew R. Green
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -13,8 +13,6 @@
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 3. The name of the author may not be used to endorse or promote products
-.\"    derived from this software without specific prior written permission.
 .\"
 .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -28,7 +26,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd December 6, 2008
+.Dd April 17, 2009
 .Dt HTTPD 8
 .Os
 .Sh NAME
@@ -363,7 +361,7 @@
 distributed with
 .Nx
 has support for HTTP Basic Authorisation enabled by default,
-the portable distribution it is excluded.
+in the portable distribution it is excluded.
 Compile
 .Nm
 with
@@ -425,6 +423,30 @@
 .Pp
 To use
 .Nm
+has support for SSLv2, SSLv3, and TLSv1 protocols that is included by
+default. It requires linking with the crypto and ssl library, using
+.Dq -lcrypto -lssl .
+To disable SSL SUPPORT compile
+.Nm
+with
+.Dq -DNO_SSL_SUPPORT
+on the compiler command line.
+.Sh EXAMPLES
+To configure set of virtual hosts, one would use an
+.Xr inetd.conf 5
+entry like:
+.Bd -literal
+http stream tcp  nowait:600 httpd /usr/pkg/libexec/bozohttpd bozohttpd -v /var/vroot /var/www
+.Ed
+.Pp
+and inside
+.Pa /var/vroot
+create a directory (or a symlink to a directory) with the same name as
+the virtual host, for each virtual host.
+Lookups for these names are done in a case-insensitive manner.
+.Pp
+To use
+.Nm
 with PHP, one must use the
 .Fl C
 option to specify a CGI handler for a particular file type.
@@ -438,22 +460,21 @@
 .Sh HISTORY
 The
 .Nm
-program was first written in perl, based on another perl http server
+program is actually called
+.Dq bozohttpd .
+It was first written in perl, based on another perl http server
 called
 .Dq tinyhttpd .
 It was then rewritten from scratch in perl, and then once again in C.
-It was known for many years as
-.Dq bozohttpd .
+From 
 .Dq bozohttpd
-version 20060517 was integrated into
-.Nx 5.0
-as
-.Nm .
+version 20060517, it has been integrated into
+.Nx .
 The focus has always been simplicity and security, with minimal features
 and regular code audits.
 This manual documents
 .Nm
-version 20080303.
+version 20090417.
 .Sh AUTHORS
 .Nm
 was written by Matthew R. Green
@@ -478,6 +499,10 @@
 .Aq [email protected]
 provided cgi-bin support fixes, and more
 .It
+DEGROOTE Arnaud
+.Aq [email protected]
+provided a fix for daemon mode
+.It
 Andrew Doran
 .Aq [email protected]
 provided directory indexing support
@@ -486,10 +511,6 @@
 .Aq [email protected]
 provided a fix for a minor (non-security) buffer overflow condition
 .It
-Zak Johnson
-.Aq [email protected]
-provided cgi-bin enhancements
-.It
 Jun-ichiro itojun Hagino, KAME
 .Aq [email protected]
 provided initial IPv6 support
@@ -498,10 +519,18 @@
 .Aq [email protected]
 provided .bzabsredirect support
 .It
+Arto Huusko
+.Aq [email protected]
+provided fixes cgi-bin
+.It
 Roland Illig
 .Aq [email protected]
 provided some off-by-one fixes
 .It
+Zak Johnson
+.Aq [email protected]
+provided cgi-bin enhancements
+.It
 Nicolas Jombart
 .Aq [email protected]
 provided fixes for HTTP basic authorisation support
@@ -537,15 +566,31 @@
 .Fl V
 option.
 .It
+Joerg Sonnenberger
+.Aq [email protected]
+implemented If-Modified-Since support
+.It
 ISIHARA Takanori
 .Aq [email protected]
 provided a man page fix
 .It
+Holger Weiss
+.Aq [email protected]
+provided http authorisation fixes
+.It
 .Aq [email protected]
 provided chroot and change-to-user support, and other various fixes
+.It
+Coyote Point provided various CGI fixes
 .El
 .Pp
 There are probably others I have forgotten (let me know if you care)
+.Pp
+Please send all updates to
+.Nm
+to
+.Aq [email protected]
+for inclusion in future releaases.
 .Sh BUGS
 .Nm
 does not handled HTTP/1.1 chunked input from the client yet.

Index: src/libexec/httpd/bozohttpd.c
diff -u src/libexec/httpd/bozohttpd.c:1.11 src/libexec/httpd/bozohttpd.c:1.12
--- src/libexec/httpd/bozohttpd.c:1.11	Mon Mar 23 12:49:28 2009
+++ src/libexec/httpd/bozohttpd.c	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: bozohttpd.c,v 1.11 2009/03/23 12:49:28 reinoud Exp $	*/
+/*	$NetBSD: bozohttpd.c,v 1.12 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: bozohttpd.c,v 1.142 2008/03/03 03:36:11 mrg Exp $	*/
+/*	$eterna: bozohttpd.c,v 1.152 2009/04/18 05:36:04 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -111,7 +109,7 @@
 #define INDEX_HTML		"index.html"
 #endif
 #ifndef SERVER_SOFTWARE
-#define SERVER_SOFTWARE		"bozohttpd/20080303"
+#define SERVER_SOFTWARE		"bozohttpd/20090417"
 #endif
 #ifndef DIRECT_ACCESS_FILE
 #define DIRECT_ACCESS_FILE	".bzdirect"
@@ -195,29 +193,26 @@
 volatile sig_atomic_t	alarmhit;
 
 static	void	parse_request(char *, char **, char **, char **, char **);
+static	void	clean_request(http_req *request);
 static	http_req *read_request(void);
-static	struct headers *addmerge_header(http_req *request, char *val,
-					char *str, ssize_t len);
+static	struct headers *addmerge_header(http_req *, char *, char *, ssize_t);
+static int mmap_and_write_part(int, off_t, size_t);
 static	void	process_request(http_req *);
 static	int	check_direct_access(http_req *request);
-static	char	*transform_request(http_req *, int *);
+static	int	transform_request(http_req *, int *);
 static	void	handle_redirect(http_req *, const char *, int);
 
-static	void	check_virtual(http_req *);
+static	int	check_virtual(http_req *);
 static	void	check_bzredirect(http_req *);
 static	void	fix_url_percent(http_req *);
-static	void	process_method(http_req *, const char *);
-static	void	process_proto(http_req *, const char *);
+static	int	process_proto(http_req *, const char *);
+static	int	process_method(http_req *, const char *);
 static	void	escape_html(http_req *);
 
 static	const char *http_errors_short(int);
 static	const char *http_errors_long(int);
 
 
-void	*bozomalloc(size_t);
-void	*bozorealloc(void *, size_t);
-char	*bozostrdup(const char *);
-
 /* bozotic io */
 int	(*bozoprintf)(const char *, ...) = printf;
 ssize_t	(*bozoread)(int, void *, size_t) = read;
@@ -277,7 +272,7 @@
 int
 main(int argc, char **argv)
 {
-	http_req *http_request;
+	http_req *request;
 	extern	char **environ;
 	char	*cleanenv[1];
 	uid_t	uid;
@@ -531,9 +526,10 @@
 	 * read and process the HTTP request.
 	 */
 	do {
-		http_request = read_request();
-		if (http_request) {
-			process_request(http_request);
+		request = read_request();
+		if (request) {
+			process_request(request);
+			clean_request(request);
 			return (0);
 		}
 	} while (bflag);
@@ -556,7 +552,9 @@
 }
 
 /*
- * convert "in" into the three parts of a request (first line)
+ * convert "in" into the three parts of a request (first line).
+ * we allocate into file and query, but return pointers into
+ * "in" for proto and method.
  */
 static void
 parse_request(char *in, char **method, char **file, char **query, char **proto)
@@ -564,13 +562,15 @@
 	ssize_t	len;
 	char	*val;
 	
-	*method = *file = *query = *proto = NULL;		/* set them up */
+	debug((DEBUG_EXPLODING, "parse in: %s", in));
+	*method = *file = *query = *proto = NULL;
 
 	len = (ssize_t)strlen(in);
 	val = bozostrnsep(&in, " \t\n\r", &len);
 	if (len < 1 || val == NULL)
 		return;
 	*method = val;
+
 	while (*in == ' ' || *in == '\t')
 		in++;
 	val = bozostrnsep(&in, " \t\n\r", &len);
@@ -581,23 +581,26 @@
 			*file = in;
 		return;
 	}
-
 	*file = val;
+
 	*query = strchr(*file, '?');
-	if (*query)  {
-	  *query = *query + 1;
-	  *(*query - 1) = '\0';
-	}
+	if (*query)
+		*(*query)++ = '\0';
 
 	if (in) {
 		while (*in && (*in == ' ' || *in == '\t'))
 			in++;
 		if (*in)
 			*proto = in;
-	}	
-	debug((DEBUG_FAT, "URL INFO: |m: %s |f: %s |q: %s |p: %s |", 
-	       *method, *file, *query, *proto));
+	}
 
+	/* allocate private copies */
+	*file = strdup(*file);
+	if (*query)
+		*query = strdup(*query);
+
+	debug((DEBUG_FAT, "url: method: \"%s\" file: \"%s\" query: \"%s\" proto: \"%s\"", 
+	       *method, *file, *query, *proto));
 }
 
 /*
@@ -611,6 +614,40 @@
 }
 
 /*
+ * cleanup a http_req after use
+ */
+static void
+clean_request(http_req *request)
+{
+	struct	headers *hdr, *ohdr = NULL;
+
+	if (request == NULL)
+		return;
+
+	/* clean up request */
+#define MF(x)	if (request->x) free(request->x)
+	MF(hr_remotehost);
+	MF(hr_remoteaddr);
+	MF(hr_serverport);
+	MF(hr_file);
+	MF(hr_query);
+#undef MF
+	auth_cleanup(request);
+	for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr;
+	    hdr = SIMPLEQ_NEXT(hdr, h_next)) {
+		free(hdr->h_value);
+		free(hdr->h_header);
+		if (ohdr)
+			free(ohdr);
+		ohdr = hdr;
+	}
+	if (ohdr)
+		free(ohdr);
+
+	free(request);
+}
+
+/*
  * This function reads a http request from stdin, returning a pointer to a
  * http_req structure, describing the request.
  */
@@ -640,8 +677,9 @@
 	request->hr_allow = request->hr_host = NULL;
 	request->hr_content_type = request->hr_content_length = NULL;
 	request->hr_range = NULL;
-	request->hr_if_modified_since = NULL;
 	request->hr_last_byte_pos = -1;
+	request->hr_if_modified_since = NULL;
+	request->hr_file = NULL;
 
 	slen = sizeof(ss);
 	if (getpeername(0, (struct sockaddr *)&ss, &slen) < 0)
@@ -687,38 +725,54 @@
 	alarm(MAX_WAIT_TIME);
 	while ((str = bozodgetln(STDIN_FILENO, &len, bozoread)) != NULL) {
 		alarm(0);
-		if (alarmhit)
-			http_error(408, NULL, "request timed out");
+		if (alarmhit) {
+			(void)http_error(408, NULL, "request timed out");
+			goto cleanup;
+		}
 		line++;
 
 		if (line == 1) {
-			str = bozostrdup(str);	/* we use this copy */
 
-			if (len < 1)
-				http_error(404, NULL, "null method");
+			if (len < 1) {
+				(void)http_error(404, NULL, "null method");
+				goto cleanup;
+			}
+
 			warning("got request ``%s'' from host %s to port %s",
 			    str,
 			    host ? host : addr ? addr : "<local>",
 			    port ? port : "<stdin>");
+#if 0
 			debug((DEBUG_FAT, "read_req, getting request: ``%s''",
 			    str));
+#endif
 
+			/* we allocate return space in file and query only */
 			parse_request(str, &method, &file, &query, &proto);
-
-			if (method == NULL)
-				http_error(404, NULL, "null method");
-			if (file == NULL)
-				http_error(404, NULL, "null file");
+			request->hr_file = file;
+			request->hr_query = query;
+			if (method == NULL) {
+				(void)http_error(404, NULL, "null method");
+				goto cleanup;
+			}
+			if (file == NULL) {
+				(void)http_error(404, NULL, "null file");
+				goto cleanup;
+			}
 
 			/*
 			 * note that we parse the proto first, so that we
 			 * can more properly parse the method and the url.
 			 */
-			request->hr_file = file;
-			request->hr_query = query;
 
-			process_proto(request, proto);
-			process_method(request, method);
+			if (process_proto(request, proto) ||
+			    process_method(request, method)) {
+				goto cleanup;
+			}
+
+			debug((DEBUG_FAT, "got file \"%s\" query \"%s\"",
+			    request->hr_file,
+			    request->hr_query ? request->hr_query : "<none>"));
 
 			/* http/0.9 has no header processing */
 			if (request->hr_proto == http_09)
@@ -733,8 +787,10 @@
 			debug((DEBUG_EXPLODING,
 			    "read_req2: after bozostrnsep: str ``%s'' val ``%s''",
 			    str, val));
-			if (val == NULL || len == -1)
-				http_error(404, request, "no header");
+			if (val == NULL || len == -1) {
+				(void)http_error(404, request, "no header");
+				goto cleanup;
+			}
 			while (*str == ' ' || *str == '\t')
 				len--, str++;
 			while (*val == ' ' || *val == '\t')
@@ -752,8 +808,10 @@
 			else if (strcasecmp(hdr->h_header, "host") == 0)
 				request->hr_host = hdr->h_value;
 			/* HTTP/1.1 rev06 draft spec: 14.20 */
-			else if (strcasecmp(hdr->h_header, "expect") == 0)
-				http_error(417, request, "we don't support Expect:");
+			else if (strcasecmp(hdr->h_header, "expect") == 0) {
+				(void)http_error(417, request, "we don't support Expect:");
+				goto cleanup;
+			}
 			else if (strcasecmp(hdr->h_header, "referrer") == 0 ||
 			         strcasecmp(hdr->h_header, "referer") == 0)
 				request->hr_referrer = hdr->h_value;
@@ -774,12 +832,44 @@
 	signal(SIGALRM, SIG_DFL);
 
 	/* RFC1945, 8.3 */
-	if (request->hr_method == HTTP_POST && request->hr_content_length == NULL)
-		http_error(400, request, "missing content length");
+	if (request->hr_method == HTTP_POST && request->hr_content_length == NULL) {
+		(void)http_error(400, request, "missing content length");
+		goto cleanup;
+	}
 
 	/* HTTP/1.1 draft rev-06, 14.23 & 19.6.1.1 */
-	if (request->hr_proto == http_11 && request->hr_host == NULL)
-		http_error(400, request, "missing Host header");
+	if (request->hr_proto == http_11 && request->hr_host == NULL) {
+		(void)http_error(400, request, "missing Host header");
+		goto cleanup;
+	}
+
+	if (request->hr_range != NULL) {
+		debug((DEBUG_FAT, "hr_range: %s", request->hr_range));
+		/* support only simple ranges %d- and %d-%d */
+		if (strchr(request->hr_range, ',') == NULL) {
+			const char *rstart, *dash;
+
+			rstart = strchr(request->hr_range, '=');
+			if (rstart != NULL) {
+				rstart++;
+				dash = strchr(rstart, '-');
+				if (dash != NULL && dash != rstart) {
+					dash++;
+					request->hr_have_range = 1;
+					request->hr_first_byte_pos =
+					    strtoll(rstart, NULL, 10);
+					if (request->hr_first_byte_pos < 0)
+						request->hr_first_byte_pos = 0;
+					if (*dash != '\0') {
+						request->hr_last_byte_pos =
+						    strtoll(dash, NULL, 10);
+						if (request->hr_last_byte_pos < 0)
+							request->hr_last_byte_pos = -1;
+					}
+				}
+			}
+		}
+	}
 
 	if (request->hr_range != NULL) {
 		debug((DEBUG_FAT, "hr_range: %s", request->hr_range));
@@ -812,6 +902,14 @@
 	debug((DEBUG_FAT, "read_request returns url %s in request", 
 	       request->hr_file));
 	return (request);
+
+cleanup:
+	clean_request(request);
+
+	/* If SSL enabled cleanup SSL structure. */
+	ssl_destroy();
+
+	return NULL;
 }
 
 /*
@@ -821,7 +919,6 @@
 addmerge_header(http_req *request, char *val, char *str, ssize_t len)
 {
 	struct	headers *hdr;
-	static char space[2] = { ' ', 0 };
 
 	/* do we exist already? */
 	SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) {
@@ -831,17 +928,15 @@
 
 	if (hdr) {
 		/* yup, merge it in */
-		if (hdr->h_value == space)
-			hdr->h_value = bozostrdup(str);
-		else  {
-			char *nval;
+		char *nval;
 
-			if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1)
-				http_error(500, NULL,
-				     "memory allocation failure");
-			free(hdr->h_value);
-			hdr->h_value = nval;
+		if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1) {
+			(void)http_error(500, NULL,
+			     "memory allocation failure");
+			return NULL;
 		}
+		free(hdr->h_value);
+		hdr->h_value = nval;
 	} else {
 		/* nope, create a new one */
 
@@ -850,7 +945,7 @@
 		if (str && *str)
 			hdr->h_value = bozostrdup(str);
 		else
-			hdr->h_value = space;
+			hdr->h_value = bozostrdup(" ");
 
 		SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next);
 		request->hr_nheaders++;
@@ -860,6 +955,47 @@
 }
 
 static int
+mmap_and_write_part(int fd, off_t first_byte_pos, size_t sz)
+{
+	size_t mappedsz; 
+	char *addr;
+	void *oaddr;
+
+	addr = mmap(0, sz, PROT_READ, MAP_SHARED, fd, first_byte_pos);
+	if (addr == (char *)-1) {
+		warning("mmap failed: %s", strerror(errno));
+		return -1;
+	}
+	oaddr = addr;
+	mappedsz = sz;
+
+#ifdef MADV_SEQUENTIAL
+	(void)madvise(addr, sz, MADV_SEQUENTIAL);
+#endif
+	while (sz > WRSZ) {
+		if (bozowrite(STDOUT_FILENO, addr, WRSZ) != WRSZ) {
+			warning("write failed: %s", strerror(errno));
+			goto out;
+		}
+		debug((DEBUG_OBESE, "wrote %d bytes", WRSZ));
+		sz -= WRSZ;
+		addr += WRSZ;
+	}
+	if (sz && (size_t)bozowrite(STDOUT_FILENO, addr, sz) != sz) {
+		warning("final write failed: %s", strerror(errno));
+		goto out;
+	}
+	debug((DEBUG_OBESE, "wrote %d bytes", (int)sz));
+ out:
+	if (munmap(oaddr, mappedsz) < 0) {
+		warning("munmap failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
 parse_http_date(const char *val, time_t *timestamp)
 {
 	char *remainder;
@@ -895,30 +1031,37 @@
 
 	/*
 	 * note that transform_request chdir()'s if required.  also note
-	 * that cgi is handed here, and a cgi request will never return
-	 * back here.
+	 * that cgi is handed here.  if transform_request() returns 0
+	 * then the request has been handled already.
 	 */
-	file = transform_request(request, &isindex);
-	if (file == NULL)
-		http_error(404, request, "empty file after transform");
+	if (transform_request(request, &isindex) == 0)
+		return;
+
+	file = request->hr_file;
 
 	fd = open(file, O_RDONLY);
 	if (fd < 0) {
 		debug((DEBUG_FAT, "open failed: %s", strerror(errno)));
 		if (errno == EPERM)
-			http_error(403, request, "no permission to open file");
+			(void)http_error(403, request, "no permission to open file");
 		else if (errno == ENOENT) {
-			if (directory_index(request, file, isindex))
-				return;
-			http_error(404, request, "no file");
+			if (directory_index(request, file, isindex)) 
+				;
+			else
+				(void)http_error(404, request, "no file");
 		} else
-			http_error(500, request, "open file");
+			(void)http_error(500, request, "open file");
+		return;
+	}
+	if (fstat(fd, &sb) < 0) {
+		(void)http_error(500, request, "can't fstat");
+		goto cleanup;
 	}
-	if (fstat(fd, &sb) < 0)
-		http_error(500, request, "can't fstat");
-	if (S_ISDIR(sb.st_mode))
+	if (S_ISDIR(sb.st_mode)) {
 		handle_redirect(request, NULL, 0);
-		/* NOTREACHED */
+		goto cleanup;
+	}
+
 	if (request->hr_if_modified_since &&
 	    parse_http_date(request->hr_if_modified_since, &timestamp) &&
 	    timestamp >= sb.st_mtime) {
@@ -926,7 +1069,7 @@
 		bozoprintf("%s 304 Not Modified\r\n", request->hr_proto);
 		bozoprintf("\r\n");
 		bozoflush(stdout);
-		exit(0);
+		goto cleanup;
 	}
 
 	/* validate requested range */
@@ -957,37 +1100,27 @@
 	bozoflush(stdout);
 
 	if (request->hr_method != HTTP_HEAD) {
-		char *addr;
-		void *oaddr;
-		size_t mappedsz; 
-		size_t sz;
-
-		sz = mappedsz = request->hr_last_byte_pos - request->hr_first_byte_pos + 1;
-		oaddr = addr = mmap(0, mappedsz, PROT_READ,
-		    MAP_SHARED, fd, request->hr_first_byte_pos);
-		if (addr == (char *)-1)
-			error(1, "mmap failed: %s", strerror(errno));
+		off_t szleft, cur_byte_pos;
 
-#ifdef MADV_SEQUENTIAL
-		madvise(addr, sz, MADV_SEQUENTIAL);
-#endif
-		while (sz > WRSZ) {
-			if (bozowrite(STDOUT_FILENO, addr, WRSZ) != WRSZ)
-				error(1, "write failed: %s", strerror(errno));
-			debug((DEBUG_OBESE, "wrote %d bytes", WRSZ));
-			sz -= WRSZ;
-			addr += WRSZ;
-		}
-		if (sz && (size_t)bozowrite(STDOUT_FILENO, addr, sz) != sz)
-			error(1, "final write failed: %s", strerror(errno));
-		debug((DEBUG_OBESE, "wrote %d bytes", (int)sz));
-		if (munmap(oaddr, mappedsz) < 0)
-			warning("munmap failed");
+		szleft =
+		     request->hr_last_byte_pos - request->hr_first_byte_pos + 1;
+		cur_byte_pos = request->hr_first_byte_pos;
+
+		while (szleft) {
+			size_t sz;
+
+			if (MMAPSZ < szleft)
+				sz = MMAPSZ;
+			else
+				sz = szleft;
+			if (mmap_and_write_part(fd, cur_byte_pos, sz))
+				goto cleanup;
+			cur_byte_pos += sz;
+			szleft -= sz;
+		}
 	}
-	/* If SSL enabled cleanup SSL structure. */
-	ssl_destroy();
+cleanup:
 	close(fd);
-	free(file);
 }
 
 /*
@@ -997,7 +1130,7 @@
  *	directory exists under vpath.  if it does, use this as the
  #	new slashdir.
  */
-static void
+static int
 check_virtual(http_req *request)
 {
 	char *file = request->hr_file, *s;
@@ -1052,7 +1185,7 @@
 		if (s == 0) {
 			if (Vflag)
 				goto use_slashdir;
-			http_error(404, request, "unknown URL");
+			return http_error(404, request, "unknown URL");
 		}
 	} else
 use_slashdir:
@@ -1062,24 +1195,25 @@
 	 * ok, nailed the correct slashdir, chdir to it
 	 */
 	if (chdir(s) < 0)
-		error(1, "can't chdir %s: %s", s, strerror(errno));
+		return http_error(404, request, "can't chdir to slashdir");
+	return 0;
 }
 
 /* make sure we're not trying to access special files */
-void
+int
 check_special_files(http_req *request, const char *name)
 {
 	/* ensure basename(name) != special files */
 	if (strcmp(name, DIRECT_ACCESS_FILE) == 0)
-		http_error(403, request,
+		return http_error(403, request,
 		    "no permission to open direct access file");
 	if (strcmp(name, REDIRECT_FILE) == 0)
-		http_error(403, request,
+		return http_error(403, request,
 		    "no permission to open redirect file");
 	if (strcmp(name, ABSREDIRECT_FILE) == 0)
-		http_error(403, request,
+		return http_error(403, request,
 		    "no permission to open redirect file");
-	auth_check_special_files(request, name);
+	return auth_check_special_files(request, name);
 }
 
 /*
@@ -1190,23 +1324,27 @@
  *	- disallow anything ending up with a file starting
  *	  at "/" or having ".." in it.
  *	- anything else is a really weird internal error
+ *	- returns malloced file to serve, if unhandled
  */
-static char *
+int
 transform_request(http_req *request, int *isindex)
 {
-        char	*new_file;  // the new file name we're going to fetch
-	char	*req_file;  // the original file in the request
+	char	*file, *newfile = NULL;
 	size_t	len;
 
-	new_file = NULL;
+	file = NULL;
 	*isindex = 0;
-	debug((DEBUG_FAT, "tf_req: url %s", request->hr_file));
+	debug((DEBUG_FAT, "tf_req: file %s", request->hr_file));
 	fix_url_percent(request);
-	check_virtual(request);
-	req_file = request->hr_file;
+	if (check_virtual(request)) {
+		goto bad_done;
+	}
+	file = request->hr_file;
 
-	if (req_file[0] != '/')
-		http_error(404, request, "unknown URL");
+	if (file[0] != '/') {
+		(void)http_error(404, request, "unknown URL");
+		goto bad_done;
+	}
 
 	check_bzredirect(request);
 
@@ -1233,14 +1371,14 @@
 			   "checking referrer \"%s\" vs myname %s", r, myname));
 			if (strncmp(r, "http://";, 7) != 0 ||
 			    (strncasecmp(r + 7, myname, strlen(myname)) != 0 &&
-			     !TOP_PAGE(req_file)))
+			     !TOP_PAGE(file)))
 				to_indexhtml = 1;
 		} else {
 			const char *h = request->hr_host;
 
 			debug((DEBUG_FAT, "url has no referrer at all"));
 			/* if there's no referrer, let / or /index.html past */
-			if (!TOP_PAGE(req_file) ||
+			if (!TOP_PAGE(file) ||
 			    (h && strncasecmp(h, myname, strlen(myname)) != 0))
 				to_indexhtml = 1;
 		}
@@ -1250,43 +1388,52 @@
 
 			if (asprintf(&slashindexhtml, "/%s", index_html) < 0)
 				error(1, "asprintf");
-			debug((DEBUG_FAT, "rflag: redirecting %s to %s", req_file, slashindexhtml));
+			debug((DEBUG_FAT, "rflag: redirecting %s to %s", file, slashindexhtml));
 			handle_redirect(request, slashindexhtml, 0);
-			/* NOTREACHED */
+			free(slashindexhtml);
+			return 0;
 		}
 	}
 
-	len = strlen(req_file);
+	len = strlen(file);
 	if (0) {
 #ifndef NO_USER_SUPPORT
-	} else if (len > 1 && uflag && req_file[1] == '~') {
-		if (req_file[2] == '\0')
-			http_error(404, request, "missing username");
-		if (strchr(req_file + 2, '/') == NULL)
+	} else if (len > 1 && uflag && file[1] == '~') {
+		if (file[2] == '\0') {
+			(void)http_error(404, request, "missing username");
+			goto bad_done;
+		}
+		if (strchr(file + 2, '/') == NULL) {
 			handle_redirect(request, NULL, 0);
-			/* NOTREACHED */
+			return 0;
+		}
 		debug((DEBUG_FAT, "calling user_transform"));
+
 		return (user_transform(request, isindex));
 #endif /* NO_USER_SUPPORT */
 	} else if (len > 1) {
-		debug((DEBUG_FAT, "url[len-1] == %c", req_file[len-1]));
-		if (req_file[len-1] == '/') {	/* append index.html */
+		debug((DEBUG_FAT, "file[len-1] == %c", file[len-1]));
+		if (file[len-1] == '/') {	/* append index.html */
 			*isindex = 1;
 			debug((DEBUG_FAT, "appending index.html"));
-			new_file = bozomalloc(len + strlen(index_html) + 1);
-			strcpy(new_file, req_file + 1);
-			strcat(new_file, index_html);
+			newfile = bozomalloc(len + strlen(index_html) + 1);
+			strcpy(newfile, file + 1);
+			strcat(newfile, index_html);
 		} else
-			new_file = bozostrdup(req_file + 1);
+			newfile = bozostrdup(file + 1);
 	} else if (len == 1) {
 		debug((DEBUG_EXPLODING, "tf_req: len == 1"));
-		new_file = bozostrdup(index_html);
+		newfile = bozostrdup(index_html);
 		*isindex = 1;
-	} else		/* len == 0 ? */
-		http_error(500, request, "request->hr_file is nul?");
+	} else {	/* len == 0 ? */
+		(void)http_error(500, request, "request->hr_file is nul?");
+		goto bad_done;
+	}
 
-	if (new_file == NULL)
-		http_error(500, request, "internal failure");
+	if (newfile == NULL) {
+		(void)http_error(500, request, "internal failure");
+		goto bad_done;
+	}
 
 	/*
 	 * look for "http://myname/"; and deal with it as necessary.
@@ -1298,21 +1445,28 @@
 	 * XXX true security only comes from our parent using chroot(2)
 	 * before execve(2)'ing us.  or our own built in chroot(2) support.
 	 */
-	if (*new_file == '/' || strcmp(new_file, "..") == 0 ||
-	    strstr(new_file, "/..") || strstr(new_file, "../"))
-		http_error(403, request, "illegal request");
+	if (*newfile == '/' || strcmp(newfile, "..") == 0 ||
+	    strstr(newfile, "/..") || strstr(newfile, "../")) {
+		(void)http_error(403, request, "illegal request");
+		goto bad_done;
+	}
 
-	auth_check(request, new_file);
+	if (auth_check(request, newfile))
+		goto bad_done;
 
-	if (new_file && strlen(new_file)) {
-	  free(request->hr_file);
-	  request->hr_file = new_file;
-	}
+	if (strlen(newfile))
+		request->hr_file = newfile;
 
-	process_cgi(request);	
-	
-	debug((DEBUG_FAT, "transform_request returned: %s", new_file));
-	return (new_file);
+	if (process_cgi(request))
+		return 0;
+
+	debug((DEBUG_FAT, "transform_request set: %s", newfile));
+	return 1;
+bad_done:
+	debug((DEBUG_FAT, "transform_request returning: 0"));
+	if (newfile)
+		free(newfile);
+	return 0;
 }
 
 /*
@@ -1325,15 +1479,16 @@
 	char *urlbuf;
 	char portbuf[20];
 	int query = 0;
-
+	
 	if (url == NULL) {
 		if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0)
 			error(1, "asprintf");
 		url = urlbuf;
-	}
-	
+	} else
+		urlbuf = NULL;
+
 	if (request->hr_query && strlen(request->hr_query)) {
-	  query = 1;
+		query = 1;
 	}
 
 	if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0)
@@ -1377,7 +1532,8 @@
 	bozoprintf("</body></html>\n");
 head:
 	bozoflush(stdout);
-	exit(0);
+	if (urlbuf)
+		free(urlbuf);
 }
 
 /* generic header printing routine */
@@ -1471,10 +1627,7 @@
 	}
 	tmp[j] = 0;
 
-	/*
-	 * original "url" is a substring of an allocation, so we
-	 * can't touch it.  so, ignore it and replace the request.
-	 */
+	free(request->hr_file);
 	request->hr_file = tmp;
 }
 
@@ -1503,22 +1656,30 @@
 		}
 		debug((DEBUG_EXPLODING, "fu_%%: got s == %%, s[1]s[2] == %c%c",
 		    s[1], s[2]));
-		if (s[1] == '\0' || s[2] == '\0')
-			http_error(400, request,
+		if (s[1] == '\0' || s[2] == '\0') {
+			(void)http_error(400, request,
 			    "percent hack missing two chars afterwards");
-		if (s[1] == '0' && s[2] == '0')
-			http_error(404, request, "percent hack was %00");
-		if (s[1] == '2' && s[2] == 'f')
-			http_error(404, request, "percent hack was %2f (/)");
+			goto copy_rest;
+		}
+		if (s[1] == '0' && s[2] == '0') {
+			(void)http_error(404, request, "percent hack was %00");
+			goto copy_rest;
+		}
+		if (s[1] == '2' && s[2] == 'f') {
+			(void)http_error(404, request, "percent hack was %2f (/)");
+			goto copy_rest;
+		}
 			
 		buf[0] = *++s;
 		buf[1] = *++s;
 		buf[2] = '\0';
 		s++;
 		*t = (char)strtol(buf, NULL, 16);
-		debug((DEBUG_EXPLODING, "fu_%%: strtol put '%c' into *t", *t));
-		if (*t++ == '\0')
-			http_error(400, request, "percent hack got a 0 back");
+		debug((DEBUG_EXPLODING, "fu_%%: strtol put '%02x' into *t", *t));
+		if (*t++ == '\0') {
+			(void)http_error(400, request, "percent hack got a 0 back");
+			goto copy_rest;
+		}
 
 		while (*s && *s != '%') {
 			if (end && s >= end)
@@ -1526,6 +1687,12 @@
 			*t++ = *s++;
 		}
 	} while (*s);
+copy_rest:
+	while (*s) {
+		if (s >= end)
+			break;
+		*t++ = *s++;
+	}
 	*t = '\0';
 	debug((DEBUG_FAT, "fix_url_percent returns %s in url", request->hr_file));
 }
@@ -1551,28 +1718,29 @@
 	{ NULL,		0, },
 };
 
-static void
+static int
 process_method(http_req *request, const char *method)
 {
 	struct	method_map *mmp;
 
+	if (request->hr_proto == http_11)
+		request->hr_allow = "GET, HEAD, POST";
+
 	for (mmp = method_map; mmp->name; mmp++)
 		if (strcasecmp(method, mmp->name) == 0) {
 			request->hr_method = mmp->type;
 			request->hr_methodstr = mmp->name;
-			return;
+			return 0;
 		}
 
-	if (request->hr_proto == http_11)
-		request->hr_allow = "GET, HEAD, POST";
-	http_error(404, request, "unknown method");
+	return http_error(404, request, "unknown method");
 }
 
 /*
  * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent
  * to "HTTP/001.01"), we MUST parse this.
  */
-static void
+static int
 process_proto(http_req *request, const char *proto)
 {
 	char	majorstr[16], *minorstr;
@@ -1582,7 +1750,7 @@
 got_proto_09:
 		request->hr_proto = http_09;
 		debug((DEBUG_FAT, "request %s is http/0.9", request->hr_file));
-		return;
+		return 0;
 	}
 
 	if (strncasecmp(proto, "HTTP/", 5) != 0)
@@ -1614,10 +1782,10 @@
 		    request->hr_proto));
 		SIMPLEQ_INIT(&request->hr_headers);
 		request->hr_nheaders = 0;
-		return;
+		return 0;
 	}
 bad:
-	http_error(404, NULL, "unknown prototype");
+	return http_error(404, NULL, "unknown prototype");
 }
 
 #ifdef DEBUG
@@ -1675,7 +1843,7 @@
 
 /* the follow functions and variables are used in handling HTTP errors */
 /* ARGSUSED */
-void
+int
 http_error(int code, http_req *request, const char *msg)
 {
 	static	char buf[BUFSIZ];
@@ -1686,9 +1854,11 @@
 	int	size;
 
 	debug((DEBUG_FAT, "http_error %d: %s", code, msg));
-	if (header == NULL || reason == NULL)
+	if (header == NULL || reason == NULL) {
 		error(1, "http_error() failed (short = %p, long = %p)",
 		    header, reason);
+		return code;
+	}
 
 	if (request && request->hr_serverport &&
 	    strcmp(request->hr_serverport, "80") != 0)
@@ -1706,8 +1876,10 @@
 		    "</body></html>\n",
 		    header, header, request->hr_file, reason,
 		    myname, portbuf, myname, portbuf);
-		if (size >= (int)sizeof buf)
+		if (size >= (int)sizeof buf) {
 			warning("http_error buffer too small, truncated");
+			size = (int)sizeof buf;
+		}
 	} else
 		size = 0;
 
@@ -1724,7 +1896,7 @@
 		bozoprintf("%s", buf);
 	bozoflush(stdout);
 
-	exit(1);
+	return code;
 }
 
 /* short map between error code, and short/long messages */
@@ -1885,8 +2057,10 @@
 	void	*p;
 
 	p = realloc(ptr, size);
-	if (p == NULL)
-		http_error(500, NULL, "memory allocation failure");
+	if (p == NULL) {
+		(void)http_error(500, NULL, "memory allocation failure");
+		exit(1);
+	}
 	return (p);
 }
 
@@ -1896,8 +2070,10 @@
 	void	*p;
 
 	p = malloc(size);
-	if (p == NULL)
-		http_error(500, NULL, "memory allocation failure");
+	if (p == NULL) {
+		(void)http_error(500, NULL, "memory allocation failure");
+		exit(1);
+	}
 	return (p);
 }
 
@@ -1907,7 +2083,9 @@
 	char	*p;
 
 	p = strdup(str);
-	if (p == NULL)
-		http_error(500, NULL, "memory allocation failure");
+	if (p == NULL) {
+		(void)http_error(500, NULL, "memory allocation failure");
+		exit(1);
+	}
 	return (p);
 }
Index: src/libexec/httpd/cgi-bozo.c
diff -u src/libexec/httpd/cgi-bozo.c:1.11 src/libexec/httpd/cgi-bozo.c:1.12
--- src/libexec/httpd/cgi-bozo.c:1.11	Wed Mar 11 06:53:25 2009
+++ src/libexec/httpd/cgi-bozo.c	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: cgi-bozo.c,v 1.11 2009/03/11 06:53:25 mrg Exp $	*/
+/*	$NetBSD: cgi-bozo.c,v 1.12 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: cgi-bozo.c,v 1.18 2008/03/03 03:36:11 mrg Exp $	*/
+/*	$eterna: cgi-bozo.c,v 1.28 2009/04/18 05:36:04 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -85,9 +83,10 @@
 /*
  * Checks if the request has asked for a cgi-bin.  Should only be called if
  * cgibin is set.  If it starts CGIBIN_PREFIX or has a ncontent handler,
- * process the cgi, otherwise just return.
+ * process the cgi, otherwise just return.  Returns 0 if it did not handle
+ * the request.
  */
-void
+int
 process_cgi(http_req *request)
 {
 	char	buf[WRSZ];
@@ -102,16 +101,19 @@
 	int	sv[2];
 
 	if (!cgibin && !Cflag)
-		return;
+		return 0;
 
 	asprintf(&file, "/%s", request->hr_file);
-	if (request->hr_query && strlen(request->hr_query)) {
-	  query = bozostrdup(request->hr_query);
-	} else {
-	  query = NULL;
-	}
-	
-	asprintf(&url, "%s%c%s", file, query?'?':0, query);
+	if (file == NULL)
+		return 0;
+	if (request->hr_query && strlen(request->hr_query))
+		query = bozostrdup(request->hr_query);
+	else
+		query = NULL;
+
+	asprintf(&url, "%s%s%s", file, query ? "?" : "", query ? query : "");
+	if (url == NULL)
+		goto out;
 	debug((DEBUG_NORMAL, "process_cgi: url `%s'", url));
 
 	path = NULL;
@@ -121,14 +123,14 @@
 	info = NULL;
 	len = strlen(url);
 
-	auth_check(request, url + 1);
+	if (auth_check(request, url + 1))
+		goto out;
+
 	if (!cgibin || strncmp(url + 1, CGIBIN_PREFIX, CGIBIN_PREFIX_LEN) != 0) {
 		cgihandler = content_cgihandler(request, file + 1);
 		if (cgihandler == NULL) {
-		        debug((DEBUG_FAT, "process_cgi: no handler, returning"));
-			free(file);
-			free(url);
-			return;
+			debug((DEBUG_FAT, "process_cgi: no handler, returning"));
+			goto out;
 		}
 		if (len == 0 || file[len - 1] == '/')
 			append_index_html(&file);
@@ -136,6 +138,7 @@
 		    cgihandler));
 	} else if (len - 1 == CGIBIN_PREFIX_LEN)	/* url is "/cgi-bin/" */
 		append_index_html(&file);
+
 	ix = 0;
 	if (cgihandler) {
 		command = file + 1;
@@ -220,7 +223,7 @@
 	spsetenv("SCRIPT_NAME", file, curenvp++);
 	spsetenv("SCRIPT_FILENAME", file + 1, curenvp++);
 	spsetenv("SERVER_SOFTWARE", server_software, curenvp++);
-	spsetenv("REQUEST_URI", url, curenvp++);
+	spsetenv("REQUEST_URI", request->hr_file, curenvp++);
 	spsetenv("DATE_GMT", http_date(), curenvp++);
 	if (query && *query)
 		spsetenv("QUERY_STRING", query, curenvp++);
@@ -241,6 +244,9 @@
 	free(file);
 	free(url);
 
+	debug((DEBUG_FAT, "process_cgi: going exec %s, %s %s %s",
+	    path, argv[0], strornull(argv[1]), strornull(argv[2])));
+
 	if (-1 == socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv))
 		error(1, "child socketpair failed: %s", strerror(errno));
 
@@ -302,6 +308,15 @@
 	}
 	debug((DEBUG_FAT, "done processing cgi input"));
 	exit(0);
+
+ out:
+	if (query)
+		free(query);
+	if (file)
+		free(file);
+	if (url)
+		free(url);
+	return 0;
 }
 
 /*
@@ -323,7 +338,7 @@
 	SIMPLEQ_INIT(&headers);
 	write_header = nph == 0;
 	while (nph == 0 && (str = bozodgetln(in, &len, read)) != NULL) {
-		char * hdr_name, * hdr_value;
+		char	*hdr_name, *hdr_value;
 
 		if (parse_header(str, len, &hdr_name, &hdr_value))
 			break;
@@ -393,15 +408,15 @@
 }
 
 static int
-parse_header(const char * str, ssize_t len, char ** hdr_str, char ** hdr_val)
+parse_header(const char *str, ssize_t len, char **hdr_str, char **hdr_val)
 {
-	char * name, * value;
+	char	*name, *value;
 
 	/* if the string passed is zero-length bail out */
 	if (*str == '\0')
 		return -1;
 
-	name = value = bozostrdup(str);
+	value = bozostrdup(str);
 
 	/* locate the ':' separator in the header/value */
 	name = bozostrnsep(&value, ":", &len);
@@ -430,7 +445,6 @@
 	struct	content_map	*map;
 
 	debug((DEBUG_FAT, "content_cgihandler: trying file %s", file));
-
 	map = match_content_map(file, 0);
 	if (map)
 		return (map->cgihandler);

Index: src/libexec/httpd/bozohttpd.h
diff -u src/libexec/httpd/bozohttpd.h:1.7 src/libexec/httpd/bozohttpd.h:1.8
--- src/libexec/httpd/bozohttpd.h:1.7	Mon Feb  9 17:06:11 2009
+++ src/libexec/httpd/bozohttpd.h	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: bozohttpd.h,v 1.7 2009/02/09 17:06:11 joerg Exp $	*/
+/*	$NetBSD: bozohttpd.h,v 1.8 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: bozohttpd.h,v 1.18 2008/03/03 03:36:11 mrg Exp $	*/
+/*	$eterna: bozohttpd.h,v 1.25 2009/04/18 05:36:04 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -36,6 +34,8 @@
 
 #include <stdio.h>
 
+/* lots of "const" but gets free()'ed etc at times, sigh */
+
 /* headers */
 struct headers {
 	/*const*/ char *h_header;
@@ -55,8 +55,8 @@
 #define HTTP_TRACE	0x07	/* not supported */
 #define HTTP_CONNECT	0x08	/* not supported */
 	const char *hr_methodstr;
-	char	   *hr_file;
-        char       *hr_query;  
+	char	*hr_file;
+	char	*hr_query;  
 	const char *hr_proto;
 	const char *hr_content_type;
 	const char *hr_content_length;
@@ -68,9 +68,9 @@
 	int         hr_have_range;
 	off_t       hr_first_byte_pos;
 	off_t       hr_last_byte_pos;
-	const char *hr_remotehost;
-	const char *hr_remoteaddr;
-	const char *hr_serverport;
+	/*const*/ char *hr_remotehost;
+	/*const*/ char *hr_remoteaddr;
+	/*const*/ char *hr_serverport;
 #ifdef DO_HTPASSWD
 	const char *hr_authrealm;
 	const char *hr_authuser;
@@ -88,7 +88,9 @@
 	const char *cgihandler;	/* optional CGI handler */
 };
 
+/* write in upto 64KiB chunks, and mmap in upto 64MiB chunks */
 #define WRSZ	(64 * 1024)
+#define MMAPSZ	(WRSZ * 1024)
 
 /* debug flags */
 #define DEBUG_NORMAL	1
@@ -114,11 +116,12 @@
 void	warning(const char *, ...)
 		__attribute__((__format__(__printf__, 1, 2)));
 void	error(int, const char *, ...)
-		__attribute__((__format__(__printf__, 2, 3)));
-void	http_error(int, http_req *, const char *)
+		__attribute__((__format__(__printf__, 2, 3)))
+		__attribute__((__noreturn__));
+int	http_error(int, http_req *, const char *)
 		__attribute__((__noreturn__));
 
-void	check_special_files(http_req *, const char *);
+int	check_special_files(http_req *, const char *);
 char	*http_date(void);
 void	print_header(http_req *, struct stat *, const char *, const char *);
 
@@ -161,16 +164,18 @@
 
 /* auth-bozo.c */
 #ifdef DO_HTPASSWD
-extern	void	auth_check(http_req *, const char *);
+extern	int	auth_check(http_req *, const char *);
+extern	void	auth_cleanup(http_req *);
 extern	int	auth_check_headers(http_req *, char *, char *, ssize_t);
-extern	void	auth_check_special_files(http_req *, const char *);
+extern	int	auth_check_special_files(http_req *, const char *);
 extern	void	auth_check_401(http_req *, int);
 extern	void	auth_cgi_setenv(http_req *, char ***);
 extern	int	auth_cgi_count(http_req *);
 #else
-#define		auth_check(x, y)		/* nothing */
+#define		auth_check(x, y)		0
+#define		auth_cleanup(x)			/* nothing */
 #define		auth_check_headers(x, y, z, a)	0
-#define		auth_check_special_files(x, y)	/* nothing */
+#define		auth_check_special_files(x, y)	0
 #define		auth_check_401(x, y)		/* nothing */
 #define		auth_cgi_setenv(x, y)		/* nothing */
 #define		auth_cgi_count(x)		0
@@ -179,12 +184,12 @@
 
 /* cgi-bozo.c */
 #ifndef NO_CGIBIN_SUPPORT
-void	set_cgibin(char *);
-void	spsetenv(const char *env, const char *val, char **envp);
-void	process_cgi(http_req *);
-void	add_content_map_cgi(char *, char *);
+extern	void	set_cgibin(char *);
+extern	void	spsetenv(const char *env, const char *val, char **envp);
+extern	int	process_cgi(http_req *);
+extern	void	add_content_map_cgi(char *, char *);
 #else
-#define	process_cgi(r)				/* nothing */
+#define	process_cgi(r)				0
 #endif /* NO_CGIBIN_SUPPORT */
 
 
@@ -194,8 +199,8 @@
 #ifndef NO_DAEMON_MODE
 extern	char	*iflag;
 
-void	daemon_init(void);
-void	daemon_fork(void);
+extern	void	daemon_init(void);
+extern	void	daemon_fork(void);
 #else
 #define daemon_init()				/* nothing */
 #define daemon_fork()				/* nothing */
@@ -207,7 +212,9 @@
 extern	int	uflag;
 extern	const char *public_html;
 
-char *	user_transform(http_req *, int *);
+int	user_transform(http_req *, int *);
+#else
+#define user_transform(a, b)			0
 #endif /* NO_USER_SUPPORT */
 
 
@@ -222,10 +229,10 @@
 
 
 /* content-bozo.c */
-const char *content_type(http_req *, const char *);
-const char *content_encoding(http_req *, const char *);
-struct content_map *match_content_map(const char *, int);
-struct content_map *get_content_map(const char *);
+extern	const char *content_type(http_req *, const char *);
+extern	const char *content_encoding(http_req *, const char *);
+extern	struct content_map *match_content_map(const char *, int);
+extern	struct content_map *get_content_map(const char *);
 #ifndef NO_DYNAMIC_CONTENT
 void	add_content_map_mime(char *, char *, char *, char *);
 #endif

Index: src/libexec/httpd/content-bozo.c
diff -u src/libexec/httpd/content-bozo.c:1.3 src/libexec/httpd/content-bozo.c:1.4
--- src/libexec/httpd/content-bozo.c:1.3	Mon Mar  3 22:15:09 2008
+++ src/libexec/httpd/content-bozo.c	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: content-bozo.c,v 1.3 2008/03/03 22:15:09 mrg Exp $	*/
+/*	$NetBSD: content-bozo.c,v 1.4 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: content-bozo.c,v 1.9 2008/03/03 03:36:11 mrg Exp $	*/
+/*	$eterna: content-bozo.c,v 1.12 2009/04/17 22:52:20 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -36,6 +34,7 @@
 
 #include <sys/param.h>
 
+#include <errno.h>
 #include <string.h>
 
 #include "bozohttpd.h"
@@ -242,6 +241,8 @@
 	dynamic_content_map_size++;
 	dynamic_content_map = bozorealloc(dynamic_content_map,
 	    (dynamic_content_map_size + 1) * sizeof *map);
+	if (dynamic_content_map == NULL)
+		error(1, "out of memory allocating content map");
 	map = &dynamic_content_map[dynamic_content_map_size];
 	map->name = map->type = map->encoding = map->encoding11 =
 	    map->cgihandler = NULL;

Index: src/libexec/httpd/dir-index-bozo.c
diff -u src/libexec/httpd/dir-index-bozo.c:1.5 src/libexec/httpd/dir-index-bozo.c:1.6
--- src/libexec/httpd/dir-index-bozo.c:1.5	Wed Feb  4 22:55:58 2009
+++ src/libexec/httpd/dir-index-bozo.c	Sat Apr 18 07:28:24 2009
@@ -1,9 +1,9 @@
-/*	$NetBSD: dir-index-bozo.c,v 1.5 2009/02/04 22:55:58 tls Exp $	*/
+/*	$NetBSD: dir-index-bozo.c,v 1.6 2009/04/18 07:28:24 mrg Exp $	*/
 
-/*	$eterna: dir-index-bozo.c,v 1.10 2008/03/03 03:36:11 mrg Exp $	*/
+/*	$eterna: dir-index-bozo.c,v 1.14 2009/04/18 01:48:18 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2008 Matthew R. Green
+ * Copyright (c) 1997-2009 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -15,8 +15,6 @@
  *    notice, this list of conditions and the following disclaimer and
  *    dedication in the documentation and/or other materials provided
  *    with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -41,6 +39,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <string.h>
+#include <stdlib.h>
 #include <time.h>
 #include <assert.h>
 
@@ -68,6 +67,7 @@
 	DIR *dp;
 	char buf[MAXPATHLEN];
 	char spacebuf[48];
+	char *file = NULL;
 	int l, i;
 
 	if (!isindex || !Xflag)
@@ -76,7 +76,7 @@
 	if (strlen(dirname) <= strlen(index_html))
 		dirname = ".";
 	else {
-		char *file = bozostrdup(dirname);
+		file = bozostrdup(dirname);
 
 		file[strlen(file) - strlen(index_html)] = '\0';
 		dirname = file;
@@ -85,12 +85,13 @@
 	if (stat(dirname, &sb) < 0 ||
 	    (dp = opendir(dirname)) == NULL) {
 		if (errno == EPERM)
-			http_error(403, request,
+			(void)http_error(403, request,
 			    "no permission to open directory");
 		else if (errno == ENOENT)
-			http_error(404, request, "no file");
+			(void)http_error(404, request, "no file");
 		else
-			http_error(500, request, "open directory");
+			(void)http_error(500, request, "open directory");
+		goto done;
 		/* NOTREACHED */
 	}
 
@@ -104,7 +105,7 @@
 
 	if (request->hr_method == HTTP_HEAD) {
 		closedir(dp);
-		return 1;
+		goto done;
 	}
 
 	bozoprintf("<html><head><title>Index of %s</title></head>\r\n",
@@ -182,6 +183,9 @@
 	bozoprintf("</body></html>\r\n\r\n");
 	bozoflush(stdout);
 	
+done:
+	if (file)
+		free(file);
 	return 1;
 }
 #endif /* NO_DIRINDEX_SUPPORT */

Reply via email to