This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

atHTTPD new patch


Belated patch to atHTTPD after much coaching from
Tad, Rene, Danny and Oyvind. Lots of work went into
improving the collection of requests and making
authentication work right. The chengelog says it all.

Anthony Tonizzo
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/eCos.hhc /home/atonizzo/ecos/devo_athttpd/eCos.hhc
--- /home/atonizzo/ecos/clean/eCos.hhc	1969-12-31 16:00:00.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/eCos.hhc	2007-11-26 10:31:54.000000000 -0800
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<UL>
+</UL>
+</BODY></HTML>
\ No newline at end of file
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/eCos.hhp /home/atonizzo/ecos/devo_athttpd/eCos.hhp
--- /home/atonizzo/ecos/clean/eCos.hhp	1969-12-31 16:00:00.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/eCos.hhp	2007-11-26 10:31:54.000000000 -0800
@@ -0,0 +1,19 @@
+[OPTIONS]
+Auto Index=Yes
+Binary Index=No
+Compatibility=1.1 or later
+Compiled file=eCos.chm
+Contents file=eCos.hhc
+Default Window=mainwin
+Default topic=/home/atonizzo/ecos/devo_athttpd/doc/index.html
+Display compile progress=Yes
+Full-text search=Yes
+Language=0x409 English (United States)
+Title=eCos
+[WINDOWS]
+mainwin="eCos Documentation","eCos.hhc",,,"index.html","http://sources.redhat.com/ecos/","Net Release","http://www.redhat.com/products/ecos/","eCos Product",0x40060420,,0xc287e,[0,0,762,400],,,,,,,0
+
+[FILES]
+index.html
+
+[INFOTYPES]
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/ChangeLog /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/ChangeLog
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/ChangeLog	2007-11-14 06:39:13.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/ChangeLog	2007-12-10 08:29:18.000000000 -0800
@@ -1,5 +1,63 @@
+2007-12-02  Danny Sade <danny@channelot.com>
+
+	* src/socket.c: Added a extra check for end of header in case of POST
+	requests. In the original code the payload following the POST header could
+	be confused with the start of a new request.
+
+2007-12-02  Danny Sade <danny@channelot.com> and Anthony Tonizzo <atonizzo@gmail.com>
+
+	* src/socket.c src/httpd.c: 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. Corrected a bug in the 
+	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. The cyg_httpd_process_request() now loops as many times
+	as the number of full requests received. Modified the function 
+	cyg_httpd_end_chunked() so that the flag CYG_HTTPD_MODE_TRANSFER_CHUNKED
+	is always cleared after it terminates. Renamed header_end to request_end
+	to better reflect its real use.
+	* doc/athttpd.sgml: Included Digest (MD5) authentication as fully supported.
+
+2007-11-28  Rene' Nielsen <rbn@vitesse.com> and Anthony Tonizzo <atonizzo@gmail.com>
+
+	* cdl/httpd.cdl: Updated the cdl to reflect the fact that MD5 authentication
+	is now a fully tested feature (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.
+    * src/auth.c: Removed dead code and made static a bunch of functions.
+	* 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>
+
+	* 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.
+	* 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.
+
 2007-11-12  Oyvind Harboe  <oyvind.harboe@zylin.com>
 2007-11-12  Jonathan Larmour  <jifl@eCosCentric.com>
 
 	* doc/athttpd.sgml: added an example of a tcl script.
 	* src/http.c, forms.c: serve cgi requests before file system requests,
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/doc/athttpd.sgml /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/doc/athttpd.sgml
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/doc/athttpd.sgml	2007-11-14 06:39:13.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/doc/athttpd.sgml	2007-12-04 13:58:25.000000000 -0800
@@ -48,11 +48,11 @@
   <listitem><para>File system Access</para></listitem>
   <listitem><para>Callbacks to C functions</para></listitem>
   <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>
 
 <para>
@@ -335,11 +335,14 @@
 form variable called foo, and during the GET request we are defining foo
 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>
 
 <sect3 id="athttpd-start-chunked">
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/auth.h /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/include/auth.h
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/auth.h	2006-07-18 09:37:24.000000000 -0700
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/include/auth.h	2007-11-26 10:11:49.000000000 -0800
@@ -67,10 +67,14 @@
 {
     CYG_HTTPD_AUTH_BASIC = 0, 
     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. 
 // Lets' make an example. Let's say that the files of your web site reside in 
 //  the directory '/fs/jffs2/html'. Your CYG_HTTPD_DEFAULT_PATH will likely be
@@ -92,15 +96,12 @@
 typedef struct cyg_httpd_auth_table_entry cyg_httpd_auth_table_entry;
 #define CYG_HTTPD_AUTH_TABLE_ENTRY( __name, __path, __domain, __un, __pw, __mode )  \
  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 *);
 
 // The following code is a slightly modified version of those available at the
@@ -121,26 +122,6 @@
 extern char cyg_httpd_md5_response[];
 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__
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/http.h /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/include/http.h
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/http.h	2006-11-27 07:41:56.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/include/http.h	2007-12-05 10:15:12.000000000 -0800
@@ -152,26 +152,32 @@
     cyg_uint16   status_code;
     char        *mime_type;
     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;
     
 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
     Jim_Interp *jim_interp;
 #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
     //  handlers and cgi scripts.
     char        *post_data;
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/auth.c /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/auth.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/auth.c	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/auth.c	2007-11-26 10:16:38.000000000 -0800
@@ -72,14 +72,16 @@
 CYG_HAL_TABLE_END(cyg_httpd_auth_table_end, httpd_auth_table );
 
 __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'};
 char cyg_httpd_md5_ha1[HASHHEXLEN+1];
 
@@ -124,65 +126,11 @@
     }
             
     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;
     char     *top = to;
     char     *p;
@@ -250,23 +198,13 @@
     if (len)
         return -1;
     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;
 
     for (i = 0; i < HASHLEN; i++)
@@ -284,11 +222,11 @@
     };
     Hex[HASHHEXLEN] = '\0';
 };
 
 // Calculate H(A1) as per spec.
-void
+static void
 cyg_httpd_digest_calc_HA1( char    *pszAlg,
                            char    *pszUserName,
                            char    *pszRealm,
                            char    *pszPassword,
                            char    *pszNonce,
@@ -376,32 +314,30 @@
                                 cyg_httpd_auth_entry_from_path(fname);
     if (entry != 0)
     {
         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
                 //  challenged the request: The only things that are returned 
                 //  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);
             }    
         }
         else
         {
@@ -454,17 +390,22 @@
         case '\r':
         case '\n':
             *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:
             *dest++ = *src++;
         }    
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/cgi.c /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/cgi.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/cgi.c	2006-11-27 07:41:56.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/cgi.c	2007-12-04 13:59:32.000000000 -0800
@@ -163,13 +163,10 @@
 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
 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)
     {
         if (strlen(entry->buf) != 0)
@@ -182,10 +179,12 @@
     if (httpstate.post_data != NULL)
         Jim_SetVariableStrWithStr(httpstate.jim_interp, 
                                   "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;
 }
 
 int
@@ -269,19 +268,19 @@
         cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
         return 0;
     }    
 
 #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);
         return rc;
     }    
 #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);
         return rc;
     }    
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/forms.c /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/forms.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/forms.c	2007-11-14 06:39:13.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/forms.c	2007-12-07 09:00:16.000000000 -0800
@@ -43,10 +43,11 @@
  * #####DESCRIPTIONBEGIN####
  * 
  *  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:  
  *               
  * ####DESCRIPTIONEND####
@@ -74,15 +75,19 @@
 CYG_HAL_TABLE_END(cyg_httpd_fvars_table_end, 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*
 cyg_httpd_store_form_variable(char *query, cyg_httpd_fvars_table_entry *entry)
 {
@@ -107,18 +112,18 @@
             p++;
             len++;
             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;
 } 
 
 // We'll try to parse the data from the form, and store it in the variables
@@ -135,23 +140,29 @@
     {
         entry->buf[0] = '\0';
         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++;
         }
                 
         if (entry == cyg_httpd_fvars_table_end)
         {
@@ -189,15 +200,22 @@
     }
             
     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);
         return;
     }
@@ -209,47 +227,47 @@
     {
         cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
         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);
 
 #if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER) || \
@@ -265,30 +283,27 @@
                               strlen(CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR)))
     {                              
         // 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    
     
     handler h = cyg_httpd_find_handler();
     if (h != 0)
     {
         // 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;
 }
 
 
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/handler.c /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/handler.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/handler.c	2006-11-27 07:41:56.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/handler.c	2007-12-04 13:45:35.000000000 -0800
@@ -66,11 +66,11 @@
 #include <cyg/athttpd/socket.h>
 #include <cyg/athttpd/handler.h>
 #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,
     0x34, 0xe4, 0xe4, 0xe4, 0xc1, 0xc1, 0xc1, 0xa3,
     0x71, 0x0b, 0xc2, 0x8f, 0x2a, 0xb7, 0xb7, 0xb7,
@@ -116,11 +116,11 @@
     0x05, 0x2b, 0xc9, 0xca, 0xcb, 0x05, 0x04, 0x44,
     0x00, 0x01, 0x26, 0xd2, 0xd3, 0xd4, 0x52, 0x4b,
     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,
     0xff, 0xc1, 0xc1, 0xc1, 0xe4, 0xe4, 0xe4, 0xfd,
     0xfd, 0xfd, 0xd8, 0xeb, 0xff, 0xc0, 0xdf, 0xff,
@@ -192,11 +192,11 @@
     0x87, 0xc3, 0x87, 0x0e, 0x09, 0x14, 0x30, 0x04,
     0x20, 0x80, 0x8e, 0x8b, 0x18, 0x2f, 0x76, 0x42,
     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,
     0xc3, 0x9d, 0xb5, 0xce, 0xd0, 0xdc, 0xe7, 0xa9,
     0xbe, 0xd4, 0x74, 0x97, 0xb9, 0x65, 0x8c, 0xb2,
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/http.c /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/http.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/http.c	2007-11-14 06:39:13.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/http.c	2007-12-05 16:07:22.000000000 -0800
@@ -290,13 +290,11 @@
                     &tm_mod.tm_hour,
                     &tm_mod.tm_min,
                     &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,
                         &tm_mod.tm_hour,
                         &tm_mod.tm_min,
@@ -409,21 +407,10 @@
     cyg_int32  err;
     FILE      *fp;
     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, "/");
     strcat(file_name, name);
     cyg_httpd_cleanup_filename(file_name);
@@ -456,27 +443,18 @@
         // Directories need a trialing slash, and if missing, we'll redirect
         //  the client to the right URL. This is called (appropriately
         //  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;
         }
 
         // We are going to try to locate an index page in the directory we got
@@ -505,13 +483,13 @@
         return;
     }    
     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;
     else    
         httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
@@ -520,11 +498,11 @@
     httpstate.mode &= ~CYG_HTTPD_MODE_NO_CACHE;
     cyg_int32 payload_size = cyg_httpd_format_header();
     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, 
              payload_size,
              0);
@@ -556,11 +534,11 @@
         bread = fread(httpstate.outbuffer, 1, CYG_HTTPD_MAXOUTBUFFER, fp);
         bytes_written += cyg_httpd_write(httpstate.outbuffer, bread);
     }    
     
     err = fclose(fp);
-    if(err < 0)
+    if (err < 0)
         cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
 }
 #endif
 
 cyg_int32
@@ -607,18 +585,18 @@
                     httpstate.needs_auth->auth_domainname);
         }
         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);
             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                     "stale=false, algorithm=%s, qop=\"%s\"\r\n",
@@ -706,11 +684,12 @@
 }
 
 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, 
                               CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR, 
                               strlen(CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR)))
@@ -720,11 +699,11 @@
     }
     // If the OBJLOADER package is not loaded, then the request for a library
     //  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)
     {
         h(&httpstate);
         return;
@@ -782,14 +761,25 @@
     {
         // Look for encoded characters in the URL.
         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++;
     }
 
@@ -807,11 +797,10 @@
 
 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;
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
     diag_printf("POST Request URL: %s\n", httpstate.url);
@@ -847,16 +836,25 @@
 }
 
 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)
         {
             // We need separate flags for HEAD and SEND_HEADERS_ONLY since
             //  we can send a header only even in the case of a GET request
@@ -929,26 +927,35 @@
             p += 14;
             while (*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)
                         p = cyg_httpd_digest_skip(p + 9);
                     else if (strncasecmp(p, "nonce=", 6) == 0)
                         p = cyg_httpd_digest_skip(p + 6);
@@ -966,11 +973,14 @@
                         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');
         }   
 #endif // CYGOPT_NET_ATHTTPD_USE_AUTH
@@ -985,40 +995,10 @@
         }
         else
             // 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;
 }
 
 void
 cyg_httpd_process_method(void)
@@ -1028,16 +1008,28 @@
     // Some browsers send an extra '\r\n' after the POST data that is not
     //  accounted in the "Content-Length:" field. We are going to junk all
     //  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:
             case CYG_HTTPD_METHOD_HEAD:
                 cyg_httpd_handle_method_GET();
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/socket.c /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/socket.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/socket.c	2006-11-27 07:41:56.000000000 -0800
+++ /home/atonizzo/ecos/devo_athttpd/packages/net/athttpd/current/src/socket.c	2007-12-06 10:46:40.000000000 -0800
@@ -69,11 +69,11 @@
 #include <cyg/athttpd/cgi.h>
 
 #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]     
                                        __attribute__((__aligned__ (16)));
 CYG_HTTPD_STATE httpstate;
@@ -106,35 +106,36 @@
                     buf_len);
 #endif    
     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
 //  be present in the header with a correct value, and whenever that is not
 //  possible, chunked transfers must be used.
 //
 // A chunked transer takes the form of:
 // -----------------------------------------------------------------------------
 //    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();
 // -----------------------------------------------------------------------------
 ssize_t
 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'
     //  should be enough, but several posting on the subject I read seem to
     //  imply otherwise, at least with early generation browsers that supported
@@ -145,11 +146,11 @@
     //  requires it.
     httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
 #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);
     
     httpstate.last_modified = -1;
     httpstate.mime_type = cyg_httpd_find_mime_string(extension);
@@ -158,34 +159,33 @@
 }
 
 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)
-        return;
-    strcpy(httpstate.outbuffer, "0\r\n\r\n");
-    cyg_httpd_write(httpstate.outbuffer, 5);
+    if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) == 0)
+    {
+        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
 //  _will_trust_ the "Content-Length:" field when present!), and the user 
 //  is expected to make sure that the total number of bytes (octets) sent out
@@ -210,20 +210,21 @@
 void
 cyg_httpd_process_request(cyg_int32 index)
 {
     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'
     //  before the terminator than we can safely assume that the header has
     //  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
     {            
         int len = recv(descr,
                        httpstate.inbuffer + httpstate.inbuffer_len,
                        CYG_HTTPD_MAXINBUFFER - httpstate.inbuffer_len,
                        0);
@@ -253,33 +254,68 @@
             diag_printf("ERROR reading from socket. read() returned: %d\n", 
                         httpstate.inbuffer_len);
 #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 just received 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';
 
-    // 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
 cyg_httpd_handle_new_connection(cyg_int32 listener)
 {
@@ -392,11 +428,11 @@
 
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
     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;
     }
     

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]