include $(TOPDIR)/rules.mk
PKG_NAME:=uhttpd
-PKG_RELEASE:=14
+PKG_RELEASE:=17
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := libcyassl liblua
# Default is /cgi-bin
option cgi_prefix /cgi-bin
+ # List of extension->interpreter mappings.
+ # Files with an associated interpreter can
+ # be called outside of the CGI prefix and do
+ # not need to be executable.
+# list interpreter ".php=/usr/bin/php-cgi"
+# list interpreter ".cgi=/usr/bin/perl"
+
# Lua url prefix and handler script.
# Lua support is disabled if no prefix given.
# option lua_prefix /luci
local cfg="$1"
local realm="$(uci_get system.@system[0].hostname)"
- local listen http https
+ local listen http https interpreter path
append_arg "$cfg" home "-h"
append_arg "$cfg" realm "-r" "${realm:-OpenWrt}"
append UHTTPD_ARGS "-p $listen"
done
+ config_get interpreter "$cfg" interpreter
+ for path in $interpreter; do
+ append UHTTPD_ARGS "-i $path"
+ done
+
config_get https "$cfg" listen_https
config_get UHTTPD_KEY "$cfg" key /etc/uhttpd.key
config_get UHTTPD_CERT "$cfg" cert /etc/uhttpd.crt
}
-void uh_cgi_request(struct client *cl, struct http_request *req, struct path_info *pi)
-{
- int i, hdroff, bufoff;
+void uh_cgi_request(
+ struct client *cl, struct http_request *req,
+ struct path_info *pi, struct interpreter *ip
+) {
+ int i, hdroff, bufoff, rv;
int hdrlen = 0;
int buflen = 0;
int fd_max = 0;
dup2(rfd[1], 1);
dup2(wfd[0], 0);
- /* check for regular, world-executable file */
- if( (pi->stat.st_mode & S_IFREG) &&
- (pi->stat.st_mode & S_IXOTH)
+ /* check for regular, world-executable file _or_ interpreter */
+ if( ((pi->stat.st_mode & S_IFREG) &&
+ (pi->stat.st_mode & S_IXOTH)) || (ip != NULL)
) {
/* build environment */
clearenv();
if( chdir(pi->root) )
perror("chdir()");
- execl(pi->phys, pi->phys, NULL);
+ if( ip != NULL )
+ execl(ip->path, ip->path, pi->phys, NULL);
+ else
+ 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)
+ ip ? ip->path : pi->phys, strerror(errno)
);
}
memset(hdr, 0, sizeof(hdr));
- timeout.tv_sec = cl->server->conf->script_timeout;
- timeout.tv_usec = 0;
-
-#define ensure(x) \
- do { if( x < 0 ) goto out; } while(0)
-
/* I/O loop, watch our pipe ends and dispatch child reads/writes from/to socket */
while( 1 )
{
FD_SET(rfd[0], &reader);
FD_SET(wfd[1], &writer);
+ timeout.tv_sec = (header_sent < 1) ? cl->server->conf->script_timeout : 3;
+ timeout.tv_usec = 0;
+
+ ensure_out(rv = select_intr(fd_max, &reader,
+ (content_length > -1) ? &writer : NULL, NULL, &timeout));
+
+ /* timeout */
+ if( rv == 0 )
+ {
+ ensure_out(kill(child, 0));
+ }
+
/* wait until we can read or write or both */
- if( select_intr(fd_max, &reader,
- (content_length > -1) ? &writer : NULL, NULL,
- (header_sent < 1) ? &timeout : NULL) > 0
- ) {
+ else if( rv > 0 )
+ {
/* ready to write to cgi program */
if( FD_ISSET(wfd[1], &writer) )
{
if( content_length > 0 )
{
/* read it from socket ... */
- if( (buflen = uh_tcp_recv(cl, buf, min(content_length, sizeof(buf)))) > 0 )
+ ensure_out(buflen = uh_tcp_recv(cl, buf,
+ min(content_length, sizeof(buf))));
+
+ if( buflen > 0 )
{
/* ... and write it to child's stdin */
if( write(wfd[1], buf, buflen) < 0 )
if( (res = uh_cgi_header_parse(hdr, hdrlen, &hdroff)) != NULL )
{
/* write status */
- ensure(uh_http_sendf(cl, NULL,
+ ensure_out(uh_http_sendf(cl, NULL,
"HTTP/%.1f %03d %s\r\n"
"Connection: close\r\n",
req->version, res->statuscode,
if( !uh_cgi_header_lookup(res, "Location") &&
!uh_cgi_header_lookup(res, "Content-Type")
) {
- ensure(uh_http_send(cl, NULL,
+ ensure_out(uh_http_send(cl, NULL,
"Content-Type: text/plain\r\n", -1));
}
if( (req->version > 1.0) &&
!uh_cgi_header_lookup(res, "Transfer-Encoding")
) {
- ensure(uh_http_send(cl, NULL,
+ ensure_out(uh_http_send(cl, NULL,
"Transfer-Encoding: chunked\r\n", -1));
}
/* write headers from CGI program */
foreach_header(i, res->headers)
{
- ensure(uh_http_sendf(cl, NULL, "%s: %s\r\n",
+ ensure_out(uh_http_sendf(cl, NULL, "%s: %s\r\n",
res->headers[i], res->headers[i+1]));
}
/* terminate header */
- ensure(uh_http_send(cl, NULL, "\r\n", -1));
+ ensure_out(uh_http_send(cl, NULL, "\r\n", -1));
/* push out remaining head buffer */
if( hdroff < hdrlen )
- ensure(uh_http_send(cl, req, &hdr[hdroff], hdrlen - hdroff));
+ ensure_out(uh_http_send(cl, req, &hdr[hdroff], hdrlen - hdroff));
}
/* ... failed and head buffer exceeded */
else if( hdrlen >= sizeof(hdr) )
{
- ensure(uh_cgi_error_500(cl, req,
+ ensure_out(uh_cgi_error_500(cl, req,
"The CGI program generated an invalid response:\n\n"));
- ensure(uh_http_send(cl, req, hdr, hdrlen));
+ ensure_out(uh_http_send(cl, req, hdr, hdrlen));
}
/* ... failed but free buffer space, try again */
/* push out remaining read buffer */
if( bufoff < buflen )
- ensure(uh_http_send(cl, req, &buf[bufoff], buflen - bufoff));
+ ensure_out(uh_http_send(cl, req, &buf[bufoff], buflen - bufoff));
header_sent = 1;
continue;
/* headers complete, pass through buffer to socket */
- ensure(uh_http_send(cl, req, buf, buflen));
+ ensure_out(uh_http_send(cl, req, buf, buflen));
}
/* looks like eof from child */
* build the required headers here.
*/
- ensure(uh_http_sendf(cl, NULL,
+ ensure_out(uh_http_sendf(cl, NULL,
"HTTP/%.1f 200 OK\r\n"
"Content-Type: text/plain\r\n"
"%s\r\n",
? "Transfer-Encoding: chunked\r\n" : ""
));
- ensure(uh_http_send(cl, req, hdr, hdrlen));
+ ensure_out(uh_http_send(cl, req, hdr, hdrlen));
}
/* send final chunk if we're in chunked transfer mode */
- ensure(uh_http_send(cl, req, "", 0));
+ ensure_out(uh_http_send(cl, req, "", 0));
break;
}
}
{
if( (errno != EINTR) && ! header_sent )
{
- ensure(uh_http_sendhf(cl, 504, "Gateway Timeout",
+ ensure_out(uh_http_sendhf(cl, 504, "Gateway Timeout",
"The CGI script took too long to produce "
"a response"));
}
/* send final chunk if we're in chunked transfer mode */
- ensure(uh_http_send(cl, req, "", 0));
+ ensure_out(uh_http_send(cl, req, "", 0));
break;
}
#include <linux/limits.h>
void uh_cgi_request(
- struct client *cl, struct http_request *req, struct path_info *pi
+ struct client *cl, struct http_request *req,
+ struct path_info *pi, struct interpreter *ip
);
#endif
return NULL;
}
-#define ensure_ret(x) \
- do { if( x < 0 ) return; } while(0)
-static void uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s)
{
ensure_ret(uh_http_sendf(cl, NULL, "Connection: close\r\n"));
ensure_ret(uh_http_sendf(cl, NULL, "Last-Modified: %s\r\n", uh_file_unix2date(s->st_mtime)));
}
- ensure_ret(uh_http_sendf(cl, NULL, "Date: %s\r\n", uh_file_unix2date(time(NULL))));
+ return uh_http_sendf(cl, NULL, "Date: %s\r\n", uh_file_unix2date(time(NULL)));
}
-static void uh_file_response_200(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_200(struct client *cl, struct http_request *req, struct stat *s)
{
ensure_ret(uh_http_sendf(cl, NULL, "HTTP/%.1f 200 OK\r\n", req->version));
- uh_file_response_ok_hdrs(cl, req, s);
+ return uh_file_response_ok_hdrs(cl, req, s);
}
-static void uh_file_response_304(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_response_304(struct client *cl, struct http_request *req, struct stat *s)
{
ensure_ret(uh_http_sendf(cl, NULL, "HTTP/%.1f 304 Not Modified\r\n", req->version));
- uh_file_response_ok_hdrs(cl, req, s);
+ return uh_file_response_ok_hdrs(cl, req, s);
}
-static void uh_file_response_412(struct client *cl, struct http_request *req)
+static int uh_file_response_412(struct client *cl, struct http_request *req)
{
- ensure_ret(uh_http_sendf(cl, NULL,
+ return uh_http_sendf(cl, NULL,
"HTTP/%.1f 412 Precondition Failed\r\n"
- "Connection: close\r\n", req->version));
+ "Connection: close\r\n", req->version);
}
-static int uh_file_if_match(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_match(struct client *cl, struct http_request *req, struct stat *s, int *ok)
{
const char *tag = uh_file_mktag(s);
char *hdr = uh_file_header_lookup(req, "If-Match");
}
else if( !strcmp(p, "*") || !strcmp(p, tag) )
{
- return 1;
+ *ok = 1;
+ return *ok;
}
}
- uh_file_response_412(cl, req);
- return 0;
+ *ok = 0;
+ ensure_ret(uh_file_response_412(cl, req));
+ return *ok;
}
- return 1;
+ *ok = 1;
+ return *ok;
}
-static int uh_file_if_modified_since(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_modified_since(struct client *cl, struct http_request *req, struct stat *s, int *ok)
{
char *hdr = uh_file_header_lookup(req, "If-Modified-Since");
+ *ok = 1;
if( hdr )
{
- if( uh_file_date2unix(hdr) < s->st_mtime )
+ if( uh_file_date2unix(hdr) >= s->st_mtime )
{
- return 1;
- }
- else
- {
- uh_file_response_304(cl, req, s);
- return 0;
+ *ok = 0;
+ ensure_ret(uh_file_response_304(cl, req, s));
}
}
- return 1;
+ return *ok;
}
-static int uh_file_if_none_match(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_none_match(struct client *cl, struct http_request *req, struct stat *s, int *ok)
{
const char *tag = uh_file_mktag(s);
char *hdr = uh_file_header_lookup(req, "If-None-Match");
char *p;
int i;
+ *ok = 1;
if( hdr )
{
}
else if( !strcmp(p, "*") || !strcmp(p, tag) )
{
+ *ok = 0;
+
if( (req->method == UH_HTTP_MSG_GET) ||
(req->method == UH_HTTP_MSG_HEAD) )
- uh_file_response_304(cl, req, s);
+ ensure_ret(uh_file_response_304(cl, req, s));
else
- uh_file_response_412(cl, req);
+ ensure_ret(uh_file_response_412(cl, req));
- return 0;
+ break;
}
}
}
- return 1;
+ return *ok;
}
-static int uh_file_if_range(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_range(struct client *cl, struct http_request *req, struct stat *s, int *ok)
{
char *hdr = uh_file_header_lookup(req, "If-Range");
+ *ok = 1;
if( hdr )
{
- uh_file_response_412(cl, req);
- return 0;
+ *ok = 0;
+ ensure_ret(uh_file_response_412(cl, req));
}
- return 1;
+ return *ok;
}
-static int uh_file_if_unmodified_since(struct client *cl, struct http_request *req, struct stat *s)
+static int uh_file_if_unmodified_since(struct client *cl, struct http_request *req, struct stat *s, int *ok)
{
char *hdr = uh_file_header_lookup(req, "If-Unmodified-Since");
+ *ok = 1;
if( hdr )
{
if( uh_file_date2unix(hdr) <= s->st_mtime )
{
- uh_file_response_412(cl, req);
- return 0;
+ *ok = 0;
+ ensure_ret(uh_file_response_412(cl, req));
}
}
- return 1;
+ return *ok;
}
-#define ensure_out(x) \
- do { if( x < 0 ) goto out; } while(0)
-
static int uh_file_scandir_filter_dir(const struct dirent *e)
{
return strcmp(e->d_name, ".") ? 1 : 0;
void uh_file_request(struct client *cl, struct http_request *req, struct path_info *pi)
{
int rlen;
+ int ok = 1;
int fd = -1;
char buf[UH_LIMIT_MSGHEAD];
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) &&
- 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)
- ) {
+ if(ok) ensure_out(uh_file_if_modified_since(cl, req, &pi->stat, &ok));
+ if(ok) ensure_out(uh_file_if_match(cl, req, &pi->stat, &ok));
+ if(ok) ensure_out(uh_file_if_range(cl, req, &pi->stat, &ok));
+ if(ok) ensure_out(uh_file_if_unmodified_since(cl, req, &pi->stat, &ok));
+ if(ok) ensure_out(uh_file_if_none_match(cl, req, &pi->stat, &ok));
+
+ if( ok > 0 )
+ {
/* write status */
- uh_file_response_200(cl, req, &pi->stat);
+ ensure_out(uh_file_response_200(cl, req, &pi->stat));
ensure_out(uh_http_sendf(cl, NULL, "Content-Type: %s\r\n", uh_file_mime_lookup(pi->name)));
ensure_out(uh_http_sendf(cl, NULL, "Content-Length: %i\r\n", pi->stat.st_size));
else if( (pi->stat.st_mode & S_IFDIR) && !cl->server->conf->no_dirlists )
{
/* write status */
- uh_file_response_200(cl, req, NULL);
+ ensure_out(uh_file_response_200(cl, req, NULL));
if( req->version > 1.0 )
ensure_out(uh_http_send(cl, NULL, "Transfer-Encoding: chunked\r\n", -1));
int uh_tls_client_recv(struct client *c, void *buf, int len)
{
- return SSL_read(c->tls, buf, len);
+ int rv = SSL_read(c->tls, buf, len);
+ return (rv > 0) ? rv : -1;
}
int uh_tls_client_send(struct client *c, void *buf, int len)
{
- return SSL_write(c->tls, buf, len);
+ int rv = SSL_write(c->tls, buf, len);
+ return (rv > 0) ? rv : -1;
}
void uh_tls_client_close(struct client *c)
/* unblock SIGCHLD */
sigemptyset(&ssn);
sigaddset(&ssn, SIGCHLD);
+ sigaddset(&ssn, SIGPIPE);
sigprocmask(SIG_UNBLOCK, &ssn, &sso);
rv = select(n, r, w, e, t);
return sz;
}
-#define ensure(x) \
- do { if( x < 0 ) return -1; } while(0)
int uh_http_sendhf(struct client *cl, int code, const char *summary, const char *fmt, ...)
{
code, summary
);
- ensure(uh_tcp_send(cl, buffer, len));
+ ensure_ret(uh_tcp_send(cl, buffer, len));
va_start(ap, fmt);
len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
- ensure(uh_http_sendc(cl, buffer, len));
- ensure(uh_http_sendc(cl, NULL, 0));
+ ensure_ret(uh_http_sendc(cl, buffer, len));
+ ensure_ret(uh_http_sendc(cl, NULL, 0));
return 0;
}
if( len > 0 )
{
clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len);
- ensure(uh_tcp_send(cl, chunk, clen));
- ensure(uh_tcp_send(cl, data, len));
- ensure(uh_tcp_send(cl, "\r\n", 2));
+ ensure_ret(uh_tcp_send(cl, chunk, clen));
+ ensure_ret(uh_tcp_send(cl, data, len));
+ ensure_ret(uh_tcp_send(cl, "\r\n", 2));
}
else
{
- ensure(uh_tcp_send(cl, "0\r\n\r\n", 5));
+ ensure_ret(uh_tcp_send(cl, "0\r\n\r\n", 5));
}
return 0;
va_end(ap);
if( (req != NULL) && (req->version > 1.0) )
- ensure(uh_http_sendc(cl, buffer, len));
+ ensure_ret(uh_http_sendc(cl, buffer, len));
else if( len > 0 )
- ensure(uh_tcp_send(cl, buffer, len));
+ ensure_ret(uh_tcp_send(cl, buffer, len));
return 0;
}
len = strlen(buf);
if( (req != NULL) && (req->version > 1.0) )
- ensure(uh_http_sendc(cl, buf, len));
+ ensure_ret(uh_http_sendc(cl, buf, len));
else if( len > 0 )
- ensure(uh_tcp_send(cl, buf, len));
+ ensure_ret(uh_tcp_send(cl, buf, len));
return 0;
}
}
-static char uh_realms[UH_LIMIT_AUTHREALMS * sizeof(struct auth_realm)] = { 0 };
-static int uh_realm_count = 0;
+static struct auth_realm *uh_realms = NULL;
struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
{
struct passwd *pwd;
struct spwd *spwd;
- if( uh_realm_count < UH_LIMIT_AUTHREALMS )
+ if((new = (struct auth_realm *)malloc(sizeof(struct auth_realm))) != NULL)
{
- new = (struct auth_realm *)
- &uh_realms[uh_realm_count * sizeof(struct auth_realm)];
-
memset(new, 0, sizeof(struct auth_realm));
memcpy(new->path, path,
) {
memcpy(new->pass, pwd->pw_passwd,
min(strlen(pwd->pw_passwd), sizeof(new->pass) - 1));
- }
+ }
}
/* ordinary pwd */
if( new->pass[0] )
{
- uh_realm_count++;
+ new->next = uh_realms;
+ uh_realms = new;
+
return new;
}
+
+ free(new);
}
return NULL;
protected = 0;
/* check whether at least one realm covers the requested url */
- for( i = 0; i < uh_realm_count; i++ )
+ for( realm = uh_realms; realm; realm = realm->next )
{
- realm = (struct auth_realm *)
- &uh_realms[i * sizeof(struct auth_realm)];
-
rlen = strlen(realm->path);
if( (plen >= rlen) && !strncasecmp(pi->name, realm->path, rlen) )
if( user && pass )
{
/* find matching realm */
- for( i = 0, realm = NULL; i < uh_realm_count; i++ )
+ for( realm = uh_realms; realm; realm = realm->next )
{
- realm = (struct auth_realm *)
- &uh_realms[i * sizeof(struct auth_realm)];
-
rlen = strlen(realm->path);
if( (plen >= rlen) &&
req->realm = realm;
break;
}
-
- realm = NULL;
}
/* found a realm matching the username */
}
-static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
-static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
-
-static int uh_listener_count = 0;
-static int uh_client_count = 0;
-
+static struct listener *uh_listeners = NULL;
+static struct client *uh_clients = NULL;
struct listener * uh_listener_add(int sock, struct config *conf)
{
struct listener *new = NULL;
socklen_t sl;
- if( uh_listener_count < UH_LIMIT_LISTENERS )
+ if( (new = (struct listener *)malloc(sizeof(struct listener))) != NULL )
{
- new = (struct listener *)
- &uh_listeners[uh_listener_count * sizeof(struct listener)];
+ memset(new, 0, sizeof(struct listener));
new->socket = sock;
new->conf = conf;
memset(&(new->addr), 0, sl);
getsockname(sock, (struct sockaddr *) &(new->addr), &sl);
- uh_listener_count++;
+ new->next = uh_listeners;
+ uh_listeners = new;
+
+ return new;
}
- return new;
+ return NULL;
}
struct listener * uh_listener_lookup(int sock)
{
struct listener *cur = NULL;
- int i;
-
- for( i = 0; i < uh_listener_count; i++ )
- {
- cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
+ for( cur = uh_listeners; cur; cur = cur->next )
if( cur->socket == sock )
return cur;
- }
return NULL;
}
struct client *new = NULL;
socklen_t sl;
- if( uh_client_count < UH_LIMIT_CLIENTS )
+ if( (new = (struct client *)malloc(sizeof(struct client))) != NULL )
{
- new = (struct client *)
- &uh_clients[uh_client_count * sizeof(struct client)];
+ memset(new, 0, sizeof(struct client));
new->socket = sock;
new->server = serv;
memset(&(new->servaddr), 0, sl);
getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl);
- uh_client_count++;
+ new->next = uh_clients;
+ uh_clients = new;
}
return new;
struct client * uh_client_lookup(int sock)
{
struct client *cur = NULL;
- int i;
-
- for( i = 0; i < uh_client_count; i++ )
- {
- cur = (struct client *) &uh_clients[i * sizeof(struct client)];
+ for( cur = uh_clients; cur; cur = cur->next )
if( cur->socket == sock )
return cur;
- }
return NULL;
}
void uh_client_remove(int sock)
{
- struct client *del = uh_client_lookup(sock);
+ struct client *cur = NULL;
+ struct client *prv = NULL;
+
+ for( cur = uh_clients; cur; prv = cur, cur = cur->next )
+ {
+ if( cur->socket == sock )
+ {
+ if( prv )
+ prv->next = cur->next;
+ else
+ uh_clients = cur->next;
+
+ free(cur);
+ break;
+ }
+ }
+}
+
+
+#ifdef HAVE_CGI
+static struct interpreter *uh_interpreters = NULL;
+
+struct interpreter * uh_interpreter_add(const char *extn, const char *path)
+{
+ struct interpreter *new = NULL;
- if( del )
+ if( (new = (struct interpreter *)
+ malloc(sizeof(struct interpreter))) != NULL )
{
- memmove(del, del + 1,
- sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));
+ memset(new, 0, sizeof(struct interpreter));
+
+ memcpy(new->extn, extn, min(strlen(extn), sizeof(new->extn)-1));
+ memcpy(new->path, path, min(strlen(path), sizeof(new->path)-1));
- uh_client_count--;
+ new->next = uh_interpreters;
+ uh_interpreters = new;
+
+ return new;
}
+
+ return NULL;
}
+struct interpreter * uh_interpreter_lookup(const char *path)
+{
+ struct interpreter *cur = NULL;
+ const char *e;
+
+ for( cur = uh_interpreters; cur; cur = cur->next )
+ {
+ e = &path[max(strlen(path) - strlen(cur->extn), 0)];
+
+ if( !strcmp(e, cur->extn) )
+ return cur;
+ }
+ return NULL;
+}
+#endif
#define fd_cloexec(fd) \
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)
+#define ensure_out(x) \
+ do { if((x) < 0) goto out; } while(0)
+
+#define ensure_ret(x) \
+ do { if((x) < 0) return -1; } while(0)
+
+
struct path_info {
char *root;
char *phys;
struct client * uh_client_lookup(int sock);
void uh_client_remove(int sock);
+#ifdef HAVE_CGI
+struct interpreter * uh_interpreter_add(const char *extn, const char *path);
+struct interpreter * uh_interpreter_lookup(const char *path);
+#endif
+
#endif
{
FILE *c;
char line[512];
- char *user = NULL;
- char *pass = NULL;
+ char *col1 = NULL;
+ char *col2 = NULL;
char *eol = NULL;
const char *path = conf->file ? conf->file : "/etc/httpd.conf";
{
if( (line[0] == '/') && (strchr(line, ':') != NULL) )
{
- if( !(user = strchr(line, ':')) || (*user++ = 0) ||
- !(pass = strchr(user, ':')) || (*pass++ = 0) ||
- !(eol = strchr(pass, '\n')) || (*eol++ = 0) )
+ if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+ !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
+ !(eol = strchr(col2, '\n')) || (*eol++ = 0) )
continue;
- if( !uh_auth_add(line, user, pass) )
+ if( !uh_auth_add(line, col1, col2) )
{
fprintf(stderr,
"Notice: No password set for user %s, ignoring "
- "authentication on %s\n", user, line
+ "authentication on %s\n", col1, line
);
}
}
else if( !strncmp(line, "I:", 2) )
{
- if( !(user = strchr(line, ':')) || (*user++ = 0) ||
- !(eol = strchr(user, '\n')) || (*eol++ = 0) )
+ if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+ !(eol = strchr(col1, '\n')) || (*eol++ = 0) )
continue;
- conf->index_file = strdup(user);
+ conf->index_file = strdup(col1);
}
else if( !strncmp(line, "E404:", 5) )
{
- if( !(user = strchr(line, ':')) || (*user++ = 0) ||
- !(eol = strchr(user, '\n')) || (*eol++ = 0) )
+ if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
+ !(eol = strchr(col1, '\n')) || (*eol++ = 0) )
continue;
- conf->error_handler = strdup(user);
+ conf->error_handler = strdup(col1);
}
+#ifdef HAVE_CGI
+ else if( (line[0] == '*') && (strchr(line, ':') != NULL) )
+ {
+ if( !(col1 = strchr(line, '*')) || (*col1++ = 0) ||
+ !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
+ !(eol = strchr(col2, '\n')) || (*eol++ = 0) )
+ continue;
+
+ if( !uh_interpreter_add(col1, col2) )
+ {
+ fprintf(stderr,
+ "Unable to add interpreter %s for extension %s: "
+ "Out of memory\n", col2, col1
+ );
+ }
+ }
+#endif
}
fclose(c);
int status;
int bound = 0;
+ int tcp_ka_idl = 1;
+ int tcp_ka_int = 1;
+ int tcp_ka_cnt = 3;
+
struct listener *l = NULL;
struct addrinfo *addrs = NULL, *p = NULL;
}
/* "address already in use" */
- if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1 )
+ if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) )
{
perror("setsockopt()");
goto error;
}
+ /* TCP keep-alive */
+ if( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) ||
+ setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &tcp_ka_idl, sizeof(tcp_ka_idl)) ||
+ setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int)) ||
+ setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &tcp_ka_cnt, sizeof(tcp_ka_cnt)) )
+ {
+ fprintf(stderr, "Notice: Unable to enable TCP keep-alive: %s\n",
+ strerror(errno));
+ }
+
/* required to get parallel v4 + v6 working */
if( p->ai_family == AF_INET6 )
{
/* add listener to global list */
if( ! (l = uh_listener_add(sock, conf)) )
{
- fprintf(stderr,
- "uh_listener_add(): Can not create more than "
- "%i listen sockets\n", UH_LIMIT_LISTENERS
- );
-
+ fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n");
goto error;
}
ssize_t blen = sizeof(buffer)-1;
ssize_t rlen = 0;
-
memset(buffer, 0, sizeof(buffer));
while( blen > 0 )
if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0 )
{
/* receive data */
- rlen = uh_tcp_peek(cl, bufptr, blen);
+ ensure_out(rlen = uh_tcp_peek(cl, bufptr, blen));
- if( rlen > 0 )
+ if( (idxptr = strfind(buffer, sizeof(buffer), "\r\n\r\n", 4)) )
{
- if( (idxptr = strfind(buffer, sizeof(buffer), "\r\n\r\n", 4)) )
- {
- blen -= uh_tcp_recv(cl, bufptr, (int)(idxptr - bufptr) + 4);
+ ensure_out(rlen = uh_tcp_recv(cl, bufptr,
+ (int)(idxptr - bufptr) + 4));
- /* header read complete ... */
- return uh_http_header_parse(cl, buffer, sizeof(buffer) - blen - 1);
- }
- else
- {
- rlen = uh_tcp_recv(cl, bufptr, rlen);
- blen -= rlen;
- bufptr += rlen;
- }
+ /* header read complete ... */
+ blen -= rlen;
+ return uh_http_header_parse(cl, buffer,
+ sizeof(buffer) - blen - 1);
}
else
{
- /* invalid request (unexpected eof/timeout) */
- uh_http_response(cl, 408, "Request Timeout");
- return NULL;
+ ensure_out(rlen = uh_tcp_recv(cl, bufptr, rlen));
+
+ blen -= rlen;
+ bufptr += rlen;
}
}
else
{
/* invalid request (unexpected eof/timeout) */
- uh_http_response(cl, 408, "Request Timeout");
return NULL;
}
}
/* request entity too large */
uh_http_response(cl, 413, "Request Entity Too Large");
+
+out:
return NULL;
}
+#if defined(HAVE_LUA) || defined(HAVE_CGI)
static int uh_path_match(const char *prefix, const char *url)
{
if( (strstr(url, prefix) == url) &&
return 0;
}
+#endif
+static void uh_dispatch_request(
+ struct client *cl, struct http_request *req, struct path_info *pin
+) {
+#ifdef HAVE_CGI
+ struct interpreter *ipr = NULL;
-int main (int argc, char **argv)
+ if( uh_path_match(cl->server->conf->cgi_prefix, pin->name) ||
+ (ipr = uh_interpreter_lookup(pin->phys)) )
+ {
+ uh_cgi_request(cl, req, pin, ipr);
+ }
+ else
+#endif
+ {
+ uh_file_request(cl, req, pin);
+ }
+}
+
+static void uh_mainloop(struct config *conf, fd_set serv_fds, int max_fd)
{
+ /* master file descriptor list */
+ fd_set used_fds, read_fds;
+
+ /* working structs */
+ struct http_request *req;
+ struct path_info *pin;
+ struct client *cl;
+
+ /* maximum file descriptor number */
+ int new_fd, cur_fd = 0;
+
+ /* clear the master and temp sets */
+ FD_ZERO(&used_fds);
+ FD_ZERO(&read_fds);
+
+ /* backup server descriptor set */
+ used_fds = serv_fds;
+
+ /* loop */
+ while(run)
+ {
+ /* create a working copy of the used fd set */
+ read_fds = used_fds;
+
+ /* sleep until socket activity */
+ if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
+ {
+ perror("select()");
+ exit(1);
+ }
+
+ /* run through the existing connections looking for data to be read */
+ for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
+ {
+ /* is a socket managed by us */
+ if( FD_ISSET(cur_fd, &read_fds) )
+ {
+ /* is one of our listen sockets */
+ if( FD_ISSET(cur_fd, &serv_fds) )
+ {
+ /* handle new connections */
+ if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
+ {
+ /* add to global client list */
+ if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
+ {
+#ifdef HAVE_TLS
+ /* setup client tls context */
+ if( conf->tls )
+ conf->tls_accept(cl);
+#endif
+
+ /* add client socket to global fdset */
+ FD_SET(new_fd, &used_fds);
+ fd_cloexec(new_fd);
+ max_fd = max(max_fd, new_fd);
+ }
+
+ /* insufficient resources */
+ else
+ {
+ fprintf(stderr,
+ "uh_client_add(): Cannot allocate memory\n");
+
+ close(new_fd);
+ }
+ }
+ }
+
+ /* is a client socket */
+ else
+ {
+ if( ! (cl = uh_client_lookup(cur_fd)) )
+ {
+ /* this should not happen! */
+ fprintf(stderr,
+ "uh_client_lookup(): No entry for fd %i!\n",
+ cur_fd);
+
+ goto cleanup;
+ }
+
+ /* parse message header */
+ if( (req = uh_http_header_recv(cl)) != NULL )
+ {
+ /* RFC1918 filtering required? */
+ if( conf->rfc1918_filter &&
+ sa_rfc1918(&cl->peeraddr) &&
+ !sa_rfc1918(&cl->servaddr) )
+ {
+ uh_http_sendhf(cl, 403, "Forbidden",
+ "Rejected request from RFC1918 IP "
+ "to public server address");
+ }
+ else
#ifdef HAVE_LUA
- /* Lua runtime */
- lua_State *L = NULL;
+ /* Lua request? */
+ if( conf->lua_state &&
+ uh_path_match(conf->lua_prefix, req->url) )
+ {
+ conf->lua_request(cl, req, conf->lua_state);
+ }
+ else
#endif
+ /* dispatch request */
+ if( (pin = uh_path_lookup(cl, req->url)) != NULL )
+ {
+ /* auth ok? */
+ if( uh_auth_check(cl, req, pin) )
+ uh_dispatch_request(cl, req, pin);
+ }
+
+ /* 404 */
+ else
+ {
+ /* Try to invoke an error handler */
+ pin = uh_path_lookup(cl, conf->error_handler);
+ if( pin && uh_auth_check(cl, req, pin) )
+ {
+ req->redirect_status = 404;
+ uh_dispatch_request(cl, req, pin);
+ }
+ else
+ {
+ uh_http_sendhf(cl, 404, "Not Found",
+ "No such file or directory");
+ }
+ }
+ }
+
+#ifdef HAVE_TLS
+ /* free client tls context */
+ if( conf->tls )
+ conf->tls_close(cl);
+#endif
+
+ cleanup:
+
+ /* close client socket */
+ close(cur_fd);
+ FD_CLR(cur_fd, &used_fds);
+
+ /* remove from global client list */
+ uh_client_remove(cur_fd);
+ }
+ }
+ }
+ }
+
+#ifdef HAVE_LUA
+ /* destroy the Lua state */
+ if( conf->lua_state != NULL )
+ conf->lua_close(conf->lua_state);
+#endif
+}
+
+
+int main (int argc, char **argv)
+{
/* master file descriptor list */
fd_set used_fds, serv_fds, read_fds;
/* working structs */
struct addrinfo hints;
- struct http_request *req;
- struct path_info *pin;
- struct client *cl;
struct sigaction sa;
struct config conf;
sigset_t ss;
/* maximum file descriptor number */
- int new_fd, cur_fd, max_fd = 0;
+ int cur_fd, max_fd = 0;
#ifdef HAVE_TLS
int tls = 0;
#endif
while( (opt = getopt(argc, argv,
- "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+ "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:")) > 0
) {
switch(opt)
{
case 'x':
conf.cgi_prefix = optarg;
break;
+
+ /* interpreter */
+ case 'i':
+ if( (optarg[0] == '.') && (port = strchr(optarg, '=')) )
+ {
+ *port++ = 0;
+ uh_interpreter_add(optarg, port);
+ }
+ else
+ {
+ fprintf(stderr, "Error: Invalid interpreter: %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
#endif
#ifdef HAVE_LUA
#endif
#ifdef HAVE_CGI
" -x string URL prefix for CGI handler, default is '/cgi-bin'\n"
+ " -i .ext=path Use interpreter at path for files with the given extension\n"
#endif
#if defined(HAVE_CGI) || defined(HAVE_LUA)
" -t seconds CGI and Lua script timeout in seconds, default is 60\n"
if( ! conf.lua_prefix )
conf.lua_prefix = "/lua";
- L = conf.lua_init(conf.lua_handler);
+ conf.lua_state = conf.lua_init(conf.lua_handler);
}
}
#endif
}
}
- /* backup server descriptor set */
- used_fds = serv_fds;
-
- /* loop */
- while(run)
- {
- /* create a working copy of the used fd set */
- read_fds = used_fds;
-
- /* sleep until socket activity */
- if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
- {
- perror("select()");
- exit(1);
- }
-
- /* run through the existing connections looking for data to be read */
- for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
- {
- /* is a socket managed by us */
- if( FD_ISSET(cur_fd, &read_fds) )
- {
- /* is one of our listen sockets */
- if( FD_ISSET(cur_fd, &serv_fds) )
- {
- /* handle new connections */
- if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
- {
- /* add to global client list */
- if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
- {
-#ifdef HAVE_TLS
- /* setup client tls context */
- if( conf.tls )
- conf.tls_accept(cl);
-#endif
-
- /* add client socket to global fdset */
- FD_SET(new_fd, &used_fds);
- fd_cloexec(new_fd);
- max_fd = max(max_fd, new_fd);
- }
-
- /* insufficient resources */
- else
- {
- fprintf(stderr,
- "uh_client_add(): Can not manage more than "
- "%i client sockets, connection dropped\n",
- UH_LIMIT_CLIENTS
- );
-
- close(new_fd);
- }
- }
- }
-
- /* is a client socket */
- else
- {
- if( ! (cl = uh_client_lookup(cur_fd)) )
- {
- /* this should not happen! */
- fprintf(stderr,
- "uh_client_lookup(): No entry for fd %i!\n",
- cur_fd);
-
- goto cleanup;
- }
-
- /* parse message header */
- if( (req = uh_http_header_recv(cl)) != NULL )
- {
- /* RFC1918 filtering required? */
- if( conf.rfc1918_filter && sa_rfc1918(&cl->peeraddr) &&
- !sa_rfc1918(&cl->servaddr) )
- {
- uh_http_sendhf(cl, 403, "Forbidden",
- "Rejected request from RFC1918 IP to public server address");
- }
- else
-#ifdef HAVE_LUA
- /* Lua request? */
- if( L && uh_path_match(conf.lua_prefix, req->url) )
- {
- conf.lua_request(cl, req, L);
- }
- else
-#endif
- /* dispatch request */
- if( (pin = uh_path_lookup(cl, req->url)) != NULL )
- {
- /* auth ok? */
- if( uh_auth_check(cl, req, pin) )
- {
-#ifdef HAVE_CGI
- if( uh_path_match(conf.cgi_prefix, pin->name) )
- {
- uh_cgi_request(cl, req, pin);
- }
- else
-#endif
- {
- uh_file_request(cl, req, pin);
- }
- }
- }
-
- /* 404 */
- else
- {
- /* Try to invoke an error handler */
- pin = uh_path_lookup(cl, conf.error_handler);
-
- if( pin && uh_auth_check(cl, req, pin) )
- {
- req->redirect_status = 404;
-
-#ifdef HAVE_CGI
- if( uh_path_match(conf.cgi_prefix, pin->name) )
- {
- uh_cgi_request(cl, req, pin);
- }
- else
-#endif
- {
- uh_file_request(cl, req, pin);
- }
- }
- else
- {
- uh_http_sendhf(cl, 404, "Not Found",
- "No such file or directory");
- }
- }
- }
-
-#ifdef HAVE_TLS
- /* free client tls context */
- if( conf.tls )
- conf.tls_close(cl);
-#endif
-
- cleanup:
-
- /* close client socket */
- close(cur_fd);
- FD_CLR(cur_fd, &used_fds);
-
- /* remove from global client list */
- uh_client_remove(cur_fd);
- }
- }
- }
- }
+ /* server main loop */
+ uh_mainloop(&conf, serv_fds, max_fd);
#ifdef HAVE_LUA
/* destroy the Lua state */
- if( L != NULL )
- conf.lua_close(L);
+ if( conf.lua_state != NULL )
+ conf.lua_close(conf.lua_state);
#endif
return 0;
#include <sys/select.h>
#include <sys/wait.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <linux/limits.h>
#include <netdb.h>
#include <openssl/ssl.h>
#endif
+/* uClibc... */
+#ifndef SOL_TCP
+#define SOL_TCP 6
+#endif
+
#define UH_LIMIT_MSGHEAD 4096
#define UH_LIMIT_HEADERS 64
-#define UH_LIMIT_LISTENERS 16
#define UH_LIMIT_CLIENTS 64
-#define UH_LIMIT_AUTHREALMS 8
#define UH_HTTP_MSG_GET 0
#define UH_HTTP_MSG_HEAD 1
struct listener;
struct client;
+struct interpreter;
struct http_request;
struct config {
#ifdef HAVE_LUA
char *lua_prefix;
char *lua_handler;
+ lua_State *lua_state;
lua_State * (*lua_init) (const char *handler);
void (*lua_close) (lua_State *L);
void (*lua_request) (struct client *cl, struct http_request *req, lua_State *L);
#ifdef HAVE_TLS
SSL_CTX *tls;
#endif
+ struct listener *next;
};
struct client {
#ifdef HAVE_TLS
SSL *tls;
#endif
+ struct client *next;
};
struct auth_realm {
char path[PATH_MAX];
char user[32];
char pass[128];
+ struct auth_realm *next;
};
struct http_request {
char *headers[UH_LIMIT_HEADERS];
};
+#ifdef HAVE_CGI
+struct interpreter {
+ char path[PATH_MAX];
+ char extn[32];
+ struct interpreter *next;
+};
+#endif
+
#endif