uhttpd:
- rework url parsing and path resolving - handle more cgi quirks - change request dispatching - clean up cflags
This commit is contained in:
parent
66ffcefa55
commit
0f18174879
10 changed files with 291 additions and 344 deletions
|
@ -2,10 +2,10 @@ CGI_SUPPORT ?= 1
|
||||||
LUA_SUPPORT ?= 1
|
LUA_SUPPORT ?= 1
|
||||||
TLS_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
|
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
|
LDFLAGS += -lm -lcrypt
|
||||||
|
|
||||||
OBJ = uhttpd.o uhttpd-file.o uhttpd-utils.o
|
OBJ = uhttpd.o uhttpd-file.o uhttpd-utils.o
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "uhttpd.h"
|
#include "uhttpd.h"
|
||||||
#include "uhttpd-cgi.h"
|
|
||||||
#include "uhttpd-utils.h"
|
#include "uhttpd-utils.h"
|
||||||
|
#include "uhttpd-cgi.h"
|
||||||
|
|
||||||
static struct http_response * uh_cgi_header_parse(char *buf, int len, int *off)
|
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 i, hdroff, bufoff;
|
||||||
int hdrlen = 0;
|
int hdrlen = 0;
|
||||||
|
@ -134,7 +134,6 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
|
||||||
|
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
struct http_response *res;
|
struct http_response *res;
|
||||||
struct uh_path_info *pi;
|
|
||||||
|
|
||||||
|
|
||||||
/* spawn pipes for me->child, child->me */
|
/* spawn pipes for me->child, child->me */
|
||||||
|
@ -170,143 +169,129 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
|
||||||
dup2(rfd[1], 1);
|
dup2(rfd[1], 1);
|
||||||
dup2(wfd[0], 0);
|
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) &&
|
||||||
/* check for regular, world-executable file */
|
(pi->stat.st_mode & S_IXOTH)
|
||||||
if( (pi->stat.st_mode & S_IFREG) &&
|
) {
|
||||||
(pi->stat.st_mode & S_IXOTH)
|
/* build environment */
|
||||||
) {
|
clearenv();
|
||||||
/* build environment */
|
|
||||||
clearenv();
|
|
||||||
|
|
||||||
/* common information */
|
/* common information */
|
||||||
setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
|
setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
|
||||||
setenv("SERVER_SOFTWARE", "uHTTPd", 1);
|
setenv("SERVER_SOFTWARE", "uHTTPd", 1);
|
||||||
setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1);
|
setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1);
|
||||||
|
|
||||||
#ifdef HAVE_TLS
|
#ifdef HAVE_TLS
|
||||||
/* https? */
|
/* https? */
|
||||||
if( cl->tls )
|
if( cl->tls )
|
||||||
setenv("HTTPS", "on", 1);
|
setenv("HTTPS", "on", 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* addresses */
|
/* addresses */
|
||||||
setenv("SERVER_NAME", sa_straddr(&cl->servaddr), 1);
|
setenv("SERVER_NAME", sa_straddr(&cl->servaddr), 1);
|
||||||
setenv("SERVER_ADDR", sa_straddr(&cl->servaddr), 1);
|
setenv("SERVER_ADDR", sa_straddr(&cl->servaddr), 1);
|
||||||
setenv("SERVER_PORT", sa_strport(&cl->servaddr), 1);
|
setenv("SERVER_PORT", sa_strport(&cl->servaddr), 1);
|
||||||
setenv("REMOTE_HOST", sa_straddr(&cl->peeraddr), 1);
|
setenv("REMOTE_HOST", sa_straddr(&cl->peeraddr), 1);
|
||||||
setenv("REMOTE_ADDR", sa_straddr(&cl->peeraddr), 1);
|
setenv("REMOTE_ADDR", sa_straddr(&cl->peeraddr), 1);
|
||||||
setenv("REMOTE_PORT", sa_strport(&cl->peeraddr), 1);
|
setenv("REMOTE_PORT", sa_strport(&cl->peeraddr), 1);
|
||||||
|
|
||||||
/* path information */
|
/* path information */
|
||||||
setenv("SCRIPT_NAME", pi->name, 1);
|
setenv("SCRIPT_NAME", pi->name, 1);
|
||||||
setenv("SCRIPT_FILENAME", pi->phys, 1);
|
setenv("SCRIPT_FILENAME", pi->phys, 1);
|
||||||
setenv("SCRIPT_WORKDIR", pi->wdir, 1); /* nonstandard */
|
setenv("DOCUMENT_ROOT", pi->root, 1);
|
||||||
setenv("DOCUMENT_ROOT", pi->root, 1);
|
setenv("QUERY_STRING", pi->query ? pi->query : "", 1);
|
||||||
setenv("QUERY_STRING", pi->query ? pi->query : "", 1);
|
|
||||||
|
|
||||||
if( pi->info )
|
if( pi->info )
|
||||||
setenv("PATH_INFO", pi->info, 1);
|
setenv("PATH_INFO", pi->info, 1);
|
||||||
|
|
||||||
|
|
||||||
/* http version */
|
/* http version */
|
||||||
if( req->version > 1.0 )
|
if( req->version > 1.0 )
|
||||||
setenv("SERVER_PROTOCOL", "HTTP/1.1", 1);
|
setenv("SERVER_PROTOCOL", "HTTP/1.1", 1);
|
||||||
else
|
|
||||||
setenv("SERVER_PROTOCOL", "HTTP/1.0", 1);
|
|
||||||
|
|
||||||
/* request method */
|
|
||||||
switch( req->method )
|
|
||||||
{
|
|
||||||
case UH_HTTP_MSG_GET:
|
|
||||||
setenv("REQUEST_METHOD", "GET", 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UH_HTTP_MSG_HEAD:
|
|
||||||
setenv("REQUEST_METHOD", "HEAD", 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UH_HTTP_MSG_POST:
|
|
||||||
setenv("REQUEST_METHOD", "POST", 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* request url */
|
|
||||||
setenv("REQUEST_URI", req->url, 1);
|
|
||||||
|
|
||||||
/* request message headers */
|
|
||||||
foreach_header(i, req->headers)
|
|
||||||
{
|
|
||||||
if( ! strcasecmp(req->headers[i], "Accept") )
|
|
||||||
setenv("HTTP_ACCEPT", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Accept-Charset") )
|
|
||||||
setenv("HTTP_ACCEPT_CHARSET", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Accept-Encoding") )
|
|
||||||
setenv("HTTP_ACCEPT_ENCODING", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Accept-Language") )
|
|
||||||
setenv("HTTP_ACCEPT_LANGUAGE", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Authorization") )
|
|
||||||
setenv("HTTP_AUTHORIZATION", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Connection") )
|
|
||||||
setenv("HTTP_CONNECTION", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Cookie") )
|
|
||||||
setenv("HTTP_COOKIE", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Host") )
|
|
||||||
setenv("HTTP_HOST", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Referer") )
|
|
||||||
setenv("HTTP_REFERER", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "User-Agent") )
|
|
||||||
setenv("HTTP_USER_AGENT", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Content-Type") )
|
|
||||||
setenv("CONTENT_TYPE", req->headers[i+1], 1);
|
|
||||||
|
|
||||||
else if( ! strcasecmp(req->headers[i], "Content-Length") )
|
|
||||||
setenv("CONTENT_LENGTH", req->headers[i+1], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* execute child code ... */
|
|
||||||
if( chdir(pi->wdir) )
|
|
||||||
perror("chdir()");
|
|
||||||
|
|
||||||
execl(pi->phys, pi->phys, NULL);
|
|
||||||
|
|
||||||
/* in case it fails ... */
|
|
||||||
printf(
|
|
||||||
"Status: 500 Internal Server Error\r\n\r\n"
|
|
||||||
"Unable to launch the requested CGI program:\n"
|
|
||||||
" %s: %s\n",
|
|
||||||
pi->phys, strerror(errno)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 403 */
|
|
||||||
else
|
else
|
||||||
|
setenv("SERVER_PROTOCOL", "HTTP/1.0", 1);
|
||||||
|
|
||||||
|
/* request method */
|
||||||
|
switch( req->method )
|
||||||
{
|
{
|
||||||
printf(
|
case UH_HTTP_MSG_GET:
|
||||||
"Status: 403 Forbidden\r\n\r\n"
|
setenv("REQUEST_METHOD", "GET", 1);
|
||||||
"Access to this resource is forbidden\n"
|
break;
|
||||||
);
|
|
||||||
|
case UH_HTTP_MSG_HEAD:
|
||||||
|
setenv("REQUEST_METHOD", "HEAD", 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UH_HTTP_MSG_POST:
|
||||||
|
setenv("REQUEST_METHOD", "POST", 1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* request url */
|
||||||
|
setenv("REQUEST_URI", req->url, 1);
|
||||||
|
|
||||||
|
/* request message headers */
|
||||||
|
foreach_header(i, req->headers)
|
||||||
|
{
|
||||||
|
if( ! strcasecmp(req->headers[i], "Accept") )
|
||||||
|
setenv("HTTP_ACCEPT", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Accept-Charset") )
|
||||||
|
setenv("HTTP_ACCEPT_CHARSET", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Accept-Encoding") )
|
||||||
|
setenv("HTTP_ACCEPT_ENCODING", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Accept-Language") )
|
||||||
|
setenv("HTTP_ACCEPT_LANGUAGE", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Authorization") )
|
||||||
|
setenv("HTTP_AUTHORIZATION", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Connection") )
|
||||||
|
setenv("HTTP_CONNECTION", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Cookie") )
|
||||||
|
setenv("HTTP_COOKIE", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Host") )
|
||||||
|
setenv("HTTP_HOST", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Referer") )
|
||||||
|
setenv("HTTP_REFERER", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "User-Agent") )
|
||||||
|
setenv("HTTP_USER_AGENT", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Content-Type") )
|
||||||
|
setenv("CONTENT_TYPE", req->headers[i+1], 1);
|
||||||
|
|
||||||
|
else if( ! strcasecmp(req->headers[i], "Content-Length") )
|
||||||
|
setenv("CONTENT_LENGTH", req->headers[i+1], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* execute child code ... */
|
||||||
|
if( chdir(pi->root) )
|
||||||
|
perror("chdir()");
|
||||||
|
|
||||||
|
execl(pi->phys, pi->phys, NULL);
|
||||||
|
|
||||||
|
/* in case it fails ... */
|
||||||
|
printf(
|
||||||
|
"Status: 500 Internal Server Error\r\n\r\n"
|
||||||
|
"Unable to launch the requested CGI program:\n"
|
||||||
|
" %s: %s\n",
|
||||||
|
pi->phys, strerror(errno)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 404 */
|
/* 403 */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
"Status: 404 Not Found\r\n\r\n"
|
"Status: 403 Forbidden\r\n\r\n"
|
||||||
"Unable to launch the requested CGI program:\n"
|
"Access to this resource is forbidden\n"
|
||||||
" No such file or directory\n"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,6 +466,15 @@ void uh_cgi_request(struct client *cl, struct http_request *req)
|
||||||
/* looks like eof from child */
|
/* looks like eof from child */
|
||||||
else
|
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 */
|
/* send final chunk if we're in chunked transfer mode */
|
||||||
uh_http_send(cl, req, "", 0);
|
uh_http_send(cl, req, "", 0);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -6,15 +6,8 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
|
|
||||||
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
|
||||||
struct path_info {
|
);
|
||||||
char *root;
|
|
||||||
char *wdir;
|
|
||||||
char *phys;
|
|
||||||
char *name;
|
|
||||||
char *info;
|
|
||||||
char *query;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
#define _BSD_SOURCE /* scandir() ... */
|
#define _BSD_SOURCE /* scandir() ... */
|
||||||
|
|
||||||
#include "uhttpd.h"
|
#include "uhttpd.h"
|
||||||
#include "uhttpd-file.h"
|
|
||||||
#include "uhttpd-utils.h"
|
#include "uhttpd-utils.h"
|
||||||
|
#include "uhttpd-file.h"
|
||||||
|
|
||||||
#include "uhttpd-mimetypes.h"
|
#include "uhttpd-mimetypes.h"
|
||||||
|
|
||||||
|
@ -296,40 +296,38 @@ 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;
|
int fd, rlen;
|
||||||
char buf[UH_LIMIT_MSGHEAD];
|
char buf[UH_LIMIT_MSGHEAD];
|
||||||
struct uh_path_info *pi;
|
|
||||||
|
|
||||||
/* obtain path information */
|
/* we have a file */
|
||||||
if( (pi = uh_path_lookup(cl, req->url)) != NULL )
|
if( (pi->stat.st_mode & S_IFREG) && ((fd = open(pi->phys, O_RDONLY)) > 0) )
|
||||||
{
|
{
|
||||||
/* we have a file */
|
/* test preconditions */
|
||||||
if( (pi->stat.st_mode & S_IFREG) &&
|
if(
|
||||||
((fd = open(pi->phys, O_RDONLY)) > 0)
|
uh_file_if_modified_since(cl, req, &pi->stat) &&
|
||||||
|
uh_file_if_match(cl, req, &pi->stat) &&
|
||||||
|
uh_file_if_range(cl, req, &pi->stat) &&
|
||||||
|
uh_file_if_unmodified_since(cl, req, &pi->stat) &&
|
||||||
|
uh_file_if_none_match(cl, req, &pi->stat)
|
||||||
) {
|
) {
|
||||||
/* test preconditions */
|
/* write status */
|
||||||
if(
|
uh_file_response_200(cl, req, &pi->stat);
|
||||||
uh_file_if_modified_since(cl, req, &pi->stat) &&
|
|
||||||
uh_file_if_match(cl, req, &pi->stat) &&
|
|
||||||
uh_file_if_range(cl, req, &pi->stat) &&
|
|
||||||
uh_file_if_unmodified_since(cl, req, &pi->stat) &&
|
|
||||||
uh_file_if_none_match(cl, req, &pi->stat)
|
|
||||||
) {
|
|
||||||
/* write status */
|
|
||||||
uh_file_response_200(cl, req, &pi->stat);
|
|
||||||
|
|
||||||
uh_http_sendf(cl, NULL, "Content-Type: %s\r\n", uh_file_mime_lookup(pi->name));
|
uh_http_sendf(cl, NULL, "Content-Type: %s\r\n", uh_file_mime_lookup(pi->name));
|
||||||
uh_http_sendf(cl, NULL, "Content-Length: %i\r\n", pi->stat.st_size);
|
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 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);
|
uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
|
||||||
|
|
||||||
/* close header */
|
/* close header */
|
||||||
uh_http_send(cl, NULL, "\r\n", -1);
|
uh_http_send(cl, NULL, "\r\n", -1);
|
||||||
|
|
||||||
|
/* send body */
|
||||||
|
if( req->method != UH_HTTP_MSG_HEAD )
|
||||||
|
{
|
||||||
/* pump file data */
|
/* pump file data */
|
||||||
while( (rlen = read(fd, buf, sizeof(buf))) > 0 )
|
while( (rlen = read(fd, buf, sizeof(buf))) > 0 )
|
||||||
{
|
{
|
||||||
|
@ -339,44 +337,37 @@ void uh_file_request(struct client *cl, struct http_request *req)
|
||||||
/* send trailer in chunked mode */
|
/* send trailer in chunked mode */
|
||||||
uh_http_send(cl, req, "", 0);
|
uh_http_send(cl, req, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* one of the preconditions failed, terminate opened header and exit */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uh_http_send(cl, NULL, "\r\n", -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* directory */
|
/* one of the preconditions failed, terminate opened header and exit */
|
||||||
else if( pi->stat.st_mode & S_IFDIR )
|
|
||||||
{
|
|
||||||
/* write status */
|
|
||||||
uh_file_response_200(cl, req, NULL);
|
|
||||||
|
|
||||||
if( req->version > 1.0 )
|
|
||||||
uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
|
|
||||||
|
|
||||||
uh_http_send(cl, NULL, "Content-Type: text/html\r\n\r\n", -1);
|
|
||||||
|
|
||||||
/* content */
|
|
||||||
uh_file_dirlist(cl, req, pi);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 403 */
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uh_http_sendhf(cl, 403, "Forbidden",
|
uh_http_send(cl, NULL, "\r\n", -1);
|
||||||
"Access to this resource is forbidden");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 404 */
|
/* directory */
|
||||||
|
else if( pi->stat.st_mode & S_IFDIR )
|
||||||
|
{
|
||||||
|
/* write status */
|
||||||
|
uh_file_response_200(cl, req, NULL);
|
||||||
|
|
||||||
|
if( req->version > 1.0 )
|
||||||
|
uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1);
|
||||||
|
|
||||||
|
uh_http_send(cl, NULL, "Content-Type: text/html\r\n\r\n", -1);
|
||||||
|
|
||||||
|
/* content */
|
||||||
|
uh_file_dirlist(cl, req, pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 403 */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uh_http_sendhf(cl, 404, "Not Found",
|
uh_http_sendhf(cl, 403, "Forbidden",
|
||||||
"No such file or directory");
|
"Access to this resource is forbidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#ifndef _UHTTPD_CGI_
|
#ifndef _UHTTPD_FILE_
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
@ -13,6 +13,8 @@ struct mimetype {
|
||||||
const char *mime;
|
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
|
#endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "uhttpd.h"
|
#include "uhttpd.h"
|
||||||
#include "uhttpd-lua.h"
|
|
||||||
#include "uhttpd-utils.h"
|
#include "uhttpd-utils.h"
|
||||||
|
#include "uhttpd-lua.h"
|
||||||
|
|
||||||
|
|
||||||
static int uh_lua_recv(lua_State *L)
|
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;
|
int i;
|
||||||
char *query_string;
|
char *query_string;
|
||||||
|
const char *prefix = cl->server->conf->lua_prefix;
|
||||||
const char *err_str = NULL;
|
const char *err_str = NULL;
|
||||||
|
|
||||||
/* put handler callback on stack */
|
/* 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_pushstring(L, query_string + 1);
|
||||||
lua_setfield(L, -2, "query_string");
|
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_setfield(L, -2, "path_info");
|
{
|
||||||
|
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");
|
lua_setfield(L, -2, "path_info");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
lua_State * uh_lua_init();
|
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
|
#endif
|
||||||
|
|
|
@ -344,9 +344,7 @@ struct uh_path_info * uh_path_lookup(struct client *cl, const char *url)
|
||||||
char *docroot = cl->server->conf->docroot;
|
char *docroot = cl->server->conf->docroot;
|
||||||
char *pathptr = NULL;
|
char *pathptr = NULL;
|
||||||
|
|
||||||
int skip = 0;
|
int i = 0;
|
||||||
int plen = 0;
|
|
||||||
|
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
|
||||||
|
|
||||||
|
@ -355,129 +353,101 @@ struct uh_path_info * uh_path_lookup(struct client *cl, const char *url)
|
||||||
memset(buffer, 0, sizeof(buffer));
|
memset(buffer, 0, sizeof(buffer));
|
||||||
memset(&p, 0, sizeof(p));
|
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 )
|
if( (pathptr = strchr(url, '?')) != NULL )
|
||||||
{
|
{
|
||||||
p.query = pathptr[1] ? pathptr + 1 : NULL;
|
p.query = pathptr[1] ? pathptr + 1 : NULL;
|
||||||
|
|
||||||
/* urldecode component w/o query */
|
/* urldecode component w/o query */
|
||||||
if( pathptr > url )
|
if( pathptr > url )
|
||||||
plen = uh_urldecode(
|
uh_urldecode(
|
||||||
buffer, sizeof(buffer), url,
|
&buffer[strlen(docroot)],
|
||||||
(int)(pathptr - url) - 1
|
sizeof(buffer) - strlen(docroot) - 1,
|
||||||
|
url, (int)(pathptr - url) - 1
|
||||||
);
|
);
|
||||||
else
|
|
||||||
plen = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no query string, decode all of url */
|
/* no query string, decode all of url */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
plen = uh_urldecode(
|
uh_urldecode(
|
||||||
buffer, sizeof(buffer), url, strlen(url)
|
&buffer[strlen(docroot)],
|
||||||
|
sizeof(buffer) - strlen(docroot) - 1,
|
||||||
|
url, strlen(url)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy docroot */
|
/* create canon path */
|
||||||
memcpy(path_phys, docroot, sizeof(path_phys));
|
for( i = strlen(buffer); i >= 0; i-- )
|
||||||
|
|
||||||
/* 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 )
|
|
||||||
{
|
{
|
||||||
/* test current path */
|
if( (buffer[i] == 0) || (buffer[i] == '/') )
|
||||||
if( !stat(path_phys, &p.stat) )
|
|
||||||
{
|
{
|
||||||
/* is a regular file */
|
memset(path_info, 0, sizeof(path_info));
|
||||||
if( p.stat.st_mode & S_IFREG )
|
memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1));
|
||||||
|
|
||||||
|
if( realpath(path_info, path_phys) )
|
||||||
{
|
{
|
||||||
p.root = docroot;
|
memset(path_info, 0, sizeof(path_info));
|
||||||
p.phys = path_phys;
|
memcpy(path_info, &buffer[i],
|
||||||
p.name = &path_phys[strlen(docroot)-1];
|
min(strlen(buffer) - i, sizeof(path_info) - 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* is a directory */
|
|
||||||
else if( (p.stat.st_mode & S_IFDIR) && (skip < 1) )
|
|
||||||
{
|
|
||||||
/* ensure trailing slash */
|
|
||||||
if( path_phys[plen-1] != '/' )
|
|
||||||
path_phys[plen] = '/';
|
|
||||||
|
|
||||||
/* 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++ )
|
|
||||||
{
|
|
||||||
strncat(buffer, uh_index_files[skip], 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
*pathptr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.root = docroot;
|
|
||||||
p.phys = path_phys;
|
|
||||||
p.name = &path_phys[strlen(docroot)-1];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* not found */
|
|
||||||
else if( skip )
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if( (strlen(path_phys) > strlen(docroot)) &&
|
/* check whether found path is within docroot */
|
||||||
((pathptr = strrchr(path_phys, '/')) != NULL)
|
if( strncmp(path_phys, docroot, strlen(docroot)) ||
|
||||||
) {
|
((path_phys[strlen(docroot)] != 0) &&
|
||||||
*pathptr = 0;
|
(path_phys[strlen(docroot)] != '/'))
|
||||||
skip = 1;
|
) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test current path */
|
||||||
|
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)];
|
||||||
|
p.info = path_info[0] ? path_info : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
/* is a directory */
|
||||||
|
else if( (p.stat.st_mode & S_IFDIR) && !strlen(path_info) )
|
||||||
{
|
{
|
||||||
break;
|
/* ensure trailing slash */
|
||||||
|
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( i = 0; i < array_size(uh_index_files); i++ )
|
||||||
|
{
|
||||||
|
strncat(buffer, uh_index_files[i], sizeof(buffer));
|
||||||
|
|
||||||
|
if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
|
||||||
|
{
|
||||||
|
memcpy(path_phys, buffer, sizeof(path_phys));
|
||||||
|
memcpy(&p.stat, &s, sizeof(p.stat));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pathptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.root = docroot;
|
||||||
|
p.phys = path_phys;
|
||||||
|
p.name = &path_phys[strlen(docroot)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#define min(x, y) ((x) < (y)) ? (x) : (y)
|
#define min(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
#define max(x, y) ((x) > (y)) ? (x) : (y)
|
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
#define array_size(x) \
|
#define array_size(x) \
|
||||||
(sizeof(x) / sizeof(x[0]))
|
(sizeof(x) / sizeof(x[0]))
|
||||||
|
|
|
@ -315,34 +315,6 @@ static struct http_request * uh_http_header_recv(struct client *cl)
|
||||||
return NULL;
|
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)
|
int main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
@ -357,6 +329,7 @@ int main (int argc, char **argv)
|
||||||
/* working structs */
|
/* working structs */
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct http_request *req;
|
struct http_request *req;
|
||||||
|
struct uh_path_info *pin;
|
||||||
struct client *cl;
|
struct client *cl;
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
struct config conf;
|
struct config conf;
|
||||||
|
@ -467,9 +440,9 @@ int main (int argc, char **argv)
|
||||||
|
|
||||||
/* docroot */
|
/* docroot */
|
||||||
case 'h':
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -551,9 +524,10 @@ int main (int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* default docroot */
|
/* 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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,31 +647,44 @@ int main (int argc, char **argv)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parse message header and dispatch request */
|
/* parse message header */
|
||||||
if( (req = uh_http_header_recv(cl)) != NULL )
|
if( (req = uh_http_header_recv(cl)) != NULL )
|
||||||
{
|
{
|
||||||
#ifdef HAVE_CGI
|
/* dispatch request */
|
||||||
if( strstr(req->url, conf.cgi_prefix) == req->url )
|
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 )
|
||||||
else
|
{
|
||||||
|
uh_cgi_request(cl, req, pin);
|
||||||
|
}
|
||||||
|
else
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
uh_file_request(cl, req, pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
#ifdef HAVE_LUA
|
#ifdef HAVE_LUA
|
||||||
if( (L != NULL) &&
|
/* Lua request? */
|
||||||
(strstr(req->url, conf.lua_prefix) == req->url)
|
else if( strstr(req->url, conf.lua_prefix) == req->url )
|
||||||
) {
|
{
|
||||||
uh_lua_request(cl, req, L);
|
uh_lua_request(cl, req, L);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
#endif
|
#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
|
#ifdef HAVE_TLS
|
||||||
/* free client tls context */
|
/* free client tls context */
|
||||||
|
|
Loading…
Reference in a new issue