diff --git a/netutils/thttpd/Make.defs b/netutils/thttpd/Make.defs index 532ab47197..9bc86cab1a 100644 --- a/netutils/thttpd/Make.defs +++ b/netutils/thttpd/Make.defs @@ -34,5 +34,5 @@ ############################################################################ THTTPD_ASRCS = -THTTPD_CSRCS = thttpd.c libhttpd.c httpd_alloc.c timers.c fdwatch.c tdate_parse.c +THTTPD_CSRCS = thttpd.c libhttpd.c thttpd_cgi.c thttpd_alloc.c thttpd_strings.c timers.c fdwatch.c tdate_parse.c diff --git a/netutils/thttpd/fdwatch.c b/netutils/thttpd/fdwatch.c index 1933d1d6dc..71e81c2b27 100644 --- a/netutils/thttpd/fdwatch.c +++ b/netutils/thttpd/fdwatch.c @@ -47,7 +47,7 @@ #include #include "config.h" -#include "httpd_alloc.h" +#include "thttpd_alloc.h" #include "fdwatch.h" #ifdef CONFIG_THTTPD diff --git a/netutils/thttpd/libhttpd.c b/netutils/thttpd/libhttpd.c index ee2a74e815..d3703586bd 100644 --- a/netutils/thttpd/libhttpd.c +++ b/netutils/thttpd/libhttpd.c @@ -57,13 +57,13 @@ #include #include -#include -#include #include #include "config.h" #include "libhttpd.h" -#include "httpd_alloc.h" +#include "thttpd_alloc.h" +#include "thttpd_strings.h" +#include "thttpd_cgi.h" #include "timers.h" #include "tdate_parse.h" #include "fdwatch.h" @@ -112,25 +112,6 @@ extern char *crypt(const char *key, const char *setting); * Private Types ****************************************************************************/ -#ifdef CONFIG_THTTPD_CGI_PATTERN -enum cgi_outbuffer_e -{ - CGI_OUTBUFFER_READHEADER = 0, /* Reading header from HTTP client */ - CGI_OUTBUFFER_HEADERREAD, /* Header has been read */ - CGI_OUTBUFFER_HEADERSENT, /* Header has been sent to the CGI program */ - CGI_OUTBUFFER_READDATA, /* Transferring data from CGI to client */ - CGI_OUTBUFFER_DONE, /* Finished */ -}; - -struct cgi_outbuffer_s -{ - enum cgi_outbuffer_e state; /* State of the transfer */ - char *buffer; /* Allocated I/O buffer */ - size_t size; /* Size of the allocation */ - size_t len; /* Amount of valid data in the allocated buffer */ -}; -#endif - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -155,11 +136,6 @@ static int auth_check(httpd_conn *hc, char *dirname); static int auth_check2(httpd_conn *hc, char *dirname); #endif static void send_dirredirect(httpd_conn *hc); -static int hexit(char c); -static void strdecode(char *to, char *from); -#ifdef CONFIG_THTTPD_GENERATE_INDICES -static void strencode(char *to, int tosize, char *from); -#endif #ifdef CONFIG_THTTPD_TILDE_MAP1 static int httpd_tilde_map1(httpd_conn *hc); #endif @@ -186,18 +162,6 @@ static int ls(httpd_conn *hc); static char *hostname_map(char *hostname); #endif -/* CGI Support */ - -#ifdef CONFIG_THTTPD_CGI_PATTERN -static void create_environment(httpd_conn *hc); -static char **make_argp(httpd_conn *hc); -static inline int cgi_interpose_input(httpd_conn *hc, int wfd, char *buffer); -static inline int cgi_interpose_output(httpd_conn *hc, int rfd, char *inbuffer, - struct cgi_outbuffer_s *hdr); -static int cgi_child(int argc, char **argv); -static int cgi(httpd_conn *hc); -#endif - static int check_referer(httpd_conn *hc); #ifdef CONFIG_THTTPD_URLPATTERN static int really_check_referer(httpd_conn *hc); @@ -222,65 +186,13 @@ static size_t sockaddr_len(httpd_sockaddr *saP); * of a hack but it seems to do the right thing. */ -static pid_t main_thread; - -/* This is the 'root' of the Filesystem as seen by the HTTP client */ - -static const char httpd_root[] = CONFIG_THTTPD_PATH; +static pid_t main_thread; /* Include MIME encodings and types */ #include "mime_types.h" -/**************************************************************************** - * HTTP Strings - ****************************************************************************/ - -static const char ok200title[] = "OK"; -static const char ok206title[] = "Partial Content"; - -static const char err302title[] = "Found"; -static const char err302form[] = "The actual URL is '%s'.\n"; - -static const char err304title[] = "Not Modified"; - -const char httpd_err400title[] = "Bad Request"; -const char httpd_err400form[] = "Your request has bad syntax or is inherently impossible to satisfy.\n"; - -#ifdef CONFIG_THTTPD_AUTH_FILE -static const char err401title[] = "Unauthorized"; -static const char err401form[] = "Authorization required for the URL '%s'.\n"; -#endif - -static const char err403title[] = "Forbidden"; -#ifndef EXPLICIT_ERROR_PAGES -static const char err403form[] = "You do not have permission to get URL '%s' from this server.\n"; -#endif - -static const char err404title[] = "Not Found"; -static const char err404form[] = "The requested URL '%s' was not found on this server.\n"; - -const char httpd_err408title[] = "Request Timeout"; -const char httpd_err408form[] = "No request appeared within a reasonable time period.\n"; - -static const char err500title[] = "Internal Error"; -static const char err500form[] = "There was an unusual problem serving the requested URL '%s'.\n"; - -static const char err501title[] = "Not Implemented"; -static const char err501form[] = "The requested method '%s' is not implemented by this server.\n"; - -static const char httpd_err503title[] = "Service Temporarily Overloaded"; -static const char httpd_err503form[] = "The requested URL '%s' is temporarily overloaded. Please try again later.\n"; - -static const char html_crlf[] = "\r\n"; -static const char html_html[] = "\r\n"; -static const char html_endhtml[] = "\r\n"; -static const char html_hdtitle[] = ""; -static const char html_titlehd[] = "\r\n"; -static const char html_body[] = "\r\n"; -static const char html_endbody[] = "\r\n"; -static const char html_hdr2[] = "

"; -static const char html_endhdr2[] = "

"; +/* Names for index file */ static const char *index_names[] = { CONFIG_THTTPD_INDEX_NAMES }; @@ -972,70 +884,6 @@ static void send_dirredirect(httpd_conn *hc) send_response(hc, 302, err302title, header, err302form, location); } -static int hexit(char nibble) -{ - if (nibble >= '0' && nibble <= '9') - { - return nibble - '0'; - } - else if (nibble >= 'a' && nibble <= 'f') - { - return nibble - 'a' + 10; - } - else if (nibble >= 'A' && nibble <= 'F') - { - return nibble - 'A' + 10; - } - return 0; -} - -/* Copies and decodes a string. It's ok for from and to to be the - * same string. - */ - -static void strdecode(char *to, char *from) -{ - for (; *from != '\0'; ++to, ++from) - { - if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) - { - *to = hexit(from[1]) * 16 + hexit(from[2]); - from += 2; - } - else - { - *to = *from; - } - } - *to = '\0'; -} - -/* Copies and encodes a string. */ - -#ifdef CONFIG_THTTPD_GENERATE_INDICES -static void strencode(char *to, int tosize, char *from) -{ - int tolen; - - for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) - { - if (isalnum(*from) || strchr("/_.-~", *from) != NULL) - { - *to = *from; - ++to; - ++tolen; - } - else - { - (void)sprintf(to, "%%%02x", (int)*from & 0xff); - to += 3; - tolen += 3; - } - } - *to = '\0'; -} -#endif /* CONFIG_THTTPD_GENERATE_INDICES */ - /* Map a ~username/whatever URL into /username. */ #ifdef CONFIG_THTTPD_TILDE_MAP1 @@ -1863,7 +1711,7 @@ static void ls_child(int argc, char **argv) } httpd_realloc_str(&encrname, &maxencrname, 3 * strlen(rname) + 1); - strencode(encrname, maxencrname, rname); + httpd_strencode(encrname, maxencrname, rname); if (stat(name, &sb) < 0 || lstat(name, &lsb) < 0) { @@ -2094,851 +1942,6 @@ static int ls(httpd_conn *hc) } #endif /* CONFIG_THTTPD_GENERATE_INDICES */ -/* Set up environment variables. Be real careful here to avoid - * letting malicious clients overrun a buffer. We don't have - * to worry about freeing stuff since we're a sub-task. - */ - -#ifdef CONFIG_THTTPD_CGI_PATTERN -static void create_environment(httpd_conn *hc) -{ - char *cp; - char buf[256]; - - setenv("PATH", CONFIG_THTTPD_CGI_PATH, TRUE); -#ifdef CGI_LD_LIBRARY_PATH - setenv("LD_LIBRARY_PATH", CGI_LD_LIBRARY_PATH, TRUE); -#endif /* CGI_LD_LIBRARY_PATH */ - - setenv("SERVER_SOFTWARE", CONFIG_THTTPD_SERVER_SOFTWARE, TRUE); - - /* If vhosting, use that server-name here. */ -#ifdef CONFIG_THTTPD_VHOST - if (hc->vhostname) - { - cp = hc->vhostname; - } - else -#endif - { - cp = hc->hs->hostname; - } - - if (cp) - { - setenv("SERVER_NAME", cp, TRUE); - } - - setenv("GATEWAY_INTERFACE", "CGI/1.1", TRUE); - setenv("SERVER_PROTOCOL", hc->protocol, TRUE); - - (void)snprintf(buf, sizeof(buf), "%d", (int)CONFIG_THTTPD_PORT); - setenv("SERVER_PORT", buf, TRUE); - - setenv("REQUEST_METHOD", httpd_method_str(hc->method), TRUE); - - if (hc->pathinfo[0] != '\0') - { - char *cp2; - size_t l; - - (void)snprintf(buf, sizeof(buf), "/%s", hc->pathinfo); - setenv("PATH_INFO", buf, TRUE); - - l = strlen(httpd_root) + strlen(hc->pathinfo) + 1; - cp2 = NEW(char, l); - if (cp2) - { - (void)snprintf(cp2, l, "%s%s", httpd_root, hc->pathinfo); - setenv("PATH_TRANSLATED", cp2, TRUE); - } - } - - (void)snprintf(buf, sizeof(buf), "/%s",strcmp(hc->origfilename, ".") == 0 ? "" : hc->origfilename); - setenv("SCRIPT_NAME", buf, TRUE); - - if (hc->query[0] != '\0') - { - setenv("QUERY_STRING", hc->query, TRUE); - } - - setenv("REMOTE_ADDR", httpd_ntoa(&hc->client_addr), TRUE); - if (hc->referer[0] != '\0') - { - setenv("HTTP_REFERER", hc->referer, TRUE); - } - - if (hc->useragent[0] != '\0') - { - setenv("HTTP_USER_AGENT", hc->useragent, TRUE); - } - - if (hc->accept[0] != '\0') - { - setenv("HTTP_ACCEPT", hc->accept, TRUE); - } - - if (hc->accepte[0] != '\0') - { - setenv("HTTP_ACCEPT_ENCODING", hc->accepte, TRUE); - } - - if (hc->acceptl[0] != '\0') - { - setenv("HTTP_ACCEPT_LANGUAGE", hc->acceptl, TRUE); - } - - if (hc->cookie[0] != '\0') - { - setenv("HTTP_COOKIE", hc->cookie, TRUE); - } - - if (hc->contenttype[0] != '\0') - { - setenv("CONTENT_TYPE", hc->contenttype, TRUE); - } - - if (hc->hdrhost[0] != '\0') - { - setenv("HTTP_HOST", hc->hdrhost, TRUE); - } - - if (hc->contentlength != -1) - { - (void)snprintf(buf, sizeof(buf), "%lu", (unsigned long)hc->contentlength); - setenv("CONTENT_LENGTH", buf, TRUE); - } - - if (hc->remoteuser[0] != '\0') - { - setenv("REMOTE_USER", hc->remoteuser, TRUE); - } - - if (hc->authorization[0] != '\0') - { - setenv("AUTH_TYPE", "Basic", TRUE); - } - - /* We only support Basic auth at the moment. */ - - if (getenv("TZ") != NULL) - { - setenv("TZ", getenv("TZ"), TRUE); - } - - setenv("CGI_PATTERN", CONFIG_THTTPD_CGI_PATTERN, TRUE); -} -#endif - -/* Set up argument vector */ - -#ifdef CONFIG_THTTPD_CGI_PATTERN -static FAR char **make_argp(httpd_conn *hc) -{ - FAR char **argp; - int argn; - char *cp1; - char *cp2; - - /* By allocating an arg slot for every character in the query, plus one - * for the filename and one for the NULL, we are guaranteed to have - * enough. We could actually use strlen/2. - */ - - argp = NEW(char *, strlen(hc->query) + 2); - if (!argp) - { - return NULL; - } - - argp[0] = strrchr(hc->expnfilename, '/'); - if (argp[0]) - { - ++argp[0]; - } - else - { - argp[0] = hc->expnfilename; - } - argn = 1; - - /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html, - * "The server should search the query information for a non-encoded = - * character to determine if the command line is to be used, if it finds - * one, the command line is not to be used." - */ - - if (strchr(hc->query, '=') == NULL) - { - for (cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2) - { - if (*cp2 == '+') - { - *cp2 = '\0'; - strdecode(cp1, cp1); - argp[argn++] = cp1; - cp1 = cp2 + 1; - } - } - - if (cp2 != cp1) - { - strdecode(cp1, cp1); - argp[argn++] = cp1; - } - } - - argp[argn] = NULL; - return argp; -} -#endif - -/* Data is available from the client socket. This routine is used only for POST - * requests. It reads the data from the client and sends it to the child thread. - */ - -#ifdef CONFIG_THTTPD_CGI_PATTERN -static inline int cgi_interpose_input(httpd_conn *hc, int wfd, char *buffer) -{ - size_t nbytes; - ssize_t nbytes_read; - ssize_t nbytes_written; - - nbytes = hc->read_idx - hc->checked_idx; - if (nbytes > 0) - { - if (httpd_write(wfd, &(hc->read_buf[hc->checked_idx]), nbytes) != nbytes) - { - return 1; - } - } - - if (nbytes < hc->contentlength) - { - do - { - nbytes_read = read(hc->conn_fd, buffer, MIN(sizeof(buffer), hc->contentlength - nbytes)); - if (nbytes_read < 0) - { - if (errno != EINTR) - { - return 1; - } - } - } - while (nbytes_read < 0); - - if (nbytes_read > 0) - { - nbytes_written = httpd_write(wfd, buffer, nbytes_read); - if (nbytes_written != nbytes_read) - { - return 1; - } - } - } - - if (nbytes >= hc->contentlength) - { - /* Special hack to deal with broken browsers that send a LF or CRLF - * after POST data, causing TCP resets - we just read and discard up - * to 2 bytes. Unfortunately this doesn't fix the problem for CGIs - * which avoid the interposer task due to their POST data being - * short. Creating an interposer task for all POST CGIs is - * unacceptably expensive. The eventual fix will come when interposing - * gets integrated into the main loop as a tasklet instead of a task. - */ - - /* Turn on no-delay mode in case we previously cleared it. */ - - httpd_set_ndelay(hc->conn_fd); - - /* And read up to 2 bytes. */ - - (void)read(hc->conn_fd, buffer, sizeof(buffer)); - return 1; - } - return 0; -} -#endif - -/* This routine is used for parsed-header CGIs. The idea here is that the - * CGI can return special headers such as "Status:" and "Location:" which - * change the return status of the response. Since the return status has to - * be the very first line written out, we have to accumulate all the headers - * and check for the special ones before writing the status. Then we write - * out the saved headers and proceed to echo the rest of the response. - */ - -#ifdef CONFIG_THTTPD_CGI_PATTERN -static inline int cgi_interpose_output(httpd_conn *hc, int rfd, char *inbuffer, - struct cgi_outbuffer_s *hdr) -{ - ssize_t nbytes_read; - char *br = NULL; - int status; - const char *title; - char *cp; - - /* Make sure the connection is in blocking mode. It should already be - * blocking, but we might as well be sure. - */ - - httpd_clear_ndelay(hc->conn_fd); - - /* Loop while there are things we can do without waiting for more input */ - - switch (hdr->state) - { - case CGI_OUTBUFFER_READHEADER: - { - /* Slurp in all headers as they become available from the client. */ - - do - { - /* Read until we successfully read data or until an error occurs. - * EAGAIN is not an error, but it is still cause to return. - */ - - nbytes_read = read(hc->conn_fd, inbuffer, sizeof(inbuffer)); - nvdbg("Read %d bytes from fd %d\n", nbytes_read, hc->conn_fd); - - if (nbytes_read < 0) - { - if (errno != EINTR) - { - if (errno != EAGAIN) - { - ndbg("read: %d\n", errno); - } - return 1; - } - } - } - while (nbytes_read < 0); - - /* Check for end-of-file */ - - if (nbytes_read <= 0) - { - nvdbg("End-of-file\n"); - br = &(hdr->buffer[hdr->len]); - hdr->state = CGI_OUTBUFFER_HEADERREAD; - } - else - { - /* Accumulate more header data */ - - httpd_realloc_str(&hdr->buffer, &hdr->size, hdr->len + nbytes_read); - (void)memcpy(&(hdr->buffer[hdr->len]), inbuffer, nbytes_read); - hdr->len += nbytes_read; - hdr->buffer[hdr->len] = '\0'; - nvdbg("Header bytes accumulated: %d\n", hdr->len); - - /* Check for end of header */ - - if ((br = strstr(hdr->buffer, "\r\n\r\n")) != NULL || - (br = strstr(hdr->buffer, "\012\012")) != NULL) - { - nvdbg("End-of-header\n"); - hdr->state = CGI_OUTBUFFER_HEADERREAD; - } - else - { - /* Return. We will be called again when more data is available - * on the socket. - */ - - return 0; - } - } - } - break; - - case CGI_OUTBUFFER_HEADERREAD: - { - /* If there were no headers, bail. */ - - if (hdr->buffer[0] == '\0') - { - hdr->state = CGI_OUTBUFFER_DONE; - return 1; - } - - /* Figure out the status. Look for a Status: or Location: header; else if - * there's an HTTP header line, get it from there; else default to 200. - */ - - status = 200; - if (strncmp(hdr->buffer, "HTTP/", 5) == 0) - { - cp = hdr->buffer; - cp += strcspn(cp, " \t"); - status = atoi(cp); - } - - if ((cp = strstr(hdr->buffer, "Status:")) != NULL && - cp < br && (cp == hdr->buffer || *(cp - 1) == '\012')) - { - cp += 7; - cp += strspn(cp, " \t"); - status = atoi(cp); - } - - if ((cp = strstr(hdr->buffer, "Location:")) != NULL && - cp < br && (cp == hdr->buffer || *(cp - 1) == '\012')) - { - status = 302; - } - - /* Write the status line. */ - - nvdbg("Status: %d\n", status); - switch (status) - { - case 200: - title = ok200title; - break; - - case 302: - title = err302title; - break; - - case 304: - title = err304title; - break; - - case 400: - BADREQUEST("status"); - title = httpd_err400title; - break; - -#ifdef CONFIG_THTTPD_AUTH_FILE - case 401: - title = err401title; - break; -#endif - - case 403: - title = err403title; - break; - - case 404: - title = err404title; - break; - - case 408: - title = httpd_err408title; - break; - - case 500: - INTERNALERROR("status"); - title = err500title; - break; - - case 501: - NOTIMPLEMENTED("status"); - title = err501title; - break; - - case 503: - title = httpd_err503title; - break; - - default: - title = "Something"; - break; - } - - (void)snprintf(inbuffer, sizeof(inbuffer), "HTTP/1.0 %d %s\r\n", status, title); - (void)httpd_write(hc->conn_fd, inbuffer, strlen(inbuffer)); - - /* Write the saved hdr->buffer to the client. */ - - (void)httpd_write(hc->conn_fd, hdr->buffer, hdr->len); - } - - /* Then set up to read the data following the header from the CGI program and - * pass it back to the client. We return now; we will be called again when - * data is available on the pipe. - */ - - hdr->state = CGI_OUTBUFFER_READDATA; - return 0; - - case CGI_OUTBUFFER_READDATA: - { - /* Read data from the pipe. */ - - do - { - /* Read until we successfully read data or until an error occurs. - * EAGAIN is not an error, but it is still cause to return. - */ - - nbytes_read = read(rfd, inbuffer, sizeof(inbuffer)); - nvdbg("Read %d bytes from fd %d\n", nbytes_read, rfd); - - if (nbytes_read < 0) - { - if (errno != EINTR) - { - if (errno != EAGAIN) - { - ndbg("read: %d\n", errno); - } - return 1; - } - } - } - while (nbytes_read < 0); - - /* Check for end of file */ - - if (nbytes_read == 0) - { - nvdbg("End-of-file\n"); - close(hc->conn_fd); - close(rfd); - hdr->state = CGI_OUTBUFFER_DONE; - return 1; - } - else - { - /* Forward the data from the CGI program to the client */ - - (void)httpd_write(hc->conn_fd, inbuffer, strlen(inbuffer)); - } - } - break; - - case CGI_OUTBUFFER_DONE: - default: - return 1; - } - return 0; -} -#endif - -/* CGI child task. */ - -#ifdef CONFIG_THTTPD_CGI_PATTERN -static int cgi_child(int argc, char **argv) -{ - FAR httpd_conn *hc = (FAR httpd_conn*)strtoul(argv[1], NULL, 16); -#if CONFIG_THTTPD_CGI_TIMELIMIT > 0 - ClientData client_data; -#endif - FAR char **argp; - struct cgi_outbuffer_s hdr; - struct fdwatch_s *fw; - char *buffer; - char *binary; - char *directory; - boolean indone; - boolean outdone; - int child; - int pipefd[2]; - int wfd = -1; - int rfd = -1; - int fd; - int ret; - int err = 1; - - /* Use low-level debug out (because the low-level output may survive closing - * all file descriptors - */ - - nllvdbg("Started: %s\n", argv[1]); - - /* Update all of the environment variable settings, these will be inherited - * by the CGI task. - */ - - create_environment(hc); - - /* Make the argument vector. */ - - argp = make_argp(hc); - - /* Close all file descriptors (including stdio, stdin, stdout) EXCEPT for - * stderr and hc->conn_fd - */ - - nllvdbg("Closing all descriptors\n"); - for (fd = 0; fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS); fd++) - { - /* Keep stderr open for debug; keep hc->conn_fd open for obvious reasons */ - - if (fd != 2 && fd != hc->conn_fd) - { - close(fd); - } - } - - /* Create pipes that will be interposed between the CGI task's stdin or - * stdout and the socket. - * - * Setup up the STDIN pipe - a pipe to transfer data received on the - * socket to the CGI program. - */ - - nllvdbg("Create STDIN pipe\n"); - ret = pipe(pipefd); - if (ret < 0) - { - nlldbg("STDIN pipe: %d\n", errno); - goto errout_with_descriptors; - } - else - { - /* Then map the receiving end the pipe to stdin, save the sending end, and - * closing the original receiving end - */ - - ret = dup2(pipefd[0], 0); - if (ret < 0) - { - nlldbg("STDIN dup2: %d\n", errno); - close(pipefd[1]); - goto errout_with_descriptors; - } - - wfd = pipefd[1]; - close(pipefd[0]); - } - - /* Set up the STDOUT pipe - a pipe to transfer data received from the CGI program - * to the client. - */ - - if (ret == 0) - { - nllvdbg("Create STDOUT pipe\n"); - ret = pipe(pipefd); - if (ret < 0) - { - nlldbg("STDOUT pipe: %d\n", errno); - goto errout_with_descriptors; - } - else - { - /* Then map the sending end the pipe to stdout, save the receiving end, and - * closing the original sending end - */ - - ret = dup2(pipefd[1], 1); - if (ret < 0) - { - nlldbg("STDOUT dup2: %d\n", errno); - close(pipefd[1]); - goto errout_with_descriptors; - } - - rfd = pipefd[0]; - close(pipefd[1]); - } - } - - /* Split the program into directory and binary, so we can chdir() to the - * program's own directory. This isn't in the CGI 1.1 spec, but it's what - * other HTTP servers do. - */ - - directory = strdup(hc->expnfilename); - if (!directory) - { - binary = hc->expnfilename; /* ignore errors */ - } - else - { - binary = strrchr(directory, '/'); - if (!binary) - { - binary = hc->expnfilename; - } - else - { - *binary++ = '\0'; - (void)chdir(directory); /* ignore errors */ - } - } - - /* Allocate memory for buffering */ - - memset(&hdr, 0, sizeof(struct cgi_outbuffer_s)); - httpd_realloc_str(&hdr.buffer, &hdr.size, 500); - if (!hdr.buffer) - { - nlldbg("hdr allocation failed\n"); - goto errout_with_descriptors; - } - - buffer = (char*)httpd_malloc(CONFIG_THTTPD_IOBUFFERSIZE); - if (!buffer) - { - nlldbg("buffer allocation failed\n"); - goto errout_with_header; - } - - /* Create fdwatch structures */ - - fw = fdwatch_initialize(2); - if (!fw) - { - nlldbg("fdwatch allocation failed\n"); - goto errout_with_buffer; - } - - /* Run the CGI program. */ - - nllvdbg("Starting CGI\n"); - child = exec(binary, (FAR const char **)argp, g_thttpdsymtab, g_thttpdnsymbols); - if (child < 0) - { - /* Something went wrong. */ - - nlldbg("execve %s: %d\n", hc->expnfilename, errno); - goto errout_with_watch; - } - - /* Schedule a kill for the child task in case it runs too long. */ - -#if CONFIG_THTTPD_CGI_TIMELIMIT > 0 - client_data.i = child; - if (tmr_create(NULL, cgi_kill, client_data, CONFIG_THTTPD_CGI_TIMELIMIT * 1000L, 0) == NULL) - { - nlldbg("tmr_create(cgi_kill child) failed\n"); - goto errout_with_watch; - } -#endif - - /* Add the read descriptors to the watch */ - - fdwatch_add_fd(fw, hc->conn_fd, NULL); - fdwatch_add_fd(fw, rfd, NULL); - - /* Then perform the interposition */ - - indone = FALSE; - outdone = FALSE; - - nllvdbg("Interposing\n"); - do - { - (void)fdwatch(fw, 5000); - - if (!indone && fdwatch_check_fd(fw, hc->conn_fd)) - { - /* Transfer data from the client to the CGI program (POST) */ - - nllvdbg("Interpose input\n"); - indone = cgi_interpose_input(hc, wfd, buffer); - if (indone) - { - fdwatch_del_fd(fw, hc->conn_fd); - } - } - - if (fdwatch_check_fd(fw, rfd)) - { - /* Handle receipt of headers and CGI program response (GET) */ - - nllvdbg("Interpose output\n"); - outdone = cgi_interpose_output(hc, rfd, buffer, &hdr); - } - } - while (!outdone); - err = 0; - - /* Get rid of watch structures */ - -errout_with_watch: - fdwatch_uninitialize(fw); - - /* Free memory */ - -errout_with_buffer: - httpd_free(buffer); -errout_with_header: - httpd_free(hdr.buffer); - - /* Close all descriptors */ - -errout_with_descriptors: - close(wfd); - close(rfd); - close(hc->conn_fd); - - INTERNALERROR("errout"); - httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); - httpd_write_response(hc); - nllvdbg("Return %d\n", ret); - return err; -} -#endif /* CONFIG_THTTPD_CGI_PATTERN */ - -#ifdef CONFIG_THTTPD_CGI_PATTERN -static int cgi(httpd_conn *hc) -{ - char arg[16]; - char *argv[1]; - pid_t child; - - if (hc->method == METHOD_GET || hc->method == METHOD_POST) - { -#ifdef CONFIG_THTTPD_CGILIMIT - if (hc->hs->cgi_count >= CONFIG_THTTPD_CGILIMIT) - { - httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, - hc->encodedurl); - return -1; - } -#endif - ++hc->hs->cgi_count; - httpd_clear_ndelay(hc->conn_fd); - - /* Start the child task. We use a trampoline task here so that we can - * safely muck with the file descriptors before actually started the CGI - * task. - */ - - snprintf(arg, 16, "%p", hc); /* task_create doesn't handle binary arguments. */ - argv[0] = arg; - -#ifndef CONFIG_CUSTOM_STACK - child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, - CONFIG_THTTPD_CGI_STACKSIZE, - (main_t)cgi_child, (const char **)argv); -#else - child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, - (main_t)cgi_child, (const char **)argv); -#endif - if (child < 0) - { - ndbg("task_create: %d\n", errno); - INTERNALERROR("task_create"); - httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); - return -1; - } - - ndbg("Started CGI task %d for file '%s'\n", child, hc->expnfilename); - - hc->status = 200; - hc->bytes_sent = CONFIG_THTTPD_CGI_BYTECOUNT; - hc->should_linger = FALSE; - } - else - { - NOTIMPLEMENTED("CGI"); - httpd_send_err(hc, 501, err501title, "", err501form, httpd_method_str(hc->method)); - return -1; - } - - return 0; -} -#endif - /* Returns 1 if ok to serve the url, 0 if not. */ static int check_referer(httpd_conn *hc) @@ -3750,7 +2753,7 @@ int httpd_parse_request(httpd_conn *hc) hc->encodedurl = url; httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, strlen(hc->encodedurl)); - strdecode(hc->decodedurl, hc->encodedurl); + httpd_strdecode(hc->decodedurl, hc->encodedurl); httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, strlen(hc->decodedurl)); (void)strcpy(hc->origfilename, &hc->decodedurl[1]); diff --git a/netutils/thttpd/libhttpd.h b/netutils/thttpd/libhttpd.h index 6b1ee0d7d0..662f5011ca 100644 --- a/netutils/thttpd/libhttpd.h +++ b/netutils/thttpd/libhttpd.h @@ -311,13 +311,6 @@ extern void httpd_destroy_conn(httpd_conn *hc); extern void httpd_send_err(httpd_conn *hc, int status, const char *title, const char *extraheads, const char *form, const char *arg); -/* Some error messages. */ - -extern const char httpd_err400title[]; -extern const char httpd_err400form[]; -extern const char httpd_err408title[]; -extern const char httpd_err408form[]; - /* Generate a string representation of a method number. */ extern const char *httpd_method_str(int method); diff --git a/netutils/thttpd/thttpd.c b/netutils/thttpd/thttpd.c index 693e141ebb..84507bce9c 100644 --- a/netutils/thttpd/thttpd.c +++ b/netutils/thttpd/thttpd.c @@ -59,7 +59,8 @@ #include "config.h" #include "fdwatch.h" #include "libhttpd.h" -#include "httpd_alloc.h" +#include "thttpd_alloc.h" +#include "thttpd_strings.h" #include "timers.h" #ifdef CONFIG_THTTPD diff --git a/netutils/thttpd/httpd_alloc.c b/netutils/thttpd/thttpd_alloc.c similarity index 98% rename from netutils/thttpd/httpd_alloc.c rename to netutils/thttpd/thttpd_alloc.c index f3504993f9..85f79adc46 100755 --- a/netutils/thttpd/httpd_alloc.c +++ b/netutils/thttpd/thttpd_alloc.c @@ -1,5 +1,5 @@ /**************************************************************************** - * netutils/thttpd/httpd_alloc.c + * netutils/thttpd/thttpd_alloc.c * * Copyright (C) 2009 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -45,7 +45,7 @@ #include #include "config.h" -#include "httpd_alloc.h" +#include "thttpd_alloc.h" #ifdef CONFIG_THTTPD diff --git a/netutils/thttpd/httpd_alloc.h b/netutils/thttpd/thttpd_alloc.h similarity index 98% rename from netutils/thttpd/httpd_alloc.h rename to netutils/thttpd/thttpd_alloc.h index c3b3eb27ed..c20834965e 100755 --- a/netutils/thttpd/httpd_alloc.h +++ b/netutils/thttpd/thttpd_alloc.h @@ -1,5 +1,5 @@ /**************************************************************************** - * netutils/thttpd/httpd_alloc.h + * netutils/thttpd/thttpd_alloc.h * * Copyright (C) 2009 Gregory Nutt. All rights reserved. * Author: Gregory Nutt diff --git a/netutils/thttpd/thttpd_cgi.c b/netutils/thttpd/thttpd_cgi.c new file mode 100755 index 0000000000..247a71d0b9 --- /dev/null +++ b/netutils/thttpd/thttpd_cgi.c @@ -0,0 +1,942 @@ +/**************************************************************************** + * netutils/thttpd/thttpd_cgi.c + * CGI support + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file libhttpd.c in the original THTTPD package: + * + * Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "config.h" +#include "libhttpd.h" +#include "thttpd_alloc.h" +#include "thttpd_strings.h" +#include "timers.h" +#include "fdwatch.h" + +#if defined(CONFIG_THTTPD) && defined(CONFIG_THTTPD_CGI_PATTERN) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum cgi_outbuffer_e +{ + CGI_OUTBUFFER_READHEADER = 0, /* Reading header from HTTP client */ + CGI_OUTBUFFER_HEADERREAD, /* Header has been read */ + CGI_OUTBUFFER_HEADERSENT, /* Header has been sent to the CGI program */ + CGI_OUTBUFFER_READDATA, /* Transferring data from CGI to client */ + CGI_OUTBUFFER_DONE, /* Finished */ +}; + +struct cgi_outbuffer_s +{ + enum cgi_outbuffer_e state; /* State of the transfer */ + char *buffer; /* Allocated I/O buffer */ + size_t size; /* Size of the allocation */ + size_t len; /* Amount of valid data in the allocated buffer */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void create_environment(httpd_conn *hc); +static char **make_argp(httpd_conn *hc); +static inline int cgi_interpose_input(httpd_conn *hc, int wfd, char *buffer); +static inline int cgi_interpose_output(httpd_conn *hc, int rfd, char *inbuffer, + struct cgi_outbuffer_s *hdr); +static int cgi_child(int argc, char **argv); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Set up environment variables. Be real careful here to avoid + * letting malicious clients overrun a buffer. We don't have + * to worry about freeing stuff since we're a sub-task. + */ + +static void create_environment(httpd_conn *hc) +{ + char *cp; + char buf[256]; + + setenv("PATH", CONFIG_THTTPD_CGI_PATH, TRUE); +#ifdef CGI_LD_LIBRARY_PATH + setenv("LD_LIBRARY_PATH", CGI_LD_LIBRARY_PATH, TRUE); +#endif /* CGI_LD_LIBRARY_PATH */ + + setenv("SERVER_SOFTWARE", CONFIG_THTTPD_SERVER_SOFTWARE, TRUE); + + /* If vhosting, use that server-name here. */ +#ifdef CONFIG_THTTPD_VHOST + if (hc->vhostname) + { + cp = hc->vhostname; + } + else +#endif + { + cp = hc->hs->hostname; + } + + if (cp) + { + setenv("SERVER_NAME", cp, TRUE); + } + + setenv("GATEWAY_INTERFACE", "CGI/1.1", TRUE); + setenv("SERVER_PROTOCOL", hc->protocol, TRUE); + + (void)snprintf(buf, sizeof(buf), "%d", (int)CONFIG_THTTPD_PORT); + setenv("SERVER_PORT", buf, TRUE); + + setenv("REQUEST_METHOD", httpd_method_str(hc->method), TRUE); + + if (hc->pathinfo[0] != '\0') + { + char *cp2; + size_t l; + + (void)snprintf(buf, sizeof(buf), "/%s", hc->pathinfo); + setenv("PATH_INFO", buf, TRUE); + + l = strlen(httpd_root) + strlen(hc->pathinfo) + 1; + cp2 = NEW(char, l); + if (cp2) + { + (void)snprintf(cp2, l, "%s%s", httpd_root, hc->pathinfo); + setenv("PATH_TRANSLATED", cp2, TRUE); + } + } + + (void)snprintf(buf, sizeof(buf), "/%s",strcmp(hc->origfilename, ".") == 0 ? "" : hc->origfilename); + setenv("SCRIPT_NAME", buf, TRUE); + + if (hc->query[0] != '\0') + { + setenv("QUERY_STRING", hc->query, TRUE); + } + + setenv("REMOTE_ADDR", httpd_ntoa(&hc->client_addr), TRUE); + if (hc->referer[0] != '\0') + { + setenv("HTTP_REFERER", hc->referer, TRUE); + } + + if (hc->useragent[0] != '\0') + { + setenv("HTTP_USER_AGENT", hc->useragent, TRUE); + } + + if (hc->accept[0] != '\0') + { + setenv("HTTP_ACCEPT", hc->accept, TRUE); + } + + if (hc->accepte[0] != '\0') + { + setenv("HTTP_ACCEPT_ENCODING", hc->accepte, TRUE); + } + + if (hc->acceptl[0] != '\0') + { + setenv("HTTP_ACCEPT_LANGUAGE", hc->acceptl, TRUE); + } + + if (hc->cookie[0] != '\0') + { + setenv("HTTP_COOKIE", hc->cookie, TRUE); + } + + if (hc->contenttype[0] != '\0') + { + setenv("CONTENT_TYPE", hc->contenttype, TRUE); + } + + if (hc->hdrhost[0] != '\0') + { + setenv("HTTP_HOST", hc->hdrhost, TRUE); + } + + if (hc->contentlength != -1) + { + (void)snprintf(buf, sizeof(buf), "%lu", (unsigned long)hc->contentlength); + setenv("CONTENT_LENGTH", buf, TRUE); + } + + if (hc->remoteuser[0] != '\0') + { + setenv("REMOTE_USER", hc->remoteuser, TRUE); + } + + if (hc->authorization[0] != '\0') + { + setenv("AUTH_TYPE", "Basic", TRUE); + } + + /* We only support Basic auth at the moment. */ + + if (getenv("TZ") != NULL) + { + setenv("TZ", getenv("TZ"), TRUE); + } + + setenv("CGI_PATTERN", CONFIG_THTTPD_CGI_PATTERN, TRUE); +} + +/* Set up argument vector */ + +static FAR char **make_argp(httpd_conn *hc) +{ + FAR char **argp; + int argn; + char *cp1; + char *cp2; + + /* By allocating an arg slot for every character in the query, plus one + * for the filename and one for the NULL, we are guaranteed to have + * enough. We could actually use strlen/2. + */ + + argp = NEW(char *, strlen(hc->query) + 2); + if (!argp) + { + return NULL; + } + + argp[0] = strrchr(hc->expnfilename, '/'); + if (argp[0]) + { + ++argp[0]; + } + else + { + argp[0] = hc->expnfilename; + } + argn = 1; + + /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html, + * "The server should search the query information for a non-encoded = + * character to determine if the command line is to be used, if it finds + * one, the command line is not to be used." + */ + + if (strchr(hc->query, '=') == NULL) + { + for (cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2) + { + if (*cp2 == '+') + { + *cp2 = '\0'; + httpd_strdecode(cp1, cp1); + argp[argn++] = cp1; + cp1 = cp2 + 1; + } + } + + if (cp2 != cp1) + { + httpd_strdecode(cp1, cp1); + argp[argn++] = cp1; + } + } + + argp[argn] = NULL; + return argp; +} + +/* Data is available from the client socket. This routine is used only for POST + * requests. It reads the data from the client and sends it to the child thread. + */ + +static inline int cgi_interpose_input(httpd_conn *hc, int wfd, char *buffer) +{ + size_t nbytes; + ssize_t nbytes_read; + ssize_t nbytes_written; + + nbytes = hc->read_idx - hc->checked_idx; + if (nbytes > 0) + { + if (httpd_write(wfd, &(hc->read_buf[hc->checked_idx]), nbytes) != nbytes) + { + return 1; + } + } + + if (nbytes < hc->contentlength) + { + do + { + nbytes_read = read(hc->conn_fd, buffer, MIN(sizeof(buffer), hc->contentlength - nbytes)); + if (nbytes_read < 0) + { + if (errno != EINTR) + { + return 1; + } + } + } + while (nbytes_read < 0); + + if (nbytes_read > 0) + { + nbytes_written = httpd_write(wfd, buffer, nbytes_read); + if (nbytes_written != nbytes_read) + { + return 1; + } + } + } + + if (nbytes >= hc->contentlength) + { + /* Special hack to deal with broken browsers that send a LF or CRLF + * after POST data, causing TCP resets - we just read and discard up + * to 2 bytes. Unfortunately this doesn't fix the problem for CGIs + * which avoid the interposer task due to their POST data being + * short. Creating an interposer task for all POST CGIs is + * unacceptably expensive. The eventual fix will come when interposing + * gets integrated into the main loop as a tasklet instead of a task. + */ + + /* Turn on no-delay mode in case we previously cleared it. */ + + httpd_set_ndelay(hc->conn_fd); + + /* And read up to 2 bytes. */ + + (void)read(hc->conn_fd, buffer, sizeof(buffer)); + return 1; + } + return 0; +} + +/* This routine is used for parsed-header CGIs. The idea here is that the + * CGI can return special headers such as "Status:" and "Location:" which + * change the return status of the response. Since the return status has to + * be the very first line written out, we have to accumulate all the headers + * and check for the special ones before writing the status. Then we write + * out the saved headers and proceed to echo the rest of the response. + */ + +static inline int cgi_interpose_output(httpd_conn *hc, int rfd, char *inbuffer, + struct cgi_outbuffer_s *hdr) +{ + ssize_t nbytes_read; + char *br = NULL; + int status; + const char *title; + char *cp; + + /* Make sure the connection is in blocking mode. It should already be + * blocking, but we might as well be sure. + */ + + httpd_clear_ndelay(hc->conn_fd); + + /* Loop while there are things we can do without waiting for more input */ + + switch (hdr->state) + { + case CGI_OUTBUFFER_READHEADER: + { + /* Slurp in all headers as they become available from the client. */ + + do + { + /* Read until we successfully read data or until an error occurs. + * EAGAIN is not an error, but it is still cause to return. + */ + + nbytes_read = read(hc->conn_fd, inbuffer, sizeof(inbuffer)); + nvdbg("Read %d bytes from fd %d\n", nbytes_read, hc->conn_fd); + + if (nbytes_read < 0) + { + if (errno != EINTR) + { + if (errno != EAGAIN) + { + ndbg("read: %d\n", errno); + } + return 1; + } + } + } + while (nbytes_read < 0); + + /* Check for end-of-file */ + + if (nbytes_read <= 0) + { + nvdbg("End-of-file\n"); + br = &(hdr->buffer[hdr->len]); + hdr->state = CGI_OUTBUFFER_HEADERREAD; + } + else + { + /* Accumulate more header data */ + + httpd_realloc_str(&hdr->buffer, &hdr->size, hdr->len + nbytes_read); + (void)memcpy(&(hdr->buffer[hdr->len]), inbuffer, nbytes_read); + hdr->len += nbytes_read; + hdr->buffer[hdr->len] = '\0'; + nvdbg("Header bytes accumulated: %d\n", hdr->len); + + /* Check for end of header */ + + if ((br = strstr(hdr->buffer, "\r\n\r\n")) != NULL || + (br = strstr(hdr->buffer, "\012\012")) != NULL) + { + nvdbg("End-of-header\n"); + hdr->state = CGI_OUTBUFFER_HEADERREAD; + } + else + { + /* Return. We will be called again when more data is available + * on the socket. + */ + + return 0; + } + } + } + break; + + case CGI_OUTBUFFER_HEADERREAD: + { + /* If there were no headers, bail. */ + + if (hdr->buffer[0] == '\0') + { + hdr->state = CGI_OUTBUFFER_DONE; + return 1; + } + + /* Figure out the status. Look for a Status: or Location: header; else if + * there's an HTTP header line, get it from there; else default to 200. + */ + + status = 200; + if (strncmp(hdr->buffer, "HTTP/", 5) == 0) + { + cp = hdr->buffer; + cp += strcspn(cp, " \t"); + status = atoi(cp); + } + + if ((cp = strstr(hdr->buffer, "Status:")) != NULL && + cp < br && (cp == hdr->buffer || *(cp - 1) == '\012')) + { + cp += 7; + cp += strspn(cp, " \t"); + status = atoi(cp); + } + + if ((cp = strstr(hdr->buffer, "Location:")) != NULL && + cp < br && (cp == hdr->buffer || *(cp - 1) == '\012')) + { + status = 302; + } + + /* Write the status line. */ + + nvdbg("Status: %d\n", status); + switch (status) + { + case 200: + title = ok200title; + break; + + case 302: + title = err302title; + break; + + case 304: + title = err304title; + break; + + case 400: + BADREQUEST("status"); + title = httpd_err400title; + break; + +#ifdef CONFIG_THTTPD_AUTH_FILE + case 401: + title = err401title; + break; +#endif + + case 403: + title = err403title; + break; + + case 404: + title = err404title; + break; + + case 408: + title = httpd_err408title; + break; + + case 500: + INTERNALERROR("status"); + title = err500title; + break; + + case 501: + NOTIMPLEMENTED("status"); + title = err501title; + break; + + case 503: + title = httpd_err503title; + break; + + default: + title = "Something"; + break; + } + + (void)snprintf(inbuffer, sizeof(inbuffer), "HTTP/1.0 %d %s\r\n", status, title); + (void)httpd_write(hc->conn_fd, inbuffer, strlen(inbuffer)); + + /* Write the saved hdr->buffer to the client. */ + + (void)httpd_write(hc->conn_fd, hdr->buffer, hdr->len); + } + + /* Then set up to read the data following the header from the CGI program and + * pass it back to the client. We return now; we will be called again when + * data is available on the pipe. + */ + + hdr->state = CGI_OUTBUFFER_READDATA; + return 0; + + case CGI_OUTBUFFER_READDATA: + { + /* Read data from the pipe. */ + + do + { + /* Read until we successfully read data or until an error occurs. + * EAGAIN is not an error, but it is still cause to return. + */ + + nbytes_read = read(rfd, inbuffer, sizeof(inbuffer)); + nvdbg("Read %d bytes from fd %d\n", nbytes_read, rfd); + + if (nbytes_read < 0) + { + if (errno != EINTR) + { + if (errno != EAGAIN) + { + ndbg("read: %d\n", errno); + } + return 1; + } + } + } + while (nbytes_read < 0); + + /* Check for end of file */ + + if (nbytes_read == 0) + { + nvdbg("End-of-file\n"); + close(hc->conn_fd); + close(rfd); + hdr->state = CGI_OUTBUFFER_DONE; + return 1; + } + else + { + /* Forward the data from the CGI program to the client */ + + (void)httpd_write(hc->conn_fd, inbuffer, strlen(inbuffer)); + } + } + break; + + case CGI_OUTBUFFER_DONE: + default: + return 1; + } + return 0; +} + +/* CGI child task. */ + +static int cgi_child(int argc, char **argv) +{ + FAR httpd_conn *hc = (FAR httpd_conn*)strtoul(argv[1], NULL, 16); +#if CONFIG_THTTPD_CGI_TIMELIMIT > 0 + ClientData client_data; +#endif + FAR char **argp; + struct cgi_outbuffer_s hdr; + struct fdwatch_s *fw; + char *buffer; + char *binary; + char *directory; + boolean indone; + boolean outdone; + int child; + int pipefd[2]; + int wfd = -1; + int rfd = -1; + int fd; + int ret; + int err = 1; + + /* Use low-level debug out (because the low-level output may survive closing + * all file descriptors + */ + + nllvdbg("Started: %s\n", argv[1]); + + /* Update all of the environment variable settings, these will be inherited + * by the CGI task. + */ + + create_environment(hc); + + /* Make the argument vector. */ + + argp = make_argp(hc); + + /* Close all file descriptors (including stdio, stdin, stdout) EXCEPT for + * stderr and hc->conn_fd + */ + + nllvdbg("Closing all descriptors\n"); + for (fd = 0; fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS); fd++) + { + /* Keep stderr open for debug; keep hc->conn_fd open for obvious reasons */ + + if (fd != 2 && fd != hc->conn_fd) + { + close(fd); + } + } + + /* Create pipes that will be interposed between the CGI task's stdin or + * stdout and the socket. + * + * Setup up the STDIN pipe - a pipe to transfer data received on the + * socket to the CGI program. + */ + + nllvdbg("Create STDIN pipe\n"); + ret = pipe(pipefd); + if (ret < 0) + { + nlldbg("STDIN pipe: %d\n", errno); + goto errout_with_descriptors; + } + else + { + /* Then map the receiving end the pipe to stdin, save the sending end, and + * closing the original receiving end + */ + + ret = dup2(pipefd[0], 0); + if (ret < 0) + { + nlldbg("STDIN dup2: %d\n", errno); + close(pipefd[1]); + goto errout_with_descriptors; + } + + wfd = pipefd[1]; + close(pipefd[0]); + } + + /* Set up the STDOUT pipe - a pipe to transfer data received from the CGI program + * to the client. + */ + + if (ret == 0) + { + nllvdbg("Create STDOUT pipe\n"); + ret = pipe(pipefd); + if (ret < 0) + { + nlldbg("STDOUT pipe: %d\n", errno); + goto errout_with_descriptors; + } + else + { + /* Then map the sending end the pipe to stdout, save the receiving end, and + * closing the original sending end + */ + + ret = dup2(pipefd[1], 1); + if (ret < 0) + { + nlldbg("STDOUT dup2: %d\n", errno); + close(pipefd[1]); + goto errout_with_descriptors; + } + + rfd = pipefd[0]; + close(pipefd[1]); + } + } + + /* Split the program into directory and binary, so we can chdir() to the + * program's own directory. This isn't in the CGI 1.1 spec, but it's what + * other HTTP servers do. + */ + + directory = strdup(hc->expnfilename); + if (!directory) + { + binary = hc->expnfilename; /* ignore errors */ + } + else + { + binary = strrchr(directory, '/'); + if (!binary) + { + binary = hc->expnfilename; + } + else + { + *binary++ = '\0'; + (void)chdir(directory); /* ignore errors */ + } + } + + /* Allocate memory for buffering */ + + memset(&hdr, 0, sizeof(struct cgi_outbuffer_s)); + httpd_realloc_str(&hdr.buffer, &hdr.size, 500); + if (!hdr.buffer) + { + nlldbg("hdr allocation failed\n"); + goto errout_with_descriptors; + } + + buffer = (char*)httpd_malloc(CONFIG_THTTPD_IOBUFFERSIZE); + if (!buffer) + { + nlldbg("buffer allocation failed\n"); + goto errout_with_header; + } + + /* Create fdwatch structures */ + + fw = fdwatch_initialize(2); + if (!fw) + { + nlldbg("fdwatch allocation failed\n"); + goto errout_with_buffer; + } + + /* Run the CGI program. */ + + nllvdbg("Starting CGI\n"); + child = exec(binary, (FAR const char **)argp, g_thttpdsymtab, g_thttpdnsymbols); + if (child < 0) + { + /* Something went wrong. */ + + nlldbg("execve %s: %d\n", hc->expnfilename, errno); + goto errout_with_watch; + } + + /* Schedule a kill for the child task in case it runs too long. */ + +#if CONFIG_THTTPD_CGI_TIMELIMIT > 0 + client_data.i = child; + if (tmr_create(NULL, cgi_kill, client_data, CONFIG_THTTPD_CGI_TIMELIMIT * 1000L, 0) == NULL) + { + nlldbg("tmr_create(cgi_kill child) failed\n"); + goto errout_with_watch; + } +#endif + + /* Add the read descriptors to the watch */ + + fdwatch_add_fd(fw, hc->conn_fd, NULL); + fdwatch_add_fd(fw, rfd, NULL); + + /* Then perform the interposition */ + + indone = FALSE; + outdone = FALSE; + + nllvdbg("Interposing\n"); + do + { + (void)fdwatch(fw, 5000); + + if (!indone && fdwatch_check_fd(fw, hc->conn_fd)) + { + /* Transfer data from the client to the CGI program (POST) */ + + nllvdbg("Interpose input\n"); + indone = cgi_interpose_input(hc, wfd, buffer); + if (indone) + { + fdwatch_del_fd(fw, hc->conn_fd); + } + } + + if (fdwatch_check_fd(fw, rfd)) + { + /* Handle receipt of headers and CGI program response (GET) */ + + nllvdbg("Interpose output\n"); + outdone = cgi_interpose_output(hc, rfd, buffer, &hdr); + } + } + while (!outdone); + err = 0; + + /* Get rid of watch structures */ + +errout_with_watch: + fdwatch_uninitialize(fw); + + /* Free memory */ + +errout_with_buffer: + httpd_free(buffer); +errout_with_header: + httpd_free(hdr.buffer); + + /* Close all descriptors */ + +errout_with_descriptors: + close(wfd); + close(rfd); + close(hc->conn_fd); + + INTERNALERROR("errout"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + nllvdbg("Return %d\n", ret); + return err; +} + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +int cgi(httpd_conn *hc) +{ + char arg[16]; + char *argv[1]; + pid_t child; + + if (hc->method == METHOD_GET || hc->method == METHOD_POST) + { +#ifdef CONFIG_THTTPD_CGILIMIT + if (hc->hs->cgi_count >= CONFIG_THTTPD_CGILIMIT) + { + httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl); + return -1; + } +#endif + ++hc->hs->cgi_count; + httpd_clear_ndelay(hc->conn_fd); + + /* Start the child task. We use a trampoline task here so that we can + * safely muck with the file descriptors before actually started the CGI + * task. + */ + + snprintf(arg, 16, "%p", hc); /* task_create doesn't handle binary arguments. */ + argv[0] = arg; + +#ifndef CONFIG_CUSTOM_STACK + child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, + CONFIG_THTTPD_CGI_STACKSIZE, + (main_t)cgi_child, (const char **)argv); +#else + child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, + (main_t)cgi_child, (const char **)argv); +#endif + if (child < 0) + { + ndbg("task_create: %d\n", errno); + INTERNALERROR("task_create"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + + ndbg("Started CGI task %d for file '%s'\n", child, hc->expnfilename); + + hc->status = 200; + hc->bytes_sent = CONFIG_THTTPD_CGI_BYTECOUNT; + hc->should_linger = FALSE; + } + else + { + NOTIMPLEMENTED("CGI"); + httpd_send_err(hc, 501, err501title, "", err501form, httpd_method_str(hc->method)); + return -1; + } + + return 0; +} + +#endif /* CONFIG_THTTPD && CONFIG_THTTPD_CGI_PATTERN */ \ No newline at end of file diff --git a/netutils/thttpd/thttpd_cgi.h b/netutils/thttpd/thttpd_cgi.h new file mode 100755 index 0000000000..2d12d7269f --- /dev/null +++ b/netutils/thttpd/thttpd_cgi.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * netutils/thttpd/thttpd_cgi.h + * CGI support + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file libhttpd.c in the original THTTPD package: + * + * Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __HTTPD_CGI_H +#define __HTTPD_CGI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "config.h" +#include "libhttpd.h" + +#if defined(CONFIG_THTTPD) && defined(CONFIG_THTTPD_CGI_PATTERN) + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern int cgi(httpd_conn *hc); + +#endif /* CONFIG_THTTPD && CONFIG_THTTPD_CGI_PATTERN */ +#endif /* __HTTPD_CGI_H */ diff --git a/netutils/thttpd/thttpd_strings.c b/netutils/thttpd/thttpd_strings.c new file mode 100755 index 0000000000..591f15665b --- /dev/null +++ b/netutils/thttpd/thttpd_strings.c @@ -0,0 +1,179 @@ +/**************************************************************************** + * netutils/thttpd/thttpd_strings.c + * HTTP strings + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file of the same name in the original THTTPD package: + * + * Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "config.h" +#include "thttpd_strings.h" + +#ifdef CONFIG_THTTPD + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* This is the 'root' of the Filesystem as seen by the HTTP client */ + +const char httpd_root[] = CONFIG_THTTPD_PATH; + +/* HTPP status */ + +const char ok200title[] = "OK"; +const char ok206title[] = "Partial Content"; + +const char err302title[] = "Found"; +const char err302form[] = "The actual URL is '%s'.\n"; + +const char err304title[] = "Not Modified"; + +const char httpd_err400title[] = "Bad Request"; +const char httpd_err400form[] = "Your request has bad syntax or is inherently impossible to satisfy.\n"; + +#ifdef CONFIG_THTTPD_AUTH_FILE +const char err401title[] = "Unauthorized"; +const char err401form[] = "Authorization required for the URL '%s'.\n"; +#endif + +const char err403title[] = "Forbidden"; +#ifndef EXPLICIT_ERROR_PAGES +const char err403form[] = "You do not have permission to get URL '%s' from this server.\n"; +#endif + +const char err404title[] = "Not Found"; +const char err404form[] = "The requested URL '%s' was not found on this server.\n"; + +const char httpd_err408title[] = "Request Timeout"; +const char httpd_err408form[] = "No request appeared within a reasonable time period.\n"; + +const char err500title[] = "Internal Error"; +const char err500form[] = "There was an unusual problem serving the requested URL '%s'.\n"; + +const char err501title[] = "Not Implemented"; +const char err501form[] = "The requested method '%s' is not implemented by this server.\n"; + +const char httpd_err503title[] = "Service Temporarily Overloaded"; +const char httpd_err503form[] = "The requested URL '%s' is temporarily overloaded. Please try again later.\n"; + +/* HTML strings */ + +const char html_crlf[] = "\r\n"; +const char html_html[] = "\r\n"; +const char html_endhtml[] = "\r\n"; +const char html_hdtitle[] = ""; +const char html_titlehd[] = "\r\n"; +const char html_body[] = "\r\n"; +const char html_endbody[] = "\r\n"; +const char html_hdr2[] = "

"; +const char html_endhdr2[] = "

"; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int hexit(char nibble) +{ + if (nibble >= '0' && nibble <= '9') + { + return nibble - '0'; + } + else if (nibble >= 'a' && nibble <= 'f') + { + return nibble - 'a' + 10; + } + else if (nibble >= 'A' && nibble <= 'F') + { + return nibble - 'A' + 10; + } + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Copies and decodes a string. It's ok for from and to to be the same string. */ + +void httpd_strdecode(char *to, char *from) +{ + for (; *from != '\0'; ++to, ++from) + { + if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) + { + *to = hexit(from[1]) * 16 + hexit(from[2]); + from += 2; + } + else + { + *to = *from; + } + } + *to = '\0'; +} + +/* Copies and encodes a string. */ + +#ifdef CONFIG_THTTPD_GENERATE_INDICES +static void httpd_strencode(char *to, int tosize, char *from) +{ + int tolen; + + for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) + { + if (isalnum(*from) || strchr("/_.-~", *from) != NULL) + { + *to = *from; + ++to; + ++tolen; + } + else + { + (void)sprintf(to, "%%%02x", (int)*from & 0xff); + to += 3; + tolen += 3; + } + } + *to = '\0'; +} +#endif /* CONFIG_THTTPD_GENERATE_INDICES */ +#endif /* CONFIG_THTTPD */ \ No newline at end of file diff --git a/netutils/thttpd/thttpd_strings.h b/netutils/thttpd/thttpd_strings.h new file mode 100755 index 0000000000..dbd22e4e09 --- /dev/null +++ b/netutils/thttpd/thttpd_strings.h @@ -0,0 +1,119 @@ +/**************************************************************************** + * netutils/thttpd/thttpd_strings.h + * HTTP strings + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file of the same name in the original THTTPD package: + * + * Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "config.h" + +#ifdef CONFIG_THTTPD + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* This is the 'root' of the Filesystem as seen by the HTTP client */ + +extern const char httpd_root[]; + +/* HTPP status */ + +extern const char ok200title[]; +extern const char ok206title[]; + +extern const char err302title[]; +extern const char err302form[]; + +extern const char err304title[]; + +extern const char httpd_err400title[]; +extern const char httpd_err400form[]; + +#ifdef CONFIG_THTTPD_AUTH_FILE +extern const char err401title[]; +extern const char err401form[]; +#endif + +extern const char err403title[]; +#ifndef EXPLICIT_ERROR_PAGES +extern const char err403form[]; +#endif + +extern const char err404title[]; +extern const char err404form[]; + +extern const char httpd_err408title[]; +extern const char httpd_err408form[]; + +extern const char err500title[]; +extern const char err500form[]; + +extern const char err501title[]; +extern const char err501form[]; + +extern const char httpd_err503title[]; +extern const char httpd_err503form[]; + +/* HTML strings */ + +extern const char html_crlf[]; +extern const char html_html[]; +extern const char html_endhtml[]; +extern const char html_hdtitle[]; +extern const char html_titlehd[]; +extern const char html_body[]; +extern const char html_endbody[]; +extern const char html_hdr2[]; +extern const char html_endhdr2[]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Copies and decodes a string. It's ok for from and to to be the same string. */ + +extern void httpd_strdecode(char *to, char *from); + +/* Copies and encodes a string. */ + +#ifdef CONFIG_THTTPD_GENERATE_INDICES +extern void httpd_strencode(char *to, int tosize, char *from); +#endif + +#endif /* CONFIG_THTTPD */ \ No newline at end of file diff --git a/netutils/thttpd/timers.c b/netutils/thttpd/timers.c index e03570f6ac..7e67e6cbe9 100644 --- a/netutils/thttpd/timers.c +++ b/netutils/thttpd/timers.c @@ -45,7 +45,7 @@ #include #include -#include "httpd_alloc.h" +#include "thttpd_alloc.h" #include "timers.h" /****************************************************************************