/***************************************************************************
 *
 * File: h245ws.c
 *
 * INTEL Corporation Proprietary Information
 * Copyright (c) 1996 Intel Corporation.
 *
 * This listing is supplied under the terms of a license agreement
 * with INTEL Corporation and may not be used, copied, nor disclosed
 * except in accordance with the terms of that agreement.
 *
 ***************************************************************************
 *
 *
 * $Workfile:   h245ws.cpp  $
 * $Revision:   2.11  $
 * $Modtime:   31 Jan 1997 19:22:28  $
 * $Log:   S:\sturgeon\src\h245ws\vcs\h245ws.cpv  $
 * 
 *    Rev 2.11   31 Jan 1997 20:24:34   SBELL1
 * Relinquished CallControl Stack lock before DefWindowProc
 * 
 *    Rev 2.10   31 Jan 1997 14:54:12   EHOWARDX
 * Added CCLOCK support.
 *
 *    Rev 2.9   20 Jan 1997 20:42:34   SBELL1
 * Fixed GPF when shutting down.
 * 
 *    Rev 2.8   07 Jan 1997 11:51:48   EHOWARDX
 * 
 * Fixed "assignment within conditional expression" warning
 * in GetLinkLayerInstance().
 * 
 *    Rev 2.7   03 Jan 1997 13:15:18   EHOWARDX
 * Attempt at workaround for #1718 linkLayerListen() returns WSAENOBUFS.
 * 
 *    Rev 2.6   23 Dec 1996 15:30:16   EHOWARDX
 * 
 * Set window to zero after call to destroy window -- Uninitialize seems
 * to be called more than once.
 * 
 *    Rev 2.5   20 Dec 1996 17:49:10   SBELL1
 * changed to blocking mode before closesocket in SocketClose. 
 * This makes the linger work.
 * 
 *    Rev 2.4   19 Dec 1996 19:03:18   SBELL1
 * Moved Initialize to linkLayerInit
 * Set linger option on accept socket
 * reversed "new" change 
 * 
 *    Rev 2.3   Dec 13 1996 17:12:36   plantz
 * fixed string for UNICODE.
// 
//    Rev 1.3   13 Dec 1996 14:32:10   SBELL1
// fixed string for UNICODE.
// 
//    Rev 1.1   12 Dec 1996 17:59:02   SBELL1
// Fixed bug in lingering on Q.931 Listen socket.
// 
//    Rev 1.0   11 Dec 1996 13:41:14   SBELL1
// Initial revision.
 * 
 *    Rev 1.46   18 Oct 1996 16:46:12   EHOWARDX
 * 
 * Changed GetIpAddress to take wide char cAddr field.
 * 
 *    Rev 1.45   Oct 01 1996 14:29:56   EHOWARDX
 * Moved Initialize() and Unitialize() calls to DllMain().
 * 
 *    Rev 1.44   26 Sep 1996 18:52:10   EHOWARDX
 * 
 * Moved some initialization around to prevent possible ASSERTion failure
 * in SocketClose().
 * 
 *    Rev 1.43   15 Aug 1996 13:59:00   rodellx
 * 
 * Added additional address validation for DOMAIN_NAME addresses
 * which cannot be resolved, but are used with SocketBind().
 * 
 *    Rev 1.42   Aug 07 1996 14:38:00   mandrews
 * Set bMulticast field of CC_ADDR structures correctly.
 * 
 *    Rev 1.41   24 Jul 1996 11:53:02   EHOWARDX
 * Changed ADDR to CC_ADDR, IP_XXX to CC_IP_XXX.
 * Fixed bug in SocketCloseEvent - needed to revalidate pHws after callback.
 * 
 *    Rev 1.40   08 Jul 1996 19:27:18   unknown
 * Second experiment to try to fix Q.931 shutdown problem.
 * 
 *    Rev 1.39   02 Jul 1996 16:23:02   EHOWARDX
 * Backed out experimental change.
 * 
 *    Rev 1.37   28 Jun 1996 18:06:50   unknown
 * Added breaks to GetPort.
 * 
 *    Rev 1.36   27 Jun 1996 14:06:06   EHOWARDX
 * Byte-swapped port number for debug trace in linkLayerListen & linkLayerConn
 * 
 *    Rev 1.35   21 Jun 1996 18:52:14   unknown
 * Fixed yet another shutdown bug - linkLayerShutdown re-entrancy check.
 * 
 *    Rev 1.34   18 Jun 1996 16:56:20   EHOWARDX
 * Added check to see if callback deallocated our instance to SocketConnect().
 * 
 *    Rev 1.33   17 Jun 1996 13:23:48   EHOWARDX
 * Workaround for PostQuitMessage() bug.
 * 
 *    Rev 1.32   12 Jun 1996 11:43:26   EHOWARDX
 * Changed linkLayerConnect errors from HWS_CRITICAL to HWS_WARNING.
 * 
 *    Rev 1.31   May 28 1996 18:14:00   plantz
 * Change error codes to use HRESULT. Propogate Winsock errors where appropriate
 * 
 *    Rev 1.30   May 28 1996 10:38:08   plantz
 * Change sprintf to wsprintf.
 * 
 *    Rev 1.29   17 May 1996 16:49:24   EHOWARDX
 * Shutdown fix.
 * 
 *    Rev 1.28   16 May 1996 13:09:18   EHOWARDX
 * Made reporting of IP Addres and port consistent between linkLayerListen
 * and LinkLayerConnect.
 * 
 *    Rev 1.27   14 May 1996 11:31:50   EHOWARDX
 * Fixed bug with doing another connect on instance that failed previous
 * connect. Instance now returns LINK_INVALID_STATE, and must be closed
 * and reopened.
 * 
 *    Rev 1.26   09 May 1996 18:33:22   EHOWARDX
 * 
 * Changes to build with new LINKAPI.H.
 * 
 *    Rev 1.25   Apr 29 1996 19:06:48   plantz
 * Reenable code to try to send all messages when shutting down.
 * 
 *    Rev 1.24   Apr 29 1996 14:02:58   plantz
 * Add NotifyRead and NotifyWrite functions.
 * Delete unused function FindH245Instance.
 * .
 * 
 *    Rev 1.23   Apr 25 1996 21:16:26   plantz
 * Add connect callback parameter to linkLayerAccept.
 * Pass message type to connect callback.
 * 
 *    Rev 1.22   Apr 24 1996 20:49:30   plantz
 * Listen on address passed to linkLayerListen; use INADDR_ANY if it is 0.
 * Return the result of getsockname after listening.
 * Add a callback parameter to linkLayerConnect. Call it when FD_CONNECT event
 * occurs and after calling accept, passing error code and local and peer
 * addresses.
 * 
 *    Rev 1.21   Apr 24 1996 16:55:20   plantz
 * Merge 1.15.1.0 with 1.20 (winsock 1 changes)
 * 
 *    Rev 1.20   19 Apr 1996 18:28:50   EHOWARDX
 * Changed Send and receive flush to call send and receive callback with
 * LINK_FLUSH_COMPLETE message to more accurately emulate behavior
 * of H245SRP.DLL.
 * 
 *    Rev 1.19   19 Apr 1996 10:34:26   EHOWARDX
 * Updated to latest LINKAPI.H - WINAPI keywork eliminated.
 * 
 *    Rev 1.18   12 Apr 1996 19:17:02   unknown
 * Removed annoying trace message.
 * 
 *    Rev 1.17   11 Apr 1996 14:53:22   EHOWARDX
 * Changed to include INCOMMON.H instead of CALLCONT.H.
 * 
 *    Rev 1.16   04 Apr 1996 12:35:04   EHOWARDX
 * Valiantly trying to track never-ending changes to Link Layer API
 * (Thanks Dan! Changing linkLayerGetInstId to linkLayerGetInstance() --
 * what a stroke of genius!)
 * 
 *    Rev 1.15.1.0   Apr 24 1996 16:23:02   plantz
 * Change to use winsock 1.
 * 
 *    Rev 1.15   03 Apr 1996 16:35:46   EHOWARDX
 * CLOSED state no longer implies that we have a thread;
 * replaced ASSERT with if statement.
 * 
 *    Rev 1.14   03 Apr 1996 14:52:24   EHOWARDX
 * Fixed yet another shutdown problem.
 * 
 *    Rev 1.13   02 Apr 1996 18:28:50   EHOWARDX
 * Added ProcessQueuedRecvs() to SocketAccept().
 * 
 *    Rev 1.12   01 Apr 1996 16:25:46   EHOWARDX
 * Experiment with calling ProcessRecvQueue on FD_WRITE event.
 * 
 *    Rev 1.11   01 Apr 1996 14:20:40   unknown
 * Shutdown redesign.
 * 
 *    Rev 1.10   29 Mar 1996 11:12:56   EHOWARDX
 * Added line to SocketClose to set state to HWS_CLOSED.
 * 
 *    Rev 1.9   27 Mar 1996 13:00:30   EHOWARDX
 * Added dwThreadId to H245WS instance structure.
 * Reversed shutdown loop to check state first BEFORE checking send queue.
 * 
 *    Rev 1.8   22 Mar 1996 10:54:20   unknown
 * 
 * Minor change in trace text.
 * 
 *    Rev 1.7   20 Mar 1996 14:11:20   unknown
 * Added Sleep(0) to bind retry loop.
 * 
 *    Rev 1.6   19 Mar 1996 20:21:56   EHOWARDX
 * Redesigned shutdown.
 * 
 *    Rev 1.4   18 Mar 1996 19:08:28   EHOWARDX
 * Fixed shutdown; eliminated TPKT/WSCB dependencies.
 * Define TPKT to put TPKT/WSCB dependencies back in.
 * 
 *    Rev 1.3   14 Mar 1996 17:01:46   EHOWARDX
 * 
 * NT4.0 testing; got rid of HwsAssert(); got rid of TPKT/WSCB.
 * 
 *    Rev 1.2   09 Mar 1996 21:12:26   EHOWARDX
 * Fixes as result of testing.
 * 
 *    Rev 1.1   08 Mar 1996 20:24:24   unknown
 * This is the real version of the main h245ws.dll code.
 * Version 1.0 was a stub version created by Mike Andrews.
 *
 ***************************************************************************/

#define LINKDLL_EXPORT
#undef _WIN32_WINNT	// override bogus platform definition in our common build environment
#pragma warning ( disable : 4115 4201 4214 4514 )

#include "precomp.h"

//#include <winsock.h>
#include "queue.h"
#include "linkapi.h"
#include "incommon.h"
#include "h245ws.h"
#include "tstable.h"
#ifdef FORCE_SERIALIZE_CALL_CONTROL
#include "cclock.h"
#endif

#include "q931.h"
#include "hcall.h"

#if defined(__cplusplus)
extern "C"
{
#endif  // (__cplusplus)

// REVIEW: Should we use the newer definition from winsock2.h ?
#undef FD_ALL_EVENTS
#define FD_ALL_EVENTS (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE)
#define WINSOCK_EVENT_MSG (WM_USER+1)

static HRESULT SocketOpen(PHWSINST pHws, BOOL bSetLinger = TRUE);
void Uninitialize();

/*
 * Static variables
 */
static BOOL bInitialized = FALSE;
static HWND        window   = 0;
TSTable<HWSINST>* gpInstanceTable;	// global ptr to the instance table
static CRITICAL_SECTION SocketToHWSInstanceMapLock;
static SOCKET_TO_INSTANCE *pSocketToHWSInstanceMap = NULL;

// If we are not using the Unicode enabled tracing, then undefine the __TEXT macro.
// Do not place anything that should be a Unicode constant string between this #undef
// and the corresponding redefinition of the macro

#if defined(_DEBUG)
#ifndef UNICODE_TRACE
#undef  __TEXT
#define __TEXT(x) x

static const char * StateMap[] =

#else

static const LPTSTR StateMap[] =

#endif
{
   __TEXT("HWS_START"),         // 0  Initial state
   __TEXT("HWS_LISTENING"),     // 1  Waiting for FD_ACCEPT
   __TEXT("HWS_CONNECTING"),    // 2  Waiting for FD_CONNECT
   __TEXT("HWS_CONNECTED"),     // 3  Data transfer state
   __TEXT("HWS_CLOSING"),       // 4  Waiting for FD_CLOSE
   __TEXT("HWS_CLOSED"),        // 5  Waiting for linkLayerShutdown()
   __TEXT("HWS_SHUTDOWN"),      // 6  linkLayerShutdown() called from callback
};


static const ERROR_MAP ErrorMap[] =
{
   0,                     __TEXT("OK"),
   WSAEINTR,              __TEXT("WSAEINTR - Interrupted function call"),
   WSAEBADF,              __TEXT("WSAEBADF"),
   WSAEACCES,             __TEXT("WSAEACCES - Permission denied"),
   WSAEFAULT,             __TEXT("WSAEFAULT - Bad address"),
   WSAEINVAL,             __TEXT("WSAEINVAL - Invalid argument"),
   WSAEMFILE,             __TEXT("WSAEMFILE - Too many open files"),
   WSAEWOULDBLOCK,        __TEXT("WSAEWOULDBLOCK - Resource temporarily unavailable"),
   WSAEINPROGRESS,        __TEXT("WSAEINPROGRESS - Operation now in progress"),
   WSAEALREADY,           __TEXT("WSAEALREADY - Operation already in progress"),
   WSAENOTSOCK,           __TEXT("WSAENOTSOCK - Socket operation on non-socket"),
   WSAEDESTADDRREQ,       __TEXT("WSAEDESTADDRREQ - Destination address required"),
   WSAEMSGSIZE,           __TEXT("WSAEMSGSIZE - Message too long"),
   WSAEPROTOTYPE,         __TEXT("WSAEPROTOTYPE - Protocol wrong type for socket"),
   WSAENOPROTOOPT,        __TEXT("WSAENOPROTOOPT - Bad protocol option"),
   WSAEPROTONOSUPPORT,    __TEXT("WSAEPROTONOSUPPORT - Protocol not supported"),
   WSAESOCKTNOSUPPORT,    __TEXT("WSAESOCKTNOSUPPORT - Socket type not supported"),
   WSAEOPNOTSUPP,         __TEXT("WSAEOPNOTSUPP - Operation not supported"),
   WSAEPFNOSUPPORT,       __TEXT("WSAEPFNOSUPPORT - Protocol family not supported"),
   WSAEAFNOSUPPORT,       __TEXT("WSAEAFNOSUPPORT - Address family not supported by protocol family"),
   WSAEADDRINUSE,         __TEXT("WSAEADDRINUSE - Address already in use"),
   WSAEADDRNOTAVAIL,      __TEXT("WSAEADDRNOTAVAIL - Cannot assign requested address"),
   WSAENETDOWN,           __TEXT("WSAENETDOWN - Network is down"),
   WSAENETUNREACH,        __TEXT("WSAENETUNREACH - Network is unreachable"),
   WSAENETRESET,          __TEXT("WSAENETRESET - Network dropped connection on reset"),
   WSAECONNABORTED,       __TEXT("WSAECONNABORTED - Software caused connection abort"),
   WSAECONNRESET,         __TEXT("WSAECONNRESET - Connection reset by peer"),
   WSAENOBUFS,            __TEXT("WSAENOBUFS - No buffer space available"),
   WSAEISCONN,            __TEXT("WSAEISCONN - Socket is already connected"),
   WSAENOTCONN,           __TEXT("WSAENOTCONN - Socket is not connected"),
   WSAESHUTDOWN,          __TEXT("WSAESHUTDOWN - Cannot send after socket shutdown"),
   WSAETOOMANYREFS,       __TEXT("WSAETOOMANYREFS"),
   WSAETIMEDOUT,          __TEXT("WSAETIMEDOUT - Connection timed out"),
   WSAECONNREFUSED,       __TEXT("WSAECONNREFUSED - Connection refused"),
   WSAELOOP,              __TEXT("WSAELOOP"),
   WSAENAMETOOLONG,       __TEXT("WSAENAMETOOLONG"),
   WSAEHOSTDOWN,          __TEXT("WSAEHOSTDOWN - Host is down"),
   WSAEHOSTUNREACH,       __TEXT("WSAEHOSTUNREACH - No route to host"),
   WSAENOTEMPTY,          __TEXT("WSAENOTEMPTY"),
   WSAEPROCLIM,           __TEXT("WSAEPROCLIM - Too many processes"),
   WSAEUSERS,             __TEXT("WSAEUSERS"),
   WSAEDQUOT,             __TEXT("WSAEDQUOT"),
   WSAESTALE,             __TEXT("WSAESTALE"),
   WSAEREMOTE,            __TEXT("WSAEREMOTE"),
   WSASYSNOTREADY,        __TEXT("WSASYSNOTREADY - Network subsystem is unavailable"),
   WSAVERNOTSUPPORTED,    __TEXT("WSAVERNOTSUPPORTED - WINSOCK.DLL version out of range"),
   WSANOTINITIALISED,     __TEXT("WSANOTINITIALISED - Successful WSAStartup() not yet performed"),
   WSAEDISCON,            __TEXT("WSAEDISCON - Graceful shutdown in progress"),
   WSAHOST_NOT_FOUND,     __TEXT("WSAHOST_NOT_FOUND - Host not found"),
   WSATRY_AGAIN,          __TEXT("WSATRY_AGAIN - Non-authoritative host not found"),
   WSANO_RECOVERY,        __TEXT("WSANO_RECOVERY - This is a non-recoverable error"),
   WSANO_DATA,            __TEXT("WSANO_DATA - Valid name, no data record of requested type"),
//   WSA_INVALID_HANDLE,    __TEXT("WSA_INVALID_HANDLE - Specified event object handle is invalid"),
//   WSA_INVALID_PARAMETER, __TEXT("WSA_INVALID_PARAMETER - One or more parameters are invalid"),
//   WSA_IO_PENDING,        __TEXT("WSA_IO_PENDING - Overlapped operations will complete later"),
//   WSA_IO_INCOMPLETE,     __TEXT("WSA_IO_INCOMPLETE - Overlapped I/O event object not in signaled state"),
//   WSA_NOT_ENOUGH_MEMORY, __TEXT("WSA_NOT_ENOUGH_MEMORY - Insufficient memory available"),
//   WSA_OPERATION_ABORTED, __TEXT("WSA_OPERATION_ABORTED - Overlapped operation aborted"),
};



//
// Function Definitions
//

#include <string.h>

#ifdef UNICODE_TRACE
static const LPTSTR
#else
static const char *
#endif
SocketStateText(UINT uState)
{
#ifdef UNICODE_TRACE
   static TCHAR      szSocketStateText[80];
#else
   static char       szSocketStateText[80];
#endif

   if (uState <= HWS_SHUTDOWN)
      return StateMap[uState];
   wsprintf(szSocketStateText, __TEXT("Unknown state %d"), uState);
   return szSocketStateText;
} // SocketStateText()



#ifdef UNICODE_TRACE
static LPCTSTR
#else
static const char *
#endif
SocketErrorText1(int nErrorCode)
{
   register int      nIndex = sizeof(ErrorMap) / sizeof(ErrorMap[0]);
#ifdef UNICODE_TRACE
   static TCHAR      szSocketErrorText[80];
#else
   static char       szSocketErrorText[80];
#endif


   while (nIndex > 0)
   {
      if (ErrorMap[--nIndex].nErrorCode == nErrorCode)
      {
         return ErrorMap[nIndex].pszErrorText;
      }
   }
   wsprintf(szSocketErrorText, __TEXT("Unknown error 0x%x"), nErrorCode);
   return szSocketErrorText;
} // SocketErrorText1()

#ifdef UNICODE_TRACE
LPCTSTR
#else
const char *
#endif
SocketErrorText(void)
{
   return SocketErrorText1(WSAGetLastError());
} // SocketErrorText()

#endif  // (_DEBUG)


/***************************************************************************
 *
 * Local routines
 *
 ***************************************************************************/

static DWORD
HashSocket(SOCKET socket)
{
	return(DWORD)((socket >> 2) % SOCK_TO_PHYSID_TABLE_SIZE);
}



DWORD
SocketToPhysicalId(SOCKET socket)
{
	// hash the socket to get an index into the SocketToHWSInstanceMap table
	DWORD idx = HashSocket(socket);
	if(pSocketToHWSInstanceMap == NULL)
		return(INVALID_PHYS_ID);

	EnterCriticalSection(&SocketToHWSInstanceMapLock);
	
	// idx indicates the entry point in the array, now traverse the linked list
	PSOCKET_TO_INSTANCE pEntry = &pSocketToHWSInstanceMap[idx]; 
	while(pEntry != NULL) 
	{
		if(pEntry->socket == socket)
		{
			LeaveCriticalSection(&SocketToHWSInstanceMapLock);
			return(pEntry->dwPhysicalId);
		} else
		{
			pEntry = pEntry->next;
		}
	}

	LeaveCriticalSection(&SocketToHWSInstanceMapLock);
	return(INVALID_PHYS_ID);

} // SocketToPhysicalId()

BOOL
CreateSocketToPhysicalIdMapping(SOCKET socket, DWORD dwPhysicalId)
{
	// hash the socket to get an index into the SocketToHWSInstanceMap table
	DWORD idx = HashSocket(socket);
	
	EnterCriticalSection(&SocketToHWSInstanceMapLock);
	
	// idx indicates the entry point in the array, now traverse the linked list
	PSOCKET_TO_INSTANCE pEntry = &pSocketToHWSInstanceMap[idx]; 
	PSOCKET_TO_INSTANCE pNewEntry;
	if (pEntry->socket == INVALID_SOCKET) 
	{
		pNewEntry = pEntry; 
	} else
	{
		pNewEntry = new SOCKET_TO_INSTANCE;
		if (pNewEntry == NULL) 
		{
			LeaveCriticalSection(&SocketToHWSInstanceMapLock);
			return(FALSE);
		}
		pNewEntry->next = pEntry->next;
		pEntry->next = pNewEntry;
	}

	pNewEntry->socket = socket;
	pNewEntry->dwPhysicalId = dwPhysicalId;

	LeaveCriticalSection(&SocketToHWSInstanceMapLock);
	return(TRUE);
}

BOOL
RemoveSocketToPhysicalIdMapping(SOCKET socket)
{

	if (socket == INVALID_SOCKET)
		return(FALSE);

	// hash the socket to get an index into the SocketToHWSInstanceMap table
	DWORD idx = HashSocket(socket);
	BOOL bFound = FALSE;

	EnterCriticalSection(&SocketToHWSInstanceMapLock);
	
	// idx indicates the entry point in the array, now traverse the linked list
	PSOCKET_TO_INSTANCE pEntry = &pSocketToHWSInstanceMap[idx]; 
	if (pEntry->socket == socket) 
	{
		pEntry->socket = INVALID_SOCKET;
		bFound = TRUE;
	} else
	{
		PSOCKET_TO_INSTANCE pNextEntry;
		pNextEntry = pEntry->next;
		while (bFound == FALSE && pNextEntry != NULL)
		{
			if(pNextEntry->socket == socket)
			{
				pEntry->next = pNextEntry->next;
				delete pNextEntry;
				bFound = TRUE;
			} else
			{
				pEntry     = pNextEntry;
				pNextEntry = pNextEntry->next;
			}
		}
	}

	LeaveCriticalSection(&SocketToHWSInstanceMapLock);
	return(bFound);
}

static unsigned short GetPort (CC_ADDR *pAddr)
{
   unsigned short int port = 0;
   switch (pAddr->nAddrType)
   {
   case CC_IP_DOMAIN_NAME:
      port = pAddr->Addr.IP_DomainName.wPort;
      break;

   case CC_IP_DOT:
      port = pAddr->Addr.IP_Dot.wPort;
      break;

   case CC_IP_BINARY:
      port = pAddr->Addr.IP_Binary.wPort;
      break;

   } // switch

   return htons(port);
} // GetPort()


static unsigned long GetIPAddress (CC_ADDR *pAddr)
{
   struct hostent *     pHostEnt;
   char                 szAddr[256];

   switch (pAddr->nAddrType)
   {
   case CC_IP_DOMAIN_NAME:
      WideCharToMultiByte(CP_ACP,           // code page
                          0,                // dwFlags
                          pAddr->Addr.IP_DomainName.cAddr,
                          -1,               // Unicode string length (bytes)
                          szAddr,           // ASCII string
                          sizeof(szAddr),   // max ASCII string length
                          NULL,             // default character
                          NULL);            // default character used
      pHostEnt = gethostbyname(szAddr);
      if (pHostEnt == NULL || pHostEnt->h_addr_list == NULL)
         return 0;
      return *((unsigned long *)pHostEnt->h_addr_list[0]);

   case CC_IP_DOT:
      WideCharToMultiByte(CP_ACP,           // code page
                          0,                // dwFlags
                          pAddr->Addr.IP_Dot.cAddr,
                          -1,               // Unicode string length (bytes)
                          szAddr,           // ASCII string
                          sizeof(szAddr),   // max ASCII string length
                          NULL,             // default character
                          NULL);            // default character used
      return inet_addr(szAddr);

   case CC_IP_BINARY:
       return pAddr->Addr.IP_Binary.dwAddr == 0 ? INADDR_ANY : htonl(pAddr->Addr.IP_Binary.dwAddr);
   } // switch
   return 0;
} // GetIPAddress()


static HRESULT GetLocalAddr(PHWSINST pHws, CC_ADDR *pAddr)
{
    SOCKADDR_IN sockaddr;
    int len = sizeof(sockaddr);

    if (getsockname(pHws->hws_Socket,
                    (struct sockaddr *)&sockaddr,
                    &len) == SOCKET_ERROR)
    {
        return MAKE_WINSOCK_ERROR(WSAGetLastError());
    }

    pAddr->nAddrType = CC_IP_BINARY;
	pAddr->bMulticast = FALSE;
    pAddr->Addr.IP_Binary.wPort = ntohs(sockaddr.sin_port);
    pAddr->Addr.IP_Binary.dwAddr = ntohl(sockaddr.sin_addr.S_un.S_addr);

    return NOERROR;
}

static HRESULT GetPeerAddr(PHWSINST pHws, CC_ADDR *pAddr)
{
    SOCKADDR_IN sockaddr;
    int len = sizeof(sockaddr);

    if (getpeername(pHws->hws_Socket,
                    (struct sockaddr *)&sockaddr,
                    &len) == SOCKET_ERROR)
    {
        return MAKE_WINSOCK_ERROR(WSAGetLastError());
    }

    pAddr->nAddrType = CC_IP_BINARY;
	pAddr->bMulticast = FALSE;
    pAddr->Addr.IP_Binary.wPort = ntohs(sockaddr.sin_port);
    pAddr->Addr.IP_Binary.dwAddr = ntohl(sockaddr.sin_addr.S_un.S_addr);

    return NOERROR;
}


static void
SocketFlushRecv(PHWSINST pHws)
{
   register PREQUEST    pReq;

   if (pHws->hws_pRecvQueue)
   {
      while ((pReq = (PREQUEST) QRemove(pHws->hws_pRecvQueue)) != NULL)
      {
         pHws->hws_h245RecvCallback(pHws->hws_dwH245Instance,
                                    LINK_RECV_ABORT,
                                    pReq->req_client_data,
                                    0);
         MemFree(pReq);
      }
   }
   pHws->hws_h245RecvCallback(pHws->hws_dwH245Instance,
                              LINK_FLUSH_COMPLETE,
                              NULL,
                              0);
} // SocketFlushRecv()



static void
SocketFlushSend(PHWSINST pHws)
{
   register PREQUEST    pReq;

   if (pHws->hws_pSendQueue)
   {
      while ((pReq = (PREQUEST)  QRemove(pHws->hws_pSendQueue)) != NULL)
      {
         pHws->hws_h245SendCallback(pHws->hws_dwH245Instance,
                                    LINK_SEND_ABORT,
                                    pReq->req_client_data,
                                    0);
         MemFree(pReq);
      }
   }
   pHws->hws_h245SendCallback(pHws->hws_dwH245Instance,
                              LINK_FLUSH_COMPLETE,
                              NULL,
                              0);
} // SocketFlushSend()



void
SocketCloseEvent(PHWSINST pHws)
{
   HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE, __TEXT("SocketCloseEvent"));
   if (pHws->hws_uState == HWS_CONNECTED)
   {
      register DWORD dwPhysicalId = pHws->hws_dwPhysicalId;
      pHws->hws_h245RecvCallback(pHws->hws_dwH245Instance,LINK_RECV_CLOSED,0,0);

      // Check to see if callback deallocated our instance or state changed

      if(gpInstanceTable->Validate(dwPhysicalId) == FALSE)
        return;

      HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE,
				__TEXT("SocketCloseEvent: calling shutdown"));
      if (shutdown(pHws->hws_Socket, 1) == SOCKET_ERROR)
      {
         HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                  __TEXT("HandleNetworkEvent: shutdown() returned %s"),
                  SocketErrorText());
      }
   }
   pHws->hws_uState = HWS_CLOSED;
} // SocketCloseEvent()



/*
 * DESCRIPTION
 *    Deallocate all allocated objects except for task handle
 */

static void
SocketClose(PHWSINST pHws)
{
   HWSASSERT(pHws != NULL);
   HWSASSERT(pHws->hws_dwMagic == HWSINST_MAGIC);
   HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE, __TEXT("SocketClose"));

   pHws->hws_uState = HWS_CLOSED;

   RemoveSocketToPhysicalIdMapping(pHws->hws_Socket);

   // Close the socket
   if (pHws->hws_Socket != INVALID_SOCKET)
   {
      // To make the linger work, turn off WSAAsyncSelect to turn off 
      // non-blocking via ioctlsocket, to close the socket.
      unsigned long blocking = 0;
      HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE,
                __TEXT("SocketClose: calling closesocket"));
         
      WSAAsyncSelect(pHws->hws_Socket,
                     window, WINSOCK_EVENT_MSG,
                     0);
      ioctlsocket(pHws->hws_Socket, FIONBIO,&blocking);
      if (closesocket(pHws->hws_Socket) == SOCKET_ERROR)
      {
         HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                   __TEXT("SocketClose: closesocket() returned %s"),
                   SocketErrorText());
      }
      pHws->hws_Socket = INVALID_SOCKET;
   }

} // SocketClose()



static HRESULT
SocketOpen(PHWSINST pHws, BOOL bSetLinger)
{
   HWSASSERT(pHws != NULL);
   HWSASSERT(pHws->hws_dwMagic == HWSINST_MAGIC);
   HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE, __TEXT("SocketOpen"));

   // Create a socket
   pHws->hws_Socket = socket(AF_INET, SOCK_STREAM, 0);
   if (pHws->hws_Socket == INVALID_SOCKET)
   {
      // WSASocket() failed
      int err = WSAGetLastError();
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                __TEXT("SocketOpen: socket() returned %s"),
                SocketErrorText1(err));
      SocketClose(pHws);
      return MAKE_WINSOCK_ERROR(err);
   }

   /*
   ** Request notification messages for all events on this socket.
   ** Note that this call automatically puts the socket into non-blocking
   ** mode, as if we had called WSAIoctl with the FIONBIO flag.
   **/
   if (WSAAsyncSelect(pHws->hws_Socket,
                      window, WINSOCK_EVENT_MSG,
                      FD_ALL_EVENTS) == SOCKET_ERROR)
   {
      int err = WSAGetLastError();
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                  __TEXT("SocketOpen: WSAASyncSelect() returned %s"),
                  SocketErrorText1(err));
      SocketClose(pHws);
      return MAKE_WINSOCK_ERROR(err);
   }

   if(bSetLinger == TRUE)
   {
		// Set a linger structure for the socket so that closesocket will block for a period of time (until all
		// data is sent or the timeout value) before actually killing the connection.
		// This change is being made in order to get rid of the PeekMessage() loops in linklayerShutdown().
		struct linger sockLinger;
		sockLinger.l_onoff = 1;				// yes we want to linger (wait for FIN ACK after sending data and FIN)
		sockLinger.l_linger = 1;			// linger for up to 1 second

	 
		if(setsockopt (pHws->hws_Socket,
								SOL_SOCKET,
								SO_LINGER,
								(const char*) &sockLinger,
								sizeof(sockLinger)) == SOCKET_ERROR)
		{
            int err = WSAGetLastError();
            HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                  __TEXT("SocketOpen: setsockopt returned %s"),
                  SocketErrorText1(err));
            SocketClose(pHws);
            return MAKE_WINSOCK_ERROR(err);
		}

	 }

   // add an entry in the socket to instance map
	if(CreateSocketToPhysicalIdMapping(pHws->hws_Socket, pHws->hws_dwPhysicalId) != TRUE)
	{
		HWSTRACE0(pHws->hws_dwPhysicalId, HWS_WARNING,
			__TEXT("SocketOpen: CreateSocketToPhysicalIdMapping() failed"));
		SocketClose(pHws);
		return(LINK_MEM_FAILURE);
	}

   return NOERROR;
} // SocketOpen()



static HRESULT
SocketBind(PHWSINST pHws, CC_ADDR *pAddr)
{
   HWSASSERT(pHws != NULL);
   HWSASSERT(pHws->hws_dwMagic == HWSINST_MAGIC);
   HWSASSERT(pAddr != NULL);
   HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE, __TEXT("SocketBind"));

   // Get a local address to bind the socket to
   pHws->hws_SockAddr.sin_family           = AF_INET;
   pHws->hws_SockAddr.sin_port             = GetPort(pAddr);
   pHws->hws_SockAddr.sin_addr.S_un.S_addr = GetIPAddress(pAddr);
   if ((pAddr->nAddrType == CC_IP_DOMAIN_NAME) &&
           (pHws->hws_SockAddr.sin_addr.S_un.S_addr == 0))
   {
       return LINK_UNKNOWN_ADDR;
   }

   // Bind the socket
   while (bind(pHws->hws_Socket,                            // s
               (const struct sockaddr *)&pHws->hws_SockAddr, // name
               sizeof(pHws->hws_SockAddr)) == SOCKET_ERROR)      // namelen
   {
      // bind() failed
      int err = WSAGetLastError();
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                __TEXT("SocketBind: bind() returned %s"),
                SocketErrorText1(err));
      if (err != WSAENOBUFS)
      {
         return MAKE_WINSOCK_ERROR(err);
      }
      Sleep(0);
   }

   return NOERROR;
} // SocketBind()

static void
SocketConnect(PHWSINST pHws, int error)
{
   HWSASSERT(pHws != NULL);
   HWSASSERT(pHws->hws_dwMagic == HWSINST_MAGIC);
   HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE, __TEXT("SocketConnect"));

   if (error == 0)
   {
       pHws->hws_uState = HWS_CONNECTED;

       if (pHws->hws_h245ConnectCallback)
       {
            CC_ADDR   LocalAddr;
            CC_ADDR   PeerAddr;
            PCC_ADDR  pLocalAddr = &LocalAddr;
            PCC_ADDR  pPeerAddr  = &PeerAddr;
            DWORD     dwPhysicalId = pHws->hws_dwPhysicalId;
            HRESULT     hr;

            hr = GetLocalAddr(pHws, pLocalAddr);
            if (FAILED(hr))
            {
                WARNING_OUT(("SocketConnect - failed to get local address w/ error 0x%08x", hr));
                pLocalAddr = NULL;
            }
            else
            {
                hr = GetPeerAddr(pHws, pPeerAddr);
                if (FAILED(hr))
                {
                    WARNING_OUT(("SocketConnect - failed to get remote address w/ error 0x%08x", hr));
                    pPeerAddr = NULL;
                }
                else
                {
                    // All is OK
                    hr = LINK_CONNECT_COMPLETE;
                }
            }

            pHws->hws_h245ConnectCallback(pHws->hws_dwH245Instance,
                hr, pLocalAddr, pPeerAddr);

            // Check to see if callback deallocated our instance - this can be done
		    // by attempting a lock - which will now fail if the entry has been marked
		    // for deletion.  Thus, if the lock succeeds, then just unlock it (since we 
		    // already have a lock on it).

		    if (gpInstanceTable->Validate(dwPhysicalId) == FALSE)
			    return;
       }

       NotifyWrite(pHws);
       NotifyRead(pHws);
   }
   else
   {
      if (pHws->hws_h245ConnectCallback)
      {
         pHws->hws_h245ConnectCallback(pHws->hws_dwH245Instance,
                                       MAKE_WINSOCK_ERROR(error), NULL, NULL);
      }
   }
} // SocketConnect()

static HRESULT
SocketAccept(PHWSINST pHwsListen, PHWSINST pHwsAccept)
{
   SOCKET listen_socket = pHwsListen->hws_Socket;
   struct linger sockLinger;
   sockLinger.l_onoff = 1;	// yes we want to linger (wait for FIN ACK after sending data and FIN)
   sockLinger.l_linger = 1;	// linger for up to 1 second

   // Accept the connection.
   pHwsAccept->hws_uSockAddrLen = sizeof(pHwsAccept->hws_SockAddr);
   pHwsAccept->hws_Socket = accept(pHwsListen->hws_Socket,
                       (struct sockaddr *)&pHwsAccept->hws_SockAddr,
                       (int *)&pHwsAccept->hws_uSockAddrLen);

   if (pHwsAccept->hws_Socket == INVALID_SOCKET)
   {
      int err = WSAGetLastError();
      HWSTRACE1(pHwsAccept->hws_dwPhysicalId, HWS_WARNING,
                __TEXT("linkLayerAccept: accept() returned %s"),
                SocketErrorText1(err));
      SocketConnect(pHwsAccept, err);
      return MAKE_WINSOCK_ERROR(err);
   }

   if (pHwsListen == pHwsAccept)
   {
      HWSTRACE0(pHwsListen->hws_dwPhysicalId, HWS_TRACE,
                __TEXT("SocketClose: calling closesocket"));
      closesocket(listen_socket);
      RemoveSocketToPhysicalIdMapping(listen_socket);
   }

   
   // Set a linger structure for the socket so that closesocket will block for a period of time (until all
   // data is sent or the timeout value) before actually killing the connection.
   // This change is being made in order to get rid of the PeekMessage() loops in linklayerShutdown().
   if(setsockopt (pHwsAccept->hws_Socket,
                  SOL_SOCKET,
	     		  SO_LINGER,
                  (const char*) &sockLinger,
                  sizeof(sockLinger)) == SOCKET_ERROR)
   {
      int err = WSAGetLastError();
      HWSTRACE1(pHwsAccept->hws_dwPhysicalId, HWS_WARNING,
                  __TEXT("SocketAccept: setsockopt returned %s"),
                  SocketErrorText1(err));
      SocketClose(pHwsAccept);
      return MAKE_WINSOCK_ERROR(err);
   }
   

   // add the new socket to the socket to phys id map
   CreateSocketToPhysicalIdMapping(pHwsAccept->hws_Socket, pHwsAccept->hws_dwPhysicalId);

   SocketConnect(pHwsAccept, 0);
   return NOERROR;
} // SocketAccept()



/*++

Description:

   Handles network events that may occur on a connected socket.
   The events handled by this function are FD_ACCEPT, FD_CLOSE, FD_READ, and
   FD_WRITE.

Arguments:

   pHws - pointer to data for the connection on which the event happened.
   event - event that occurred
   error - error code accompanying the event

Return Value:

   SUCCESS - The network event was successfully handled.

   LINK_FATAL_ERROR - Some kind of error occurred while handling the
   event, and the connection should be closed.

   LINK_RECV_CLOSED - The connection has been gracefully closed.

--*/

static void
HandleNetworkEvent(PHWSINST pHws, int event, int error)
{
   HWSASSERT(pHws != NULL);
   HWSASSERT(pHws->hws_dwMagic == HWSINST_MAGIC);
   HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE, __TEXT("HandleNetworkEvent"));

   if (error == WSAENETDOWN)
   {
       HWSTRACE0(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                 __TEXT("HandleSocketEvent: Connection error"));
       pHws->hws_uState = HWS_CLOSED;
       return;
   }

   switch (event)
   {
   case FD_READ:
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                __TEXT("HandleNetworkEvent: FD_READ %s"),
                SocketErrorText1(error));
      if (error == 0 && pHws->hws_uState <= HWS_CLOSING)
      {
         ProcessQueuedRecvs(pHws);
      }
      break;

   case FD_WRITE:
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                __TEXT("HandleNetworkEvent: FD_WRITE %s"),
                SocketErrorText1(error));
      if (error == 0 && pHws->hws_uState <= HWS_CONNECTED)
      {
         ProcessQueuedSends(pHws);
      }
      break;

   case FD_OOB:
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                __TEXT("HandleNetworkEvent: FD_OOB %s"),
                SocketErrorText1(error));
      break;


   case FD_ACCEPT:
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                __TEXT("HandleNetworkEvent: FD_ACCEPT %s"),
                SocketErrorText1(error));
      if (pHws->hws_h245ConnectCallback != NULL)
      {
         if (error == 0)
         {
             pHws->hws_h245ConnectCallback(pHws->hws_dwH245Instance,
                                           LINK_CONNECT_REQUEST, NULL, NULL);
         }
         else
         {
             pHws->hws_h245ConnectCallback(pHws->hws_dwH245Instance,
                                           MAKE_WINSOCK_ERROR(error), NULL, NULL);
         }
      }
      else if (error == 0)
      {
         // If the client did not specify a callback, accept the call using the same physical
         // Id as the listen. This will result in the listen socket being closed.
         SocketAccept(pHws, pHws);
      }
      break;

   case FD_CONNECT:
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                __TEXT("HandleNetworkEvent: FD_CONNECT %s"),
                SocketErrorText1(error));
      SocketConnect(pHws, error);
      break;

   case FD_CLOSE:
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                __TEXT("HandleNetworkEvent: FD_CLOSE %s"),
                SocketErrorText1(error));
      SocketCloseEvent(pHws);
      break;
   }
} // HandleNetworkEvent()


LRESULT APIENTRY WndProc(
        HWND hWnd,                /* window handle                   */
        UINT message,             /* type of message                 */
        WPARAM wParam,              /* additional information          */
        LPARAM lParam)              /* additional information          */
{
   PHWSINST pHws;
   DWORD dwPhysicalId;

#ifdef FORCE_SERIALIZE_CALL_CONTROL
	 CCLOCK_AcquireLock();
#endif 

   switch (message)
   {
   case WINSOCK_EVENT_MSG:
		if (((dwPhysicalId=SocketToPhysicalId((SOCKET)wParam)) == INVALID_PHYS_ID) ||
				((pHws=gpInstanceTable->Lock(dwPhysicalId)) == NULL))

      {
         HWSTRACE1(0, HWS_WARNING,
                   __TEXT("WndProc: Winsock event on unknown socket 0x%x"),
                   wParam);
         break;
      }

      HandleNetworkEvent(pHws, WSAGETSELECTEVENT(lParam), WSAGETSELECTERROR(lParam));
	  gpInstanceTable->Unlock(dwPhysicalId);

      break;

   default:
   		{
   		#ifdef FORCE_SERIALIZE_CALL_CONTROL
	        CCLOCK_RelinquishLock();
		#endif 
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
   }
   
   #ifdef FORCE_SERIALIZE_CALL_CONTROL
           CCLOCK_RelinquishLock();
   #endif 
   
   return (0);
}

void NotifyRead(PHWSINST pHws)
{
   PostMessage(window, WINSOCK_EVENT_MSG, (WPARAM)pHws->hws_Socket, (LPARAM)MAKELONG(FD_READ, 0));
}

void NotifyWrite(PHWSINST pHws)
{
   PostMessage(window, WINSOCK_EVENT_MSG, (WPARAM)pHws->hws_Socket, (LPARAM)MAKELONG(FD_WRITE, 0));
}

/*++

Description:

   Calls WSAStartup, makes sure we have a good version of WinSock

Arguments:

   None.

Return Value:
   0                 WinSock DLL successfully started up.
   LINK_FATAL_ERROR  Error starting up WinSock DLL.

--*/


static const TCHAR CLASS_NAME[] = __TEXT("H245WSWndClass");

int     WinsockInitError = -1;
HRESULT InitializeStatus = LINK_INVALID_STATE;

LINKDLL VOID H245WSShutdown()
{
	Uninitialize();
}

void Initialize()
{
   WORD wVersion = MAKEWORD(1, 1);
   WSADATA  WsaData;      // receives data from WSAStartup
   WNDCLASS wndclass = { 0, WndProc, 0, 0, 0, 0, 0, 0, NULL, CLASS_NAME };
   DWORD    dwIndex;

   // Caveat: We can't use WSAGetLastError() for WSAStartup failure!
   if ((WinsockInitError = WSAStartup(wVersion, &WsaData)) != 0)
   {
      HWSTRACE0(0, HWS_WARNING, __TEXT("linkLayerInit: WSAStartup() failed"));
      InitializeStatus = MAKE_WINSOCK_ERROR(WinsockInitError);
      return;
   }

   if (LOBYTE(WsaData.wVersion) != 1 ||
       HIBYTE(WsaData.wVersion) != 1)
   {
      HWSTRACE0(0, HWS_WARNING, __TEXT("linkLayerInit: Winsock version mismatch"));
      InitializeStatus = MAKE_WINSOCK_ERROR(WSAVERNOTSUPPORTED);
      return;
   }

   // Create window to receive Winsock messages
   if (RegisterClass(&wndclass) == 0
       || (window = CreateWindow(CLASS_NAME, __TEXT(""), WS_OVERLAPPED, 0, 0, 0, 0, 0, 0, 0, NULL)) == 0)
   {
      HWSTRACE0(0, HWS_WARNING, __TEXT("linkLayerInit: error creating window"));
      InitializeStatus = HRESULT_FROM_WIN32(GetLastError());
      return;
   }

    //
    // LAURABU
    // BOGUS BUGBUG
    // 
    // This table code was never stressed by the people who wrote it.  It
    // totally falls apart when it completely fills up
    //      * Allocating the last item doesn't work
    //      * Freeing the last item doesn't work
    //      * Resizing larger doesn't work
    //
    // Since it doesn't take a lot of memory, a decent solution is to just
    // allocate it maximum+1 sized, and leave the last item free.
    //
   gpInstanceTable = new TSTable <HWSINST> (257);

   if(gpInstanceTable == NULL || gpInstanceTable->IsInitialized() == FALSE)
   {
	  InitializeStatus = LINK_MEM_FAILURE;
      return;
   }
      
   pSocketToHWSInstanceMap = new SOCKET_TO_INSTANCE[SOCK_TO_PHYSID_TABLE_SIZE];
   if(pSocketToHWSInstanceMap == NULL)
   {
	  InitializeStatus = LINK_MEM_FAILURE;
      return;
   }
   InitializeCriticalSection(&SocketToHWSInstanceMapLock);

   memset(pSocketToHWSInstanceMap, 0, sizeof(SOCKET_TO_INSTANCE) * SOCK_TO_PHYSID_TABLE_SIZE);
	
   // Init the sockets to a bad value

   for (dwIndex = 0; dwIndex < SOCK_TO_PHYSID_TABLE_SIZE; dwIndex++)
   {
      pSocketToHWSInstanceMap[dwIndex].socket = INVALID_SOCKET;
   }

   InitializeStatus = NOERROR;
   bInitialized = TRUE;
}

void Uninitialize()
{
   if(bInitialized)
   {
	   if (WinsockInitError == 0)
	   {
	      if (window)
	      {
	         DestroyWindow(window);
	         window = 0;
	         UnregisterClass(CLASS_NAME, 0);

	         if (gpInstanceTable)
	         {
	            delete gpInstanceTable;

	            if (InitializeStatus == NOERROR)
	            {
	               EnterCriticalSection(&SocketToHWSInstanceMapLock);
	               delete pSocketToHWSInstanceMap;
	               LeaveCriticalSection(&SocketToHWSInstanceMapLock);
	               DeleteCriticalSection(&SocketToHWSInstanceMapLock);
	            }
	         }
	      }
	      WSACleanup();
	   }
	   bInitialized = FALSE;
   }
 }


/***************************************************************************
 *
 * External entry points
 *
 ***************************************************************************/
//MULTITHREAD => dwH245Instance is now an OUTPUT param not an INPUT param.
LINKDLL HRESULT linkLayerInit (DWORD          *dwPhysicalId,
                              DWORD_PTR          dwH245Instance,
                              H245SRCALLBACK cbReceiveComplete,
                              H245SRCALLBACK cbTransmitComplete)
{
   register PHWSINST    pHws;
   *dwPhysicalId = INVALID_PHYS_ID; // Just in case...

   // Put Initialize back in here so we know everything we need is up and
   // running.
   if (InitializeStatus == LINK_INVALID_STATE)
      Initialize();
  
   if (InitializeStatus != NOERROR)
      return InitializeStatus;

   pHws = (PHWSINST)MemAlloc(sizeof(*pHws));
   if (pHws == NULL)
   {
      // couldn't allocate our context. Return
      HWSTRACE0(INVALID_PHYS_ID, HWS_WARNING, __TEXT("linkLayerInit: could not allocate context"));
      return LINK_MEM_FAILURE;
   }
   memset(pHws, 0, sizeof(*pHws));
   pHws->hws_Socket           = INVALID_SOCKET;
#if defined(_DEBUG)
   pHws->hws_dwMagic = HWSINST_MAGIC;
#endif  // (_DEBUG)

   // Create and initialize the Receive queue
   pHws->hws_pRecvQueue = QCreate();
   if (pHws->hws_pRecvQueue == NULL)
   {
      HWSTRACE0(INVALID_PHYS_ID, HWS_WARNING, __TEXT("linkLayerInit: could not allocate Receive queue"));
      MemFree(pHws);
      return LINK_MEM_FAILURE;
   }

   // Create and initialize the Send queue
   pHws->hws_pSendQueue = QCreate();
   if (pHws->hws_pSendQueue == NULL)
   {
      HWSTRACE0(INVALID_PHYS_ID, HWS_WARNING, __TEXT("linkLayerInit: could not allocate Send queue"));
      QFree(pHws->hws_pRecvQueue);
      MemFree(pHws);
      return LINK_MEM_FAILURE;
   }

   // Save intance identifiers and callback pointers
   pHws->hws_uState           = HWS_START;
   pHws->hws_dwH245Instance   = dwH245Instance;
   pHws->hws_h245RecvCallback = cbReceiveComplete;
   pHws->hws_h245SendCallback = cbTransmitComplete;

   // Open Channel 0 for the Forward and Reverse Directions
   // TBD

   // Add instance to instance list

   if(!gpInstanceTable->CreateAndLock(pHws, &pHws->hws_dwPhysicalId))
   {
      Q931HangupPendingCalls(NULL);

      if(!gpInstanceTable->CreateAndLock(pHws, &pHws->hws_dwPhysicalId))
      {
         HWSTRACE0(INVALID_PHYS_ID, HWS_WARNING, __TEXT("linkLayerInit: could not add to instance table"));
         QFree(pHws->hws_pSendQueue);
         QFree(pHws->hws_pRecvQueue);
         MemFree(pHws);
         return LINK_INSTANCE_TABLE_FULL;
      }
   }
   
   *dwPhysicalId = pHws->hws_dwPhysicalId;

   gpInstanceTable->Unlock(pHws->hws_dwPhysicalId);

   HWSTRACE2(*dwPhysicalId, HWS_TRACE,
             __TEXT("linkLayerInit(%d, %d) succeeded"), dwPhysicalId, dwH245Instance);

   return NOERROR;
} // linkLayerInit()



LINKDLL HRESULT linkLayerShutdown(DWORD dwPhysicalId)
{
   register PHWSINST    pHws;
   UINT                 uRetryCount;
   register PREQUEST    pReq;

   if (InitializeStatus != NOERROR)
      return InitializeStatus;
	
   if ((dwPhysicalId == INVALID_PHYS_ID) ||
      (!(pHws = gpInstanceTable->Lock(dwPhysicalId))))
   {
      HWSTRACE0(dwPhysicalId, HWS_ERROR,
                __TEXT("linkLayerShutdown: dwPhysicalId not found"));
      return LINK_INVALID_INSTANCE;
   }

   // mark this instance as deleted so we don't process anymore messages for it - 
   // note: the FALSE indicates to the TSTable class to NOT do the deletion of memory
   // once the last unlock has completed (which is at the end of this function)
   gpInstanceTable->Delete(dwPhysicalId, FALSE);

   RemoveSocketToPhysicalIdMapping(pHws->hws_Socket);

   switch (pHws->hws_uState)
   {
   case HWS_START:
   case HWS_LISTENING:
   case HWS_CONNECTING:
      break;

   case HWS_CONNECTED:
      // Try to send all messages waiting on send queue
      HWSASSERT(pHws->hws_pSendQueue != NULL);
      ProcessQueuedSends(pHws);
      uRetryCount = 0;
      while (  pHws->hws_uState == HWS_CONNECTED &&
               IsQEmpty(pHws->hws_pSendQueue) == FALSE &&
               ++uRetryCount <= 5)
      {
         HWSTRACE1(pHws->hws_dwPhysicalId, HWS_NOTIFY,
                   __TEXT("linkLayerShutdown: Waiting for send %d"), uRetryCount);
         ProcessQueuedSends(pHws);
         Sleep(100);
      }

      if (pHws->hws_uState == HWS_CONNECTED)
      {
         HWSTRACE0(pHws->hws_dwPhysicalId, HWS_TRACE,
                   __TEXT("linkLayerShutdown: calling shutdown"));
         if (shutdown(pHws->hws_Socket, 1) == SOCKET_ERROR)
         {
            HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                     __TEXT("linkLayerShutdown: shutdown() returned %s"),
                     SocketErrorText());
         }
         pHws->hws_uState = HWS_CLOSING;
      }

   case HWS_CLOSING:
      break;

   case HWS_CLOSED:
      break;

   default:
      HWSASSERT(FALSE);
   } // switch

   // Deallocate instance objects

   SocketClose(pHws);


   // Deallocate Receive queue
   if (pHws->hws_pRecvQueue)
   {
      while ((pReq = (PREQUEST) QRemove(pHws->hws_pRecvQueue)) != NULL)
      {
         pHws->hws_h245RecvCallback(pHws->hws_dwH245Instance,
                                    LINK_RECV_ABORT,
                                    pReq->req_client_data,
                                    0);
         MemFree(pReq);
      }
      QFree(pHws->hws_pRecvQueue);
      pHws->hws_pRecvQueue = NULL;
   }

   // Deallocate Send queue
   if (pHws->hws_pSendQueue)
   {
      while ((pReq = (PREQUEST) QRemove(pHws->hws_pSendQueue)) != NULL)
      {
         pHws->hws_h245SendCallback(pHws->hws_dwH245Instance,
                                    LINK_SEND_ABORT,
                                    pReq->req_client_data,
                                    0);
         MemFree(pReq);
      }
      QFree(pHws->hws_pSendQueue);
      pHws->hws_pSendQueue = NULL;
   }

   gpInstanceTable->Unlock(dwPhysicalId);

   MemFree(pHws);

   HWSTRACE0(dwPhysicalId, HWS_TRACE, __TEXT("linkLayerShutdown: succeeded"));
   return NOERROR;
} // linkLayerShutdown



/***************************************************************************
 *
 *  NAME
 *      linkLayerGetInstance - return instance id corresponding to physical id
 *
 *  SYNOPSIS
 *      LINKDLL DWORD linkLayerGetInstance(DWORD dwPhysicalId);
 *
 * DESCRIPTION
 *      Returns the Instance identifier corresponding to dwPhysId.
 *
 *  PARAMETERS
 *      dwPhysicalId    Physical identifier to search for
 *
 *  RETURN VALUE
 //MULTITHREAD
 *      INVALID_PHYS_ID    (-1)No instance corresponding to dwPhysicalId found
 *      n>0             Instance Id corresponding to dwPhysicalId
 *
 ***************************************************************************/

LINKDLL DWORD linkLayerGetInstance(DWORD dwPhysicalId)
{
   if (InitializeStatus == NOERROR &&
       dwPhysicalId != INVALID_PHYS_ID &&
       gpInstanceTable->Lock(dwPhysicalId) != NULL)
   {
      HWSTRACE0(dwPhysicalId, HWS_TRACE, __TEXT("linkLayerGetInstance: succeeded"));
      gpInstanceTable->Unlock(dwPhysicalId); 
      return dwPhysicalId;
   }

   HWSTRACE0(dwPhysicalId, HWS_ERROR, __TEXT("linkLayerGetInstance: failed"));
   return INVALID_PHYS_ID;                           // return failure
} // linkLayerGetInstance()



/***************************************************************************
 *
 * NAME
 *    linkLayerFlushChannel - flush transmit and/or receive channels
 *
 * DESCRIPTION
 *    This is a dummy function required only for compatibility with the SRP API.
 *
 * RETURN VALUE
 *    0           Function succeeded.
 *    n!=0        Link error code (see LINKAPI.H for definitions.)
 *
 ***************************************************************************/

LINKDLL HRESULT linkLayerFlushChannel(DWORD dwPhysicalId, DWORD dwDirectionMask)
{
   if (InitializeStatus != NOERROR)
      return InitializeStatus;

   PHWSINST pHws;
   if ((dwPhysicalId == INVALID_PHYS_ID) ||
      (!(pHws = gpInstanceTable->Lock(dwPhysicalId))))
   {
      HWSTRACE0(dwPhysicalId, HWS_ERROR,
                __TEXT("linkLayerFlushChannel: dwPhysicalId not found"));
      return LINK_INVALID_INSTANCE;
   }

   switch (dwDirectionMask & (DATALINK_RECEIVE | DATALINK_TRANSMIT))
   {
   case DATALINK_TRANSMIT | DATALINK_RECEIVE:
      // Flush both receive and transmit
      // Caveat: H.245 expects us to flush receive first!
      SocketFlushRecv(pHws);

      // Fall through to next case

   case DATALINK_TRANSMIT:
      // Flush transmit
      // SockFlushSend() just removes the entries from the queue - it doesn't send them.
      // So instead, call ProcessQueuedSends() to actually empty the send queue.
      //      SocketFlushSend(pHws);	
			ProcessQueuedSends(pHws);
      break;

   case DATALINK_RECEIVE:
      // Flush receive
      SocketFlushRecv(pHws);
      break;

   } // switch

 	gpInstanceTable->Unlock(dwPhysicalId);

   HWSTRACE2(dwPhysicalId, HWS_TRACE,
             __TEXT("linkLayerFlushChannel(%d, 0x%X) succeeded"),
             dwPhysicalId, dwDirectionMask);
   return NOERROR;
} // linkLayerFlushChannel()



/***************************************************************************
 *
 * NAME
 *    linkLayerFlushAll - flush transmit and receive channels
 *
 * DESCRIPTION
 *    This is a dummy function required only for compatibility with the SRP API.
 *
 * RETURN VALUE
 *    0           Function succeeded.
 *    n!=0        Link error code (see LINKAPI.H for definitions.)
 *
 ***************************************************************************/

LINKDLL HRESULT linkLayerFlushAll(DWORD dwPhysicalId)
{

   if (InitializeStatus != NOERROR)
      return InitializeStatus;

   PHWSINST pHws;
   if ((dwPhysicalId == INVALID_PHYS_ID) ||
      (!(pHws = gpInstanceTable->Lock(dwPhysicalId))))
   {
      HWSTRACE0(dwPhysicalId, HWS_ERROR,
                __TEXT("linkLayerFlushAll: dwPhysicalId not found"));
      return LINK_INVALID_INSTANCE;
   }

   // Flush both receive and transmit
   // Caveat: H.245 expects us to flush receive first!
   SocketFlushRecv(pHws);
   SocketFlushSend(pHws);

   gpInstanceTable->Unlock(dwPhysicalId);

   HWSTRACE0(dwPhysicalId, HWS_TRACE, __TEXT("linkLayerFlushAll succeeded"));
   return NOERROR;
} // linkLayerFlushAll()



/*++

Description:

   Initiates a call to a remote node.
   1) Create a socket.
   2) Set the socket up for windows message notification of FD_CONNECT events.
   3) Attempt a connection

Arguments:
   pHws  - Pointer to context data from connection.

Return Value:
   SUCCESS  - A connection attempt was successfully initiated.
   LINK_FATAL_ERROR  - Error occurred while attempting to initiate a connection.

--*/

LINKDLL HRESULT linkLayerConnect(DWORD dwPhysicalId, CC_ADDR *pAddr, H245CONNECTCALLBACK callback)
{
   register PHWSINST    pHws;
   CC_ADDR              Addr;
   HRESULT              hr;

   if (InitializeStatus != NOERROR)
      return InitializeStatus;

   HWSTRACE5(dwPhysicalId, HWS_TRACE, __TEXT("linkLayerConnect: connecting to %d.%d.%d.%d/%d"),
             GetIPAddress(pAddr) & 0xFF,
             (GetIPAddress(pAddr) >>  8) & 0xFF,
             (GetIPAddress(pAddr) >> 16) & 0xFF,
             GetIPAddress(pAddr) >> 24,
             ntohs(GetPort(pAddr)));

   if ((dwPhysicalId == INVALID_PHYS_ID) ||
      (!(pHws = gpInstanceTable->Lock(dwPhysicalId))))
   {
      HWSTRACE0(dwPhysicalId, HWS_ERROR, __TEXT("linkLayerConnect: dwPhysicalId not found"));
      return LINK_INVALID_INSTANCE;
   }

   if (pHws->hws_uState != HWS_START)
   {
      HWSTRACE1(dwPhysicalId, HWS_ERROR, __TEXT("linkLayerConnect: State = %s"),
                SocketStateText(pHws->hws_uState));
   	  gpInstanceTable->Unlock(dwPhysicalId);
      return LINK_INVALID_STATE;
   }

   pHws->hws_h245ConnectCallback = callback;

   // Create a socket, bind to a local address and initiate a connection.
   if ((hr = SocketOpen(pHws)) != NOERROR)
   {
      HWSTRACE0(dwPhysicalId, HWS_WARNING,
                __TEXT("linkLayerConnect: SocketOpen() failed"));
      pHws->hws_uState = HWS_START;
      gpInstanceTable->Unlock(dwPhysicalId);
      return hr;
   }

   Addr.nAddrType = CC_IP_BINARY;
   Addr.bMulticast = FALSE;
   Addr.Addr.IP_Binary.wPort = 0;
   Addr.Addr.IP_Binary.dwAddr = 0;
   if ((hr = SocketBind(pHws, &Addr)) != NOERROR)
   {
      HWSTRACE0(dwPhysicalId, HWS_WARNING,
	      __TEXT("linkLayerConnect: SocketBind() failed"));
      pHws->hws_uState = HWS_START;
      gpInstanceTable->Unlock(dwPhysicalId);
      return hr;
   }

   // Reminder: WinSock requires that we pass a struct sockaddr *
   // to connect; however, the service provider is free to
   // interpret the pointer as an arbitrary chunk of data of size
   // uSockAddrLen.
   pHws->hws_SockAddr.sin_port             = GetPort(pAddr);
   pHws->hws_SockAddr.sin_addr.S_un.S_addr = GetIPAddress(pAddr);
   if (connect(pHws->hws_Socket,                                   // s
            (const struct sockaddr *)&pHws->hws_SockAddr,   // name
            sizeof(pHws->hws_SockAddr)) == SOCKET_ERROR)   // namelen
   {
      int err = WSAGetLastError();
      switch (err)
      {
      case WSAEWOULDBLOCK:
         break;

      default:
         HWSTRACE1(dwPhysicalId, HWS_WARNING,
                   __TEXT("linkLayerConnect: WSAConnect() returned %s"),
                   SocketErrorText1(err));
         SocketClose(pHws);
         pHws->hws_uState = HWS_START;
         gpInstanceTable->Unlock(dwPhysicalId);
         return MAKE_WINSOCK_ERROR(err);
      } // switch
   } // if
   else
   {
      HWSTRACE0(dwPhysicalId, HWS_TRACE, __TEXT("connect() succeeded"));
      SocketConnect(pHws, 0);
      gpInstanceTable->Unlock(dwPhysicalId);
      return NOERROR;
   } // else

   pHws->hws_uState = HWS_CONNECTING;
   HWSTRACE0(dwPhysicalId, HWS_TRACE, __TEXT("linkLayerConnect: succeeded"));
   gpInstanceTable->Unlock(dwPhysicalId);
   return NOERROR;
} // linkLayerConnect()



/***************************************************************************
 *
 * NAME
 *    linkLayerListen - start listen on connection
 *
 * DESCRIPTION
 *    This function creates a socket,
 *    binds to a local address, and listens on the created socket.
 *    The address being listened to is returned in the structure
 *    pointed to by pAddr.
 *
 * PARAMETERS
 *    dwPhysicalId      Link layer identifier returned by linkLayerGetInstance().
 *    pAddr             Pointer to address structure (defined in CALLCONT.H.)
 *
 * RETURN VALUE
 *    0                 Function succeeded.
 *    n!=0              Error code (see LINKAPI.H for definitions.)
 *
 ***************************************************************************/
//MULTITHREAD => dwPhysicalID was INPUT now it's OUTPUT
LINKDLL HRESULT linkLayerListen (DWORD *dwPhysicalId, DWORD_PTR dwH245Instance, CC_ADDR *pAddr, H245CONNECTCALLBACK callback)
{
   HRESULT hr;
   register PHWSINST    pHws;

   if (InitializeStatus == LINK_INVALID_STATE)
      Initialize();
   if (InitializeStatus != NOERROR)
      return InitializeStatus;

   if ((*dwPhysicalId == INVALID_PHYS_ID) ||
      (!(pHws = gpInstanceTable->Lock(*dwPhysicalId))))
   {
      if ((hr = linkLayerInit(dwPhysicalId, dwH245Instance, NULL, NULL)) != NOERROR)
         return hr;

      pHws = gpInstanceTable->Lock(*dwPhysicalId);
      HWSASSERT(pHws != NULL);
   }
   else
   {
      HWSASSERT(pHws->hws_dwH245Instance == dwH245Instance);
   }

   if (pHws->hws_uState != HWS_START)
   {
      HWSTRACE1(*dwPhysicalId, HWS_ERROR, __TEXT("linkLayerListen: State = %s"),
                SocketStateText(pHws->hws_uState));
      gpInstanceTable->Unlock(*dwPhysicalId);
      return LINK_INVALID_STATE;
   }

   pHws->hws_h245ConnectCallback = callback;

   if ((hr = SocketOpen(pHws, FALSE)) != NOERROR)
   {
      HWSTRACE0(*dwPhysicalId, HWS_WARNING, __TEXT("linkLayerListen: SocketBind() failed"));
      pHws->hws_uState = HWS_START;
      gpInstanceTable->Unlock(*dwPhysicalId);
      return hr;
   }

   if ((hr = SocketBind(pHws, pAddr)) != NOERROR)
   {
      HWSTRACE0(*dwPhysicalId, HWS_WARNING, __TEXT("linkLayerListen: SocketBind() failed"));
      pHws->hws_uState = HWS_START;
      gpInstanceTable->Unlock(*dwPhysicalId);
      return hr;
   }

   // Listen for incoming connection requests on the socket.
   int nBacklog = SOMAXCONN;
   while (listen(pHws->hws_Socket, nBacklog) == SOCKET_ERROR)
   {
      int err = WSAGetLastError();
      HWSTRACE1(*dwPhysicalId,
                HWS_WARNING,
                __TEXT("linkLayerListen: listen() returned %s"),
                SocketErrorText1(err));
      if (nBacklog == SOMAXCONN)
         nBacklog = 10;
      if (err != WSAENOBUFS || --nBacklog == 0)
      {
         SocketClose(pHws);
         pHws->hws_uState = HWS_START;
         gpInstanceTable->Unlock(*dwPhysicalId);
         return MAKE_WINSOCK_ERROR(err);
      }     
      Sleep(0);
   }

   if ((hr = GetLocalAddr(pHws, pAddr)) != NOERROR)
   {
      // getsockname() failed
      HWSTRACE1(pHws->hws_dwPhysicalId, HWS_WARNING,
                __TEXT("linkLayerListen: getsockname() returned %s"),
                SocketErrorText());
      SocketClose(pHws);
      pHws->hws_uState = HWS_START;
      gpInstanceTable->Unlock(*dwPhysicalId);
      return hr;
   }

   HWSTRACE5(*dwPhysicalId, HWS_TRACE, __TEXT("linkLayerListen: listening on %d.%d.%d.%d/%d"),
             GetIPAddress(pAddr) & 0xFF,
             (GetIPAddress(pAddr) >>  8) & 0xFF,
             (GetIPAddress(pAddr) >> 16) & 0xFF,
             GetIPAddress(pAddr) >> 24,
             ntohs(GetPort(pAddr)));

   pHws->hws_uState = HWS_LISTENING;
   HWSTRACE0(*dwPhysicalId, HWS_TRACE, __TEXT("linkLayerListen: succeeded"));
   gpInstanceTable->Unlock(*dwPhysicalId);
   return NOERROR;
} // linkLayerListen()


LINKDLL HRESULT
linkLayerAccept(DWORD dwPhysicalIdListen, DWORD dwPhysicalIdAccept, H245CONNECTCALLBACK callback)
{
    PHWSINST pHwsListen;
    PHWSINST pHwsAccept;

    HRESULT rc;
    if (InitializeStatus != NOERROR)
       return InitializeStatus;

	HWSTRACE0(dwPhysicalIdAccept, HWS_TRACE, __TEXT("linkLayerAccept"));
    if ((dwPhysicalIdListen == INVALID_PHYS_ID) ||
       (!(pHwsListen = gpInstanceTable->Lock(dwPhysicalIdListen))))
    {
       HWSTRACE0(dwPhysicalIdListen, HWS_ERROR, __TEXT("linkLayerAccept: dwPhysicalIdListen not found"));
       return LINK_INVALID_INSTANCE;
    }

    pHwsAccept = gpInstanceTable->Lock(dwPhysicalIdAccept);
    if (pHwsAccept == NULL)
    {
       HWSTRACE0(dwPhysicalIdAccept, HWS_ERROR, __TEXT("linkLayerAccept: dwPhysicalIdAccept not found"));
		 gpInstanceTable->Unlock(dwPhysicalIdListen);
       return LINK_INVALID_INSTANCE;
    }

    HWSASSERT(pHwsListen->hws_uState == HWS_LISTENING);
    HWSASSERT(pHwsAccept == pHwsListen || pHwsAccept->hws_uState == HWS_START);

    pHwsAccept->hws_h245ConnectCallback = callback;

    rc = SocketAccept(pHwsListen, pHwsAccept);
	gpInstanceTable->Unlock(dwPhysicalIdListen);
    gpInstanceTable->Unlock(dwPhysicalIdAccept);
    return rc;
}

/***************************************************************************
 *
 * NAME
 *    linkLayerReject   - reject an incomming connection request
 *
 * DESCRIPTION
 *    This function accepts an incomming connection
 *    request then immediatly closes the new socket. 
 *
 * PARAMETERS
 *    dwPhysicalId      Link layer identifier of the listening socket
 *
 * RETURN VALUE
 *    0                 Function succeeded.
 *    n!=0              Error code (see LINKAPI.H for definitions.)
 *
 ***************************************************************************/
LINKDLL HRESULT
linkLayerReject(DWORD dwPhysicalIdListen)
{
    HRESULT     hr          = NOERROR;
    PHWSINST    pHwsListen  = NULL;
    SOCKET      sockReject  = INVALID_SOCKET;
    SOCKADDR_IN sockAddr;
    INT         sockAddrLen = sizeof(sockAddr);

    // Lock the table to get the socket handle
    pHwsListen = gpInstanceTable->Lock(dwPhysicalIdListen);
    if(NULL != pHwsListen)
    {
        // Accept incoming conenction then immediately close it
        sockReject = accept(pHwsListen->hws_Socket, (SOCKADDR *)&sockAddr, &sockAddrLen);
        if(INVALID_SOCKET != sockReject)
        {
            closesocket(sockReject);
        }
        else
        {
            hr = MAKE_WINSOCK_ERROR(WSAGetLastError());
        }
    }
    else
    {
        hr =  LINK_INVALID_INSTANCE;
    }

    // Unlock the instance table
    gpInstanceTable->Unlock(dwPhysicalIdListen);

    return hr;       
}

// Here is the corresponding redefinition of the __TEXT macro

#ifndef UNICODE_TRACE
#undef  __TEXT
#define __TEXT(x) L##x
#endif


#if defined(__cplusplus)
}
#endif  // (__cplusplus)
