/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: win_net.cpp,v 1.6.2.3 2004/07/09 01:46:44 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hxtypes.h"

#ifdef _WIN16
#include <stdlib.h>
#endif _WIN16

#include <stdio.h>
#include <string.h>

/* mcast_multiple_if test */
#ifndef _WINCE
#include <iostream.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#endif /* _WINCE */

#include "hxslist.h"
#include "platform/win/sock.h"
#include "platform/win/hxsock.h"		//  Low level socket calls

#include "hxthread.h"
#include "hxcom.h"
#include "hxengin.h"
#include "threngin.h"

#include "platform/win/win_net.h"      //  Our declaration
#include "platform/win/casynnet.h"     //  Declaration of async socket notifier
#include "netbyte.h"
#include "hxbuffer.h"
#include "timebuff.h"
#include "hxtick.h"
#include "debugout.h"		//  Debug macros
#include "hxstrutl.h"

#include "hxheap.h"
#include "thrdconn.h"

#ifdef _DEBUG
#undef HX_THIS_FILE		
static const char HX_THIS_FILE[] = __FILE__;
#endif									 

static WinsockManager OnlyOneOfThisInTheWholeProgram;

//#define _LOGSMIL    1
win_net::win_net() 			     // PostThreadMessage
{
    set_sock( INVALID_SOCKET );
    mLastError	    = HXR_OK;
    callRaConnect   = 1;    
    bReadyToWrite   = 0;
    m_SocketState   = CONN_CLOSED;
#ifndef WIN32_PLATFORM_PSPC
    m_hAsyncHandle  = NULL;
#endif

    m_AsyncAddress  = NULL;
    CurrentAddr	    = 0;
    m_DNSOnly	    = FALSE;
    m_pAsyncHost    = NULL;
    InBlockingMode  = 0;
    m_lRefCount	    = 0;

    m_pInBuffer = new char[TCP_BUF_SIZE];

    m_bReuseAddr = FALSE;
    m_bReusePort = FALSE;
    m_bIgnoreWSAECONNRESET = FALSE;
}

win_net::~win_net() 
{
#ifndef WIN32_PLATFORM_PSPC
    m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier( m_hInst , FALSE);
    if (m_AsyncNotifier)
    {
	m_AsyncNotifier->CancelSelect( this );
    }
#endif

    m_SocketState = CONN_CLOSING;
    if ((get_sock() != INVALID_SOCKET) && sockObj)       
    {       
	if (sockObj->HXclosesocket(get_sock()))
	{
	    int code = 0;
	    code = sockObj->HXWSAGetLastError();
	}    
    }

    set_sock( INVALID_SOCKET );
    m_SocketState = CONN_CLOSED;
    mConnectionOpen = 0;

    if (m_AsyncAddress)
    {
	delete [] m_AsyncAddress;
	m_AsyncAddress = NULL;
    }

    HX_VECTOR_DELETE(m_pAsyncHost);

    delete[] m_pInBuffer;
}

win_net * win_net::new_socket(UINT16 type)
{
    win_net* c = NULL;

    if (sockObj)
    { 
	switch(type)
	{
	case HX_TCP_SOCKET:
	    c = new win_TCP;
	    break;
		
	case HX_UDP_SOCKET:
	    c = new win_UDP;
	    break;
	}
    }

    return(c);
}


// init_drivers() should do any network driver initialization here
// params is a pointer to a platform specfic defined struct that 
// contains an required initialization data

HX_RESULT win_net::init_drivers(void *params)
{
    if(!sockObj)
      sockObj = new CHXSock;  

    if(!sockObj)
      return(HXR_OUTOFMEMORY);

    return(HXR_OK);
}


/* 	close_drivers() should close any network drivers used by the program
 	NOTE: The program MUST not make any other calls to the network drivers
 	until init_drivers() has been called */

HX_RESULT win_net::close_drivers(void *params)
{
    if(sockObj)
    {
	sockObj->HXWSACleanup();
	delete sockObj;
	sockObj = NULL;
    }  
    return(HXR_OK);
}


HX_RESULT win_net::host_to_ip_str( char *host, char *ip, UINT32 ulIPBufLen)
{
    ULONG32		dwAddress;
    IN_ADDR		rInAddress;
    HX_RESULT	theErr;
    PHOSTENT	pHostEntry;

    theErr = HXR_OK;

    //	Let's look for this in the cache first
    if (conn::is_cached( host, &dwAddress))
    {
	//	Found it, copy the 32bit address into rInAddress w/o calling memcpy()
	rInAddress = *(IN_ADDR *)&dwAddress;
    }
    else
    {
	//	Do DNS on the host name
	if (!(pHostEntry = sockObj->HXgethostbyname( host )))
	{
	    //	Error
	    theErr = HXR_DNR;
	}

	//	Return w/o attempting any copies if there's an error
	if (HXR_OK != theErr)
	{
	    goto FuncExit;
	}
	
	//	copy the ip address into rInAddress w/o calling memcpy()
	rInAddress = *(IN_ADDR *)(pHostEntry->h_addr);
    }

    //	Convert the ULONG32 IP address into a string and copy it into ip
    SafeStrCpy( ip, sockObj->HXinet_ntoa( rInAddress ) , ulIPBufLen);

    //	Single exit point
FuncExit:
    return( theErr );
}


ULONG32 win_net::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

ULONG32 win_net::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

HX_RESULT win_net::init_win(UINT16 type, UINT32 local_addr, 
			    UINT16 port, UINT16 blocking)
{                                        
    int            s;

    s = sockObj->HXsocket(PF_INET, type, 0);
    if (s == INVALID_SOCKET) 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	return mLastError;
    }

    InBlockingMode = 1;
	     
    {
	struct linger Ling;
	Ling.l_onoff = 0;
	Ling.l_linger = 0;
	if (callRaConnect && sockObj->HXsetsockopt(s, SOL_SOCKET, SO_LINGER, 
			     (char *)&Ling, sizeof(Ling)) != 0)
	{                 
	    if(!InBlockingMode)
		return HXR_BLOCK_CANCELED;                      
	    int code;
	    code = sockObj->HXWSAGetLastError(); 
	    mLastError = HXR_NET_SOCKET_INVALID;
	    InBlockingMode = 0;
	    return mLastError;
	}
    }
	
    if(!InBlockingMode)
	return HXR_BLOCK_CANCELED;
	    
    InBlockingMode = 0;
 
    { 
	if (sockObj->HXsetsockopt(s, SOL_SOCKET, SO_REUSEADDR, 
		                      (const char *)&m_bReuseAddr, sizeof(m_bReuseAddr)) != 0)
	{
   	    int code;
	    code = sockObj->HXWSAGetLastError(); 
	    mLastError = HXR_NET_SOCKET_INVALID;
	    return mLastError;
	}

// Reuse port is not supported on win's right now...
/*
	if (sockObj->HXsetsockopt(s, SOL_SOCKET, SO_REUSEPORT, 
		                      (const char *)&m_bReusePort, sizeof(m_bReusePort)) != 0)
	{
   	    int code;
	    code = sockObj->HXWSAGetLastError(); 
	    mLastError = HXR_NET_SOCKET_INVALID;
	    return mLastError;
	}
*/	
    }    
    
    struct sockaddr_in	SockAddr;
    memset(&SockAddr, 0, sizeof(SockAddr));
    SockAddr.sin_family = AF_INET;
    
    SockAddr.sin_addr.s_addr = sockObj->HXhtonl(local_addr);
    SockAddr.sin_port = sockObj->HXhtons(port);
    if (sockObj->HXbind(s, (sockaddr*)&SockAddr, sizeof(SockAddr)) < 0) 
    {
	//perror("bind");
	goto err;
    } 

    {
	unsigned long lMode = 1;                                                            
	if (!blocking && sockObj->HXioctlsocket(s, FIONBIO, &lMode) < 0) 
	{
	    goto err;
	}    
    }

    if (!callRaConnect)
    {
	m_SocketState = CONN_OPEN;
    }
    else
    {
	m_SocketState = CONN_NO_CONN;
    }

    set_sock( s );
    return HXR_OK;
err:
    sockObj->HXclosesocket(s);
    mLastError = HXR_NET_CONNECT;
    return mLastError;
}

HX_RESULT win_net::connect(const char* host, UINT16 port, UINT16 blocking, ULONG32 ulPlatform)
{   
    DEBUGOUTSTR( "win_net::connect()\r\n" );

    m_hInst = (HINSTANCE)ulPlatform;

    if (!blocking)
    {
#ifndef WIN32_PLATFORM_PSPC
	return( ConnectAsync( host, port ) );
#else
	HX_ASSERT(0 && "No Async connect");
#endif
    }
    else
    {
	bReadyToWrite = 0;

	if(!host)                 
	{
	    mLastError = HXR_DNR;
	    return mLastError;
	}
	    
	if(get_sock() < 0)                 
	{
	    mLastError = HXR_NET_SOCKET_INVALID;
	    return mLastError;
	}
	    
	struct in_addr addr;
        char* pTemp = strrchr(host, '.');
        if (pTemp && isdigit(*(pTemp + 1)))
	{   /* IP address. */
	    InBlockingMode = 1;
	    addr.s_addr = sockObj->HXinet_addr(host);
	    
	    if(!InBlockingMode)
		return HXR_BLOCK_CANCELED;
	    
	    InBlockingMode = 0;
	    
	    if ((UINT)addr.s_addr == (UINT)-1) 
	    {
		mLastError = HXR_DNR;
		return mLastError;
	    }
	    m_SocketState = CONN_CONNECT_INPROG;
	} 
	else
	{   /* Host name. */
	    InBlockingMode = 1;
	    struct hostent *h = sockObj->HXgethostbyname(host);
	    
	    if(!InBlockingMode)
		return HXR_BLOCK_CANCELED;
		
	    InBlockingMode = 0;
	    if (!h || !h->h_addr ) 
	    {
		mLastError = HXR_DNR;
		return mLastError;
	    }
	    memcpy(&addr, h->h_addr, sizeof(struct in_addr)); /* Flawfinder: ignore */
	    
	    // Rahul
	    m_AsyncPort = port;
	    if (m_pAsyncHost != host)
	    {
		HX_VECTOR_DELETE(m_pAsyncHost);
		m_pAsyncHost = ::new_string(host);
	    }
	}                    
	    
	struct sockaddr_in	SockAddr;
	memset(&SockAddr, 0, sizeof(SockAddr));
	SockAddr.sin_family = AF_INET;
	SockAddr.sin_addr.s_addr = *(long*)&addr;
	SockAddr.sin_port = sockObj->HXhtons(port);

	// this stores info about current addr 
	CurrentAddr		= *(ULONG32*)&addr;
	            
	InBlockingMode = 1;                
	
	if(callRaConnect && sockObj->HXconnect(get_sock(), (sockaddr*)&SockAddr, sizeof(SockAddr)))
	{   
	    if(!InBlockingMode)
		return HXR_BLOCK_CANCELED;
		
	    InBlockingMode = 0;
	    int code;
	    code = sockObj->HXWSAGetLastError(); 
	    if(!blocking && (code == WSAEWOULDBLOCK || code == WSAEINPROGRESS))
	    {  
	       mConnectionOpen = 1;
	       return HXR_OK;
	    }   

	    mLastError = HXR_NET_CONNECT;
	    return mLastError;
	}       
	 	  
	if(!InBlockingMode)
	    return HXR_BLOCK_CANCELED;
		           
	InBlockingMode = 0;
		          
	mConnectionOpen = 1;
	m_SocketState = CONN_OPEN; 
	bReadyToWrite = 1;
#ifdef WIN32_PLATFORM_PSPC 
	// Without asyncronous notification we cannot tolerate Asyncronous connect
	// so set non-blocking here rather than at connect
	ULONG lMode = 1;
	sockObj->HXioctlsocket(get_sock(), FIONBIO, &lMode);
	if (mCallBack)
	{
	    mCallBack->Func(CONNECT_NOTIFICATION);
	}
#endif
  return HXR_OK;
    }
}

//-------------------------------------------------
#if defined(HELIX_FEATURE_NETWORK_USE_SELECT)
HX_RESULT 
win_net::WaitforSelect(void *pE,void *pC)
{
    ThreadEngine *pEngine = (ThreadEngine *)pE;
    ThreadedConn *pProcessConn = (ThreadedConn*) pC;
    if (!pEngine)
	return HXR_FAIL;

    CHXMapPtrToPtr::Iterator ndxConn;
    CHXMapPtrToPtr* pSockMap = pEngine->GetSockMap();
    fd_set readfds,exceptfds;
    BOOL bHaveSock = FALSE;
    HX_RESULT theErr = HXR_OK;

    FD_ZERO(&readfds);
    FD_ZERO(&exceptfds);
    
    for (ndxConn = pSockMap->Begin(); 
	 ndxConn != pSockMap->End(); ++ndxConn)
    {
	ThreadedConn* pConn = (ThreadedConn*) (*ndxConn);
	if (pProcessConn &&
	    pConn != pProcessConn)
	    continue; // dont process other connections while closing
	if (!pConn->connection_really_open())
	    continue;
	if (!pConn->GetActualConn())
	    continue;
	unsigned int s = pConn->get_sock();
	if (s != INVALID_SOCKET)
	{
	    FD_SET(s,&readfds);
	    FD_SET(s,&exceptfds);
	    bHaveSock = TRUE;
	}
    }
    if (!bHaveSock)
	return HXR_FAIL;

    theErr = sockObj->HXselect(NULL,&readfds,NULL,NULL/*&exceptfds*/,NULL);

    if (theErr == SOCKET_ERROR)
    {
	theErr = sockObj->HXWSAGetLastError();
    }
    else
    {
	theErr = HXR_OK;
    }

    // check for read ready
    for (ndxConn = pSockMap->Begin(); 
	 ndxConn != pSockMap->End(); ++ndxConn)
    {
	ThreadedConn* pConn = (ThreadedConn*) (*ndxConn);
	if (pProcessConn &&
	    pConn != pProcessConn)
	    continue; // dont process other connections while closing
	if (!pConn->GetActualConn())
	    continue;
	int s = pConn->get_sock();
	if (s != INVALID_SOCKET)
	{
	    if (FD_ISSET(s,&readfds))
	       pConn->DoRead(TRUE);
	}
     }
    return theErr;
}

HX_RESULT  
win_net::CheckForConnection()
{
    HX_ASSERT(m_SocketState == CONN_LISTENNING);

    if (m_SocketState != CONN_LISTENNING)
	return HXR_FAIL;

    sockaddr_in addr;
    int len = sizeof(sockaddr_in);
    memset(&addr, 0, len);
    
    SOCKET sock = sockObj->HXaccept(get_sock(), (sockaddr*)&addr, &len);

    if ( sock == INVALID_SOCKET )
    {
	  int code = sockObj->HXWSAGetLastError();
	// igno all errors...  r
 	  return HXR_WOULD_BLOCK;
    }

    win_net* c = (win_net*)conn::actual_new_socket(HX_TCP_SOCKET);
    c->AddRef();
    conn::add_connection_to_list ( c );

    if ( c )
    {
	c->set_sock(sock);
	c->connect_accept((ULONG32)m_hInst, addr);
	if (mCallBack && (m_SocketState == CONN_LISTENNING))
	{
	    mCallBack->Func(ACCEPT_NOTIFICATION, TRUE, (conn*)c);
	}
    }
    HX_RELEASE(c);
    return HXR_OK;
}
#endif //defined(HELIX_FEATURE_NETWORK_USE_SELECT)


//	This function actually starts an async DNS request
//	It does it on either the first read or first write after we
#ifndef WIN32_PLATFORM_PSPC
//	call ConnectAsync();
HX_RESULT win_net::DoStartAsyncConn()
{
    DEBUGOUTSTR( "win_net::DoStartAsyncConn()\r\n" );
    
    if (!m_pAsyncHost)
    {
	return (mLastError = HXR_FAILED);
    }

    if (!m_AsyncAddress)
    {
	m_AsyncAddress = new char[MAXGETHOSTSTRUCT];
    }

    if (!m_AsyncAddress)
    {
	return (mLastError = HXR_OUTOFMEMORY);
    }

    m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier( m_hInst , TRUE);
    m_AsyncNotifier->DoAsyncDNS( this, m_pAsyncHost, m_AsyncAddress, MAXGETHOSTSTRUCT );
    m_SocketState = CONN_DNS_INPROG;

    return( mLastError = HXR_WOULD_BLOCK );
}

//	This method get's called by connect() in the case of an async request
//	It doesn't however actually start the connection.  It just registers
//	that we need to do the connection.  DoStartAsyncConn() will really do it.
HX_RESULT win_net::ConnectAsync( LPCSTR host, UINT16 port )
{
    DEBUGOUTSTR( "win_net::ConnectAsync()\r\n" );

    bReadyToWrite = 0;

    if (!host)                 
    {
	mLastError = HXR_DNR;
	return mLastError;
    }
	
    if (get_sock() == INVALID_SOCKET)
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	return mLastError;
    }
	
    struct in_addr addr;  
    char* pTemp = strrchr(host, '.');
    if (pTemp && atoi(pTemp + 1))
    {   /* IP address. */
	addr.s_addr = sockObj->HXinet_addr(host);
	
	if ((UINT)addr.s_addr == (UINT)-1) 
	{
	    mLastError = HXR_DNR;
	    return mLastError;
	}
	else
	{
	    LPSTR			pTemp;
	    hostent			*pHost;

	    if (!m_AsyncAddress)
	    {
		m_AsyncAddress = new char[MAXGETHOSTSTRUCT];
	    }

	    if (!m_AsyncAddress)
	    {
		mLastError = HXR_OUTOFMEMORY;
		return (mLastError);
	    }

	    pHost = (hostent *)m_AsyncAddress;

	    // this stores info about current addr 
	    CurrentAddr		= *(ULONG32 *)&addr;

	    m_AsyncPort = port;
	    pTemp = (LPSTR)&addr.s_addr;
	    pHost->h_addr_list = &pTemp;

	    CB_DNSComplete( TRUE );
	}
    } 
    else if (conn::is_cached((char *)host,(ULONG32 *) &addr))
    {
	LPSTR			pTemp;
	hostent			*pHost;

	if (!m_AsyncAddress)
	{
	    m_AsyncAddress = new char[MAXGETHOSTSTRUCT];
	}

	if (!m_AsyncAddress)
	{
	    mLastError = HXR_OUTOFMEMORY;
	    return (mLastError);
	}

	pHost = (hostent *)m_AsyncAddress;

	// this stores info about current addr 
	CurrentAddr		= *(ULONG32 *)&addr;

	m_AsyncPort = port;
	pTemp = (LPSTR)&addr.s_addr;
	pHost->h_addr_list = &pTemp;

	CB_DNSComplete( TRUE );
    }
    else
    {
	//	Save the parameters, and tell ourselves we're starting up
	m_AsyncPort = port;
	if (m_pAsyncHost != host)
	{
	    HX_VECTOR_DELETE(m_pAsyncHost);
	    m_pAsyncHost = ::new_string(host);
	}
	m_SocketState = CONN_NO_CONN;
	return(DoStartAsyncConn());
    }

    return( HXR_OK );
}

//	Once async DNS has commpleted then we'll call this guy to do the
//	connection (again asynchronously).
void win_net::ContinueAsyncConnect()
{
    hostent			*pHost;
    struct in_addr	addr;

    DEBUGOUTSTR( "win_net::ContinueAsyncConnect()\r\n" );

    pHost = (hostent *)m_AsyncAddress;

    if (!pHost)
	return;

    memcpy( &(addr.s_addr), pHost->h_addr, sizeof( addr.s_addr ) ); /* Flawfinder: ignore */
    m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier( m_hInst ,TRUE);
    m_AsyncNotifier->DoAsyncSelect( this );
    
    struct sockaddr_in	SockAddr;

    memset( &SockAddr, 0, sizeof( SockAddr ) );
    SockAddr.sin_family = AF_INET;
    SockAddr.sin_addr.s_addr = *(long*)&(addr.s_addr);
    SockAddr.sin_port = sockObj->HXhtons( m_AsyncPort );
                

    if(callRaConnect && sockObj->HXconnect( get_sock(), (sockaddr*)&SockAddr, sizeof( SockAddr ) ))
    {   
	int code;
	code = sockObj->HXWSAGetLastError(); 
	if (code == WSAEWOULDBLOCK || code == WSAEINPROGRESS)
	{  
	    m_SocketState = CONN_CONNECT_INPROG;
	    mConnectionOpen = 1;
	    return;
	}   

	mLastError = HXR_NET_CONNECT;
	m_SocketState = CONN_CONNECT_FAILED;
	return;
    }       

    m_SocketState = CONN_CONNECT_INPROG;
	              
    mConnectionOpen = 1;
    return;
}

//	Called by the notifier to tell us that the DNS request completed
void win_net::CB_DNSComplete( int iSuccess )
{
    DEBUGOUTSTR( "CB_DNSComplete()\r\n" );

    mDNSDone	 = TRUE;

    if (iSuccess && m_pAsyncHost)
    {
	conn::add_to_cache(m_pAsyncHost, get_addr());
    }

    if (m_DNSOnly)
    {
	if (iSuccess)
	{
	    mHostIPValid = TRUE;
	    mHostIPAddr = get_addr();

	}
	else
	{
	    mHostIPValid = FALSE;
	}
    }
    else
    {
	if (iSuccess)
	{
	    m_SocketState = CONN_CONNECT_INPROG;
	    ContinueAsyncConnect();
	}
	else
	{
	    m_SocketState = CONN_DNS_FAILED;
	}
    }

    if (mCallBack)	 
    {
	mCallBack->Func(DNS_NOTIFICATION, iSuccess);
    }
}

//	Called by the notifier to tell us that the Connection completed
void win_net::CB_ConnectionComplete( int iSuccess )
{
    DEBUGOUTSTR( "CB_ConnectionComplete()\r\n" );

    if (iSuccess)
    {
	m_SocketState = CONN_OPEN;
	if (mCallBack)
	{
	    mCallBack->Func(CONNECT_NOTIFICATION);
	}
    }
    else
    {
	m_SocketState = CONN_CONNECT_FAILED;

	if (mCallBack)	 
	{
	    mCallBack->Func(CONNECT_NOTIFICATION, FALSE);
	}
    }
}

//	Called by the notifier when data ready for read/write
void win_net::CB_ReadWriteNotification( int iType )
{
#ifndef _DEMPSEY
	// noisy output
    DEBUGOUTSTR( "CB_ReadWriteNotification()....\r\n" );
#endif // _DEMPSEY
    //	Should do something here....
    if (mCallBack && (m_SocketState == CONN_OPEN))
    {
	if (iType == FD_WRITE)
	{
	    mCallBack->Func(WRITE_NOTIFICATION);
	}
	else if (iType == FD_READ)
	{
	    mCallBack->Func(READ_NOTIFICATION);
	}
	
	//mCallBack->callback_task( HX_UDP_CALLBACK, NULL );
    }
}

void win_net::CB_AcceptNotification()
{
    DEBUGOUTSTR( "CB_AcceptNotification()....\r\n" );
    HX_ASSERT(m_SocketState == CONN_LISTENNING);
    
    win_net* c = (win_net*)conn::actual_new_socket(HX_TCP_SOCKET);
    c->AddRef();
    conn::add_connection_to_list ( c );

    if ( c )
    {
	sockaddr_in addr;
	int len = sizeof(addr);
	SOCKET sock = sockObj->HXaccept(get_sock(), (sockaddr*)&addr, &len);
	if ( sock != INVALID_SOCKET)
	{
	    c->set_sock(sock);
	    c->connect_accept((ULONG32)m_hInst, addr);

	    if (mCallBack && (m_SocketState == CONN_LISTENNING))
	    {
		mCallBack->Func(ACCEPT_NOTIFICATION, TRUE, (conn*)c);
	    }
	}
    }
    HX_RELEASE(c);
}

void win_net::CB_CloseNotification()
{
    DEBUGOUTSTR( "CB_CloseNotification()....\r\n" );
    m_SocketState = CONN_CLOSED;
    if (mCallBack)
    {
	mCallBack->Func(CLOSE_NOTIFICATION);
    }
}
#endif

HX_RESULT win_net::connect( sockaddr_in *addr )
{
    if(callRaConnect && sockObj->HXconnect( get_sock(), (sockaddr*)addr, sizeof( addr ) ))
    {
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }                   
    
    mConnectionOpen = 1;
    return HXR_OK;     
}


HX_RESULT win_net::write(void * buf, UINT16  *len) 
{
    int got;

    DEBUGOUTSTR( "win_net::write()\r\n" );

    if (get_sock() == INVALID_SOCKET) 
    {
	// Not connected
	return( mLastError = HXR_NET_SOCKET_INVALID );
    }

    if (m_SocketState != CONN_OPEN ) //&& m_SocketState != CONN_DNS_INPROG)
    {
	//	We won't be able to write anything here, so clear this
	//	we'll return why we didn't write anything.
	*len = 0;

	switch( m_SocketState )
	{
	case CONN_DNS_INPROG:
	case CONN_CONNECT_INPROG:
	case CONN_CLOSING:
		return( mLastError = HXR_WOULD_BLOCK );

	case CONN_CLOSED:
		return( mLastError = HXR_NET_SOCKET_INVALID );

	case CONN_NO_CONN:
#ifndef WIN32_PLATFORM_PSPC
		return( DoStartAsyncConn() );
#else
        HX_ASSERT(0 && "No Async net");
        return( HXR_NET_READ );
#endif

	case CONN_DNS_FAILED:
		return( mLastError = HXR_DNR );

	case CONN_CONNECT_FAILED:
		return( mLastError = HXR_NET_CONNECT );
	default:
		//	HUH???
		assert( 0 );
		return( mLastError = HXR_NET_READ );
	};
    }
    else
    {
	got = sockObj->HXsend( get_sock(), (char *)buf, *len, 0 );
	if (got == -1)
	{	
	    int code;
	    *len = 0;
	    code = sockObj->HXWSAGetLastError();
	    // Mask the "so what?" errors
	    if (code == WSAEWOULDBLOCK || code == WSAEINPROGRESS) 
	    {
		return HXR_WOULD_BLOCK;
	    }
	    else
	    {
		mLastError = HXR_NET_WRITE;
		return mLastError;
	    }
	}

//#if defined(_DEBUG) && defined(_LOGSMIL)
//	if (::HXDebugOptionEnabled("zLogSMIL"))
//{FILE* f1 = ::fopen("e:\\temp\\foo.txt", "a+"); ::fwrite(buf, got, 1, f1);::fclose(f1);}
//#endif
	*len = got;
	return HXR_OK;
    }
}


HX_RESULT win_net::writeto(void * buf, UINT16  *len, ULONG32 addr, UINT16 port) 
{
    //sendto
    int got;

    sockaddr_in resend_addr;
    
    ::memset( &resend_addr, 0, sizeof( resend_addr ) );
    resend_addr.sin_family = AF_INET;
    resend_addr.sin_addr.s_addr = addr;
    resend_addr.sin_port = sockObj->HXhtons(port);

    got = sockObj->HXsendto(get_sock(), (char *) buf, *len, 0, (sockaddr *)&resend_addr, sizeof (resend_addr)); 
    if (got == -1)
    {	
	int code;
	*len = 0;
	code = sockObj->HXWSAGetLastError();
		// Mask the "so what?" errors
	if (code == WSAEWOULDBLOCK || code == WSAEINPROGRESS) 
	{
	    return HXR_WOULD_BLOCK;
	}
	else
	{
	    mLastError = HXR_NET_WRITE;
	    return mLastError;
	}
    }

    *len = got;
    return HXR_OK;
}


HX_RESULT win_net::WriteComplete   (char * Buffer, int length)
{
    int sent = 0;
    unsigned short cur_sent=0;

    while(sent < length)
    {
	cur_sent = length - sent;

	HX_RESULT ret = write(Buffer + sent, &cur_sent);
	if(ret != HXR_OK && ret != HXR_WOULD_BLOCK)
	    break;
	sent += cur_sent;
    }

//	m_SocketState = CONN_NO_CONN;

    if(sent < length)
    {
	mLastError = HXR_NET_WRITE;
	return mLastError;
    }

    return HXR_OK;
}

int win_net::ReadyToWrite()
{
    if(get_sock() < 0)
    {
	bReadyToWrite = 0;
	return bReadyToWrite;
    }

    if(bReadyToWrite)
	return 1;
    
    fd_set writefds;
    FD_ZERO(&writefds);
    FD_SET((UINT32)get_sock(), &writefds);
    
    TIMEVAL timeout;
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    if(sockObj->HXselect(0, NULL, &writefds,NULL, &timeout) == 1)
		bReadyToWrite = 1;
    
    return bReadyToWrite;        
}

void win_net::done (void)
{
#ifndef WIN32_PLATFORM_PSPC
    m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier( m_hInst , FALSE);
    if (m_AsyncNotifier)
    {
	m_AsyncNotifier->CancelSelect( this );
    }
#endif

    m_SocketState = CONN_CLOSING;
    if ((get_sock() != INVALID_SOCKET) && sockObj)
    {       
	if (sockObj->HXclosesocket(get_sock()))
	{
	    int code = 0;
	    code = sockObj->HXWSAGetLastError();
	}    
    }	

    set_sock( INVALID_SOCKET );
    m_SocketState = CONN_CLOSED;
    mConnectionOpen = 0;
}

inline HX_RESULT win_net::listen(ULONG32 ulLocalAddr, UINT16 port, 
				 UINT16 backlog, UINT16 blocking,
				 ULONG32 ulPlatform)
{
    m_hInst = (HINSTANCE)ulPlatform;

// accept is polled
#ifndef WIN32_PLATFORM_PSPC
    m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier(m_hInst ,TRUE);
    m_AsyncNotifier->DoAsyncSelect((win_net*)this );
#endif

    HX_RESULT ret = sockObj->HXlisten(get_sock(), backlog);
    if ( SUCCEEDED(ret) )
    {
	m_SocketState = CONN_LISTENNING;
	ret = HXR_OK;
    }
    else
    {
	ret = HXR_NET_SOCKET_INVALID;
    }
    return ret;
}

inline HX_RESULT win_net::blocking (void)
{
    unsigned long nonblocking = 0;
    return sockObj->HXioctlsocket(get_sock(), FIONBIO, &nonblocking); 
}
	
inline HX_RESULT win_net::nonblocking (void) 
{
    unsigned long nonblocking = 1;
    return sockObj->HXioctlsocket(get_sock(), FIONBIO, &nonblocking); 
}

HX_RESULT win_net::connect_accept(ULONG32 ulPlatform, sockaddr_in addr)
{
    m_hInst = (HINSTANCE)ulPlatform;
    
    CurrentAddr = addr.sin_addr.s_addr;

    // set up the new connection so it will use its own notifier. 
    // now we want to add this so it will recieve messages.
#ifndef WIN32_PLATFORM_PSPC
    m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier( m_hInst, TRUE);
    m_AsyncNotifier->DoAsyncSelect(this);
#endif

    m_SocketState = CONN_OPEN;
    mConnectionOpen = 1;

    return HXR_OK;
}

CHXMapPtrToPtr win_net::zm_MapObjectsToWinsockTasks;
CHXMapPtrToPtr win_net::zm_WinsockTasksRefCounts;
int win_net::zm_LibRefCount = 0;


BOOL win_net::IsWinsockAvailable(void* pObject)
{
    BOOL bWinSockInitedForTask = FALSE;

    // First check to see that the drivers are loaded...
    //
    // NOTE: this doesn't include initializing Winsock for 
    // the object's task...

    if(!OnlyOneOfThisInTheWholeProgram.IsInitialized())
    {
	OnlyOneOfThisInTheWholeProgram.Initialize();
    }

    if(OnlyOneOfThisInTheWholeProgram.IsInitialized())
    {
	zm_LibRefCount++;
    
    // Make sure Winsock is initialized for the object's task...

	// get current task.
#if defined( WIN32 )
	void* ulTask = (void*)GetCurrentProcessId();
#else
	void* ulTask = (void*)GetCurrentTask();
#endif

	// add map of object (http or session) to task
	zm_MapObjectsToWinsockTasks[pObject] = ulTask;

	// bump up ref count for task
	void* pVoid;
	ULONG32 TaskRefCount = 0;

	if (zm_WinsockTasksRefCounts.Lookup(ulTask,pVoid))
	{
		TaskRefCount = (ULONG32)pVoid;
	}
	TaskRefCount++;

	bWinSockInitedForTask = TRUE;

	HX_ASSERT_VALID_PTR(sockObj);
	zm_WinsockTasksRefCounts[ulTask] = (void*)TaskRefCount;
    }

    return bWinSockInitedForTask;
}

BOOL win_net::ReleaseWinsockUsage(void* pObject)
{
    // Only do any of this if we've first called IsWinsockAvailable()
    if (zm_LibRefCount > 0)
    {
	// get previous task from map of object (http or session) to task
	void* ulTask;

	if (zm_MapObjectsToWinsockTasks.Lookup(pObject,ulTask))
	{
	    zm_MapObjectsToWinsockTasks.RemoveKey(pObject);

	    // bump up ref count for task
	    void* pVoid;
	    ULONG32 TaskRefCount = 0;
	    if (zm_WinsockTasksRefCounts.Lookup(ulTask,pVoid))
	    {
		TaskRefCount = (ULONG32)pVoid;
	    }
	    TaskRefCount--;

	    // if 0 then call WSACleanup
	    if (TaskRefCount == 0)
	    {
		zm_WinsockTasksRefCounts.RemoveKey(ulTask);
	    }
	}

	// Decerement total ref count
	zm_LibRefCount--;
    }

    return TRUE;
}

HX_RESULT win_net::read(void * buf, UINT16 *len) 
{
    int 		got;
    static int breakpoint = 0;

    assert( buf );
    assert( len );

// This DEBUGOUTSTR is noisy.
#ifndef _DEMPSEY
    DEBUGOUTSTR( "win_net::read()\r\n" );
#endif // !_DEMPSEY

    if (get_sock() == INVALID_SOCKET || !callRaConnect) 
    {
	// Not connected
	return( mLastError = HXR_NET_SOCKET_INVALID );
    }

    //	Is the TCP socket actually connected yet?
    /* We want to read data even if we have received CloseNotification. 
     * This is because some unread data may be still be in the pipe
     * - RA 10/01/1997
     */
    if (m_SocketState != CONN_OPEN && m_SocketState != CONN_CLOSED)
    {
	//	No
	//	We won't be able to write anything here, so clear this
	//	we'll return why we didn't write anything.
	*len = 0;

	switch( m_SocketState )
	{
	case CONN_DNS_INPROG:
	case CONN_CONNECT_INPROG:
	case CONN_CLOSING:
		return( mLastError = HXR_WOULD_BLOCK );

	case CONN_CLOSED:
		return( mLastError = HXR_NET_SOCKET_INVALID );

	case CONN_NO_CONN:
#ifndef WIN32_PLATFORM_PSPC
		return( DoStartAsyncConn() );
#else
        HX_ASSERT(0 && "No Async net");
        return( HXR_NET_READ );
#endif


	case CONN_DNS_FAILED:
		return( mLastError = HXR_DNR );

	case CONN_CONNECT_FAILED:
		return( mLastError = HXR_NET_CONNECT );

	default:
		//	HUH???
		HX_ASSERT (FALSE);
		return( mLastError = HXR_NET_READ );
	};
    }
    else
    {
	//	Now we can actually do the read
	if (callRaConnect)
	{	
	    got = sockObj->HXrecv( get_sock(), (char *)buf, *len, 0 );
	}
	else
	{
	    int 		fromlen;
	    struct sockaddr 	from;
	
	    fromlen = sizeof( from );
	    got = sockObj->HXrecvfrom( get_sock(), (char *)buf, *len, 0, &from, &fromlen );
	    if (got > 0) breakpoint++;
	}

	// Did we get an error?
	if (got == SOCKET_ERROR) 
	{   
	    *len = 0;

	    if (m_SocketState == CONN_CLOSED)
	    {
		return( mLastError = HXR_NET_SOCKET_INVALID );
	    }

	    int code;

	    code = sockObj->HXWSAGetLastError();

	    //	Translate the error
	    switch (code)
	    {
	    case WSAEWOULDBLOCK:
	    case WSAEINPROGRESS:
		    return( mLastError = HXR_WOULD_BLOCK );

	    case WSAEFAULT:
	    case WSAENOTCONN:
	    case WSAENOTSOCK:
		    return( mLastError = HXR_NET_SOCKET_INVALID );

	    case WSAENETDOWN:
		    return( mLastError = HXR_GENERAL_NONET );

	    case WSAEINTR:
		    return( mLastError = HXR_BLOCK_CANCELED );

	    case WSAEMSGSIZE:
		    return( mLastError = HXR_MSG_TOOLARGE);

	    case WSAETIMEDOUT:
	    case WSAESHUTDOWN:
	    case WSAECONNABORTED:
		    return( mLastError = HXR_SERVER_DISCONNECTED );

	    case WSAECONNRESET:
		/*
		 * WinSock Recvfrom() Now Returns WSAECONNRESET Instead of Blocking or Timing Out (Q263823)
		 * workaround
		 */
		if (!m_bIgnoreWSAECONNRESET)
		{
		    return( mLastError = HXR_SERVER_DISCONNECTED );
		}
		else
		{
		    return( mLastError = HXR_WOULD_BLOCK );
		}

	    default:
		    return( mLastError = HXR_NET_READ );   // Error we don't know what to do about
	    }
	    //	Shouldn't really get here
	    return( mLastError );
	}
	else if (got == 0 && *len != 0)
	{
	    *len = 0;	

	    if (m_SocketState == CONN_CLOSED)
	    {
		return( mLastError = HXR_NET_SOCKET_INVALID );
	    }
	    else
	    {
		return( mLastError = HXR_SERVER_DISCONNECTED );
	    }
	}
	else
	{
	    //	This should be our exit point for successful read
	    *len = got;
	    return( HXR_OK );
	}
    }
}

HX_RESULT	
win_net::readfrom (REF(IHXBuffer*) pBuffer,
		   REF(UINT32)	    ulAddress,
		   REF(UINT16)	    ulPort)
{
    int 		got = 0;
    UINT16		size = 0;

#ifndef _DEMPSEY
    DEBUGOUTSTR( "win_net::readfrom()\r\n" );
#endif // !_DEMPSEY

    pBuffer = NULL;
    ulAddress = 0;
    ulPort = 0;

    if (get_sock() == INVALID_SOCKET || callRaConnect) 
    {
	// Not connected
	return( mLastError = HXR_NET_SOCKET_INVALID );
    }

    /* We want to read data even if we have received CloseNotification. 
     * This is because some unread data may be still be in the pipe
     * - RA 10/01/1997
     */
    if (m_SocketState != CONN_OPEN && m_SocketState != CONN_CLOSED)
    {
	//	No
	//	We won't be able to write anything here, so clear this
	//	we'll return why we didn't write anything.
	switch( m_SocketState )
	{
	case CONN_DNS_INPROG:
	case CONN_CONNECT_INPROG:
	case CONN_CLOSING:
		return( mLastError = HXR_WOULD_BLOCK );

	case CONN_CLOSED:
		return( mLastError = HXR_NET_SOCKET_INVALID );

	case CONN_NO_CONN:
#ifndef WIN32_PLATFORM_PSPC
		return( DoStartAsyncConn() );
#else
        HX_ASSERT(0 && "No Async net");
        return( HXR_NET_READ );
#endif

	case CONN_DNS_FAILED:
		return( mLastError = HXR_DNR );

	case CONN_CONNECT_FAILED:
		return( mLastError = HXR_NET_CONNECT );

	default:
		//	HUH???
		HX_ASSERT (FALSE);
		return( mLastError = HXR_NET_READ );
	};
    }
    else
    {
	int 			fromlen;
	struct sockaddr_in 	from;
    
	fromlen = sizeof( from );

	got = sockObj->HXrecvfrom( get_sock(), m_pInBuffer, TCP_BUF_SIZE, 0, (struct sockaddr*)&from, &fromlen );

	// Did we get an error?
	if (got == SOCKET_ERROR) 
	{   
	    if (m_SocketState == CONN_CLOSED)
	    {
		return( mLastError = HXR_NET_SOCKET_INVALID );
	    }

	    int code;

	    code = sockObj->HXWSAGetLastError();

	    //	Translate the error
	    switch (code)
	    {
	    case WSAEWOULDBLOCK:
	    case WSAEINPROGRESS:
		    return( mLastError = HXR_WOULD_BLOCK );

	    case WSAEFAULT:
	    case WSAENOTCONN:
	    case WSAENOTSOCK:
		    return( mLastError = HXR_NET_SOCKET_INVALID );

	    case WSAENETDOWN:
		    return( mLastError = HXR_GENERAL_NONET );

	    case WSAEINTR:
		    return( mLastError = HXR_BLOCK_CANCELED );

	    case WSAEMSGSIZE:
		    return( mLastError = HXR_MSG_TOOLARGE);

	    case WSAETIMEDOUT:
	    case WSAESHUTDOWN:
	    case WSAECONNABORTED:
		    return( mLastError = HXR_SERVER_DISCONNECTED );

	    case WSAECONNRESET:
		/*
		 * WinSock Recvfrom() Now Returns WSAECONNRESET Instead of Blocking or Timing Out (Q263823)
		 * workaround
		 */
		if (!m_bIgnoreWSAECONNRESET)
		{
		    return( mLastError = HXR_SERVER_DISCONNECTED );
		}
		else
		{
		    return( mLastError = HXR_WOULD_BLOCK );
		}
		    
	    default:
		    return( mLastError = HXR_NET_READ );   // Error we don't know what to do about
	    }
	    //	Shouldn't really get here
	    return( mLastError );
	}
	else if (got == 0)
	{
	    if (m_SocketState == CONN_CLOSED)
	    {
		return( mLastError = HXR_NET_SOCKET_INVALID );
	    }
	    else
	    {
		return( mLastError = HXR_SERVER_DISCONNECTED );
	    }
	}
	else
	{
	    //	This should be our exit point for successful read
	    CHXTimeStampedBuffer* pTimeBuffer = new CHXTimeStampedBuffer;
	    pTimeBuffer->AddRef();
	    pTimeBuffer->SetTimeStamp(HX_GET_TICKCOUNT());
	    pTimeBuffer->Set((UCHAR*)m_pInBuffer, got);
	    pBuffer = (IHXBuffer*) pTimeBuffer;

	    ulAddress = DwToHost(from.sin_addr.s_addr);
	    ulPort = WToHost(from.sin_port);
	    
	    return( HXR_OK );
	}
    }
}
    
ULONG32 win_net::get_addr()
{
    hostent			*pHost = (hostent *)m_AsyncAddress;
    ULONG32 addr = 0;
    
    // if CurrentAddr is set, we must have passed
    // a dotted IP address...
    if (CurrentAddr)
    {
	addr = CurrentAddr;
    }
    else if (pHost)
    {
	addr = (ULONG32) (*(ULONG32*)pHost->h_addr);
    }

    return addr;
}

UINT16 win_net::get_local_port()
{
    sockaddr_in addr;
    int addr_len = sizeof addr;
    memset(&addr, 0, HX_SAFESIZE_T(addr_len));
    int ret = sockObj->HXgetsockname(get_sock(), (sockaddr*)&addr, &addr_len);
 
    return (ret < 0) ? -1: WToHost(addr.sin_port);
}


// we need it for dns_find_ip_addr since async stuff needs a window handle...
HX_RESULT win_net::SetWindowHandle(ULONG32 handle) 
{
    m_hInst = (HINSTANCE)handle; 
    return HXR_OK;
}



HX_RESULT win_net::dns_find_ip_addr(const char * host, UINT16 blocking)
{
    if(!host)                 
    {
	mLastError = HXR_DNR;
	return mLastError;
    }

    if(get_sock() < 0)                 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	return mLastError;
    }

    if (conn::is_cached((char *) host, &mHostIPAddr))
    {
	mHostIPValid = TRUE;
	mDNSDone	= TRUE;
	mLastError = HXR_OK;
	if (mCallBack)	 
	{
	    mCallBack->Func(DNS_NOTIFICATION, TRUE);
	}
	return mLastError;
    }

    char* pTemp = strrchr(host, '.');
    if (pTemp && atoi(pTemp + 1))
    {   /* IP address. */

	struct in_addr addr;  
	mHostIPValid = FALSE;
	mHostIPAddr = 0;
	mDNSDone	= TRUE;

	addr.s_addr = sockObj->HXinet_addr(host);
	
	if ((UINT)addr.s_addr == (UINT)-1) 
	{
	    mLastError = HXR_DNR;
	    return mLastError;
	}

	mHostIPValid = TRUE;
	mHostIPAddr = *(ULONG32 *) &addr;
	conn::add_to_cache((char *) host, mHostIPAddr);

	if (mCallBack)	 
	{
	    mCallBack->Func(DNS_NOTIFICATION, TRUE);
	}
	return HXR_OK;
    } 

    if (blocking)
    {
	struct in_addr addr;  
		            
	mHostIPValid = FALSE;
	mHostIPAddr = 0;
	mDNSDone	= TRUE;

	struct hostent *h = sockObj->HXgethostbyname(host);
	
	if (!h || !h->h_addr ) 
	{
	    mLastError = HXR_DNR;
	    return mLastError;
	}

	memcpy(&addr, h->h_addr, sizeof(struct in_addr)); /* Flawfinder: ignore */

	mHostIPValid = TRUE;
	mHostIPAddr = *(ULONG32 *) &addr;
	conn::add_to_cache((char *) host, mHostIPAddr);

	if (mCallBack)	 
	{
	    mCallBack->Func(DNS_NOTIFICATION, TRUE);
	}
	return( mLastError = HXR_OK);
    }
    else
    {
	if (m_pAsyncHost != host)
	{
	    //	Save the parameters, and tell ourselves we're starting up
	    HX_VECTOR_DELETE(m_pAsyncHost);
	    m_pAsyncHost = ::new_string(host);
	}

	if (!m_AsyncAddress)
	{
	    m_AsyncAddress = new char[MAXGETHOSTSTRUCT];
	}

	if (!m_AsyncAddress)
	{
	    return (mLastError = HXR_OUTOFMEMORY);
	}

	// set the boolean variable that we are only doing async DNs and
	// do not intend to connect
#ifndef WIN32_PLATFORM_PSPC
	m_DNSOnly   = TRUE;

	m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier( m_hInst , TRUE);
	m_AsyncNotifier->DoAsyncDNS( this, m_pAsyncHost, m_AsyncAddress, MAXGETHOSTSTRUCT );
	m_SocketState = CONN_DNS_INPROG;
#else 
	m_DNSOnly   = FALSE;
#endif

	return( mLastError = HXR_WOULD_BLOCK );
    }
}


BOOL win_net::dns_ip_addr_found(BOOL * valid, ULONG32 *addr)
{
    if (mDNSDone)
    {
	// reset DNS only flag...
	m_DNSOnly   = FALSE;

	*valid = mHostIPValid;
	*addr  = mHostIPAddr;

	return TRUE;
    }
    else
	return FALSE;
}


BOOL win_net::set_receive_buf_size(int DesiredSize)
{
    int s = get_sock();
    if (s == INVALID_SOCKET) 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	return FALSE;
    }

    int RcvBufSize = 0;
    int iSize = sizeof(RcvBufSize);
    if (sockObj->HXgetsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&RcvBufSize, 
			      &iSize))
    {
	RcvBufSize = 0;
    }

    if (RcvBufSize < DesiredSize)
    {
	RcvBufSize = DesiredSize;
	if(sockObj->HXsetsockopt(s, SOL_SOCKET, SO_RCVBUF, 
				(char *)&RcvBufSize, sizeof(RcvBufSize)))
	{
	    int code;
	    code = sockObj->HXWSAGetLastError(); 
	    return FALSE;
	}
    }

    return TRUE;
}

/*
*   reuse_addr/reuse_port has to be called before a sock binds.  however, 
*   socket is not available until it binds as it is implemented.  So, set a 
*   flag here and do the actual setsockopt right before a sock binds.
*   Look in init_win().
*/
HX_RESULT	
win_net::reuse_addr(BOOL enable)
{
    m_bReuseAddr = enable;
    return HXR_OK;
}

HX_RESULT	
win_net::reuse_port(BOOL enable)
{
    m_bReusePort = enable;
    return HXR_OK;
}

HX_RESULT
win_net::get_host_name(char* name, int namelen)
{
    HX_ASSERT(sockObj);
    
    if (sockObj->HXgethostname(name, namelen) != 0)
    {
	return HXR_FAILED;
    }
    else
    {
	return HXR_OK;
    }   
}

HX_RESULT
win_net::get_host_by_name(char* name, REF(struct hostent*) h)
{
    HX_ASSERT(sockObj);
    
    h = sockObj->HXgethostbyname(name);
    if (!h || !h->h_addr_list)
    {
	return HXR_DNR;
    }
    else
    {
	return HXR_OK;
    }
}

HX_RESULT win_UDP::set_broadcast(BOOL enable)
{
	int ret;
	SOCKET s = get_sock();
	if(s == INVALID_SOCKET)
	{
		return( mLastError = HXR_NET_SOCKET_INVALID );
	}
	ret = sockObj->HXsetsockopt( s, SOL_SOCKET, SO_BROADCAST, (char*)&enable, sizeof(UINT32) );
	if(ret == -1)
		ret = HXR_BIND;
	return ret;
}

HX_RESULT 
win_UDP::set_send_size(UINT32 send_size)
{
    int s = get_sock();
    int ret = 0;
again:
    ret = sockObj->HXsetsockopt(s, SOL_SOCKET, SO_SNDBUF,
		     (char*)&send_size, sizeof(INT32));
    if (ret < 0 && send_size > 8192)
    {
	send_size >>= 1;
        goto again;
    }
    return ret;
}


HX_RESULT win_UDP::connect(const char* host, UINT16 port, UINT16 blocking, ULONG32 ulPlatform ) 
{
    HX_RESULT ret;
    if (get_sock() < 0 && (ret = init(INADDR_ANY, 0, blocking)) != HXR_OK)
    {                
	if(ret == HXR_BLOCK_CANCELED)
	    return ret;
  
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }

    return win_net::connect(host, port, blocking, ulPlatform );
}

HX_RESULT win_UDP::SetWindowHandle(ULONG32 handle)
{
    m_hInst = (HINSTANCE)handle;
#ifndef WIN32_PLATFORM_PSPC
    m_AsyncNotifier = CAsyncSockN::GetCAsyncSockNotifier( m_hInst ,TRUE);
    m_AsyncNotifier->DoAsyncSelect( this );
#endif
    return HXR_OK;		
}

HX_RESULT win_UDP::connect(sockaddr_in * addr, UINT16 blocking) 
{                
    HX_RESULT ret;
    if (get_sock() < 0 && (ret = init(INADDR_ANY, 0, blocking)) != HXR_OK)
    { 
	if(ret == HXR_BLOCK_CANCELED)
	    return ret;                                          
	  
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }

    return win_net::connect(addr);
}                                  

HX_RESULT win_TCP::connect(const char* host, UINT16 port, UINT16 blocking, ULONG32 ulPlatform ) 
{   
    HX_RESULT ret;
    if (get_sock() < 0 && (ret = init(INADDR_ANY, 0, blocking)) != HXR_OK)
    {    
	if(ret == HXR_BLOCK_CANCELED)
	    return ret;                                          
      
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }

    return win_net::connect(host, port, blocking, ulPlatform );
}

HX_RESULT win_TCP::connect(sockaddr_in * addr, UINT16 blocking) 
{
    HX_RESULT ret;
    if (get_sock() < 0 && (ret = init(INADDR_ANY, 0, blocking)) != HXR_OK)
    { 
	if(ret == HXR_BLOCK_CANCELED)
	    return ret;                                          
	  
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }

    return win_net::connect(addr);
}

inline HX_RESULT win_UDP::listen(ULONG32 ulLocalAddr, UINT16 port, 
				 UINT16 backlog, UINT16 blocking,
				 ULONG32 ulPlatform)
{
    return HXR_INVALID_OPERATION;
}

inline HX_RESULT win_TCP::listen(ULONG32 ulLocalAddr, UINT16 port, 
				 UINT16 backlog, UINT16 blocking,
				 ULONG32 ulPlatform)
{
    HX_RESULT ret = HXR_NET_SOCKET_INVALID;
    if ( get_sock() < 0 )
    {
	if ( ulLocalAddr == HX_INADDR_ANY )
	    ret = init(INADDR_ANY, port, blocking);
	else
	    ret = init(ulLocalAddr, port, blocking);
    }
    if ( FAILED(ret) )
    {
	if(ret == HXR_BLOCK_CANCELED)
	    return ret;
	mLastError = HXR_NET_SOCKET_INVALID;
	return mLastError;
    }

    return win_net::listen(ulLocalAddr, port, backlog, blocking, ulPlatform);
}


HX_RESULT
win_UDP::set_multicast()
{
#ifdef NO_MULTICAST
    return HXR_MULTICAST_UDP;
#else
    INT32         	ret;
    sockaddr_in addr;
    int addr_len = sizeof addr;

    if (get_sock() == INVALID_SOCKET) 
    {
        // Not connected
        return( mLastError = HXR_NET_SOCKET_INVALID );
    }

    memset(&addr, 0, HX_SAFESIZE_T(addr_len));
    ret = sockObj->HXgetsockname(get_sock(), (sockaddr*)&addr, &addr_len);

    if (ret < 0)
    {
        return HXR_MULTICAST_UDP;
    }

    ret = sockObj->HXsetsockopt(get_sock(), IPPROTO_IP, IP_MULTICAST_IF,
                       (char*) &addr.sin_addr.s_addr,
                       sizeof (addr.sin_addr.s_addr));
    if (ret < 0)
    {
	return HXR_MULTICAST_UDP;
    }
    return HXR_OK;

#endif /* NO_MULTICAST */
}

HX_RESULT
win_UDP::set_multicast_ttl(unsigned char ttl)
{
#ifdef NO_MULTICAST
    return HXR_MULTICAST_UDP;
#else
    if (get_sock() == INVALID_SOCKET) 
    {
        // Not connected
        return( mLastError = HXR_NET_SOCKET_INVALID );
    }
    
    INT32         ret;
    INT32         ttl_proxy = ttl;

    ret = sockObj->HXsetsockopt(get_sock(), IPPROTO_IP, IP_MULTICAST_TTL,
                       (char*) &ttl_proxy, sizeof (ttl_proxy));

    if (ret < 0)
    {
        return HXR_MULTICAST_UDP;
    }
    return HXR_OK;

#endif /* ! NO_MULTICAST */
}

HX_RESULT win_UDP::join_multicast_group(ULONG32 addr, ULONG32 if_addr)
{	
    int ret;
    ip_mreq		multicast_group;

    if (get_sock() == INVALID_SOCKET) 
    {
	// Not connected
	return( mLastError = HXR_NET_SOCKET_INVALID );
    }
    
    multicast_group.imr_multiaddr.s_addr = sockObj->HXhtonl(addr);
    multicast_group.imr_interface.s_addr = sockObj->HXhtonl(if_addr);
    
    ret = sockObj->HXsetsockopt(get_sock(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicast_group , sizeof (multicast_group) );
    if (ret == -1)
    {
	int err;
	err = sockObj->HXWSAGetLastError();
	return HXR_MULTICAST_JOIN;
    }
    return HXR_OK;
}

HX_RESULT win_UDP::leave_multicast_group(ULONG32 addr, ULONG32 if_addr)
{
    int ret;
    ip_mreq		multicast_group;

    if (get_sock() == INVALID_SOCKET) 
    {
	// Not connected
	return( mLastError = HXR_NET_SOCKET_INVALID );
    }

    multicast_group.imr_multiaddr.s_addr = sockObj->HXhtonl(addr);
//	multicast_group.imr_multiaddr.s_addr = sockObj->HXinet_addr("226.0.0.8");
    multicast_group.imr_interface.s_addr = sockObj->HXhtonl(if_addr);
    
    ret = sockObj->HXsetsockopt(get_sock(), IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&multicast_group , sizeof (multicast_group) );
    if (ret == -1)
    {
	return HXR_GENERAL_MULTICAST;
    }

    return HXR_OK;
}
HX_RESULT 
win_UDP::set_multicast_if(ULONG32 ulInterface)
{
    int ret;
    int s = get_sock();
    if(s == INVALID_SOCKET)
    {
	return( mLastError = HXR_NET_SOCKET_INVALID );
    }

    unsigned long addr = sockObj->HXhtonl(ulInterface);    
    ret = sockObj->HXsetsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, 
	(char*)&addr, sizeof(addr));

    if(ret == -1)
	ret = HXR_GENERAL_MULTICAST;
    return ret;
}

HX_RESULT	
win_UDP::GetFromInfo(ULONG32& ulAddr, UINT16& nPort)
{
    return HXR_OK;
}

WinsockManager::WinsockManager()
{
    bInitialized = FALSE;
    Initialize();
}


WinsockManager::~WinsockManager()
{
    conn::close_drivers(NULL);

    bInitialized = FALSE;
}

void
WinsockManager::Initialize()
{
    bInitialized = FALSE;

    HX_ASSERT(!sockObj);
    if(!sockObj)
	sockObj = new CHXSock;  

    if(conn::init_drivers(NULL) != HXR_OK)
	    return;

    if(sockObj && sockObj->WinSockAvail() && sockObj->InitWinsock())
    {
	bInitialized = TRUE;
    }

    UINT8 nWinSockVersion = sockObj->HXGetVersion();

    /*
     * Check to see what platform we are on
     */
    OSVERSIONINFO winver;
    winver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

#ifndef WIN32_PLATFORM_PSPC
    if(GetVersionEx(&winver))
    {
	if(nWinSockVersion == 2	&&
	   winver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
	   winver.dwMinorVersion == 0)
	{
	    /*
	     * Async lookup in Win 95 with WinSock2.0 installed returns the same handle for
	     * multiple requests. If there are multiple outstanding requests, there is
	     * no way to map the handle in the response to the outstansing request since
	     * all replies get mapped to one request. NT 4.0 returns different handles so
	     * name resolution works fine.
	     *
	     * We workaround this by queueing up the DNS requests.
	     */
	    sockGlobals.m_bWinSock2Suck = TRUE;
	}
    }
#endif
}

BOOL    
WinsockManager::IsInitialized()
{
    return bInitialized && sockObj;
}

