/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: unix_net.cpp,v 1.6.2.4 2004/07/09 01:46:41 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 ***** */

#if defined _LINUX && defined __GLIBC__ && 0
#define _JAVA_GREENTHREADS
#endif

// Java with green threads needs you to use the internal entry points
// for these system calls
#ifdef _JAVA_GREENTHREADS
#define READ ::__read
#define CONNECT ::__connect
#define RECVFROM ::__recvfrom
#else
#define READ ::read
#define CONNECT ::connect
#define RECVFROM ::recvfrom
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include "hxcom.h"
#include "hxbuffer.h"
#include "timebuff.h"
#include "hxtick.h"
#include "netbyte.h"
#include "platform/unix/unix_net.h"		//	Our declaration
#include "hxheap.h"
#include "hxslist.h"
#include "hxcom.h"
#include "hxengin.h"
#include "hxstrutl.h"
#include <errno.h>

#ifdef _IRIX
#include <bstring.h>		// 	used in FD_ZERO
#endif
// #include "netplay.h"		//	why?

#ifdef _AIX
#include <sys/select.h>
#endif

#include <sys/types.h> //for waitpid
#include <sys/wait.h>  //for waitpid

#include <fcntl.h>

#ifdef _UNIX_THREADS_SUPPORTED
#include "hxthread.h"
#endif  

int unix_net::InBlockingMode = 0;

// This thing doesn't get initialized if this is linked into a shared library
CHXSimpleList* unix_net::readers = NULL;
// So I've done a silly thing and labelled it so.
static BOOL silly_unix_hack_initialized = FALSE;

const int HOSTSTRUCTSIZE = sizeof( hostent ); 

unix_net::unix_net() 
{
	set_sock( INVALID_SOCKET );
	mLastError = HXR_OK;
	callRaConnect = 1;    
	bReadyToWrite = 0;
	m_SocketState = CONN_CLOSED;

	//Async DNS vars.
	CurrentAddr    = 0;
	m_DNSOnly      = FALSE;
	m_anDNSPipe[0] = nInvalidPipe;
	m_anDNSPipe[1] = nInvalidPipe;
	m_nChildProcID = 0;
	m_szPipeIP[0]  = '\0';

	
	read_pending = FALSE;
	m_lRefCount	    = 0;
	// Don't allocate this yet. Not all unix_net instances are actually
	// used to read, and if one isn't, allocating here wastes memory.
	m_pInBuffer = NULL;

	m_bReuseAddr = FALSE;
	m_bReusePort = FALSE;
	m_pAsyncHost = NULL;
        
#ifdef _UNIX_THREADS_SUPPORTED
        m_pResolver  = NULL;
#endif        
}

unix_net::~unix_net() 
{
    m_SocketState = CONN_CLOSING;
    if ((get_sock() != INVALID_SOCKET) )       
    {
	::close(get_sock());
	set_sock( INVALID_SOCKET );
    }
    m_SocketState = CONN_CLOSED;
    mConnectionOpen = 0;
    
    LISTPOSITION listpos = readers->Find(this);
    if(listpos)
    {
	readers->RemoveAt(listpos);
    }
    HX_VECTOR_DELETE(m_pInBuffer); 

    //If the DNS forked proccess is still running lets
    //kill it here.
    //Ignore any returned error, what would we do anyway?
    CleanUpChildProc();
    HX_VECTOR_DELETE(m_pAsyncHost);

#ifdef _UNIX_THREADS_SUPPORTED
    if( m_bThreadedDNS )
    {
        if(m_pResolver)
            m_pResolver->Exit(0);

        m_nResolved    = 0;
        HX_DELETE( m_pResolver );
    }
#endif  

}

void unix_net::CleanUpChildProc()
{
    //Close any open pipes as well.
    if( m_anDNSPipe[0] >= 0 )
    {
	::close( m_anDNSPipe[0] );
	m_anDNSPipe[0] = nInvalidPipe;
    }
    
    if( m_anDNSPipe[1] >= 0 )
    {
	::close( m_anDNSPipe[1] );
	m_anDNSPipe[1] = nInvalidPipe;
    }
    
    if( m_nChildProcID != 0 )
    {
	kill( m_nChildProcID, SIGKILL );
	m_nChildProcID = 0;
    }
}

unix_net * unix_net::new_socket(UINT16 type)
{
    unix_net *c = NULL;
    
    if(!silly_unix_hack_initialized)
    {
	readers = new CHXSimpleList;
	silly_unix_hack_initialized = TRUE; 
    }
    
    switch(type)
    {
    case HX_TCP_SOCKET:
	c = new unix_TCP;
	readers->AddTail(c);
	break;
	
    case HX_UDP_SOCKET:
	c = new unix_UDP;
	readers->AddTail(c);
	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 unix_net::init_drivers(void *params)
{
	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 unix_net::close_drivers(void *params)
{
	return(HXR_OK);
}

HX_RESULT 
unix_net::get_host_name(char *name, int namelen)
{
    if (::gethostname(name, namelen) == 0)
    {
	return HXR_OK;
    }
    else
    {
	return HXR_FAIL;
    }
}

HX_RESULT 
unix_net::get_host_by_name(char *name, REF(struct hostent*) pHostent)
{
    if (pHostent = ::gethostbyname(name))
    {
        return HXR_OK;
    }
    else
    {
        return HXR_FAIL;
    }    
}

HX_RESULT unix_net::host_to_ip_str( char *host, char *ip, UINT32 ulIPBufLen)
{
    HX_RESULT           theErr = HXR_OK;
    ULONG32             dwAddress;
    struct sockaddr_in  rInAddress;
    struct hostent*     pHostEntry;

    //	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.sin_addr.s_addr  = dwAddress;
    }
    else
    { 
	// Do DNS on the host name
	if (!(pHostEntry = gethostbyname( host )))
	{
	    // Error
	    theErr = HXR_DNR;
	}

	// Return w/o attempting any copies if there's an error
	if (theErr != HXR_OK)
	{
	    goto FuncExit;
	}
		
	// copy the ip address into rInAddress w/o calling memcpy()                    
        struct in_addr** ppAddr = (struct in_addr**)(pHostEntry->h_addr_list);
        memcpy(&rInAddress.sin_addr, ppAddr[0], sizeof(struct in_addr)); /* Flawfinder: ignore */
	
	// add to the dns cache
	conn::add_to_cache(host, (ULONG32) rInAddress.sin_addr.s_addr ); 
    }

    // Convert the ULONG32 IP address into a string and copy it into ip
    SafeStrCpy( ip, inet_ntoa( rInAddress.sin_addr ) , ulIPBufLen);

    // Single exit point
 FuncExit:

    return( theErr );
}

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

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

    delete this;
    return 0;
}

/*
*   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_unix().
*/
HX_RESULT	
unix_net::reuse_addr(BOOL enable)
{
    m_bReuseAddr = enable;
    return HXR_OK;
}

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

HX_RESULT unix_net::init_unix(UINT16 type, UINT32 local_addr, UINT16 port, 
                              UINT16 blocking)
{                                        
    int                s = INVALID_SOCKET;
    struct sockaddr_in addr;
#ifdef _BEOS
    char    mode = 1;
#else
    int	    mode = 1;
#endif
    
    mLastError = HXR_OK;
#ifdef _BEOS
    s = socket(AF_INET, type, 0);
#else
    s = socket(PF_INET, type, 0);
#endif
    if (s < 0) 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	return mLastError;
    }
    
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&m_bReuseAddr, sizeof(m_bReuseAddr)) < 0) 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	goto sock_err;
    }

#if defined SO_REUSEPORT
    if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (const char*)&m_bReusePort, sizeof(m_bReusePort)) < 0) 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	goto sock_err;
    }
#endif    
        
    memset(&addr, 0, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(local_addr);
    addr.sin_port = htons(port);
    
    if (::bind(s, (sockaddr*)&addr, sizeof addr) < 0) 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	goto sock_err;
    }
#ifdef FIONBIO
    if (!blocking && ioctl(s, FIONBIO, &mode) < 0) 
#elif SO_NONBLOCK
    if (!blocking && setsockopt(s,SOL_SOCKET,SO_NONBLOCK,&mode,1)<0)
#else
    if (!blocking && ::fcntl(get_sock(), F_SETFL, ::fcntl(get_sock(), F_GETFL, 0) | O_NONBLOCK) < 0)
#endif
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	goto sock_err;
    }
    DPRINTF(D_MSG,("unix_net::socket opened: %d\n", s));
    m_SocketState = CONN_OPEN;
    set_sock( s );

    return mLastError;
    
sock_err:
    ::close(s);
    m_SocketState = CONN_NO_CONN;
    return mLastError;
}

#if 0
/* mcast_multiple_if test */
/*
 *  it returns a number of multicast enabled NICs with a default multicast
 *  interface as the very first entry in the pIFList
 */
UINT32
unix_net::detectMcastIF(REF(BYTE**) pIFList) 
{

    struct ifconf ifc;
    int i;

    ifc.ifc_len = sizeof(buff);
    ifc.ifc_buf = buff;
	if (ioctl(skfd, SIOCGIFCONF, &ifc) >= 0) {
    {
        // XXXGo
        //printf("%s\n", pc);
	printf("%u\n", ul);
    }
    else
    {   
        printf("coudn't detect if\n");
    }
    //HX_VECTOR_DELETE(pc);
 
}
#endif

HX_RESULT unix_net::connect(const char* host, UINT16 port, UINT16 blocking, ULONG32 ulPlatform)
{
    DPRINTF(D_MSG,("unix_net::connect(): b: %d\n", blocking));
    
    bReadyToWrite = 0;
    
    //Get a host at all?
    if(!host)                 
    {
	mLastError = HXR_DNR;
	return mLastError;
    }

    //Do we have a socket yet?
    if(get_sock() < 0)                 
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	return mLastError;
    }
    
    if( blocking )
    {
	//Set our state.
	m_SocketState = CONN_DNS_INPROG;
	
	//check and see if we were passed a dot format IP address.
	memset(&m_sSockAddrIn, 0, sizeof(struct sockaddr_in));    
	char* pTemp = (char*)strrchr(host, '.');
	if (pTemp && atoi(pTemp + 1))
	{
	    m_sSockAddrIn.sin_addr.s_addr = inet_addr(host);
	    
	    if ((ULONG32)m_sSockAddrIn.sin_addr.s_addr == (ULONG32)-1) 
	    {
		mLastError = HXR_DNR;
		return mLastError;
	    }
	    //Set state to show we have the address ready to go.
	    m_SocketState = CONN_CONNECT_INPROG;
	} 
	
	//do a blocking gethostbyname() call.
	if( m_SocketState == CONN_DNS_INPROG )
	{ 
	    struct hostent *h = gethostbyname(host);
	    if (!h || !h->h_addr ) 
	    {
		mLastError = HXR_DNR;
                DPRINTF(D_MSG,("unix_net::connect() HXR_INVALID_HOST\r\n"));
		CB_ConnectionComplete(0);
		return HXR_DNR;
	    }
            
            struct in_addr** ppAddr = (struct in_addr**)(h->h_addr_list);
            memcpy(&m_sSockAddrIn.sin_addr, ppAddr[0], sizeof(struct in_addr)); /* Flawfinder: ignore */

	    if (m_pAsyncHost != host)
	    {
		HX_VECTOR_DELETE(m_pAsyncHost);
		m_pAsyncHost = ::new_string(host);
	    }
	    m_AsyncPort = port; 
	}
			
	m_sSockAddrIn.sin_family = AF_INET;
	m_sSockAddrIn.sin_port = htons(port);

	// this stores info about current addr 
	CurrentAddr = m_sSockAddrIn.sin_addr.s_addr;

	if(CONNECT(get_sock(), (sockaddr*)&m_sSockAddrIn, sizeof(struct sockaddr_in)))
	{   
	    if(!blocking && (errno == EWOULDBLOCK || errno == EINPROGRESS))
	    {  
		mConnectionOpen = 1;
		nonblocking();
		CB_ConnectionComplete(1);
		return HXR_OK;
	    }
            
            DPRINTF(D_MSG,("unix_net::connect() HXR_NET_CONNECT\r\n"));
            mLastError = HXR_NET_CONNECT;
	    CB_ConnectionComplete(0);
	    return HXR_NET_CONNECT;
	}       
	 	      
	mConnectionOpen = 1;
	nonblocking();
	m_SocketState = CONN_OPEN; // Rahul
	bReadyToWrite = 1;
	CB_ConnectionComplete(1);
	return HXR_OK;
    }//blocking

    return ConnectAsync( host, port );
}





#ifdef _UNIX_THREADS_SUPPORTED
void *unix_net::_ResolveIt( void* pArg )
{
    unix_net* pIt = (unix_net*)pArg;
    struct hostent *h = gethostbyname(pIt->m_pAsyncHost);
    if( (NULL!=h) && (NULL!=h->h_addr) ) 
    {
        //Got good IP, send it back in dot format.
        const u_char *src = (u_char*)h->h_addr_list[0];
        static const char fmt[] = "%u.%u.%u.%u";
        sprintf(pIt->m_szPipeIP, fmt, src[0], src[1], src[2], src[3]); /* Flawfinder: ignore */
    }
    pIt->m_nResolved = 1;
    return NULL;
}
#endif





HX_RESULT unix_net::DoStartAsyncConn()
{
    DPRINTF(D_MSG,("unix_net::DoStartAsyncConn()\r\n"));
    m_SocketState = CONN_DNS_INPROG;
    
#ifdef _UNIX_THREADS_SUPPORTED
    //Make the thread if we haven't already.

    if( m_bThreadedDNS )
    {
        if( NULL == m_pResolver)
        {
            m_nResolved    = 0;
            HXThread::MakeThread( m_pResolver );
            HX_ASSERT( m_pResolver );
        }
        
        //Set the state
        m_nResolved = 0;
        //Start the thread
        m_pResolver->CreateThread( _ResolveIt, (void*)this );
        //That is it!
        return( mLastError = HXR_WOULD_BLOCK );
    }
#endif
    //
    // Fork here to start the DNS process. The process will
    // be monitored by calls from proccess_idle to the method
    // 'CheckOnDNS()'.
    //

    //Create pipe to communicate between the child and parent.
    if ( 0 != pipe(m_anDNSPipe) )
    {
	//Can't create pipe.
	m_anDNSPipe[0] = nInvalidPipe;
	m_anDNSPipe[1] = nInvalidPipe;
	mLastError = HXR_GENERAL_NONET;
	return mLastError;
    }
    if( 0 > (m_nChildProcID = fork()))
    {
	//Error trying to fork.
	//What should we do?
	mLastError = HXR_GENERAL_NONET;
	m_SocketState = CONN_DNS_FAILED;
	CB_DNSComplete( 0 );
	return HXR_GENERAL_NONET;
    }
    
    if( 0 == m_nChildProcID )
    {
	//This is the child proc....

	//Close the read end of the pipe.
	if ( 0 != ::close( m_anDNSPipe[0]) )
	{
	    //close, error. Kill this child proc.
	    //This proc just exits. Return codes will come
	    //from the parent proc. Just write a NULL to 
	    //the pipe.
	    ::write( m_anDNSPipe[1], "\0", 1 );
	    exit(1);
	}

	m_anDNSPipe[0] = nInvalidPipe;

	//
	// Do the blocking DNS here
	//
	struct hostent *h = gethostbyname(m_pAsyncHost);
	if ( (NULL==h) || (NULL==h->h_addr) ) 
	{
	    //Bad host or other nasty.
        //printf("Bad host or some other nasty\n");	
	    //Send back \0 for now.
	    ::write( m_anDNSPipe[1], "\0", 1 );
	    exit(1);
	}
	//Got good IP, send it back in dot format.
	const u_char *src = (u_char*)h->h_addr_list[0];
	char szTmp[20]; /* Flawfinder: ignore */ //Just big enough to hold "255.255.255.255"
	static const char fmt[] = "%u.%u.%u.%u";
	SafeSprintf(szTmp, 20, fmt, src[0], src[1], src[2], src[3]);
        //printf("The Address is: %s\n", szTmp);	
        ::write( m_anDNSPipe[1], szTmp, strlen(szTmp)+1 );

        //Now close the pipe to ensure an EOF is written.
        //Close the read end of the pipe.
	if ( 0 != ::close( m_anDNSPipe[1]) )
	{
            //Unlikly, but if it does happend then we won't get
            //an EOF written to the pipe and the AsyncDNS will
            //never complete.
#ifdef _DEBUG            
            fprintf( stderr, "AsyncDNS can't close pipe. Disable AsyncDNS with NoAsyncDNS=1\n");
            fprintf( stderr, "If you have problems connecting.\n");
#endif            
	}
	_exit(0);
    }//m_nChildProcID

    //In parent....
    //Close the write end of the pipe.

    if ( 0 != ::close(m_anDNSPipe[1]) )
    {
	//close error.

	m_anDNSPipe[0] = nInvalidPipe;
	m_anDNSPipe[1] = nInvalidPipe;
	
	mLastError = HXR_GENERAL_NONET;
	return mLastError;
    }

    m_anDNSPipe[1] = nInvalidPipe;
    
    //We are now ready to read off of anDNSPipe[0] the
    //IP address of the host in dot format.

    //Set the pipes to non blocking.
    int flags;
    if( (flags = fcntl( m_anDNSPipe[0], F_GETFL, 0)) < 0 )
    {
	//Error, can't get the current flags for this pipe.
	mLastError = HXR_GENERAL_NONET;
	m_SocketState = CONN_DNS_FAILED;

	//Close the pipes and kill the child proc.
	CleanUpChildProc();

	//Report the disaster.
	CB_DNSComplete(0);
	return HXR_GENERAL_NONET;
    }
    flags |= O_NONBLOCK;
    if( fcntl( m_anDNSPipe[0], F_SETFL, flags) < 0 )
    {
	//Error, can't set the flags for this pipe.
	mLastError = HXR_GENERAL_NONET;
	m_SocketState = CONN_DNS_FAILED;
	
	//Close the pipes and kill the child proc.
	CleanUpChildProc();

	//Report the disaster.
	CB_DNSComplete(0);
	return HXR_GENERAL_NONET;
    }
    return( mLastError = HXR_WOULD_BLOCK );
}


HX_RESULT unix_net::CheckOnDNS()
{
    //Return this if nothing changes.
    mLastError = HXR_WOULD_BLOCK;

#ifdef _UNIX_THREADS_SUPPORTED
    if( m_bThreadedDNS )
    {
	AddRef();
        //Is the thread done yet?
        if( m_nResolved == 1 )
        {
            //Yeah, we are done. Wait on the thread.
            m_pResolver->Exit(0);

            //use it.
            if( strlen(m_szPipeIP) == 0 )
            {
                //badhost or DNS error. Close the pipe and go home.
                mLastError = HXR_DNR;
                CB_DNSComplete(0);
            }
            else
            {
                //Assume at this point that m_szPipeIP has a good IP in it.
                m_sSockAddrIn.sin_addr.s_addr = inet_addr(m_szPipeIP);
                if((ULONG32)m_sSockAddrIn.sin_addr.s_addr == (ULONG32)-1) 
                {
                    mLastError = HXR_DNR;
                    CB_DNSComplete(0);
                }
                else
                {
                    //Clear it for next time.
                    m_szPipeIP[0]='\0';
                    
                    //Set our current address...
                    CurrentAddr = m_sSockAddrIn.sin_addr.s_addr;
                    
                    CB_DNSComplete(1);
                    mLastError = HXR_OK;
                }
            }
        }
	HX_RESULT res = mLastError;
	Release();
        return res;
    }
    
#endif
    //Keep checking and see if the the DNS lookup is
    //done or not.

    char      szBuff[256]; /* Flawfinder: ignore */
    int       status = 0;


    //If its there grab it.
    memset(szBuff, 0, 256);
    status = ::read(m_anDNSPipe[0], szBuff, 255);
    
    if( status > 0 )
    {
	strncat( m_szPipeIP, szBuff, status ); /* Flawfinder: ignore */
    }

    //Did we find EOF?
    if( 0 == status )
    {
	//At this point m_szPipeIP has a good IP address or
	//an error (NULL byte)
	

	//close the last pipe.
	::close(m_anDNSPipe[0]); //Don't care about an error here.
	m_anDNSPipe[0] = nInvalidPipe;
	
	if( strlen(m_szPipeIP) == 0 )
	{
	    //badhost or DNS error. Close the pipe and go home.
	    mLastError = HXR_DNR;
	    CB_DNSComplete(0);
	}
	else
	{
	    //Assume at this point that m_szPipeIP has a good IP in it.
	    m_sSockAddrIn.sin_addr.s_addr = inet_addr(m_szPipeIP);
	    if((ULONG32)m_sSockAddrIn.sin_addr.s_addr == (ULONG32)-1) 
	    {
		mLastError = HXR_DNR;
		CB_DNSComplete(0);
	    }
	    else
	    {
		
		//Clear it for next time.
		m_szPipeIP[0]='\0';
		
		
		//grab the zombie child.
		::waitpid( m_nChildProcID, NULL,  0 );
		m_nChildProcID = 0;
		
		//Set our current address...
		CurrentAddr = m_sSockAddrIn.sin_addr.s_addr;
		
		CB_DNSComplete(1);
		mLastError = HXR_OK;
	    }
	}
    } else if( status<0 && EAGAIN!=errno )
    {
	//Just make sure the read returned EAGAIN and not
	//some other error on the pipe.
	
	m_szPipeIP[0]='\0';

	//Kill the DNS child proc and close the pipe we're going home.
	CleanUpChildProc();
	
	//Report the problem.
	mLastError = HXR_GENERAL_NONET;
	CB_DNSComplete(0);
    }

    return mLastError;
}

HX_RESULT unix_net::CheckForConnection()
{

    sockaddr_in cliaddr;
    HX_SOCKLEN_T addrlen = sizeof(sockaddr_in);
    //memset(&cliaddr, 0, addrlen);
    
    //Return this if nothing changes.
    mLastError = HXR_WOULD_BLOCK;
    
    int newSock = accept(&cliaddr, &addrlen);

    if ( newSock == INVALID_SOCKET )
    {
	// igno all errors...  r
 	  return HXR_WOULD_BLOCK;
    }
    else
    {
        unix_net* pNewConn = (unix_net*)conn::actual_new_socket(HX_TCP_SOCKET);
        pNewConn->AddRef();
        conn::add_connection_to_list(pNewConn);
	if ( pNewConn )
	{
	    pNewConn->set_sock(newSock);
	    if ( SUCCEEDED(pNewConn->connect_accept(&cliaddr)) )
	    {
	    	mLastError = HXR_OK;
	        CB_NewConnectionReady (TRUE, pNewConn);
	    }
	    else
	    {
	        CB_NewConnectionReady(FALSE, NULL);
	    }
	}
	else
	{
	    mLastError = HXR_OUTOFMEMORY;
	}
    }

    return mLastError;
}


// 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 unix_net::ConnectAsync( const char* host, UINT16 port )
{
    

    //If we have our child(forked) process going on then
    //make sure we kill it and start a new one. Also, close
    //any open pipes.
    CleanUpChildProc();

    bReadyToWrite = 0;

    if (!host)                 
    {
	mLastError = HXR_DNR;
	return mLastError;
    }
	
    if (get_sock() == INVALID_SOCKET)
    {
	mLastError = HXR_NET_SOCKET_INVALID;
	return mLastError;
    }
	
    char* pTemp = (char*)strrchr(host, '.');

    if (pTemp && atoi(pTemp + 1))
    {   /* IP address. */
	m_sSockAddrIn.sin_addr.s_addr = inet_addr(host);
	
	if ((UINT)m_sSockAddrIn.sin_addr.s_addr == (UINT)-1) 
	{
	    mLastError = HXR_DNR;
	    CB_DNSComplete(0);
	    return mLastError;
	}
	else
	{
	    // this stores info about current addr 
	    CurrentAddr = m_sSockAddrIn.sin_addr.s_addr;

	    m_AsyncPort = port;
	    if (m_pAsyncHost != host)
	    {
		HX_VECTOR_DELETE(m_pAsyncHost);
		m_pAsyncHost = ::new_string(host);
	    }
	    CB_DNSComplete(1);
	}
    } 
    else if (conn::is_cached((char *)host,(ULONG32 *) &m_sSockAddrIn.sin_addr.s_addr))
    {
	// this stores info about current addr 
	CurrentAddr = m_sSockAddrIn.sin_addr.s_addr;
	m_AsyncPort = port;
	if (m_pAsyncHost != host)
	{
	    HX_VECTOR_DELETE(m_pAsyncHost);
	    m_pAsyncHost = ::new_string(host);
	}

	CB_DNSComplete(1);
    }
    else
    {
	//We are going to do Async DNS.....
	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 unix_net::ContinueAsyncConnect()
{
    DPRINTF(D_MSG,("unix_net::ContinueAsyncConnect() socket: %d\r\n", get_sock()));
    int nResult=0;
    nResult = CONNECT( get_sock(),
		       (sockaddr*)&m_sSockAddrIn,
		       sizeof(m_sSockAddrIn) );

    if( nResult != 0 && errno != EISCONN )
    {   
	if( errno == EWOULDBLOCK || errno == EINPROGRESS || errno == EALREADY )
	{
	    m_SocketState = CONN_CONNECT_INPROG;
	}
	else
	{
	    mLastError = HXR_NET_CONNECT;
	    m_SocketState = CONN_CONNECT_FAILED;
	    nonblocking();
            DPRINTF(D_MSG,("unix_net::ContinueAsyncConnect() CONN_CONNECT_FAILED nResult: %d errno: %d(%s)\r\n", nResult, errno, strerror(errno)));
	    CB_ConnectionComplete(0);
	}
    }
    else
    {
	mConnectionOpen = 1;
	CB_ConnectionComplete(1);
    }
    return;
}


// Called by the notifier to tell us that the DNS request completed
void unix_net::CB_DNSComplete( int iSuccess )
{

    ULONG32 ulNotUsed;
    
    mDNSDone = TRUE;

    //Put it into the cache if its good and not there.
    if( iSuccess && 
	m_pAsyncHost &&
	0 == conn::is_cached(m_pAsyncHost, &ulNotUsed)
	)
    {
	conn::add_to_cache(m_pAsyncHost, m_sSockAddrIn.sin_addr.s_addr);
    }


    if( TRUE == m_DNSOnly )
    {
	
	//This is an DNSOnly object. Don't do the connect.
	if (iSuccess)
	{
	    mHostIPValid = TRUE;
	    mHostIPAddr = get_addr();   
	}
	else
	{
	    mHostIPValid = FALSE;
	}
    }
    else 
    {
	if(iSuccess)
	{
	    m_SocketState = CONN_CONNECT_INPROG;
	}
	else
	{
	    m_SocketState = CONN_DNS_FAILED;
	}
    }//TRUE==m_DNSOnly


    //
    // Handle any DNS notification callbacks.
    //
    if (mCallBack)
    {
	mCallBack->Func(DNS_NOTIFICATION, iSuccess);
    }

    if( FALSE==m_DNSOnly && iSuccess )
    {
	m_sSockAddrIn.sin_family = AF_INET;
	m_sSockAddrIn.sin_port = htons( m_AsyncPort );
	ContinueAsyncConnect();
    }
    return;
}

void unix_net::CB_NewConnectionReady(int iSuccess, unix_net* pConn)
{
    if ( mCallBack )
    {
        mCallBack->Func(ACCEPT_NOTIFICATION, iSuccess?TRUE:FALSE, (conn*)pConn);
    }
}

// Called by the notifier to tell us that the Connection completed
void unix_net::CB_ConnectionComplete( int iSuccess )
{
    DPRINTF(D_MSG,("CB_ConnectionComplete(%d)\r\n", iSuccess) );

    if (iSuccess)
    {
	m_SocketState = CONN_OPEN;
    }
    else
    {
	m_SocketState = CONN_CONNECT_FAILED;
    }
    
    if (mCallBack)
    {
	mCallBack->Func(CONNECT_NOTIFICATION, iSuccess?TRUE:FALSE);
    }
}

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

void unix_net::CB_CloseNotification()
{
    DPRINTF(D_MSG,("CB_CloseNotification()....\r\n") );
    m_SocketState = CONN_CLOSED;
}


HX_RESULT unix_net::connect( sockaddr_in *addr )
{
    //XXXgfw Is this method used?????
    //XXXgfw If this is used, should there be a non-blocking
    //XXXgfw version of this? If so, we need to pass in a flag
    //XXXgfw or something. Eitherway, I can't see where this is
    //XXXgfw used at all. Only blocking for now I guess.
    
    if(CONNECT( get_sock(), (sockaddr*)addr, sizeof( addr ) ))
    {
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }                   
    
    mConnectionOpen = 1;
    return HXR_OK;     
}


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

	DPRINTF(D_MSG, ("unix_net::write() s: %d l: %d\n",get_sock(),*len));

	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:
			return( mLastError = HXR_NET_CONNECT );
			// return( DoStartAsyncConn() );

		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
	{
#if defined(_BEOS)
		got = ::send( get_sock(), (void *)buf, *len, 0 );
#else
		got = ::write( get_sock(), (char *)buf, *len );
#endif
		if (got == -1)
		{	
			*len = 0;
				// Mask the "so what?" errors
			if (errno == EWOULDBLOCK || errno == EINPROGRESS) 
			{
			   return HXR_WOULD_BLOCK;
			}
			else
			{
			   mLastError = HXR_NET_WRITE;
			   return mLastError;
			}
		}
		*len = got;
		
		return HXR_OK;
	}
}

HX_RESULT unix_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 unix_net::ReadyToWrite()
{
	if(get_sock() < 0)
	{
		bReadyToWrite = 0;
		return bReadyToWrite;
	}

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

#ifdef _HPUX
	// mwebb added HP-specifix lines (for the curious, do "man select")
	// also changed timeout call to be a non-blocking "poll"
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
#endif
	if(select(0, NULL, &writefds,NULL, &timeout) == 1)
	    bReadyToWrite = 1;
	return bReadyToWrite;        
}

void unix_net::done (void)
{
    m_SocketState = CONN_CLOSING;
    if ((get_sock() != INVALID_SOCKET))
    {
        ::close(get_sock());
    }	
    set_sock( INVALID_SOCKET );
    m_SocketState = CONN_CLOSED;
    mConnectionOpen = 0;
    
    LISTPOSITION listpos = readers->Find(this);
    if(listpos)
    {
	readers->RemoveAt(listpos);
    }
}

inline HX_RESULT unix_net::listen (UINT16 	backlog)
{
    if ( ::listen(get_sock(), backlog) != -1 )
    {
        m_SocketState = CONN_LISTENNING;
	mConnectionOpen = 0;
	return HXR_OK;
    }
    else
    {
	m_SocketState = CONN_NO_CONN;	
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }
}

/*
inline HX_RESULT unix_net::blocking (void)
{
    unsigned long nonblocking = 0;
    return ::ioctl(get_sock(), FIONBIO, (char*)&nonblocking); 
}
	
inline HX_RESULT unix_net::nonblocking (void) 
{
    unsigned long nonblocking = 1;
    return ::ioctl(get_sock(), FIONBIO, (char*)&nonblocking); 
}
*/

inline HX_RESULT unix_net::connect_accept(sockaddr_in *addr)
{
    CurrentAddr = addr->sin_addr.s_addr;
    memcpy(&m_sSockAddrIn, addr, sizeof(sockaddr_in)); /* Flawfinder: ignore */
    mConnectionOpen = 1;
    m_SocketState = CONN_OPEN;
    mLastError = HXR_OK;
    nonblocking();
    return HXR_OK;
}
/*										 
inline HX_RESULT unix_net::accept (sockaddr_in *addr, UINT16 *addrlen)
{
    return ::accept(get_sock(), (sockaddr*)addr, (int *)addrlen);
}

inline HX_RESULT unix_net::bind (sockaddr_in *addr)
{
    return ::bind(get_sock(), (sockaddr*)addr, sizeof(addr));
}
*/

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

	assert( (PTR_INT)buf );
	assert( (PTR_INT)len );


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

	//	Is the TCP socket actually connected yet?
	if (m_SocketState != CONN_OPEN)
	{
		//	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:
			return( mLastError = HXR_NET_CONNECT );
			// return( DoStartAsyncConn() );

		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
	{
		//	Now we can actually do the read
#ifdef _BEOS
        got = ::recv( get_sock(), (char *)buf, *len, 0 ); 
#else
		got = READ( get_sock(), (char *)buf, *len );
#endif

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

			//	Translate the error
			switch (errno)
			{
			case EWOULDBLOCK:
			    add_read_request();
			    return( mLastError = HXR_WOULD_BLOCK );

			case ECONNRESET:
				return( mLastError = HXR_SERVER_DISCONNECTED );

			default:
				return( mLastError = HXR_NET_READ );   // Error we don't know what to do about
			}
		}
		else if (got == 0) 
		{
			return (mLastError = HXR_SERVER_DISCONNECTED);
		}
		else
		{
			//	This should be our exit point for successful read
			DPRINTF(D_MSG, ("unix_net::read() s: %d l: %d\n",get_sock(),*len));
			*len = got;
			return( HXR_OK );
		}
	}
}

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

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

    // Allocate m_pInBuffer now, since we know we need it.
    if( m_pInBuffer == NULL )
    {
	m_pInBuffer = new char[TCP_BUF_SIZE];
    }

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

    //	Is the TCP socket actually connected yet?
    if (m_SocketState != CONN_OPEN)
    {
	//	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:
		return( mLastError = HXR_NET_CONNECT );
		// return( DoStartAsyncConn() );

	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
    {
#if defined _AIX42
	//SCO's Unixware compiler will choke and die otherwise:
  	size_t	    fromlen = 0;
#elif defined _HPUX
	int fromlen = 0;
#else
	socklen_t   fromlen = 0;
#endif
	struct	sockaddr_in from;

	fromlen = sizeof(from);

	//	Now we can actually do the read
	got = RECVFROM( get_sock(), m_pInBuffer, TCP_BUF_SIZE, 0, (struct sockaddr*)&from, (HX_SOCKLEN_T *)&fromlen);

	// Did we get an error?
	if (got <= 0 ) 
	{   	
	    //	Translate the error
	    switch (errno)
	    {
	    case EWOULDBLOCK:
		add_read_request();
		return( mLastError = HXR_WOULD_BLOCK );

	    case ECONNRESET:
		    return( mLastError = HXR_SERVER_DISCONNECTED );

	    default:
		    return( mLastError = HXR_NET_READ );   // Error we don't know what to do about
	    }
	}


        //	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 );
    }
}

HX_RESULT unix_UDP::connect(const char* host, UINT16 port, UINT16 blocking, ULONG32 ulPlatform ) 
{
    blocking = GetAsyncDNSPref() ? 1 : blocking;

    HX_RESULT ret = HXR_OK;
    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;
    }

    ret = unix_net::connect(host, port, blocking, ulPlatform );
    if ( !ret )
	nonblocking();
	
    return ret;
}

HX_RESULT unix_UDP::connect(sockaddr_in * addr, UINT16 blocking) 
{

    blocking = GetAsyncDNSPref() ? 1 : blocking;

    HX_RESULT ret = HXR_OK;
    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 unix_net::connect(addr);
}                                  

HX_RESULT unix_TCP::connect(const char* host, UINT16 port, UINT16 blocking, ULONG32 ulPlatform ) 
{
    blocking = GetAsyncDNSPref() ? 1 : blocking;

    HX_RESULT ret = HXR_OK;
    
    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;
    }
    ret = unix_net::connect(host, port, blocking, ulPlatform );
#if 0
    if ( !ret )
	nonblocking();
#endif
    return  ret;
}

HX_RESULT unix_TCP::connect(sockaddr_in * addr, UINT16 blocking) 
{
    blocking = GetAsyncDNSPref() ? 1 : blocking;

    HX_RESULT ret = HXR_OK;
    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 unix_net::connect(addr);
}

HX_RESULT unix_TCP::listen(ULONG32 ulLocalAddr, UINT16 port,
			   UINT16 backlog, UINT16 blocking, 
			   ULONG32 ulPlatform) 
{
    // We will ignor the blockng parameter, and block for this
    // function but leave the socket in a non blocking state...
    HX_RESULT ret = HXR_OK;
    if ( get_sock() < 0 )
    {
	if ( ulLocalAddr == HX_INADDR_ANY )
	    ret = init(INADDR_ANY, port, 1);
	else
	    ret = init(ulLocalAddr, port, 1);
    }
    if ( FAILED(ret) )
    {
	if(ret == HXR_BLOCK_CANCELED)
	    return ret;
	mLastError = HXR_NET_CONNECT;
	return mLastError;
    }
    if ( unix_net::listen(backlog) != HXR_OK)
    {
        ret = HXR_NET_CONNECT;
    }

    if ( SUCCEEDED(ret) )
    {
        ret = unix_net::nonblocking();
    }
    return ret;
}

HX_RESULT
unix_UDP::set_multicast()
{
#ifdef NO_MULTICAST
    return HXR_MULTICAST_UDP;
#endif /* NO_MULTICAST */
    INT32         	ret;
    sockaddr_in 	addr;
#if defined _AIX42
    size_t addr_len = sizeof addr;
#elif defined _HPUX || defined _OSF1
    int addr_len = sizeof addr;
#else
    socklen_t addr_len = sizeof addr;
#endif

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

    memset(&addr, 0, HX_SAFESIZE_T(addr_len));
    ret = getsockname(get_sock(), (sockaddr*)&addr, &addr_len);
    if (ret < 0)
    {
        return HXR_MULTICAST_UDP;
    }
/*
 * Linux and Solairs seem to return 127.0.0.1 from getsockname() which they
 * don't accept as a valid interface in setsockopt(IP_MULTICAST_IF)
 */
#if defined _LINUX || defined _SOLARIS
    addr.sin_addr.s_addr = INADDR_ANY;
#endif /* _LINUX || _SOLARIS */
#ifdef _BEOS
    // FIXME no multicast for BeOS
    return HXR_MULTICAST_UDP;
#else
    ret = ::setsockopt(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 /* _BEOS */
}

HX_RESULT
unix_UDP::set_multicast_ttl(unsigned char ttl)
{
#ifdef NO_MULTICAST
    return HXR_MULTICAST_UDP;
#endif /* NO_MULTICAST */
    INT32         ret;
    unsigned char       ttl_proxy;
    ttl_proxy = ttl;

#ifdef _BEOS
    // FIXME no multicast for BeOS
    return HXR_MULTICAST_UDP;
#else
    if (get_sock() == INVALID_SOCKET) 
    {
        // Not connected
        return( mLastError = HXR_NET_SOCKET_INVALID );
    }
    
    ret = ::setsockopt(get_sock(), IPPROTO_IP, IP_MULTICAST_TTL,
                       (char*) &ttl_proxy, sizeof (ttl_proxy));
    if (ret < 0)
    {
        return HXR_MULTICAST_UDP;
    }
    return HXR_OK;
#endif /* _BEOS */
}

HX_RESULT unix_UDP::join_multicast_group(ULONG32 addr, ULONG32 if_addr)
{   
#if defined(_BEOS) 
    return HXR_NOTIMPL; 
#else
    
    int ret;
    ip_mreq multicast_group;

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

    u_char ttl = 254; // As Per Sujal.

    ret = ::setsockopt(get_sock(), IPPROTO_IP, IP_MULTICAST_TTL, &ttl, 
                                                        sizeof(ttl));
    if (ret == -1)
    {
        return HXR_MULTICAST_JOIN;
    }

    multicast_group.imr_multiaddr.s_addr = htonl(addr);
#ifdef _UNIX    
    multicast_group.imr_interface.s_addr = htonl(INADDR_ANY);
#elif    
    multicast_group.imr_interface.s_addr = htonl(if_addr);
#endif    

    ret = ::setsockopt(get_sock(), IPPROTO_IP, IP_ADD_MEMBERSHIP, 
               (char*) &multicast_group, 
               sizeof (multicast_group));

    if (ret < 0)
    {
        return HXR_MULTICAST_JOIN;
    }

    return HXR_OK;
#endif
}
HX_RESULT unix_UDP::leave_multicast_group(ULONG32 addr, ULONG32 if_addr)
{
#if defined(_BEOS) 
    return HXR_NOTIMPL; 
#else

    
    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 = htonl(addr);
    multicast_group.imr_interface.s_addr = htonl(if_addr);

    ret = ::setsockopt(get_sock(), IPPROTO_IP, IP_DROP_MEMBERSHIP,
                (char*) &multicast_group, sizeof (multicast_group));
    if (ret == -1)
    {
        return HXR_GENERAL_MULTICAST;
    }
    return HXR_OK;
#endif
}

HX_RESULT 
unix_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 = htonl(ulInterface);
    ret = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, 
	(char*)&addr, sizeof(addr));

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


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

HX_RESULT 
unix_UDP::set_send_size(UINT32 send_size)
{
#ifdef _BEOS
    // Be is messed up
    return -1;
#else
    int s = get_sock();
    int ret = 0;
again:
    ret = setsockopt(s, SOL_SOCKET, SO_SNDBUF,
		     (char*)&send_size, sizeof(INT32));
    if (ret < 0 && send_size > 8192)
    {
	send_size >>= 1;
        goto again;
    }
    return ret;
#endif
}


HX_RESULT unix_net::writeto(	void * buf, 	
				UINT16  *len, 
				ULONG32 addr, 
				UINT16 port)
{
    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 = htons(port);

	got = sendto(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;
}

ULONG32 unix_net::get_addr()
{
    return m_sSockAddrIn.sin_addr.s_addr;
}

UINT16 unix_net::get_local_port()
{
    sockaddr_in addr;
#if defined _AIX42
    size_t addr_len = sizeof addr;
#elif defined _HPUX || defined _OSF1
    int addr_len = sizeof addr;
#else
    socklen_t addr_len = sizeof addr;
#endif
    memset(&addr, 0, HX_SAFESIZE_T(addr_len));
    int ret = getsockname(get_sock(), (sockaddr*)&addr, &addr_len);
 
    return (ret < 0) ? -1 : WToHost(addr.sin_port);
}



HX_RESULT unix_net::dns_find_ip_addr(const char * host, UINT16 blocking)
{
    mHostIPValid = FALSE;
    mDNSDone	 = TRUE;

    blocking = GetAsyncDNSPref() ? 1 : 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;

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

    //Check for a dot format IP string.
    char* pTemp = (char*)strrchr(host, '.');
    if (pTemp && isdigit(*(pTemp + 1)))
    {   /* IP address. */
	
	mHostIPValid = FALSE;
	mHostIPAddr  = 0;
	mDNSDone     = TRUE;

	m_sSockAddrIn.sin_addr.s_addr = inet_addr(host);
	
	if ((UINT)m_sSockAddrIn.sin_addr.s_addr == (UINT)-1) 
	{
	    mLastError = HXR_DNR;
	    return mLastError;
	}

	mHostIPValid = TRUE;
	mHostIPAddr = m_sSockAddrIn.sin_addr.s_addr;
	conn::add_to_cache((char *)host, mHostIPAddr);

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

    if( blocking )
    { 
	hostent	*pHost = gethostbyname((char *) host);
        
	mHostIPValid = FALSE;
	mHostIPAddr  = 0;
	mDNSDone     = TRUE;

	if( !pHost || !pHost->h_addr )
	{
	    mLastError = HXR_DNR;
	    return mLastError;
	}

	mHostIPValid = TRUE;
        
        struct in_addr** ppAddr = (struct in_addr**)(pHost->h_addr_list);
	mHostIPAddr = ppAddr[0]->s_addr;

        conn::add_to_cache((char *) host, mHostIPAddr); 
	if (mCallBack) 
	{       
	    mCallBack->Func(DNS_NOTIFICATION, TRUE);
	}
	return HXR_OK;
    }
    else
    {
	//Non blocking code here.
	if (m_pAsyncHost != host)
	{
	    HX_VECTOR_DELETE(m_pAsyncHost);
	    m_pAsyncHost = ::new_string(host);
	}
	//Tell everyone we just want DNS and no connect.
	m_DNSOnly = TRUE;

	//Start up the non-blocking DNS.
	DoStartAsyncConn();
	
    }//blocking

    mLastError = HXR_OK;
    return mLastError;
}


BOOL unix_net::dns_ip_addr_found(BOOL* valid, ULONG32* addr)
{
    if (mDNSDone)
    {
	*valid = mHostIPValid;
	*addr  = mHostIPAddr;
	return TRUE;
    }
    else
	return FALSE;
}

void unix_net::add_read_request()
{
    read_pending = TRUE;
}

void 
unix_net::add_select(int *n, 
		     fd_set* readfds, 
		     fd_set* writefds, 
		     fd_set* exceptfds)
{
    unix_net* un;
    int sock;
    if(!silly_unix_hack_initialized)
    {
	readers = new CHXSimpleList;
	silly_unix_hack_initialized = TRUE; 
    }
    if(!unix_net::readers->IsEmpty())
    {
	CHXSimpleList::Iterator i;
	for(i = unix_net::readers->Begin(); i != unix_net::readers->End(); ++i)
	{
	    un = (unix_net*)(*i);
	    
	    sock = un->get_sock();
	    if(un->read_pending && sock >= 0)
	    {
		if(sock > *n)
		    *n = sock + 1;
		FD_SET(sock, readfds);
	    }
	}
    }
}

void 
unix_net::process_select(int n, 
			 fd_set* readfds, 
			 fd_set* writefds, 
			 fd_set* exceptfds)
{
    unix_net* un;
    int sock;
    if(!unix_net::readers->IsEmpty())
    {
	CHXSimpleList::Iterator i, next;
	for(i = unix_net::readers->Begin(); i != unix_net::readers->End(); i = next)
	{
	    next = i;
	    ++next;
	    un = (unix_net*)(*i);
	    
	    sock = un->get_sock();
	    if(sock >= 0)
	    {
		if(FD_ISSET(sock, readfds))
		{
		    un->read_pending = FALSE;
		    un->CB_ReadWriteNotification(0);
		}
	    }
	}
    }
}

void unix_net::process_idle()
{
    unix_net* un;
    fd_set read_fds;
    struct timeval tv;
    int maxfd = 0;
    int sock = INVALID_SOCKET;

    if(!silly_unix_hack_initialized)
    {
	readers = new CHXSimpleList;
	silly_unix_hack_initialized = TRUE; 
    }

    FD_ZERO(&read_fds);
    if(!unix_net::readers->IsEmpty())
    {
	CHXSimpleList::Iterator i;
	for(i = unix_net::readers->Begin(); i != unix_net::readers->End(); ++i)
	{
	    un = (unix_net*)(*i);

	    //Hold on to this reference.
	    un->AddRef();

	    //Check the progress of each unix_net object's async DNS
	    //and connect.
	    if( un->GetSocketState() == CONN_DNS_INPROG )
		un->CheckOnDNS();
	    else if( un->GetSocketState() == CONN_CONNECT_INPROG )
		un->ContinueAsyncConnect();
	    else if ( un->GetSocketState() == CONN_LISTENNING )
	        un->CheckForConnection();
	    
	    sock = un->get_sock();
	    if(un->read_pending && sock >= 0)
	    {
		if(sock > maxfd)
		    maxfd = sock;
		FD_SET(sock, &read_fds);
	    }

	    //Release this reference.
	    if( 0 == un->Release() )
	    {
		//This object just got deleted. Make sure we adjust
		//the iterator back to a valid one because deleting
		//the object an iterator points to invalidates the 
		//iterator.
		
		//Make sure we don't iterate past the end with the
		//upcoming i++ in the for loop above.
		if( unix_net::readers->IsEmpty() )
		{
		    break;
		}

		//XXXgfw Can we do better then just setting it all
		//XXXgfw the way back to the begining?
		i = unix_net::readers->Begin();
	    }
	}
    }

    tv.tv_sec = 0;
    tv.tv_usec = 0;
    
    if(select(maxfd + 1, &read_fds, 0, 0, &tv) > 0)
    {
	if(!unix_net::readers->IsEmpty())
	{
	    CHXSimpleList::Iterator i, next;
	    for(i = unix_net::readers->Begin(); i != unix_net::readers->End(); i = next)
	    {
		next = i;
		++next;
		un = (unix_net*)(*i);
		
		sock = un->get_sock();
		if(un->read_pending && sock >= 0)
		{
		    if(FD_ISSET(sock, &read_fds))
		    {
			un->read_pending = FALSE;
			un->CB_ReadWriteNotification(0);
		    }
		}
	    }
	}
    }
}
