From 9225e812bd3287bf490c7e13f56441be6d9bbc65 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 18 Aug 2010 00:04:52 +0000 Subject: [PATCH] [package] uhttpd: - fix parsing of interpreter entries in the config file, fixes serving of static files as .cgi with X-Wrt - better cope with connection aborts, especially during header transfer - fix return value checking of TLS reads and writes, solves some blocking issues SVN-Revision: 22692 --- package/uhttpd/Makefile | 2 +- package/uhttpd/src/uhttpd-cgi.c | 61 ++++++++++++----------- package/uhttpd/src/uhttpd-file.c | 80 ++++++++++++++++--------------- package/uhttpd/src/uhttpd-tls.c | 6 ++- package/uhttpd/src/uhttpd-utils.c | 27 +++++------ package/uhttpd/src/uhttpd-utils.h | 7 +++ package/uhttpd/src/uhttpd.c | 60 +++++++++++++---------- package/uhttpd/src/uhttpd.h | 6 +++ 8 files changed, 141 insertions(+), 108 deletions(-) diff --git a/package/uhttpd/Makefile b/package/uhttpd/Makefile index de9eee9e9f..1227174536 100644 --- a/package/uhttpd/Makefile +++ b/package/uhttpd/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=uhttpd -PKG_RELEASE:=15 +PKG_RELEASE:=16 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) PKG_BUILD_DEPENDS := libcyassl liblua diff --git a/package/uhttpd/src/uhttpd-cgi.c b/package/uhttpd/src/uhttpd-cgi.c index f9dd9810da..8c17251b31 100644 --- a/package/uhttpd/src/uhttpd-cgi.c +++ b/package/uhttpd/src/uhttpd-cgi.c @@ -139,7 +139,7 @@ void uh_cgi_request( struct client *cl, struct http_request *req, struct path_info *pi, struct interpreter *ip ) { - int i, hdroff, bufoff; + int i, hdroff, bufoff, rv; int hdrlen = 0; int buflen = 0; int fd_max = 0; @@ -376,12 +376,6 @@ void uh_cgi_request( 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 ) { @@ -391,11 +385,21 @@ void uh_cgi_request( 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) ) { @@ -403,7 +407,10 @@ void uh_cgi_request( 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 ) @@ -456,7 +463,7 @@ void uh_cgi_request( 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, @@ -466,7 +473,7 @@ void uh_cgi_request( 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)); } @@ -474,32 +481,32 @@ void uh_cgi_request( 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 */ @@ -510,7 +517,7 @@ void uh_cgi_request( /* 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; @@ -518,7 +525,7 @@ void uh_cgi_request( /* 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 */ @@ -538,7 +545,7 @@ void uh_cgi_request( * 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", @@ -546,11 +553,11 @@ void uh_cgi_request( ? "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; } } @@ -561,13 +568,13 @@ void uh_cgi_request( { 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; } diff --git a/package/uhttpd/src/uhttpd-file.c b/package/uhttpd/src/uhttpd-file.c index 25a5f6ece3..fda86d7263 100644 --- a/package/uhttpd/src/uhttpd-file.c +++ b/package/uhttpd/src/uhttpd-file.c @@ -97,8 +97,6 @@ static char * uh_file_header_lookup(struct http_request *req, const char *name) return NULL; } -#define ensure_ret(x) \ - do { if( x < 0 ) return -1; } while(0) static int uh_file_response_ok_hdrs(struct client *cl, struct http_request *req, struct stat *s) { @@ -132,7 +130,7 @@ static int uh_file_response_412(struct client *cl, struct http_request *req) "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"); @@ -152,43 +150,44 @@ static int uh_file_if_match(struct client *cl, struct http_request *req, struct } 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 ) { @@ -203,53 +202,54 @@ static int uh_file_if_none_match(struct client *cl, struct http_request *req, st } 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; @@ -335,6 +335,7 @@ out: 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]; @@ -342,13 +343,14 @@ void uh_file_request(struct client *cl, struct http_request *req, struct path_in 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 */ ensure_out(uh_file_response_200(cl, req, &pi->stat)); diff --git a/package/uhttpd/src/uhttpd-tls.c b/package/uhttpd/src/uhttpd-tls.c index 26143ddf71..008f8e0df6 100644 --- a/package/uhttpd/src/uhttpd-tls.c +++ b/package/uhttpd/src/uhttpd-tls.c @@ -70,12 +70,14 @@ void uh_tls_client_accept(struct client *c) 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) diff --git a/package/uhttpd/src/uhttpd-utils.c b/package/uhttpd/src/uhttpd-utils.c index e68926e935..8a06c930af 100644 --- a/package/uhttpd/src/uhttpd-utils.c +++ b/package/uhttpd/src/uhttpd-utils.c @@ -112,6 +112,7 @@ int select_intr(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) /* unblock SIGCHLD */ sigemptyset(&ssn); sigaddset(&ssn, SIGCHLD); + sigaddset(&ssn, SIGPIPE); sigprocmask(SIG_UNBLOCK, &ssn, &sso); rv = select(n, r, w, e, t); @@ -193,8 +194,6 @@ int uh_tcp_recv(struct client *cl, char *buf, int len) 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, ...) { @@ -211,14 +210,14 @@ int uh_http_sendhf(struct client *cl, int code, const char *summary, const char 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; } @@ -235,13 +234,13 @@ int uh_http_sendc(struct client *cl, const char *data, int len) 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; @@ -259,9 +258,9 @@ int uh_http_sendf( 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; } @@ -273,9 +272,9 @@ int uh_http_send( 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; } @@ -639,7 +638,7 @@ struct auth_realm * uh_auth_add(char *path, char *user, char *pass) ) { memcpy(new->pass, pwd->pw_passwd, min(strlen(pwd->pw_passwd), sizeof(new->pass) - 1)); - } + } } /* ordinary pwd */ diff --git a/package/uhttpd/src/uhttpd-utils.h b/package/uhttpd/src/uhttpd-utils.h index 95535d6fe7..3514ce1cac 100644 --- a/package/uhttpd/src/uhttpd-utils.h +++ b/package/uhttpd/src/uhttpd-utils.h @@ -36,6 +36,13 @@ #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; diff --git a/package/uhttpd/src/uhttpd.c b/package/uhttpd/src/uhttpd.c index 6406e459ad..6f5e616345 100644 --- a/package/uhttpd/src/uhttpd.c +++ b/package/uhttpd/src/uhttpd.c @@ -96,17 +96,18 @@ static void uh_config_parse(struct config *conf) conf->error_handler = strdup(col1); } #ifdef HAVE_CGI - else if( (line[0] == '.') && (strchr(line, ':') != NULL) ) + else if( (line[0] == '*') && (strchr(line, ':') != NULL) ) { - if( !(col1 = strchr(line, ':')) || (*col1++ = 0) || - !(eol = strchr(col1, '\n')) || (*eol++ = 0) ) + if( !(col1 = strchr(line, '*')) || (*col1++ = 0) || + !(col2 = strchr(col1, ':')) || (*col2++ = 0) || + !(eol = strchr(col2, '\n')) || (*eol++ = 0) ) continue; - if( !uh_interpreter_add(line, col1) ) + if( !uh_interpreter_add(col1, col2) ) { fprintf(stderr, "Unable to add interpreter %s for extension %s: " - "Out of memory\n", col1, line + "Out of memory\n", col2, col1 ); } } @@ -126,6 +127,10 @@ static int uh_socket_bind( 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; @@ -145,12 +150,22 @@ static int uh_socket_bind( } /* "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 ) { @@ -355,7 +370,6 @@ static struct http_request * uh_http_header_recv(struct client *cl) ssize_t blen = sizeof(buffer)-1; ssize_t rlen = 0; - memset(buffer, 0, sizeof(buffer)); while( blen > 0 ) @@ -371,41 +385,37 @@ static struct http_request * uh_http_header_recv(struct client *cl) 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; } diff --git a/package/uhttpd/src/uhttpd.h b/package/uhttpd/src/uhttpd.h index 78cca7b3b2..6747b905ff 100644 --- a/package/uhttpd/src/uhttpd.h +++ b/package/uhttpd/src/uhttpd.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,11 @@ #include #endif +/* uClibc... */ +#ifndef SOL_TCP +#define SOL_TCP 6 +#endif + #define UH_LIMIT_MSGHEAD 4096 #define UH_LIMIT_HEADERS 64 -- 2.30.2