History

This section covers the history of GoAhead Webserver and information about previous releases.
Currently the GoAhead web server is in bugfix mode no new features are planned.

Comparison - see this page for a quick assessment of capabilities compared to other embedded and non-embedded web servers.

2.1.8

This is the latest version released on 2003/12/8.

Requirements

This is a list of requirements needed to build and run the webserver.

Operating system

By default the webserver can be built on the following platforms without much problems (build files included):

  • Windows CE
  • Windows
  • eCos
  • Linux
  • VxWorks
  • MacOSX
  • Lynx
  • NetWare
  • QNX

Altough the package does not include makefiles for these platforms it's easy to port to:

  • Solaris
  • HP-UX

Compiler

You will need one if you want to compile the webserver from sources. In many cases you will want to customize the server to fit your needs so a compiler is needed.
Depending on the used platform the options are:

Commercial Name Notes
No GCC Shiped with several Linux platforms
No Borland C command line Windows builds
Yes Borland C++ Builder Suite
Yes Microsoft Visual Studio Windows and WindowsCE development

Footprint

The webserver will run on environments with limited memory. Depending on the platform used, the binary (without the html content) is aproximatively 60-120 KB.

The value depends on:

  1. the code that gets compiled in. Some code can be disabled with compile time flags:
    • __NO_CGI_BIN to disable CGI.
    • __NO_EJ_FILE disables Embedded Javascript.
  2. static/dynamic linking, using shared libraries will result in a much smaller binary.
    In case of static linking try to build against uClibc as it will generate a much smaller binary.
  3. symbol information. Don't forget to strip this out since it's not needed in release builds.

A table with compiled binary size on various environments:

OS Footprint
size (KB)
Notes
WindowsCE 52
WindowsCE 65 with WEBS_IF_MODIFIED_SUPPORT=1 enabled
Linux 94 dynamic build, without symbol information
VxWorks 120 without symbol information
Linux 300 dynamic build, with symbol information
Linux 750 static build, without symbol information, SSL enabled
Linux 1100 static build, with symbol information, SSL enabled

:!: The table does not include the memory requirements needed to run the webserver.
The webserver uses it's own memory allocator thus will alloc and cache memory. The memory required to run the webserver may be more than +1 MB of RAM (depends on the type of request used and number of connections).

Documentation

Most of the documentation comes with the install package downloaded from the GoAhead website. After compilling and running the webserver you will be able to access the html pages that include the documentation.

Windows CHM format

Compiled by Raj, posted on 2004/01/06

Download it here
Searchable format used by Windows help, includes the documentation that comes with version 2.1.8 of the GoAhead webserver.
The main advantage is that you don't have to compile and run the webserver in order to read the documentation.

Patches for 2.1.8

This is a list of known issues still present with the latest webserver release 2.1.8. that have been solved by the developer community.

IMPORTANT:

  1. all diff's should be posted against the latest revision. The unified diff format (-u) preferred.
  2. not all the changes were tested properly. Look up the newsgroup for detailed information.
  3. if you apply any of these patches please append your feeback/experiences here.
  4. if any of this issues is in fact solved in release 2.1.8 please report it.

2005/10/28 - Carlitos
Consolidated into asingle patch the following fixes:

Infinite loop

Fix by Simon Byholm, 2004/06/24

Sending the web server an invalid request that contains no newline character (\n) causes the web server to get into an infinite loop and to consume 100% CPU usage (at least on Windows).
Removing the #ifdef HP_FIX will terminate the session as soon as there is an error (no data available). This would solve the problem, if data stops to come the session is ended, but if there is a small delay between two packets in the middle of a request it will discard the request. This might discard some longer POST or PUT requests on a slow connection.

It's the assumption in socketInputBuffered that if there is data in the lineBuf the socket is marked as readable in socketSelect() line 822. This is not a correct assumption because the lineBuf is never filled with more than one line at a time and it is immideatly cleared when the line is returned. There will never be a full line waiting in the linebuf.

Code that uses socketInputBuffered() and gets it all wrong:

            if (readFds[index] & bit || socketInputBuffered(sid) > 0) {
                sp->currentEvents |= SOCKET_READABLE;
            }

FIX: Change in sock.c:365 socketInputBuffered() function this line:

         if (socketEof(sid)) {
                return -1;
         }
-        return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf);
+        return ringqLen(&sp->inBuf);

Bad POST causes segfault

Fix by Fabien R, 2001/07/22

I discovered that if you set Content-Length to 0 in a POST invoking formTest(), webs will GP fault.
The problem lies in webs.c:websReadEvent(webs_t wp):
In the switch() statement, within case WEBS_POST_CLEN: there is a if(text) statement missing.

/*
 *     Special case where the POST request also had query data
 *     specified in the URL, ie. url?query_data. In this case
 *     the URL query data is separated by a '&' from the posted
 *     query data.
 */
       len = gstrlen(wp->query);
 
       if(text) /*** WAS MISSING ***/
       {
          wp->query = (char_t*) brealloc(B_L, wp->query, (len + gstrlen(text) + 2) * sizeof(char_t));
          wp->query[len++] = '&';
          gstrcpy(&wp->query[len], text);
       }

Segfault in SSL builds

Fix by Joakim Tjernlund, 2004/3/26

The problem is caused by websSSLGets don't null terminate strings with len=256 (BUF_BLOCK len long). This leads to bQhead corruption and later SEGV.

The solution to the problem:

--- websSSL.c 6 May 2003 14:01:09 -0000 1.5
+++ websSSL.c 26 Mar 2004 09:27:21 -0000 1.6
@@ -674,25 +674,26 @@
    }
   }
 /*
- *  If a newline is seen, return the data excluding the new line to the
- *  caller. If carriage return is seen, just eat it.
+ *  If carriage return is seen, just eat it.
  */
-  if (c == '\n') {
-   if ((len > 0) && (len < lenBuf)) {
-    (*buf)[len] = 0;
-   }
-   return len;
-  } else if (c == '\r') {
+  if (c == '\r')
    continue;
-  }
-/*
- *  Append character to buf
- */
+
   if (len >= lenBuf) {
    lenBuf += BUF_BLOCK;
    *buf = brealloc(B_L, *buf, lenBuf);
   }
-
+/*
+ *  If a newline is seen, return the data excluding the new line to the
+ *  caller.
+ */
+  if (c == '\n') {
+    (*buf)[len] = 0;
+    return len;
+  }
+/*
+ *  Append character to buf
+ */
   a_assert(*buf);
   (*buf)[len] = c;
   len++;

Incorect fd_mask size

Fix by Petchesi Gabriel Horatiu,
Verified by Jourdon Shell (2004/11/06), David Tiktin

Affects the eCos platform but noticed on other OS's and architectures (64 bit builds of HP-UX and Solaris).
The solution is to modify in sockGen.c: socketSelect() function this way:

@@ -737,7 +737,7 @@
  *     Allocate and zero the select masks
  */
        nwords = (socketHighestFd + NFDBITS) / NFDBITS;
-       len = nwords * sizeof(int);
+       len = nwords * sizeof(fd_mask);
 
        readFds = balloc(B_L, len);
        memset(readFds, 0, len);

This change is needed whenever:

    sizeof(int) != sizeof(fd_mask)

Java applet in treeapp.asp not working (webrom)

The java based navigation panel used to navigate the documentation will not work (altough it loads properly) if the pages are in ROM because the applet is using a wrong URI to access the documentation (without ”/”).
This is something of a problem because the online documentation is contained in the web site you can't navigate properly.

If you don't use it to access the documentation from webrom you will not need this fix.

Fix 1

Fix by David Tiktin, 2004/11/24

In web/treeapp.asp, line 22:

-   <PARAM NAME="datascript" VALUE="contents.asp"> 
+   <PARAM NAME="datascript" VALUE="/contents.asp">

Note the slash (/) before contents.asp. That's all it takes.
Rebuild webrom.o and away you go!

Fix 2

Fix by Grant Edwards, 2000/12/15

The problem is that the Java app requests a page called “contents.asp” and receives a “page not found” response when the server is running with pages in ROM.
When running with pages in the filesystem, the “contents.asp” page is found and works fine.

Oddly, this problem only seems to happen when “contents.asp” is requested by the Java app. When requested by Netscape or by a program I wrote in Python, “contents.asp” works in both ROM and filesystem. Browsers and my Python program request ”/contents.asp” and the Java app requests “contents.asp” (note the lack of a leading '/').

It could be that behavior is undefined and “page not found” is a proper response.
In any case I've place a patch to make GoAhead's example work from either filesystem or ROM image:
:!: This patch probably only works if 'char_t' is 'char'.

--- rom.c.old	Fri Dec 15 13:26:14 2000
+++ rom.c	Fri Dec 15 13:26:43 2000
@@ -61,10 +61,25 @@
 void websRomClose()
 {
 	symClose(romTab);
 }
 
+static sym_t *trySymLookup(char_t *path)
+{
+  char newpath[128];
+  sym_t	*sp;
+  
+  sp = symLookup(romTab,path);
+  if (sp)
+	return sp;
+  newpath[0] = '/';
+  newpath[(sizeof newpath)-1] = '\0';
+  strncpy(newpath+1,path,(sizeof newpath)-2);
+  return symLookup(romTab,newpath);
+}
+
+
 /******************************************************************************/
 /*
  *	Open a web page
  */
 
@@ -74,11 +89,11 @@
 	sym_t					*sp;
 
 	a_assert(websValid(wp));
 	a_assert(path && *path);
 
-	if ((sp = symLookup(romTab, path)) == NULL) {
+	if ((sp = trySymLookup(path)) == NULL) {
 		return -1;
 	}
 	wip = (websRomPageIndexType*) sp->content.value.integer;
 	wip->pos = 0;
 	return (wp->docfd = wip - websRomPageIndex);
@@ -103,11 +118,11 @@
 	websRomPageIndexType	*wip;
 	sym_t					*sp;
 
 	a_assert(path && *path);
 
-	if ((sp = symLookup(romTab, path)) == NULL) {
+	if ((sp = trySymLookup(path)) == NULL) {
 		return -1;
 	}
 	wip = (websRomPageIndexType*) sp->content.value.integer;
 
 	memset(sbuf, 0, sizeof(websStatType));

According to my reading of HTTP 1.1 (RFC 2068), the GET request for “contents.asp” generated by the Java app is not valid. The syntax for an HTTP request is

[from section 5.1.0]

   Request-Line   = Method SP Request-URI SP HTTP-Version CRLF 

in our case it's a GET command, so the syntax (spaces/CRLF implied) is

   GET Request-URI HTTP-Version 

[from 5.1.2]

   Request-URI    = "*" | absoluteURI | abs_path 

[from 3.2.1]

   abs_path       = "/" rel_path 

The leading '/' is required syntax.

The filesystem code is a bit more forgiving (either by design or by accident) when the required '/' is not present. The server is probably within it's rights to return an error of some sort.

In any case, the patch I posted makes the ROM image code work with the invalid URI the same way the filesystem code does.

Java applet does not run

Fix by Carlitos, 2005/10/27

The Java applet shipped with the GoAhead documentation and loaded in treeapp.asp may not work correctly in some situations. The problem is caused by the security constraints imposed to unsigned Java applets by the JVM.

From the browser point of view a java applet can be:

  1. unsigned - the JVM will enforce a set of security limitations one being that the Java applet can not connect to a host different from which it was loaded.
  2. signed - the JVM will treat it as a secure applet and allow communication to any web server and even read files from the system.

Here is a snipet from the treeapp.asp file that loads the Java applet:

<APPLET align=left code=treeApp.class codebase="/classes"
        height=100% id=TreeObject width=180 valign=top archive=treeapp.jar VSPACE="0" HSPACE="0">
  <PARAM NAME="authorization" VALUE="">
  <PARAM NAME="fontcolor" VALUE="FFFFFF">
  <PARAM NAME="linecolor" VALUE="FFFFFF">
  <PARAM NAME="backgroundcolor" VALUE="000000">
  <PARAM NAME="datascript" VALUE="/contents.asp">
  <PARAM NAME="port" VALUE="80">
  <PARAM NAME="hilightfontcolor" VALUE="FFF000">
  <PARAM NAME="system" VALUE="127.0.0.1">
  <PARAM NAME="font" VALUE="Ariel,Helvetica bold 10">
</APPLET>

And here is the ASP page used to generate that output:

<APPLET align=left code=treeApp.class codebase="/classes"
        height=100% id=TreeObject width=180 valign=top archive=treeapp.jar VSPACE="0" HSPACE="0">
  <PARAM NAME="authorization" VALUE="<% write(HTTP_AUTHORIZATION); %>">
  <PARAM NAME="fontcolor" VALUE="FFFFFF">
  <PARAM NAME="linecolor" VALUE="FFFFFF">
  <PARAM NAME="backgroundcolor" VALUE="000000">
  <PARAM NAME="datascript" VALUE="/contents.asp">
  <PARAM NAME="port" VALUE="<% write(SERVER_PORT); %>">
  <PARAM NAME="hilightfontcolor" VALUE="FFF000">
  <PARAM NAME="system" VALUE="<% write(SERVER_ADDR); %>">
  <PARAM NAME="font" VALUE="Ariel,Helvetica bold 10">
</APPLET>

The problem is with the SERVER_ADDR value used as a parameter for the Java applet. This value stays the same no matter from what network interface (IP address) the client connection is comming from.

The web server will bind to all network interfaces available, you can check this with netstat -ntl command.
:!: If a new network interface is brought up after the web server was started the web server will still reply to requests comming trough that interface.

EXAMPLE
Consider a Unix/Linux machine with 3 network interfaces:

lo   : 127.0.0.1
eth0 : 192.168.64.2
ppp  : 23.21.12.2

A browser can connect to any of these IP addresses and get web server files but the applet will work only when loaded from one of these address (the one set by SERVER_ADDR because this value is hardcoded).

FIX
Here goes a patch to webs.c and webs.h to solve the connection problem.
This patch sets SERVER_ADDR as the IP of the interface that accepts the connection.
I've tested with a eth0 and ppp0 interface, and it works fine, and now works the treeApp.class and other application that uses the SERVER_ADDR.

--- ../_ws031202/webs.h 2005-10-28 08:53:54.000000000 +0200
+++ webs.h      2005-10-27 17:25:35.000000000 +0200
@@ -85,6 +85,7 @@
        time_t                  timestamp;                      /* Last transaction with browser */
        int                     timeout;                        /* Timeout handle */
        char_t                  ipaddr[32];                     /* Connecting ipaddress */
+       char_t                  ifaddr[32];                     /* Local interface ipaddress */
        char_t                  type[64];                       /* Mime type */
        char_t                  *dir;                           /* Directory containing the page */
        char_t                  *path;                          /* Path name without query */
 
--- ../_ws031202/webs.c 2005-10-28 08:53:54.000000000 +0200
+++ webs.c      2005-10-27 17:28:43.000000000 +0200
@@ -22,6 +22,8 @@
        #include        "websda.h"
 #endif
 
+extern socket_t                **socketList;                   /* List of open sockets */
+
 /******************************** Global Data *********************************/
 
 websStatsType  websStats;                              /* Web access stats */
@@ -256,6 +258,9 @@
 {
        webs_t  wp;
        int             wid;
+       struct sockaddr_in      ifAddr;
+       unsigned int len;
+       char *pString;
 
        a_assert(ipaddr && *ipaddr);
        a_assert(sid >= 0);
@@ -275,6 +280,15 @@
        ascToUni(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr), strlen(ipaddr) + 1));
 
 /*
+ *     Get the ip address of the interface that acept the connection.
+ */
+       len = sizeof(struct sockaddr_in);
+       if (getsockname(socketList[sid]->sock, (struct sockaddr *)&ifAddr, &len) < 0)
+               return -1;
+       pString = inet_ntoa(ifAddr.sin_addr);
+       gstrncpy(wp->ifaddr, pString, gstrlen(pString));
+
+/*
  *     Check if this is a request from a browser on this system. This is useful
  *     to know for permitting administrative operations only for local access
  */
@@ -1142,7 +1161,7 @@
        websSetVar(wp, T("PATH_INFO"), wp->path);
        stritoa(websPort, portBuf, sizeof(portBuf));
        websSetVar(wp, T("SERVER_PORT"), portBuf);
-       websSetVar(wp, T("SERVER_ADDR"), websIpaddr);
+       websSetVar(wp, T("SERVER_ADDR"), wp->ifaddr);
        fmtAlloc(&value, FNAMESIZE, T("%s/%s"), WEBS_NAME, WEBS_VERSION);
        websSetVar(wp, T("SERVER_SOFTWARE"), value);
        bfreeSafe(B_L, value);

Base64 fix

Fix by Ian Bothwell,

base64.c has an error in its map64[] lookup table.
The entries for the '+' and '/' symbol are in the wrong place and will screw up any attempt to encode the NULL character. This was not found before because they never provisioned for it.

base64.c.diff - included in this patch there is a fix for this issue and also a function that encodes a buffer based on it's length ignoring null char at the end of the string.

Can't upload binary files (CGI)

Fix by Holger Lindeberg Bille, 2003/12/16

I implemented file upload capability in external CGI script with the help of the library 'cgic 2.02: an ANSI C library for CGI Programming'. I have troubles with uploading binary files, uploading text files works OK.
Then I found the cause. In file webs.c, below WEBS_POST_CLEN in define __NO_CGI_BIN the line with gwrite(fd, text, gstrlen(text));.
When I changed this line to gwrite(fd, text, nbytes); the upload was OK.
Probably the same problem exist somewhere else in the code. The new piece of the code is:

  case WEBS_POST_CLEN:
/*
 *   POST request with content specified by a content length.
 *   If this is a CGI request, write the data to the cgi stdin.
 *   socketGets was used to get the data and it strips \n's so
 *   add them back in here.
 */
#ifndef __NO_CGI_BIN
                if (wp->flags & WEBS_CGI_REQUEST) {
                         if (fd == -1) {
                                           fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY,
                                                      0666); 
                         }
 
-                        gwrite(fd, text, gstrlen(text)); 
+                        gwrite(fd, text, nbytes);

User reports:

  • Piggyboy, 2005/02/17 - it works.

64 bit compatibility

The webserver code is not 64 bit ready. It makes the wrong assumption in many places that an int can hold a void * .
This is not always the case so you will encounter problems on any plaform where:

sizeof(int) < sizeof(void *)

This could include also some embedded environments (no user reports on this).
Encountered this problem for 64 bit builds on the following platforms:

  • HP-UX 11.X, 11.23
  • Solaris 8,9
  • DEC Alpha

:!: 32 bit builds running on a 64 bit platform don't need this change.

Change list

Fix by Barry Stone, 2003/1/17
Verified by Petchesi Gabriel Horatiu

A list with the necessary changes to be made.

md5.h

Fix by Petchesi Gabriel Horatiu

The md5 algorithm will not work correctly on 64 bit builds without the following change:

@@ -29,7 +29,7 @@
 #define _h_MD5 1
 
 #ifndef UINT4
-#define UINT4 unsigned long
+#define UINT4 unsigned int
 #endif
 
 #ifndef POINTER

:!: If on your platform sizeof(int) < 4 then don't apply this change.

ejscript memory leak (ASP)

Fix by Fred Sauer, 2002/12/23

It appears that the following .asp file will cause the GoAhead webserver (2.1.4 on Windows) to have a memory leak. Repeated calls cause the memory usage of the webserver to keep increasing. Splitting the single write statement into two seems to avoid the problem. Here is a code segment that will show the problem:

<html>
<head>
<% language=javascript %>
</head>
<body>
<% for (i=0; i < 6; i++) write ("<h1> When the string is longer than 128
characters it seems to cause a memory leak, at least within the body of a
for loop. Try it yourself </h1>\n"); %>
</body>
</html>

The problem seems to be in ejlex.c, inputPutback(). It appears that under some circumstances it can be called with ip→lineColumn set to zero. In this case the lines:

ip->lineColumn-- ;
ip->line[ip->lineColumn] = '\0';

result in the balloc header being corrupted sine lineColumn is now -1. The memory is never freed therefore.

Ths simple solution is to check for ip→lineColumn == 0 before doing the decrement:

-  ip->lineColumn-- ;
+  if (ip->lineColumn > 0) ip->lineColumn-- ;
   ip->line[ip->lineColumn] = '\0';

User reports: there is no confirmation that this issue is solved in the latest version (2.1.8). The problem was reported against an earlier version of the webserver.

DAA

Fixes by Richard Laing, 2003/7/15

A collection of fixes for the Digest Access Authentication implementation in GoAhead webserver.

URI digest

The webserver calculates the digest using the URI which does not include the request parameters (when using HTTP GET). All the browsers I have tested (except Safari on Mac OS X) include the parameters in the digest. I believe this is in line with the specification.

I have added a new function identical to the websCalcDigest function called websCalcUrlDigest. This calculates the digest from wp→url rather than wp→uri. This code should be added to websda.c

 /*
  *	Calculate second portion of digest H(A2)
  */
	method = websGetVar(wp, T("REQUEST_METHOD"), NULL);
	a_assert(method);
-       a2 = NULL;
-	fmtAlloc(&a2, 255, T("%s:%s"), method, wp->uri); 
+       /* more save for GET url */
+       a2 = balloc(B_L, (gstrlen(method) + 2 + gstrlen(wp->url) ) * sizeof(char_t));
+       a_assert(a2);
+       gsprintf(a2, T("%s:%s"), method, wp->url);
	a_assert(a2);

In security.c websSecurityHandler() I now call the current method first (Safari and any other browser not including the parameters will therefore work) and if that fails the second method is used:

/* Try the URI based digest */			
digestCalc = websCalcDigest(wp);
a_assert(digestCalc);
 
if (gstrcmp(wp->digest, digestCalc) != 0) {
 
/* RCL; Calculate digest based on URL rather than URI this is required for 
   Mozilla and IE (Mac at least) to work with GET requests with parameters.   */
 
       	digestCalc = websCalcUrlDigest(wp);
	a_assert(digestCalc);
 
	if (gstrcmp(wp->digest, digestCalc) != 0) {
     		websStats.access++;
     		websError(wp, 401, T("Access Denied\nWrong Password"));
		nRet = 1;
	}
}

isgoodchar()

This macro is defined in webs.c:825 as:

#define isgoodchar(s) (gisalnum((s)) || ((s) == '/') || ((s) == '_') || ((s) == '.')  || ((s) == '-') )

The wp→cnonce value includes characters not considered valid by isgoodchar() causing problems with parsing the digest parameters.
This problem shows up currently when using Opera.

The fix is to reverse the logic, instead of looking for good characters look for invalid characters to terminate the value.
Apply this change in webs.c:websParseRequest()

 /*
  *		Zero-terminate value
  */
					npv = vp;
/* RCL : the field ends at a quote or a comma, this fix allows
   Digest Authentication with Opera, basically reverse the logic,
   look for an invalid character, we know what those are. */
 
/*   while (isgoodchar(*npv)) { */
     while ( (*npv != ',') && (*npv != '\"') ) {
		npv++;
     }
     tpv = *npv;
     *npv = 0;
 /*
  *		Extract the fields
  */

Domain incorect

The domain needs to be set to / rather than to what websGetHostUrl() function returns in websResponse().
This is needed for Opera to work.

Access Control Precedence

Alternative by Bob Pizzi, 2009/10/20

The existing code within um.c:umGetAccessMethodForURL forces the access method of the group upon the page. This could be too limiting in certain cases. Rather, defining a precedence of access between the group (if one is used) and the URL might be a better approach.

In the folowing reimplementation, the 'higher' access method between the group and URL is returned.

accessMeth_t umGetAccessMethodForURL(char_t *url)
{
    accessMeth_t	amRet;
    char_t		*urlHavingLimit, *group;
	
    urlHavingLimit = umGetAccessLimit(url);
    if (urlHavingLimit) {
        accessMeth_t    amGroup, amUrl;

	group = umGetAccessLimitGroup(urlHavingLimit);
        if (group && *group) {
            amGroup = umGetGroupAccessMethod(group);
	} else {
            amGroup = AM_NONE;
	}
        amUrl = umGetAccessLimitMethod(urlHavingLimit);

        //if the url's page is more strict than the group, use
        //the url's access, else use the group's
        amRet = (amUrl > amGroup ? amUrl : amGroup);

	bfree(B_L, urlHavingLimit);
    } else {
	amRet = AM_FULL;
    }

    return amRet;
}

Bugs in 2.1.8

We list here bug reports with no resolution or patches.

Luigi Auriemma

Has a collection of security advisories. Search for “GoAhead” on the list.

Worms

There are reports that worms targeting IIS and Apache using long URL's crash the web server. The attacker use a very very long string in the URL that goes something like this:

SEARCH /\220\002\261\002\061 .... 

and it goes on forever. The websever at some point of time will die off and stop responding.

Users reported these problems with version 2.1.5 and 2.1.6 running on VxWorks. Because of this the webserver is not suitable for deployment on the Internet.


Simon Byholm, 2004/06/23 proposed the following patch:

I have done some debugging and found out that if a very long string without a newline is received every char is added to a ringbuffer that uses several bytes for every character stored and eventually you will run out of memory.
This doesn't crash the web server, but if your version of malloc blocks while waiting for memory to be available the web server will hang forever (or until some other task is letting go of some memory).

In our case the rest of the system is running safe as the balloc() function tries to allocate a megabyte in its last failed attempt which means there is still memory left for most normal operation. Of course the system might as well crash loudly of some other part of it don't get the memory it needs.

I was thinking of adding a safeguard in socketGets():

+
+ /* 
+  *            This check makes sure we are not using up all memory if some virus 
+  *            send us long long string without newlines. 
+  */
+               if( ringqLen( lq) > 2048) c = '\n';
+
 
 /*
  *		If a newline is seen, return the data excluding the new line to the
  *		caller. If carriage return is seen, just eat it.
  */
		if (c == '\n') {
			len = ringqLen(lq);
			if (len > 0) {
				*buf = ballocAscToUni((char *)lq->servp, len);
			} else {
				*buf = NULL;
			}
			ringqFlush(lq);
			return len;
 
		} else if (c == '\r') {
			continue;
		}
		ringqPutcA(lq, c);
	}

Does anyone have any idea if 2048 bytes is enough for a line in a request?


Michael O'Brien, 2004/06/24 said:

This is a clearly a bug.
This should be a configurable (compile) constant. 2048 bytes is ample for 99.99% of requests.
Someone may need more but they could recompile to meet that need. I'd recommend a simple test when the URL first line is parsed.
Similarly, all header lines and the total header size should be validated.

Negative numbers (ASP)

The following problems are caused by limitations in the ejscript parser.

Comparison

Ivano Guerra, 2003/05/26

I have a problem with negative numbers using embedded jscript (ASP) in version 2.1.4. The following expression returns FALSE:

(-1 > -2)

I checked the source code, it seems a problem when the parser finds the - that is not managed as negative.
So it will not work in statements like if()/for() whenever I use negative numbers to check conditions.

Substraction

Richard Cullen, 2000/08/23

If you put the following snippet into an ASP file the script processor barfs.

<% \
var NewEnd; \
NewEnd = -1 - 1; \
%>

This works OK:

<% \
var NewEnd; \
NewEnd = -1 + 1; \
%>

So it seems to be a problem subtracting a number from a negative number.

POST CGI

Allan, 15 Aug 2005
“We are trying to use Go-Ahead together with a perl based cgi script in order to allow uploading of binary files. The encoding type I am using is multipart/form-data with a POST method. The perl script uses CGI_Lite and it works consistently with other web servers.
The symptom of failure that we are seeing is that occasionally the cgi script receives no input. That is, it is not passed in the content stream that was posted to webs.
Our suspicion is that webs prematurely deletes the tmp file that is used to store the stream.
When we strace webs, we can get the failure rate to drop very low, maybe even not happening at all. This would suggest that the problem has some relationship with timing.

We are currently trying to log a failing trace in order to identify what exactly happens in the failing case. The system is x86 and has lots of free memory.
The file being uploaded doesn't have to be big in order to exhibit the failure but it does appear that larger files fail more often. We are using a 1.2 MB file for testing and have seen it fail with 90k sized files.”

Claude, 20 Aug 2005
“In my case, it is Linux 2.4.31 running on a small PPC embedded system … I modified it to dump, in parallel to returning the data to the caller, the data into a temp file … The resulting temp file is not always ok”

Summary:

  1. the temporary file is closed/removed before the full content is received by the CGI process.
  2. upload binary files patch does not fix this issue.

Problem is tracked on a separate page here.

VxWorks

Fix by newzy@hotmail.com, 2006/6/9
http://www.eybuild.com

VxWorks does not support O_APPEND flag to open a file thus you must seek each time to the end of the stream:

    fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY | O_APPEND, 0666);
    /*  Should better write as: */
    fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY, 0666);
    lseek(fd, 0, SEEK_END);

NOTE: You should replace anywhere O_APPEND is used.

Buffer Overflow

2007-Feb-14 Helder Magalhães wrote:

It isn't clear if the following security issue (reported by Gilad Ben-Yossef) applies to most recent version (2.1.8):

2008-Feb-19 Gilad Ben-Yossef wrote: It doesn't.

2008-Nov-09 TRChang wrote: It did.

Known issues

We list here problems experienced by developers but not necessarily caused by the webserver.
The problems may be caused by other factors like:

  • developer changes to the webserver code (like GoForms).
  • development platforms with known problems in a subsystem (like TCP/IP for VxWorks).
  • other problems.

We list the issues here for reference in case other developers encounter these problems thus it would be easier to identify the root cause of the problem. Post your own experiences in case you encounter any of these issues.

TCP checksum errors (VxWorks)

Reported by vnarayan and Vernon Fuller,

This is a problem at the TCP/IP communication level. The problem was noticed on the following platforms:

  • VxWorks 5.5 with MIPS 32 SoC, 4km core SOC. Goahead version : 2.1.8

Another user reported that the problem happens when it takes a long time (2+ seconds) to process asp calls. The bad page ends up having stray html code from another page embedded in the <head> section.
Since this problem does not occur on other platforms it may be something caused by VxWorks TCP/IP stack implementation/configuration.

Marc S. Yaxley, 2000/05/11 reports

I've found out more on this. If I add at least a 166 millisecond delay just after the send( …. ) call in “socket.c” the packets go out ok, but rather slow.
I've now found that the WindRiver webserver also has this SAME PROBLEM! So, it's not the GoAhead product.
Environment: VxWorks 5.4 kernel

J Duncan, 2006-Jul-08

I saw something similar happen in ecos. In that case the problem was that the ethernet driver made some assumptions on data alignment coming from the TCP/IP stack, losing bytes and causing a checksum error. The assumption held true for other sources of data but not for the GoAhead web server.

Slow CGI (Linux)

Reported by Nanda Reddy,

Static HTML's are very fast but CGI scripts are very very slow … taking 30 times more than normal static page.
The problem was caused by insufficient memory, after increasing that from 8 MB to 64 MB everything is working correctly.

System crash (VxWorks)

Reported by Alexandre, 2004/12/27

I am running Goahead 2.1.8 in Vxworks, and implement some pages and corresponding processing procedures.
Problem comes, when I click and only when I click the “Refresh” button in IE. Sometimes the system crash and the serial com output msgs look like this:

data access
Exception current instruction address: 0x001f7858
Machine Status Register: 0x0000b032
Data Access Register: 0x04000000
Condition Register: 0x44000042
Data storage interrupt Register: 0x0000b032
Task: 0x3338488 "tWebsTask"
Task tWebsTask is dead
23f49c vxTaskEntry    +60 : websvxmain (0, 0)
190b18 websvxmain     +c0 : socketSelect (3, 64)
1a99d4 socketSelect   +354: select ()

  NAME        ENTRY       TID    PRI   STATUS      PC       SP     ERRNO   DELAY
---------- ------------ -------- --- ---------- -------- -------- -------  -----
tWebsTask  websvxmain    3338488 100 SUSPEND      1f7858  3338320      46     0

stack: base 0x3338488  end 0x3333668  size 19312  high 2312   margin 17000

options: 0x4
VX_DEALLOC_STACK

r0     =  3ffffd8   sp     =  3338320   r2     =        0   r3     = e
r4     =  1523ce8   r5     =  3ffffe8   r6     =  3ffffc8   r7     = 33383e0
r8     = 74650000   r9     =  3338378   r10    = 65666c61   r11    = 8
r12    = 42000042   r13    =        0   r14    =        0   r15    = 0
r16    =        0   r17    =        0   r18    =        0   r19    = 0
r20    =        0   r21    =        0   r22    =        0   r23    = 33383e0
r24    =        0   r25    =  3ffffc8   r26    =        e   r27    = 0
r28    =  3ffffe8   r29    =        0   r30    =  1523ce8   r31    = 0
msr    =     b032   lr     =   1a99d4   ctr    =        0   pc     = 1f7858
cr     = 44000042   xer    =        0 

data access
Exception current instruction address: 0x001f7858
Machine Status Register: 0x0000b032
Data Access Register: 0x04000000
Condition Register: 0x44000042
Data storage interrupt Register: 0x0000b032

AP software 3.0.0.27
Created: Dec 15 2004, 11:02:59

Rebooting ...
  • select() is a system call, it blows up with errno = 0×46, EWOULDBLOCK.

Unaligned data access error in bfree (WindowsCE)

Reported by Stefan Muenzel, 2004/09/03

Problem reported for Windows CE 3 with 41856KB of memory and a MIPS 32 bit processor.
We have observed an unaligned data access exception in bfree during a period of high memory usage of our device.

  • is this behavior normal or is it the result of memory corruption?
  • has this platform been tested before?

This behavior was observed in both debug and release builds, but is difficult to recreate.

Content not cached (VxWorks)

Reported by x, 2002/12/09

I get a staggering performance increase in my browser after doing this.
Note: it only affects the default handler usage (such as WEB_ROM'ed files). Your goforms and CGI stuff are unaffected. This should help vxworks users in particular since there are some weird intermittent file access issues going on. In my system, icons seemed not to be cached and cause constant requests to the server, which suffer the intermittent communication snafu mentioned above.

The change goes in default.c, websDefaultHandler(). Add a -DWEBS_ALLOW_CLIENT_CACHE to your makefile:

 if (flags & WEBS_ASP) {
    bytes = 0;
    websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
 
 } else {
 
#ifdef WEBS_ALLOW_CLIENT_CACHE
    websWrite(wp, T("Cache-Control: public\r\n"));
#endif
 
    if ((date = websGetDateString(&sbuf)) != NULL) {
        websWrite(wp, T("Last-modified: %s\r\n"), date);
        bfree(B_L, date);
    }
    bytes = sbuf.size;
 }

User reports:

  • gabip - this seems to be a browser issue more than a webserver one. The content should be properly cached after the first request.

documentation/goahead/general.txt · Last modified: 2009/10/20 12:39 by embeddedbob