/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sal.hxx"

#include "system.h"

#include <osl/socket.h>
#include <osl/diagnose.h>
#include <rtl/alloc.h>

#include "sockimpl.h"

extern "C" {

/* defines for shutdown */
#ifdef GCC
#	define SD_RECEIVE 0
#	define SD_SEND 1
#	define SD_BOTH 2
#endif

/*
	oslSocketAddr is a pointer to a Berkeley struct sockaddr.
	I refrained from using sockaddr_in because of possible further
	extensions of this socket-interface (IP-NG?).
	The intention was to hide all Berkeley data-structures from
	direct access past the osl-interface.

	The current implementation is internet (IP) centered. All
	the constructor-functions (osl_create...) take parameters
	that will probably make sense only in the IP-environment
	(e.g. because of using the dotted-Addr-format).

	If the interface will be extended to host other protocol-
	families, I expect no externally visible changes in the
	existing functions. You'll probably need only new
	constructor-functions who take the different Addr
	formats into consideration (maybe a long dotted Addr
	or whatever).
*/

/*
  _Note_ that I rely on the fact that oslSocketAddr and struct sockaddr
  are the same! I don't like it very much but see no other easy way to
  conceal the struct sockaddr from the eyes of the user.
*/

#define OSL_INVALID_SOCKET		INVALID_SOCKET			/* WIN32 */
#define OSL_SOCKET_ERROR		SOCKET_ERROR			/* WIN32 */

/*****************************************************************************/
/* enum oslAddrFamily */
/*****************************************************************************/

/* map */
static DWORD FamilyMap[]= {
	AF_INET,					/* osl_Socket_FamilyInet */
	AF_IPX,						/* osl_Socket_FamilyIpx */
	0							/* osl_Socket_FamilyInvalid */
};

/* reverse map */
static oslAddrFamily osl_AddrFamilyFromNative(DWORD nativeType)
{
	oslAddrFamily i= (oslAddrFamily) 0;
	while(i != osl_Socket_FamilyInvalid)
	{
		if(FamilyMap[i] == nativeType)
			return i;
		i = (oslAddrFamily) ( (int)i + 1);
	}
	return i;
}

/* macros */
#define FAMILY_FROM_NATIVE(y) osl_AddrFamilyFromNative(y)
#define FAMILY_TO_NATIVE(x)	(short)FamilyMap[x]

/*****************************************************************************/
/* enum oslProtocol */
/*****************************************************************************/

/* map */
static DWORD ProtocolMap[]= {
	0,							/* osl_Socket_FamilyInet */
	NSPROTO_IPX,				/* osl_Socket_FamilyIpx */
	NSPROTO_SPX,				/* osl_Socket_ProtocolSpx */
	NSPROTO_SPXII,				/* osl_Socket_ProtocolSpx_ii */
	0							/* osl_Socket_ProtocolInvalid */
};

/* macros */
#define PROTOCOL_FROM_NATIVE(y) osl_ProtocolFromNative(y)
#define PROTOCOL_TO_NATIVE(x)	ProtocolMap[x]

/*****************************************************************************/
/* enum oslSocketType */
/*****************************************************************************/

/* map */
static DWORD TypeMap[]= {
	SOCK_STREAM,				/* osl_Socket_TypeStream */
	SOCK_DGRAM,					/* osl_Socket_TypeDgram  */
	SOCK_RAW,					/* osl_Socket_TypeRaw */
	SOCK_RDM,					/* osl_Socket_TypeRdm */
	SOCK_SEQPACKET,				/* osl_Socket_TypeSeqPacket */
	0							/* osl_Socket_TypeInvalid */
};

/* reverse map */
static oslSocketType osl_SocketTypeFromNative(DWORD nativeType)
{
	oslSocketType i= (oslSocketType)0;
	while(i != osl_Socket_TypeInvalid)
	{
		if(TypeMap[i] == nativeType)
			return i;
		i = (oslSocketType)((int)i+1);
	}
	return i;
}

/* macros */
#define TYPE_TO_NATIVE(x)		TypeMap[x]
#define TYPE_FROM_NATIVE(y)		osl_SocketTypeFromNative(y)

/*****************************************************************************/
/* enum oslSocketOption */
/*****************************************************************************/

/* map */
static DWORD OptionMap[]= {
	SO_DEBUG,					/* osl_Socket_OptionDebug */
	SO_ACCEPTCONN,				/* osl_Socket_OptionAcceptConn */
	SO_REUSEADDR,				/* osl_Socket_OptionReuseAddr */
	SO_KEEPALIVE,				/* osl_Socket_OptionKeepAlive */
	SO_DONTROUTE,				/* osl_Socket_OptionDontRoute */
	SO_BROADCAST,				/* osl_Socket_OptionBroadcast */
	SO_USELOOPBACK,				/* osl_Socket_OptionUseLoopback */
	SO_LINGER,					/* osl_Socket_OptionLinger */
	SO_OOBINLINE,				/* osl_Socket_OptionOOBinLine */
	SO_SNDBUF,					/* osl_Socket_OptionSndBuf */
	SO_RCVBUF,					/* osl_Socket_OptionRcvBuf */
	SO_SNDLOWAT,				/* osl_Socket_OptionSndLowat */
	SO_RCVLOWAT,				/* osl_Socket_OptionRcvLowat */
	SO_SNDTIMEO,				/* osl_Socket_OptionSndTimeo */
	SO_RCVTIMEO,				/* osl_Socket_OptionRcvTimeo */
	SO_ERROR,					/* osl_Socket_OptionError */
	SO_TYPE,					/* osl_Socket_OptionType */
	TCP_NODELAY,				/* osl_Socket_OptionTcpNoDelay */
	0							/* osl_Socket_OptionInvalid */
};

/* macros */
#define OPTION_TO_NATIVE(x)		OptionMap[x]
#define OPTION_FROM_NATIVE(y)	osl_SocketOptionFromNative(y)

/*****************************************************************************/
/* enum oslSocketOptionLevel */
/*****************************************************************************/

static DWORD OptionLevelMap[]= {
	SOL_SOCKET,					/* osl_Socket_LevelSocket */
	IPPROTO_TCP,				/* osl_Socket_LevelTcp */
	0							/* osl_invalid_SocketLevel */
};

/* macros */
#define OPTION_LEVEL_TO_NATIVE(x)		OptionLevelMap[x]
#define OPTION_LEVEL_FROM_NATIVE(y)		osl_SocketOptionLevelFromNative(y)

/*****************************************************************************/
/* enum oslSocketMsgFlag */
/*****************************************************************************/

static DWORD SocketMsgFlagMap[]= {
	0,							/* osl_Socket_MsgNormal */
	MSG_OOB,					/* osl_Socket_MsgOOB */
	MSG_PEEK,					/* osl_Socket_MsgPeek */
	MSG_DONTROUTE,				/* osl_Socket_MsgDontRoute */
	MSG_MAXIOVLEN				/* osl_Socket_MsgMaxIOVLen */
};

/* macros */
#define MSG_FLAG_TO_NATIVE(x)		SocketMsgFlagMap[x]
#define MSG_FLAG_FROM_NATIVE(y)		osl_SocketMsgFlagFromNative(y)

/*****************************************************************************/
/* enum oslSocketDirection */
/*****************************************************************************/

static DWORD SocketDirection[]= {
	SD_RECEIVE,					/* osl_Socket_DirRead */
	SD_SEND,					/* osl_Socket_DirWrite */
	SD_BOTH						/* osl_Socket_DirReadwrite */
};

/* macros */
#define DIRECTION_TO_NATIVE(x)		SocketDirection[x]
#define DIRECTION_FROM_NATIVE(y)	osl_SocketDirectionFromNative(y)

/*****************************************************************************/
/* enum oslSocketError */
/*****************************************************************************/

static int SocketError[]= {

	0,					/* no error */
	WSAENOTSOCK,			/* Socket operation on non-socket */
	WSAEDESTADDRREQ,		/* Destination address required */
	WSAEMSGSIZE,			/* Message too long */
	WSAEPROTOTYPE,			/* Protocol wrong type for socket */
	WSAENOPROTOOPT,		/* Protocol not available */
	WSAEPROTONOSUPPORT,	/* Protocol not supported */
	WSAESOCKTNOSUPPORT,	/* Socket type not supported */
	WSAEOPNOTSUPP,			/* Operation not supported on socket */
	WSAEPFNOSUPPORT,		/* Protocol family not supported */
	WSAEAFNOSUPPORT,		/* Address family not supported by */
							/* protocol family */
	WSAEADDRINUSE,			/* Address already in use */
	WSAEADDRNOTAVAIL,		/* Can't assign requested address */
	WSAENETDOWN,			/* Network is down */
	WSAENETUNREACH,		/* Network is unreachable */
	WSAENETRESET,			/* Network dropped connection because */
							/* of reset */
	WSAECONNABORTED,		/* Software caused connection abort */
	WSAECONNRESET,			/* Connection reset by peer */
	WSAENOBUFS,			/* No buffer space available */
	WSAEISCONN,			/* Socket is already connected */
	WSAENOTCONN,			/* Socket is not connected */
	WSAESHUTDOWN,			/* Can't send after socket shutdown */
	WSAETOOMANYREFS,		/* Too many references: can't splice */
	WSAETIMEDOUT,			/* Connection timed out */
	WSAECONNREFUSED,		/* Connection refused */
	WSAEHOSTDOWN,			/* Host is down */
	WSAEHOSTUNREACH,		/* No route to host */
	WSAEWOULDBLOCK,		/* call would block on non-blocking socket */
	WSAEALREADY,			/* operation already in progress */
	WSAEINPROGRESS		/* operation now in progress */
};

/* reverse map */
static oslSocketError osl_SocketErrorFromNative(int nativeType)
{
	oslSocketError i= (oslSocketError)0;

	while(i != osl_Socket_E_InvalidError)
	{
		if(SocketError[i] == nativeType)
			return i;

		i = (oslSocketError)( (int) i + 1);
	}
	return i;
}

/* macros */
#define ERROR_TO_NATIVE(x)		SocketError[x]
#define ERROR_FROM_NATIVE(y)	osl_SocketErrorFromNative(y)

/*****************************************************************************/
/* oslSocketDialupImpl */
/*****************************************************************************/
static oslSocketDialupImpl *pDialupImpl = NULL;

#if 0  /* INTERNAL DEBUG ONLY */
BOOL WINAPI __osl_autodial_Impl (DWORD dwFlags, DWORD dwReserved)
{
	return 0;
}

BOOL WINAPI __osl_autodialHangup_Impl (DWORD dwReserved)
{
	return 1;
}

BOOL WINAPI __osl_getConnectedState_Impl (LPDWORD lpdwFlags, DWORD dwReserved)
{
	if (lpdwFlags)
		*lpdwFlags = 0;
	return 0;
}
#endif /* INTERNAL DEBUG ONLY */

/*
 * __osl_createSocketDialupImpl.
 */
static oslSocketDialupImpl* __osl_createSocketDialupImpl (void)
{
	oslSocketDialupImpl *pImpl;
	pImpl = (oslSocketDialupImpl*)rtl_allocateZeroMemory( sizeof (oslSocketDialupImpl));

	InitializeCriticalSection (&pImpl->m_hMutex);

	return (pImpl);
}

/*
 * __osl_initSocketDialupImpl.
 */
static void __osl_initSocketDialupImpl (oslSocketDialupImpl *pImpl)
{
#ifdef SOCKET_USE_AUTODIAL
	if (pImpl)
	{
		HINSTANCE hModule;

		EnterCriticalSection (&pImpl->m_hMutex);

		hModule = LoadLibrary (INTERNET_MODULE_NAME);
		if (!(hModule <= (HINSTANCE)HINSTANCE_ERROR))
		{
			pImpl->m_pfnAttemptConnect = (INTERNETATTEMPTCONNECT)
				(GetProcAddress (hModule, "InternetAttemptConnect"));
			pImpl->m_pfnAutodial = (INTERNETAUTODIAL)
				(GetProcAddress (hModule, "InternetAutodial"));
			pImpl->m_pfnAutodialHangup = (INTERNETAUTODIALHANGUP)
				(GetProcAddress (hModule, "InternetAutodialHangup"));
			pImpl->m_pfnGetConnectedState = (INTERNETGETCONNECTEDSTATE)
				(GetProcAddress (hModule, "InternetGetConnectedState"));
			pImpl->m_hModule = hModule;
		}

		LeaveCriticalSection (&pImpl->m_hMutex);
	}
#else
    pImpl = pImpl; /* avoid warnings */
#endif
}

/*
 * __osl_destroySocketDialupImpl.
 */
static void __osl_destroySocketDialupImpl (oslSocketDialupImpl *pImpl)
{
	if (pImpl)
	{
		EnterCriticalSection (&pImpl->m_hMutex);

		if (pImpl->m_dwFlags & INTERNET_CONNECTION_HANGUP)
		{
			if (pImpl->m_pfnAutodialHangup)
			{
				(pImpl->m_pfnAutodialHangup)(0);
				pImpl->m_dwFlags &= ~INTERNET_CONNECTION_HANGUP;
			}
		}

		if (pImpl->m_hModule)
			FreeLibrary (pImpl->m_hModule);

		LeaveCriticalSection (&pImpl->m_hMutex);
		DeleteCriticalSection (&pImpl->m_hMutex);

		rtl_freeMemory (pImpl);
	}
}

/*
 * __osl_querySocketDialupImpl.
 */
static sal_Bool __osl_querySocketDialupImpl (void)
{
	sal_Bool result;

	if (pDialupImpl == NULL)
	{
		pDialupImpl = __osl_createSocketDialupImpl();
		__osl_initSocketDialupImpl (pDialupImpl);
	}

	EnterCriticalSection (&pDialupImpl->m_hMutex);

	result = sal_True;
	if (pDialupImpl->m_pfnGetConnectedState)
	{
		DWORD dwFlags = 0;

		result = (sal_Bool)(pDialupImpl->m_pfnGetConnectedState)(&dwFlags, 0);
		pDialupImpl->m_dwFlags |= dwFlags;
	}

	LeaveCriticalSection (&pDialupImpl->m_hMutex);
	return (result);
}

/*
 * __osl_attemptSocketDialupImpl.
 */
static sal_Bool __osl_attemptSocketDialupImpl (void)
{
	sal_Bool result;

	if (pDialupImpl == NULL)
	{
		pDialupImpl = __osl_createSocketDialupImpl();
		__osl_initSocketDialupImpl (pDialupImpl);
	}

	EnterCriticalSection (&pDialupImpl->m_hMutex);

	result = __osl_querySocketDialupImpl();
	if (!result)
	{
		result = sal_True;
		if (pDialupImpl->m_pfnAutodial)
		{
			result = (sal_Bool)(pDialupImpl->m_pfnAutodial)(0, 0);
			if (result)
				pDialupImpl->m_dwFlags |= INTERNET_CONNECTION_HANGUP;
			else
				WSASetLastError (WSAENETDOWN);
		}
	}

	LeaveCriticalSection (&pDialupImpl->m_hMutex);
	return (result);
}

/*****************************************************************************/
/* oslSocketImpl */
/*****************************************************************************/
static sal_uInt32 g_nSocketImpl = 0;

#if OSL_DEBUG_LEVEL > 1
static sal_uInt32 g_nSocketAddr = 0;
struct LeakWarning
{
	~LeakWarning()
	{
		if( g_nSocketImpl )
			OSL_TRACE( "sal_socket: %d socket instances leak\n" , g_nSocketImpl );
		if( g_nSocketAddr )
			OSL_TRACE( "sal_socket: %d socket address instances leak\n" , g_nSocketAddr );
	}
};
LeakWarning socketWarning;
#endif

/*
 * __osl_createSocketImpl.
 */
oslSocket __osl_createSocketImpl(SOCKET Socket)
{
	oslSocket pSockImpl = (oslSocket) rtl_allocateZeroMemory( sizeof(struct oslSocketImpl));
	pSockImpl->m_Socket = Socket;
	pSockImpl->m_nRefCount = 1;

	g_nSocketImpl++;

	return (pSockImpl);
}

/*
 * __osl_destroySocketImpl.
 */
void __osl_destroySocketImpl(oslSocketImpl *pImpl)
{
	if (pImpl)
	{
		if (--g_nSocketImpl == 0)
		{
			__osl_destroySocketDialupImpl (pDialupImpl);
			pDialupImpl = NULL;
		}
		rtl_freeMemory (pImpl);
	}
}
/*****************************************************************************/
static oslSocketAddr __osl_createSocketAddr(  )
{
	oslSocketAddr pAddr = (oslSocketAddr) rtl_allocateZeroMemory( sizeof( struct oslSocketAddrImpl ));
	pAddr->m_nRefCount = 1;
#if OSL_DEBUG_LEVEL > 1
	g_nSocketAddr ++;
#endif
	return pAddr;
}

static oslSocketAddr __osl_createSocketAddrWithFamily(
	oslAddrFamily family, sal_Int32 port, sal_uInt32 nAddr )
{
	OSL_ASSERT( family == osl_Socket_FamilyInet );

	oslSocketAddr pAddr = __osl_createSocketAddr();
	switch( family )
	{
	case osl_Socket_FamilyInet:
	{
		struct sockaddr_in* pInetAddr= (struct sockaddr_in*)&(pAddr->m_sockaddr);

		pInetAddr->sin_family = FAMILY_TO_NATIVE(osl_Socket_FamilyInet);
		pInetAddr->sin_addr.s_addr = nAddr;
		pInetAddr->sin_port = (sal_uInt16)(port&0xffff);
		break;
   	}   	
	default:
		pAddr->m_sockaddr.sa_family = FAMILY_TO_NATIVE(family);
	}
	return pAddr;
}

static oslSocketAddr __osl_createSocketAddrFromSystem( struct sockaddr *pSystemSockAddr )
{
	oslSocketAddr pAddr = __osl_createSocketAddr();
	memcpy( &(pAddr->m_sockaddr), pSystemSockAddr, sizeof( sockaddr ) );
	return pAddr;
}

static void __osl_destroySocketAddr( oslSocketAddr addr )
{
#if OSL_DEBUG_LEVEL > 1
	g_nSocketAddr --;
#endif
	rtl_freeMemory( addr );
}
/*****************************************************************************/
/* osl_createEmptySocketAddr */
/*****************************************************************************/
oslSocketAddr SAL_CALL osl_createEmptySocketAddr(oslAddrFamily Family)
{
	oslSocketAddr pAddr = 0;

	/* is it an internet-Addr? */
	if (Family == osl_Socket_FamilyInet)
	{
		pAddr = __osl_createSocketAddrWithFamily(Family, 0 , htonl(INADDR_ANY) );
	}
	else
	{
		pAddr = __osl_createSocketAddrWithFamily( Family , 0 , 0 );
	}

	return pAddr;
}

/*****************************************************************************/
/* osl_copySocketAddr */
/*****************************************************************************/
// @deprecated, to be removed
oslSocketAddr SAL_CALL osl_copySocketAddr(oslSocketAddr Addr)
{
	oslSocketAddr pCopy = 0;
	if (Addr)
	{
		pCopy = __osl_createSocketAddr();

		if (pCopy)
			memcpy(&(pCopy->m_sockaddr),&(Addr->m_sockaddr), sizeof(struct sockaddr));
	}
	return pCopy;
}

/*****************************************************************************/
/* osl_isEqualSocketAddr */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isEqualSocketAddr(oslSocketAddr Addr1, oslSocketAddr Addr2)
{
	struct sockaddr* pAddr1= &(Addr1->m_sockaddr);
	struct sockaddr* pAddr2= &(Addr2->m_sockaddr);

	OSL_ASSERT(pAddr1);
	OSL_ASSERT(pAddr2);

	if (pAddr1->sa_family == pAddr2->sa_family)
	{
		switch (pAddr1->sa_family)
		{
			case AF_INET:
			{
				struct sockaddr_in* pInetAddr1= (struct sockaddr_in*)pAddr1;
				struct sockaddr_in* pInetAddr2= (struct sockaddr_in*)pAddr2;

				if ((pInetAddr1->sin_family == pInetAddr2->sin_family) &&
					(pInetAddr1->sin_addr.s_addr == pInetAddr2->sin_addr.s_addr) &&
					(pInetAddr1->sin_port == pInetAddr2->sin_port))
					return (sal_True);
			}

			default:
			{
				return (memcmp(pAddr1, Addr2, sizeof(struct sockaddr)) == 0);
			}
		}
	}

	return (sal_False);
}

/*****************************************************************************/
/* osl_createInetBroadcastAddr */
/*****************************************************************************/
oslSocketAddr SAL_CALL osl_createInetBroadcastAddr (
	rtl_uString *strDottedAddr,
	sal_Int32    Port)
{
	sal_uInt32          nAddr = OSL_INADDR_NONE;

	if (strDottedAddr && strDottedAddr->length)
	{
		/* Dotted host address for limited broadcast */
		rtl_String *pDottedAddr = NULL;

		rtl_uString2String (
			&pDottedAddr, strDottedAddr->buffer, strDottedAddr->length,
			RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);

		nAddr = inet_addr (pDottedAddr->buffer);
		rtl_string_release (pDottedAddr);
	}

	if (nAddr != OSL_INADDR_NONE)
	{
		/* Limited broadcast */
		nAddr = ntohl(nAddr);
		if (IN_CLASSA(nAddr))
		{
			nAddr &= IN_CLASSA_NET;
			nAddr |= IN_CLASSA_HOST;
		}
		else if (IN_CLASSB(nAddr))
		{
			nAddr &= IN_CLASSB_NET;
			nAddr |= IN_CLASSB_HOST;
		}
		else if (IN_CLASSC(nAddr))
		{
			nAddr &= IN_CLASSC_NET;
			nAddr |= IN_CLASSC_HOST;
		}
		else
		{
			/* No broadcast in class D */
			return ((oslSocketAddr)NULL);
		}
		nAddr = htonl(nAddr);
	}

	oslSocketAddr pAddr =
		__osl_createSocketAddrWithFamily( osl_Socket_FamilyInet, htons( (sal_uInt16) Port), nAddr );
	return pAddr;
}

/*****************************************************************************/
/* osl_createInetSocketAddr */
/*****************************************************************************/
oslSocketAddr SAL_CALL osl_createInetSocketAddr (
	rtl_uString *strDottedAddr,
	sal_Int32    Port)
{
	DWORD Addr;
	rtl_String	*pDottedAddr=NULL;

	rtl_uString2String(
		&pDottedAddr, strDottedAddr->buffer, strDottedAddr->length,
		RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);

	Addr= inet_addr (pDottedAddr->buffer);
	rtl_string_release (pDottedAddr);

	oslSocketAddr pAddr = 0;
	if(Addr != -1)
	{
		pAddr = __osl_createSocketAddrWithFamily( osl_Socket_FamilyInet, htons( (sal_uInt16)Port), Addr );
	}
	return pAddr;
}

oslSocketResult SAL_CALL osl_setAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence *pByteSeq )
{
	OSL_ASSERT( pAddr );
	OSL_ASSERT( pByteSeq );

	oslSocketResult res = osl_Socket_Error;
	if( pAddr && pByteSeq )
	{
		OSL_ASSERT( pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE( osl_Socket_FamilyInet ) );
		OSL_ASSERT( pByteSeq->nElements == 4 );
		struct sockaddr_in * pSystemInetAddr = (struct sockaddr_in * ) &(pAddr->m_sockaddr);
		memcpy( &(pSystemInetAddr->sin_addr) , pByteSeq->elements , 4 );
		res = osl_Socket_Ok;
	}
	return res;
}

/** Returns the addr field in the struct sockaddr. ppByteSeq is in network byteorder. *ppByteSeq may
	either be 0 or contain a constructed sal_Sequence.
 */
oslSocketResult SAL_CALL osl_getAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence **ppByteSeq )
{
	OSL_ASSERT( pAddr );
	OSL_ASSERT( ppByteSeq );

	oslSocketResult res = osl_Socket_Error;
	if( pAddr && ppByteSeq )
	{
		struct sockaddr_in * pSystemInetAddr = (struct sockaddr_in * ) &(pAddr->m_sockaddr);
		rtl_byte_sequence_constructFromArray( ppByteSeq , (sal_Int8 *) &(pSystemInetAddr->sin_addr),4);
		res = osl_Socket_Ok;
	}
	return res;
}

/*****************************************************************************/
/* oslHostAddr */
/*****************************************************************************/
struct oslHostAddrImpl {
	rtl_uString     *pHostName;
	oslSocketAddr   pSockAddr;
} ;

static oslHostAddr __osl_hostentToHostAddr (const struct hostent *he)
{
	oslHostAddr pAddr= NULL;
	oslSocketAddr pSocketAddr = 0;

	rtl_uString     *cn= NULL;

	if ((he == NULL) || (he->h_name == NULL) || (he->h_addr_list[0] == NULL))
		return ((oslHostAddr)NULL);

	rtl_string2UString(
		&cn, he->h_name, strlen(he->h_name),
		RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
    OSL_ASSERT(cn != 0);

	pSocketAddr = __osl_createSocketAddr();

	if (pSocketAddr == NULL)
	{
		rtl_uString_release(cn);
		return ((oslHostAddr)NULL);
	}

	pSocketAddr->m_sockaddr.sa_family = he->h_addrtype;
	if (pSocketAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
	{
		struct sockaddr_in *sin= (struct sockaddr_in *)&(pSocketAddr->m_sockaddr);
		memcpy (
			&(sin->sin_addr.s_addr),
			he->h_addr_list[0],
			he->h_length);
	}
	else
	{
		/* unknown address family */
		/* future extensions for new families might be implemented here */

        OSL_TRACE("_osl_hostentToHostAddr(): unknown address family.\n");
		OSL_ASSERT(sal_False);

		__osl_destroySocketAddr( pSocketAddr );
		rtl_uString_release(cn);
		return ((oslHostAddr)NULL);
	}

	pAddr= (oslHostAddr )rtl_allocateMemory (sizeof (struct oslHostAddrImpl));

	if (pAddr == NULL)
	{
		__osl_destroySocketAddr( pSocketAddr );
		rtl_uString_release(cn);
		return ((oslHostAddr)NULL);
	}

	pAddr->pHostName= cn;
	pAddr->pSockAddr= pSocketAddr;

	return pAddr;
}

/*****************************************************************************/
/* osl_createHostAddr */
/*****************************************************************************/
oslHostAddr SAL_CALL osl_createHostAddr (
	rtl_uString         *strHostname,
	const oslSocketAddr  pSocketAddr)
{
	oslHostAddr pAddr;
	rtl_uString     *cn= NULL;

	if ((strHostname == NULL)  || (strHostname->length == 0) || (pSocketAddr == NULL))
		return ((oslHostAddr)NULL);

	rtl_uString_newFromString( &cn, strHostname);

	if ( ! pSocketAddr )
	{
		rtl_uString_release(cn);
		return ((oslHostAddr)NULL);
	}

	pAddr= (oslHostAddr)rtl_allocateMemory (sizeof (struct oslHostAddrImpl));

	if (pAddr == NULL)
	{
		rtl_uString_release(cn);
		return ((oslHostAddr)NULL);
	}

	pAddr->pHostName= cn;
	pAddr->pSockAddr= osl_copySocketAddr( pSocketAddr );

	return ((oslHostAddr)pAddr);
}

/*****************************************************************************/
/* osl_createHostAddrByName */
/*****************************************************************************/
oslHostAddr SAL_CALL osl_createHostAddrByName(rtl_uString *strHostname)
{
	if ((strHostname == NULL) || (strHostname->length == 0))
		return ((oslHostAddr)NULL);

	if (__osl_attemptSocketDialupImpl())
	{
		struct hostent *he;
		rtl_String     *Hostname= NULL;

		rtl_uString2String(
			&Hostname, strHostname->buffer, strHostname->length,
			RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);

		he= gethostbyname (Hostname->buffer);

		rtl_string_release (Hostname);
		return __osl_hostentToHostAddr (he);
	}
	return ((oslHostAddr)NULL);
}

/*****************************************************************************/
/* osl_createHostAddrByAddr */
/*****************************************************************************/
oslHostAddr SAL_CALL osl_createHostAddrByAddr(const oslSocketAddr pAddr)
{
	if (pAddr == NULL)
		return ((oslHostAddr)NULL);

	if (pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
	{
		const struct sockaddr_in *sin= (const struct sockaddr_in *)&(pAddr->m_sockaddr);

		if (sin->sin_addr.s_addr == htonl(INADDR_ANY))
			return ((oslHostAddr)NULL);

		if (__osl_attemptSocketDialupImpl())
		{
			struct hostent *he;
			he= gethostbyaddr ((const sal_Char *)&(sin->sin_addr),
							   sizeof (sin->sin_addr),
							   sin->sin_family);
			return __osl_hostentToHostAddr (he);
		}
	}

	return ((oslHostAddr)NULL);
}

/*****************************************************************************/
/* osl_copyHostAddr */
/*****************************************************************************/
oslHostAddr SAL_CALL osl_copyHostAddr(const oslHostAddr Addr)
{
	oslHostAddr pAddr= (oslHostAddr)Addr;

	if (pAddr)
		return osl_createHostAddr (pAddr->pHostName, pAddr->pSockAddr);
	else
		return ((oslHostAddr)NULL);
}

/*****************************************************************************/
/* osl_getHostnameOfHostAddr */
/*****************************************************************************/
void SAL_CALL osl_getHostnameOfHostAddr(
	const oslHostAddr pAddr, rtl_uString **strHostname)
{
	if (pAddr)
		rtl_uString_assign (strHostname, pAddr->pHostName);
	else
		rtl_uString_new (strHostname);
}

/*****************************************************************************/
/* osl_getSocketAddrOfHostAddr */
/*****************************************************************************/
oslSocketAddr SAL_CALL osl_getSocketAddrOfHostAddr(const oslHostAddr pAddr)
{
	if (pAddr)
		return (const oslSocketAddr)(pAddr->pSockAddr);
	else
		return NULL;
}

/*****************************************************************************/
/* osl_destroyHostAddr */
/*****************************************************************************/
void SAL_CALL osl_destroyHostAddr(oslHostAddr pAddr)
{
	if (pAddr)
	{
		if (pAddr->pHostName)
			rtl_uString_release (pAddr->pHostName);
		if (pAddr->pSockAddr)
			osl_destroySocketAddr( pAddr->pSockAddr );

		rtl_freeMemory (pAddr);
	}
}

/*****************************************************************************/
/* osl_getLocalHostname */
/*****************************************************************************/
oslSocketResult SAL_CALL osl_getLocalHostname (rtl_uString **strLocalHostname)
{
	static sal_Unicode LocalHostname[256] = {0};

	if (rtl_ustr_getLength(LocalHostname) == 0)
	{
		sal_Char Host[256]= "";
		if (gethostname(Host, sizeof(Host)) == 0)
		{
			/* check if we have an FQDN */
	    	if (strchr(Host, '.') == NULL)
	        {
				oslHostAddr pAddr;
				rtl_uString     *hostName= NULL;

				rtl_string2UString(
					&hostName, Host, strlen(Host),
					RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
                OSL_ASSERT(hostName != 0);

				/* no, determine it via dns */
				pAddr = osl_createHostAddrByName(hostName);
				rtl_uString_release (hostName);

				if (pAddr && pAddr->pHostName)
					memcpy(LocalHostname, pAddr->pHostName->buffer, sizeof(sal_Unicode)*(rtl_ustr_getLength(pAddr->pHostName->buffer)+1));
				else
					memset(LocalHostname, 0, sizeof(LocalHostname));

				osl_destroyHostAddr ((oslHostAddr)pAddr);
			}
		}
	}

	if (rtl_ustr_getLength(LocalHostname) > 0)
	{
		rtl_uString_newFromStr (strLocalHostname, LocalHostname);
		return osl_Socket_Ok;
	}

	return osl_Socket_Error;
}

/*****************************************************************************/
/* osl_resolveHostname */
/*****************************************************************************/
oslSocketAddr SAL_CALL osl_resolveHostname(rtl_uString* strHostname)
{
	oslHostAddr pAddr=
		(oslHostAddr )osl_createHostAddrByName (strHostname);
	if (pAddr)
	{
		oslSocketAddr SockAddr = osl_copySocketAddr( pAddr->pSockAddr );
		osl_destroyHostAddr(pAddr);
		return (SockAddr);
	}
	return ((oslSocketAddr)NULL);
}

/*****************************************************************************/
/* osl_getServicePort */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_getServicePort (
	rtl_uString* strServicename,
	rtl_uString* strProtocol)
{
	struct servent* ps;

	rtl_String *str_Servicename=NULL;
	rtl_String *str_Protocol=NULL;

	rtl_uString2String(
		&str_Servicename,
		rtl_uString_getStr(strServicename),
		rtl_uString_getLength(strServicename),
		RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
	rtl_uString2String(
		&str_Protocol,
		rtl_uString_getStr(strProtocol),
		rtl_uString_getLength(strProtocol),
		RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);

	ps= getservbyname(
		rtl_string_getStr(str_Servicename),
		rtl_string_getStr(str_Protocol));

	rtl_string_release( str_Servicename );
	rtl_string_release( str_Protocol );

	if (ps != 0)
		return ntohs(ps->s_port);

	return OSL_INVALID_PORT;
}

/*****************************************************************************/
/* osl_destroySocketAddr */
/*****************************************************************************/
void SAL_CALL osl_destroySocketAddr(oslSocketAddr pAddr)
{
	__osl_destroySocketAddr( pAddr );
}

/*****************************************************************************/
/* osl_getFamilyOfSocketAddr */
/*****************************************************************************/
oslAddrFamily SAL_CALL osl_getFamilyOfSocketAddr(oslSocketAddr pAddr)
{
	if (pAddr)
		return FAMILY_FROM_NATIVE(pAddr->m_sockaddr.sa_family);
	else
		return osl_Socket_FamilyInvalid;
}

/*****************************************************************************/
/* osl_getInetPortOfSocketAddr */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_getInetPortOfSocketAddr(oslSocketAddr pAddr)
{
	if( pAddr )
	{
		struct sockaddr_in* pSystemInetAddr= (struct sockaddr_in*)&(pAddr->m_sockaddr);

		if ( (pSystemInetAddr->sin_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet)))
			return ntohs(pSystemInetAddr->sin_port);
	}
	return OSL_INVALID_PORT;
}

/*****************************************************************************/
/* osl_setInetPortOfSocketAddr */
/*****************************************************************************/
sal_Bool SAL_CALL osl_setInetPortOfSocketAddr (
	oslSocketAddr pAddr,
	sal_Int32     Port)
{
	if (pAddr == NULL)
		return sal_False;

	struct sockaddr_in* pSystemInetAddr= (struct sockaddr_in*)&(pAddr->m_sockaddr);

	if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
		return sal_False;

	pSystemInetAddr->sin_port= htons((short)Port);
	return sal_True;
}

/*****************************************************************************/
/* osl_getHostnameOfSocketAddr */
/*****************************************************************************/
oslSocketResult SAL_CALL osl_getHostnameOfSocketAddr (
	oslSocketAddr   Addr,
	rtl_uString   **strHostName)
{
	oslHostAddr pAddr= osl_createHostAddrByAddr (Addr);

	if (pAddr)
	{
		rtl_uString_newFromString(strHostName, pAddr->pHostName);

		osl_destroyHostAddr(pAddr);

		return osl_Socket_Ok;
	}

	return osl_Socket_Error;
}

/*****************************************************************************/
/* osl_getDottedInetAddrOfSocketAddr */
/*****************************************************************************/
oslSocketResult	SAL_CALL osl_getDottedInetAddrOfSocketAddr (
	oslSocketAddr   pAddr,
	rtl_uString   **strDottedInetAddr)
{
	sal_Char           *pDotted;

	if (pAddr == NULL)
		return osl_Socket_Error;

	struct sockaddr_in *pSystemInetAddr = (struct sockaddr_in*) &(pAddr->m_sockaddr);
	if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
		return osl_Socket_Error;

	pDotted = inet_ntoa (pSystemInetAddr->sin_addr);
	rtl_string2UString(
		strDottedInetAddr, pDotted, strlen (pDotted),
		RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
    OSL_ASSERT(*strDottedInetAddr != 0);

	return osl_Socket_Ok;
}

/*****************************************************************************/
/* osl_createSocket  */
/*****************************************************************************/
oslSocket SAL_CALL osl_createSocket (
	oslAddrFamily Family,
	oslSocketType Type,
	oslProtocol   Protocol)
{
	/* alloc memory */
	oslSocket pSocket = __osl_createSocketImpl(0);

	if (pSocket == NULL)
		return 0;

	/* create socket */
	pSocket->m_Socket= socket(FAMILY_TO_NATIVE(Family),
								TYPE_TO_NATIVE(Type),
								PROTOCOL_TO_NATIVE(Protocol));

	/* creation failed => free memory */
	if(pSocket->m_Socket == OSL_INVALID_SOCKET)
	{
		__osl_destroySocketImpl(pSocket);
		pSocket= 0;
	}
	else
	{
		pSocket->m_Flags			= 0;
		pSocket->m_CloseCallback	= NULL;
		pSocket->m_CallbackArg	= NULL;
	}

	return pSocket;
}

void SAL_CALL osl_acquireSocket( oslSocket pSocket )
{
	osl_incrementInterlockedCount( &(pSocket->m_nRefCount) );
}

void SAL_CALL osl_releaseSocket( oslSocket pSocket )
{
	if( pSocket && 0 == osl_decrementInterlockedCount( &(pSocket->m_nRefCount) ) )
	{
		osl_closeSocket( pSocket );
		__osl_destroySocketImpl( pSocket );
	}
}

/*****************************************************************************/
/* osl_closeSocket  */
/*****************************************************************************/
void SAL_CALL osl_closeSocket(oslSocket pSocket)
{
	/* socket already invalid */
	if(pSocket==0)
		return;

	/* close */
	closesocket(pSocket->m_Socket);

	pSocket->m_Socket = OSL_INVALID_SOCKET;

	/* registrierten Callback ausfuehren */
	if (pSocket->m_CloseCallback != NULL)
	{
		pSocket->m_CloseCallback(pSocket->m_CallbackArg);
	}
}

/*****************************************************************************/
/* osl_getLocalAddrOfSocket  */
/* Note that I rely on the fact that oslSocketAddr and struct sockaddr */
/* are the same! I don't like it very much but see no other easy way */
/* to conceal the struct sockaddr from the eyes of the user. */
/*****************************************************************************/
oslSocketAddr SAL_CALL osl_getLocalAddrOfSocket(oslSocket pSocket)
{
	struct sockaddr Addr;
	int             AddrLen;

	if (pSocket == NULL) /* ENOTSOCK */
		return ((oslSocketAddr)NULL);

	AddrLen= sizeof(struct sockaddr);

	if (getsockname(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
		return ((oslSocketAddr)NULL);

	oslSocketAddr pAddr = __osl_createSocketAddrFromSystem( &Addr );
	return pAddr;
}

/*****************************************************************************/
/* osl_getPeerAddrOfSocket  */
/*****************************************************************************/
oslSocketAddr SAL_CALL osl_getPeerAddrOfSocket(oslSocket pSocket)
{
	struct sockaddr Addr;
	int             AddrLen;

	if (pSocket == NULL) /* ENOTSOCK */
		return ((oslSocketAddr)NULL);

	AddrLen= sizeof(struct sockaddr);

	if (getpeername(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
		return ((oslSocketAddr)NULL);

	oslSocketAddr pAddr = __osl_createSocketAddrFromSystem( &Addr );
	return pAddr;
}

/*****************************************************************************/
/* osl_bindAddrToSocket  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_bindAddrToSocket ( oslSocket pSocket, oslSocketAddr pAddr)
{
	OSL_ASSERT( pAddr );

	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	return (bind(pSocket->m_Socket,
				 &(pAddr->m_sockaddr),
				 sizeof(struct sockaddr)) != OSL_SOCKET_ERROR);
}

/*****************************************************************************/
/* osl_connectSocketTo  */
/*****************************************************************************/
oslSocketResult SAL_CALL osl_connectSocketTo (
	oslSocket        pSocket,
	oslSocketAddr    pAddr,
	const TimeValue* pTimeout)
{

	if (pSocket == NULL) /* ENOTSOCK */
		return osl_Socket_Error;

	if (pAddr == NULL) /* EDESTADDRREQ */
		return osl_Socket_Error;

	if (!__osl_attemptSocketDialupImpl()) /* ENETDOWN */
		return osl_Socket_Error;

	if (pTimeout == NULL)
	{
		if(connect(pSocket->m_Socket,
				   &(pAddr->m_sockaddr),
					sizeof(struct sockaddr)) == OSL_SOCKET_ERROR)
			return osl_Socket_Error;
		else
			return osl_Socket_Ok;
	}
	else
	{
		fd_set          fds;
		int	            error;
	    struct timeval  tv;
		unsigned long   Param;
		oslSocketResult Result= osl_Socket_Ok;

		if (pSocket->m_Flags & OSL_SOCKET_FLAGS_NONBLOCKING)
	    {
			if (connect(pSocket->m_Socket,
			    	    &(pAddr->m_sockaddr),
						sizeof(struct sockaddr)) == OSL_SOCKET_ERROR)
			{
			    switch (WSAGetLastError())
			    {
					case WSAEWOULDBLOCK:
					case WSAEINPROGRESS:
						return osl_Socket_InProgress;

					default:
						return osl_Socket_Error;
				}
			}
			else
				return osl_Socket_Ok;
		}

		/* set socket temporarily to non-blocking */
		Param= 1;
		OSL_VERIFY(ioctlsocket(
			pSocket->m_Socket, FIONBIO, &Param) != OSL_SOCKET_ERROR);

		/* initiate connect */
		if (connect(pSocket->m_Socket,
		     	    &(pAddr->m_sockaddr),
				    sizeof(struct sockaddr)) != OSL_SOCKET_ERROR)
		{
		   /* immediate connection */

			Param= 0;
			ioctlsocket(pSocket->m_Socket, FIONBIO, &Param);

			return osl_Socket_Ok;
	    }
		else
		{
			error = WSAGetLastError();

		    /* really an error or just delayed? */
		    if (error != WSAEWOULDBLOCK && error != WSAEINPROGRESS)
		    {
			     Param= 0;
			     ioctlsocket(pSocket->m_Socket, FIONBIO, &Param);

			     return osl_Socket_Error;
		    }
		}

		/* prepare select set for socket  */
		FD_ZERO(&fds);
		FD_SET(pSocket->m_Socket, &fds);

		/* divide milliseconds into seconds and microseconds */
		tv.tv_sec=	pTimeout->Seconds;
		tv.tv_usec=	pTimeout->Nanosec / 1000L;

		/* select */
	    error= select(pSocket->m_Socket+1,
		 		      0,
					  &fds,
					  0,
					  &tv);

		if (error > 0)  /* connected */
		{
			OSL_POSTCOND(
				FD_ISSET(pSocket->m_Socket, &fds),
				"osl_connectSocketTo(): select returned but socket not set\n");

			Result= osl_Socket_Ok;

		}
		else if(error < 0)  /* error */
		{
		    /* errno == EBADF: most probably interrupted by close() */
		    if(WSAGetLastError() == WSAEBADF)
			{
			    /* do not access pSockImpl because it is about to be or */
			    /* already destroyed */
			    return osl_Socket_Interrupted;
			}
			else
			    Result= osl_Socket_Error;

		}
		else    /* timeout */
			Result= osl_Socket_TimedOut;


		/* clean up */
		Param= 0;
		ioctlsocket(pSocket->m_Socket, FIONBIO, &Param);

		return Result;
	}
}

/*****************************************************************************/
/* osl_listenOnSocket  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_listenOnSocket (
	oslSocket  pSocket,
	sal_Int32  MaxPendingConnections)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	return (listen(pSocket->m_Socket,
				   MaxPendingConnections == -1 ?
				   SOMAXCONN :
				   MaxPendingConnections) != OSL_SOCKET_ERROR);
}

/*****************************************************************************/
/* osl_acceptConnectionOnSocket  */
/*****************************************************************************/
oslSocket SAL_CALL osl_acceptConnectionOnSocket (
	oslSocket      pSocket,
	oslSocketAddr* ppAddr)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return ((oslSocket)NULL);

	SOCKET          Connection;
	if(ppAddr)
	{
		if( *ppAddr )
		{
			osl_destroySocketAddr( *ppAddr );
			*ppAddr = 0;
		}
		int AddrLen= sizeof(struct sockaddr);

		/* user wants to know peer Addr */
		struct sockaddr Addr;

		Connection=	accept(pSocket->m_Socket, &Addr, &AddrLen);
		OSL_ASSERT(AddrLen == sizeof(struct sockaddr));

		if(Connection != OSL_SOCKET_ERROR)
			*ppAddr= __osl_createSocketAddrFromSystem(&Addr);
		else
			*ppAddr = NULL;
	}
	else
	{
		/* user is not interested in peer-addr */
		Connection=	accept(pSocket->m_Socket, 0, 0);
	}

	/* accept failed? */
	if(Connection == OSL_SOCKET_ERROR)
		return ((oslSocket)NULL);

	/* alloc memory */
	oslSocket  pConnectionSocket;
	pConnectionSocket= __osl_createSocketImpl(Connection);

	pConnectionSocket->m_Flags			= 0;
	pConnectionSocket->m_CloseCallback	= NULL;
	pConnectionSocket->m_CallbackArg	= NULL;

	return pConnectionSocket;
}

/*****************************************************************************/
/* osl_receiveSocket  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_receiveSocket (
	oslSocket        pSocket,
	void*            pBuffer,
	sal_uInt32       BytesToRead,
	oslSocketMsgFlag Flag)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return osl_Socket_Error;

	return recv(pSocket->m_Socket,
				(sal_Char*)pBuffer,
				BytesToRead,
				MSG_FLAG_TO_NATIVE(Flag));
}

/*****************************************************************************/
/* osl_receiveFromSocket  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_receiveFromSocket (
	oslSocket        pSocket,
	oslSocketAddr    SenderAddr,
	void*            pBuffer,
	sal_uInt32       BufferSize,
	oslSocketMsgFlag Flag)
{
	struct sockaddr *pSystemSockAddr = 0;
	int AddrLen = 0;
	if( SenderAddr )
	{
		AddrLen = sizeof( struct sockaddr );
		pSystemSockAddr = &(SenderAddr->m_sockaddr);
	}

	if (pSocket == NULL) /* ENOTSOCK */
		return osl_Socket_Error;

	return recvfrom(pSocket->m_Socket,
					 (sal_Char*)pBuffer,
					 BufferSize,
					 MSG_FLAG_TO_NATIVE(Flag),
					 pSystemSockAddr,
					 &AddrLen);
}

/*****************************************************************************/
/* osl_sendSocket  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_sendSocket (
	oslSocket        pSocket,
	const void*      pBuffer,
	sal_uInt32       BytesToSend,
	oslSocketMsgFlag Flag)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return osl_Socket_Error;

	return send(pSocket->m_Socket,
				(sal_Char*)pBuffer,
				BytesToSend,
				MSG_FLAG_TO_NATIVE(Flag));
}

/*****************************************************************************/
/* osl_sendToSocket  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_sendToSocket (
	oslSocket        pSocket,
	oslSocketAddr    ReceiverAddr,
	const void*      pBuffer,
	sal_uInt32       BytesToSend,
	oslSocketMsgFlag Flag)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return osl_Socket_Error;

	/* ReceiverAddr might be 0 when used on a connected socket. */
	/* Then sendto should behave like send. */

	struct sockaddr *pSystemSockAddr = 0;
	if( ReceiverAddr )
		pSystemSockAddr = &(ReceiverAddr->m_sockaddr);

	return sendto(pSocket->m_Socket,
				  (sal_Char*)pBuffer,
				  BytesToSend,
				  MSG_FLAG_TO_NATIVE(Flag),
				  pSystemSockAddr,
				  pSystemSockAddr == 0 ? 0 : sizeof(struct sockaddr));
}

/*****************************************************************************/
/* osl_readSocket  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_readSocket( oslSocket pSocket, void *pBuffer, sal_Int32 n )
{
	sal_uInt8 * Ptr = (sal_uInt8 *)pBuffer;

	OSL_ASSERT( pSocket);

	/* loop until all desired bytes were read or an error occured */
	sal_uInt32 BytesRead= 0;
	sal_uInt32 BytesToRead= n;
	while (BytesToRead > 0)
	{
		sal_Int32 RetVal;
		RetVal= osl_receiveSocket(pSocket,
								   Ptr,
								   BytesToRead,
								   osl_Socket_MsgNormal);

		/* error occured? */
		if(RetVal <= 0)
		{
			break;
		}

		BytesToRead -= RetVal;
		BytesRead += RetVal;
		Ptr += RetVal;
	}

	return BytesRead;
}

/*****************************************************************************/
/* osl_writeSocket  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_writeSocket( oslSocket pSocket, const void *pBuffer, sal_Int32 n )
{
    OSL_ASSERT( pSocket );

	/* loop until all desired bytes were send or an error occured */
	sal_uInt32 BytesSend= 0;
	sal_uInt32 BytesToSend= n;
	sal_uInt8 *Ptr = ( sal_uInt8 * )pBuffer;
	while (BytesToSend > 0)
	{
		sal_Int32 RetVal;

		RetVal= osl_sendSocket( pSocket,Ptr,BytesToSend,osl_Socket_MsgNormal);

		/* error occured? */
		if(RetVal <= 0)
		{
			break;
		}

		BytesToSend -= RetVal;
		BytesSend += RetVal;
		Ptr += RetVal;

	}
	return BytesSend;
}


/*****************************************************************************/
/* osl_isReceiveReady  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isReceiveReady (
	oslSocket        pSocket,
	const TimeValue* pTimeout)
{
	fd_set         fds;
	struct timeval tv;

	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	FD_ZERO(&fds);
	FD_SET(pSocket->m_Socket, &fds);

	if (pTimeout)
	{
		tv.tv_sec  = pTimeout->Seconds;
		tv.tv_usec = pTimeout->Nanosec / 1000L;
	}

	return (select(pSocket->m_Socket + 1,		/* no of sockets to monitor */
				   &fds,						/* check read operations */
				   0,							/* check write ops */
				   0,							/* ckeck for OOB */
				   (pTimeout) ? &tv : 0)==1);	/* use timeout? */
}

/*****************************************************************************/
/* osl_isSendReady  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isSendReady (
	oslSocket        pSocket,
	const TimeValue* pTimeout)
{
	fd_set         fds;
	struct timeval tv;

	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	FD_ZERO(&fds);
	FD_SET(pSocket->m_Socket, &fds);

	if (pTimeout)
	{
		tv.tv_sec  = pTimeout->Seconds;
		tv.tv_usec = pTimeout->Nanosec / 1000L;
	}

	return (select(pSocket->m_Socket + 1,		/* no of sockets to monitor */
				   0,							/* check read operations */
				   &fds,						/* check write ops */
				   0,							/* ckeck for OOB */
				   (pTimeout) ? &tv : 0)==1);	/* use timeout? */
}

/*****************************************************************************/
/* osl_isExceptionPending  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isExceptionPending (
	oslSocket        pSocket,
	const TimeValue* pTimeout)
{
	fd_set         fds;
	struct timeval tv;

	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	FD_ZERO(&fds);
	FD_SET(pSocket->m_Socket, &fds);

	if (pTimeout)
	{
		tv.tv_sec  = pTimeout->Seconds;
		tv.tv_usec = pTimeout->Nanosec / 1000L;
	}

	return (select(pSocket->m_Socket + 1,	    /* no of sockets to monitor */
				   0,							/* check read operations */
				   0,							/* check write ops */
				   &fds,						/* ckeck for OOB */
				   (pTimeout) ? &tv : 0)==1);	/* use timeout? */
}

/*****************************************************************************/
/* osl_shutdownSocket  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_shutdownSocket (
	oslSocket          pSocket,
	oslSocketDirection Direction)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	return (shutdown(pSocket->m_Socket, DIRECTION_TO_NATIVE(Direction))==0);
}

/*****************************************************************************/
/* osl_getSocketOption  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_getSocketOption (
	oslSocket            pSocket,
	oslSocketOptionLevel Level,
	oslSocketOption      Option,
	void*                pBuffer,
	sal_uInt32           BufferLen)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return osl_Socket_Error;

	if (getsockopt(pSocket->m_Socket,
	     		   OPTION_LEVEL_TO_NATIVE(Level),
				   OPTION_TO_NATIVE(Option),
				   (sal_Char*)pBuffer,
				   (int*)&BufferLen) == -1)
	{
		return -1;
	}

	return (sal_Int32)BufferLen;
}

/*****************************************************************************/
/* osl_setSocketOption  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_setSocketOption (
	oslSocket            pSocket,
	oslSocketOptionLevel Level,
	oslSocketOption      Option,
	void*                pBuffer,
	sal_uInt32           BufferLen)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	return(setsockopt(pSocket->m_Socket,
					  OPTION_LEVEL_TO_NATIVE(Level),
					  OPTION_TO_NATIVE(Option),
					  (sal_Char*)pBuffer,
					  BufferLen) == 0);
}

/*****************************************************************************/
/* osl_enableNonBlockingMode  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_enableNonBlockingMode ( oslSocket pSocket, sal_Bool  On)
{
	unsigned long  Param= On ? 1 : 0;

	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	pSocket->m_Flags = Param ?
		(pSocket->m_Flags |  OSL_SOCKET_FLAGS_NONBLOCKING) :
		(pSocket->m_Flags & ~OSL_SOCKET_FLAGS_NONBLOCKING) ;

	return (
		ioctlsocket(pSocket->m_Socket, FIONBIO, &Param) != OSL_SOCKET_ERROR);
}

/*****************************************************************************/
/* osl_isNonBlockingMode  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isNonBlockingMode(oslSocket pSocket)
{
	if (pSocket == NULL) /* ENOTSOCK */
		return sal_False;

	return (sal_Bool)((pSocket->m_Flags & OSL_SOCKET_FLAGS_NONBLOCKING) != 0);
}

/*****************************************************************************/
/* osl_getSocketType  */
/*****************************************************************************/
oslSocketType SAL_CALL osl_getSocketType(oslSocket pSocket)
{
	int            Type=0;
	int            TypeSize= sizeof(Type);

	if (pSocket == NULL) /* ENOTSOCK */
		return osl_Socket_TypeInvalid;

	if(getsockopt(pSocket->m_Socket,
				  OPTION_LEVEL_TO_NATIVE(osl_Socket_LevelSocket),
				  OPTION_TO_NATIVE(osl_Socket_OptionType),
				  (sal_Char *)&Type,
				  &TypeSize) == -1)
	{
		/* error */
		return osl_Socket_TypeInvalid;
	}

	return TYPE_FROM_NATIVE(Type);
}

/*****************************************************************************/
/* osl_getLastSocketErrorDescription  */
/*****************************************************************************/
void SAL_CALL osl_getLastSocketErrorDescription (
	oslSocket  /*Socket*/,
	rtl_uString	**strError)
{
	int error;

	switch(error = WSAGetLastError())
	{
		case WSAENOTSOCK:
			rtl_uString_newFromAscii (strError, "WSAENOTSOCK, Socket operation on non-socket. A socket created in one process is used by another process.");
            break;

        case WSAEDESTADDRREQ:
			rtl_uString_newFromAscii (strError, "WSAEDESTADDRREQ, Destination Addr required");
            break;

        case WSAEMSGSIZE:
            rtl_uString_newFromAscii (strError, "WSAEMSGSIZE, Message too long");
            break;

        case WSAEPROTOTYPE:
			rtl_uString_newFromAscii (strError, "WSAEPROTOTYPE, Protocol wrong type for socket");
            break;

        case WSAENOPROTOOPT:
			rtl_uString_newFromAscii (strError, "WSAENOPROTOOPT, Protocol not available");
            break;

        case WSAEPROTONOSUPPORT:
			rtl_uString_newFromAscii (strError, "WSAEPROTONOSUPPORT, Protocol not supported");
            break;

        case WSAESOCKTNOSUPPORT:
			rtl_uString_newFromAscii (strError, "WSAESOCKTNOSUPPORT, Socket type not supported");
            break;

        case WSAEOPNOTSUPP:
			rtl_uString_newFromAscii (strError, "WSAEOPNOTSUPP, Operation not supported on socket");
            break;

        case WSAEPFNOSUPPORT:
			rtl_uString_newFromAscii (strError, "WSAEPFNOSUPPORT, Protocol family not supported");
            break;

        case WSAEAFNOSUPPORT:
			rtl_uString_newFromAscii (strError, "WSEAFNOSUPPORT, Addr family not supported by protocol family");
            break;

        case WSAEADDRINUSE:
			rtl_uString_newFromAscii (strError, "WSAEADDRINUSE, Triggered by bind() because a process went down without closing a socket.");
            break;

        case WSAEADDRNOTAVAIL:
			rtl_uString_newFromAscii (strError, "WSAEADDRNOTAVAIL, Can't assign requested Addr");
            break;

        case WSAENETDOWN:
			rtl_uString_newFromAscii (strError, "WSAENETDOWN, Network is down");
            break;

        case WSAENETUNREACH:
			rtl_uString_newFromAscii (strError, "WSAENETUNREACH, Network is unreachable");
            break;

        case WSAENETRESET:
			rtl_uString_newFromAscii (strError, "WSAENETRESET, Network dropped connection or reset");
            break;

        case WSAECONNABORTED:
			rtl_uString_newFromAscii (strError, "WSAECONNABORTED, Software caused connection abort");
            break;

        case WSAECONNRESET:
			rtl_uString_newFromAscii (strError, "WSAECONNRESET, Connection reset by peer");
            break;

        case WSAENOBUFS:
			rtl_uString_newFromAscii (strError, "WSAENOBUFS, No buffer space available.");
            break;

        case WSAEISCONN:
			rtl_uString_newFromAscii (strError, "WSAEISCONN, Socket is already connected");
            break;

        case WSAENOTCONN:
			rtl_uString_newFromAscii (strError, "WSAENOTCONN, Socket is not connected");
            break;

        case WSAESHUTDOWN:
			rtl_uString_newFromAscii (strError, "WSAESHUTDOWN, Can't send after socket shutdown");
            break;

        case WSAETIMEDOUT:
			rtl_uString_newFromAscii (strError, "WSAETIMEDOUT, Connection timed out");
            break;

        case WSAECONNREFUSED:
			rtl_uString_newFromAscii (strError, "WSAECONNREFUSED, Connection refused");
            break;

        case WSAEHOSTDOWN:
			rtl_uString_newFromAscii (strError, "WSAEHOSTDOWN, Networking subsystem not started");
            break;

        case WSAEHOSTUNREACH:
			rtl_uString_newFromAscii (strError, "WSAEHOSTUNREACH, No route to host");
            break;

        case WSAEWOULDBLOCK:
			rtl_uString_newFromAscii (strError, "WSAEWOULDBLOCK, Operation would block");
            break;

        case WSAEINPROGRESS:
			rtl_uString_newFromAscii (strError, "WSAEINPROGRESS, Operation now in progress");
            break;

        case WSAEALREADY:
			rtl_uString_newFromAscii (strError, "WSAEALREADY, Operation already in progress");
            break;

        case WSAEINTR:
			rtl_uString_newFromAscii (strError, "WSAEALREADY, Operation was interrupted");
            break;

        case WSAEBADF:
			rtl_uString_newFromAscii (strError, "WSAEBADF, Bad file number");
            break;

        case WSAEACCES:
			rtl_uString_newFromAscii (strError, "WSAEACCES, Access is denied");
            break;

        case WSAEFAULT:
			rtl_uString_newFromAscii (strError, "WSAEFAULT, Bad memory Addr");
            break;

        case WSAEINVAL:
			rtl_uString_newFromAscii (strError, "WSAEINVAL, The socket has not been bound with bind() or is already connected");
            break;

        case WSAEMFILE:
			rtl_uString_newFromAscii (strError, "WSAEMFILE, No more file descriptors are available");
            break;

        case WSAETOOMANYREFS:
			rtl_uString_newFromAscii (strError, "WSAETOOMANYREFS, Undocumented WinSock error");
            break;

        case WSAENAMETOOLONG:
			rtl_uString_newFromAscii (strError, "WSAENAMETOOLONG, Undocumented WinSock error");
            break;

        case WSAENOTEMPTY:
			rtl_uString_newFromAscii (strError, "WSAENOTEMPTY, Undocumented WinSock error");
            break;

        case WSAEPROCLIM:
			rtl_uString_newFromAscii (strError, "WSAEPROCLIM, Undocumented WinSock error");
            break;

        case WSAEUSERS:
			rtl_uString_newFromAscii (strError, "WSAEUSERS, Undocumented WinSock error");
            break;

        case WSAEDQUOT:
			rtl_uString_newFromAscii (strError, "WSAEDQUOT, Undocumented WinSock error");
            break;

        case WSAESTALE:
			rtl_uString_newFromAscii (strError, "WSAESTALE, Undocumented WinSock error");
            break;

        case WSAEREMOTE:
			rtl_uString_newFromAscii (strError, "WSAEREMOTE, Undocumented WinSock error");
            break;

        case WSAEDISCON:
			rtl_uString_newFromAscii (strError, "WSAEDISCON, Circuit was gracefully terminated");
            break;

        case WSASYSNOTREADY:
			rtl_uString_newFromAscii (strError, "WSASYSNOTREADY, The underlying network subsystem is not ready for network communication");
            break;

        case WSAVERNOTSUPPORTED:
			rtl_uString_newFromAscii (strError, "WSAVERNOTSUPPORTED, The version of Windows Sockets API support requested is not provided by this particular Windows Sockets implementation");
            break;

        case WSANOTINITIALISED:
			rtl_uString_newFromAscii (strError, "WSANOTINITIALISED, WSAStartup() has not been called");
            break;

        case WSAHOST_NOT_FOUND:
			rtl_uString_newFromAscii (strError, "WSAHOST_NOT_FOUND, Authoritative answer host not found");
            break;

        case WSATRY_AGAIN:
			rtl_uString_newFromAscii (strError, "WSATRY_AGAIN, Non-authoritative answer host not found or SERVERFAIL");
            break;

        case WSANO_RECOVERY:
			rtl_uString_newFromAscii (strError, "WSANO_RECOVERY, Non recoverable errors, FORMERR, REFUSED, NOTIMP");
            break;

        case WSANO_DATA:
			rtl_uString_newFromAscii (strError, "WSANO_DATA or WSANO_ADDRESS, Valid name, no data record of requested type");
            break;

		default:
		{
			sal_Unicode message[128];

            wsprintfW(reinterpret_cast<LPWSTR>(message), L"Unknown WinSock Error Number %d", error);
			rtl_uString_newFromStr (strError, message);
        }

        return;

	}
}

/*****************************************************************************/
/* osl_getLastSocketError  */
/*****************************************************************************/
oslSocketError SAL_CALL osl_getLastSocketError(oslSocket /*Socket*/)
{
	return ERROR_FROM_NATIVE(WSAGetLastError());
}

/*****************************************************************************/
/* SocketSet                                                                 */
/*****************************************************************************/
typedef struct _TSocketSetImpl
{
	fd_set	m_Set;			/* the set of descriptors */

} TSocketSetImpl;

/*****************************************************************************/
/* osl_createSocketSet  */
/*****************************************************************************/
oslSocketSet SAL_CALL osl_createSocketSet()
{
	TSocketSetImpl* pSet;

	pSet = (TSocketSetImpl*) rtl_allocateMemory(sizeof(TSocketSetImpl));

	if(pSet)
	{
		FD_ZERO(&pSet->m_Set);
	}

	return (oslSocketSet)pSet;
}

/*****************************************************************************/
/* osl_destroySocketSet  */
/*****************************************************************************/
void SAL_CALL osl_destroySocketSet (oslSocketSet Set)
{
	if(Set)
		rtl_freeMemory(Set);
}

/*****************************************************************************/
/* osl_clearSocketSet  */
/*****************************************************************************/
void SAL_CALL osl_clearSocketSet (oslSocketSet Set)
{
	TSocketSetImpl* pSet;

	pSet= (TSocketSetImpl*)Set;

	if (pSet)
		FD_ZERO(&pSet->m_Set);
}

/*****************************************************************************/
/* osl_addToSocketSet  */
/*****************************************************************************/
void SAL_CALL osl_addToSocketSet (
	oslSocketSet Set,
	oslSocket    Socket)
{
	TSocketSetImpl* pSet;
	oslSocketImpl*  pSockImpl;

	pSet= (TSocketSetImpl*)Set;
	pSockImpl= (oslSocketImpl*)Socket;

	if (pSet && pSockImpl)
		FD_SET(pSockImpl->m_Socket, &pSet->m_Set);
}

/*****************************************************************************/
/* osl_removeFromSocketSet  */
/*****************************************************************************/
void SAL_CALL osl_removeFromSocketSet (
	oslSocketSet Set,
	oslSocket    Socket)
{
	TSocketSetImpl* pSet;
	oslSocketImpl*  pSockImpl;

	pSet= (TSocketSetImpl*)Set;
	pSockImpl= (oslSocketImpl*)Socket;

	if (pSet && pSockImpl)
		FD_CLR(pSockImpl->m_Socket, &pSet->m_Set);
}

/*****************************************************************************/
/* osl_isInSocketSet  */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isInSocketSet (
	oslSocketSet Set,
	oslSocket    Socket)
{
	TSocketSetImpl* pSet;
	oslSocketImpl*  pSockImpl;

	pSet= (TSocketSetImpl*)Set;
	pSockImpl= (oslSocketImpl*)Socket;

	if (pSet && pSockImpl)
		return (FD_ISSET(pSockImpl->m_Socket, &pSet->m_Set) != 0);
	else
		return sal_False;
}

/*****************************************************************************/
/* osl_demultiplexSocketEvents  */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_demultiplexSocketEvents (
	oslSocketSet IncomingSet,
	oslSocketSet OutgoingSet,
	oslSocketSet OutOfBandSet,
	const TimeValue* pTimeout)
{
	int             MaxHandle= 0;
	struct timeval  tv;
	TSocketSetImpl* pInSet;
	TSocketSetImpl* pOutSet;
	TSocketSetImpl* pOOBSet;

	if(pTimeout)
	{
		/* divide milliseconds into seconds and microseconds */
		tv.tv_sec  = pTimeout->Seconds;
		tv.tv_usec = pTimeout->Nanosec / 1000L;
	}

	/* map opaque data to impl-types */
	pInSet= (TSocketSetImpl*)IncomingSet;
	pOutSet= (TSocketSetImpl*)OutgoingSet;
	pOOBSet= (TSocketSetImpl*)OutOfBandSet;

	return select(MaxHandle,				/* redundant in WIN32 */
				  pInSet ? &pInSet->m_Set : 0,
				  pOutSet ? &pOutSet->m_Set : 0,
				  pOOBSet ? &pOOBSet->m_Set : 0,
				  pTimeout ? &tv : 0);
}

}
