- rework url parsing and path resolving
	- handle more cgi quirks
	- change request dispatching
	- clean up cflags
This commit is contained in:
Jo-Philipp Wich 2010-03-20 13:45:50 +00:00
parent 66ffcefa55
commit 0f18174879
10 changed files with 291 additions and 344 deletions

View file

@ -2,10 +2,10 @@ CGI_SUPPORT ?= 1
LUA_SUPPORT ?= 1
TLS_SUPPORT ?= 1
CFLAGS ?= -I./lua-5.1.4/src -I./cyassl-1.4.0/include -O0 -ggdb3
LDFLAGS ?= -L./lua-5.1.4/src -L./cyassl-1.4.0/src/.libs -lm
CFLAGS ?= -Wall -I./lua-5.1.4/src -I./cyassl-1.4.0/include -O0 -ggdb3
CFLAGS += --std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500
CFLAGS += -Wall --std=gnu99
LDFLAGS += -lm -lcrypt
OBJ = uhttpd.o uhttpd-file.o uhttpd-utils.o

View file

@ -1,6 +1,6 @@
#include "uhttpd.h"
#include "uhttpd-cgi.h"
#include "uhttpd-utils.h"
#include "uhttpd-cgi.h"
static struct http_response * uh_cgi_header_parse(char *buf, int len, int *off)
{
@ -114,7 +114,7 @@ static void uh_cgi_error_500(struct client *cl, struct http_request *req, const
}
void uh_cgi_request(struct client *cl, struct http_request *req)
void uh_cgi_request(struct client *cl, struct http_request *req, struct uh_path_info *pi)
{
int i, hdroff, bufoff;
int hdrlen = 0;
@ -134,7 +134,6 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
struct timeval timeout;
struct http_response *res;
struct uh_path_info *pi;
/* spawn pipes for me->child, child->me */
@ -170,8 +169,6 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
dup2(rfd[1], 1);
dup2(wfd[0], 0);
if( (pi = uh_path_lookup(cl, req->url)) != NULL )
{
/* check for regular, world-executable file */
if( (pi->stat.st_mode & S_IFREG) &&
(pi->stat.st_mode & S_IXOTH)
@ -201,7 +198,6 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
/* path information */
setenv("SCRIPT_NAME", pi->name, 1);
setenv("SCRIPT_FILENAME", pi->phys, 1);
setenv("SCRIPT_WORKDIR", pi->wdir, 1); /* nonstandard */
setenv("DOCUMENT_ROOT", pi->root, 1);
setenv("QUERY_STRING", pi->query ? pi->query : "", 1);
@ -276,7 +272,7 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
/* execute child code ... */
if( chdir(pi->wdir) )
if( chdir(pi->root) )
perror("chdir()");
execl(pi->phys, pi->phys, NULL);
@ -298,17 +294,6 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
"Access to this resource is forbidden\n"
);
}
}
/* 404 */
else
{
printf(
"Status: 404 Not Found\r\n\r\n"
"Unable to launch the requested CGI program:\n"
" No such file or directory\n"
);
}
close(wfd[0]);
close(rfd[1]);
@ -481,6 +466,15 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
/* looks like eof from child */
else
{
/* cgi script did not output useful stuff at all */
if( ! header_sent )
{
uh_cgi_error_500(cl, req,
"The CGI program generated an invalid response:\n\n");
uh_http_send(cl, req, hdr, hdrlen);
}
/* send final chunk if we're in chunked transfer mode */
uh_http_send(cl, req, "", 0);
break;

View file

@ -6,15 +6,8 @@
#include <sys/types.h>
#include <linux/limits.h>
void uh_cgi_request(struct client *cl, struct http_request *req);
struct path_info {
char *root;
char *wdir;
char *phys;
char *name;
char *info;
char *query;
};
void uh_cgi_request(
struct client *cl, struct http_request *req, struct uh_path_info *pi
);
#endif

View file

@ -2,8 +2,8 @@
#define _BSD_SOURCE /* scandir() ... */
#include "uhttpd.h"
#include "uhttpd-file.h"
#include "uhttpd-utils.h"
#include "uhttpd-file.h"
#include "uhttpd-mimetypes.h"
@ -296,19 +296,14 @@ static void uh_file_dirlist(struct client *cl, struct http_request *req, struct
}
void uh_file_request(struct client *cl, struct http_request *req)
void uh_file_request(struct client *cl, struct http_request *req, struct uh_path_info *pi)
{
int fd, rlen;
char buf[UH_LIMIT_MSGHEAD];
struct uh_path_info *pi;
/* obtain path information */
if( (pi = uh_path_lookup(cl, req->url)) != NULL )
{
/* we have a file */
if( (pi->stat.st_mode & S_IFREG) &&
((fd = open(pi->phys, O_RDONLY)) > 0)
) {
if( (pi->stat.st_mode & S_IFREG) && ((fd = open(pi->phys, O_RDONLY)) > 0) )
{
/* test preconditions */
if(
uh_file_if_modified_since(cl, req, &pi->stat) &&
@ -324,12 +319,15 @@ void uh_file_request(struct client *cl, struct http_request *req)
uh_http_sendf(cl, NULL, "Content-Length: %i\r\n", pi->stat.st_size);
/* if request was HTTP 1.1 we'll respond chunked */
if( req->version > 1.0 )
if( (req->version > 1.0) && (req->method != UH_HTTP_MSG_HEAD) )
uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
/* close header */
uh_http_send(cl, NULL, "\r\n", -1);
/* send body */
if( req->method != UH_HTTP_MSG_HEAD )
{
/* pump file data */
while( (rlen = read(fd, buf, sizeof(buf))) > 0 )
{
@ -339,6 +337,7 @@ void uh_file_request(struct client *cl, struct http_request *req)
/* send trailer in chunked mode */
uh_http_send(cl, req, "", 0);
}
}
/* one of the preconditions failed, terminate opened header and exit */
else
@ -370,13 +369,5 @@ void uh_file_request(struct client *cl, struct http_request *req)
uh_http_sendhf(cl, 403, "Forbidden",
"Access to this resource is forbidden");
}
}
/* 404 */
else
{
uh_http_sendhf(cl, 404, "Not Found",
"No such file or directory");
}
}

View file

@ -1,4 +1,4 @@
#ifndef _UHTTPD_CGI_
#ifndef _UHTTPD_FILE_
#include <fcntl.h>
#include <time.h>
@ -13,6 +13,8 @@ struct mimetype {
const char *mime;
};
void uh_file_request(struct client *cl, struct http_request *req);
void uh_file_request(
struct client *cl, struct http_request *req, struct uh_path_info *pi
);
#endif

View file

@ -1,6 +1,6 @@
#include "uhttpd.h"
#include "uhttpd-lua.h"
#include "uhttpd-utils.h"
#include "uhttpd-lua.h"
static int uh_lua_recv(lua_State *L)
@ -196,6 +196,7 @@ void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
{
int i;
char *query_string;
const char *prefix = cl->server->conf->lua_prefix;
const char *err_str = NULL;
/* put handler callback on stack */
@ -237,12 +238,19 @@ void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
lua_pushstring(L, query_string + 1);
lua_setfield(L, -2, "query_string");
lua_pushlstring(L, req->url, (int)(query_string - req->url));
if( (int)(query_string - req->url) > strlen(prefix) )
{
lua_pushlstring(L,
&req->url[strlen(prefix)],
(int)(query_string - req->url) - strlen(prefix)
);
lua_setfield(L, -2, "path_info");
}
else
}
else if( strlen(req->url) > strlen(prefix) )
{
lua_pushstring(L, req->url);
lua_pushstring(L, &req->url[strlen(prefix)]);
lua_setfield(L, -2, "path_info");
}

View file

@ -15,6 +15,8 @@
lua_State * uh_lua_init();
void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L);
void uh_lua_request(
struct client *cl, struct http_request *req, lua_State *L
);
#endif

View file

@ -344,9 +344,7 @@ struct uh_path_info * uh_path_lookup(struct client *cl, const char *url)
char *docroot = cl->server->conf->docroot;
char *pathptr = NULL;
int skip = 0;
int plen = 0;
int i = 0;
struct stat s;
@ -355,99 +353,92 @@ struct uh_path_info * uh_path_lookup(struct client *cl, const char *url)
memset(buffer, 0, sizeof(buffer));
memset(&p, 0, sizeof(p));
/* first separate query string from url */
/* copy docroot */
memcpy(buffer, docroot, sizeof(buffer));
/* separate query string from url */
if( (pathptr = strchr(url, '?')) != NULL )
{
p.query = pathptr[1] ? pathptr + 1 : NULL;
/* urldecode component w/o query */
if( pathptr > url )
plen = uh_urldecode(
buffer, sizeof(buffer), url,
(int)(pathptr - url) - 1
uh_urldecode(
&buffer[strlen(docroot)],
sizeof(buffer) - strlen(docroot) - 1,
url, (int)(pathptr - url) - 1
);
else
plen = 0;
}
/* no query string, decode all of url */
else
{
plen = uh_urldecode(
buffer, sizeof(buffer), url, strlen(url)
uh_urldecode(
&buffer[strlen(docroot)],
sizeof(buffer) - strlen(docroot) - 1,
url, strlen(url)
);
}
/* copy docroot */
memcpy(path_phys, docroot, sizeof(path_phys));
/* append normalized path, leave two bytes free
* for trailing slash and terminating zero byte */
plen = strlen(docroot) + uh_path_normalize(
&path_phys[strlen(docroot)],
sizeof(path_phys) - strlen(docroot) - 2,
buffer, plen
);
/* copy result to info buffer */
memcpy(path_info, path_phys, sizeof(path_info));
/* find path */
while( 1 )
/* create canon path */
for( i = strlen(buffer); i >= 0; i-- )
{
if( (buffer[i] == 0) || (buffer[i] == '/') )
{
memset(path_info, 0, sizeof(path_info));
memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1));
if( realpath(path_info, path_phys) )
{
memset(path_info, 0, sizeof(path_info));
memcpy(path_info, &buffer[i],
min(strlen(buffer) - i, sizeof(path_info) - 1));
break;
}
}
}
/* check whether found path is within docroot */
if( strncmp(path_phys, docroot, strlen(docroot)) ||
((path_phys[strlen(docroot)] != 0) &&
(path_phys[strlen(docroot)] != '/'))
) {
return NULL;
}
/* test current path */
if( !stat(path_phys, &p.stat) )
if( ! stat(path_phys, &p.stat) )
{
/* is a regular file */
if( p.stat.st_mode & S_IFREG )
{
p.root = docroot;
p.phys = path_phys;
p.name = &path_phys[strlen(docroot)-1];
/* find workdir */
if( (pathptr = strrchr(path_phys, '/')) != NULL )
{
path_info[(int)(pathptr - path_phys) + 1] = 0;
p.wdir = path_info;
}
else
{
p.wdir = docroot;
}
/* find path info */
if( path_info[strlen(path_phys)] != 0 )
{
p.info = &path_info[strlen(path_phys)];
}
break;
p.name = &path_phys[strlen(docroot)];
p.info = path_info[0] ? path_info : NULL;
}
/* is a directory */
else if( (p.stat.st_mode & S_IFDIR) && (skip < 1) )
else if( (p.stat.st_mode & S_IFDIR) && !strlen(path_info) )
{
/* ensure trailing slash */
if( path_phys[plen-1] != '/' )
path_phys[plen] = '/';
if( path_phys[strlen(path_phys)-1] != '/' )
path_phys[strlen(path_phys)] = '/';
/* try to locate index file */
memset(buffer, 0, sizeof(buffer));
memcpy(buffer, path_phys, sizeof(buffer));
pathptr = &buffer[strlen(buffer)];
for( skip = 0; skip < array_size(uh_index_files); skip++ )
for( i = 0; i < array_size(uh_index_files); i++ )
{
strncat(buffer, uh_index_files[skip], sizeof(buffer));
strncat(buffer, uh_index_files[i], sizeof(buffer));
if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
{
memset(path_info, 0, sizeof(path_info));
memcpy(path_info, path_phys, strlen(path_phys));
memcpy(path_phys, buffer, sizeof(path_phys));
memcpy(&p.stat, &s, sizeof(p.stat));
p.wdir = path_info;
break;
}
@ -456,28 +447,7 @@ struct uh_path_info * uh_path_lookup(struct client *cl, const char *url)
p.root = docroot;
p.phys = path_phys;
p.name = &path_phys[strlen(docroot)-1];
break;
}
/* not found */
else if( skip )
{
break;
}
}
else if( (strlen(path_phys) > strlen(docroot)) &&
((pathptr = strrchr(path_phys, '/')) != NULL)
) {
*pathptr = 0;
skip = 1;
}
else
{
break;
p.name = &path_phys[strlen(docroot)];
}
}

View file

@ -4,8 +4,8 @@
#include <fcntl.h>
#include <sys/stat.h>
#define min(x, y) ((x) < (y)) ? (x) : (y)
#define max(x, y) ((x) > (y)) ? (x) : (y)
#define min(x, y) (((x) < (y)) ? (x) : (y))
#define max(x, y) (((x) > (y)) ? (x) : (y))
#define array_size(x) \
(sizeof(x) / sizeof(x[0]))

View file

@ -315,34 +315,6 @@ static struct http_request * uh_http_header_recv(struct client *cl)
return NULL;
}
static int uh_docroot_resolve(const char *path, char *buf)
{
char curpath[PATH_MAX];
if( ! getcwd(curpath, sizeof(curpath)) )
{
perror("getcwd()");
return 0;
}
if( chdir(path) || !getcwd(buf, PATH_MAX) )
{
return 0;
}
else
{
buf[strlen(buf)] = '/';
}
if( chdir(curpath) )
{
perror("chdir()");
return 0;
}
return 1;
}
int main (int argc, char **argv)
{
@ -357,6 +329,7 @@ int main (int argc, char **argv)
/* working structs */
struct addrinfo hints;
struct http_request *req;
struct uh_path_info *pin;
struct client *cl;
struct sigaction sa;
struct config conf;
@ -467,9 +440,9 @@ int main (int argc, char **argv)
/* docroot */
case 'h':
if( ! uh_docroot_resolve(optarg, conf.docroot) )
if( ! realpath(optarg, conf.docroot) )
{
fprintf(stderr, "Invalid directory: %s\n", optarg);
fprintf(stderr, "Invalid directory %s: %s\n", optarg, strerror(errno));
exit(1);
}
break;
@ -551,9 +524,10 @@ int main (int argc, char **argv)
}
/* default docroot */
if( !conf.docroot[0] && !uh_docroot_resolve(".", conf.docroot) )
if( !conf.docroot[0] && !realpath(".", conf.docroot) )
{
fprintf(stderr, "Can not determine default document root\n");
fprintf(stderr, "Can not determine default document root: %s\n",
strerror(errno));
exit(1);
}
@ -673,31 +647,44 @@ int main (int argc, char **argv)
goto cleanup;
}
/* parse message header and dispatch request */
/* parse message header */
if( (req = uh_http_header_recv(cl)) != NULL )
{
#ifdef HAVE_CGI
if( strstr(req->url, conf.cgi_prefix) == req->url )
/* dispatch request */
if( (pin = uh_path_lookup(cl, req->url)) != NULL )
{
uh_cgi_request(cl, req);
#ifdef HAVE_CGI
if( strstr(pin->name, conf.cgi_prefix) == pin->name )
{
uh_cgi_request(cl, req, pin);
}
else
#endif
{
uh_file_request(cl, req, pin);
}
}
#ifdef HAVE_LUA
if( (L != NULL) &&
(strstr(req->url, conf.lua_prefix) == req->url)
) {
/* Lua request? */
else if( strstr(req->url, conf.lua_prefix) == req->url )
{
uh_lua_request(cl, req, L);
}
else
#endif
/* 404 */
else
{
uh_file_request(cl, req);
uh_http_sendhf(cl, 404, "Not Found",
"No such file or directory");
}
}
/* 400 */
else
{
uh_http_sendhf(cl, 400, "Bad Request",
"Malformed request received");
}
#ifdef HAVE_TLS
/* free client tls context */