diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2009-07-18 18:04:05 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2009-07-18 18:04:05 +0000 |
commit | 0ae84141bbd645eff4c096b608a2aee7704ea17a (patch) | |
tree | 8f69951d409be32bcb4b4895ddb3d3a4d8171e42 | |
parent | 9b9e5bbafa1f5d03f903a4ec0985f68af7164792 (diff) | |
download | px4-nuttx-0ae84141bbd645eff4c096b608a2aee7704ea17a.tar.gz px4-nuttx-0ae84141bbd645eff4c096b608a2aee7704ea17a.tar.bz2 px4-nuttx-0ae84141bbd645eff4c096b608a2aee7704ea17a.zip |
Fix CGI I/O redirection and interposer tasks
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@1988 42af7a65-404d-4744-a932-0658087f49c3
-rw-r--r-- | nuttx/Documentation/NuttxPortingGuide.html | 6 | ||||
-rw-r--r-- | nuttx/configs/README.txt | 2 | ||||
-rw-r--r-- | nuttx/configs/eagle100/thttpd/defconfig | 3 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/cgi-src/phf.c | 9 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/cgi-src/redirect.c | 49 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/cgi-src/ssi.c | 185 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/config.h | 8 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/fdwatch.c | 213 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/fdwatch.h | 43 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/libhttpd.c | 912 | ||||
-rw-r--r-- | nuttx/netutils/thttpd/thttpd.c | 38 |
11 files changed, 771 insertions, 697 deletions
diff --git a/nuttx/Documentation/NuttxPortingGuide.html b/nuttx/Documentation/NuttxPortingGuide.html index 5c5b49ef1..ee91135ff 100644 --- a/nuttx/Documentation/NuttxPortingGuide.html +++ b/nuttx/Documentation/NuttxPortingGuide.html @@ -12,7 +12,7 @@ <h1><big><font color="#3c34ec"> <i>NuttX RTOS Porting Guide</i> </font></big></h1> - <p>Last Updated: July 12, 2009</p> + <p>Last Updated: July 18, 2009</p> </td> </tr> </table> @@ -2385,10 +2385,6 @@ extern void up_ledoff(int led); to run before killing them. </li> <li> - <code>CONFIG_THTTPD_CGI_OUTFD</code>: In NuttX, CGI cannot use stdout for output. - Rather, it must use this file descriptor number. - </li> - <li> <code>CONFIG_THTTPD_CHARSET- The default character set name to use with text MIME types. </li> diff --git a/nuttx/configs/README.txt b/nuttx/configs/README.txt index 9c23b9d92..c16e22a5c 100644 --- a/nuttx/configs/README.txt +++ b/nuttx/configs/README.txt @@ -369,8 +369,6 @@ defconfig -- This is a configuration file similar to the Linux CONFIG_THTTPD_CGI_BYTECOUNT - Byte output limit for CGI tasks. CONFIG_THTTPD_CGI_TIMELIMIT - How many seconds to allow CGI programs to run before killing them. - CONFIG_THTTPD_CGI_OUTFD - In NuttX, CGI cannot use stdout for output. - Rather, it must use this file descriptor number. CONFIG_THTTPD_CHARSET- The default character set name to use with text MIME types. CONFIG_THTTPD_IOBUFFERSIZE - diff --git a/nuttx/configs/eagle100/thttpd/defconfig b/nuttx/configs/eagle100/thttpd/defconfig index 189aa0e87..49dd962c9 100644 --- a/nuttx/configs/eagle100/thttpd/defconfig +++ b/nuttx/configs/eagle100/thttpd/defconfig @@ -494,8 +494,6 @@ CONFIG_NET_RESOLV_ENTRIES=4 # CONFIG_THTTPD_CGI_BYTECOUNT - Byte output limit for CGI tasks. # CONFIG_THTTPD_CGI_TIMELIMIT - How many seconds to allow CGI programs # to run before killing them. -# CONFIG_THTTPD_CGI_OUTFD - In NuttX, CGI cannot use stdout for output. -# Rather, it must use this file descriptor number. # CONFIG_THTTPD_CHARSET- The default character set name to use with # text MIME types. # CONFIG_THTTPD_IOBUFFERSIZE - @@ -544,7 +542,6 @@ CONFIG_THTTPD_CGI_PRIORITY=50 CONFIG_THTTPD_CGI_STACKSIZE=1024 CONFIG_THTTPD_CGI_BYTECOUNT=20000 CONFIG_THTTPD_CGI_TIMELIMIT=0 -#CONFIG_THTTPD_CGI_OUTFD CONFIG_THTTPD_CHARSET="iso-8859-1" CONFIG_THTTPD_IOBUFFERSIZE=256 #CONFIG_THTTPD_INDEX_NAMES diff --git a/nuttx/netutils/thttpd/cgi-src/phf.c b/nuttx/netutils/thttpd/cgi-src/phf.c index 16ea2fefe..a108468a8 100644 --- a/nuttx/netutils/thttpd/cgi-src/phf.c +++ b/nuttx/netutils/thttpd/cgi-src/phf.c @@ -61,15 +61,9 @@ int main(int argc, char *argv[]) { - FILE *outstream; - fprintf(stderr, "phf CGI probe from %s\n", getenv("REMOTE_ADDR")); - outstream = fdopen(CONFIG_THTTPD_CGI_OUTFD, "w"); - if (outstream) - { - (void)fprintf(outstream, -"\ + (void)printf("\ Content-type: text/html\n\ Status: 404/html\n\ \n\ @@ -79,7 +73,6 @@ The requested object does not exist on this server.\n\ The link you followed is either outdated, inaccurate,\n\ or the server has been instructed not to let you have it.\n\ </BODY></HTML>\n"); - fclose(outstream); return 0; } return 1; diff --git a/nuttx/netutils/thttpd/cgi-src/redirect.c b/nuttx/netutils/thttpd/cgi-src/redirect.c index 5aa099d59..8177b8df9 100644 --- a/nuttx/netutils/thttpd/cgi-src/redirect.c +++ b/nuttx/netutils/thttpd/cgi-src/redirect.c @@ -98,12 +98,11 @@ static char g_url[LINE_SIZE]; * Private Functions ****************************************************************************/ -static void internal_error(FILE *outstream, char *reason) +static void internal_error(char *reason) { char *title = "500 Internal Error"; - (void)fprintf(outstream, -"\ + (void)printf("\ Status: %s\n\ Content-type: text/html\n\ \n\ @@ -116,12 +115,11 @@ Something unusual went wrong during a redirection request:\n\ </BODY></HTML>\n", title, title, title, reason); } -static void not_found(FILE *outstream, char *script_name) +static void not_found(char *script_name) { char *title = "404 Not Found"; - (void)fprintf(outstream, -"\ + (void)printf("\ Status: %s\n\ Content-type: text/html\n\ \n\ @@ -132,12 +130,11 @@ however, the new URL has not yet been specified.\n\ </BODY></HTML>\n", title, title, title, script_name); } -static void moved(FILE *outstream, char *script_name, char *url) +static void moved(char *script_name, char *url) { char *title = "Moved"; - (void)fprintf(outstream, -"\ + (void)printf("\ Location: %s\n\ Content-type: text/html\n\ \n\ @@ -154,7 +151,6 @@ The requested filename, %s, has moved to a new URL:\n\ int main(int argc, char **argv) { - FILE *outstream; char *script_name; char *path_info; char *cp = 0; @@ -162,14 +158,6 @@ int main(int argc, char **argv) char *star; int err = 0; - outstream = fdopen(CONFIG_THTTPD_CGI_OUTFD, "w"); - if (!outstream) - { - fprintf(stderr, "fdopen failed: %d\n", errno); - err = 1; - goto errout; - } - /* Get the name that we were run as, which is the filename being ** * redirected. */ @@ -177,9 +165,8 @@ int main(int argc, char **argv) script_name = getenv("SCRIPT_NAME"); if (!script_name) { - internal_error(outstream, "Couldn't get SCRIPT_NAME environment variable."); - err = 2; - goto errout_with_outstream; + internal_error("Couldn't get SCRIPT_NAME environment variable."); + return 1; } /* Append the PATH_INFO, if any. This allows redirection of whole ** @@ -192,9 +179,8 @@ int main(int argc, char **argv) cp = (char *)malloc(strlen(script_name) + strlen(path_info) + 1); if (!cp) { - internal_error(outstream, "Out of memory."); - err = 3; - goto errout_with_outstream; + internal_error("Out of memory."); + return 2; } (void)sprintf(cp, "%s%s", script_name, path_info); @@ -206,8 +192,8 @@ int main(int argc, char **argv) fp = fopen(".redirects", "r"); if (fp == (FILE *) 0) { - internal_error(outstream, "Couldn't open .redirects file."); - err = 4; + internal_error("Couldn't open .redirects file."); + err = 3; goto errout_with_cp; } @@ -251,7 +237,7 @@ int main(int argc, char **argv) /* XXX Whack the script_name, too? */ - moved(outstream, script_name, g_url); + moved(script_name, g_url); goto success_out; } } @@ -262,7 +248,7 @@ int main(int argc, char **argv) { /* Got it. */ - moved(outstream, script_name, g_url); + moved(script_name, g_url); goto success_out; } } @@ -271,8 +257,8 @@ int main(int argc, char **argv) /* No match found. */ - not_found(outstream, script_name); - err = 5; + not_found(script_name); + err = 4; success_out: fclose(fp); @@ -281,8 +267,5 @@ errout_with_cp: { free(cp); } -errout_with_outstream: - fclose(outstream); -errout: return err; } diff --git a/nuttx/netutils/thttpd/cgi-src/ssi.c b/nuttx/netutils/thttpd/cgi-src/ssi.c index 042f43b65..befb7f769 100644 --- a/nuttx/netutils/thttpd/cgi-src/ssi.c +++ b/nuttx/netutils/thttpd/cgi-src/ssi.c @@ -78,7 +78,7 @@ * Private Function Prototypes ****************************************************************************/ -static void read_file(FILE *outstream, FILE *instream, char *vfilename, char *filename); +static void read_file(FILE *instream, char *vfilename, char *filename); /**************************************************************************** * Private Data @@ -96,12 +96,11 @@ static struct stat g_sb; * Private Functions ****************************************************************************/ -static void internal_error(FILE *outstream, char *reason) +static void internal_error(char *reason) { char *title = "500 Internal Error"; - (void)fprintf(outstream, -"\ + (void)printf("\ <HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\ <BODY><H2>%s</H2>\n\ Something unusual went wrong during a server-side-includes request:\n\ @@ -111,12 +110,11 @@ Something unusual went wrong during a server-side-includes request:\n\ </BODY></HTML>\n", title, title, reason); } -static void not_found(FILE *outstream, char *filename) +static void not_found(char *filename) { char *title = "404 Not Found"; - (void)fprintf(outstream, -"\ + (void)printf("\ <HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\ <BODY><H2>%s</H2>\n\ The requested server-side-includes filename, %s,\n\ @@ -124,67 +122,62 @@ does not seem to exist.\n\ </BODY></HTML>\n", title, title, filename); } -static void not_found2(FILE *outstream, char *directive, char *tag, char *filename) +static void not_found2(char *directive, char *tag, char *filename) { char *title = "Not Found"; - (void)fprintf(outstream, -"\ + (void)printf("\ <HR><H2>%s</H2>\n\ The filename requested in a %s %s directive, %s,\n\ does not seem to exist.\n\ <HR>\n", title, directive, tag, filename); } -static void not_permitted(FILE *outstream, char *directive, char *tag, char *val) +static void not_permitted(char *directive, char *tag, char *val) { char *title = "Not Permitted"; - (void)fprintf(outstream, -"\ + (void)printf("\ <HR><H2>%s</H2>\n\ The filename requested in the %s %s=%s directive\n\ may not be fetched.\n\ <HR>\n", title, directive, tag, val); } -static void unknown_directive(FILE *outstream, char *filename, char *directive) +static void unknown_directive(char *filename, char *directive) { char *title = "Unknown Directive"; - (void)fprintf(outstream, -"\ + (void)printf("\ <HR><H2>%s</H2>\n\ The requested server-side-includes filename, %s,\n\ tried to use an unknown directive, %s.\n\ <HR>\n", title, filename, directive); } -static void unknown_tag(FILE *outstream, char *filename, char *directive, char *tag) +static void unknown_tag(char *filename, char *directive, char *tag) { char *title = "Unknown Tag"; - (void)fprintf(outstream, -"\ + (void)printf("\ <HR><H2>%s</H2>\n\ The requested server-side-includes filename, %s,\n\ tried to use the directive %s with an unknown tag, %s.\n\ <HR>\n", title, filename, directive, tag); } -static void unknown_value(FILE *outstream, char *filename, char *directive, char *tag, char *val) +static void unknown_value(char *filename, char *directive, char *tag, char *val) { char *title = "Unknown Value"; - (void)fprintf(outstream, -"\ + (void)printf("\ <HR><H2>%s</H2>\n\ The requested server-side-includes filename, %s,\n\ tried to use the directive %s %s with an unknown value, %s.\n\ <HR>\n", title, filename, directive, tag, val); } -static int get_filename(FILE *outstream, char *vfilename, char *filename, +static int get_filename(char *vfilename, char *filename, char *directive, char *tag, char *val, char *fn, int fnsize) { @@ -205,7 +198,7 @@ static int get_filename(FILE *outstream, char *vfilename, char *filename, { if (strstr(val, "../") != (char *)0) { - not_permitted(outstream, directive, tag, val); + not_permitted(directive, tag, val); return -1; } @@ -228,7 +221,7 @@ static int get_filename(FILE *outstream, char *vfilename, char *filename, { if (val[0] == '/' || strstr(val, "../") != (char *)0) { - not_permitted(outstream, directive, tag, val); + not_permitted(directive, tag, val); return -1; } if (fl + 1 + strlen(val) >= fnsize) @@ -247,7 +240,7 @@ static int get_filename(FILE *outstream, char *vfilename, char *filename, } else { - unknown_tag(outstream, filename, directive, tag); + unknown_tag(filename, directive, tag); return -1; } return 0; @@ -353,7 +346,7 @@ static int check_filename(char *filename) return 1; } -static void show_time(FILE *outstream, time_t t, int gmt) +static void show_time(time_t t, int gmt) { struct tm *tmP; @@ -368,40 +361,40 @@ static void show_time(FILE *outstream, time_t t, int gmt) if (strftime(g_iobuffer2, BUFFER_SIZE, g_timeformat, tmP) > 0) { - (void)fputs(g_iobuffer2, outstream); + (void)puts(g_iobuffer2); } } -static void show_size(FILE *outstream, off_t size) +static void show_size(off_t size) { switch (g_sizefmt) { case SF_BYTES: - (void)fprintf(outstream, "%ld", (long)size); /* spec says should have commas */ + (void)printf("%ld", (long)size); /* spec says should have commas */ break; case SF_ABBREV: if (size < 1024) { - (void)fprintf(outstream, "%ld", (long)size); + (void)printf("%ld", (long)size); } else if (size < 1024) { - (void)fprintf(outstream, "%ldK", (long)size / 1024L); + (void)printf("%ldK", (long)size / 1024L); } else if (size < 1024 * 1024) { - (void)fprintf(outstream, "%ldM", (long)size / (1024L * 1024L)); + (void)printf("%ldM", (long)size / (1024L * 1024L)); } else { - (void)fprintf(outstream, "%ldG", (long)size / (1024L * 1024L * 1024L)); + (void)printf("%ldG", (long)size / (1024L * 1024L * 1024L)); } break; } } -static void do_config(FILE *outstream, FILE *instream, char *vfilename, char *filename, +static void do_config(FILE *instream, char *vfilename, char *filename, char *directive, char *tag, char *val) { /* The config directive controls various aspects of the file parsing. ** @@ -431,16 +424,16 @@ static void do_config(FILE *outstream, FILE *instream, char *vfilename, char *fi } else { - unknown_value(outstream, filename, directive, tag, val); + unknown_value(filename, directive, tag, val); } } else { - unknown_tag(outstream, filename, directive, tag); + unknown_tag(filename, directive, tag); } } -static void do_include(FILE *outstream, FILE *instream, char *vfilename, char *filename, +static void do_include(FILE *instream, char *vfilename, char *filename, char *directive, char *tag, char *val) { FILE *instream2; @@ -448,7 +441,7 @@ static void do_include(FILE *outstream, FILE *instream, char *vfilename, char *f /* Inserts the text of another document into the parsed document. */ - ret = get_filename(outstream, vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE); + ret = get_filename(vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE); if (ret < 0) { return; @@ -456,14 +449,14 @@ static void do_include(FILE *outstream, FILE *instream, char *vfilename, char *f if (!check_filename(g_iobuffer1)) { - not_permitted(outstream, directive, tag, g_iobuffer1); + not_permitted(directive, tag, g_iobuffer1); return; } instream2 = fopen(g_iobuffer1, "r"); if (instream2 == (FILE *) 0) { - not_found2(outstream, directive, tag, g_iobuffer1); + not_found2(directive, tag, g_iobuffer1); return; } @@ -498,11 +491,11 @@ static void do_include(FILE *outstream, FILE *instream, char *vfilename, char *f } } - read_file(outstream, instream2, g_iobuffer2, g_iobuffer1); + read_file(instream2, g_iobuffer2, g_iobuffer1); (void)fclose(instream2); } -static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *filename, +static void do_echo(FILE *instream, char *vfilename, char *filename, char *directive, char *tag, char *val) { char *cp; @@ -515,7 +508,7 @@ static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *file if (strcmp(tag, "var") != 0) { - unknown_tag(outstream, filename, directive, tag); + unknown_tag(filename, directive, tag); } else { @@ -523,13 +516,13 @@ static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *file { /* The current filename. */ - (void)fputs(filename, outstream); + (void)puts(filename); } else if (strcmp(val, "DOCUMENT_URI") == 0) { /* The virtual path to this file (such as /~robm/foo.shtml). */ - (void)fputs(vfilename, outstream); + (void)puts(vfilename); } else if (strcmp(val, "QUERY_STRING_UNESCAPED") == 0) { @@ -538,7 +531,7 @@ static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *file cp = getenv("QUERY_STRING"); if (cp != (char *)0) { - (void)fputs(cp, outstream); + (void)puts(cp); } } else if (strcmp(val, "DATE_LOCAL") == 0) @@ -548,7 +541,7 @@ static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *file /* The current date, local time zone. */ gettimeofday(&tm, NULL); - show_time(outstream, tm.tv_sec, 0); + show_time(tm.tv_sec, 0); } else if (strcmp(val, "DATE_GMT") == 0) { @@ -557,7 +550,7 @@ static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *file /* Same as DATE_LOCAL but in Greenwich mean time. */ gettimeofday(&tm, NULL); - show_time(outstream, t, 1); + show_time(t, 1); } else if (strcmp(val, "LAST_MODIFIED") == 0) { @@ -565,7 +558,7 @@ static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *file if (fstat(fileno(instream), &g_sb) >= 0) { - show_time(outstream, g_sb.st_mtime, 0); + show_time(g_sb.st_mtime, 0); } } else @@ -575,24 +568,24 @@ static void do_echo(FILE *outstream, FILE *instream, char *vfilename, char *file cp = getenv(val); if (cp == (char *)0) { - unknown_value(outstream, filename, directive, tag, val); + unknown_value(filename, directive, tag, val); } else { - (void)fputs(cp, outstream); + (void)puts(cp); } } } } -static void do_fsize(FILE *outstream, FILE *instream, char *vfilename, char *filename, +static void do_fsize(FILE *instream, char *vfilename, char *filename, char *directive, char *tag, char *val) { int ret; /* Prints the size of the specified file. */ - ret = get_filename(outstream, vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE); + ret = get_filename(vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE); if (ret < 0) { return; @@ -600,21 +593,21 @@ static void do_fsize(FILE *outstream, FILE *instream, char *vfilename, char *fil if (stat(g_iobuffer1, &g_sb) < 0) { - not_found2(outstream, directive, tag, g_iobuffer1); + not_found2(directive, tag, g_iobuffer1); return; } - show_size(outstream, g_sb.st_size); + show_size(g_sb.st_size); } -static void do_flastmod(FILE *outstream, FILE *instream, char *vfilename, char *filename, +static void do_flastmod(FILE *instream, char *vfilename, char *filename, char *directive, char *tag, char *val) { int ret; /* Prints the last modification date of the specified file. */ - ret = get_filename(outstream, vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE); + ret = get_filename(vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE); if (ret < 0) { return; @@ -622,13 +615,13 @@ static void do_flastmod(FILE *outstream, FILE *instream, char *vfilename, char * if (stat(g_iobuffer1, &g_sb) < 0) { - not_found2(outstream, directive, tag, g_iobuffer1); + not_found2(directive, tag, g_iobuffer1); return; } - show_time(outstream, g_sb.st_mtime, 0); + show_time(g_sb.st_mtime, 0); } -static void parse(FILE *outstream, FILE *instream, char *vfilename, char *filename, char *str) +static void parse(FILE *instream, char *vfilename, char *filename, char *str) { char *directive; char *cp; @@ -695,7 +688,7 @@ static void parse(FILE *outstream, FILE *instream, char *vfilename, char *filena } else { - unknown_directive(outstream, filename, directive); + unknown_directive(filename, directive); return; } @@ -703,7 +696,7 @@ static void parse(FILE *outstream, FILE *instream, char *vfilename, char *filena { if (i > 0) { - putc(' ', outstream); + putchar(' '); } val = strchr(g_tags[i], '='); @@ -725,29 +718,29 @@ static void parse(FILE *outstream, FILE *instream, char *vfilename, char *filena switch (dirn) { case DI_CONFIG: - do_config(outstream, instream, vfilename, filename, directive, g_tags[i], val); + do_config(instream, vfilename, filename, directive, g_tags[i], val); break; case DI_INCLUDE: - do_include(outstream, instream, vfilename, filename, directive, g_tags[i], val); + do_include(instream, vfilename, filename, directive, g_tags[i], val); break; case DI_ECHO: - do_echo(outstream, instream, vfilename, filename, directive, g_tags[i], val); + do_echo(instream, vfilename, filename, directive, g_tags[i], val); break; case DI_FSIZE: - do_fsize(outstream, instream, vfilename, filename, directive, g_tags[i], val); + do_fsize(instream, vfilename, filename, directive, g_tags[i], val); break; case DI_FLASTMOD: - do_flastmod(outstream, instream, vfilename, filename, directive, g_tags[i], val); + do_flastmod(instream, vfilename, filename, directive, g_tags[i], val); break; } } } -static void slurp(FILE *outstream, FILE *instream, char *vfilename, char *filename) +static void slurp(FILE *instream, char *vfilename, char *filename) { int state; int ich; @@ -783,7 +776,7 @@ static void slurp(FILE *outstream, FILE *instream, char *vfilename, char *filena if (ich == '>') { g_iobuffer1[i - 2] = '\0'; - parse(outstream, instream, vfilename, filename, g_iobuffer1); + parse(instream, vfilename, filename, g_iobuffer1); return; } else if (ich != '-') @@ -800,7 +793,7 @@ static void slurp(FILE *outstream, FILE *instream, char *vfilename, char *filena } } -static void read_file(FILE *outstream, FILE *instream, char *vfilename, char *filename) +static void read_file(FILE *instream, char *vfilename, char *filename) { int ich; int state; @@ -831,7 +824,7 @@ static void read_file(FILE *outstream, FILE *instream, char *vfilename, char *fi else { state = ST_GROUND; - putc('<', outstream); + putchar('<'); } break; @@ -844,7 +837,7 @@ static void read_file(FILE *outstream, FILE *instream, char *vfilename, char *fi else { state = ST_GROUND; - (void)fputs("<!", outstream); + (void)puts("<!"); } break; @@ -857,26 +850,26 @@ static void read_file(FILE *outstream, FILE *instream, char *vfilename, char *fi else { state = ST_GROUND; - (void)fputs("<!-", outstream); + (void)puts("<!-"); } break; case ST_MINUS2: if (ich == '#') { - slurp(outstream, instream, vfilename, filename); + slurp(instream, vfilename, filename); state = ST_GROUND; continue; } else { state = ST_GROUND; - (void)fputs("<!--", outstream); + (void)puts("<!--"); } break; } - putc((char)ich, outstream); + putcchar((char)ich); } } @@ -886,21 +879,12 @@ static void read_file(FILE *outstream, FILE *instream, char *vfilename, char *fi int main(int argc, char **argv) { - FILE *outstream; FILE *instream; char *script_name; char *path_info; char *path_translated; int err = 0; - outstream = fdopen(CONFIG_THTTPD_CGI_OUTFD, "w"); - if (!outstream) - { - fprintf(stderr, "fdopen failed: %d\n", errno); - err = 1; - goto errout; - } - /* Default formats. */ (void)strcpy(g_timeformat, "%a %b %e %T %Z %Y"); @@ -908,16 +892,15 @@ int main(int argc, char **argv) /* The MIME type has to be text/html. */ - (void)fputs("Content-type: text/html\n\n", outstream); + (voidfputs("Content-type: text/html\n\n"); /* Get the name that we were run as. */ script_name = getenv("SCRIPT_NAME"); if (!script_name) { - internal_error(outstream, "Couldn't get SCRIPT_NAME environment variable."); - err = 2; - goto errout_with_outstream; + internal_error("Couldn't get SCRIPT_NAME environment variable."); + return 1; } /* Append the PATH_INFO, if any, to get the full URL. */ @@ -931,9 +914,8 @@ int main(int argc, char **argv) g_url = (char*)malloc(strlen(script_name) + strlen(path_info) + 1); if (!g_url) { - internal_error(outstream, "Out of memory."); - err = 3; - goto errout_with_outstream; + internal_error("Out of memory."); + return 2; } (void)sprintf(g_url, "%s%s", script_name, path_info); @@ -942,15 +924,15 @@ int main(int argc, char **argv) path_translated = getenv("PATH_TRANSLATED"); if (!path_translated) { - internal_error(outstream, "Couldn't get PATH_TRANSLATED environment variable."); - err = 4; + internal_error("Couldn't get PATH_TRANSLATED environment variable."); + err = 3; goto errout_with_g_url; } if (!check_filename(path_translated)) { - not_permitted(outstream, "initial", "PATH_TRANSLATED", path_translated); - err = 5; + not_permitted("initial", "PATH_TRANSLATED", path_translated); + err = 4; goto errout_with_g_url; } @@ -959,21 +941,18 @@ int main(int argc, char **argv) instream = fopen(path_translated, "r"); if (!instream) { - not_found(outstream, path_translated); - err = 6; + not_found(path_translated); + err = 5; goto errout_with_g_url; } /* Read and handle the file. */ - read_file(outstream, instream, path_info, path_translated); + read_file(instream, path_info, path_translated); (void)fclose(instream); errout_with_g_url: free(g_url); -errout_with_outstream: - fclose(outstream); -errout: return err; } diff --git a/nuttx/netutils/thttpd/config.h b/nuttx/netutils/thttpd/config.h index 6ee5fa27d..c40510918 100644 --- a/nuttx/netutils/thttpd/config.h +++ b/nuttx/netutils/thttpd/config.h @@ -119,14 +119,6 @@ # define CONFIG_THTTPD_CGI_TIMELIMIT 0 /* No time limit */ #endif -/* In NuttX, CGI cannot use stdout for output. Rather, it must use - * the following file descriptor number. - */ - -#ifndef CONFIG_THTTPD_CGI_OUTFD -# define CONFIG_THTTPD_CGI_OUTFD CONFIG_NFILE_DESCRIPTORS -#endif - /* The default character set name to use with text MIME types. */ #ifndef CONFIG_THTTPD_CHARSET diff --git a/nuttx/netutils/thttpd/fdwatch.c b/nuttx/netutils/thttpd/fdwatch.c index 2f82fc19c..724631918 100644 --- a/nuttx/netutils/thttpd/fdwatch.c +++ b/nuttx/netutils/thttpd/fdwatch.c @@ -71,18 +71,9 @@ ****************************************************************************/ #if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) -static long nwatches; +static long nwatches = 0; #endif -static int *fd_rw; -static void **fd_data; -static struct pollfd *pollfds; -static int *poll_pollndx; -static int *poll_rfdidx; -static int npoll_fds; -static int nreturned; -static int next_rfndx; - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -97,87 +88,123 @@ static int next_rfndx; /* Initialize the fdwatch data structures. Returns -1 on failure. */ -int fdwatch_initialize(void) +struct fdwatch_s *fdwatch_initialize(int nfds) { + struct fdwatch_s *fw; int i; + /* Allocate the fdwatch data structure */ + + fw = (struct fdwatch_s*)zalloc(sizeof(struct fdwatch_s)); + if (!fw) + { + ndbg("Failed to allocate fdwatch\n"); + return NULL; + } + /* Initialize the fdwatch data structures. */ -#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) - nwatches = 0; -#endif + fw->nfds = nfds; - fd_rw = (int *)malloc(sizeof(int) * CONFIG_NSOCKET_DESCRIPTORS); - if (!fd_rw) + fw->fd_rw = (int*)malloc(sizeof(int) * nfds); + if (!fw->fd_rw) { - goto errout; + goto errout_with_allocations; } - fd_data = (void **)malloc(sizeof(void*) * CONFIG_NSOCKET_DESCRIPTORS); - if (!fd_data) + fw->fd_data = (void**)malloc(sizeof(void*) * nfds); + if (!fw->fd_data) { - goto errout_with_fd_rw; + goto errout_with_allocations; } - for (i = 0; i < CONFIG_NSOCKET_DESCRIPTORS; ++i) + for (i = 0; i < nfds; ++i) { - fd_rw[i] = -1; + fw->fd_rw[i] = -1; } - pollfds = (struct pollfd *)malloc(sizeof(struct pollfd) * CONFIG_NSOCKET_DESCRIPTORS); - if (!pollfds) + fw->pollfds = (struct pollfd*)malloc(sizeof(struct pollfd) * nfds); + if (!fw->pollfds) { - goto errout_with_fd_data; + goto errout_with_allocations; } - poll_pollndx = (int *)malloc(sizeof(int) * CONFIG_NSOCKET_DESCRIPTORS); - if (!poll_pollndx) + fw->poll_pollndx = (int*)malloc(sizeof(int) * nfds); + if (!fw->poll_pollndx) { - goto errout_with_pollfds; + goto errout_with_allocations; } - poll_rfdidx = (int *)malloc(sizeof(int) * CONFIG_NSOCKET_DESCRIPTORS); - if (!poll_rfdidx) + fw->poll_rfdidx = (int*)malloc(sizeof(int) * nfds); + if (!fw->poll_rfdidx) { - goto errout_with_poll_pollndx; + goto errout_with_allocations; } - for (i = 0; i < CONFIG_NSOCKET_DESCRIPTORS; i++) + for (i = 0; i < nfds; i++) { - pollfds[i].fd = poll_pollndx[i] = -1; + fw->pollfds[i].fd = fw->poll_pollndx[i] = -1; } - return OK; - -errout_with_poll_pollndx: - free(poll_pollndx); -errout_with_pollfds: - free(pollfds); -errout_with_fd_data: - free(fd_data); -errout_with_fd_rw: - free(fd_rw); -errout: - return ERROR; + return fw; + +errout_with_allocations: + fdwatch_uninitialize(fw); + return NULL; +} + +/* Uninitialize the fwdatch data structure */ + +void fdwatch_uninitialize(struct fdwatch_s *fw) +{ + if (fw) + { + if (fw->fd_rw) + { + free(fw->fd_rw); + } + + if (fw->fd_data) + { + free(fw->fd_data); + } + + if (fw->pollfds) + { + free(fw->pollfds); + } + + if (fw->poll_pollndx) + { + free(fw->poll_pollndx); + } + + if (fw->poll_rfdidx) + { + free(fw->poll_rfdidx); + } + + free(fw); + } } /* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ -void fdwatch_add_fd(int fd, void *client_data, int rw) +void fdwatch_add_fd(struct fdwatch_s *fw, int fd, void *client_data, int rw) { int fdndx; #ifdef CONFIG_DEBUG if (fd < CONFIG_NFILE_DESCRIPTORS || - fd >= CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS || - fd_rw[fd-CONFIG_NFILE_DESCRIPTORS] != -1) + fd >= CONFIG_NFILE_DESCRIPTORS+fw->nfds || + fw->fd_rw[fd-CONFIG_NFILE_DESCRIPTORS] != -1) { ndbg("Received bad fd (%d)\n", fd); return; } #endif - if (npoll_fds >= CONFIG_NSOCKET_DESCRIPTORS) + if (fw->npoll_fds >= fw->nfds) { ndbg("too many fds\n"); return; @@ -189,31 +216,31 @@ void fdwatch_add_fd(int fd, void *client_data, int rw) /* Save the new fd at the end of the list */ - pollfds[npoll_fds].fd = fd; + fw->pollfds[fw->npoll_fds].fd = fd; switch (rw) { default: case FDW_READ: - pollfds[npoll_fds].events = POLLIN; + fw->pollfds[fw->npoll_fds].events = POLLIN; break; case FDW_WRITE: - pollfds[npoll_fds].events = POLLOUT; + fw->pollfds[fw->npoll_fds].events = POLLOUT; break; } /* Save the new index and increment the cound of watched descriptors */ - poll_pollndx[fdndx] = npoll_fds; - npoll_fds++; + fw->poll_pollndx[fdndx] = fw->npoll_fds; + fw->npoll_fds++; - fd_rw[fdndx] = rw; - fd_data[fdndx] = client_data; + fw->fd_rw[fdndx] = rw; + fw->fd_data[fdndx] = client_data; } /* Remove a descriptor from the watch list. */ -void fdwatch_del_fd(int fd) +void fdwatch_del_fd(struct fdwatch_s *fw, int fd) { int fdndx; int pollndx; @@ -221,8 +248,8 @@ void fdwatch_del_fd(int fd) #ifdef CONFIG_DEBUG if (fd < CONFIG_NFILE_DESCRIPTORS || - fd >= CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS || - fd_rw[fd-CONFIG_NFILE_DESCRIPTORS] == -1) + fd >= CONFIG_NFILE_DESCRIPTORS+fw->nfds || + fw->fd_rw[fd-CONFIG_NFILE_DESCRIPTORS] == -1) { ndbg("Received bad fd: %d\n", fd); return; @@ -234,10 +261,10 @@ void fdwatch_del_fd(int fd) */ fdndx = fd-CONFIG_NFILE_DESCRIPTORS; - pollndx = poll_pollndx[fdndx]; + pollndx = fw->poll_pollndx[fdndx]; #ifdef CONFIG_DEBUG - if (pollndx < 0 || pollndx >= CONFIG_NSOCKET_DESCRIPTORS) + if (pollndx < 0 || pollndx >= fw->nfds) { ndbg("Bad poll index: %d\n", pollndx); return; @@ -246,20 +273,20 @@ void fdwatch_del_fd(int fd) /* Decrement the number of fds in the poll table */ - npoll_fds--; + fw->npoll_fds--; /* Replace the deleted one with the one at the the end * of the list. */ - tmpndx = pollfds[pollndx].fd - CONFIG_NFILE_DESCRIPTORS; - pollfds[pollndx] = pollfds[npoll_fds]; - poll_pollndx[tmpndx] = poll_pollndx[fdndx];; - pollfds[npoll_fds].fd = -1; - poll_pollndx[fdndx] = -1; + tmpndx = fw->pollfds[pollndx].fd - CONFIG_NFILE_DESCRIPTORS; + fw->pollfds[pollndx] = fw->pollfds[fw->npoll_fds]; + fw->poll_pollndx[tmpndx] = fw->poll_pollndx[fdndx];; + fw->pollfds[fw->npoll_fds].fd = -1; + fw->poll_pollndx[fdndx] = -1; - fd_rw[fdndx] = -1; - fd_data[fdndx] = NULL; + fw->fd_rw[fdndx] = -1; + fw->fd_data[fdndx] = NULL; } /* Do the watch. Return value is the number of descriptors that are ready, @@ -267,7 +294,7 @@ void fdwatch_del_fd(int fd) * wait indefinitely. */ -int fdwatch(long timeout_msecs) +int fdwatch(struct fdwatch_s *fw, long timeout_msecs) { int rfndx; int ret; @@ -277,18 +304,18 @@ int fdwatch(long timeout_msecs) nwatches++; #endif - ret = poll(pollfds, npoll_fds, (int)timeout_msecs); + ret = poll(fw->pollfds, fw->npoll_fds, (int)timeout_msecs); if (ret <= 0) { return ret; } rfndx = 0; - for (i = 0; i < npoll_fds; i++) + for (i = 0; i < fw->npoll_fds; i++) { - if (pollfds[i].revents & (POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL)) + if (fw->pollfds[i].revents & (POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL)) { - poll_rfdidx[rfndx++] = pollfds[i].fd; + fw->poll_rfdidx[rfndx++] = fw->pollfds[i].fd; if (rfndx == ret) { break; @@ -296,21 +323,20 @@ int fdwatch(long timeout_msecs) } } - next_rfndx = 0; return rfndx; } /* Check if a descriptor was ready. */ -int fdwatch_check_fd(int fd) +int fdwatch_check_fd(struct fdwatch_s *fw, int fd) { int fdndx; int pollndx; #ifdef CONFIG_DEBUG if (fd < CONFIG_NFILE_DESCRIPTORS || - fd >= CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS || - fd_rw[fd-CONFIG_NFILE_DESCRIPTORS] == -1) + fd >= CONFIG_NFILE_DESCRIPTORS+fw->nfds || + fw->fd_rw[fd-CONFIG_NFILE_DESCRIPTORS] == -1) { ndbg("Bad fd: %d\n", fd); return 0; @@ -322,28 +348,28 @@ int fdwatch_check_fd(int fd) */ fdndx = fd-CONFIG_NFILE_DESCRIPTORS; - pollndx = poll_pollndx[fdndx]; + pollndx = fw->poll_pollndx[fdndx]; #ifdef CONFIG_DEBUG - if (pollndx < 0 || pollndx >= CONFIG_NSOCKET_DESCRIPTORS) + if (pollndx < 0 || pollndx >= fw->nfds) { ndbg("Bad poll index: %d\n", pollndx); return 0; } #endif - if (pollfds[pollndx].revents & POLLERR) + if (fw->pollfds[pollndx].revents & POLLERR) { return 0; } - switch (fd_rw[fdndx]) + switch (fw->fd_rw[fdndx]) { case FDW_READ: - return pollfds[pollndx].revents & (POLLIN | POLLHUP | POLLNVAL); + return fw->pollfds[pollndx].revents & (POLLIN | POLLHUP | POLLNVAL); case FDW_WRITE: - return pollfds[pollndx].revents & (POLLOUT | POLLHUP | POLLNVAL); + return fw->pollfds[pollndx].revents & (POLLOUT | POLLHUP | POLLNVAL); default: break; @@ -351,40 +377,33 @@ int fdwatch_check_fd(int fd) return 0; } -void *fdwatch_get_next_client_data(void) +void *fdwatch_get_next_client_data(struct fdwatch_s *fw) { int rfndx; int fdndx; int fd; - if (next_rfndx >= nreturned) - { - return (void*)-1; - } - - rfndx = next_rfndx++; - #ifdef CONFIG_DEBUG - if (rfndx < 0 || rfndx >= CONFIG_NSOCKET_DESCRIPTORS) + if (rfndx < 0 || rfndx >= fw->nfds) { ndbg("Bad rfndx: %d\n", rfndx); return NULL; } #endif - fd = poll_rfdidx[rfndx]; + fd = fw->poll_rfdidx[rfndx]; fdndx = fd-CONFIG_NFILE_DESCRIPTORS; - if (fdndx < 0 || fdndx >= CONFIG_NSOCKET_DESCRIPTORS) + if (fdndx < 0 || fdndx >= fw->nfds) { return NULL; } - return fd_data[fdndx]; + return fw->fd_data[fdndx]; } /* Generate debugging statistics ndbg message. */ #if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) -void fdwatch_logstats(long secs) +void fdwatch_logstats(struct fdwatch_s *fw, long secs) { if (secs > 0) ndbg("fdwatch - %ld polls (%g/sec)\n", nwatches, (float)nwatches / secs); diff --git a/nuttx/netutils/thttpd/fdwatch.h b/nuttx/netutils/thttpd/fdwatch.h index cf6bd551e..65a6704ce 100644 --- a/nuttx/netutils/thttpd/fdwatch.h +++ b/nuttx/netutils/thttpd/fdwatch.h @@ -40,6 +40,10 @@ #ifndef __NETUTILS_THTTPD_FDWATCH_H #define __NETUTILS_THTTPD_FDWATCH_H +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + #define FDW_READ 0 #define FDW_WRITE 1 @@ -47,39 +51,62 @@ # define INFTIM -1 #endif -/* initialize the fdwatch data structures. Returns -1 on failure. */ +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct fdwatch_s +{ + int *fd_rw; + void **fd_data; + struct pollfd *pollfds; + int *poll_pollndx; + int *poll_rfdidx; + int nfds; + int npoll_fds; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initialize the fdwatch data structures. Returns NULL on failure. */ + +extern struct fdwatch_s *fdwatch_initialize(int nfds); + +/* Uninitialize the fwdatch data structure */ -extern int fdwatch_initialize(void); +extern void fdwatch_uninitialize(struct fdwatch_s *fw); /* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ -extern void fdwatch_add_fd(int fd, void *client_data, int rw); +extern void fdwatch_add_fd(struct fdwatch_s *fw, int fd, void *client_data, int rw); /* Delete a descriptor from the watch list. */ -extern void fdwatch_del_fd(int fd); +extern void fdwatch_del_fd(struct fdwatch_s *fw, int fd); /* Do the watch. Return value is the number of descriptors that are ready, * or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means * wait indefinitely. */ -extern int fdwatch(long timeout_msecs); +extern int fdwatch(struct fdwatch_s *fw, long timeout_msecs); /* Check if a descriptor was ready. */ -extern int fdwatch_check_fd(int fd); +extern int fdwatch_check_fd(struct fdwatch_s *fw, int fd); /* Get the client data for the next returned event. Returns -1 when there * are no more events. */ -extern void *fdwatch_get_next_client_data(void); +extern void *fdwatch_get_next_client_data(struct fdwatch_s *fw); /* Generate debugging statistics syslog message. */ #if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) -extern void fdwatch_logstats(long secs); +extern void fdwatch_logstats(struct fdwatch_s *fw, long secs); #endif #endif /* __NETUTILS_THTTPD_FDWATCH_H */ diff --git a/nuttx/netutils/thttpd/libhttpd.c b/nuttx/netutils/thttpd/libhttpd.c index ed49a4aee..b9b2508ba 100644 --- a/nuttx/netutils/thttpd/libhttpd.c +++ b/nuttx/netutils/thttpd/libhttpd.c @@ -65,6 +65,7 @@ #include "libhttpd.h" #include "timers.h" #include "tdate_parse.h" +#include "fdwatch.h" #ifdef CONFIG_THTTPD @@ -107,6 +108,29 @@ extern char *crypt(const char *key, const char *setting); #endif /**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_THTTPD_CGI_PATTERN +enum cgi_outbuffer_e +{ + CGI_OUTBUFFER_READHEADER = 0, + CGI_OUTBUFFER_HEADERREAD, + CGI_OUTBUFFER_HEADERSENT, + CGI_OUTBUFFER_READDATA, + CGI_OUTBUFFER_DONE, +}; + +struct cgi_outbuffer_s +{ + enum cgi_outbuffer_e state; + char *buffer; + size_t size; + size_t len; +}; +#endif + +/**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -166,10 +190,10 @@ static char *hostname_map(char *hostname); #ifdef CONFIG_THTTPD_CGI_PATTERN static void create_environment(httpd_conn *hc); static char **make_argp(httpd_conn *hc); -static void cgi_interpose_input(httpd_conn *hc, int wfd); -static void post_post_garbage_hack(httpd_conn *hc); -static void cgi_interpose_output(httpd_conn *hc, int rfd); -static void cgi_child(int argc, char **argv); +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 @@ -198,37 +222,6 @@ static pid_t main_thread; static int str_alloc_count = 0; static size_t str_alloc_size = 0; -/* Base-64 decoding. This represents binary data as printable ASCII - * characters. Three 8-bit binary bytes are turned into four 6-bit - * values, like so: - * - * [11111111][22222222][33333333] -> [111111][112222][222233][333333] - * - * Then the 6-bit values are represented using the characters "A-Za-z0-9+/". - */ - -#ifdef CONFIG_THTTPD_AUTH_FILE -static const int b64_decode_table[256] = -{ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */ - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */ - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */ - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */ - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */ - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */ -}; -#endif - /* Include MIME encodings and types */ #include "mime_types.h" @@ -313,7 +306,6 @@ static int initialize_listen_socket(httpd_sockaddr *saP) ndbg("socket %s: %d\n", httpd_ntoa(saP), errno); return -1; } - (void)fcntl(listen_fd, F_SETFD, 1); /* Allow reuse of local addresses. */ @@ -590,7 +582,7 @@ static int send_err_file(httpd_conn *hc, int status, char *title, char *extrahea return 1; } -#endif /* CONFIG_THTTPD_ERROR_DIRECTORY */ +#endif #ifdef CONFIG_THTTPD_AUTH_FILE static void send_authenticate(httpd_conn *hc, char *realm) @@ -613,6 +605,52 @@ static void send_authenticate(httpd_conn *hc, char *realm) } } +/* Base-64 decoding. This represents binary data as printable ASCII + * characters. Three 8-bit binary bytes are turned into four 6-bit + * values, like so: + * + * [11111111][22222222][33333333] -> [111111][112222][222233][333333] + * + * Then the 6-bit values are represented using the characters "A-Za-z0-9+/". + */ + +static inline b64_charmap(char *ch) +{ + char bin6; + + bin6 = -1; + if (c == 0x20) /* ' ' */ + { + bin6 = 62; /* ' ' maps to 62 */ + } + elseif (c == 0x2f) /* '/' */ + { + bin6 = 63; /* '/' maps to 63 */ + } + else if (c >= 0x30) /* '0' */ + { + else if (c <= 0x39) /* '9' */ + { + bin6 = (c - 0x39 + 52); /* '0'-'9' maps to 52-61 */ + } + else if (c >= 0x41) /* 'A' */ + { + if (c <= 0x5a) /* 'Z' */ + { + bin6 = c - 0x41; /* 'A'-'Z' map to 0 - 25 */ + } + else if (c >= 0x61) /* 'a' */ + { + if (c <= 0x7a) /* 'z' */ + { + bin6 = c - 0x61 + 26; /* 'a'-'z' map to 0 - 25 */ + } + } + } + } + +} + /* Do base-64 decoding on a string. Ignore any non-base64 bytes. * Return the actual number of bytes generated. The decoded size will * be at most 3/4 the size of the encoded, and may be smaller if there @@ -622,45 +660,46 @@ static void send_authenticate(httpd_conn *hc, char *realm) static int b64_decode(const char *str, unsigned char *space, int size) { const char *cp; - int space_idx, phase; - int d, prev_d = 0; - unsigned char c; + int ndx; + int phase; + int decoded; + int prev_decoded = 0; + unsigned char packed; - space_idx = 0; + ndx = 0; phase = 0; - for (cp = str; *cp != '\0'; ++cp) + for (cp = str; *cp != '\0', ndx < size; cp++) { - d = b64_decode_table[(int)*cp]; - if (d != -1) + /* Decode base-64 */ + + decoded = b64_charmap(*cp); /* Decode ASCII representations to 6-bit binary */ + if (decoded != -1) { switch (phase) { case 0: - ++phase; + phase = 1; break; + case 1: - c = ((prev_d << 2) | ((d & 0x30) >> 4)); - if (space_idx < size) - space[space_idx++] = c; - ++phase; + space[ndx++] = ((prev_decoded << 2) | ((decoded & 0x30) >> 4)); + phase = 2; break; + case 2: - c = (((prev_d & 0xf) << 4) | ((d & 0x3c) >> 2)); - if (space_idx < size) - space[space_idx++] = c; - ++phase; + space[ndx++] =(((prev_decoded & 0xf) << 4) | ((decoded & 0x3packed) >> 2)); + phase = 3; break; + case 3: - c = (((prev_d & 0x03) << 6) | d); - if (space_idx < size) - space[space_idx++] = c; + space[ndx++] =(((prev_decoded & 0x03) << 6) | decoded); phase = 0; break; } - prev_d = d; + prev_decoded = decoded; } } - return space_idx; + return ndx; } /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ @@ -901,19 +940,19 @@ static void send_dirredirect(httpd_conn *hc) send_response(hc, 302, err302title, header, err302form, location); } -static int hexit(char c) +static int hexit(char nibble) { - if (c >= '0' && c <= '9') + if (nibble >= '0' && nibble <= '9') { - return c - '0'; + return nibble - '0'; } - else if (c >= 'a' && c <= 'f') + else if (nibble >= 'a' && nibble <= 'f') { - return c - 'a' + 10; + return nibble - 'a' + 10; } - else if (c >= 'A' && c <= 'F') + else if (nibble >= 'A' && nibble <= 'F') { - return c - 'A' + 10; + return nibble - 'A' + 10; } return 0; } @@ -990,7 +1029,7 @@ static intCONFIG_THTTPD_TILDE_MAP1(httpd_conn *hc) (void)strcat(hc->expnfilename, temp); return 1; } -#endif /*CONFIG_THTTPD_TILDE_MAP1 */ +#endif /* Map a ~username/whatever URL into <user's homedir>/<postfix>. */ @@ -1057,7 +1096,7 @@ static intCONFIG_THTTPD_TILDE_MAP2(httpd_conn *hc) hc->tildemapped = TRUE; return 1; } -#endif /*CONFIG_THTTPD_TILDE_MAP2 */ +#endif /* Virtual host mapping. */ @@ -1073,7 +1112,7 @@ static int vhost_map(httpd_conn *hc) #ifdef VHOST_DIRLEVELS int i; char *cp2; -#endif /* VHOST_DIRLEVELS */ +#endif /* Figure out the virtual hostname. */ @@ -2186,80 +2225,74 @@ static FAR char **make_argp(httpd_conn *hc) } #endif -/* This routine is used only for POST requests. It reads the data - * from the request and sends it to the child process. The only reason - * we need to do it this way instead of just letting the child read - * directly is that we have already read part of the data into our - * buffer. +/* 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 void cgi_interpose_input(httpd_conn *hc, int wfd) +static inline int cgi_interpose_input(httpd_conn *hc, int wfd, char *buffer) { - size_t c; - ssize_t r; - char buf[1024]; + size_t nbytes; + ssize_t nbytes_read; + ssize_t nbytes_written; - c = hc->read_idx - hc->checked_idx; - if (c > 0) + nbytes = hc->read_idx - hc->checked_idx; + if (nbytes > 0) { - if (httpd_write(wfd, &(hc->read_buf[hc->checked_idx]), c) != c) + if (httpd_write(wfd, &(hc->read_buf[hc->checked_idx]), nbytes) != nbytes) { - return; + return 1; } } - while (c < hc->contentlength) + + if (nbytes < hc->contentlength) { - r = read(hc->conn_fd, buf, MIN(sizeof(buf), hc->contentlength - c)); - if (r < 0 && (errno == EINTR || errno == EAGAIN)) - { - sleep(1); - continue; - } - if (r <= 0) + do { - return; + 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 (httpd_write(wfd, buf, r) != r) + if (nbytes_read > 0) { - return; + nbytes_written = httpd_write(wfd, buffer, nbytes_read); + if (nbytes_written != nbytes_read) + { + return 1; + } } - - c += r; } - post_post_garbage_hack(hc); -} -#endif - -/* 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 process due to their POST data being - * short. Creating an interposer process 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 process. - */ -#ifdef CONFIG_THTTPD_CGI_PATTERN -static void post_post_garbage_hack(httpd_conn *hc) -{ - char buf[2]; + 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 process due to their POST data being + * short. Creating an interposer process 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 process. + */ - /* If we are in a sub-process, turn on no-delay mode in case we previously - * cleared it. - */ + /* Turn on no-delay mode in case we previously cleared it. */ - if (main_thread != getpid()) - { httpd_set_ndelay(hc->conn_fd); - } - /* And read up to 2 bytes. */ + /* And read up to 2 bytes. */ - (void)read(hc->conn_fd, buf, sizeof(buf)); + (void)read(hc->conn_fd, buffer, sizeof(buffer)); + return 1; + } + return 0; } -#endif /* CONFIG_THTTPD_CGI_PATTERN */ +#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 @@ -2270,12 +2303,10 @@ static void post_post_garbage_hack(httpd_conn *hc) */ #ifdef CONFIG_THTTPD_CGI_PATTERN -static void cgi_interpose_output(httpd_conn *hc, int rfd) +static inline int cgi_interpose_output(httpd_conn *hc, int rfd, char *inbuffer, + struct cgi_outbuffer_s *hdr) { - int r; - char buf[1024]; - size_t headers_size, headers_len; - char *headers; + ssize_t nbytes_read; char *br; int status; char *title; @@ -2287,203 +2318,251 @@ static void cgi_interpose_output(httpd_conn *hc, int rfd) httpd_clear_ndelay(hc->conn_fd); - /* Slurp in all headers. */ + /* Loop while there are things we can do without waiting for more input */ - headers_size = 0; - httpd_realloc_str(&headers, &headers_size, 500); - headers_len = 0; - for (;;) + switch (hdr->state) { - r = read(rfd, buf, sizeof(buf)); - if (r < 0 && (errno == EINTR || errno == EAGAIN)) + case CGI_OUTBUFFER_READHEADER: { - sleep(1); - continue; - } + /* Slurp in all headers as they become available from the CGI program. */ - if (r <= 0) - { - br = &(headers[headers_len]); - break; - } + do + { + /* Read until we successfully read data or until an error occurs. + * EAGAIN is not an error, but it is still cause to return. + */ - httpd_realloc_str(&headers, &headers_size, headers_len + r); - (void)memmove(&(headers[headers_len]), buf, r); - headers_len += r; - headers[headers_len] = '\0'; + nbytes_read = read(rfd, inbuffer, sizeof(inbuffer)); + if (nbytes_read < 0) + { + if (errno != EINTR) + { + if (errno != EAGAIN) + { + ndbg("read: %d\n", errno); + } + return 1; + } + } + } + while (nbytes_read < 0); - if ((br = strstr(headers, "\015\012\015\012")) != NULL || - (br = strstr(headers, "\012\012")) != NULL) - { - break; + /* Check for end-of-file */ + + if (nbytes_read <= 0) + { + 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'; + + /* Check for end of header */ + + if ((br = strstr(hdr->buffer, "\015\012\015\012")) != NULL || + (br = strstr(hdr->buffer, "\012\012")) != NULL) + { + hdr->state = CGI_OUTBUFFER_HEADERREAD; + } + else + { + /* Return. We will be called again when more data is available + * on the pipe. + */ + + return 0; + } + } } - } + break; - /* If there were no headers, bail. */ + case CGI_OUTBUFFER_HEADERREAD: + { + /* If there were no headers, bail. */ - if (headers[0] == '\0') - { - return; - } + 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. - */ + /* 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(headers, "HTTP/", 5) == 0) - { - cp = headers; - cp += strcspn(cp, " \t"); - status = atoi(cp); - } + status = 200; + if (strncmp(hdr->buffer, "HTTP/", 5) == 0) + { + cp = hdr->buffer; + cp += strcspn(cp, " \t"); + status = atoi(cp); + } - if ((cp = strstr(headers, "Status:")) != (char *)0 && - cp < br && (cp == headers || *(cp - 1) == '\012')) - { - cp += 7; - cp += strspn(cp, " \t"); - status = atoi(cp); - } + if ((cp = strstr(hdr->buffer, "Status:")) != (char *)0 && + cp < br && (cp == hdr->buffer || *(cp - 1) == '\012')) + { + cp += 7; + cp += strspn(cp, " \t"); + status = atoi(cp); + } - if ((cp = strstr(headers, "Location:")) != (char *)0 && - cp < br && (cp == headers || *(cp - 1) == '\012')) - { - status = 302; - } + if ((cp = strstr(hdr->buffer, "Location:")) != (char *)0 && + cp < br && (cp == hdr->buffer || *(cp - 1) == '\012')) + { + status = 302; + } - /* Write the status line. */ + /* Write the status line. */ - switch (status) - { - case 200: - title = ok200title; - break; + switch (status) + { + case 200: + title = ok200title; + break; - case 302: - title = err302title; - break; + case 302: + title = err302title; + break; - case 304: - title = err304title; - break; + case 304: + title = err304title; + break; - case 400: - title = httpd_err400title; - break; + case 400: + title = httpd_err400title; + break; #ifdef CONFIG_THTTPD_AUTH_FILE - case 401: - title = err401title; - break; -#endif /* CONFIG_THTTPD_AUTH_FILE */ + case 401: + title = err401title; + break; +#endif - case 403: - title = err403title; - break; + case 403: + title = err403title; + break; - case 404: - title = err404title; - break; + case 404: + title = err404title; + break; - case 408: - title = httpd_err408title; - break; + case 408: + title = httpd_err408title; + break; - case 500: - title = err500title; - break; + case 500: + title = err500title; + break; - case 501: - title = err501title; - break; + case 501: + title = err501title; + break; - case 503: - title = httpd_err503title; - break; + case 503: + title = httpd_err503title; + break; - default: - title = "Something"; - break; - } + default: + title = "Something"; + break; + } - (void)snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, title); - (void)httpd_write(hc->conn_fd, buf, strlen(buf)); + (void)snprintf(inbuffer, sizeof(inbuffer), "HTTP/1.0 %d %s\015\012", status, title); + (void)httpd_write(hc->conn_fd, inbuffer, strlen(inbuffer)); - /* Write the saved headers. */ + /* Write the saved hdr->buffer. */ - (void)httpd_write(hc->conn_fd, headers, headers_len); + (void)httpd_write(hc->conn_fd, hdr->buffer, hdr->len); + } - /* Echo the rest of the output. */ + /* Then set up to read the data following the header from the CGI program. + * We return here; we will be called again when data is available on the pipe. + */ - for (;;) - { - r = read(rfd, buf, sizeof(buf)); - if (r < 0 && (errno == EINTR || errno == EAGAIN)) - { - sleep(1); - continue; - } + hdr->state = CGI_OUTBUFFER_READDATA; + return 0; - if (r <= 0) + case CGI_OUTBUFFER_READDATA: { - break; - } + /* Read data from the pipe. */ - if (httpd_write(hc->conn_fd, buf, r) != r) - { - break; + 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)); + 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) + { + 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; - close(hc->conn_fd); + case CGI_OUTBUFFER_DONE: + default: + return 1; + } + return 0; } #endif /* CGI child process. */ #ifdef CONFIG_THTTPD_CGI_PATTERN -static void cgi_child(int argc, char **argv) +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; - char *binary; - char *directory; - int child; - - /* Unset close-on-exec flag for this socket. This actually shouldn't be - * necessary, according to POSIX a dup()'d file descriptor does *not* - * inherit the close-on-exec flag, its flag is always clear. However, - * Linux messes this up and does copy the flag to the dup()'d descriptor, - * so we have to clear it. This could be ifdeffed for Linux only. - */ - - (void)fcntl(hc->conn_fd, F_SETFD, 0); - - /* If the socket happens to be using one of the stdin/stdout/stderr - * descriptors, move it to another descriptor so that the dup2 calls below - * don't screw things up. We arbitrarily pick fd 3 - if there was already - * something on it, we clobber it, but that doesn't matter since at this - * point the only fd of interest is the connection. All others will be - * closed on exec. - */ - - if (hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO || - hc->conn_fd == STDERR_FILENO) - { - int newfd = dup2(hc->conn_fd, STDERR_FILENO + 1); - if (newfd >= 0) - { - hc->conn_fd = newfd; - } - - /* If the dup2 fails, shrug. We'll just take our chances. Shouldn't - * happen though. - */ - } + 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; + int rfd; + int fd; + int ret; + int err = 1; /* Update all of the environment variable settings, these will be inherited * by the CGI task. @@ -2495,140 +2574,80 @@ static void cgi_child(int argc, char **argv) argp = make_argp(hc); - /* Set up stdin. For POSTs we may have to set up a pipe from an - * interposer process, depending on if we've read some of the data into - * our buffer. + /* Close all file descriptors (including stdio, stdin, stdout) EXCEPT for + * stderr and hc->conn_fd */ - if (hc->method == METHOD_POST && hc->read_idx > hc->checked_idx) + for (fd = 0; fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS); fd++) { - char child_arg1[16]; - char child_arg2[16]; - char *child_argv[2]; - int p[2]; - - if (pipe(p) < 0) - { - ndbg("pipe: %d\n", errno); - httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); - httpd_write_response(hc); - exit(1); - } - - /* Start the cig_interpose_input task */ - - snprintf(child_arg1, 16, "%p", hc); /* task_create doesn't handle binary arguments. */ - child_argv[0] = child_arg1; - snprintf(child_arg2, 16, "%08x", p[1]); /* task_create doesn't handle binary arguments. */ - child_argv[1] = child_arg2; + /* Keep stderr open for debug; keep hc->conn_fd open for obvious reasons */ -#ifndef CONFIG_CUSTOM_STACK - child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, - CONFIG_THTTPD_CGI_STACKSIZE, - (main_t)cgi_interpose_input, (const char **)child_argv); -#else - child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, - (main_t)cgi_interpose_input, (const char **)child_argv); -#endif - if (child < 0) - { - ndbg("task_create: %d\n", errno); - httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); - httpd_write_response(hc); - exit(1); - } + if (fd != 2 && fd != hc->conn_fd) + { + close(fd); + } + } - /* Need to schedule a kill for process child; but in the main process! */ + /* 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. + */ - (void)close(p[1]); - if (p[0] != STDIN_FILENO) - { - (void)dup2(p[0], STDIN_FILENO); - (void)close(p[0]); - } + ret = pipe(pipefd); + if (ret < 0) + { + ndbg("STDIN pipe: %d\n", errno); + goto errout_with_descriptors; } else { - /* Otherwise, the request socket is stdin. */ + /* Then map the receiving end the pipe to stdin, save the sending end, and + * closing the original receiving end + */ - if (hc->conn_fd != STDIN_FILENO) + ret = dup2(pipefd[0], 0); + if (ret < 0) { - (void)dup2(hc->conn_fd, STDIN_FILENO); + ndbg("STDIN dup2: %d\n", errno); + close(pipefd[1]); + goto errout_with_descriptors; } + + wfd = pipefd[1]; + close(pipefd[0]); } - /* Set up stdout/stderr. If we're doing CGI header parsing, we need an - * output interposer too. + /* Set up the STDOUT pipe - a pipe to transfer data received from the CGI program + * to the client. */ - if (strncmp(argp[0], "nph-", 4) != 0 && hc->mime_flag) + if (ret == 0) { - char child_arg1[16]; - char child_arg2[16]; - char *child_argv[2]; - int p[2]; - - if (pipe(p) < 0) - { - ndbg("pipe: %d\n", errno); - httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); - httpd_write_response(hc); - exit(1); - } - - /* Start the cgi_interpose_output task */ - - snprintf(child_arg1, 16, "%p", hc); /* task_create doesn't handle binary arguments. */ - child_argv[0] = child_arg1; - snprintf(child_arg2, 16, "%08x", p[0]); /* task_create doesn't handle binary arguments. */ - child_argv[1] = child_arg2; - -#ifndef CONFIG_CUSTOM_STACK - child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, - CONFIG_THTTPD_CGI_STACKSIZE, - (main_t)cgi_interpose_output, (const char **)child_argv); -#else - child = task_create("CGI child", CONFIG_THTTPD_CGI_PRIORITY, - (main_t)cgi_interpose_output, (const char **)child_argv); -#endif - if (child < 0) + ret = pipe(pipefd); + if (ret < 0) { - ndbg("fork: %d\n", errno); - httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); - httpd_write_response(hc); - exit(1); + ndbg("STDOUT pipe: %d\n", errno); + goto errout_with_descriptors; } - - /* Need to schedule a kill for process child; but in the main process! */ - - (void)close(p[0]); - if (p[1] != STDOUT_FILENO) - { - (void)dup2(p[1], STDOUT_FILENO); - } - - if (p[1] != STDERR_FILENO) - { - (void)dup2(p[1], STDERR_FILENO); - } - - if (p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO) + else { - (void)close(p[1]); - } - } - else - { - /* Otherwise, the request socket is stdout/stderr. */ + /* Then map the sending end the pipe to stdout, save the receiving end, and + * closing the original sending end + */ - if (hc->conn_fd != STDOUT_FILENO) - { - (void)dup2(hc->conn_fd, STDOUT_FILENO); - } + ret = dup2(pipefd[1], 1); + if (ret < 0) + { + ndbg("STDOUT dup2: %d\n", errno); + close(pipefd[1]); + goto errout_with_descriptors; + } - if (hc->conn_fd != STDERR_FILENO) - { - (void)dup2(hc->conn_fd, STDERR_FILENO); + rfd = pipefd[0]; + close(pipefd[1]); } } @@ -2656,7 +2675,38 @@ static void cgi_child(int argc, char **argv) } } - /* Run the program. */ + /* Allocate memory for buffering */ + + memset(&hdr, 0, sizeof(struct cgi_outbuffer_s)); + httpd_realloc_str(&hdr.buffer, &hdr.size, 500); + if (!hdr.buffer) + { + ndbg("hdr allocation failed\n"); + goto errout_with_descriptors; + } + + buffer = (char*)malloc(CONFIG_THTTPD_IOBUFFERSIZE); + if (!buffer) + { + ndbg("buffer allocation failed\n"); + goto errout_with_header; + } + + /* Create fdwatch structures */ + + fw = fdwatch_initialize(2); + if (!fw) + { + ndbg("fdwatch allocation failed\n"); + goto errout_with_buffer; + } + + /* Add the read descriptors to the watch */ + + fdwatch_add_fd(fw, hc->conn_fd, NULL, FDW_READ); + fdwatch_add_fd(fw, rfd, NULL, FDW_READ); + + /* Run the CGI program. */ child = exec(binary, (FAR const char **)argp, g_thttpdsymtab, g_thttpdnsymbols); if (child < 0) @@ -2664,24 +2714,63 @@ static void cgi_child(int argc, char **argv) /* Something went wrong. */ ndbg("execve %s: %d\n", hc->expnfilename, errno); - httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); - httpd_write_response(hc); - exit(1); + goto errout_with_watch; } - else - { - /* Schedule a kill for the child process, in case it runs too long. */ + + /* Schedule a kill for the child process, in case it runs too long. */ #if CONFIG_THTTPD_CGI_TIMELIMIT > 0 - client_data.i = child; - if (tmr_create((struct timeval *)0, cgi_kill, client_data, - CONFIG_THTTPD_CGI_TIMELIMIT * 1000L, 0) == (Timer *) 0) + client_data.i = child; + if (tmr_create((struct timeval *)0, cgi_kill, client_data, + CONFIG_THTTPD_CGI_TIMELIMIT * 1000L, 0) == (Timer *) 0) + { + ndbg("tmr_create(cgi_kill child) failed\n"); + goto errout_with_watch; + } +#endif + + /* Then perform the interposition */ + + indone = FALSE; + outdone = FALSE; + + do + { + if (!indone) { - ndbg("tmr_create(cgi_kill child) failed\n"); - exit(1); + indone = cgi_interpose_input(hc, wfd, buffer); } -#endif - } + + if (!outdone) + { + 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: + free(buffer); +errout_with_header: + free(hdr.buffer); + + /* Close all descriptors */ + +errout_with_descriptors: + close(wfd); + close(rfd); + close(hc->conn_fd); + + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + return err; } #endif /* CONFIG_THTTPD_CGI_PATTERN */ @@ -2873,7 +2962,7 @@ static int really_start_request(httpd_conn *hc, struct timeval *nowP) /* Ok, generate an index. */ return ls(hc); -#else /* CONFIG_THTTPD_GENERATE_INDICES */ +#else ndbg("%s URL \"%s\" tried to index a directory\n", httpd_ntoa(&hc->client_addr), hc->encodedurl); httpd_send_err(hc, 403, err403title, "", @@ -2881,7 +2970,7 @@ static int really_start_request(httpd_conn *hc, struct timeval *nowP) "The requested URL '%s' is a directory, and directory indexing is disabled on this server.\n"), hc->encodedurl); return -1; -#endif /* CONFIG_THTTPD_GENERATE_INDICES */ +#endif got_one: @@ -2962,7 +3051,7 @@ static int really_start_request(httpd_conn *hc, struct timeval *nowP) hc->encodedurl); return -1; } -#endif /* CONFIG_THTTPD_AUTH_FILE */ +#endif /* Referer check. */ @@ -3169,7 +3258,7 @@ static int really_check_referer(httpd_conn *hc) return 1; } -#endif /* CONFIG_THTTPD_VHOST */ +#endif #endif /* CONFIG_THTTPD_LOCALPATTERN */ /* If the referer host doesn't match the local host pattern, and the @@ -3187,7 +3276,7 @@ static int really_check_referer(httpd_conn *hc) return 1; } -#endif /* CONFIG_THTTPD_URLPATTERN */ +#endif static int sockaddr_check(httpd_sockaddr * saP) { @@ -3199,7 +3288,7 @@ static int sockaddr_check(httpd_sockaddr * saP) #ifdef CONFIG_NET_IPv6 case AF_INET6: return 1; -#endif /* CONFIG_NET_IPv6 */ +#endif default: return 0; @@ -3216,7 +3305,7 @@ static size_t sockaddr_len(httpd_sockaddr * saP) #ifdef CONFIG_NET_IPv6 case AF_INET6: return sizeof(struct sockaddr_in6); -#endif /* CONFIG_NET_IPv6 */ +#endif default: break; @@ -3410,11 +3499,11 @@ void httpd_send_err(httpd_conn *hc, int status, char *title, char *extraheads, send_response(hc, status, title, extraheads, form, arg); -#else /* CONFIG_THTTPD_ERROR_DIRECTORY */ +#else send_response(hc, status, title, extraheads, form, arg); -#endif /* CONFIG_THTTPD_ERROR_DIRECTORY */ +#endif } char *httpd_method_str(int method) @@ -3492,7 +3581,6 @@ int httpd_get_conn(httpd_server * hs, int listen_fd, httpd_conn *hc) return GC_FAIL; } - (void)fcntl(hc->conn_fd, F_SETFD, 1); hc->hs = hs; (void)memset(&hc->client_addr, 0, sizeof(hc->client_addr)); (void)memmove(&hc->client_addr, &sa, sockaddr_len(&sa)); @@ -3526,7 +3614,7 @@ int httpd_get_conn(httpd_server * hs, int listen_fd, httpd_conn *hc) hc->buffer[0] = '\0'; #ifdef CONFIG_THTTPD_TILDE_MAP2 hc->altdir[0] = '\0'; -#endif /*CONFIG_THTTPD_TILDE_MAP2 */ +#endif hc->buflen = 0; hc->if_modified_since = (time_t) - 1; hc->range_if = (time_t) - 1; @@ -4108,7 +4196,7 @@ int httpd_parse_request(httpd_conn *hc) { ndbg("unknown request header: %s\n", buf); } -#endif /* LOG_UNKNOWN_HEADERS */ +#endif } } @@ -4223,7 +4311,7 @@ int httpd_parse_request(httpd_conn *hc) hc->expnfilename[strlen(hc->altdir)] == '/'))) { } -#endif /*CONFIG_THTTPD_TILDE_MAP2 */ +#endif else { ndbg("%s URL \"%s\" goes outside the web tree\n", @@ -4317,7 +4405,7 @@ char *httpd_ntoa(httpd_sockaddr *saP) return inet_ntoa(saP->sin_addr); -#endif /* CONFIG_NET_IPv6 */ +#endif } /* Read to requested buffer, accounting for interruptions and EOF */ diff --git a/nuttx/netutils/thttpd/thttpd.c b/nuttx/netutils/thttpd/thttpd.c index cccacaf3b..8d26933fa 100644 --- a/nuttx/netutils/thttpd/thttpd.c +++ b/nuttx/netutils/thttpd/thttpd.c @@ -108,6 +108,7 @@ static struct connect_s *connects; static int num_connects; static int first_free_connect; static int httpd_conn_count; +static struct fdwatch_s *fw; /**************************************************************************** * Public Data @@ -180,7 +181,7 @@ static void shut_down(void) hs = (httpd_server *) 0; if (ths->listen_fd != -1) { - fdwatch_del_fd(ths->listen_fd); + fdwatch_del_fd(fw, ths->listen_fd); } httpd_terminate(ths); } @@ -271,7 +272,7 @@ static int handle_newconnect(struct timeval *tv, int listen_fd) httpd_set_ndelay(conn->hc->conn_fd); - fdwatch_add_fd(conn->hc->conn_fd, conn, FDW_READ); + fdwatch_add_fd(fw, conn->hc->conn_fd, conn, FDW_READ); #if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) ++stats_connections; @@ -403,8 +404,8 @@ static void handle_read(struct connect_s *conn, struct timeval *tv) conn->conn_state = CNST_SENDING; client_data.p = conn; - fdwatch_del_fd(hc->conn_fd); - fdwatch_add_fd(hc->conn_fd, conn, FDW_WRITE); + fdwatch_del_fd(fw, hc->conn_fd); + fdwatch_add_fd(fw, hc->conn_fd, conn, FDW_WRITE); return; errout_with_400: @@ -560,12 +561,12 @@ static void clear_connection(struct connect_s *conn, struct timeval *tv) { if (conn->conn_state != CNST_PAUSING) { - fdwatch_del_fd(conn->hc->conn_fd); + fdwatch_del_fd(fw, conn->hc->conn_fd); } conn->conn_state = CNST_LINGERING; close(conn->hc->conn_fd); - fdwatch_add_fd(conn->hc->conn_fd, conn, FDW_READ); + fdwatch_add_fd(fw, conn->hc->conn_fd, conn, FDW_READ); client_data.p = conn; if (conn->linger_timer != (Timer *) 0) @@ -594,7 +595,7 @@ static void really_clear_connection(struct connect_s *conn, struct timeval *tv) #endif if (conn->conn_state != CNST_PAUSING) { - fdwatch_del_fd(conn->hc->conn_fd); + fdwatch_del_fd(fw, conn->hc->conn_fd); } httpd_close_conn(conn->hc, tv); @@ -685,7 +686,7 @@ static void logstats(struct timeval *nowP) thttpd_logstats(stats_secs); httpd_logstats(stats_secs); - fdwatch_logstats(stats_secs); + fdwatch_logstats(fw, stats_secs); tmr_logstats(stats_secs); } #endif @@ -742,7 +743,6 @@ int thttpd_main(int argc, char **argv) struct sockaddr_in sa; #endif struct timeval tv; - int ret; /* Setup host address */ @@ -773,10 +773,12 @@ int thttpd_main(int argc, char **argv) (void)strcat(cwd, "/"); } - /* Initialize the fdwatch package */ + /* Initialize the fdwatch package to handle all of the configured + * socket descriptors + */ - ret = fdwatch_initialize(); - if (ret < 0) + fw = fdwatch_initialize(CONFIG_NSOCKET_DESCRIPTORS); + if (!fw) { ndbg("fdwatch initialization failure\n"); exit(1); @@ -860,7 +862,7 @@ int thttpd_main(int argc, char **argv) if (hs != (httpd_server *) 0) { if (hs->listen_fd != -1) - fdwatch_add_fd(hs->listen_fd, (void *)0, FDW_READ); + fdwatch_add_fd(fw, hs->listen_fd, (void *)0, FDW_READ); } /* Main loop */ @@ -870,7 +872,7 @@ int thttpd_main(int argc, char **argv) { /* Do the fd watch */ - num_ready = fdwatch(tmr_mstimeout(&tv)); + num_ready = fdwatch(fw, tmr_mstimeout(&tv)); if (num_ready < 0) { if (errno == EINTR || errno == EAGAIN) @@ -892,7 +894,7 @@ int thttpd_main(int argc, char **argv) /* Is it a new connection? */ if (hs != (httpd_server *) 0 && hs->listen_fd != -1 && - fdwatch_check_fd(hs->listen_fd)) + fdwatch_check_fd(fw, hs->listen_fd)) { if (handle_newconnect(&tv, hs->listen_fd)) { @@ -908,14 +910,14 @@ int thttpd_main(int argc, char **argv) /* Find the connections that need servicing */ while ((conn = - (struct connect_s *) fdwatch_get_next_client_data()) != - (struct connect_s *) - 1) + (struct connect_s*)fdwatch_get_next_client_data(fw)) != + (struct connect_s*)- 1) { if (conn == (struct connect_s *) 0) continue; hc = conn->hc; - if (!fdwatch_check_fd(hc->conn_fd)) + if (!fdwatch_check_fd(fw, hc->conn_fd)) { /* Something went wrong */ |