Bug fix to HTTCP.c

Hi,

I have found 2 bugs; Here they are with a fix:

1. Bug in the implementation of the TCP state machine: HTDoConnect (HTNet *
net) (in HTTCP.c)

Bug Details
--------------
When calling connect() in the second time, after the first call to connect()
returned WSAEWOULDBLOCK (10035), an error of WSAEINVAL (10022) is returned.
It happens often on WinNT & Win95, and rarely on Win2K & Win98, where in
most cases the second call to connect() returns WSAEISCON (10056).
I have no idea what is the behavior of connect() on UNIX.
The current implementation, consider WSAEINVAL as an error and set the
host->tcpstate to TCP_ERROR, which cause, in the layers above, cleanup to be
done (request unregister, HTTPCleanup etc. ), without retrying again.

The Microsoft docs regarding connect() says:

"... Until the connection attempt completes on a nonblocking socket,
all subsequent calls to connect on the same socket will fail with the error
code WSAEALREADY, and WSAEISCONN
when the connection completes successfully.
Due to ambiguities in version 1.1 of the Windows Sockets specification,
error codes returned from connect while a connection is already pending may
vary among implementations.
As a result, it is not recommended that applications use multiple calls to
connect to detect connection completion.
If they do, they must be prepared to handle WSAEINVAL and WSAEWOULDBLOCK
error values the same way that they handle WSAEALREADY,
to assure robust execution ..."

And here is another good one ....
This is the description for error WSAEALREADY:
"A nonblocking connect call is in progress on the specified socket.
Note In order to preserve backward compatibility, this error is reported as
WSAEINVAL
to Windows Sockets 1.1 applications that link to either Winsock.dll or
Wsock32.dll."

The Fix
----------
The bug fix is done in HTTCP.c:

1. At the WINSOCKAPI specific macros (line 47), define another macro to
handle an error of WSAEINVAL:
Note: The Unix part was not tested.

/* imperical study in socket call error codes
 */
#ifdef _WINSOCKAPI_					/* windows */
#define NETCALL_ERROR(ret)	(ret == SOCKET_ERROR)
#define NETCALL_DEADSOCKET(err)	(err == WSAEBADF)
#define NETCALL_WOULDBLOCK(err)	(err == WSAEWOULDBLOCK)
#define NETCALL_INVAL(err) (err == WSAEINVAL)				/* yovavm@contact.com */
#else /* _WINSOCKAPI_ 					   unix    */
#define NETCALL_ERROR(ret)	(ret < 0)
#define NETCALL_DEADSOCKET(err)	(err == EBADF)
#define NETCALL_INVAL(err) (err == EINVAL)					/* yovavm@contact.com - Check
if this is error code exist on on UNIX machines */


2. To the HTDoConnect (HTNet * net) function, under the TCP_NEED_CONNECT
case of the state machine (line 294), add a condition to handle an error of
WSAEINVAL:

	case TCP_NEED_CONNECT:
		status = connect(HTChannel_socket(host->channel), (struct sockaddr *)
&host->sock_addr, sizeof(host->sock_addr));
		if (NETCALL_ERROR(status))
		{
			if (NETCALL_WOULDBLOCK(socerrno))
			{
		    		HTTRACE(PROT_TRACE, "HTDoConnect. WOULD BLOCK `%s'\n" _ hostname);
			    	HTHost_register(host, net, HTEvent_CONNECT);
			    	return HT_WOULD_BLOCK;
		    	}
					/*
			*	yovavm@contact.com
			*
			*	According to Microsoft docs, the error code WSAEALREADY is described
as:
			*	"A nonblocking connect call is in progress on the specified socket.
			*	Note In order to preserve backward compatibility, this error is
reported as WSAEINVAL to
			*	Windows Sockets 1.1 applications that link to either Winsock.dll or
Wsock32.dll."
			*
			*/
			if (NETCALL_INVAL(socerrno))
			{
			    host->tcpstate = TCP_CONNECTED;
			    HTTRACE(PROT_TRACE, "Connection to HTHost %p is already in
progress.\n" _ host);
			    break;
			}

		/ * ... */

		}

2. Bug in HTEvtLst.c

See the comment for bug description and solution
This code is in function HTEventList_loop (HTRequest * theRequest) at
HTEvtLst.c line 715.

#ifdef __hpux
        active_sockets = select(maxfds+1, (int *)&treadset, (int
*)&twriteset,
				(int *)&texceptset, wt);
#else
		/*
		*	yovavm@contact.com
		*
		*	On some WINSOCK versions select() with 3 empty sets and NULL timeout
		*	returns 0 and in some it returns -1.
		*	If 0 is returned in such situation, we will go into an infinite loop
(cause the sets will stay empty forever ...),
		*	so make sure to set the active_sockets = -1 which will take us out of
the loop.
		*/
		if (( treadset.fd_count || twriteset.fd_count || texceptset.fd_count) &&
wt)
			active_sockets = select(maxfds+1, &treadset, &twriteset, &texceptset,
wt);
		else
			active_sockets = -1;
#endif

Enjoy,
Yovav

~~~~~~~~~~~~~~~~~~~~~~~~~
Yovav Meydad
Software Engineer
www.contact.com
We Invite You to make Contact !
~~~~~~~~~~~~~~~~~~~~~~~~~

Received on Thursday, 25 May 2000 10:32:58 UTC