The Internet and Netscape


Most people today believe that Netscape and other "internet" companies produce true Internet products. They think that Netscape's programmers actually sit down and write the code that interacts with your modem and ISP. They click on a link and think that it's their browser that's connecting to the server and downloading a file. They use FTP and think that it's their FTP program that's handling the data as it flows across the Internet and tumbles down the wires to their computer.

They couldn't be more wrong !!

Netscape may be a great company with an exceptional product like Navigator 4.0, but they don't dabble in Internet programming. What we're going to demonstrate is that Internet browsers and servers do not hold code of any complexity. Probably less than 5% of all the code which went into Navigator was Internet related code and when I say Internet related code I mean Windows Sockets code.

Windows sockets or WinSock is an abstraction layer applied by the University of California over the TCP/IP protocol (This might be a good time to jump over to our WinSock tutorial. The WinSock makes TCP/IP (read Internet) programming easier by equating file transfers over the Internet with file transfers over the hard disk. So, you have nice tidy little functions like send() and recv() to transfer information over the Internet. Unfortunately, if the abstraction layer is complex and intricate enough, the commands that you give and their translation into actions have no perceivable relation with each other. So, I might spend my whole life using sockets but never learn that TCP packets carry sequence and acknowledgement numbers.

So, sockets programming is not Internet programming. Netscape is not directly talking to the serial port or the network card and unless you do that, you cannot claim to be writing Internet code. All it's doing is calling other peoples' functions and code, which creates the actual bytes which flow across the lines. Infact, on most machines running Windows95, the file wsock32.dll and the VxD wsock.vxd (collectively called the stack) have been written by Microsoft ! It's Microsoft which is the Internet company in this case, not Netscape.

The same goes for Java too. It may be called an Internet programming language but all it does is call WinSock code through Netscape. When you're through with this page, jump over to our Java expose.

What we're going to do is prove to you that all the hype about Netscape being an Internet company is just so much hot air. We're going to show you how to fool Netscape into doing anything you want it to, by creating our own dummy wsock32.dll.

The Elusive .dll

All code under Windows '95 is in .dlls and the .lib files tell you exactly where in which .dll the requested function code is stored. So what we're going to do now is write our own dummy wsock32.dll, but we'll be leaving the .lib file untouched. We've used Visual C++ 4.0 for all these programs. Create a project to make a .dll. Name the project yyz and then add the code given below to a file yyz.c.

Note: These programs only work under Netscape Navigator. The reason Internet Explorer rejects them is because it call some undocumented functions in wsock32.dll which we don't really know much about.

Before creating our fake wsock32.dll, make sure you assign a default page to Netscape. Any page will do, but use an IP address (e.g. 202.54.1.18), not a name like www.neca.com. The reason we don't want to use a domain name is because the process of converting a name into a corresponding IP address is very complex and involves a whole bunch of functions. Besides, we'd also have to emulate the DNS Server and we're just too lazy to do that!

What we're going to be doing here is create a very limited .dll where the only function is DllMain, which is the first function to be called by Netscape. In DllMain we have a function called abc() which simply writes "DllMain", or anything else within it's brackets, to the hard disk in the file z.txt. This is a better way of keeping track of function calls than using messageboxes which continuously halt the program and whose messages cannot be saved.

yyz.c


#include <windows.h>
#include <stdio.h>
void abc(char * p)
{
	FILE *fp=fopen("c:\\z.txt","a+");
	fprintf(fp,"%s\n",p);
	fclose(fp);
}
char aa[1000];
BOOL WINAPI DllMain( HINSTANCE  hinstDLL,DWORD  fdwReason,LPVOID  lpvReserved)
{
	abc("DllMain");
	 return 1;
}

This exercise will prove that Netscape cannot work without the functions in Wsock32.dll.

DllMain is called when Netscape both loads and unloads and if it is absent the .dll will not load. Inside DllMain we have to put the statement return 1;, for the .dll to load successfully. A return 0; implies that an error occurred and the .dll is not loaded. For our dummy .dll to have any effect, it must be placed in the subdirectory, C:\Windows\System and renamed to wsock32.dll.

Before you do this be sure to copy the original wsock32.dll somewhere else !

Tip: When the project is built we get the file yyz.dll in the debug directory. Instead of copying the .dll into C:\Windows\System by hand each time, simple create a batch file copydll.bat with the following commands

	Copy  c:\yyz\debug\yyz.dll c:\windows\system\wsock32.dll
	del c:\z.txt 

Once the fake DLL is in place, start up Netscape and watch the errors fly. Netscape will react angrily, and spit out an 'Unable to initialize the network layer! Check your winsock for errors' error message. Netscape not finding many of the functions it needs, will hang taking your computer with it. Pay it no attention and remove Netscape from memory. To remedy the situation press Ctrl+Atl+Del once, click on the line which says Netscape and then click the button End Task. Wait for 10 seconds. If there is no change, repeat the procedure and keep your fingers crossed.

What we've demonstrated here is that Netscape will accept our .dll as genuine, even though it hardly contains anything at all, and will attempt to communicate with it.

If you analyse the file shown above, you'll see that DllMain is called when Netscape loads into memory and when it unloads from memory.

z.txt

DllMain
DllMain

Create the .def file as given below. It's in the .def file that you "export" your functions so that they may be called by other programs.

yyz.def

LIBRARY yyz
DESCRIPTION "wsock function"
EXPORTS
	WSAAsyncGetHostByAddr @102
	WSAAsyncGetHostByName @103
	WSAAsyncGetProtoByName @105
	WSAAsyncGetProtoByNumber @104
	WSAAsyncGetServByName @107
	WSAAsyncGetServByPort @106
	WSAAsyncSelect @101
	WSACancelAsyncRequest @108
	WSACancelBlockingCall @113
	WSAGetLastError @111
	WSAIsBlocking @114
	WSARecvEx @1107
	WSASetBlockingHook @109
	WSASetLastError @112
	WSAStartup @115
	WSAUnhookBlockingHook @110
	accept @1
	bind @2
	closesocket @3
	connect @4
	gethostbyaddr @51
	gethostbyname @52
	gethostname @57
	getpeername @5
	getprotobyname @53
	getprotobynumber @54
	getservbyname @55
	getservbyport @56
	getsockname @6
	getsockopt @7
	htonl @8
	htons @9
	inet_addr @10
	inet_ntoa @11
	ioctlsocket @12
	listen @13
	ntohl @14
	ntohs @15
	recv @16
	recvfrom @17
	select @18
	send @19
	sendto @20
	setsockopt @21
	shutdown @22
	socket @23
	WSACleanup @116
	__WSAFDIsSet @151
	s_perror @1108
	TransmitFile @1140
	EnumProtocolsA @1111
	EnumProtocolsW @1112
	GetAddressByNameA @1109
	GetAddressByNameW @1110
	GetNameByTypeA  @1115
	GetNameByTypeW  @1116
	GetServiceA  @1119
	GetServiceW  @1120
	GetTypeByNameA  @1113
	GetTypeByNameW  @1114
	SetServiceA  @1117
	SetServiceW  @1118
	NPLoadNameSpaces @1130
	dn_expand @1106
	inet_network @1100
	rexec @1103
	rresvport @1104
	WsControl
	DllMain		

Once you're through with the .def file, create a .c file and cut-copy-paste the code given below into it. Name the file yyz.c. This file is going to turn into our dummy WinSock once we're through compiling it. We've added our trademark abc()'s everywhere. This function will keep writing the name of the functions called, to a file on disk.

Code for dummy wsock32.dll

yyz.c

#include <windows.h>
#include <stdio.h>
void abc(char * p)
{
FILE *fp=fopen("c:\\z.txt","a+");
fprintf(fp,"%s\n",p);
fclose(fp);
}
char aa[1000];
BOOL WINAPI DllMain( HINSTANCE  hinstDLL,DWORD  fdwReason,LPVOID  lpvReserved)
{
abc("DllMain");
 return 1;
}
SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr,int FAR *addrlen)
{
abc("accept ");
return 0;
}
int PASCAL FAR closesocket (SOCKET s)
{
abc("closesocket ");
return 0;
}
int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen)
{
abc("connect ");
return 0;
}
int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp)
{
abc("ioctlsocket ");
return 0;
}
int PASCAL FAR getpeername (SOCKET s, struct sockaddr FAR *name,
                            int FAR * namelen)
{
abc("getpeername ");
return 0;
}
int PASCAL FAR getsockname (SOCKET s, struct sockaddr FAR *name,
                            int FAR * namelen)
{
abc("getsockname ");
return 0;
}
int PASCAL FAR getsockopt (SOCKET s, int level, int optname,
                           char FAR * optval, int FAR *optlen)
{
abc("getsockopt ");
return 0;
}
u_long PASCAL FAR htonl (u_long hostlong)
{
abc("htonl ");
return 0;
}
char FAR * PASCAL FAR inet_ntoa (struct in_addr in)
{
abc("inet_ntoa ");
return 0;
}
int PASCAL FAR listen (SOCKET s, int backlog)
{
abc("listen ");
return 0;
}
u_long PASCAL FAR ntohl (u_long netlong)
{
abc("ntohl ");
return 0;
}
u_short PASCAL FAR ntohs (u_short netshort)
{
abc("ntohs ");
return 0;
}
int PASCAL FAR recvfrom (SOCKET s, char FAR * buf, int len, int flags,
                         struct sockaddr FAR *from, int FAR * fromlen)
{
abc("recvfrom ");
return 0;
}
int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
                       fd_set FAR *exceptfds, const struct timeval FAR *timeout)
{
abc("select ");
return 0;
}
int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags,
                       const struct sockaddr FAR *to, int tolen)
{
abc("sendto ");
return 0;
}
int PASCAL FAR shutdown (SOCKET s, int how)
{
abc("shutdown ");
return 0;
}
struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr,
                               int len, int type)
{
abc("gethostbyaddr");
return 0;
}
struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name)
{
abc("gethostbyname");
return 0;
}
int PASCAL FAR gethostname (char FAR * name, int namelen)
{
abc("gethostname ");
return 0;
}
struct servent FAR * PASCAL FAR getservbyport(int port, const char FAR * proto)
{
abc("getservbyport");
return 0;
}
struct servent FAR * PASCAL FAR getservbyname(const char FAR * name,
                                              const char FAR * proto)
{
abc("getservbyname");
return 0;
}
struct protoent FAR * PASCAL FAR getprotobynumber(int proto)
{
abc("getprotobynumber");
return 0;
}
struct protoent FAR * PASCAL FAR getprotobyname(const char FAR * name)
{
abc("getprotobyname");
return 0;
}
int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
abc("WSAStartup");
return 0;
}
int PASCAL FAR WSACleanup(void)
{
abc("WSACleanup");
return 0;
}
void PASCAL FAR WSASetLastError(int iError)
{
abc("WSASetLastError");
}
int PASCAL FAR WSAGetLastError(void)
{
abc("WSAGetLastError");
return 0;
}
BOOL PASCAL FAR WSAIsBlocking(void)
{
abc("WSAIsBlocking");
return 0;
}
int PASCAL FAR WSAUnhookBlockingHook(void)
{
abc("WSAUnhookBlockingHook");
return 0;
}
FARPROC PASCAL FAR WSASetBlockingHook(FARPROC lpBlockFunc)
{
abc("WSASetBlockingHook");
return 0;
}
int PASCAL FAR WSACancelBlockingCall(void)
{
abc("WSACancelBlockingCall");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetServByName(HWND hWnd, u_int wMsg,
   const char FAR * name,const char FAR * proto,
                                        char FAR * buf, int buflen)
{
abc("WSAAsyncGetServByName");
return 0;
}

HANDLE PASCAL FAR WSAAsyncGetServByPort(HWND hWnd, u_int wMsg, int port,
   const char FAR * proto, char FAR * buf,int buflen)
{
abc("WSAAsyncGetServByPort");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetProtoByName(HWND hWnd, u_int wMsg,
   const char FAR * name, char FAR * buf,int buflen)
{
abc("WSAAsyncGetProtoByName");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetProtoByNumber(HWND hWnd, u_int wMsg,
    int number, char FAR * buf,int buflen)
{
abc("WSAAsyncGetProtoByNumber");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetHostByName(HWND hWnd, u_int wMsg,
  const char FAR * name, char FAR * buf,int buflen)
{
abc("WSAAsyncGetHostByName");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetHostByAddr(HWND hWnd, u_int wMsg,
  const char FAR * addr, int len, int type,char FAR * buf, int buflen)
{
abc("WSAAsyncGetHostByAddr");
return 0;
}
int PASCAL FAR WSACancelAsyncRequest(HANDLE hAsyncTaskHandle)
{
abc("WSACancelAsyncRequest");
return 0;
}
int PASCAL FAR WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,
                               long lEvent)
{
abc("WSAAsyncSelect");
return 0;
}
int PASCAL 	__WSAFDIsSet ()
{
abc("__WSAFDIsSet ");
return 0;
}
int PASCAL 	s_perror ()
{
abc("s_perror ");
return 0;
}
int PASCAL 	TransmitFile ()
{
abc("TransmitFile ");
return 0;
}
int PASCAL 	EnumProtocolsA ()
{
abc("EnumProtocolsA ");
return 0;
}
int PASCAL 	EnumProtocolsW ()
{
abc("EnumProtocolsW ");
return 0;
}
int PASCAL 	GetAddressByNameA ()
{
abc("GetAddressByNameA ");
return 0;
}
int PASCAL 	GetAddressByNameW ()
{
abc("GetAddressByNameW ");
return 0;
}
int PASCAL 	GetNameByTypeA  ()
{
abc("GetNameByTypeA  ");
return 0;
}
int PASCAL 	GetNameByTypeW  ()
{
abc("GetNameByTypeW  ");
return 0;
}
int PASCAL 	GetServiceA ()
{
abc("GetServiceA ");
return 0;
}
int PASCAL 	GetServiceW ()
{
abc("GetServiceW ");
return 0;
}
int PASCAL 	GetTypeByNameA ()
{
abc("GetTypeByNameA ");
return 0;
}
int PASCAL 	GetTypeByNameW ()
{
abc("GetTypeByNameW ");
return 0;
}
int PASCAL 	SetServiceA ()
{
abc("SetServiceA ");
return 0;
}
int PASCAL 	SetServiceW  ()
{
abc("SetServiceW  ");
return 0;
}
int PASCAL 	dn_expand ()
{
abc("dn_expand ");
return 0;
}
int PASCAL 	inet_network ()
{
abc("inet_network ");
return 0;
}
int PASCAL 	rexec ()
{
abc("rexec ");
return 0;
}
int PASCAL 	rresvport ()
{
abc("rresvport ");
return 0;
}
int PASCAL WSARecvEx ()
{
abc("WSARecvEx ");
return 0;
}
unsigned long PASCAL FAR inet_addr (const char FAR * cp)
{
abc("inet_addr ");
return 0;
}
u_short PASCAL FAR htons (u_short hostshort)
{
abc("htons ");
return 0;
}
SOCKET PASCAL FAR socket (int af, int type, int protocol)
{
abc("socket ");
return 0;
}
int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen)
{
abc("bind ");
return 0;
}
int PASCAL FAR setsockopt (SOCKET s, int level, int optname,
                           const char FAR * optval, int optlen)
{
abc("setsockopt ");
return 0;
}
int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags)
{
abc("recv ");
return 0;
}
int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags)
{
abc("send ");
return 0;
}
int PASCAL WsControl(void *i1,void *i2,void *i3,void *i4,void *i5,void *i6)
{
abc("WsControl");
return 0;
}
int PASCAL 	NPLoadNameSpaces (void *i1,void *i2,void *i3)
{
abc("NPLoadNameSpaces ");
return 0;
}

All the functions currently return a 0 with one exception, DllMain()retuns a 1.Wait just a little longer and we'll start doing some fun stuff with this! When every thing is back to normal we'll add new code to yyz.c and build the project again. Copy the new .dll to windows\system. Run Netscape again.

Examine the file z.txt.

z.txt

DllMain
WSAStartup
WSACleanup
DllMain

If you've read our tutorial on WinSock programming, or have some experience with it, you'll know that the first function which must be called by any WinSock compliant program is WSAStartup with two parameters. The first parameter is the version number of the socket and the second is the address of a structure which looks like WSAData.

WSAStartup() must succed otherwise all subsequent calls to other functions fail and Netscape will refuse to load.

Since Navigator has been written by conscientious programmes, every time there is a WSAStartup called, the last function to be called is WSACleanup. So we must fake that function too. We don't have to do anything in this function but simply return 0.

Replace the brain dead WSAStartup() we'd given you earlier with this one. Using this we will be able to discover the parameters Navigator passes to WSAStartup().

int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
	sprintf(aa,"wVersionRequired ...%u....%x",wVersionRequired,wVersionRequired);
	abc(aa);
	abc("WSAStartup");
	return 0;
}

Build the .dll as before and then copy it to the windows\system directory, overwriting the earlier version. Delete the file z.txt from the root. These steps should be repeated after every modification, otherwise the change will not be reflected in our program output.

Now load on Netscape and brace for errors.

Check the z.txt output file.

z.txt

DllMain
wVersionRequired ...257....101
WSAStartup
WSACleanup
DllMain

As you noticed, Netscape throws the same error as before. In fact, unless we correctly fill up the structure WSADATA, Netscape will refuse to work. Netscape needs the information in the structure WSAData to correctly initialize its own variables and structures and for subsequent WinSock (WSOCK32.DLL) function calls to succeed. So we have to do what the original WSAStartup does, that is, initialise all the members in WSAData and return a 0. Most functions we'll be writing return 0 for success and some other predefind number to indicate specific errors, so unless otherwise told assume that you must return a 0.

The members of the structure and the values assigned to them are shown below.

int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
	sprintf(aa,"wVersionRequired ...%u....%x",wVersionRequired,wVersionRequired);
	abc(aa);

	lpWSAData->wVersion=257;
	lpWSAData->wHighVersion=257;
	strcpy(lpWSAData->szDescription,"Microsoft Windows Sockets Version 1.1.");
	strcpy(lpWSAData->szSystemStatus,"Running on Windows 95.");
	lpWSAData->iMaxSockets=256;
	lpWSAData->iMaxUdpDg=65467;
	lpWSAData->lpVendorInfo=0;

	abc("WSAStartup");
	return 0;
}

Run Netscape again.

It still hangs

Salvage what's left of your machine and read z.txt.

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
socket 
WSAAsyncSelect
setsockopt 
ioctlsocket 
inet_addr 
connect 
send 
send 
send 

All we've done so far is discover which functions are being called by Netscape and in which order.

Right after WSAStartup, Netscape calls the function htons. This is a function which converts Little Endian numbers to Big Endian numbers. What's all this about little ends and big ends you ask. Well maybe what we need here is an example.

If I store the number 300 in memory using an Intel based machine, that number is stored internally in memory as two bytes of data (1 byte max. = 256). So this number will be stored as 44 and 1 i.e. 44*1 + 1*256 = 300 and this storage scheme is called little endian. On Motorola based machines however, the number 300 is stored upside down as 1 and 44 i.e. 1*256 + 44*1=300. This is little endian. Since each processor is aware of it's own storage scheme there is absolutely no confusion while these numbers are used within the boundaries of your computer. However, when these very same numbers are transferred across the Internet, chaos reigns !

If I transfer the number 300 from a Motorola machine, for example a Mac, to an Intel based machine, the number is stored the opposite way. So 300 will not be stored as 1*256 + 44*1=300, instead it will be stored as 1*1 + 44*256 = 11265 !! A tad off the mark.

Since some standard had to be found the good people decided that the non-Intel standard should be followed and named that standard the Network Byte Order. So all numbers used in the TCP/IP headers must be converted to the Network Byte Order using htons.

Here htons is being used by Netscape to convert the port number 80 (HTTP) to its Big Endian avatar. Ports are explained in great detail in our WinSock tutorial and so I wouldn't repeat the explanation again.

The htons being called is our htons, so we can return a 0. Since Netscape trusts us completely, it doesn't object at all.

To check, the parameters Netscape passed to htons(),we replace the earlier function with the following one.

u_short PASCAL FAR htons (u_short hostshort)
{
	abc("htons ");
	sprintf(aa,"htons....%u",hostshort);
	abc(aa);
	return 0;
}

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
WSAAsyncSelect
setsockopt 
ioctlsocket 
inet_addr 
connect 
send 
send 
send 

Right after htons, Netscape calls the function socket and passes it three parameters. A socket is a hypothetical entity created simply to make programming easier. You can think of it as a file handle like fopen, except that in fopen you are creating a file, with a socket you just define it's characteristics. This one function was created so that it could be used over several different types of networks like DecNet, ApleTalk etc. So the first parameter, AF_INET, specifies that this socket will be used over the Internet. The second parameter is SOCK_STREAM and tells the WinSock that the protocol being used is TCP/IP, which is a stream based protocol (more on that later). The last parameter is usually a 0, but we can say 6 which means TCP/IP. Our socket function returns a 0 which stands for the number of the socket created since several sockets can be created at the same time (Netscape's default is 4). From now on, all dealings with this socket will be made using its number. If we had returned a -1, Netscape would have understood that an error had occured.

In our next programme we check the values that are passed to the socket function.The function socket() has been explained in great detail in our WinSOck tutorial so there's no point in me wearing my nails down here.

SOCKET PASCAL FAR socket (int af, int type, int protocol)
{
	abc("socket ");
	sprintf(aa,"af=%d..type=%d..protocol=%d",af,type,protocol);
	abc(aa);
	return 0;
}

The value are dumped to z.txt.

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons...80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
setsockopt 
ioctlsocket 
inet_addr 
connect 
send 
send 
send 
send 
send

2 is the actual value of the #define AF_INET, the second number is SOCK_STREAM and the last number is the value of TCP/IP

After Netscape finishes of with the socket function, it calls a function named WSAAsyncSelect. Now the problem with the Internet is that you never know how long something will take to download. It may take an hour or it may appear instantaneously (fat chance !). But we can't have poor ol' Netscape hanging around counting it's toes waiting for the file to arrive, it should be free to cater to the users demands. One way of accomplishing this is to create a thread and let that wait at the COM port for the information. Another way is to use WSAAsyncSelect. The reason Netscape chooses to use WSAAsyncSelect instead of threads is because threads are not possible under Windows 3.1 and Netscape's browser must be portable across operating systems.

WSAAsyncSelect allows you to free up Netscape to perform other tasks by making the various functions non-blocking, i.e. they'll only call upon Netscape when they have something to give it, like parts of the page being downloaded. We'll explain this mechanism is greater detail later when we handle the code, now is not the time to tackle it.

Add this to find the parameters of WSAAsyncSelect().

int PASCAL FAR WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,long lEvent)
{
	abc("WSAAsyncSelect");
	sprintf(aa,"WSAAsyncSelect - wMsg = %u...lEvent=%ld",wMsg,lEvent);
	abc(aa);
	return 0;
}

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
WSAAsyncSelect - wMsg = 53615...lEvent=63
setsockopt 
ioctlsocket 
inet_addr 
connect 
send 
send 
send 
send 

The next two functions called by Netscape are setsockopt and iocltsocket. These functions change the nature of the TCP/IP headers and make them perform certain actions. Since Netscape is a commercial product, it has to make sure that every thing is in place. In real life, programmers have to do a lot more work than we've done to make our WinSock tutorial. setsockopt() is not necessary but it helps fine tune the socket connection. The optname could be SO_LINGER (which changes the sockets closing behaviour), SO_BROADCAST and so on.This stuff is too complicated to get into right now, but once you've read through our Internet programming tutorials these functions will seem easy by comparison.

int PASCAL FAR setsockopt (SOCKET s, int level, int optname,const char FAR * optval, int optlen)
{
	abc("setsockopt ");
	sprintf(aa,"level=%d..optname=%d..optval=%s.....optlen=%d",level,optname,optval,optlen);
	abc(aa);
	return 0;
}

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
WSAAsyncSelect - wMsg = 53615...lEvent=63
setsockopt 
level=65535..optname=128..optval=.....optlen=8
ioctlsocket 
inet_addr 
connect 
send 
send 
send 

int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp)
{
	abc("ioctlsocket ");
	sprintf(aa,"cmd = %lu...*argp=%ld",cmd,*argp);
	abc(aa);
	return 0;
}

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
WSAAsyncSelect - wMsg = 53615...lEvent=63
setsockopt 
ioctlsocket 
cmd = 2147772030...*argp=1
inet_addr 
connect 
send 
send 
send 

After these two, Netscape calls the function inet_addr. Inet_addr converts IP address from their dotted decimal format (e.g. 202.54.1.18) to their actual value as a four byte long number. The dotted decimal notation is used simply to make remembering IP address a little easier. In this format, each number shown represents a byte in the long which makes up a IP address. So 202.54.1.18 is actually IP address 202*(2^24) + 54*(2^16) + 1*(2^8) + 18*(2^0) = ********. As usual, instead of returning a real value, we simply tell Netscape that the answer is 0.

Add the code given below to the .dll file. Now we'll be able to see the IP address we typed in as the default page in Netscape. If we'd given a name like www.neca.com, then a couple more functions would have been called to resolve the name to it's IP address.

unsigned long PASCAL FAR inet_addr (const char FAR * cp)
{
	abc("inet_addr ");
	sprintf(aa,"cp..%s",cp);
	abc(aa);
	return 0;
}

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
WSAAsyncSelect - wMsg = 53615...lEvent=63
setsockopt 
ioctlsocket 
inet_addr 
cp..206.103.13.130
connect 
send 
send 
send 
send 

206.103.13.130 is the IP address which we have given to Netscape as the default page.

Inet_addr is actually called as Netscape fills up three members of a structure called sockaddr_in. This structure holds important data about the connection.

After initialising the members of the structure sockaddr_in, Netscape calls the function connect from within our .dll and passes it three parameters. The first parameter is the handle to the socket that's just been created which in our case is 0 because we returned 0 while in socket. Next is a pointer to the structure sockaddr_in and then its length. If you've read our WinSock tutorial, you'll know what these members mean. We know that connect() is used to connect to a remote site; it's time to discover the parameters passed to it. Add these lines to your project .

int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen)
{
abc("connect ");
sprintf(aa,"socket..%ld..namelen...%d",s,namelen);
abc(aa);
sprintf(aa, "sa_family = %d.. sa_data = %s..%d. .namelen=%d..", name->sa_family,
 name->sa_data , name->sa_data[0], namelen );
abc(aa);
return 0;
}

The output file reveals the parameters.

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
WSAAsyncSelect - wMsg = 53615...lEvent=63
setsockopt 
ioctlsocket 
inet_addr 
cp..206.103.13.130
connect 
socket..0..namelen...16
sa_family=2..sa_data = P..80..namelen=16..
send 
send 
send 
send 

In connect, we return a 0 to Netscape, any other value and Netscape will display an error. If you want, you can place a messagebox here just before the return statement and when you run Netscape you'll see the messagebox and the line at the bottom will read something like 'Connecting to host. ….Waiting for reply'. Only when you click on OK in the MessageBox will the program proceed.

Since we've returned a 0 while in connect, Netscape will assume it's connected to a site on the Internet, even though it hasn't done any such thing. Since it assumes that all's well, it will now call the send function to send a request for a file. The HTTP command that has to be sent is GET / along with some aditional information like HTTP version numbers etc. The forward slash after GET means that the server should return the default file to Netscape. Send takes four parameters. The first is a handle to the socket, the next the name of the array which contains the HTTP command , the third is the length of the array and the last one is the flag which is always 0.

int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags)
{
	abc("send ");
	sprintf(aa,"buf=%s..len=%d..flags=%d",buf,len,flags);
	abc(aa);
	return len;
}

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
WSAAsyncSelect - wMsg = 53615...lEvent=63
setsockopt 
ioctlsocket 
inet_addr 
cp..206.103.13.130
connect 
sa_family=2..sa_data = P..80..namelen=16..
send 
buf=GET / HTTP/1.0
If-Modified-Since: Thursday, 22-May-97 15:11:09 GMT; length=4031
Connection: Keep-Alive
User-Agent: Mozilla/3.0 (Win95; I)
Host: 206.103.13.130
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*

..len=232..flags=0
recv 
closesocket 
WSACleanup
DllMain

To receive a page from the 'server', we have to first convince Netscape that it's send() succeeded. We've been returning a zero from send() and this implies that the send failed. So now, we have to return the length of the data send()'s shot across and only then will Netscape realise that it's function call has succeeded.

It was because the send() was 'failing' again and again that Netscape kept calling it repeatedly in the hope that it would succeed. Send is called and it works too. If it hadn't, you wouldn't have seen recv() being called.

As send cannot return a 0, we have returned 232 which is the size of the header that Netscape wants to send and this confirms that all the data has been sent.

Netscape thinks that it really has sent some bytes across the Internet asking for a file, so it calls the recv function to receive this file as it comes. Recv is called with four parameters. The handle to the socket, a pointer to an address in memory and the size of that memory along with a 0 for flags. So what we do in our recv is copy the bytes " Fooled You " with a \r and a \n to the memory location specified and return the number of bytes received i.e. 14. When Netscape sees the 14 it will think that it just got 14 bytes of the Internet and it will store these away for future reference. It will also call the recv function again. It does this because we didn't give a header for the 'file' we returned and so Netscape doesn't know the size of the file. We could have put in a little more effort and done that, but what the hell ! Since the file size is unknown, Netscape calls the recv function a second time to check if there is more to display. Since we don't have anything else to give it, we set up an if statement and the second time round return a 0 which means 'all over'. So Netscape will display Fooled You on the screen and tidy up by calling closesocket and WSCleanup.

int cnt;
int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags)
{
	cnt++;
	abc("recv ");
	sprintf(aa,"buf=%s..len=%d..flags=%d..cnt=%d",buf,len,flags,cnt);
	abc(aa);
	if (cnt == 1)
	{
		memcpy(buf," Fooled You \r\n",14);
		return 14;
	}
	else
		return 0;
}

We need a counter here to check if the function recv() is called more than once. So we declare cnt in the beginning of the program.

z.txt

DllMain
WSAStartup
wVersionRequired ...257....101
htons 
htons....80
socket 
af=2..type=1..protocol=6
WSAAsyncSelect
WSAAsyncSelect - wMsg = 53332...lEvent=63
setsockopt 
ioctlsocket 
inet_addr 
cp..206.103.13.130
connect 
sa_family=2..sa_data = P..80..namelen=16..
send 
buf=GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/3.0 (Win95; I)
Host: 206.103.13.130
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
..len=166..flags=0
recv 
buf=¨..len=260..flags=0..cnt=1
recv 
buf=..len=31744..flags=0..cnt=2
closesocket 
WSACleanup
DllMain

Let say , we set our default page to the homepage of the site www.mafatlal.co.in. Netscape then sends some information like the date of the homepage it had last downloaded the same site, the name of the browser (Mozilla) etc. The reason Netscape keeps a track of the date stamp on a file is because if the dates of the file in the cache and the file of the server match, then Netscape quickly retrieves that page from cache instead of downloading it.

The line Connection: Keep-Alive tells the server not to disconnect after every file download, the default when using the HTTP protocol. Host is simply the IP address of the server we have connected to and Accepted informs the server about the mime type's we accept.

This is how servers keep statistics about the types of browsers on the market because as shown above, servers reveal their identity each time they each time they ask for a file from the server.

If you've been following our step by step approach properly, the final .cpp file should a lot like this...

yyz.c

#include <windows.h>
#include <stdio.h>
void abc(char * p)
{
 FILE *fp=fopen("c:\\z.txt","a+");
fprintf(fp,"%s\n",p);
fclose(fp);
}
int cnt=0;
char aa[1000];
BOOL WINAPI DllMain( HINSTANCE  hinstDLL,DWORD  fdwReason,LPVOID  lpvReserved)
{
abc("DllMain");
return 1;
}
SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr,int FAR *addrlen)
{
abc("accept ");
return 0;
}
int PASCAL FAR closesocket (SOCKET s)
{
abc("closesocket ");
return 0;
}
int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen)
{
abc("connect ");
sprintf(aa,"sa_family=%d..sa_data = %s..%d..namelen=%d..",name->sa_family,name->sa_data,name->sa_data[0],namelen);
abc(aa);
return 0;
}
int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp)
{
abc("ioctlsocket ");
return 0;
}
int PASCAL FAR getpeername (SOCKET s, struct sockaddr FAR *name,
                            int FAR * namelen)
{
abc("getpeername ");
return 0;
}
int PASCAL FAR getsockname (SOCKET s, struct sockaddr FAR *name,
                            int FAR * namelen)
{
abc("getsockname ");
return 0;
}
int PASCAL FAR getsockopt (SOCKET s, int level, int optname,
                           char FAR * optval, int FAR *optlen)
{
abc("getsockopt ");
return 0;
}
u_long PASCAL FAR htonl (u_long hostlong)
{
abc("htonl ");
return 0;
}
char FAR * PASCAL FAR inet_ntoa (struct in_addr in)
{
abc("inet_ntoa ");
return 0;
}
int PASCAL FAR listen (SOCKET s, int backlog)
{
abc("listen ");
return 0;
}
u_long PASCAL FAR ntohl (u_long netlong)
{
abc("ntohl ");
return 0;
}
u_short PASCAL FAR ntohs (u_short netshort)
{
abc("ntohs ");
return 0;
}
int PASCAL FAR recvfrom (SOCKET s, char FAR * buf, int len, int flags,
                         struct sockaddr FAR *from, int FAR * fromlen)
{
abc("recvfrom ");
return 0;
}
int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
                       fd_set FAR *exceptfds, const struct timeval FAR *timeout)
{
abc("select ");
return 0;
}
int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags,
                       const struct sockaddr FAR *to, int tolen)
{
abc("sendto ");
return 0;
}
int PASCAL FAR shutdown (SOCKET s, int how)
{
abc("shutdown ");
return 0;
}
struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr,
                               int len, int type)
{
abc("gethostbyaddr");
return 0;
}
struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name)
{
abc("gethostbyname");
return 0;
}
int PASCAL FAR gethostname (char FAR * name, int namelen)
{
abc("gethostname ");
return 0;
}
struct servent FAR * PASCAL FAR getservbyport(int port, const char FAR * proto)
{
abc("getservbyport");
return 0;
}
struct servent FAR * PASCAL FAR getservbyname(const char FAR * name,
                                              const char FAR * proto)
{
abc("getservbyname");
return 0;
}
struct protoent FAR * PASCAL FAR getprotobynumber(int proto)
{
abc("getprotobynumber");
return 0;
}
struct protoent FAR * PASCAL FAR getprotobyname(const char FAR * name)
{
abc("getprotobyname");
return 0;
}
int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
sprintf(aa,"wVersionRequired ...%u....%x",wVersionRequired,wVersionRequired);
abc(aa);
abc("WSAStartup");
lpWSAData->wVersion=257;
lpWSAData->wHighVersion=257;
strcpy(lpWSAData->szDescription,"Microsoft Windows Sockets Version 1.1.");
strcpy(lpWSAData->szSystemStatus,"Running on Windows 95.");
lpWSAData->iMaxSockets=256;
lpWSAData->iMaxUdpDg=65467;
lpWSAData->lpVendorInfo=0;
return 0;


return 0;
}
int PASCAL FAR WSACleanup(void)
{
abc("WSACleanup");
return 0;
}
void PASCAL FAR WSASetLastError(int iError)
{
abc("WSASetLastError");
}
int PASCAL FAR WSAGetLastError(void)
{
abc("WSAGetLastError");
return 0;
}
BOOL PASCAL FAR WSAIsBlocking(void)
{
abc("WSAIsBlocking");
return 0;
}
int PASCAL FAR WSAUnhookBlockingHook(void)
{
abc("WSAUnhookBlockingHook");
return 0;
}
FARPROC PASCAL FAR WSASetBlockingHook(FARPROC lpBlockFunc)
{
abc("WSASetBlockingHook");
return 0;
}
int PASCAL FAR WSACancelBlockingCall(void)
{
abc("WSACancelBlockingCall");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetServByName(HWND hWnd, u_int wMsg,
   const char FAR * name,const char FAR * proto,
                                        char FAR * buf, int buflen)
{
abc("WSAAsyncGetServByName");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetServByPort(HWND hWnd, u_int wMsg, int port,
   const char FAR * proto, char FAR * buf,int buflen)
{
abc("WSAAsyncGetServByPort");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetProtoByName(HWND hWnd, u_int wMsg,
   const char FAR * name, char FAR * buf,int buflen)
{
abc("WSAAsyncGetProtoByName");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetProtoByNumber(HWND hWnd, u_int wMsg,
    int number, char FAR * buf,int buflen)
{
abc("WSAAsyncGetProtoByNumber");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetHostByName(HWND hWnd, u_int wMsg,
  const char FAR * name, char FAR * buf,int buflen)
{
abc("WSAAsyncGetHostByName");
return 0;
}
HANDLE PASCAL FAR WSAAsyncGetHostByAddr(HWND hWnd, u_int wMsg,
  const char FAR * addr, int len, int type,char FAR * buf, int buflen)
{
abc("WSAAsyncGetHostByAddr");
return 0;
}
int PASCAL FAR WSACancelAsyncRequest(HANDLE hAsyncTaskHandle)
{
abc("WSACancelAsyncRequest");
return 0;
}
int PASCAL FAR WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,
                               long lEvent)
{
abc("WSAAsyncSelect");
sprintf(aa,"WSAAsyncSelect - wMsg = %u...lEvent=%ld",wMsg,lEvent);
abc(aa);
return 0;
}
int PASCAL 	__WSAFDIsSet ()
{
abc("__WSAFDIsSet ");
return 0;
}
int PASCAL 	s_perror ()
{
abc("s_perror ");
return 0;
}
int PASCAL 	TransmitFile ()
{
abc("TransmitFile ");
return 0;
}
int PASCAL 	EnumProtocolsA ()
{
abc("EnumProtocolsA ");
return 0;
}
int PASCAL 	EnumProtocolsW ()
{
abc("EnumProtocolsW ");
return 0;
}
int PASCAL 	GetAddressByNameA ()
{
abc("GetAddressByNameA ");
return 0;
}
int PASCAL 	GetAddressByNameW ()
{
abc("GetAddressByNameW ");
return 0;
}
int PASCAL 	GetNameByTypeA  ()
{
abc("GetNameByTypeA  ");
return 0;
}
int PASCAL 	GetNameByTypeW  ()
{
abc("GetNameByTypeW  ");
return 0;
}
int PASCAL 	GetServiceA ()
{
abc("GetServiceA ");
return 0;
}
int PASCAL 	GetServiceW ()
{
abc("GetServiceW ");
return 0;
}
int PASCAL 	GetTypeByNameA ()
{
abc("GetTypeByNameA ");
return 0;
}
int PASCAL 	GetTypeByNameW ()
{
abc("GetTypeByNameW ");
return 0;
}
int PASCAL 	SetServiceA ()
{
abc("SetServiceA ");
return 0;
}
int PASCAL 	SetServiceW  ()
{
abc("SetServiceW  ");
return 0;
}
int PASCAL 	dn_expand ()
{
abc("dn_expand ");
return 0;
}
int PASCAL 	inet_network ()
{
abc("inet_network ");
return 0;
}
int PASCAL 	rexec ()
{
abc("rexec ");
return 0;
}
int PASCAL 	rresvport ()
{
abc("rresvport ");
return 0;
}
int PASCAL WSARecvEx ()
{
abc("WSARecvEx ");
return 0;
}
unsigned long PASCAL FAR inet_addr (const char FAR * cp)
{
abc("inet_addr ");
sprintf(aa,"cp..%s",cp);
abc(aa);
return 0;
}
u_short PASCAL FAR htons (u_short hostshort)
{
abc("htons ");
sprintf(aa,"htons....%u",hostshort);
abc(aa);
return 80;
}
SOCKET PASCAL FAR socket (int af, int type, int protocol)
{
abc("socket ");
sprintf(aa,"af=%d..type=%d..protocol=%d",af,type,protocol);
abc(aa);
return 0;
}
int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen)
{
abc("bind ");
return 0;
}
int PASCAL FAR setsockopt (SOCKET s, int level, int optname,
                           const char FAR * optval, int optlen)
{
abc("setsockopt ");
return 0;
}
int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags)
{
cnt++;
abc("recv ");
sprintf(aa,"buf=%s..len=%d..flags=%d..cnt=%d",buf,len,flags,cnt);
abc(aa);

if (cnt == 1)
{
memcpy(buf," Fooled You \r\n",14);
return 14;
}
else
return 0;

}
int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags)
{

abc("send ");
sprintf(aa,"buf=%s..len=%d..flags=%d",buf,len,flags);
abc(aa);
return len;
}
int PASCAL WsControl(void *i1,void *i2,void *i3,void *i4,void *i5,void *i6)
{
abc("WsControl");
return 0;
}
int PASCAL 	NPLoadNameSpaces (void *i1,void *i2,void *i3)
{
abc("NPLoadNameSpaces ");
return 0;
}


To Block or not to Block...

Seems pretty simple huh ?! Here comes the fun part. How exactly does connect manage to act non-blocking although it is blocking in nature ? Through some tricky programming of course ! Check out connect() function and you'll get what we mean. Connect() is a function which actually establishes a connection between two computers on the Internet. When connect() is called, it establishes a TCP/IP connection through the Three Way Handshake. The actual bytes along with some programs and explainations can be found in our TCP/IP tutorial. Since connect() is a function which interacts with the big bad world of cyberspace, we really don't know how long it'll take to finish it job (netlag and all that jazz). Some way has to be found to make it non-blocking. In our simple connect emulator, we just returned a zero , but in actuallity, the fucntion returns a -1 to Netscape.

Before calling connect, Netscape first calls WSAAsyncselect and passes it two parameters. The first is the socket number which in our case is 0 and the second is the number 63. Now this number is a collection of flags which tell WSAAsyncselect that all the function, recv, send, connect etc., are to be non-blocking.

The parameters that WSAAsyncSelect() is called with includes the handle of the window that is supposed to deal with the message (wMsg) when the even (lEvent) occurs. The value of wMsg should be greater than WM_USER, which is 1024. All values less than and equal to 1024 are reserved by Microsoft. When the events occur, for the socket (s), we want the message to be sent to a window whose handle is given as hWnd. In WinSock programming (take a peek at our WinSock Tutorial which explains this in greater detail), events can be things like FD_ACCEPT,FD_READ and so on. Multiple events can be trapped by ORing then together.

What we told you earlier about setsockopt() applies here as well. ioctlsocket() is a function that allows us to set a socket as blocking or nonblocking. It also lets us check the amount of data waiting in the buffer.

The socket can be blocking or nonblocking. The commands that get affected due to this behaviour of socket are accept(),connect(),send(),recv(), and recvfrom().To understand what is blocking we have to take an example. In cricket, when two bastmans are at the crease, a new bastman cannot come in to bat. Only thing he can do is sit in pavilion and pray to god. Only when one of the bastman gets out,he can go and bat. This waiting is blocking. The flags - 63 - tell us that Netscape wants nonblocking functions.

When Netscape calls the connect function , instead of returning a 0, we return SOCKET_ERROR or -1. This means that an error has occurred during the connect, even though no such thing has happened. On seeing this message, Netscape hurries over to wsock32.dll and calls WSAGetLastError to try and find out what exactly happened. In WSAGetLastError we're supposed to return WSAEWOULDBLOCK. This is our way of telling Netscape that no error has occurred during the connect, we've just made the connect blocking so you can now leave connect alone and do something else. Netscape understands and starts calling the function select approximately every 10 seconds. Select is a function which tells Netscape how many sockets are open and what is the status of those sockets. After around 3 selects, done to simulate net lag, we use SendMessage to send a message to Netscape window with the information FD_CONNECT (Actually this long is 2 ints, one is for the error, in this case there is no error and the second is why are you calling Netscape.) which tells Netscape that it's all right now to call connect. So Netscape rushes over to connect and calls it once more. This time we return a 0 for success telling Netscape that we're connected.

int cnt1;
int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen)
{
	cnt1++;
abc("connect ");
sprintf(aa,"sa_family=%d..sa_data = %s..%d..namelen=%d..",name->sa_family,name->sa_data,name->sa_data[0],namelen);
abc(aa);
if (cnt1==1)
return SOCKET_ERROR;
else
return 0;
}

int PASCAL FAR WSAGetLastError(void)
{
abc("WSAGetLastError");
return WSAEWOULDBLOCK;
}

int sctr;
int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
                       fd_set FAR *exceptfds, const struct timeval FAR *timeout)
{
sctr++;
abc("select ");
if (sctr == 3)
{
SendMessage(ohWnd,owMsg,osock,FD_CONNECT);
abc("After SendMessage");
}
return 0;
}

Pretty much the same thing happens for send and recv too but things are much worse for recv. Recv never knows how long it'll take for some piece of information to reach us. It may be that the server at the other end is occupied or that the lines are really busy, so even recv has to be made non-blocking. Last time around we simply copied ' Fooled You ' at a memory address and told Netscape about it. This time we'll do things the right way.

Netscape first calls recv with a very small number i.e. the 260 bytes which comprise the header. What we're supposed to do is return -1 the first time. As usual Netscape will then call WSAGetLastError which will return WSAEWOULDBLOCK. So Netscape will keep calling select till at last it receives a FD_READ. When it does, it will dash over to recv and give it the address of an array 32k large. Since we've already returned our bogus data, we return a 0 here telling Netscape that it's all over at our end.

A series of functions explaining this stuff are listed below. Browse through them at leisure.

int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags)
{
cnt++;
abc("recv ");
sprintf(aa,"buf=%s..len=%d..flags=%d..cnt=%d",buf,len,flags,cnt);
abc(aa);

if (cnt == 1)
{
abc("recv ..cnt =1");
return SOCKET_ERROR;
}
else if(cnt == 2)
{
abc("recv ..cnt =2");
memcpy(buf,"Hello\r\n",7);
return 7;
}
abc("recv ..cnt =3");		
return 0;
}


int PASCAL FAR WSAGetLastError(void)
{
abc("WSAGetLastError");
return WSAEWOULDBLOCK;
}

int sctr;
int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
                       fd_set FAR *exceptfds, const struct timeval FAR *timeout)
{
sctr++;
abc("select ");
if (sctr == 3)
SendMessage(ohWnd,owMsg,osock,FD_READ);
return 0;
}


As you can see, calling code form the WinSock is much simpler than writing it yourself. There is absolutely nothing wrong with Netscape doing this also, but it doesn't give them the right to call themselves an Internet company.

The jokes on us, pal.

You've now seen how easy it really is to fool Netscape into believing that it received the text 'Fooled You' over the Internet. This is because Netscape makes socket calls instead of using the com port or the Network card . Since it's only window to the world is the WinSock, we can take advantage of this and con Netscape into displaying anything we want. It's absolutely the same with Java or any other programming language or application that uses socket calls.

We're not saying that using sockets is a bad idea. On the contrary using sockets makes Internet programming much simpler. What we are trying to point out is that there is a large difference between programming with sockets and programming with TCP/IP directly. It's only when you've done the latter that you can truly say that you understand what the Internet is all about.

Netscape has no right to call itself an "Internet programming" company. The only ones who can are the ones who write the TCP/IP stacks and WinSocks. Netscape is a great company with a remarkable product and it's done a lot to popularise the Internet, but it's not an Internet company.

Uptil now, you really couldn't say anything as Netscape's marketing men strutted around town, sniggering and whispering to each other "We sure got them fooled !". Now we've given you the chance to use all these .dlls and scream at the Navigator window, "Fooled You !!". The above tutorial is a joint effort of

Mr. Vijay Mukhi
Ms. Sonal Kotecha
Mr. Arsalan Zaidi
Mr. R. D. Parab


Back to the main page


Vijay Mukhi's Computer Institute
VMCI, B-13, Everest Building, Tardeo, Mumbai 400 034, India
Tel : 91-22-496 4335 /6/7/8/9     Fax : 91-22-307 28 59
e-mail : vmukhi@giasbm01.vsnl.net.in
http://www.vijaymukhi.com