You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
807 lines
19 KiB
807 lines
19 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Utility Library
|
|
* ----------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed 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.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Socket abstraction.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "deSocket.h"
|
|
#include "deMemory.h"
|
|
#include "deMutex.h"
|
|
#include "deInt32.h"
|
|
|
|
#if (DE_OS == DE_OS_WIN32)
|
|
# define DE_USE_WINSOCK
|
|
#elif (DE_OS == DE_OS_UNIX) || (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS) || (DE_OS == DE_OS_ANDROID) || (DE_OS == DE_OS_SYMBIAN) || (DE_OS == DE_OS_QNX)
|
|
# define DE_USE_BERKELEY_SOCKETS
|
|
#else
|
|
# error Implement deSocket for your OS.
|
|
#endif
|
|
|
|
/* Common utilities. */
|
|
|
|
const char* deGetSocketResultName (deSocketResult result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case DE_SOCKETRESULT_SUCCESS: return "DE_SOCKETRESULT_SUCCESS";
|
|
case DE_SOCKETRESULT_WOULD_BLOCK: return "DE_SOCKETRESULT_WOULD_BLOCK";
|
|
case DE_SOCKETRESULT_CONNECTION_CLOSED: return "DE_SOCKETRESULT_CONNECTION_CLOSED";
|
|
case DE_SOCKETRESULT_CONNECTION_TERMINATED: return "DE_SOCKETRESULT_CONNECTION_TERMINATED";
|
|
case DE_SOCKETRESULT_ERROR: return "DE_SOCKETRESULT_ERROR";
|
|
default: return DE_NULL;
|
|
}
|
|
}
|
|
|
|
const char* deGetSocketFamilyName (deSocketFamily family)
|
|
{
|
|
switch (family)
|
|
{
|
|
case DE_SOCKETFAMILY_INET4: return "DE_SOCKETFAMILY_INET4";
|
|
case DE_SOCKETFAMILY_INET6: return "DE_SOCKETFAMILY_INET6";
|
|
default: return DE_NULL;
|
|
}
|
|
}
|
|
|
|
#if defined(DE_USE_WINSOCK) || defined(DE_USE_BERKELEY_SOCKETS)
|
|
|
|
/* Common deSocketAddress implementation. */
|
|
|
|
struct deSocketAddress_s
|
|
{
|
|
char* host;
|
|
int port;
|
|
deSocketFamily family;
|
|
deSocketType type;
|
|
deSocketProtocol protocol;
|
|
};
|
|
|
|
deSocketAddress* deSocketAddress_create (void)
|
|
{
|
|
deSocketAddress* addr = (deSocketAddress*)deCalloc(sizeof(deSocketAddress));
|
|
if (!addr)
|
|
return addr;
|
|
|
|
/* Sane defaults. */
|
|
addr->family = DE_SOCKETFAMILY_INET4;
|
|
addr->type = DE_SOCKETTYPE_STREAM;
|
|
addr->protocol = DE_SOCKETPROTOCOL_TCP;
|
|
|
|
return addr;
|
|
}
|
|
|
|
deBool deSocketAddress_setFamily (deSocketAddress* address, deSocketFamily family)
|
|
{
|
|
address->family = family;
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deSocketFamily deSocketAddress_getFamily (const deSocketAddress* address)
|
|
{
|
|
return address->family;
|
|
}
|
|
|
|
void deSocketAddress_destroy (deSocketAddress* address)
|
|
{
|
|
deFree(address->host);
|
|
deFree(address);
|
|
}
|
|
|
|
deBool deSocketAddress_setPort (deSocketAddress* address, int port)
|
|
{
|
|
address->port = port;
|
|
return DE_TRUE;
|
|
}
|
|
|
|
int deSocketAddress_getPort (const deSocketAddress* address)
|
|
{
|
|
return address->port;
|
|
}
|
|
|
|
deBool deSocketAddress_setHost (deSocketAddress* address, const char* host)
|
|
{
|
|
if (address->host)
|
|
{
|
|
deFree(address->host);
|
|
address->host = DE_NULL;
|
|
}
|
|
|
|
address->host = deStrdup(host);
|
|
return address->host != DE_NULL;
|
|
}
|
|
|
|
const char* deSocketAddress_getHost (const deSocketAddress* address)
|
|
{
|
|
return address->host;
|
|
}
|
|
|
|
|
|
deBool deSocketAddress_setType (deSocketAddress* address, deSocketType type)
|
|
{
|
|
address->type = type;
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deSocketType deSocketAddress_getType (const deSocketAddress* address)
|
|
{
|
|
return address->type;
|
|
}
|
|
|
|
deBool deSocketAddress_setProtocol (deSocketAddress* address, deSocketProtocol protocol)
|
|
{
|
|
address->protocol = protocol;
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deSocketProtocol deSocketAddress_getProtocol (const deSocketAddress* address)
|
|
{
|
|
return address->protocol;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(DE_USE_WINSOCK)
|
|
|
|
/* WinSock spesific. */
|
|
# include <WinSock2.h>
|
|
# include <WS2tcpip.h>
|
|
# include <WinDef.h>
|
|
|
|
static deBool initWinsock (void)
|
|
{
|
|
WSADATA wsaData;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
|
|
return DE_FALSE;
|
|
|
|
return DE_TRUE;
|
|
}
|
|
|
|
#elif defined(DE_USE_BERKELEY_SOCKETS)
|
|
|
|
/* Berkeley Socket includes. */
|
|
# include <sys/socket.h>
|
|
# include <netinet/in.h>
|
|
# include <netinet/tcp.h>
|
|
# include <arpa/inet.h>
|
|
# include <netdb.h>
|
|
# include <unistd.h>
|
|
# include <fcntl.h>
|
|
# include <errno.h>
|
|
|
|
#endif
|
|
|
|
/* Socket type. */
|
|
#if defined(DE_USE_WINSOCK)
|
|
/* \note SOCKET is unsigned type! */
|
|
typedef SOCKET deSocketHandle;
|
|
typedef int NativeSocklen;
|
|
typedef int NativeSize;
|
|
# define DE_INVALID_SOCKET_HANDLE INVALID_SOCKET
|
|
#else
|
|
typedef int deSocketHandle;
|
|
typedef socklen_t NativeSocklen;
|
|
typedef size_t NativeSize;
|
|
# define DE_INVALID_SOCKET_HANDLE (-1)
|
|
#endif
|
|
|
|
DE_INLINE deBool deSocketHandleIsValid (deSocketHandle handle)
|
|
{
|
|
return handle != DE_INVALID_SOCKET_HANDLE;
|
|
}
|
|
|
|
#if defined(DE_USE_WINSOCK) || defined(DE_USE_BERKELEY_SOCKETS)
|
|
|
|
/* Shared berkeley and winsock implementation. */
|
|
|
|
struct deSocket_s
|
|
{
|
|
deSocketHandle handle;
|
|
|
|
deMutex stateLock;
|
|
volatile deSocketState state;
|
|
volatile deUint32 openChannels;
|
|
};
|
|
|
|
/* Common socket functions. */
|
|
|
|
static deUint16 deHostToNetworkOrder16 (deUint16 v)
|
|
{
|
|
#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
|
|
return deReverseBytes16(v);
|
|
#else
|
|
return v;
|
|
#endif
|
|
}
|
|
|
|
static deUint16 deNetworkToHostOrder16 (deUint16 v)
|
|
{
|
|
#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
|
|
return deReverseBytes16(v);
|
|
#else
|
|
return v;
|
|
#endif
|
|
}
|
|
|
|
DE_STATIC_ASSERT(sizeof(((struct sockaddr_in*)DE_NULL)->sin_port) == sizeof(deUint16));
|
|
DE_STATIC_ASSERT(sizeof(((struct sockaddr_in6*)DE_NULL)->sin6_port) == sizeof(deUint16));
|
|
|
|
static int deSocketFamilyToBsdFamily (deSocketFamily family)
|
|
{
|
|
switch (family)
|
|
{
|
|
case DE_SOCKETFAMILY_INET4: return AF_INET;
|
|
case DE_SOCKETFAMILY_INET6: return AF_INET6;
|
|
default:
|
|
DE_ASSERT(DE_FALSE);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int deSocketTypeToBsdType (deSocketType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case DE_SOCKETTYPE_STREAM: return SOCK_STREAM;
|
|
case DE_SOCKETTYPE_DATAGRAM: return SOCK_DGRAM;
|
|
default:
|
|
DE_ASSERT(DE_FALSE);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int deSocketProtocolToBsdProtocol (deSocketProtocol protocol)
|
|
{
|
|
switch (protocol)
|
|
{
|
|
case DE_SOCKETPROTOCOL_TCP: return IPPROTO_TCP;
|
|
case DE_SOCKETPROTOCOL_UDP: return IPPROTO_UDP;
|
|
default:
|
|
DE_ASSERT(DE_FALSE);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static deBool deSocketAddressToBsdAddress (const deSocketAddress* address, size_t bsdAddrBufSize, struct sockaddr* bsdAddr, NativeSocklen* bsdAddrLen)
|
|
{
|
|
deMemset(bsdAddr, 0, bsdAddrBufSize);
|
|
|
|
/* Resolve host. */
|
|
if (address->host != DE_NULL)
|
|
{
|
|
struct addrinfo* result = DE_NULL;
|
|
struct addrinfo hints;
|
|
|
|
deMemset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = deSocketFamilyToBsdFamily(address->family);
|
|
hints.ai_socktype = deSocketTypeToBsdType(address->type);
|
|
hints.ai_protocol = deSocketProtocolToBsdProtocol(address->protocol);
|
|
|
|
if (getaddrinfo(address->host, DE_NULL, &hints, &result) != 0 || !result)
|
|
{
|
|
if (result)
|
|
freeaddrinfo(result);
|
|
return DE_FALSE;
|
|
}
|
|
|
|
/* \note Always uses first address. */
|
|
|
|
if (bsdAddrBufSize < (size_t)result->ai_addrlen)
|
|
{
|
|
DE_FATAL("Too small bsdAddr buffer");
|
|
freeaddrinfo(result);
|
|
return DE_FALSE;
|
|
}
|
|
|
|
*bsdAddrLen = (NativeSocklen)result->ai_addrlen;
|
|
|
|
deMemcpy(bsdAddr, result->ai_addr, (size_t)result->ai_addrlen);
|
|
freeaddrinfo(result);
|
|
|
|
/* Add port. */
|
|
if (bsdAddr->sa_family == AF_INET)
|
|
{
|
|
if (*bsdAddrLen < (NativeSocklen)sizeof(struct sockaddr_in))
|
|
return DE_FALSE;
|
|
((struct sockaddr_in*)bsdAddr)->sin_port = deHostToNetworkOrder16((deUint16)address->port);
|
|
}
|
|
else if (bsdAddr->sa_family == AF_INET6)
|
|
{
|
|
if (*bsdAddrLen < (NativeSocklen)sizeof(struct sockaddr_in6))
|
|
return DE_FALSE;
|
|
((struct sockaddr_in6*)bsdAddr)->sin6_port = deHostToNetworkOrder16((deUint16)address->port);
|
|
}
|
|
else
|
|
return DE_FALSE;
|
|
|
|
return DE_TRUE;
|
|
}
|
|
else if (address->family == DE_SOCKETFAMILY_INET4)
|
|
{
|
|
struct sockaddr_in* addr4 = (struct sockaddr_in*)bsdAddr;
|
|
|
|
if (bsdAddrBufSize < sizeof(struct sockaddr_in))
|
|
{
|
|
DE_FATAL("Too small bsdAddr buffer");
|
|
return DE_FALSE;
|
|
}
|
|
|
|
addr4->sin_port = deHostToNetworkOrder16((deUint16)address->port);
|
|
addr4->sin_family = AF_INET;
|
|
addr4->sin_addr.s_addr = INADDR_ANY;
|
|
|
|
*bsdAddrLen = (NativeSocklen)sizeof(struct sockaddr_in);
|
|
|
|
return DE_TRUE;
|
|
}
|
|
else if (address->family == DE_SOCKETFAMILY_INET6)
|
|
{
|
|
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)bsdAddr;
|
|
|
|
if (bsdAddrBufSize < sizeof(struct sockaddr_in6))
|
|
{
|
|
DE_FATAL("Too small bsdAddr buffer");
|
|
return DE_FALSE;
|
|
}
|
|
|
|
addr6->sin6_port = deHostToNetworkOrder16((deUint16)address->port);
|
|
addr6->sin6_family = AF_INET6;
|
|
|
|
*bsdAddrLen = (NativeSocklen)sizeof(struct sockaddr_in6);
|
|
|
|
return DE_TRUE;
|
|
}
|
|
else
|
|
return DE_FALSE;
|
|
}
|
|
|
|
void deBsdAddressToSocketAddress (deSocketAddress* address, const struct sockaddr* bsdAddr, int addrLen)
|
|
{
|
|
/* Decode client address info. */
|
|
if (bsdAddr->sa_family == AF_INET)
|
|
{
|
|
const struct sockaddr_in* addr4 = (const struct sockaddr_in*)bsdAddr;
|
|
DE_ASSERT(addrLen >= (int)sizeof(struct sockaddr_in));
|
|
DE_UNREF(addrLen);
|
|
|
|
deSocketAddress_setFamily(address, DE_SOCKETFAMILY_INET4);
|
|
deSocketAddress_setPort(address, (int)deNetworkToHostOrder16((deUint16)addr4->sin_port));
|
|
|
|
{
|
|
char buf[16]; /* Max valid address takes 3*4 + 3 = 15 chars */
|
|
inet_ntop(AF_INET, (void*)&addr4->sin_addr, buf, sizeof(buf));
|
|
deSocketAddress_setHost(address, buf);
|
|
}
|
|
}
|
|
else if (bsdAddr->sa_family == AF_INET6)
|
|
{
|
|
const struct sockaddr_in6* addr6 = (const struct sockaddr_in6*)bsdAddr;
|
|
DE_ASSERT(addrLen >= (int)sizeof(struct sockaddr_in6));
|
|
DE_UNREF(addrLen);
|
|
|
|
deSocketAddress_setFamily(address, DE_SOCKETFAMILY_INET6);
|
|
deSocketAddress_setPort(address, (int)deNetworkToHostOrder16((deUint16)addr6->sin6_port));
|
|
|
|
{
|
|
char buf[40]; /* Max valid address takes 8*4 + 7 = 39 chars */
|
|
inet_ntop(AF_INET6, (void*)&addr6->sin6_addr, buf, sizeof(buf));
|
|
deSocketAddress_setHost(address, buf);
|
|
}
|
|
}
|
|
else
|
|
DE_ASSERT(DE_FALSE);
|
|
}
|
|
|
|
deSocket* deSocket_create (void)
|
|
{
|
|
deSocket* sock = (deSocket*)deCalloc(sizeof(deSocket));
|
|
if (!sock)
|
|
return sock;
|
|
|
|
#if defined(DE_USE_WINSOCK)
|
|
/* Make sure WSA is up. */
|
|
if (!initWinsock())
|
|
return DE_NULL;
|
|
#endif
|
|
|
|
sock->stateLock = deMutex_create(0);
|
|
sock->handle = DE_INVALID_SOCKET_HANDLE;
|
|
sock->state = DE_SOCKETSTATE_CLOSED;
|
|
|
|
return sock;
|
|
}
|
|
|
|
void deSocket_destroy (deSocket* sock)
|
|
{
|
|
if (sock->state != DE_SOCKETSTATE_CLOSED)
|
|
deSocket_close(sock);
|
|
|
|
deMutex_destroy(sock->stateLock);
|
|
deFree(sock);
|
|
}
|
|
|
|
deSocketState deSocket_getState (const deSocket* sock)
|
|
{
|
|
return sock->state;
|
|
}
|
|
|
|
deUint32 deSocket_getOpenChannels (const deSocket* sock)
|
|
{
|
|
return sock->openChannels;
|
|
}
|
|
|
|
deBool deSocket_setFlags (deSocket* sock, deUint32 flags)
|
|
{
|
|
deSocketHandle fd = sock->handle;
|
|
|
|
if (sock->state == DE_SOCKETSTATE_CLOSED)
|
|
return DE_FALSE;
|
|
|
|
/* Keepalive. */
|
|
{
|
|
int mode = (flags & DE_SOCKET_KEEPALIVE) ? 1 : 0;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&mode, sizeof(mode)) != 0)
|
|
return DE_FALSE;
|
|
}
|
|
|
|
/* Nodelay. */
|
|
{
|
|
int mode = (flags & DE_SOCKET_NODELAY) ? 1 : 0;
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&mode, sizeof(mode)) != 0)
|
|
return DE_FALSE;
|
|
}
|
|
|
|
/* Non-blocking. */
|
|
{
|
|
#if defined(DE_USE_WINSOCK)
|
|
u_long mode = (flags & DE_SOCKET_NONBLOCKING) ? 1 : 0;
|
|
if (ioctlsocket(fd, FIONBIO, &mode) != 0)
|
|
return DE_FALSE;
|
|
#else
|
|
int oldFlags = fcntl(fd, F_GETFL, 0);
|
|
int newFlags = (flags & DE_SOCKET_NONBLOCKING) ? (oldFlags | O_NONBLOCK) : (oldFlags & ~O_NONBLOCK);
|
|
if (fcntl(fd, F_SETFL, newFlags) != 0)
|
|
return DE_FALSE;
|
|
#endif
|
|
}
|
|
|
|
/* Close on exec. */
|
|
{
|
|
#if defined(DE_USE_BERKELEY_SOCKETS)
|
|
int oldFlags = fcntl(fd, F_GETFD, 0);
|
|
int newFlags = (flags & DE_SOCKET_CLOSE_ON_EXEC) ? (oldFlags | FD_CLOEXEC) : (oldFlags & ~FD_CLOEXEC);
|
|
if (fcntl(fd, F_SETFD, newFlags) != 0)
|
|
return DE_FALSE;
|
|
#endif
|
|
}
|
|
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deBool deSocket_listen (deSocket* sock, const deSocketAddress* address)
|
|
{
|
|
const int backlogSize = 4;
|
|
deUint8 bsdAddrBuf[sizeof(struct sockaddr_in6)];
|
|
struct sockaddr* bsdAddr = (struct sockaddr*)&bsdAddrBuf[0];
|
|
NativeSocklen bsdAddrLen;
|
|
|
|
if (sock->state != DE_SOCKETSTATE_CLOSED)
|
|
return DE_FALSE;
|
|
|
|
/* Resolve address. */
|
|
if (!deSocketAddressToBsdAddress(address, sizeof(bsdAddrBuf), bsdAddr, &bsdAddrLen))
|
|
return DE_FALSE;
|
|
|
|
/* Create socket. */
|
|
sock->handle = socket(bsdAddr->sa_family, deSocketTypeToBsdType(address->type), deSocketProtocolToBsdProtocol(address->protocol));
|
|
if (!deSocketHandleIsValid(sock->handle))
|
|
return DE_FALSE;
|
|
|
|
sock->state = DE_SOCKETSTATE_DISCONNECTED;
|
|
|
|
/* Allow re-using address. */
|
|
{
|
|
int reuseVal = 1;
|
|
setsockopt(sock->handle, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseVal, (int)sizeof(reuseVal));
|
|
}
|
|
|
|
/* Bind to address. */
|
|
if (bind(sock->handle, bsdAddr, (NativeSocklen)bsdAddrLen) != 0)
|
|
{
|
|
deSocket_close(sock);
|
|
return DE_FALSE;
|
|
}
|
|
|
|
/* Start listening. */
|
|
if (listen(sock->handle, backlogSize) != 0)
|
|
{
|
|
deSocket_close(sock);
|
|
return DE_FALSE;
|
|
}
|
|
|
|
sock->state = DE_SOCKETSTATE_LISTENING;
|
|
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deSocket* deSocket_accept (deSocket* sock, deSocketAddress* clientAddress)
|
|
{
|
|
deSocketHandle newFd = DE_INVALID_SOCKET_HANDLE;
|
|
deSocket* newSock = DE_NULL;
|
|
deUint8 bsdAddrBuf[sizeof(struct sockaddr_in6)];
|
|
struct sockaddr* bsdAddr = (struct sockaddr*)&bsdAddrBuf[0];
|
|
NativeSocklen bsdAddrLen = (NativeSocklen)sizeof(bsdAddrBuf);
|
|
|
|
deMemset(bsdAddr, 0, (size_t)bsdAddrLen);
|
|
|
|
newFd = accept(sock->handle, bsdAddr, &bsdAddrLen);
|
|
if (!deSocketHandleIsValid(newFd))
|
|
return DE_NULL;
|
|
|
|
newSock = (deSocket*)deCalloc(sizeof(deSocket));
|
|
if (!newSock)
|
|
{
|
|
#if defined(DE_USE_WINSOCK)
|
|
closesocket(newFd);
|
|
#else
|
|
close(newFd);
|
|
#endif
|
|
return DE_NULL;
|
|
}
|
|
|
|
newSock->stateLock = deMutex_create(0);
|
|
newSock->handle = newFd;
|
|
newSock->state = DE_SOCKETSTATE_CONNECTED;
|
|
newSock->openChannels = DE_SOCKETCHANNEL_BOTH;
|
|
|
|
if (clientAddress)
|
|
deBsdAddressToSocketAddress(clientAddress, bsdAddr, (int)bsdAddrLen);
|
|
|
|
return newSock;
|
|
}
|
|
|
|
deBool deSocket_connect (deSocket* sock, const deSocketAddress* address)
|
|
{
|
|
deUint8 bsdAddrBuf[sizeof(struct sockaddr_in6)];
|
|
struct sockaddr* bsdAddr = (struct sockaddr*)&bsdAddrBuf[0];
|
|
NativeSocklen bsdAddrLen;
|
|
|
|
/* Resolve address. */
|
|
if (!deSocketAddressToBsdAddress(address, sizeof(bsdAddrBuf), bsdAddr, &bsdAddrLen))
|
|
return DE_FALSE;
|
|
|
|
/* Create socket. */
|
|
sock->handle = socket(bsdAddr->sa_family, deSocketTypeToBsdType(address->type), deSocketProtocolToBsdProtocol(address->protocol));
|
|
if (!deSocketHandleIsValid(sock->handle))
|
|
return DE_FALSE;
|
|
|
|
/* Connect. */
|
|
if (connect(sock->handle, bsdAddr, bsdAddrLen) != 0)
|
|
{
|
|
#if defined(DE_USE_WINSOCK)
|
|
closesocket(sock->handle);
|
|
#else
|
|
close(sock->handle);
|
|
#endif
|
|
sock->handle = DE_INVALID_SOCKET_HANDLE;
|
|
return DE_FALSE;
|
|
}
|
|
|
|
sock->state = DE_SOCKETSTATE_CONNECTED;
|
|
sock->openChannels = DE_SOCKETCHANNEL_BOTH;
|
|
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deBool deSocket_shutdown (deSocket* sock, deUint32 channels)
|
|
{
|
|
deUint32 closedChannels = 0;
|
|
|
|
deMutex_lock(sock->stateLock);
|
|
|
|
if (sock->state == DE_SOCKETSTATE_DISCONNECTED ||
|
|
sock->state == DE_SOCKETSTATE_CLOSED)
|
|
{
|
|
deMutex_unlock(sock->stateLock);
|
|
return DE_FALSE;
|
|
}
|
|
|
|
DE_ASSERT(channels != 0 && (channels & ~(deUint32)DE_SOCKETCHANNEL_BOTH) == 0);
|
|
|
|
/* Don't attempt to close already closed channels on partially open socket. */
|
|
channels &= sock->openChannels;
|
|
|
|
if (channels == 0)
|
|
{
|
|
deMutex_unlock(sock->stateLock);
|
|
return DE_FALSE;
|
|
}
|
|
|
|
#if defined(DE_USE_WINSOCK)
|
|
{
|
|
int how = 0;
|
|
|
|
if ((channels & DE_SOCKETCHANNEL_BOTH) == DE_SOCKETCHANNEL_BOTH)
|
|
how = SD_BOTH;
|
|
else if (channels & DE_SOCKETCHANNEL_SEND)
|
|
how = SD_SEND;
|
|
else if (channels & DE_SOCKETCHANNEL_RECEIVE)
|
|
how = SD_RECEIVE;
|
|
|
|
if (shutdown(sock->handle, how) == 0)
|
|
closedChannels = channels;
|
|
else
|
|
{
|
|
int err = WSAGetLastError();
|
|
|
|
/* \note Due to asynchronous behavior certain errors are perfectly ok. */
|
|
if (err == WSAECONNABORTED || err == WSAECONNRESET || err == WSAENOTCONN)
|
|
closedChannels = DE_SOCKETCHANNEL_BOTH;
|
|
else
|
|
{
|
|
deMutex_unlock(sock->stateLock);
|
|
return DE_FALSE;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
int how = 0;
|
|
|
|
if ((channels & DE_SOCKETCHANNEL_BOTH) == DE_SOCKETCHANNEL_BOTH)
|
|
how = SHUT_RDWR;
|
|
else if (channels & DE_SOCKETCHANNEL_SEND)
|
|
how = SHUT_WR;
|
|
else if (channels & DE_SOCKETCHANNEL_RECEIVE)
|
|
how = SHUT_RD;
|
|
|
|
if (shutdown(sock->handle, how) == 0)
|
|
closedChannels = channels;
|
|
else
|
|
{
|
|
if (errno == ENOTCONN)
|
|
closedChannels = DE_SOCKETCHANNEL_BOTH;
|
|
else
|
|
{
|
|
deMutex_unlock(sock->stateLock);
|
|
return DE_FALSE;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
sock->openChannels &= ~closedChannels;
|
|
if (sock->openChannels == 0)
|
|
sock->state = DE_SOCKETSTATE_DISCONNECTED;
|
|
|
|
deMutex_unlock(sock->stateLock);
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deBool deSocket_close (deSocket* sock)
|
|
{
|
|
deMutex_lock(sock->stateLock);
|
|
|
|
if (sock->state == DE_SOCKETSTATE_CLOSED)
|
|
{
|
|
deMutex_unlock(sock->stateLock);
|
|
return DE_FALSE;
|
|
}
|
|
|
|
#if !defined(DE_USE_WINSOCK)
|
|
if (sock->state == DE_SOCKETSTATE_LISTENING)
|
|
{
|
|
/* There can be a thread blockin in accept(). Release it by calling shutdown. */
|
|
shutdown(sock->handle, SHUT_RDWR);
|
|
}
|
|
#endif
|
|
|
|
#if defined(DE_USE_WINSOCK)
|
|
if (closesocket(sock->handle) != 0)
|
|
return DE_FALSE;
|
|
#else
|
|
if (close(sock->handle) != 0)
|
|
return DE_FALSE;
|
|
#endif
|
|
sock->state = DE_SOCKETSTATE_CLOSED;
|
|
sock->handle = DE_INVALID_SOCKET_HANDLE;
|
|
sock->openChannels = 0;
|
|
|
|
deMutex_unlock(sock->stateLock);
|
|
return DE_TRUE;
|
|
}
|
|
|
|
static deSocketResult mapSendRecvResult (int numBytes)
|
|
{
|
|
if (numBytes > 0)
|
|
return DE_SOCKETRESULT_SUCCESS;
|
|
else if (numBytes == 0)
|
|
return DE_SOCKETRESULT_CONNECTION_CLOSED;
|
|
else
|
|
{
|
|
/* Other errors. */
|
|
#if defined(DE_USE_WINSOCK)
|
|
int error = WSAGetLastError();
|
|
switch (error)
|
|
{
|
|
case WSAEWOULDBLOCK: return DE_SOCKETRESULT_WOULD_BLOCK;
|
|
case WSAENETDOWN:
|
|
case WSAENETRESET:
|
|
case WSAECONNABORTED:
|
|
case WSAECONNRESET: return DE_SOCKETRESULT_CONNECTION_TERMINATED;
|
|
default: return DE_SOCKETRESULT_ERROR;
|
|
}
|
|
#else
|
|
switch (errno)
|
|
{
|
|
case EAGAIN: return DE_SOCKETRESULT_WOULD_BLOCK;
|
|
case ECONNABORTED:
|
|
case ECONNRESET: return DE_SOCKETRESULT_CONNECTION_TERMINATED;
|
|
default: return DE_SOCKETRESULT_ERROR;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
DE_INLINE void deSocket_setChannelsClosed (deSocket* sock, deUint32 channels)
|
|
{
|
|
deMutex_lock(sock->stateLock);
|
|
|
|
sock->openChannels &= ~channels;
|
|
if (sock->openChannels == 0)
|
|
sock->state = DE_SOCKETSTATE_DISCONNECTED;
|
|
|
|
deMutex_unlock(sock->stateLock);
|
|
}
|
|
|
|
deSocketResult deSocket_send (deSocket* sock, const void* buf, size_t bufSize, size_t* numSentPtr)
|
|
{
|
|
int numSent = (int)send(sock->handle, (const char*)buf, (NativeSize)bufSize, 0);
|
|
deSocketResult result = mapSendRecvResult(numSent);
|
|
|
|
if (numSentPtr)
|
|
*numSentPtr = (numSent > 0) ? ((size_t)numSent) : (0);
|
|
|
|
/* Update state. */
|
|
if (result == DE_SOCKETRESULT_CONNECTION_CLOSED)
|
|
deSocket_setChannelsClosed(sock, DE_SOCKETCHANNEL_SEND);
|
|
else if (result == DE_SOCKETRESULT_CONNECTION_TERMINATED)
|
|
deSocket_setChannelsClosed(sock, DE_SOCKETCHANNEL_BOTH);
|
|
|
|
return result;
|
|
}
|
|
|
|
deSocketResult deSocket_receive (deSocket* sock, void* buf, size_t bufSize, size_t* numReceivedPtr)
|
|
{
|
|
int numRecv = (int)recv(sock->handle, (char*)buf, (NativeSize)bufSize, 0);
|
|
deSocketResult result = mapSendRecvResult(numRecv);
|
|
|
|
if (numReceivedPtr)
|
|
*numReceivedPtr = (numRecv > 0) ? ((size_t)numRecv) : (0);
|
|
|
|
/* Update state. */
|
|
if (result == DE_SOCKETRESULT_CONNECTION_CLOSED)
|
|
deSocket_setChannelsClosed(sock, DE_SOCKETCHANNEL_RECEIVE);
|
|
else if (result == DE_SOCKETRESULT_CONNECTION_TERMINATED)
|
|
deSocket_setChannelsClosed(sock, DE_SOCKETCHANNEL_BOTH);
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|