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;