svn commit: trunk/busybox/networking

vda at busybox.net vda at busybox.net
Tue Nov 21 00:06:28 UTC 2006


Author: vda
Date: 2006-11-20 16:06:28 -0800 (Mon, 20 Nov 2006)
New Revision: 16593

Log:
httpd: More robust Content-length: parsing,
code reorganization (less indented)


Modified:
   trunk/busybox/networking/httpd.c


Changeset:
Modified: trunk/busybox/networking/httpd.c
===================================================================
--- trunk/busybox/networking/httpd.c	2006-11-20 19:40:36 UTC (rev 16592)
+++ trunk/busybox/networking/httpd.c	2006-11-21 00:06:28 UTC (rev 16593)
@@ -256,9 +256,11 @@
 
 
 static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
-static const char Content_length[] = "Content-length:";
 
 
+#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
+
+
 static int scan_ip(const char **ep, unsigned int *ip, unsigned char endc)
 {
 	const char *p = *ep;
@@ -880,7 +882,7 @@
 	if (config->ContentLength != -1) {    /* file */
 		strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
 		len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"\r\n",
-				timeStr, Content_length, (off_t) config->ContentLength);
+				timeStr, "Content-length:", (off_t) config->ContentLength);
 	}
 	strcat(buf, "\r\n");
 	len += 2;
@@ -962,20 +964,15 @@
 	int outFd;
 	int firstLine = 1;
 
-	do {
-		if (pipe(fromCgi) != 0) {
-			break;
-		}
-		if (pipe(toCgi) != 0) {
-			break;
-		}
+	if (pipe(fromCgi) != 0)
+		return 0;
+	if (pipe(toCgi) != 0)
+		return 0;
+	pid = fork();
+	if (pid < 0)
+		return 0;
 
-		pid = fork();
-		if (pid < 0) {
-			pid = 0;
-			break;
-		}
-
+	do {    
 		if (!pid) {
 			/* child process */
 			char *script;
@@ -1031,10 +1028,9 @@
 			if (script != NULL)
 				*script = '\0';         /* reduce /PATH_INFO */
 			 /* SCRIPT_FILENAME required by PHP in CGI mode */
-			if (realpath(purl + 1, realpath_buff))
-				setenv1("SCRIPT_FILENAME", realpath_buff);
-			else
-				*realpath_buff = '\0';
+			if (!realpath(purl + 1, realpath_buff))
+				goto error_execing_cgi;
+			setenv1("SCRIPT_FILENAME", realpath_buff);
 			/* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
 			setenv1("SCRIPT_NAME", purl);
 			setenv1("QUERY_STRING", config->query);
@@ -1045,9 +1041,8 @@
 #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
 			setenv_long("REMOTE_PORT", config->port);
 #endif
-			if (bodyLen) {
+			if (bodyLen)
 				setenv_long("CONTENT_LENGTH", bodyLen);
-			}
 			if (cookie)
 				setenv1("HTTP_COOKIE", cookie);
 			if (content_type)
@@ -1064,37 +1059,37 @@
 			/* set execve argp[0] without path */
 			argp[0] = strrchr(purl, '/') + 1;
 			/* but script argp[0] must have absolute path and chdiring to this */
-			if (*realpath_buff) {
-				script = strrchr(realpath_buff, '/');
-				if (script) {
-					*script = '\0';
-					if (chdir(realpath_buff) == 0) {
-						// now run the program.  If it fails,
-						// use _exit() so no destructors
-						// get called and make a mess.
+			script = strrchr(realpath_buff, '/');
+			if (!script)
+				goto error_execing_cgi;
+			*script = '\0';
+			if (chdir(realpath_buff) == 0) {
+				// now run the program.  If it fails,
+				// use _exit() so no destructors
+				// get called and make a mess.
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
-						char *interpr = NULL;
-						char *suffix = strrchr(purl, '.');
+				char *interpr = NULL;
+				char *suffix = strrchr(purl, '.');
 
-						if (suffix) {
-							Htaccess * cur;
-							for (cur = config->script_i; cur; cur = cur->next)
-								if (strcmp(cur->before_colon + 1, suffix) == 0) {
-									interpr = cur->after_colon;
-									break;
-								}
+				if (suffix) {
+					Htaccess *cur;
+					for (cur = config->script_i; cur; cur = cur->next) {
+						if (strcmp(cur->before_colon + 1, suffix) == 0) {
+							interpr = cur->after_colon;
+							break;
 						}
+					}
+				}
 #endif
-						*script = '/';
+				*script = '/';
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
-						if (interpr)
-							execv(interpr, argp);
-						else
+				if (interpr)
+					execv(interpr, argp);
+				else
 #endif
-							execv(realpath_buff, argp);
-					}
-				}
+					execv(realpath_buff, argp);
 			}
+ error_execing_cgi:
 			/* send to stdout (even if we are not from inetd) */
 			config->accepted_socket = 1;
 			sendHeaders(HTTP_NOT_FOUND);
@@ -1103,7 +1098,7 @@
 
 	} while (0);
 
-	if (pid) {
+	if (pid > 0) {
 		/* parent process */
 		int status;
 		size_t post_readed_size = 0, post_readed_idx = 0;
@@ -1425,7 +1420,7 @@
 	int ip_allowed;
 #if ENABLE_FEATURE_HTTPD_CGI
 	const char *prequest = request_GET;
-	long length = 0;
+	unsigned long length = 0;
 	char *cookie = 0;
 	char *content_type = 0;
 #endif
@@ -1452,11 +1447,11 @@
 
 		purl = strpbrk(buf, " \t");
 		if (purl == NULL) {
-BAD_REQUEST:
+ BAD_REQUEST:
 			sendHeaders(HTTP_BAD_REQUEST);
 			break;
 		}
-		*purl = 0;
+		*purl = '\0';
 #if ENABLE_FEATURE_HTTPD_CGI
 		if (strcasecmp(buf, prequest) != 0) {
 			prequest = "POST";
@@ -1487,29 +1482,35 @@
 		/* extract url args if present */
 		test = strchr(url, '?');
 		if (test) {
-			*test++ = 0;
+			*test++ = '\0';
 			config->query = test;
 		}
 
 		test = decodeString(url, 0);
 		if (test == NULL)
 			goto BAD_REQUEST;
+		/* FIXME: bug? should be "url+1"? */
 		if (test == (buf+1)) {
 			sendHeaders(HTTP_NOT_FOUND);
 			break;
 		}
+
 		/* algorithm stolen from libbb bb_simplify_path(),
 			 but don't strdup and reducing trailing slash and protect out root */
 		purl = test = url;
-
 		do {
 			if (*purl == '/') {
-				if (*test == '/') {        /* skip duplicate (or initial) slash */
+				/* skip duplicate (or initial) slash */
+				if (*test == '/') {
 					continue;
-				} else if (*test == '.') {
-					if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */
+				}
+				if (*test == '.') {
+					/* skip extra '.' */
+					if (test[1] == '/' || test[1] == 0) {
 						continue;
-					} else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) {
+					} else
+					/* '..': be careful */
+					if (test[1] == '.' && (test[2] == '/' || test[2] == 0)) {
 						++test;
 						if (purl == url) {
 							/* protect out root */
@@ -1522,10 +1523,9 @@
 			}
 			*++purl = *test;
 		} while (*++test);
+		*++purl = '\0';       /* so keep last character */
+		test = purl;          /* end ptr */
 
-		*++purl = 0;        /* so keep last character */
-		test = purl;        /* end ptr */
-
 		/* If URL is directory, adding '/' */
 		if (test[-1] != '/') {
 			if (is_directory(url + 1, 1, &sb)) {
@@ -1560,36 +1560,40 @@
 
 #if ENABLE_FEATURE_HTTPD_CGI
 				/* try and do our best to parse more lines */
-				if ((strncasecmp(buf, Content_length, 15) == 0)) {
-					if (prequest != request_GET)
-						length = strtol(buf + 15, 0, 0); // extra read only for POST
-				} else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
-					for (test = buf + 7; isspace(*test); test++)
-						  ;
-					cookie = strdup(test);
-				} else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) {
-					for (test = buf + 13; isspace(*test); test++)
-						  ;
-					content_type = strdup(test);
-				} else if ((strncasecmp(buf, "Referer:", 8) == 0)) {
-					for (test = buf + 8; isspace(*test); test++)
-						  ;
-					config->referer = strdup(test);
+				if ((STRNCASECMP(buf, "Content-length:") == 0)) {
+					/* extra read only for POST */
+					if (prequest != request_GET) {
+						test = buf + sizeof("Content-length:")-1;
+						if (!test[0]) goto bail_out;
+						errno = 0;
+						/* not using strtoul: it ignores leading munis! */
+						length = strtol(test, &test, 10);
+						/* length is "ulong", but we need to pass it to int later */
+						/* so we check for negative or too large values in one go: */
+						/* (long -> ulong conv will cause negatives to be seen as > INT_MAX) */
+						if (test[0] || errno || length > INT_MAX)
+							goto bail_out;
+					}
+				} else if ((STRNCASECMP(buf, "Cookie:") == 0)) {
+					cookie = strdup(skip_whitespace(buf + sizeof("Cookie:")-1));
+				} else if ((STRNCASECMP(buf, "Content-Type:") == 0)) {
+					content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1));
+				} else if ((STRNCASECMP(buf, "Referer:") == 0)) {
+					config->referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
 				}
 #endif
 
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-				if (strncasecmp(buf, "Authorization:", 14) == 0) {
+				if (STRNCASECMP(buf, "Authorization:") == 0) {
 					/* We only allow Basic credentials.
 					 * It shows up as "Authorization: Basic <userid:password>" where
 					 * the userid:password is base64 encoded.
 					 */
-					for (test = buf + 14; isspace(*test); test++)
-						;
-					if (strncasecmp(test, "Basic", 5) != 0)
+					test = skip_whitespace(buf + sizeof("Authorization:")-1);
+					if (STRNCASECMP(test, "Basic") != 0)
 						continue;
-
-					test += 5;  /* decodeBase64() skiping space self */
+					test += sizeof("Basic")-1;
+					/* decodeBase64() skips whitespace itself */
 					decodeBase64(test);
 					credentials = checkPerm(url, test);
 				}
@@ -1627,10 +1631,6 @@
 		test = url + 1;      /* skip first '/' */
 
 #if ENABLE_FEATURE_HTTPD_CGI
-		/* if strange Content-Length */
-		if (length < 0)
-			break;
-
 		if (strncmp(test, "cgi-bin", 7) == 0) {
 			if (test[7] == '/' && test[8] == 0)
 				goto FORBIDDEN;     // protect listing cgi-bin/
@@ -1654,6 +1654,8 @@
 #endif
 	} while (0);
 
+ bail_out:
+
 	if (DEBUG)
 		fprintf(stderr, "closing socket\n\n");
 #if ENABLE_FEATURE_HTTPD_CGI




More information about the busybox-cvs mailing list