Mercurial > ecos
changeset 2485:0e9d1d1df4a0
* src/jim-aio.c (JimAioHandlerCommand): GCC 4.3.1 pointed out a
buffer overrun.
Jumbo patch containing many individual patches from a number of
different contributors on different days.
| author | asl |
|---|---|
| date | Wed, 18 Jun 2008 18:23:25 +0000 |
| parents | da947b293f1b |
| children | 792a40c91f2a |
| files | packages/net/athttpd/current/ChangeLog packages/net/athttpd/current/cdl/httpd.cdl packages/net/athttpd/current/doc/athttpd.sgml packages/net/athttpd/current/include/auth.h packages/net/athttpd/current/include/http.h packages/net/athttpd/current/src/auth.c packages/net/athttpd/current/src/cgi.c packages/net/athttpd/current/src/forms.c packages/net/athttpd/current/src/handler.c packages/net/athttpd/current/src/http.c packages/net/athttpd/current/src/jim-aio.c packages/net/athttpd/current/src/socket.c |
| diffstat | 12 files changed, 417 insertions(+), 352 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/net/athttpd/current/ChangeLog +++ b/packages/net/athttpd/current/ChangeLog @@ -1,3 +1,83 @@ +2008-06-18 Oyvind Harboe <oyvind.harboe@zylin.com> + + * src/jim-aio.c (JimAioHandlerCommand): GCC 4.3.1 pointed out a + buffer overrun. + +2008-06-17 Anthony Tonizzo <atonizzo@gmail.com> + + Jumbo patch containing many individual patches from a number of + different contributors on different days. The ChangeLog entries + for all these individual patches are: + + 2007-12-02 Anthony Tonizzo <atonizzo@gmail.com> + + * src/socket.c src/httpd.c: Corrected a bug in + cyg_httpd_write_chunked(): Now the CRLF that terminates a chunk is + added by the function and not expected to be there already in the + data supplied by the user. Modified the function + cyg_httpd_end_chunked() so that the flag + CYG_HTTPD_MODE_TRANSFER_CHUNKED is always cleared after it + terminates. The cyg_httpd_process_request() now loops as many + times as the number of full requests received. Renamed header_end + to request_end to better reflect its real use, and the fact that + the former name was misleading for POST requests (Danny + Sade). Changed the function that collects a request: Now the + remaining part of an incomplete request is copied back to the + beginning of the buffer. This avoids that multiple split headers + might creep towards the end of the buffer. + + 2007-11-28 Rene' Nielsen <rbn@vitesse.com> and + Anthony Tonizzo <atonizzo@gmail.com> + + * doc/athttpd.sgml: Included Digest (MD5) authentication as fully + supported. + * cdl/httpd.cdl: Updated the cdl to reflect the fact that MD5 + authentication is now a fully tested feature, at least on some + clients (Thanks to Tad for testing this!) + * src/httpd.c: Added the initialization of the global variable + cyg_httpd_md5_response. This corrects a security hole that could + allow unauthenticated browsers to access pages that require + authentication. This required a minor change (switch of variables + used) in the authentication code. Added code to avoid a buffer + overflow during the parsing of headers for authenticated + pages. Clear the CYG_HTTPD_MODE_SEND_HEADER_ONLY at the beginning + of any request. + * src/auth.c: Removed dead code and made static a bunch of + functions and variables. + * src/auth.h: The AUTH_STORAGE_BUFFER_LENGTH value is now split + into two numbers, one that gives the maximum length of the login + phrase and one for the password. + * src/socket.c: Added a NULL terminator after each header packet + that is received. Now the strstr() is guaranteed to find + terminated string. + + 2007-11-27 Tad Artis <ecos@ds3switch.com> + + * src/auth.c: Modified the cyg_httpd_digest_data() and + cyg_httpd_digest_skip() functions to support IE7. A careful read + of the augmented BNF in RFC2616 indicates that spaces within the + elements of the authentication header are optional. The original + atHTTPD code incorrectly relied on a space after the comma. Moved + the authentication check inside cyg_httpd_process_method() so that + each request, regardless of the type, will have to go through + authentication. + + 2007-11-26 Tad Artis <ecos@ds3switch.com> and + Anthony Tonizzo <atonizzo@gmail.com> + + * src/forms.c: Checked for a null terminator inside + cyg_httpd_store_form_variable, so that we do not scan past the end + of the packet. Check the form variables for both length and + content, to avoid false positives. Modified cyg_httpd_from_hex() + to return -1 in case of error. + * src/httpd.c: Corrected the strings sent back by the server to + challenge the client to an MD5 authentication. Adds a couple of + commas to separate the items in the string. + * src/http.h: + * src/socket.h: Corrected a number of indexes where + CYGNUM_FILEIO_NFILE was used instead of the correct + CYGPKG_NET_MAXSOCKETS. + 2008-03-04 Danny Sade <danny@channelot.com> * src/socket.c: put NULL at the end of inbuff on each iteration,
--- a/packages/net/athttpd/current/cdl/httpd.cdl +++ b/packages/net/athttpd/current/cdl/httpd.cdl @@ -79,24 +79,26 @@ cdl_package CYGPKG_ATHTTPD { flavor data default_value { CYGNUM_KERNEL_SCHED_PRIORITIES/2 } legal_values 0 to CYGNUM_KERNEL_SCHED_PRIORITIES - description "The HTTP server threads can be run at any priority. - The exact priority depends on the importance of the - server relative to the rest of the system. The default - is to put it in the middle of the priority range to provide - reasonable response without impacting genuine high - priority threads." + description "The HTTP server threads can be run at any + priority. The exact priority depends on the + importance of the server relative to the rest + of the system. The default is to put it in the + middle of the priority range to provide + reasonable response without impacting genuine + high priority threads." } cdl_option CYGNUM_NET_ATHTTPD_THREADOPT_STACKSIZE { display "Thread stack size" flavor data default_value 4096 - description "This is the amount of extra stack to be allocated for - the HTTPD thread. This value is added to - CYGNUM_HAL_STACK_SIZE_MINIMUM to determine the final - size of the stack for the server." - } - } + description "This is the amount of extra stack to be + allocated for the HTTPD thread. This value is + added to CYGNUM_HAL_STACK_SIZE_MINIMUM to + determine the final size of the stack for the + server." + } + } cdl_component CYGOPT_NET_ATHTTPD_SERVEROPT { display "Server settings" @@ -117,7 +119,8 @@ cdl_package CYGPKG_ATHTTPD { display "Server ID string" flavor data default_value {"\"eCos Embedded Web Server\""} - description "This is the string sent out in the 'Server:' header line." + description "This is the string sent out in the 'Server:' + header line." } cdl_option CYGNUM_ATHTTPD_SERVER_BUFFER_SIZE { @@ -125,8 +128,8 @@ cdl_package CYGPKG_ATHTTPD { flavor data default_value 2048 description "This option defines the size of the buffers used to - receive and transmit transmit data to and from the TCP/IP - stack." + receive and transmit transmit data to and from the + TCP/IP stack." } cdl_option CYGNUM_ATHTTPD_SERVER_MAX_POST { @@ -141,51 +144,60 @@ cdl_package CYGPKG_ATHTTPD { display "HTTPD root directory" flavor data default_value {"\"/\""} - description "This is the absolute path in the eCos file system to the - HTML documents, including cgi-bin files and error - files and it is generally where the web server will - look for the index file. Include a trailing slash." + description "This is the absolute path in the eCos file + system to the HTML documents, including cgi-bin + files and error files and it is generally where + the web server will look for the index + file. Include a trailing slash." } cdl_option CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR { display "cgi-bin directory" - active_if { 0 != CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER || 0 != CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL } + active_if { 0 != CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER || + 0 != CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL } flavor data default_value {"\"cgi-bin/\""} - description "This is the path, relative to CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR, - where the cgi-bin files are stored. Based on the extension - of the cgi-bin file requested, the appropriate interpreter - will be used. Include a trailing slash." + description "This is the path, relative to + CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR, where the + cgi-bin files are stored. Based on the + extension of the cgi-bin file requested, the + appropriate interpreter will be used. Include a + trailing slash." } cdl_option CYGDAT_NET_ATHTTPD_SERVEROPT_ERRORDIR { display "Error files directory" flavor data default_value {"\"errors/\""} - description "This is the path, relative to the CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR, - that contains the user-defined files that are sent out - by the server in case of error. The files are named - error_XXX.html where XXX is the 3 digit HTML code. - For example, for a 404 error the server file will be - named error_404.html. Upon a 404 error, the server will - check the existence of such a file in this directory and - if found, it will send it out. Missing that file, a - standard simple message will be sent instead. Include - a trailing slash." + description "This is the path, relative to the + CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR, that + contains the user-defined files that are sent + out by the server in case of error. The files + are named error_XXX.html where XXX is the 3 + digit HTML code. For example, for a 404 error + the server file will be named + error_404.html. Upon a 404 error, the server + will check the existence of such a file in this + directory and if found, it will send it + out. Missing that file, a standard simple + message will be sent instead. Include a + trailing slash." } cdl_option CYGDAT_NET_ATHTTPD_DEFAULT_MIME_TYPE { display "Default MIME type" flavor data default_value {"\"text/plain\""} - description "When accessing internal resources or a file system, - the MIME type is determined by first finding the extension - of the file itself and then by looking up the extension - in the MIME table. In case no extension is found the user - can define the default MIME type to use. Notice that - this is the full MIME type and not the extension. - A list of standard MIME types sorted by extension can - be found in the current/doc directory" + description "When accessing internal resources or a file + system, the MIME type is determined by first + finding the extension of the file itself and + then by looking up the extension in the MIME + table. In case no extension is found the user + can define the default MIME type to use. Notice + that this is the full MIME type and not the + extension. A list of standard MIME types + sorted by extension can be found in the + current/doc directory" } cdl_option CYGDAT_NET_ATHTTPD_ALTERNATE_HOME { @@ -213,13 +225,12 @@ cdl_package CYGPKG_ATHTTPD { } cdl_option CYGOPT_NET_ATHTTPD_USE_AUTH { - display "Support for basic authentication" + display "Support for basic and digest authentication" flavor bool default_value 0 description " This option enables the use basic authentication in web pages. - Digest authentication code is also included, but has not been - extensively tested." + Not all clients have been tested." compile md5c.c compile auth.c } @@ -229,10 +240,10 @@ cdl_package CYGPKG_ATHTTPD { flavor bool default_value 1 description " - This option causes connections used for chunked transfer to be - closed after use. Persisting the connection will use less - network resources and will improve latency, but may do so at - the risk of compatibility with older browsers." + This option causes connections used for chunked transfer + to be closed after use. Persisting the connection will use + less network resources and will improve latency, but may + do so at the risk of compatibility with older browsers." } cdl_option CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME {
--- a/packages/net/athttpd/current/doc/athttpd.sgml +++ b/packages/net/athttpd/current/doc/athttpd.sgml @@ -50,7 +50,7 @@ The server has been written for the Free <listitem><para>MIME type support</para></listitem> <listitem><para>CGI mechanism through the OBJLOADER package or through a simple tcl interpreter</para></listitem> - <listitem><para>Basic Authentication</para></listitem> + <listitem><para>Basic and Digest (MD5) Authentication</para></listitem> <listitem><para>Directory Listing</para></listitem> <listitem><para>Extendable Internal Resources</para></listitem> </itemizedlist> @@ -337,7 +337,10 @@ as being "1":</para> <programlisting width=72>GET /myForm.cgi?foo=1</programlisting> -<para>then tcl will be able to access the variable foo as $foo.</para> +<para>then tcl will be able to access the variable foo as $foo. The data +in the body of a POST request is also accessible through the use of the variable +$post_data. This is useful if the data is not in "multipart/form-data" +and tcl has to perform any type of processing on the data itself.</para> <para>In order to send back a response to the client a few functions have been added to the interpreter. These functions are:</para>
--- a/packages/net/athttpd/current/include/auth.h +++ b/packages/net/athttpd/current/include/auth.h @@ -69,6 +69,10 @@ typedef enum cyg_httpd_auth_type CYG_HTTPD_AUTH_DIGEST = 1 } cyg_httpd_auth_type; +#define AUTH_STORAGE_BUFFER_LENGTH_LOGIN 32 +#define AUTH_STORAGE_BUFFER_LENGTH_PASSWORD 32 +#define AUTH_STORAGE_BUFFER_LENGTH (AUTH_STORAGE_BUFFER_LENGTH_LOGIN +\ + AUTH_STORAGE_BUFFER_LENGTH_PASSWORD) // It must be stressed that the auth_dirname field is the directory name // that will be requested by the web server, and _not_ the absolute name // in the eCos file system. @@ -94,11 +98,8 @@ typedef struct cyg_httpd_auth_table_entr cyg_httpd_auth_table_entry __name CYG_HAL_TABLE_ENTRY( httpd_auth_table ) = \ { __path, __domain, __un, __pw, __mode } -cyg_int32 cyg_httpd_base64_encode(char*, char*, cyg_uint32 ); -cyg_int32 cyg_httpd_base64_decode(char*, char*, cyg_uint32 ); cyg_httpd_auth_table_entry* cyg_httpd_auth_entry_from_path(char *); cyg_httpd_auth_table_entry* cyg_httpd_auth_entry_from_domain(char *); -cyg_httpd_auth_table_entry* cyg_httpd_verify_auth(char*, char*); cyg_httpd_auth_table_entry* cyg_httpd_is_authenticated(char*); char* cyg_httpd_digest_data(char *, char *); char* cyg_httpd_digest_skip(char *); @@ -123,24 +124,4 @@ extern char cyg_httpd_md5_noncecount[]; extern char cyg_httpd_md5_ha2[]; extern char cyg_httpd_md5_ha1[]; - -// Calculate H(A1) as per HTTP Digest spec. -void cyg_httpd_digest_calc_HA1(char *, - char *, - char *, - char *, - char *, - char *, - HASHHEX); - -// Calculate request-digest/response-digest as per HTTP Digest spec. -void cyg_httpd_digest_calc_response(HASHHEX, - char *, - char *, - char *, - char *, - char *, - char *, - HASHHEX, - HASHHEX); #endif // __AUTH_H__
--- a/packages/net/athttpd/current/include/http.h +++ b/packages/net/athttpd/current/include/http.h @@ -154,12 +154,17 @@ typedef struct cyg_int32 payload_len; char outbuffer[CYG_HTTPD_MAXOUTBUFFER+1]; - socket_entry sockets[CYGNUM_FILEIO_NFILE]; + socket_entry sockets[CYGPKG_NET_MAXSOCKETS]; cyg_int32 fdmax; // Socket handle. cyg_int32 client_index; + // Modified-since is always reset to -1 before parsing the headers of a + // request. If the "Modified-Since" element is present in the header then + // we'll copy the value in this variable, otherwise it will remain to -1. + // This will tell us if we can send a CYG_HTTPD_STATUS_NOT_MODIFIED back to + // the client or instead we'll have to send the whole page again. time_t modified_since; time_t last_modified; @@ -168,8 +173,9 @@ typedef struct #endif // Pointer to the data immediately following the last byte of the header. - // In a POST request, this is where the goods are. - char *header_end; + // In a POST request, this is where the goods are. After the post request + // is handles it will point to the start of the new request, if any. + char *request_end; // This pointer points to the buffer where we collected all the post // data (it might come in more than one frame) and must be visible to
--- a/packages/net/athttpd/current/src/auth.c +++ b/packages/net/athttpd/current/src/auth.c @@ -74,10 +74,12 @@ CYG_HAL_TABLE_END(cyg_httpd_auth_table_e __externC cyg_httpd_auth_table_entry cyg_httpd_auth_table[]; __externC cyg_httpd_auth_table_entry cyg_httpd_auth_table_end[]; -// Variables used for authorization. +// Variables used for authorization. The header parsing code will only copy +// up to AUTH_STORAGE_BUFFER_LENGTH bytes into cyg_httpd_md5_response to +// avoid overflow. +char cyg_httpd_md5_response[AUTH_STORAGE_BUFFER_LENGTH + 1]; +char cyg_httpd_md5_digest[AUTH_STORAGE_BUFFER_LENGTH + 1]; char cyg_httpd_md5_nonce[33]; -char cyg_httpd_md5_digest[33]; -char cyg_httpd_md5_response[33]; char cyg_httpd_md5_cnonce[33]; char cyg_httpd_md5_noncecount[9]; char cyg_httpd_md5_ha2[HASHHEXLEN+1] = {'\0'}; @@ -126,61 +128,7 @@ cyg_httpd_auth_entry_from_domain(char *a return (cyg_httpd_auth_table_entry *)0; } -cyg_int32 -cyg_httpd_base64_encode(char* to, char* from, cyg_uint32 len ) -{ - char *fromp = from; - char *top = to; - char cbyte; - char obyte; - cyg_int8 end[3]; - - for (; len >= 3; len -= 3) - { - cbyte = *fromp++; - *top++ = b64string[(int)(cbyte >> 2)]; - obyte = (cbyte << 4) & 0x30; - - cbyte = *fromp++; - obyte |= (cbyte >> 4); - *top++ = b64string[(cyg_int32)obyte]; - obyte = (cbyte << 2) & 0x3C; - - cbyte = *fromp++; - obyte |= (cbyte >> 6); - *top++ = b64string[(cyg_int32)obyte]; - *top++ = b64string[(cyg_int32)(cbyte & 0x3F)]; - } - - if (len) - { - end[0] = *fromp++; - if (--len ) - end[1] = *fromp++; - else - end[1] = 0; - end[2] = 0; - - cbyte = end[0]; - *top++ = b64string[(cyg_int32)(cbyte >> 2)]; - obyte = (cbyte << 4) & 0x30; - - cbyte = end[1]; - obyte |= (cbyte >> 4); - *top++ = b64string[(cyg_int32)obyte]; - obyte = (cbyte << 2) & 0x3C; - - if (len ) - *top++ = b64string[(cyg_int32)obyte]; - else - *top++ = '='; - *top++ = '='; - } - *top = 0; - return top - to; -} - -cyg_int32 +static cyg_int32 cyg_httpd_base64_decode(char* to, char* from, cyg_uint32 len ) { char *fromp = from; @@ -252,19 +200,9 @@ cyg_httpd_base64_decode(char* to, char* return (top - to) - padding; } -cyg_httpd_auth_table_entry* -cyg_httpd_verify_auth(char* username, char* password) -{ - if ((strcmp(httpstate.needs_auth->auth_username, username) == 0) && - (strcmp(httpstate.needs_auth->auth_password, password) == 0)) - return httpstate.needs_auth; - else - return (cyg_httpd_auth_table_entry*)0; -} - // The following code is a slightly modified version of those available at the // end of RFC1270. -void cyg_httpd_cvthex(HASH Bin, HASHHEX Hex) +static void cyg_httpd_cvthex(HASH Bin, HASHHEX Hex) { unsigned short i; unsigned char j; @@ -286,7 +224,7 @@ void cyg_httpd_cvthex(HASH Bin, HASHHEX }; // Calculate H(A1) as per spec. -void +static void cyg_httpd_digest_calc_HA1( char *pszAlg, char *pszUserName, char *pszRealm, @@ -378,17 +316,17 @@ cyg_httpd_is_authenticated(char* fname) { if (entry->auth_mode == CYG_HTTPD_AUTH_BASIC) { - cyg_httpd_base64_decode(cyg_httpd_md5_response, - cyg_httpd_md5_digest, - strlen(cyg_httpd_md5_digest)); - char *extension = rindex(cyg_httpd_md5_response, ':'); - if (extension == NULL) + cyg_httpd_base64_decode(cyg_httpd_md5_digest, + cyg_httpd_md5_response, + strlen(cyg_httpd_md5_response)); + char *colon = rindex(cyg_httpd_md5_digest, ':'); + if (colon == NULL) { return (httpstate.needs_auth = entry); } else { - *extension = '\0'; // Crypto now has the username. + *colon = '\0'; // Crypto now has the username. // In the case of a 'Basic" authentication, the HTTP header // did not return to us the domain name that we sent when we @@ -396,10 +334,8 @@ cyg_httpd_is_authenticated(char* fname) // are the username:password duo. In this case I will just // compare the entry's username/password to those read from // the header. - if ((strcmp(entry->auth_username, - cyg_httpd_md5_response) != 0) || - (strcmp(entry->auth_password, - ++extension) != 0)) + if ((strcmp(entry->auth_username,cyg_httpd_md5_digest) != 0) || + (strcmp(entry->auth_password, ++colon) != 0)) return (httpstate.needs_auth = entry); } } @@ -456,13 +392,18 @@ cyg_httpd_digest_data(char *dest, char * *dest = '\0'; exit = 1; break; + case ',': + // If it is a comma there might or might not be a blank space + // following it (IE7 inserts no spaces, everyone else does...) + // so before exiting the loop remove any blank space that follows. + if (src[1] == ' ') + src++; case ' ': src++; *dest = '\0'; exit = 1; break; case '"': - case ',': src++; break; default:
--- a/packages/net/athttpd/current/src/cgi.c +++ b/packages/net/athttpd/current/src/cgi.c @@ -165,9 +165,6 @@ int Jim_AioInit(Jim_Interp *); cyg_int32 cyg_httpd_exec_cgi_tcl(char *file_name) { - char tcl_cmd[CYG_HTTPD_MAXPATH]; - sprintf(tcl_cmd, "source %s", file_name); - // Make sure that tcl sees the internal variables including the post_data. cyg_httpd_fvars_table_entry *entry = cyg_httpd_fvars_table; while (entry != cyg_httpd_fvars_table_end) @@ -184,6 +181,8 @@ cyg_httpd_exec_cgi_tcl(char *file_name) "post_data", httpstate.post_data); + char tcl_cmd[CYG_HTTPD_MAXPATH]; + sprintf(tcl_cmd, "source %s", file_name); Jim_Eval(httpstate.jim_interp, tcl_cmd); return 0; } @@ -271,7 +270,7 @@ cyg_httpd_exec_cgi(void) } #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER - if ( strcmp(extension, CYG_HTTPD_DEFAULT_CGIBIN_OBJLOADER_EXTENSION) == 0) + if (strcmp(extension, CYG_HTTPD_DEFAULT_CGIBIN_OBJLOADER_EXTENSION) == 0) { // Load a cgibin via OBJLOADER. cyg_int32 rc = cyg_httpd_exec_cgi_objloader(file_name); @@ -279,7 +278,7 @@ cyg_httpd_exec_cgi(void) } #endif #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL - if ( strcmp(extension, CYG_HTTPD_DEFAULT_CGIBIN_TCL_EXTENSION) == 0) + if (strcmp(extension, CYG_HTTPD_DEFAULT_CGIBIN_TCL_EXTENSION) == 0) { // Load a cgibin via the TCL interpreter. cyg_int32 rc = cyg_httpd_exec_cgi_tcl(file_name);
--- a/packages/net/athttpd/current/src/forms.c +++ b/packages/net/athttpd/current/src/forms.c @@ -45,6 +45,7 @@ * Author(s): Anthony Tonizzo (atonizzo@gmail.com) * Contributors: Sergei Gavrikov (w3sg@SoftHome.net) * Lars Povlsen (lpovlsen@vitesse.com) + * Tad Artis (ecos@ds3switch.com) * Date: 2006-06-12 * Purpose: * Description: @@ -76,11 +77,15 @@ CYG_HAL_TABLE_END(cyg_httpd_fvars_table_ cyg_int8 blank[] = ""; cyg_int8 -cyg_httpd_from_hex (cyg_int8 c) +cyg_httpd_from_hex(cyg_int8 c) { - return c >= '0' && c <= '9' ? c - '0' - : c >= 'A' && c <= 'F'? c - 'A' + 10 - : c - 'a' + 10; + if ((c >= '0') && (c <= '9')) + return (c - '0'); + if ((c >= 'A') && (c <= 'F')) + return (c - 'A' + 10); + if ((c >= 'a') && (c <= 'f')) + return (c - 'a' + 10); + return -1; } char* @@ -109,14 +114,14 @@ cyg_httpd_store_form_variable(char *quer break; case '&': case ' ': + case '\0': // Don't parse past the end of the packet. *q++ = '\0'; return p; default: *q++ = *p++; len++; } - *q = '\0'; - while ((*p != ' ') && (*p != '&')) + while ((*p != ' ') && (*p != '&') && *p) p++; return p; } @@ -137,19 +142,25 @@ cyg_httpd_store_form_data(char *p) entry++; } - if (!p) /* No form data? just return after clearing variables */ + if (!p) // No form data? just return after clearing variables. return NULL; while (*p && *p != ' ') { if (!(p2 = strchr(p, '='))) - return NULL; /* Malformed post? */ + return NULL; // Malformed post? var_length = (cyg_int32)p2 - (cyg_int32)p; entry = cyg_httpd_fvars_table; while (entry != cyg_httpd_fvars_table_end) { - if (!strncmp((const char*)p, entry->name, var_length )) - break; + // Compare both lenght and name. + // If we do not compare the lenght of the variables as well we + // risk the the case where, for instance, the variable name 'foo' + // hits a match with a variable name 'foobar' because the first + // 3 letters of the latter are the same as the former. + if ((strlen(entry->name) == var_length) && + (strncmp((const char*)p, entry->name, var_length ) == 0)) + break; entry++; } @@ -191,11 +202,18 @@ cyg_httpd_find_form_variable(char *p) return (char*)0; } +static inline void release_post_buffer(void) +{ + free(httpstate.post_data); + httpstate.post_data = NULL; + return; +} + void cyg_httpd_handle_method_POST(void) { CYG_ASSERT(httpstate.post_data == NULL, "Leftover content data"); - CYG_ASSERT(httpstate.header_end != NULL, "Cannot see POST data"); + CYG_ASSERT(httpstate.request_end != NULL, "Cannot see POST data"); if (httpstate.content_len == 0 || httpstate.content_len > CYGNUM_ATHTTPD_SERVER_MAX_POST) { cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST); @@ -211,43 +229,43 @@ cyg_httpd_handle_method_POST(void) return; } - /* Grab partial/all content from data read with headers */ - /* - * TODO: This does NOT (yet) support multipart/form-data! - */ - int header_len = (int)httpstate.header_end - (int)httpstate.inbuffer; - int post_data_len = httpstate.inbuffer_len - header_len; + // Grab partial/all content from data read with headers. + int header_len = (int)httpstate.request_end - (int)httpstate.inbuffer; + unsigned int post_data_available = httpstate.inbuffer_len - header_len; + if (httpstate.content_len < post_data_available) + post_data_available = httpstate.content_len; // Some POST data might have come along with the header frame, and the // rest is coming in on following frames. Copy the data that already - // arriced into the post buffer. - memcpy(httpstate.post_data, httpstate.header_end, post_data_len); + // arrived into the post buffer. + memcpy(httpstate.post_data, httpstate.request_end, post_data_available); + httpstate.request_end += post_data_available; + unsigned int total_data_read = post_data_available; + // Do we need additional data? - if (post_data_len < httpstate.content_len) - { - while (post_data_len < httpstate.content_len) + if (total_data_read < httpstate.content_len) + { + while (total_data_read < httpstate.content_len) { - cyg_int32 len = read(httpstate.sockets[httpstate.client_index]. - descriptor, - httpstate.post_data + post_data_len, - httpstate.content_len - post_data_len); - if (len < 0) + // Read only the data that belongs to the POST request. + post_data_available = read( + httpstate.sockets[httpstate.client_index].descriptor, + httpstate.post_data + total_data_read, + httpstate.content_len - total_data_read); + if (post_data_available < 0) { - /* This releases POST data area*/ - free(httpstate.post_data); - httpstate.post_data = NULL; + release_post_buffer(); return; } - post_data_len += len; - } - } - CYG_ASSERT(post_data_len == httpstate.content_len, "Partial read"); - - /* httpstate.content remains available in handler */ - httpstate.post_data[httpstate.content_len] = '\0'; + total_data_read += post_data_available; + } + } - // This assumes that the data that arrived in the POST body is of - // multipart/form-data MIME type. We need to change this, if we are to + // httpstate.content remains available in handler. + httpstate.post_data[total_data_read] = '\0'; + + // The assumption here is that the data that arrived in the POST body is of + // 'multipart/form-data' MIME type. We need to change this if we are to // support things such as HTTP file transfer. if (httpstate.mode & CYG_HTTPD_MODE_FORM_DATA) cyg_httpd_store_form_data(httpstate.post_data); @@ -267,8 +285,7 @@ cyg_httpd_handle_method_POST(void) // Here we'll look for extension to the file. We'll call the cgi // handler only if the extension is '.o'. cyg_httpd_exec_cgi(); - free(httpstate.post_data); - httpstate.post_data = NULL; + release_post_buffer(); return; } #endif @@ -278,16 +295,14 @@ cyg_httpd_handle_method_POST(void) { // A handler was found. We'll call the function associated to it. h(&httpstate); - free(httpstate.post_data); - httpstate.post_data = NULL; + release_post_buffer(); return; } // No handler of any kind for a post request. Must send 404. cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND); - free(httpstate.post_data); - httpstate.post_data = NULL; + release_post_buffer(); return; }
--- a/packages/net/athttpd/current/src/handler.c +++ b/packages/net/athttpd/current/src/handler.c @@ -68,7 +68,7 @@ #include <cyg/athttpd/forms.h> #ifdef CYGOPT_NET_ATHTTPD_USE_DIRLIST -char folder_gif[] = { +static char folder_gif[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x12, 0x00, 0x12, 0x00, 0xd5, 0x00, 0x00, 0xfb, 0xfb, 0xfb, 0xef, 0xef, 0xef, 0xdb, 0xb7, 0x52, 0xcc, 0x99, @@ -118,7 +118,7 @@ char folder_gif[] = { 0x4a, 0x41, 0x00, 0x3b }; -char doc_gif[] = { +static char doc_gif[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x12, 0x00, 0x12, 0x00, 0xe6, 0x00, 0x00, 0xfb, 0xfb, 0xfb, 0xef, 0xef, 0xef, 0xf8, 0xfb, 0xff, 0xed, 0xf6, @@ -194,7 +194,7 @@ char doc_gif[] = { 0xc4, 0xd1, 0x50, 0x20, 0x00, 0x3b }; -char back_gif[] = { +static char back_gif[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x0a, 0x00, 0x09, 0x00, 0xf7, 0x00, 0x00, 0xf8, 0xfa, 0xfb, 0x3a, 0x6b, 0x9d, 0xe5, 0xeb, 0xf2, 0x87, 0xa5,
--- a/packages/net/athttpd/current/src/http.c +++ b/packages/net/athttpd/current/src/http.c @@ -292,9 +292,7 @@ cyg_httpd_parse_date(char *time) &tm_mod.tm_sec); if (rc != 6) { - // asctime() in the stdlibc library. - // The date is in the format: Sun Nov 6 08:49:37 1994 - // and needs to be converted to GMT. + // asctime(). rc = sscanf(time,"%3s %2d %2d:%2d:%2d %4d", month, &tm_mod.tm_mday, @@ -411,17 +409,6 @@ cyg_httpd_send_file(char *name) struct stat sp; char file_name[CYG_HTTPD_MAXPATH]; -#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH - // Let's check that the requested URL is not inside some directory that - // needs authentication. - cyg_httpd_auth_table_entry* auth = cyg_httpd_is_authenticated(name); - if (auth != 0) - { - cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_AUTHORIZED); - return; - } -#endif - strcpy(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR); if (file_name[strlen(file_name)-1] != '/') strcat(file_name, "/"); @@ -458,23 +445,14 @@ cyg_httpd_send_file(char *name) // enough) "Trailing-Slash Redirection". if (name[strlen(name)-1] != '/') { - if (CYGNUM_NET_ATHTTPD_SERVEROPT_PORT == 80) - sprintf(httpstate.url, - "http://%d.%d.%d.%d%s/", - httpstate.host[0], - httpstate.host[1], - httpstate.host[2], - httpstate.host[3], - tmp_url); - else - sprintf(httpstate.url, - "http://%d.%d.%d.%d:%d%s/", - httpstate.host[0], - httpstate.host[1], - httpstate.host[2], - httpstate.host[3], - CYGNUM_NET_ATHTTPD_SERVEROPT_PORT, - tmp_url); + sprintf(httpstate.url, + "http://%d.%d.%d.%d:%d%s/", + httpstate.host[0], + httpstate.host[1], + httpstate.host[2], + httpstate.host[3], + CYGNUM_NET_ATHTTPD_SERVEROPT_PORT, + tmp_url); cyg_httpd_send_error(CYG_HTTPD_STATUS_MOVED_PERMANENTLY); return; } @@ -507,9 +485,9 @@ cyg_httpd_send_file(char *name) else httpstate.status_code = CYG_HTTPD_STATUS_OK; - // Here we'll look for extension to the file. Consider the case where - // there might be more than one dot in the file name. We'll look for - // the last dot, then we'll check the extension. + // Here we'll look for an extension to the file. Consider the case where + // there might be more than one dot in the file name. We'll look for just + // the last one, then we'll check the extension. char *extension = rindex(file_name, '.'); if (extension == NULL) httpstate.mime_type = 0; @@ -522,7 +500,7 @@ cyg_httpd_send_file(char *name) if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) != 0) { #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1 - diag_printf("Sending header only for URL: %s\n", file_name); + diag_printf("Sending header only for URL: %s\n", file_name); #endif send(httpstate.sockets[httpstate.client_index].descriptor, httpstate.outbuffer, @@ -558,7 +536,7 @@ cyg_httpd_send_file(char *name) } err = fclose(fp); - if(err < 0) + if (err < 0) cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR); } #endif @@ -609,14 +587,14 @@ cyg_httpd_format_header(void) else { sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer), - "WWW-Authenticate: Digest realm=\"%s\" ", + "WWW-Authenticate: Digest realm=\"%s\", ", httpstate.needs_auth->auth_domainname); strftime(cyg_httpd_md5_nonce, 33, TIME_FORMAT_RFC1123, gmtime(&time_val)); sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer), - "nonce=\"%s\" ", cyg_httpd_md5_nonce); + "nonce=\"%s\", ", cyg_httpd_md5_nonce); sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer), "opaque=\"%s\", ", CYG_HTTPD_MD5_AUTH_OPAQUE); @@ -708,7 +686,8 @@ cyg_httpd_format_header(void) void cyg_httpd_handle_method_GET(void) { -#if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER) || defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL) +#if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER) ||\ + defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL) // If the URL is a CGI script, there is a different directory... if (httpstate.url[0] == '/' && !strncmp(httpstate.url + 1, @@ -722,7 +701,7 @@ cyg_httpd_handle_method_GET(void) // will likely generate a 404. #endif - // Use defined handlers take precedence over other forms of response. + // User defined handlers take precedence over other forms of response. handler h = cyg_httpd_find_handler(); if (h != 0) { @@ -784,10 +763,21 @@ cyg_httpd_get_URL(char* p) if (*p == '%') { p++; - if (*p) - *dest = cyg_httpd_from_hex(*p++) * 16; - if (*p) - *dest += cyg_httpd_from_hex(*p++); + cyg_int8 ch = cyg_httpd_from_hex(*p++); + if (ch == -1) + { + cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST); + return (char*)0; + } + *dest = ch << 4; + ch = cyg_httpd_from_hex(*p++); + if (ch == -1) + { + cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST); + return (char*)0; + } + *dest += ch; + dest++; } else *dest++ = *p++; @@ -809,7 +799,6 @@ char* cyg_httpd_parse_POST(char* p) { httpstate.method = CYG_HTTPD_METHOD_POST; - httpstate.mode &= ~CYG_HTTPD_MODE_SEND_HEADER_ONLY; char *cp = cyg_httpd_get_URL(p); if (cp == 0) return (char*)0; @@ -849,12 +838,21 @@ cyg_httpd_parse_GET(char* p) char* cyg_httpd_process_header(char *p) { +#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH + // Clear the previous request's response. The client properly authenticated + // will always reinitialize this variable during the header parsing + // process. This variable is also commandeered to hold the hashed + // username:password duo in the basic authentication. + cyg_httpd_md5_response[0] = '\0'; +#endif + // The deafult for HTTP 1.1 is keep-alive connections, unless specifically // closed by the far end. - httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA); + httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA |\ + CYG_HTTPD_MODE_SEND_HEADER_ONLY); httpstate.modified_since = -1; httpstate.content_len = 0; - while ((*p != '\r') && (*p != '\n') & (*p != '\0')) + while (p < httpstate.request_end) { if (strncasecmp("GET ", p, 4) == 0) { @@ -931,44 +929,56 @@ cyg_httpd_process_header(char *p) p++; if (strncasecmp("Basic", p, 5) == 0) { - char *cr = cyg_httpd_md5_digest; p += 5; while (*p == ' ') p++; - while ((*p != '\r') && (*p != '\n') && (*p != ' ')) - *cr++ = *p++; - *cr = '\0'; + cyg_int32 auth_data_length = 0; + while (*p != '\n') + { + // We are going to copy only up to + // AUTH_STORAGE_BUFFER_LENGTH characters to prevent + // overflow of the cyg_httpd_md5_response variable. + if (auth_data_length < AUTH_STORAGE_BUFFER_LENGTH) + if ((*p != '\r') && (*p != ' ')) + cyg_httpd_md5_response[auth_data_length++] = *p; + p++; + } + p++; + cyg_httpd_md5_response[auth_data_length] = '\0'; } else if (strncasecmp(p, "Digest", 6) == 0) { - p += 6; - while (*p == ' ') - p++; - while ((*p != '\r') && (*p != '\n')) - { - if (strncasecmp(p, "realm=", 6) == 0) + p += 6; + while (*p == ' ') + p++; + while (*p != '\n') + { + if (strncasecmp(p, "realm=", 6) == 0) p = cyg_httpd_digest_skip(p + 6); - else if (strncasecmp(p, "username=", 9) == 0) + else if (strncasecmp(p, "username=", 9) == 0) p = cyg_httpd_digest_skip(p + 9); else if (strncasecmp(p, "nonce=", 6) == 0) p = cyg_httpd_digest_skip(p + 6); - else if (strncasecmp(p, "response=", 9) == 0) + else if (strncasecmp(p, "response=", 9) == 0) p = cyg_httpd_digest_data(cyg_httpd_md5_response, p + 9); - else if (strncasecmp(p, "cnonce=", 7) == 0) + else if (strncasecmp(p, "cnonce=", 7) == 0) p = cyg_httpd_digest_data(cyg_httpd_md5_cnonce, p + 7); - else if (strncasecmp(p, "qop=", 4) == 0) + else if (strncasecmp(p, "qop=", 4) == 0) p = cyg_httpd_digest_skip(p + 4); - else if (strncasecmp(p, "nc=", 3) == 0) + else if (strncasecmp(p, "nc=", 3) == 0) p = cyg_httpd_digest_data(cyg_httpd_md5_noncecount, p + 3); - else if (strncasecmp(p, "algorithm=", 10) == 0) + else if (strncasecmp(p, "algorithm=", 10) == 0) p = cyg_httpd_digest_skip(p + 10); else if (strncasecmp(p, "opaque=", 7) == 0) p = cyg_httpd_digest_skip(p + 7); else if (strncasecmp(p, "uri=", 4) == 0) p = cyg_httpd_digest_skip(p + 4); + else + p++; } + p++; } else while (*p++ != '\n'); @@ -987,36 +997,6 @@ cyg_httpd_process_header(char *p) // We'll just dump the rest of the line and move on to the next. while (*p++ != '\n'); } - - if (*p == '\0') - { - // This is the case of a header that is split in two or more frames. - // We cannot process it right away and will have to wait for the rest - // of the data. This has _major_ implications because we implicitly - // and tacitly assume that the next frame that will be handled by the - // server is the continuation of this one. But if the next frame is - // for instance, a request from another client, we are in trouble - // since the new request will be processed and this request will - // be dropped. - // Caveat: This is all untested. While theoretically this is possible, - // as much as I tried, I could not coerce any of the popular browser - // to split a header in multiple frames. -#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1 - diag_printf("Split header found.\r\n"); -#endif - return 0; - } - - // If this is the end of this request, but there might be other queued up - // because of pipelining of two requests in a single frame. This while() - // will get rid of the \r\n that terminates the header section of a - // request. - while (*p++ != '\n'); - - // In the case of large POST the payload comes with the header (and - // possibly further frames.) Here is where we mark the start of the - // POST data. - httpstate.header_end = p; return p; } @@ -1030,12 +1010,24 @@ cyg_httpd_process_method(void) // the leading returns and line carriages we find. while ((*p == '\r') || (*p =='\n')) p++; + while (*p != '\0') { p = cyg_httpd_process_header(p); if (p == 0) return; - + +#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH + // Let's check that the requested URL is not inside some directory that + // needs authentication. + cyg_httpd_auth_table_entry* auth = + cyg_httpd_is_authenticated(httpstate.url); + if (auth != 0) + { + cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_AUTHORIZED); + return; + } +#endif switch (httpstate.method) { case CYG_HTTPD_METHOD_GET:
--- a/packages/net/athttpd/current/src/jim-aio.c +++ b/packages/net/athttpd/current/src/jim-aio.c @@ -137,7 +137,7 @@ static int JimAioHandlerCommand(Jim_Inte buf[AIO_BUF_LEN-1] = '_'; if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL) break; - if (buf[AIO_BUF_LEN-1] == '\0' && buf[AIO_BUF_LEN] == '\n') + if (buf[AIO_BUF_LEN-1] == '\0' && buf[AIO_BUF_LEN-2] == '\n') more = 1; if (more) { Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN-1);
--- a/packages/net/athttpd/current/src/socket.c +++ b/packages/net/athttpd/current/src/socket.c @@ -71,7 +71,7 @@ #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #define CYG_HTTPD_DAEMON_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_MINIMUM + \ CYGNUM_NET_ATHTTPD_THREADOPT_STACKSIZE) -static cyg_int32 cyg_httpd_initialized = 0; +static cyg_int32 cyg_httpd_initialized = 0; cyg_thread cyg_httpd_thread_object; cyg_handle_t cyg_httpd_thread_handle; cyg_uint8 cyg_httpd_thread_stack[CYG_HTTPD_DAEMON_STACK_SIZE] @@ -108,9 +108,11 @@ cyg_httpd_writev(cyg_iovec *iovec_bufs, return sent; } -// The need for chunked transfers arises from the fact that with dinamic -// pages it is not always possible to know the packet size upfront, and thus -// it is not possible to fill the 'Content-Length:' field in the header. +// The need for chunked transfers arises from the fact that with persistent +// connections it is not always easy to tell when a packet end. Also, with +// dynamic pages it is not always possible to know the packet size upfront, +// and thus the value of the 'Content-Length:' field in the header is not +// known upfront. // Today's web browser use 'Content-Length:' when present in the header and // when not present they read everything that comes in up to the last 2 \r\n // and then figure it out. The HTTP standard _mandates_ 'Content-Length:' to @@ -121,8 +123,7 @@ cyg_httpd_writev(cyg_iovec *iovec_bufs, // ----------------------------------------------------------------------------- // cyg_httpd_start_chunked("html"); // sprintf(phttpstate->payload, ...); -// cyg_httpd_write_chunked(phttpstate->payload, -// strlen(phttpstate->payload)); +// cyg_httpd_write_chunked(phttpstate->payload, strlen(phttpstate->payload)); // ... // cyg_httpd_end_chunked(); // ----------------------------------------------------------------------------- @@ -132,7 +133,7 @@ cyg_httpd_start_chunked(char *extension) httpstate.status_code = CYG_HTTPD_STATUS_OK; #if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS) - // I am not really sure that this is necessary, but even if it isn't, the + // I am not really sure that this is necessary, but even if it isn't, the // added overhead is not such a big deal. In simple terms, I am not sure // how much I can rely on the client to understand that the frame has ended // with the last 5 bytes sent out. In an ideal world, the data '0\r\n\r\n' @@ -147,7 +148,7 @@ cyg_httpd_start_chunked(char *extension) #endif // We do not cache chunked frames. In case they are used to display dynamic - // data we want them to be executed any every time they are requested. + // data we want them to be executed every time they are requested. httpstate.mode |= (CYG_HTTPD_MODE_TRANSFER_CHUNKED | CYG_HTTPD_MODE_NO_CACHE); @@ -160,30 +161,28 @@ cyg_httpd_start_chunked(char *extension) ssize_t cyg_httpd_write_chunked(char* buf, int len) { - char leader[16], trailer[] = {'\r', '\n'}; + if (len == 0) + return 0; - cyg_iovec iovec_bufs[] = { {leader, 0}, {buf, 0}, {trailer, 2} }; - sprintf(leader, "%x\r\n", len); - iovec_bufs[0].iov_len = strlen(leader); - iovec_bufs[1].iov_len = len; - iovec_bufs[2].iov_len = 2; + char leader[16], trailer[] = {'\r', '\n'}; + cyg_iovec iovec_bufs[] = { {leader, 0}, {buf, len}, {trailer, 2} }; + iovec_bufs[0].iov_len = sprintf(leader, "%x\r\n", len); if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) - return (iovec_bufs[0].iov_len + iovec_bufs[1].iov_len + - iovec_bufs[2].iov_len); + return (iovec_bufs[0].iov_len + len + 2); return cyg_httpd_writev(iovec_bufs, 3); } void cyg_httpd_end_chunked(void) { - if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) + httpstate.mode &= ~CYG_HTTPD_MODE_TRANSFER_CHUNKED; + if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) != 0) return; strcpy(httpstate.outbuffer, "0\r\n\r\n"); cyg_httpd_write(httpstate.outbuffer, 5); - httpstate.mode &= ~CYG_HTTPD_MODE_TRANSFER_CHUNKED; } -// This function builds and send out a standard header. It is likely going to +// This function builds and sends out a standard header. It is likely going to // be used by a c language callback function, and thus followed by one or // more calls to cyg_httpd_write(). Unlike cyg_httpd_start_chunked(), this // call requires prior knowledge of the final size of the frame (browsers @@ -212,7 +211,7 @@ cyg_httpd_process_request(cyg_int32 inde { httpstate.client_index = index; cyg_int32 descr = httpstate.sockets[index].descriptor; - + // By placing a terminating '\0' not only we have a safe stopper point // for our parsing, but also we can detect if we have a split header. // Since headers always end with an extra '\r\n', if we find a '\0' @@ -220,9 +219,12 @@ cyg_httpd_process_request(cyg_int32 inde // not been received completely and more is following (i.e. split headers.) httpstate.inbuffer[0] = '\0'; httpstate.inbuffer_len = 0; - while ((strstr(httpstate.inbuffer, "\r\n\r\n") == 0) && - (strstr(httpstate.inbuffer, "\n\n") == 0)) - { + + cyg_bool done = false; + do + { + // At this point we know we have data pending because the corresponding + // bit in the fd_set structure was set. int len = recv(descr, httpstate.inbuffer + httpstate.inbuffer_len, CYG_HTTPD_MAXINBUFFER - httpstate.inbuffer_len, @@ -255,30 +257,65 @@ cyg_httpd_process_request(cyg_int32 inde #endif return; } - + + httpstate.inbuffer[httpstate.inbuffer_len + len] = '\0'; + + // It is always possible to receive split headers, in which case a + // header is only partially sent on one packet, with the rest on + // following packets. We can tell when a full packet is in the buffer + // by scanning for a header terminator ('\r\n\r\n'). Be smart and + // scan only the data received in the last read() operation, and not + // the full buffer each time. + httpstate.request_end = + strstr(&httpstate.inbuffer[httpstate.inbuffer_len], "\r\n\r\n"); httpstate.inbuffer_len += len; - httpstate.inbuffer[httpstate.inbuffer_len] = '\0'; - } - - httpstate.inbuffer[httpstate.inbuffer_len] = '\0'; - // Timestamp the socket. - httpstate.sockets[index].timestamp = time(NULL); - - // This is where it all happens. - cyg_httpd_process_method(); - - if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN) - // There are 2 cases we can be here: - // 1) chunked frames close their connection by default - // 2) The client requested the connection be terminated with a - // "Connection: close" in the header - // In any case, we close the TX pipe and wait for the client to - // send us an EOF on the receive pipe. This is a more graceful way - // to handle the closing of the socket, compared to just calling - // close() without first asking the opinion of the client, and - // running the risk of stray data lingering around. - shutdown(descr, SHUT_WR); + // Go through all the requests that were received in this packet. + while (httpstate.request_end != 0) + { + httpstate.request_end += 4; // Include the terminator. + + // Timestamp the socket. + httpstate.sockets[index].timestamp = time(NULL); + + // This is where it all happens. + cyg_httpd_process_method(); + + if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN) + { + // There are 2 cases we can be here: + // 1) chunked frames close their connection by default + // 2) The client requested the connection be terminated with a + // "Connection: close" in the header + // In any case, we close the TX pipe and wait for the client to + // send us an EOF on the receive pipe. This is a more graceful + // way to handle the closing of the socket, compared to just + // calling close() without first asking the opinion of the + // client, and running the risk of stray data lingering + // around. + shutdown(descr, SHUT_WR); + } + + // Move back the next request (if any) to the beginning of inbuffer. + // This way we avoid inching towards the end of inbuffer with + // consecutive requests. + strcpy(httpstate.inbuffer, httpstate.request_end); + httpstate.inbuffer_len -= (int)(httpstate.request_end - + httpstate.inbuffer); + + // If there is no data left over we are done processing all + // requests. + if (httpstate.inbuffer_len == 0) + { + done = true; + break; + } + + // Any other fully formed request pending? + httpstate.request_end = strstr(httpstate.inbuffer, "\r\n\r\n"); + } + } + while (done == false); } void @@ -395,7 +432,7 @@ cyg_httpd_daemon(cyg_addrword_t data) diag_printf("Web server Started and listening...\n"); #endif cyg_int32 i; - for (i = 0; i < CYGNUM_FILEIO_NFILE; i++) + for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++) { httpstate.sockets[i].descriptor = 0; httpstate.sockets[i].timestamp = (time_t)0;
