/*
 *  TwinSock - "Troy's Windows Sockets"
 *
 *  Copyright (C) 1994  Troy Rollo <troy@cbme.unsw.EDU.AU>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <winsock.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include "twinsock.h"
#include "tx.h"

#ifdef __MSDOS__
#define	PORTSWAP(x)	ntohs(x)
#else
#define	PORTSWAP(x)	ntohl(x)
#endif

static	int	iErrno = 0;
static	short	idNext = 0;

static	struct	per_task	*pptList = 0;
static	struct	per_socket	*ppsList = 0;
static	struct	tx_queue	*ptxqList = 0;

HWND	hwndManager = 0;
BOOL	bEstablished = 0;

static	void	FireAsyncRequest(struct tx_queue *ptxq);

void _export
RegisterManager(HWND hwnd)
{
	hwndManager = hwnd;
}

void _export
SetInitialised(void)
{
	bEstablished = TRUE;
}


void
CopyDataIn(	void		*pvSource,
		enum arg_type	at,
		void		*pvDest,
		int		nLen)
{
	switch(at)
	{
	case AT_Int16:
	case AT_Int16Ptr:
		*(short *) pvDest = ntohs(*(short *) pvSource);
		break;

	case AT_Int32:
	case AT_Int32Ptr:
		*(short *) pvDest = ntohl(*(short *) pvSource);
		break;

	case AT_Char:
		*(char *) pvDest = *(char *) pvSource;
		break;

	case AT_GenPtr:
	case AT_String:
		memcpy(pvDest, pvSource, nLen);
		break;
	}
}

void
CopyDataOut(	void		*pvDest,
		enum arg_type	at,
		void		*pvSource,
		int		nLen)
{
	switch(at)
	{
	case AT_Int16Ptr:
	case AT_Int16:
		*(short *) pvDest = htons(*(short *) pvSource);
		break;

	case AT_Int32:
	case AT_Int32Ptr:
		*(short *) pvDest = htonl(*(short *) pvSource);
		break;

	case AT_Char:
		*(char *) pvDest = *(char *) pvSource;
		break;

	case AT_GenPtr:
	case AT_String:
		memcpy(pvDest, pvSource, nLen);
		break;
	}
}

static	struct	per_task *
GetAnotherTaskInfo(HTASK htask)
{
	struct per_task *ppt;

	for (ppt = pptList; ppt; ppt = ppt->pptNext)
	{
		if (ppt->htask == htask)
			return ppt;
	}
	iErrno = WSANOTINITIALISED;
	return 0;
}

static	struct	per_task *
GetTaskInfo(void)
{
	return GetAnotherTaskInfo(GetCurrentTask());
}


static	struct	per_socket *
GetSocketInfo(SOCKET s)
{
	struct per_socket *pps;


	for (pps = ppsList; pps; pps = pps->ppsNext)
	{
		if (pps->s == s)
			return pps;
	}
	iErrno = WSAENOTSOCK;
	return 0;
}

static	void
Notify(struct per_socket *pps,
	int iCode)
{
	if (pps->iEvents & iCode)
		PostMessage(pps->hWnd, pps->wMsg, pps->s, WSAMAKESELECTREPLY(iCode, 0));
}

static	struct	per_socket *
NewSocket(struct per_task *ppt, SOCKET s)
{
	struct per_socket *ppsNew;

	ppsNew = (struct per_socket *) malloc(sizeof(struct per_socket));
	ppsNew->s = s;
	ppsNew->iFlags = 0;
	ppsNew->pdIn = 0;
	ppsNew->pdOut = 0;
	ppsNew->htaskOwner = ppt->htask;
	ppsNew->ppsNext = ppsList;
	ppsNew->iEvents = 0;
	ppsList = ppsNew;
	return ppsNew;
	
}

static void SendEarlyClose(SOCKET s);

static	void
RemoveSocket(struct per_socket *pps)
{
	struct	per_socket **ppps, *ppsParent;
	struct	data	**ppd, *pd;

	/* If our parent has noticed we're here, we need to remove ourselves
	 * from the list of sockets awaiting acception.
	 */
	for (ppsParent = ppsList; ppsParent; ppsParent = ppsParent->ppsNext)
	{
		if (!(ppsParent->iFlags & PSF_ACCEPT))
			continue;
		for (ppd = &ppsParent->pdIn; *ppd; ppd = &(*ppd)->pdNext)
		{
			if ((*ppd)->pchData == (char *) pps)
			{
				pd = *ppd;
				*ppd = pd->pdNext;
				free(pd);
			}
		}
	}

	/* Find our own position in the list */
	for (ppps = &ppsList; *ppps; ppps = &(*ppps)->ppsNext)
	{
		if (*ppps == pps)
		{
			*ppps = pps->ppsNext;

			/* If we have unacknowledged children, kill them */
			while (pps->pdIn)
			{
				pd = pps->pdIn;
				pps->pdIn = pd->pdNext;
				if (pps->iFlags & PSF_ACCEPT)
				{
					SendEarlyClose(((struct per_socket *) pd->pchData)->s);
					RemoveSocket((struct per_socket *) pd->pchData);
				}
				else
				{
					free(pd->pchData);
				}
				free(pd);
			}
			free(pps);
			return;
		}
	}
}

static	void
DataReceived(SOCKET s, void *pvData, int nLen, enum Functions ft)
{
	struct	data	*pdNew, **ppdList;
	struct	per_socket *pps, *ppsNew;
	struct	per_task *ppt;
	int		ns;

	pps = GetSocketInfo(s);
	if (!pps)
	{
		if (ft == FN_Accept)
		{
			nLen -= sizeof(struct sockaddr_in);
			pvData = (char *) pvData + sizeof(struct sockaddr_in);
			if (nLen == sizeof(long))
				ns = ntohl(*(long *) pvData);
			else
				ns = ntohs(*(short*) pvData);
			SendEarlyClose(ns);
		}
		return;
	}
	for (ppdList = &pps->pdIn; *ppdList; ppdList = &(*ppdList)->pdNext);

	pdNew = (struct data *) malloc(sizeof(struct data));
	pdNew->sin = *(struct sockaddr_in *) pvData;
	pdNew->sin.sin_family = ntohs(pdNew->sin.sin_family);

	nLen -= sizeof(struct sockaddr_in);
	pvData = (char *) pvData + sizeof(struct sockaddr_in);

	pdNew->pdNext = 0;
	pdNew->nUsed = 0;
	*ppdList = pdNew;

	/* Note that the calls to Notify below should *really*
	 * only be made if the data gets put at the head of the
	 * queue, but some telnet implementations miss notifications
	 * and this screws us right up. By putting in additional
	 * notifications we at least have a chance of recovery.
	 */
	if (pps->iFlags & PSF_ACCEPT)
	{
		pdNew->iLen = 0;
		if (nLen == sizeof(long))
			ns = ntohl(*(long *) pvData);
		else
			ns = ntohs(*(short*) pvData);
		ppt = GetAnotherTaskInfo(pps->htaskOwner);
		ppsNew = NewSocket(ppt, ns);
		pdNew->pchData = (char *) ppsNew;
		ppsNew->iFlags |= PSF_CONNECT;
		Notify(pps, FD_ACCEPT);
	}
	else
	{
		pdNew->iLen = nLen;
		pdNew->pchData = (char *) malloc(nLen);
		memcpy(pdNew->pchData, pvData, nLen);
		Notify(pps, nLen ? FD_READ : FD_CLOSE);
	}
}

static	BOOL
StartBlocking(struct per_task *ppt)
{
	if (ppt->bBlocking)
	{
		iErrno = WSAEINPROGRESS;
		return FALSE;
	}
	else
	{
		ppt->bBlocking = TRUE;
		ppt->bCancel = FALSE;
		return TRUE;
	}
}

static	void
EndBlocking(struct per_task *ppt)
{
	ppt->bBlocking = FALSE;
}

static	BOOL
FlushMessages(struct per_task *ppt)
{
	MSG msg;
	BOOL ret;

	if (ppt->lpBlockFunc)
		return ((BOOL far pascal (*)()) ppt->lpBlockFunc)();

 	ret = (BOOL) PeekMessage(&msg,0,0,0,PM_REMOVE);
	if (ret)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		/* Some poorly behaved applications quit without
		 * checking the return value of WSACancel. It
		 * *can* fail.
		 */
		if (msg.message == WM_QUIT)
			ppt->bCancel = TRUE;
	}
	return ret;
}

static	void
RemoveTXQ(struct tx_queue *ptxq)
{
	struct	tx_queue **pptxq;

	for (pptxq = &ptxqList; *pptxq; pptxq = &((*pptxq)->ptxqNext))
	{
		if (*pptxq == ptxq)
		{
			*pptxq = ptxq->ptxqNext;
			free(ptxq->ptxr);
			free(ptxq);
			return;
		}
	}
}

static	void
RemoveTask(struct per_task *ppt)
{
	struct per_task **pppt;

	for (pppt = &pptList; *pppt; pppt = &((*pppt)->pptNext))
	{
		if (*pppt == ppt)
		{
			*pppt = ppt->pptNext;
			free(ppt);
		}
	}
};

void far _export
ResponseReceived(struct tx_request *ptxr)
{
	int		nLen;
	int		id;
	struct	tx_queue *ptxq;
	enum Functions	ft;

	ft = (enum Functions) ntohs(ptxr->iType);
	id = ntohs(ptxr->id);
	nLen = ntohs(ptxr->nLen);
	if (ft == FN_Data || ft == FN_Accept)
	{
		DataReceived(id, ptxr->pchData, nLen - sizeof(short) * 5, ft);
		return;
	}
	for (ptxq = ptxqList; ptxq; ptxq = ptxq->ptxqNext)
	{
		if (ptxq->id == id)
		{
			memcpy(ptxq->ptxr, ptxr, nLen);
			ptxq->bDone = TRUE;
			if (ptxq->pchLocation)
				FireAsyncRequest(ptxq);
			return;
		}
	}
}

static	struct tx_queue *
TransmitFunction(struct transmit_function *ptf)
{
	int	i;
	int	nSize = sizeof(short) * 5;
	struct	tx_request *ptxr;
	struct	tx_queue *ptxq, **pptxq;
	int	iOffset;

	for (i = 0; i < ptf->nArgs; i++)
		nSize += ptf->pfaList[i].iLen + sizeof(short) * 2;
	nSize += ptf->pfaResult->iLen + sizeof(short) * 2;
	ptxr = (struct tx_request *) malloc(nSize);
	ptxr->iType = htons((short) ptf->fn);
	ptxr->nArgs = htons((short) ptf->nArgs);
	ptxr->nError = 0;
	ptxr->nLen = htons((short) nSize);
	ptxr->id = htons(idNext);
	for (iOffset = i = 0; i < ptf->nArgs; i++)
	{
		*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaList[i].at);
		iOffset += 2;
		*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaList[i].iLen);
		iOffset += 2;
		CopyDataOut(	ptxr->pchData + iOffset,
				ptf->pfaList[i].at,
				ptf->pfaList[i].pvData,
				ptf->pfaList[i].iLen);
		iOffset += ptf->pfaList[i].iLen;
	}
	*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaResult->at);
	iOffset += 2;
	*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaResult->iLen);
	iOffset += 2;
	CopyDataOut(	ptxr->pchData + iOffset,
			ptf->pfaResult->at,
			ptf->pfaResult->pvData,
			ptf->pfaResult->iLen);

	iOffset += ptf->pfaResult->iLen;

	ptxq = (struct tx_queue *) malloc(sizeof(struct tx_queue));
	ptxq->ptxqNext = 0;
	ptxq->id = idNext;
	ptxq->ptxr = ptxr;
	ptxq->bDone = 0;
	ptxq->hwnd = 0;
	ptxq->pchLocation = 0;
	ptxq->wMsg = 0;
	idNext++;
	for (pptxq = &ptxqList; *pptxq; pptxq = &((*pptxq)->ptxqNext));
	*pptxq = ptxq;
	SendMessage(hwndManager, WM_USER, nSize, (LPARAM) ptxr);
	return ptxq;
};

static void
SendEarlyClose(SOCKET s)
{
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	int	nReturn;

	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Close, 1, pfaArgs, pfaReturn);
	RemoveTXQ(TransmitFunction(&tf));
}

static void
SetErrorResult(struct func_arg *pfaResult)
{
	switch (pfaResult->at)
	{
	case AT_Int16:
		*(short *) pfaResult->pvData = -1;
		break;

	case AT_Int32:
		*(long *) pfaResult->pvData = -1;
		break;

	case AT_Char:
		*(char *) pfaResult->pvData = -1;
		break;

	case AT_Int16Ptr:
	case AT_Int32Ptr:
	case AT_String:
	case AT_GenPtr:
		*(void **) pfaResult->pvData = 0;
		break;
	}
}

static	int	TransmitFunctionAndBlock(
			struct per_task *ppt,
			struct transmit_function *ptf)
{
	struct tx_queue *ptxq;
	struct tx_request *ptxr;
	int	i, iOffset;
	BOOL	bError = FALSE;

	if (!StartBlocking(ppt))
	{
		iErrno = WSAEINPROGRESS;
		SetErrorResult(ptf->pfaResult);
		return 0;
	}
	ptxq = TransmitFunction(ptf);
	ptxr = ptxq->ptxr;
	while (!ptxq->bDone)
	{
		FlushMessages(ppt);
		if (ppt->bCancel)
		{
			iErrno = WSAEINTR;
			RemoveTXQ(ptxq);
			SetErrorResult(ptf->pfaResult);
			EndBlocking(ppt);
			return 0;
		}
	}
	if (ptxq->ptxr->nError)
	{
		iErrno = ntohs(ptxq->ptxr->nError);
		SetErrorResult(ptf->pfaResult);
		bError = TRUE;
	}
	else
	{
		for (iOffset = i = 0; i < ptf->nArgs; i++)
		{
			*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaList[i].at);
			iOffset += 2;
			*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaList[i].iLen);
			iOffset += 2;
			if (!ptf->pfaList[i].bConstant)
				CopyDataIn(	ptxr->pchData + iOffset,
						ptf->pfaList[i].at,
						ptf->pfaList[i].pvData,
						ptf->pfaList[i].iLen);
			iOffset += ptf->pfaList[i].iLen;
		}
		*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaResult->at);
		iOffset += 2;
		*(short *) (ptxr->pchData + iOffset) = htons((short) ptf->pfaResult->iLen);
		iOffset += 2;
		CopyDataIn(	ptxr->pchData + iOffset,
				ptf->pfaResult->at,
				ptf->pfaResult->pvData,
				ptf->pfaResult->iLen);

	}
	RemoveTXQ(ptxq);
	EndBlocking(ppt);
	return bError ? 0 : 1;
}

static void
CopyHostEnt(struct per_task *ppt)
{
	int	iLocation;
	int	i;
	int	nAddrs;

	ppt->he.h_addrtype = ntohs(*(short *) ppt->achHostEnt);
	ppt->he.h_length = ntohs(*(short *) (ppt->achHostEnt + sizeof(short)));
	nAddrs = ntohs(*(short *) (ppt->achHostEnt + sizeof(short) * 2));
	iLocation = sizeof(short) * 3;
	for (i = 0; i < nAddrs; i++)
	{
		if (i < MAX_ALTERNATES - 1)
			ppt->apchHostAddresses[i] = ppt->achHostEnt + iLocation;
		iLocation += 4;
	}
	if (nAddrs < MAX_ALTERNATES - 1)
		ppt->apchHostAddresses[nAddrs] = 0;
	else
		ppt->apchHostAddresses[MAX_ALTERNATES - 1] = 0;
	ppt->he.h_addr_list = ppt->apchHostAddresses;
	ppt->he.h_name = ppt->achHostEnt + iLocation;
	iLocation += strlen(ppt->achHostEnt + iLocation)+ 1;
	for (i = 0; ppt->achHostEnt[iLocation] && i < MAX_ALTERNATES - 1; i++)
	{
		ppt->apchHostAlii[i] = ppt->achHostEnt + iLocation;
		iLocation += strlen(ppt->achHostEnt + iLocation)+ 1;
	}
	ppt->apchHostAlii[i] = 0;
	ppt->he.h_aliases = ppt->apchHostAlii;
	ppt->he.h_addr_list = ppt->apchHostAddresses;
}

static int
CopyHostEntTo(struct per_task *ppt, char *pchData)
{
	int	i;
	int	nAddrs;
	struct hostent *phe;
	char	*pchOld;
	int	nAlii;

	CopyHostEnt(ppt);

	phe = (struct hostent *) pchData;
	memcpy(phe, &ppt->he, sizeof(ppt->he));

	pchData += sizeof(*phe);

	for (nAddrs = 0; phe->h_addr_list[nAddrs]; nAddrs++);
	for (nAlii = 0; phe->h_aliases[nAlii]; nAlii++);

	memcpy(pchData, phe->h_addr_list, sizeof(char *) * (nAddrs + 1));
	phe->h_addr_list = (char **) pchData;
	pchData += sizeof(char *) * (nAddrs + 1);

	memcpy(pchData, phe->h_aliases, sizeof(char *) * (nAddrs + 1));
	phe->h_aliases = (char **) pchData;
	pchData += sizeof(char *) * (nAddrs + 1);

	pchOld = phe->h_addr_list[0];
	memcpy(pchData, pchOld, 4 * nAddrs);
	for (i = 0; i < nAddrs; i++)
		phe->h_addr_list[i] = phe->h_addr_list[i] - pchOld + pchData;
	pchData += 4 * nAddrs;

	strcpy(pchData, phe->h_name);
	phe->h_name = pchData;
	pchData += strlen(pchData) + 1;

	for (i = 0; i < nAlii; i++)
	{
		strcpy(pchData, phe->h_aliases[i]);
		phe->h_aliases[i] = pchData;
		pchData += strlen(pchData) + 1;
	}
	return (pchData - (char *) phe);
}

static void
CopyServEnt(struct per_task *ppt)
{
	int	iLocation;
	int	i;

	/* Note that s_port is needed in network byte order */
        ppt->se.s_port = *(short *) ppt->achServEnt;
	iLocation = sizeof(short);
	ppt->se.s_proto = ppt->achServEnt + iLocation;
	iLocation += strlen(ppt->achServEnt + iLocation) + 1;
	ppt->se.s_name = ppt->achServEnt + iLocation;
	iLocation += strlen(ppt->achServEnt + iLocation) + 1;
	for (i = 0; ppt->achServEnt[iLocation] && i < MAX_ALTERNATES - 1; i++)
	{
		ppt->apchServAlii[i] = ppt->achServEnt + iLocation;
		iLocation += strlen(ppt->achServEnt + iLocation) + 1;
	}
	ppt->apchServAlii[i] = 0;
	ppt->se.s_aliases = ppt->apchServAlii;
}

static int
CopyServEntTo(struct per_task *ppt, char *pchData)
{
	int	i;
	int	nAlii;
	struct	servent *pse;

	CopyServEnt(ppt);

	pse = (struct servent *) pchData;
	memcpy(pse, &ppt->se, sizeof(ppt->se));
	pchData += sizeof(*pse);

	for (nAlii = 0; pse->s_aliases[nAlii]; nAlii++);
	memcpy(pchData, pse->s_aliases, sizeof(char *) * (nAlii + 1));
	pse->s_aliases = (char **) pchData;
	pchData += sizeof(char *) * (nAlii + 1);

	strcpy(pchData, ppt->se.s_name);
	ppt->se.s_name = pchData;
	pchData += strlen(pchData) + 1;

	strcpy(pchData, ppt->se.s_proto);
	ppt->se.s_proto = pchData;
	pchData += strlen(pchData) + 1;

	for (i = 0; i < nAlii; i++)
	{
		strcpy(pchData, pse->s_aliases[i]);
		pse->s_aliases[i] = pchData;
		pchData += strlen(pchData) + 1;
	}
	return (pchData - (char *) pse);
}

static void
CopyProtoEnt(struct per_task *ppt)
{
	int	iLocation;
	int	i;
	int	nAddrs;

	ppt->pe.p_proto = ntohs(*(short *) ppt->achProtoEnt);
	iLocation = sizeof(short);
	ppt->pe.p_name = ppt->achProtoEnt + iLocation;
	iLocation += strlen(ppt->achProtoEnt + iLocation) + 1;
	for (i = 0; ppt->achProtoEnt[iLocation] && i < MAX_ALTERNATES - 1; i++)
	{
		ppt->apchProtoAlii[i] = ppt->achProtoEnt + iLocation;
		iLocation += strlen(ppt->achProtoEnt + iLocation) + 1;
	}
	ppt->apchProtoAlii[i] = 0;
	ppt->pe.p_aliases = ppt->apchProtoAlii;
}

static int
CopyProtoEntTo(struct per_task *ppt, char *pchData)
{
	int	iLocation;
	int	i;
	int	nAlii;
	struct	protoent *ppe;

	CopyProtoEnt(ppt);

	ppe = (struct protoent *) pchData;
	memcpy(ppe, &ppt->pe, sizeof(ppt->pe));
	pchData += sizeof(*ppe);

	for (nAlii = 0; ppe->p_aliases[nAlii]; nAlii++);
	memcpy(pchData, ppe->p_aliases, sizeof(char *) * (nAlii + 1));
	ppe->p_aliases = (char **) pchData;
	pchData += sizeof(char *) * (nAlii + 1);

	strcpy(pchData, ppe->p_name);
	ppe->p_name = pchData;
	pchData += strlen(pchData) + 1;

	for (i = 0; i < nAlii; i++)
	{
		strcpy(pchData, ppe->p_aliases[i]);
		ppe->p_aliases[i] = pchData;
		pchData += strlen(pchData) + 1;
	}
	return (pchData - (char *) ppe);
}

SOCKET pascal far _export
accept(SOCKET s, struct sockaddr FAR *addr,
                          int FAR *addrlen)
{
	struct per_task *ppt;
	struct per_socket *pps, *ppsNew;
	struct data *pd;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	if (!(pps->iFlags & PSF_ACCEPT))
	{
		iErrno = WSAEINVAL;
		return -1;
	}
	if (!pps->pdIn && (pps->iFlags & PSF_NONBLOCK))
	{
		iErrno = WSAEWOULDBLOCK;
		return -1;
	}
	if (!StartBlocking(ppt))
		return -1;
	while (!pps->pdIn)
	{
		FlushMessages(ppt);
		if (ppt->bCancel)
		{
			iErrno = WSAEINTR;
			EndBlocking(ppt);
			return -1;
		}
	}

	pd = pps->pdIn;
	if (addr && addrlen)
	{
		memcpy(addr, &pd->sin, sizeof(pd->sin));
		*addrlen = sizeof(pd->sin);
	}
	ppsNew = (struct per_socket *) pd->pchData;

	pps->pdIn = pd->pdNext;
	free(pd);
	if (pps->pdIn)
		Notify(pps, FD_ACCEPT);

	EndBlocking(ppt);
	return ppsNew->s;
}

int pascal far _export
bind(SOCKET s, const struct sockaddr FAR *addr, int namelen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	sockaddr	*psa;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (!GetSocketInfo(s))
		return -1;
	psa = (struct sockaddr *) malloc(namelen);
	memcpy(psa, addr, namelen);
	psa->sa_family = htons(psa->sa_family);
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_CARGS(pfaArgs[1],	AT_GenPtr,	psa,		namelen			);
	INIT_ARGS(pfaArgs[2],	AT_IntPtr,	&namelen,	sizeof(namelen)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Bind, 3, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	free(psa);
	return nReturn;
}

int pascal far _export
closesocket(SOCKET s)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Close, 1, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	RemoveSocket(pps); /* Assume success. Is this valid? */
	return nReturn;
}

int pascal far _export
connect(SOCKET s, const struct sockaddr FAR *name, int namelen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	sockaddr	*psa;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	psa = (struct sockaddr *) malloc(namelen);
	memcpy(psa, name, namelen);
	psa->sa_family = htons(psa->sa_family);
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_CARGS(pfaArgs[1],	AT_GenPtr,	psa,		namelen			);
	INIT_ARGS(pfaArgs[2],	AT_IntPtr,	&namelen,	sizeof(namelen)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Connect, 3, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	free(psa);
	if (nReturn != -1)
		pps->iFlags |= PSF_CONNECT;
	return nReturn;
}

int pascal far _export
ioctlsocket(SOCKET s, long cmd, u_long far * arg)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	switch(cmd)
	{
	case FIONBIO:
		if (*arg)
			pps->iFlags |= PSF_NONBLOCK;
		else
			pps->iFlags &= ~PSF_NONBLOCK;
		break;

	case FIONREAD:
		if (pps->pdIn)
			*arg = pps->pdIn->iLen - pps->pdIn->nUsed;
		else
			*arg = 0;
		break;

	case SIOCATMARK:
		*(BOOL *) arg = 0;
		break;
	}
	return 0;
}

int pascal far _export getpeername (SOCKET s, struct sockaddr FAR *name,
                            int FAR * namelen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (!GetSocketInfo(s))
		return -1;
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaArgs[1],	AT_GenPtr,	name,		*namelen		);
	INIT_ARGS(pfaArgs[2],	AT_IntPtr,	namelen,	sizeof(*namelen)	);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_GetPeerName, 3, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	if (nReturn != -1)
		name->sa_family = ntohs(name->sa_family);
	return nReturn;
}

int pascal far _export getsockname (SOCKET s, struct sockaddr FAR *name,
                            int FAR * namelen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (!GetSocketInfo(s))
		return -1;
	*namelen = sizeof(struct sockaddr_in); /* Just in case */
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaArgs[1],	AT_GenPtr,	name,		*namelen		);
	INIT_ARGS(pfaArgs[2],	AT_IntPtr,	namelen,	sizeof(*namelen)	);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_GetSockName, 3, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	if (nReturn != -1)
		name->sa_family = ntohs(name->sa_family);
	return nReturn;
}

int pascal far _export getsockopt (SOCKET s, int level, int optname,
                           char FAR * optval, int FAR *optlen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[5];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	long	iOptVal;
	short	iOptLen;

	iOptLen = sizeof(long);

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (!GetSocketInfo(s))
		return -1;
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaArgs[1],	AT_Int,		&level,		sizeof(level)		);
	INIT_ARGS(pfaArgs[2],	AT_Int,		&optname,	sizeof(optname)		);
	if (optname == SO_LINGER)
		INIT_ARGS(pfaArgs[3],	AT_GenPtr,	optval,		iOptLen		);
	else
		INIT_ARGS(pfaArgs[3],	AT_Int32Ptr,	&iOptVal,	iOptLen		);
	INIT_ARGS(pfaArgs[4],	AT_IntPtr,	&iOptLen,	sizeof(iOptLen)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_GetSockOpt, 5, pfaArgs, pfaReturn);
	if (TransmitFunctionAndBlock(ppt, &tf))
	{
		if (optname == SO_LINGER)
		{
			*optlen = sizeof(struct linger);
		}
		else
		{
			*optlen = sizeof(int);
			*optval = iOptVal;
		}
	}
	return nReturn;
}

u_long pascal far _export htonl (u_long hostlong)
{
	char	*pchValue = (char *) &hostlong;
	char	c;

	c = pchValue[0];
	pchValue[0] = pchValue[3];
	pchValue[3] = c;
	c = pchValue[1];
	pchValue[1] = pchValue[2];
	pchValue[2] = c;
	return hostlong;
}

u_short pascal far _export htons (u_short hostshort)
{
	char	*pchValue = (char *) &hostshort;
	char	c;

	c = pchValue[0];
	pchValue[0] = pchValue[1];
	pchValue[1] = c;
	return hostshort;
}

unsigned long pascal far _export inet_addr (const char FAR * cp)
{
	unsigned long	iValue;
	char	*pchValue = (char *) &iValue;

	if (!GetTaskInfo())
		return -1;
	pchValue[0] = atoi(cp);
	cp = strchr(cp, '.');
	if (cp)
	{
		cp++;
		pchValue[1] = atoi(cp);
		cp = strchr(cp, '.');
		if (cp)
		{
			cp++;
			pchValue[2] = atoi(cp);
			cp = strchr(cp, '.');
			if (cp)
			{
				cp++;
				pchValue[3] = atoi(cp);
				cp = strchr(cp, '.');
				if (!cp)
				{
					return iValue;
				}
			}
		}
	}
	return -1;
}

char FAR * pascal far _export inet_ntoa (struct in_addr in)
{
	struct per_task *ppt;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;

	sprintf(ppt->achAddress, "%d.%d.%d.%d",
			(int) in.S_un.S_un_b.s_b1,
			(int) in.S_un.S_un_b.s_b2,
			(int) in.S_un.S_un_b.s_b3,
			(int) in.S_un.S_un_b.s_b4);
	return ppt->achAddress;
}

int pascal far _export listen (SOCKET s, int backlog)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[2];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	if (pps->iFlags & PSF_CONNECT)
	{
		iErrno = WSAEISCONN;
		return -1;
	}
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaArgs[1],	AT_Int,		&backlog,	sizeof(backlog)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Listen, 2, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	if (nReturn != -1)
		pps->iFlags |= PSF_ACCEPT;
	return nReturn;
}

u_long pascal far _export ntohl (u_long netlong)
{
	char	*pchValue = (char *) &netlong;
	char	c;

	c = pchValue[0];
	pchValue[0] = pchValue[3];
	pchValue[3] = c;
	c = pchValue[1];
	pchValue[1] = pchValue[2];
	pchValue[2] = c;
	return netlong;
}

u_short pascal far _export ntohs (u_short netshort)
{
	char	*pchValue = (char *) &netshort;
	char	c;

	c = pchValue[0];
	pchValue[0] = pchValue[1];
	pchValue[1] = c;
	return netshort;
}

int pascal far _export recv (SOCKET s, char FAR * buf, int len, int flags)
{
	return recvfrom(s, buf, len, flags, 0, 0);
}

int pascal far _export recvfrom (SOCKET s, char FAR * buf, int len, int flags,
                         struct sockaddr FAR *from, int FAR * fromlen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	struct data *pd;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	if (pps->iFlags & PSF_CLOSED)
	{
		/* Haven't you got the message yet? */
		iErrno = WSAENOTCONN;
		return -1;
	}
	if (pps->iFlags & PSF_ACCEPT)
	{
		iErrno = WSAENOTCONN;
		return -1;
	}
	if (pps->iFlags & PSF_SHUTDOWN)
	{
		iErrno = WSAESHUTDOWN;
		return -1;
	}
	if (!pps->pdIn && (pps->iFlags & PSF_NONBLOCK))
	{
		iErrno = WSAEWOULDBLOCK;
		return -1;
	}
	if (!StartBlocking(ppt))
		return -1;
	while (!pps->pdIn)
	{
		FlushMessages(ppt);
		if (ppt->bCancel)
		{
			iErrno = WSAEINTR;
			EndBlocking(ppt);
			return -1;
		}
	}

	pd = pps->pdIn;
	if (from && fromlen)
	{
		memcpy(from, &pd->sin, sizeof(pd->sin));
		*fromlen = sizeof(pd->sin);
	}
	if (len > pd->iLen - pd->nUsed)
		len = pd->iLen - pd->nUsed;
	memcpy(buf, pd->pchData + pd->nUsed, len);
	pd->nUsed += len;
	if (pd->nUsed == pd->iLen)
	{
		pps->pdIn = pd->pdNext;
		free(pd->pchData);
		free(pd);
		if (pps->pdIn)
			Notify(pps, pps->pdIn->iLen ? FD_READ : FD_CLOSE);
	}
	else
	{
		Notify(pps, FD_READ);
	}

	EndBlocking(ppt);
	return len;
}

#define	PPS_ERROR	((struct per_socket *) -1)

static	struct	per_socket **
GetPPS(fd_set *fds)
{
	struct per_socket **pps;
	int	i;

	if (!fds || !fds->fd_count)
		return 0;
	pps = (struct per_socket *) malloc(sizeof(struct per_socket *) *
					fds->fd_count);
	for (i = 0; i < fds->fd_count; i++)
	{
		pps[i] = GetSocketInfo(fds->fd_array[i]);
		if (!pps[i])
		{
			free(pps);
			return PPS_ERROR;
		}
	}
	return pps;
}


int pascal far _export select (int nfds, fd_set *readfds, fd_set far *writefds,
                fd_set *exceptfds, const struct timeval far *timeout)
{
	struct per_task *ppt;
	struct per_socket **ppsRead, **ppsWrite, **ppsExcept;
	BOOL	bOneOK = FALSE;
	BOOL	bTimedOut = FALSE;
	int	iOld, iNew;
	unsigned	long	tExpire;
	int	i;

	if (timeout)
	{
		tExpire = GetTickCount();
		tExpire += timeout->tv_usec / 1000 + timeout->tv_sec * 1000;
	}
	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (!StartBlocking(ppt))
		return -1;

	ppsRead = GetPPS(readfds);
	if (ppsRead == PPS_ERROR)
		return -1;
	ppsWrite = GetPPS(writefds);
	if (ppsWrite == PPS_ERROR)
	{
		free(ppsRead);
		return -1;
	}
	ppsExcept = GetPPS(exceptfds);
	if (ppsExcept == PPS_ERROR)
	{
		free(ppsRead);
		free(ppsWrite);
		return -1;
	}
		
	while (!bOneOK && !bTimedOut && !ppt->bCancel)
	{
		FlushMessages(ppt);
		if (ppsWrite)
			bOneOK = TRUE;
		if (ppsRead)
		{
			for (i = 0; i < readfds->fd_count; i++)
			{
				if (ppsRead[i]->pdIn)
					bOneOK = TRUE;
			}
		}
		if (timeout && GetTickCount() >= tExpire)
			bTimedOut = TRUE;
	}

	nfds = 0;
	if (readfds)
	{
		for (iOld = iNew = 0; iOld < readfds->fd_count; iOld++)
		{
			if (iOld != iNew)
				readfds->fd_array[iNew] =
						readfds->fd_array[iOld];
			if (ppsRead[iOld]->pdIn)
				iNew++;
		}
		readfds->fd_count = iNew;
		nfds += iNew;
	}
	if (writefds)
		nfds += writefds->fd_count;
	if (exceptfds)
		exceptfds->fd_count = 0;

	EndBlocking(ppt);
	if (ppsRead)
		free(ppsRead);
	if (ppsWrite)
		free(ppsWrite);
	if (ppsExcept)
		free(ppsExcept);
	if (ppt->bCancel && !nfds)
	{
		iErrno = WSAEINTR;
		return -1;
	}
	else
	{
		return nfds;
	}
}

/* We don't wait for the result of a send because some telnets don't behave
 * correctly with WSAEINPROGRESS when sending. This results in characters
 * being dropped to hell while we wait for the response from the host.
 * We return success unless the socket is not connected or is closed.
 * This is fairly safe on connected sockets (which is what uses send).
 *
 * We don't do this with sendto because it's really only telnets that suffer
 * from this, although that doesn't stop another application from having the
 * same problem with sendto later on.
 *
 * This causes certain FTP clients to display phenomenal transfer rates.
 * They should be checking the transfer rates *after* closing their sockets.
 */
int pascal far _export send (SOCKET s, const char FAR * buf, int len, int flags)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[4];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	if (!(pps->iFlags & PSF_CONNECT))
	{
		iErrno = WSAENOTCONN;
		return -1;
	}
	if (pps->iFlags & PSF_CLOSED)
	{
		iErrno = WSAECONNRESET;
		return -1;
	}
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_CARGS(pfaArgs[1],	AT_GenPtr,	buf,		len			);
	INIT_ARGS(pfaArgs[2],	AT_IntPtr,	&len,		sizeof(len)		);
	INIT_ARGS(pfaArgs[3],	AT_Int,		&flags,		sizeof(flags)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Send, 4, pfaArgs, pfaReturn);
	RemoveTXQ(TransmitFunction(&tf));
	Notify(pps, FD_WRITE);
	return len;
}

int pascal far _export sendto (SOCKET s, const char FAR * buf, int len, int flags,
                       const struct sockaddr FAR *to, int tolen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[6];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	sockaddr	*psa;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	psa = (struct sockaddr *) malloc(tolen);
	memcpy(psa, to, tolen);
	psa->sa_family = htons(psa->sa_family);
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_CARGS(pfaArgs[1],	AT_GenPtr,	buf,		len			);
	INIT_ARGS(pfaArgs[2],	AT_IntPtr,	&len,		sizeof(len)		);
	INIT_ARGS(pfaArgs[3],	AT_Int,		&flags,		sizeof(flags)		);
	INIT_CARGS(pfaArgs[4],	AT_GenPtr,	to,		tolen			);
	INIT_ARGS(pfaArgs[5],	AT_Int,		&tolen,		sizeof(tolen)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Send, 6, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	free(psa);
	Notify(pps, FD_WRITE);
	return nReturn;
}

int pascal far _export setsockopt (SOCKET s, int level, int optname,
                           const char FAR * optval, int optlen)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[5];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	long	iOptVal;
	short	iOptLen;

	iOptLen = sizeof(long);

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (!GetSocketInfo(s))
		return -1;
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaArgs[1],	AT_Int,		&level,		sizeof(level)		);
	INIT_ARGS(pfaArgs[2],	AT_Int,		&optname,	sizeof(optname)		);
	if (optname == SO_LINGER)
	{
		INIT_ARGS(pfaArgs[3],	AT_GenPtr,	optval,		iOptLen		);
	}
	else
	{
		iOptVal = *optval;
		INIT_ARGS(pfaArgs[3],	AT_Int32Ptr,	&iOptVal,	iOptLen		);
	}
	INIT_ARGS(pfaArgs[4],	AT_Int,		&iOptLen,	sizeof(iOptLen)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_SetSockOpt, 5, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	return nReturn;
}

int pascal far _export shutdown (SOCKET s, int how)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[2];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	INIT_ARGS(pfaArgs[0],	AT_Int,		&s,		sizeof(s)		);
	INIT_ARGS(pfaArgs[1],	AT_Int,		&how,		sizeof(how)		);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Shutdown, 2, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	if (nReturn != -1 && (how == 0 || how == 2))
		pps->iFlags |= PSF_SHUTDOWN;
	return nReturn;
}

SOCKET pascal far _export socket (int af, int type, int protocol)
{
	struct per_task *ppt;
	struct per_socket *pps;
	int	nReturn;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	INIT_ARGS(pfaArgs[0],	AT_Int,		&af,		sizeof(af)		);
	INIT_ARGS(pfaArgs[1],	AT_Int,		&type,		sizeof(type)		);
	INIT_ARGS(pfaArgs[2],	AT_Int,		&protocol,	sizeof(protocol)	);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_Socket, 3, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	if (nReturn != -1)
		NewSocket(ppt, nReturn);
	return nReturn;
}

struct hostent FAR * pascal far _export gethostbyaddr(const char FAR * addr,
                                              int len, int type)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_CARGS(pfaArgs[0],	AT_GenPtr,	addr,		len			);
	INIT_ARGS(pfaArgs[1],	AT_Int,		&len,		sizeof(len)		);
	INIT_ARGS(pfaArgs[2],	AT_Int,		&type,		sizeof(type)		);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achHostEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_HostByAddr, 3, pfaArgs, pfaReturn);
	if (TransmitFunctionAndBlock(ppt, &tf))
	{
		CopyHostEnt(ppt);
		return &ppt->he;
	}
	else
	{
		return 0;
	}
}

struct hostent FAR * pascal far _export gethostbyname(const char FAR * name)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_CARGS(pfaArgs[0],	AT_String,	name,		strlen(name) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achHostEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_HostByName, 1, pfaArgs, pfaReturn);
	if (TransmitFunctionAndBlock(ppt, &tf))
	{
		CopyHostEnt(ppt);
		return &ppt->he;
	}
	else
	{
		return 0;
	}
}

struct servent FAR * pascal far _export getservbyport(int port, const char FAR * proto)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[2];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	PORTSWAP(port);
	if (!proto)
		proto = "";
	INIT_ARGS(pfaArgs[0],	AT_Int,		&port,		sizeof(port)		);
	INIT_CARGS(pfaArgs[1],	AT_String,	proto,		strlen(proto) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achServEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_ServByPort, 2, pfaArgs, pfaReturn);
	if (TransmitFunctionAndBlock(ppt, &tf))
	{
		CopyServEnt(ppt);
		return &ppt->se;
	}
	else
	{
		return 0;
	}
}

struct servent FAR * pascal far _export getservbyname(const char FAR * name,
                                              const char FAR * proto)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[2];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	if (!proto)
		proto = "";
	INIT_CARGS(pfaArgs[0],	AT_String,	name,		strlen(name) + 1	);
	INIT_CARGS(pfaArgs[1],	AT_String,	proto,		strlen(proto) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achServEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_ServByName, 2, pfaArgs, pfaReturn);
	if (TransmitFunctionAndBlock(ppt, &tf))
	{
		CopyServEnt(ppt);
		return &ppt->se;
	}
	else
	{
		return 0;
	}
}

struct protoent FAR * pascal far _export getprotobynumber(int proto)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_ARGS(pfaArgs[0],	AT_Int,		&proto,		sizeof(proto)	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achProtoEnt,MAX_HOST_ENT	);
	INIT_TF(tf, FN_ProtoByNumber, 1, pfaArgs, pfaReturn);
	if (TransmitFunctionAndBlock(ppt, &tf))
	{
		CopyProtoEnt(ppt);
		return &ppt->pe;
	}
	else
	{
		return 0;
	}
}

struct protoent FAR * pascal far _export getprotobyname(const char FAR * name)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_CARGS(pfaArgs[0],	AT_String,	name,		strlen(name) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achProtoEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_ProtoByName, 1, pfaArgs, pfaReturn);
	if (TransmitFunctionAndBlock(ppt, &tf))
	{
		CopyProtoEnt(ppt);
		return &ppt->pe;
	}
	else
	{
		return 0;
	}
}

int pascal far _export
gethostname(char *name, int namelen)
{
	struct per_task *ppt;
	int	nReturn;
	struct	func_arg pfaArgs[2];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	INIT_ARGS(pfaArgs[0],	AT_String,	name,		namelen		);
	INIT_ARGS(pfaArgs[1],	AT_Int,		&namelen,	sizeof(namelen)	);
	INIT_ARGS(pfaReturn,	AT_Int,		&nReturn,	sizeof(nReturn)		);
	INIT_TF(tf, FN_GetHostName, 2, pfaArgs, pfaReturn);
	TransmitFunctionAndBlock(ppt, &tf);
	return nReturn;
}

int pascal far _export WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
	struct	per_task	*pptNew;

	lpWSAData->wVersion = 0x0101;
	lpWSAData->wHighVersion = 0x0101;
	strcpy(lpWSAData->szDescription,
		"TwinSock 1.1 - Proxy sockets system. "
		"Copyright 1994 Troy Rollo. "
		"TwinSock is free software. "
		"See the file \"COPYING\" from the "
		"distribution for details.");
	if (!hwndManager)
		strcpy(lpWSAData->szSystemStatus, "Not Initialised.");
	else if (bEstablished)
		strcpy(lpWSAData->szSystemStatus, "Ready.");
	else
		strcpy(lpWSAData->szSystemStatus, "Initialising.");
	lpWSAData->iMaxSockets = 256;
	lpWSAData->iMaxUdpDg = 512;
	lpWSAData->lpVendorInfo = 0;
	if (wVersionRequired == 0x0001)
		return WSAVERNOTSUPPORTED;
	if (!bEstablished)
		return WSASYSNOTREADY;
	pptNew = malloc(sizeof(struct per_task));
	pptNew->htask = GetCurrentTask();
	pptNew->pptNext = pptList;
	pptNew->lpBlockFunc = 0;
	pptNew->bCancel = FALSE;
	pptNew->bBlocking = FALSE;
	pptList = pptNew;
	return 0;
}

int pascal far _export WSACleanup(void)
{
	struct	per_task *ppt;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (ppt->bBlocking)
	{
		iErrno = WSAEINPROGRESS;
		RemoveTask(ppt);
		return -1;
	}
	return 0;
}

void pascal far _export WSASetLastError(int iError)
{
	if (!GetTaskInfo())
		return -1;
	iErrno = iError;
}

int pascal far _export WSAGetLastError(void)
{
	return iErrno;
}

BOOL pascal far _export WSAIsBlocking(void)
{
	struct per_task *ppt;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	return ppt->bBlocking;
}

int pascal far _export WSAUnhookBlockingHook(void)
{
	struct	per_task *ppt;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	ppt->lpBlockFunc = 0;
	return 0;
}

FARPROC pascal far _export WSASetBlockingHook(FARPROC lpBlockFunc)
{
	struct per_task *ppt;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	ppt->lpBlockFunc = lpBlockFunc;
	return -1;
}

int pascal far _export WSACancelBlockingCall(void)
{
	struct	per_task *ppt;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if (ppt->bBlocking)
	{
		ppt->bCancel = TRUE;
		return 0;
	}
	else
	{
		iErrno = WSAEINVAL;
		return -1;
	}
}

static	void
FireAsyncRequest(struct tx_queue *ptxq)
{
	int	nLen;
	char	*pchData;
	struct	per_task *ppt;

	if (ptxq->ptxr->nError)
	{
		PostMessage(ptxq->hwnd, ptxq->wMsg, ptxq->id | 0x4000,
			WSAMAKEASYNCREPLY(0, ntohs(ptxq->ptxr->nError)));
		RemoveTXQ(ptxq);
		return;
	}

	ppt = GetAnotherTaskInfo(ptxq->htask);

	if (!ppt)
	{
		/* How **Rude** */
		RemoveTXQ(ptxq);
		return;
	}

	pchData = ptxq->ptxr->pchData +
		ntohs(ptxq->ptxr->nLen) -
		MAX_HOST_ENT -
		sizeof(short) * 5;

	switch(ptxq->ft)
	{
	case FN_HostByName:
	case FN_HostByAddr:
		memcpy(ppt->achHostEnt, pchData, MAX_HOST_ENT);
		nLen = CopyHostEntTo(ppt, ptxq->pchLocation);
		break;

	case FN_ServByName:
	case FN_ServByPort:
		memcpy(ppt->achServEnt, pchData, MAX_HOST_ENT);
		nLen = CopyServEntTo(ppt, ptxq->pchLocation);
		break;

	case FN_ProtoByName:
	case FN_ProtoByNumber:
		memcpy(ppt->achProtoEnt, pchData, MAX_HOST_ENT);
		nLen = CopyProtoEntTo(ppt, ptxq->pchLocation);
		break;
	}

	PostMessage(ptxq->hwnd, ptxq->wMsg, ptxq->id | 0x4000,
		WSAMAKEASYNCREPLY(nLen, 0));
	RemoveTXQ(ptxq);
}

HANDLE pascal far _export WSAAsyncGetServByName(HWND hWnd, u_int wMsg,
                                        const char FAR * name,
					const char FAR * proto,
                                        char FAR * buf, int buflen)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[2];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	tx_queue *txq;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	if (!proto)
		proto = "";
	INIT_CARGS(pfaArgs[0],	AT_String,	name,		strlen(name) + 1	);
	INIT_CARGS(pfaArgs[1],	AT_String,	proto,		strlen(proto) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achServEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_ServByName, 2, pfaArgs, pfaReturn);
	txq = TransmitFunction(&tf);
	txq->hwnd = hWnd;
	txq->pchLocation = buf;
	txq->wMsg = wMsg;
	txq->ft = FN_ServByName;
	txq->htask = ppt->htask;
	return (txq->id | 0x4000);
}

HANDLE pascal far _export WSAAsyncGetServByPort(HWND hWnd, u_int wMsg, int port,
                                        const char FAR * proto, char FAR * buf,
                                        int buflen)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[2];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	tx_queue *txq;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	port = PORTSWAP(port);
	if (!proto)
		proto = "";
	INIT_CARGS(pfaArgs[0],	AT_Int,		&port,		sizeof(port)		);
	INIT_CARGS(pfaArgs[1],	AT_String,	proto,		strlen(proto) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achServEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_ServByPort, 2, pfaArgs, pfaReturn);
	txq = TransmitFunction(&tf);
	txq->hwnd = hWnd;
	txq->pchLocation = buf;
	txq->wMsg = wMsg;
	txq->ft = FN_ServByPort;
	txq->htask = ppt->htask;
	return (txq->id | 0x4000);
}

HANDLE pascal far _export WSAAsyncGetProtoByName(HWND hWnd, u_int wMsg,
                                         const char FAR * name, char FAR * buf,
                                         int buflen)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	tx_queue *txq;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_CARGS(pfaArgs[0],	AT_String,	name,		strlen(name) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achProtoEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_ProtoByName, 1, pfaArgs, pfaReturn);
	txq = TransmitFunction(&tf);
	txq->hwnd = hWnd;
	txq->pchLocation = buf;
	txq->wMsg = wMsg;
	txq->ft = FN_ProtoByName;
	txq->htask = ppt->htask;
	return (txq->id | 0x4000);
}

HANDLE pascal far _export WSAAsyncGetProtoByNumber(HWND hWnd, u_int wMsg,
                                           int number, char FAR * buf,
                                           int buflen)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	tx_queue *txq;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_CARGS(pfaArgs[0],	AT_Int,		&number,	sizeof(number)		);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achProtoEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_ProtoByNumber, 1, pfaArgs, pfaReturn);
	txq = TransmitFunction(&tf);
	txq->hwnd = hWnd;
	txq->pchLocation = buf;
	txq->wMsg = wMsg;
	txq->ft = FN_ProtoByNumber;
	txq->htask = ppt->htask;
	return (txq->id | 0x4000);
}

HANDLE pascal far _export WSAAsyncGetHostByName(HWND hWnd, u_int wMsg,
                                        const char FAR * name, char FAR * buf,
                                        int buflen)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[1];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	tx_queue *txq;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_CARGS(pfaArgs[0],	AT_GenPtr,	name,		strlen(name) + 1	);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achHostEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_HostByName, 1, pfaArgs, pfaReturn);
	txq = TransmitFunction(&tf);
	txq->hwnd = hWnd;
	txq->pchLocation = buf;
	txq->wMsg = wMsg;
	txq->ft = FN_HostByName;
	txq->htask = ppt->htask;
	return (txq->id | 0x4000);
}

HANDLE pascal far _export WSAAsyncGetHostByAddr(HWND hWnd, u_int wMsg,
                                        const char FAR * addr, int len, int type,
                                        char FAR * buf, int buflen)
{
	struct per_task *ppt;
	struct	func_arg pfaArgs[3];
	struct	func_arg pfaReturn;
	struct	transmit_function tf;
	struct	tx_queue *txq;

	if ((ppt = GetTaskInfo()) == 0)
		return 0;
	INIT_CARGS(pfaArgs[0],	AT_GenPtr,	addr,		len			);
	INIT_CARGS(pfaArgs[1],	AT_Int,		&len,		sizeof(len)		);
	INIT_CARGS(pfaArgs[2],	AT_Int,		&type,		sizeof(type)		);
	INIT_ARGS(pfaReturn,	AT_GenPtr,	ppt->achHostEnt,MAX_HOST_ENT		);
	INIT_TF(tf, FN_HostByAddr, 3, pfaArgs, pfaReturn);
	txq = TransmitFunction(&tf);
	txq->hwnd = hWnd;
	txq->pchLocation = buf;
	txq->wMsg = wMsg;
	txq->ft = FN_HostByAddr;
	txq->htask = ppt->htask;
	return (txq->id | 0x4000);
}

int pascal far _export WSACancelAsyncRequest(HANDLE hAsyncTaskHandle)
{
	struct	tx_queue *ptxq;

	if (!GetTaskInfo())
		return -1;

	for (ptxq = ptxqList; ptxq; ptxq = ptxq->ptxqNext)
	{
		if ((HANDLE) (ptxq->id | 0x4000) == hAsyncTaskHandle)
		{
			RemoveTXQ(ptxq);
			return 0;
		}
	}
	iErrno = WSAEINVAL;
	return -1;
}

int pascal far _export WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,
                               long lEvent)
{
	struct	per_task *ppt;
	struct	per_socket *pps;

	if ((ppt = GetTaskInfo()) == 0)
		return -1;
	if ((pps = GetSocketInfo(s)) == 0)
		return -1;
	if (lEvent)
		pps->iFlags |= PSF_NONBLOCK;
	pps->hWnd = hWnd;
	pps->wMsg = wMsg;
	pps->iEvents = lEvent;
	Notify(pps, FD_WRITE);
	if (pps->pdIn)
		Notify(pps, (pps->iFlags & PSF_ACCEPT) ?
				FD_ACCEPT :
				(pps->pdIn->iLen ?
					FD_READ :
					FD_CLOSE));
	return 0;
}

int FAR PASCAL _export _WSAFDIsSet(SOCKET s, fd_set FAR *pfds)
{
	int	i;

	if (!GetTaskInfo())
		return -1;
	if (!GetSocketInfo(s))
		return -1;
	for (i = 0; i < pfds->fd_count; i++)
	{
		if (pfds->fd_array[i] == s)
			return TRUE;
	}
	return FALSE;
}


