/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * 
 * The contents of this file are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * 
 * The Original Code is Mozilla Communicator client code, released
 * March 31, 1998.
 * 
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998-1999
 * the Initial Developer. All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK ***** */

#include <windows.h>
#include <winsock.h>
#include <string.h>

//	Purpose of this file is to implement an intermediate layer to our network
//		services, the winsock.
//	This intermediate layer will be able to function with and without a working
//		winsock being present.
//	The attempt to activate the winsock happens as would normally be expected,
//              through the calling application's entry point to us, WSAStartup.
//	Name of the winsock we would like to load.
//	Diffs between OSs, Win32s is out in the cold if running 32 bits unless
//		they also have a winsock name wsock32.dll.
#ifndef _WIN32
#define SZWINSOCK "winsock.dll"
#else
#define SZWINSOCK "wsock32.dll"
#endif

//	Here is the enumeration for the winsock functions we have currently
//		overridden (needed to run).  Add more when needed.
//	We use these to access proc addresses, and to hold a table of strings
//		to obtain the proc addresses.
enum SockProc	{
	sp_WSAAsyncGetHostByName = 0,
	sp_WSAAsyncSelect,
	sp_WSACleanup,
	sp_WSAGetLastError,
	sp_WSASetLastError,
	sp_WSAStartup,
	sp___WSAFDIsSet,
	sp_accept,
	sp_bind,
	sp_closesocket,
	sp_connect,
	sp_gethostbyname,
	sp_gethostbyaddr,
	sp_gethostname,
	sp_getpeername,
	sp_getsockname,
	sp_getsockopt,
	sp_getprotobyname,
	sp_htonl,
	sp_htons,
	sp_inet_addr,
	sp_ioctlsocket,
	sp_listen,
	sp_ntohl,
	sp_ntohs,
	sp_recv,
	sp_select,
	sp_send,
	sp_setsockopt,
	sp_shutdown,
	sp_socket,
	sp_inet_ntoa,

	sp_MaxProcs	//	Total count.
};

//	Array of function names used in GetProcAddress to fill in our
//		proc array when needed.
//	This array must match the enumerations exactly.
char *spName[(int)sp_MaxProcs] =	{
        "WSAAsyncGetHostByName",
        "WSAAsyncSelect",
        "WSACleanup",
        "WSAGetLastError",
        "WSASetLastError",
        "WSAStartup",
        "__WSAFDIsSet",
        "accept",
        "bind",
        "closesocket",
        "connect",
        "gethostbyname",
        "gethostbyaddr",
        "gethostname",
        "getpeername",
        "getsockname",
        "getsockopt",
        "getprotobyname",
        "htonl",
        "htons",
        "inet_addr",
        "ioctlsocket",
        "listen",
        "ntohl",
        "ntohs",
        "recv",
        "select",
        "send",
        "setsockopt",
        "shutdown",
        "socket",
        "inet_ntoa"
};

//	Array of proc addresses to the winsock functions.
//      These can be NULL, indicating their absence (as in the case we couldn't
//              load the winsock.dll or one of the functions wasn't loaded).
//	The procs assigned in must corellate with the enumerations exactly.
FARPROC spArray[(int)sp_MaxProcs];

//	Typedef all the different types of functions that we must cast the
//		procs to in order to call without the compiler barfing.
//	Prefix is always sp.
//	Retval is next, spelled out.
//	Parameters in their order are next, spelled out.
typedef int (PASCAL FAR *sp_int_WORD_LPWSADATA)(WORD, LPWSADATA);
typedef int (PASCAL FAR *sp_int_void)(void);
typedef HANDLE (PASCAL FAR *sp_HANDLE_HWND_uint_ccharFARp_charFARp_int)(HWND, unsigned int, const char FAR *, char FAR *, int);
typedef int (PASCAL FAR *sp_int_SOCKET_HWND_uint_long)(SOCKET, HWND, unsigned int, long);
typedef void (PASCAL FAR *sp_void_int)(int);
typedef int (PASCAL FAR *sp_int_SOCKET_fdsetFARp)(SOCKET, fd_set FAR *);
typedef SOCKET(PASCAL FAR *sp_SOCKET_SOCKET_sockaddrFARp_intFARp)(SOCKET, struct sockaddr FAR *, int FAR *);
typedef int (PASCAL FAR *sp_int_SOCKET_csockaddrFARp_int)(SOCKET, const struct sockaddr FAR *, int);
typedef int (PASCAL FAR *sp_int_SOCKET)(SOCKET);
typedef struct hostent FAR *(PASCAL FAR *sp_hostentFARp_ccharFARp)(const char FAR *);
typedef struct hostent FAR *(PASCAL FAR *sp_hostentFARp_ccharFARp_int_int)(const char FAR *, int, int);
typedef int (PASCAL FAR *sp_int_charFARp_int)(char FAR *, int);
typedef int (PASCAL FAR *sp_int_SOCKET_sockaddrFARp_intFARp)(SOCKET, struct sockaddr FAR *, int FAR *);
typedef int (PASCAL FAR *sp_int_SOCKET_int_int_charFARp_intFARp)(SOCKET, int, int, char FAR *, int FAR *);
typedef u_long (PASCAL FAR *sp_ulong_ulong)(u_long);
typedef u_short (PASCAL FAR *sp_ushort_ushort)(u_short);
typedef unsigned long (PASCAL FAR *sp_ulong_ccharFARp)(const char FAR *);
typedef int (PASCAL FAR *sp_int_SOCKET_long_ulongFARp)(SOCKET, long, u_long FAR *);
typedef int (PASCAL FAR *sp_int_SOCKET_int)(SOCKET, int);
typedef int (PASCAL FAR *sp_int_SOCKET_charFARp_int_int)(SOCKET, char FAR *, int, int);
typedef int (PASCAL FAR *sp_int_int_fdsetFARp_fdsetFARp_fdsetFARp_ctimevalFARp)(int,fd_set FAR *,fd_set FAR *,fd_set FAR *,const struct timeval FAR*);
typedef int (PASCAL FAR *sp_int_SOCKET_ccharFARp_int_int)(SOCKET, const char FAR *, int, int);
typedef int (PASCAL FAR *sp_int_SOCKET_int_int_ccharFARp_int)(SOCKET, int, int, const char FAR *, int);
typedef SOCKET (PASCAL FAR *sp_SOCKET_int_int_int)(int, int, int);
typedef char FAR * (PASCAL FAR *sp_charFARp_in_addr)(struct in_addr in);
typedef struct protoent FAR * (PASCAL FAR *sp_protoentFARcchar)(const char FAR *);

//	Handle to the winsock, if loaded.
HINSTANCE hWinsock = NULL;

#ifndef _WIN32
//	Last error code for the winsock.
int ispError = 0;
#endif


BOOL IsWinsockLoaded (int sp)
{
	if (hWinsock == NULL)
	{
		WSADATA wsaData;
		WSAStartup(0x0101, &wsaData);
	}
//	Quick macro to tell if the winsock has actually loaded for a particular
//		function.
//  Debug version is a little more strict to make sure you get the names right.
#ifdef DEBUG
	return hWinsock != NULL && spArray[(int)(sp)] != NULL;
#else //  A little faster
	return hWinsock != NULL;
#endif
}

//	Here are the functions that we have taken over by not directly linking
//		with the winsock import library or importing through the def file.

/* In win16 we simulate blocking commands as follows.  Prior to issuing the
 * command we make the socket not-blocking (WSAAsyncSelect does that).
 * We then issue the command and see if it would have blocked.	If so, we
 * yield the processor and go to sleep until an event occurs that unblocks
 * us (WSAAsyncSelect allowed us to register what that condition is).  We
 * keep repeating until we do not get a would-block indication when issuing
 * the command.  At that time we unregister the notification condition and
 * return the result of the command to the caller.
 */

//#ifndef _WIN32
#if 0
#define NON_BLOCKING(command,condition,index,type)			      \
    type iret;								      \
    HWND hWndFrame = AfxGetApp()->m_pMainWnd->m_hWnd;			      \
    while (TRUE) {							      \
	if (WSAAsyncSelect(s, hWndFrame, msg_NetActivity, condition)	      \
		== SOCKET_ERROR) {					      \
	    break;							      \
	}								      \
	if(IsWinsockLoaded(index)) {					      \
	    iret=command;						      \
	    if (!(iret==SOCKET_ERROR && WSAGetLastError()==WSAEWOULDBLOCK)) { \
		WSAAsyncSelect(s, hWndFrame, msg_NetActivity, 0);	      \
		return iret;						      \
	    }								      \
	    PR_Yield(); 						      \
	} else {							      \
	    break;							      \
	}								      \
    }
#else
#define NON_BLOCKING(command,condition,index,type)			      \
    if(IsWinsockLoaded(index)) {					      \
	return command; 						      \
    }
#endif

int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData)	{
	//	Our default return value is failure, though we change this regardless.
	int iRetval = WSAVERNOTSUPPORTED;

	//	Before doing anything, clear out our proc array.
	memset(spArray, 0, sizeof(spArray));

	//	attempt to load the real winsock.
	hWinsock = LoadLibrary(SZWINSOCK);
#ifdef _WIN32
	if(hWinsock != NULL)	{
#else
	if(hWinsock > HINSTANCE_ERROR)	{
#endif
		//	Winsock was loaded.
		//	Get the proc addresses for each needed function next.
		int spTraverse;
		for(spTraverse = 0; spTraverse < (int)sp_MaxProcs; spTraverse++)	{
			spArray[spTraverse] = GetProcAddress(hWinsock, spName[spTraverse]);
			if ( NULL == spArray[spTraverse] )
				return iRetval;//	Bad winsock?  Bad function name?
		}

		//	AllRight, attempt to make our first proxied call.
		if(IsWinsockLoaded(sp_WSAStartup))	{
			iRetval = ((sp_int_WORD_LPWSADATA)spArray[sp_WSAStartup])(wVersionRequested, lpWSAData);
		}

		//	If the return value is still an error at this point, we unload the DLL,
		//		so that we can act as though nothing happened and the user
		//		gets no network access.
		if(iRetval != 0)	{
			//	Clear out our proc array.
			memset(spArray, 0, sizeof(spArray));

			//	Free up the winsock.
			FreeLibrary(hWinsock);
			hWinsock = NULL;
		}
	}
#ifndef _WIN32
	else	{
		//	Failed to load.
		//	Set this to NULL so it is clear.
		hWinsock = NULL;
	}
#endif

        //      Check our return value, if it isn't success, then we need to fake
	//		our own winsock implementation.
	if(iRetval != 0)	{
		//	We always return success.
		iRetval = 0;

		//	Fill in the structure.
		//	Return the version requested as the version supported.
		lpWSAData->wVersion = wVersionRequested;
		lpWSAData->wHighVersion = wVersionRequested;

		//	Fill in a discription.
                strcpy(lpWSAData->szDescription, "Mozock DLL internal implementation.");
                strcpy(lpWSAData->szSystemStatus, "Winsock running, allowing no network access.");

		//	Report a nice round number for sockets and datagram sizes.
		lpWSAData->iMaxSockets = 4096;
		lpWSAData->iMaxUdpDg = 4096;

		//	No vendor information.
		lpWSAData->lpVendorInfo = NULL;
	}

	return(iRetval);
}

int PASCAL FAR WSACleanup(void) {
	int iRetval = 0;

	//	Handling normally or internally.
	// When IsWinsockLoaded() is called and hWinsock is NULL, it winds up calling WSAStartup
	// which wedges rpcrt4.dll on win95 with some winsock implementations. Bug: 81359.
	if(hWinsock && IsWinsockLoaded(sp_WSACleanup))	{
		//	Call their cleanup routine.
		//	We could set the return value here, but it is meaning less.
		//	We always return success.
		iRetval = ((sp_int_void)spArray[sp_WSACleanup])();
		//ASSERT(iRetval == 0);
		iRetval = 0;
	}

	//	Wether or not it succeeded, we free off the library here.
	//	Clear out our proc table too.
	memset(spArray, 0, sizeof(spArray));
	if(hWinsock != NULL)	{
		FreeLibrary(hWinsock);
		hWinsock = NULL;
	}

	return(iRetval);
}

HANDLE PASCAL FAR WSAAsyncGetHostByName(HWND hWnd, unsigned int wMsg, const char FAR *name, char FAR *buf, int buflen)	{
	// Normal or shim.
	if(IsWinsockLoaded(sp_WSAAsyncGetHostByName))	{
		return(((sp_HANDLE_HWND_uint_ccharFARp_charFARp_int)spArray[sp_WSAAsyncGetHostByName])(hWnd, wMsg, name, buf, buflen));
	}

	//	Must return error here.
	//	Set our last error value to be that the net is down.
	WSASetLastError(WSAENETDOWN);
	return(NULL);
}

int PASCAL FAR WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent)	{
	//	Normal or shim.
	if(IsWinsockLoaded(sp_WSAAsyncSelect))	{
		return(((sp_int_SOCKET_HWND_uint_long)spArray[sp_WSAAsyncSelect])(s, hWnd, wMsg, lEvent));
	}

	//	Must return error here.
	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR WSAGetLastError(void)	{
	//	See if someone else can handle.
	if(IsWinsockLoaded(sp_WSAGetLastError)) {
		return(((sp_int_void)spArray[sp_WSAGetLastError])());
	}

#ifndef _WIN32
	{
		//	Fake it.
		int iRetval = ispError;
		ispError = 0;
		return(iRetval);
	}
#else
	//	Use default OS handler.
	return(GetLastError());
#endif
}

void PASCAL FAR WSASetLastError(int iError)	{
	//	See if someone else can handle.
	if(IsWinsockLoaded(sp_WSASetLastError)) {
		((sp_void_int)spArray[sp_WSASetLastError])(iError);
		return;
	}

#ifndef _WIN32
	//	Fake it.
	ispError = iError;
	return;
#else
	//	Use default OS handler.
	SetLastError(iError);
	return;
#endif
}

int PASCAL FAR __WSAFDIsSet(SOCKET fd, fd_set FAR *set) {
	int i;

	//	See if someone else will handle.
	if(IsWinsockLoaded(sp___WSAFDIsSet))	{
		return(((sp_int_SOCKET_fdsetFARp)spArray[sp___WSAFDIsSet])(fd, set));
	}

	//	Default implementation.
	i = set->fd_count;
	while (i--)	{
		if (set->fd_array[i] == fd)	{
			return 1;
		}
	}
	return 0;
}

SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen) {
	//	Internally or shim
	NON_BLOCKING(
	    (((sp_SOCKET_SOCKET_sockaddrFARp_intFARp)spArray[sp_accept])(s, addr, addrlen)),
	    FD_ACCEPT, sp_accept, SOCKET);

	//	Fail.
	WSASetLastError(WSAENETDOWN);
	return(INVALID_SOCKET);
}

int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR *name, int namelen)	{
	//	Internally or shim
	if(IsWinsockLoaded(sp_bind))	{
		return(((sp_int_SOCKET_csockaddrFARp_int)spArray[sp_bind])(s, name, namelen));
	}

	//	Fail.
	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR closesocket(SOCKET s)	{
	//	Internally or shim.
	NON_BLOCKING(
	    (((sp_int_SOCKET)spArray[sp_closesocket])(s)),
	    FD_CLOSE, sp_closesocket, int);

	//	Error.
	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR *name, int namelen)	{
	//	Internally or shim.
	if(IsWinsockLoaded(sp_connect)) {
		/* This could block and so it would seem that the NON_BLOCK
		 * macro should be used here.  However it was causing a crash
		 * and so it was decided to allow blocking here instead
		 */
		return (((sp_int_SOCKET_csockaddrFARp_int)spArray[sp_connect])(s, name, namelen));
	}

	//	Err.
	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

struct hostent FAR * PASCAL FAR gethostbyname(const char FAR *name)	{
	if(IsWinsockLoaded(sp_gethostbyname))	{
		return(((sp_hostentFARp_ccharFARp)spArray[sp_gethostbyname])(name));
	}

	WSASetLastError(WSAENETDOWN);
	return(NULL);
}

struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR *addr, int len, int type)	{
	if(IsWinsockLoaded(sp_gethostbyaddr))	{
		return(((sp_hostentFARp_ccharFARp_int_int)spArray[sp_gethostbyaddr])(addr, len, type));
	}

	WSASetLastError(WSAENETDOWN);
	return(NULL);
}

int PASCAL FAR gethostname(char FAR *name, int namelen) {
	if(IsWinsockLoaded(sp_gethostname))	{
		return(((sp_int_charFARp_int)spArray[sp_gethostname])(name, namelen));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR getpeername(SOCKET s, struct sockaddr FAR *name, int FAR *namelen)	{
	if(IsWinsockLoaded(sp_getpeername))	{
		return(((sp_int_SOCKET_sockaddrFARp_intFARp)spArray[sp_getpeername])(s, name, namelen));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR getsockname(SOCKET s, struct sockaddr FAR *name, int FAR *namelen)	{
	if(IsWinsockLoaded(sp_getsockname))	{
		return(((sp_int_SOCKET_sockaddrFARp_intFARp)spArray[sp_getsockname])(s, name, namelen));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR getsockopt(SOCKET s, int level, int optname, char FAR *optval, int FAR *optlen)	{
	if(IsWinsockLoaded(sp_getsockopt))	{
		return(((sp_int_SOCKET_int_int_charFARp_intFARp)spArray[sp_getsockopt])(s, level, optname, optval, optlen));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

struct protoent FAR * PASCAL getprotobyname(const char FAR * name) {
	if(IsWinsockLoaded(sp_getprotobyname))	{
		return(((sp_protoentFARcchar)spArray[sp_getprotobyname])(name));
	}

	WSASetLastError(WSAENETDOWN);
	return NULL;
}

u_long PASCAL FAR htonl(u_long hostlong)	{
	if(IsWinsockLoaded(sp_htonl))	{
		return(((sp_ulong_ulong)spArray[sp_htonl])(hostlong));
	}

#ifndef _WIN32
	return
	    (((hostlong&0xff)<<24) + ((hostlong&0xff00)<<8) +
	     ((hostlong&0xff0000)>>8) + ((hostlong&0xff000000)>>24));

#else
	//	Just return what was passed in.
	return(hostlong);
#endif
}

u_short PASCAL FAR htons(u_short hostshort)	{
	if(IsWinsockLoaded(sp_htons))	{
		return(((sp_ushort_ushort)spArray[sp_htons])(hostshort));
	}

#ifndef _WIN32
	return (((hostshort&0xff)<<8) + ((hostshort&0xff00)>>8));

#else
	//	Just return what was passed in.
	return(hostshort);
#endif
}

u_long PASCAL FAR ntohl(u_long hostlong)	{
	if(IsWinsockLoaded(sp_ntohl))	{
		return(((sp_ulong_ulong)spArray[sp_ntohl])(hostlong));
	}

#ifndef _WIN32
	return
	    (((hostlong&0xff)<<24) + ((hostlong&0xff00)<<8) +
	     ((hostlong&0xff0000)>>8) + ((hostlong&0xff000000)>>24));

#else
	//	Just return what was passed in.
	return(hostlong);
#endif
}

u_short PASCAL FAR ntohs(u_short hostshort)	{
	if(IsWinsockLoaded(sp_ntohs))	{
		return(((sp_ushort_ushort)spArray[sp_ntohs])(hostshort));
	}

#ifndef _WIN32
	return (((hostshort&0xff)<<8) + ((hostshort&0xff00)>>8));

#else
	//	Just return what was passed in.
	return(hostshort);
#endif
}

unsigned long PASCAL FAR inet_addr(const char FAR *cp)	{
	if(IsWinsockLoaded(sp_inet_addr))	{
		return(((sp_ulong_ccharFARp)spArray[sp_inet_addr])(cp));
	}

	return(INADDR_NONE);
}

int PASCAL FAR ioctlsocket(SOCKET s, long cmd, u_long FAR *argp)	{
	if(IsWinsockLoaded(sp_ioctlsocket))	{
		return(((sp_int_SOCKET_long_ulongFARp)spArray[sp_ioctlsocket])(s, cmd, argp));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR listen(SOCKET s, int backlog)	{
	if(IsWinsockLoaded(sp_listen))	{
		return(((sp_int_SOCKET_int)spArray[sp_listen])(s, backlog));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags)	{
	NON_BLOCKING(
	    (((sp_int_SOCKET_charFARp_int_int)spArray[sp_recv])(s, buf, len, flags)),
	    FD_READ, sp_recv, int);

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR select(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)	{
    //  If there's nothing to do, stop now before we go off into dll land.
    //	Optimization, boyz.
    if((readfds && readfds->fd_count) || (writefds && writefds->fd_count) || (exceptfds && exceptfds->fd_count))    {
	    if(IsWinsockLoaded(sp_select))	{
			return(((sp_int_int_fdsetFARp_fdsetFARp_fdsetFARp_ctimevalFARp)spArray[sp_select])(nfds,readfds,writefds,exceptfds,timeout));
	    }

	    WSASetLastError(WSAENETDOWN);
	    return(SOCKET_ERROR);
    }

    //	No need to go to the DLL, there is nothing to do.
    return(0);
}

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

	    (((sp_int_SOCKET_ccharFARp_int_int)spArray[sp_send])(s, buf, len, flags)),
	    FD_WRITE, sp_send, int);

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR setsockopt(SOCKET s, int level, int optname, const char FAR *optval, int optlen) {
	if(IsWinsockLoaded(sp_setsockopt))	{
		return(((sp_int_SOCKET_int_int_ccharFARp_int)spArray[sp_setsockopt])(s, level, optname, optval, optlen));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

int PASCAL FAR shutdown(SOCKET s, int how)	{
	if(IsWinsockLoaded(sp_shutdown))	{
		return(((sp_int_SOCKET_int)spArray[sp_shutdown])(s, how));
	}

	WSASetLastError(WSAENETDOWN);
	return(SOCKET_ERROR);
}

SOCKET PASCAL FAR socket(int af, int type, int protocol)	{
	if(IsWinsockLoaded(sp_socket))	{
		return(((sp_SOCKET_int_int_int)spArray[sp_socket])(af, type, protocol));
	}

	WSASetLastError(WSAENETDOWN);
	return(INVALID_SOCKET);
}

char FAR * PASCAL FAR inet_ntoa(struct in_addr in) {
	if(IsWinsockLoaded(sp_inet_ntoa))	{
		return ((sp_charFARp_in_addr)spArray[sp_inet_ntoa])(in);
	}

	WSASetLastError(WSAENETDOWN);
	return NULL;
}
