httpd: More robust Content-length: parsing,

code reorganization (less indented)
This commit is contained in:
Denis Vlasenko 2006-11-21 00:06:28 +00:00
parent 736230e209
commit 0bb993f39b

View File

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