1609 lines
41 KiB
C++
1609 lines
41 KiB
C++
/**
|
|
* @file kSocket.cpp
|
|
*
|
|
* @internal
|
|
* Copyright (C) 2004-2021 by LMI Technologies Inc.
|
|
* Licensed under the MIT License.
|
|
* Redistributed files must retain the above copyright notice.
|
|
*/
|
|
#define K_PLATFORM
|
|
#include <kApi/Io/kSocket.h>
|
|
|
|
/*
|
|
* kSocketType
|
|
*/
|
|
kBeginEnumEx(k, kSocketType)
|
|
kAddEnumerator(kSocketType, kSOCKET_TYPE_TCP)
|
|
kAddEnumerator(kSocketType, kSOCKET_TYPE_UDP)
|
|
kEndEnumEx()
|
|
|
|
/*
|
|
* kSocketWait
|
|
*/
|
|
kBeginEnumEx(k, kSocketEvent)
|
|
kAddEnumerator(kSocketEvent, kSOCKET_EVENT_READ)
|
|
kAddEnumerator(kSocketEvent, kSOCKET_EVENT_WRITE)
|
|
kAddEnumerator(kSocketEvent, kSOCKET_EVENT_EXCEPT)
|
|
kEndEnumEx()
|
|
|
|
/*
|
|
* kSocket
|
|
*/
|
|
|
|
kBeginClassEx(k, kSocket)
|
|
kAddPrivateVMethod(kSocket, kObject, VRelease)
|
|
kEndClassEx()
|
|
|
|
kFx(kStatus) kSocket_WaitAny(const kSocket* sockts, kSize count, k64u timeout)
|
|
{
|
|
struct timeval tv;
|
|
fd_set readSet, writeSet, exceptSet;
|
|
xkSocketHandle highest = 0;
|
|
int result;
|
|
kSize i;
|
|
|
|
kCheckArgs(count <= FD_SETSIZE);
|
|
|
|
FD_ZERO(&readSet);
|
|
FD_ZERO(&writeSet);
|
|
FD_ZERO(&exceptSet);
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
kObj(kSocket, sockts[i]);
|
|
|
|
if (obj->handle > highest)
|
|
{
|
|
highest = obj->handle;
|
|
}
|
|
|
|
if (obj->eventTypes & kSOCKET_EVENT_READ) FD_SET(obj->handle, &readSet);
|
|
if (obj->eventTypes & kSOCKET_EVENT_WRITE) FD_SET(obj->handle, &writeSet);
|
|
if (obj->eventTypes & kSOCKET_EVENT_EXCEPT) FD_SET(obj->handle, &exceptSet);
|
|
|
|
obj->eventStatus = 0;
|
|
}
|
|
|
|
result = xkSocket_Select((int) (highest) + 1, &readSet, &writeSet, &exceptSet, xkSocket_FormatTimeVal(&tv, timeout));
|
|
|
|
if (result == 0)
|
|
{
|
|
return kERROR_TIMEOUT;
|
|
}
|
|
else if (result < 0)
|
|
{
|
|
return kERROR_STREAM;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
kObj(kSocket, sockts[i]);
|
|
|
|
if ((obj->eventTypes & kSOCKET_EVENT_READ) && FD_ISSET(obj->handle, &readSet)) obj->eventStatus |= kSOCKET_EVENT_READ;
|
|
if ((obj->eventTypes & kSOCKET_EVENT_WRITE) && FD_ISSET(obj->handle, &writeSet)) obj->eventStatus |= kSOCKET_EVENT_WRITE;
|
|
if ((obj->eventTypes & kSOCKET_EVENT_EXCEPT) && FD_ISSET(obj->handle, &exceptSet)) obj->eventStatus |= kSOCKET_EVENT_EXCEPT;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(struct timeval*) xkSocket_FormatTimeVal(struct timeval* tv, k64u timeout)
|
|
{
|
|
if (timeout == kINFINITE)
|
|
{
|
|
return kNULL;
|
|
}
|
|
|
|
tv->tv_sec = (long) timeout / 1000000;
|
|
tv->tv_usec = (int) (timeout % 1000000);
|
|
|
|
return tv;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Construct(kSocket* sockt, kIpVersion ipVersion, kSocketType socketType, kAlloc allocator)
|
|
{
|
|
kAlloc alloc = kAlloc_Fallback(allocator);
|
|
kType type = kTypeOf(kSocket);
|
|
kStatus status;
|
|
|
|
kCheck(kAlloc_GetObject(alloc, type, sockt));
|
|
|
|
if (!kSuccess(status = xkSocket_Init(*sockt, type, ipVersion, socketType, alloc)))
|
|
{
|
|
kAlloc_FreeRef(alloc, sockt);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_ConstructFromHandle(kSocket* sockt, kIpVersion ipVersion, kSocketType socktType, xkSocketHandle handle, kAlloc allocator)
|
|
{
|
|
kAlloc alloc = kAlloc_Fallback(allocator);
|
|
kType type = kTypeOf(kSocket);
|
|
kStatus status;
|
|
|
|
kCheck(kAlloc_GetObject(alloc, type, sockt));
|
|
|
|
if (!kSuccess(status = xkSocket_InitFromHandle(*sockt, type, ipVersion, socktType, handle, alloc)))
|
|
{
|
|
kAlloc_FreeRef(alloc, sockt);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_Init(kSocket sockt, kType type, kIpVersion ipVersion, kSocketType socketType, kAlloc allocator)
|
|
{
|
|
if (ipVersion != kIP_VERSION_4)
|
|
{
|
|
return kERROR_UNIMPLEMENTED;
|
|
}
|
|
else
|
|
{
|
|
k32s sockFamily = AF_INET;
|
|
k32s sockType = (socketType == kSOCKET_TYPE_TCP) ? SOCK_STREAM : SOCK_DGRAM;
|
|
xkSocketHandle handle = socket(sockFamily, sockType, 0);
|
|
kStatus status = kOK;
|
|
|
|
if (handle == xkSOCKET_INVALID_SOCKET)
|
|
{
|
|
status = kERROR_NETWORK;
|
|
}
|
|
else if (!kSuccess(status = xkSocket_InitFromHandle(sockt, type, ipVersion, socketType, handle, allocator)))
|
|
{
|
|
xkSocket_CloseHandle(handle);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_InitFromHandle(kSocket sockt, kType type, kIpVersion ipVersion, kSocketType socktType, xkSocketHandle handle, kAlloc allocator)
|
|
{
|
|
kObjR(kSocket, sockt);
|
|
|
|
kCheckArgs(handle != xkSOCKET_INVALID_SOCKET);
|
|
|
|
kCheck(kObject_Init(sockt, type, allocator));
|
|
|
|
obj->ipVersion = ipVersion;
|
|
obj->socketType = socktType;
|
|
obj->handle = handle;
|
|
obj->eventTypes = kSOCKET_EVENT_READ;
|
|
obj->eventStatus = 0;
|
|
obj->status = kOK;
|
|
obj->isBlocking = kTRUE;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_VRelease(kSocket sockt)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
if (obj->handle != xkSOCKET_INVALID_SOCKET)
|
|
{
|
|
kSocket_Shutdown(sockt);
|
|
|
|
kCheck(xkSocket_CloseHandle(obj->handle));
|
|
}
|
|
|
|
kCheck(kObject_VRelease(sockt));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Bind(kSocket sockt, kIpAddress address, k32u port)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct sockaddr_in sockAddr;
|
|
|
|
kCheck(obj->status);
|
|
kCheckArgs(address.version == kIP_VERSION_4);
|
|
|
|
kCheck(xkIpAddress_ToSockAddr(address, port, &sockAddr));
|
|
kCheck(bind(obj->handle, (struct sockaddr*)&sockAddr, sizeof(sockAddr)) == 0);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Connect(kSocket sockt, kIpAddress address, k32u port, k64u timeout)
|
|
{
|
|
kCheck(kSocket_BeginConnect(sockt, address, port));
|
|
kCheck(kSocket_EndConnect(sockt, timeout));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_BeginConnect(kSocket sockt, kIpAddress address, k32u port)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct sockaddr_in sockAddr;
|
|
kStatus exception;
|
|
|
|
kTry
|
|
{
|
|
kTest(obj->status);
|
|
kTestArgs(address.version == kIP_VERSION_4);
|
|
|
|
kTest(xkIpAddress_ToSockAddr(address, port, &sockAddr));
|
|
|
|
obj->wasBlocking = obj->isBlocking;
|
|
kTest(kSocket_SetBlocking(sockt, kFALSE));
|
|
|
|
obj->status = kERROR_IN_PROGRESS;
|
|
obj->beginConnectResult = connect(obj->handle, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
|
|
obj->beginConnectLastError = xkSocket_GetLastError();
|
|
}
|
|
kCatch(&exception)
|
|
{
|
|
obj->status = exception;
|
|
|
|
kEndCatch(kOK);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_EndConnect(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
fd_set exceptSet;
|
|
fd_set writeSet;
|
|
int selectResult = 0;
|
|
struct timeval tv;
|
|
kStatus exception;
|
|
|
|
kTry
|
|
{
|
|
if (obj->status != kERROR_IN_PROGRESS)
|
|
{
|
|
kThrow(obj->status);
|
|
}
|
|
|
|
obj->status = kOK;
|
|
|
|
if (obj->beginConnectResult == xkSOCKET_ERROR)
|
|
{
|
|
if (obj->beginConnectLastError == xkSOCKET_WOULD_BLOCK || obj->beginConnectLastError == xkSOCKET_IN_PROGRESS)
|
|
{
|
|
FD_ZERO(&writeSet);
|
|
FD_ZERO(&exceptSet);
|
|
|
|
FD_SET(obj->handle, &writeSet);
|
|
FD_SET(obj->handle, &exceptSet);
|
|
|
|
selectResult = xkSocket_Select((int)(obj->handle)+1, NULL, &writeSet, &exceptSet, xkSocket_FormatTimeVal(&tv, timeout));
|
|
|
|
if ((selectResult == 1) && !FD_ISSET(obj->handle, &exceptSet))
|
|
{
|
|
int errorCode;
|
|
socklen_t errorSize = sizeof(int);
|
|
|
|
kTest(getsockopt(obj->handle, SOL_SOCKET, SO_ERROR, (char*)&errorCode, &errorSize) == 0);
|
|
|
|
if (errorCode != 0)
|
|
{
|
|
kThrow(kERROR_NETWORK);
|
|
}
|
|
}
|
|
else if (selectResult == 0)
|
|
{
|
|
kThrow(kERROR_TIMEOUT);
|
|
}
|
|
else
|
|
{
|
|
kThrow(kERROR_NETWORK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kThrow(kERROR_NETWORK);
|
|
}
|
|
}
|
|
|
|
if (obj->wasBlocking)
|
|
{
|
|
kTest(kSocket_SetBlocking(sockt, obj->wasBlocking));
|
|
}
|
|
}
|
|
kCatch(&exception)
|
|
{
|
|
obj->status = exception;
|
|
kEndCatch(exception);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Accept(kSocket sockt, kSocket* connection, kAlloc allocator)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
xkSocketHandle handle = xkSOCKET_INVALID_SOCKET;
|
|
kStatus status = kOK;
|
|
|
|
kCheck(obj->status);
|
|
|
|
if ((handle = accept(obj->handle, 0, 0)) == xkSOCKET_INVALID_SOCKET)
|
|
{
|
|
*connection = kNULL;
|
|
}
|
|
else if (!kSuccess(status = xkSocket_ConstructFromHandle(connection, obj->ipVersion, kSOCKET_TYPE_TCP, handle, allocator)))
|
|
{
|
|
xkSocket_CloseHandle(handle);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_JoinMulticastGroup(kSocket socket, kIpAddress group, kIpAddress iface)
|
|
{
|
|
kObj(kSocket, socket);
|
|
struct ip_mreq request;
|
|
|
|
request.imr_multiaddr.s_addr = kIpAddress_ToNet32u(group);
|
|
request.imr_interface.s_addr = kIpAddress_ToNet32u(iface);
|
|
|
|
kCheck(setsockopt(obj->handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &request, sizeof(request)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_LeaveMulticastGroup(kSocket socket, kIpAddress group, kIpAddress iface)
|
|
{
|
|
kObj(kSocket, socket);
|
|
struct ip_mreq request;
|
|
|
|
request.imr_multiaddr.s_addr = kIpAddress_ToNet32u(group);
|
|
request.imr_interface.s_addr = kIpAddress_ToNet32u(iface);
|
|
|
|
kCheck(setsockopt(obj->handle, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*) &request, sizeof(request)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Listen(kSocket sockt, kSize backlog)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(listen(obj->handle, (int) backlog) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Shutdown(kSocket sockt)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
if (!kIsError(obj->status))
|
|
{
|
|
xkSocket_ShutdownHandle(obj->handle);
|
|
|
|
obj->status = kERROR_CLOSED;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetEvents(kSocket sockt, kSocketEvent events)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
obj->eventTypes = events;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kSocketEvent) kSocket_Events(kSocket sockt)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return obj->eventStatus;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Wait(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return xkSocket_WaitEx(sockt, obj->eventTypes, timeout);
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_WaitEx(kSocket sockt, kSocketEvent events, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct timeval tv;
|
|
fd_set readSet, writeSet, exceptSet;
|
|
kStatus result;
|
|
|
|
kCheck(obj->status);
|
|
|
|
FD_ZERO(&readSet);
|
|
FD_ZERO(&writeSet);
|
|
FD_ZERO(&exceptSet);
|
|
|
|
if (events & kSOCKET_EVENT_READ) FD_SET(obj->handle, &readSet);
|
|
if (events & kSOCKET_EVENT_WRITE) FD_SET(obj->handle, &writeSet);
|
|
if (events & kSOCKET_EVENT_EXCEPT) FD_SET(obj->handle, &exceptSet);
|
|
|
|
obj->eventStatus = 0;
|
|
|
|
result = xkSocket_Select((int)(obj->handle)+1, &readSet, &writeSet, &exceptSet, xkSocket_FormatTimeVal(&tv, timeout));
|
|
|
|
if (result == 0)
|
|
{
|
|
return kERROR_TIMEOUT;
|
|
}
|
|
else if (result < 0)
|
|
{
|
|
obj->status = kERROR_STREAM;
|
|
return obj->status;
|
|
}
|
|
|
|
if ((events & kSOCKET_EVENT_READ) && FD_ISSET(obj->handle, &readSet)) obj->eventStatus |= kSOCKET_EVENT_READ;
|
|
if ((events & kSOCKET_EVENT_WRITE) && FD_ISSET(obj->handle, &writeSet)) obj->eventStatus |= kSOCKET_EVENT_WRITE;
|
|
if ((events & kSOCKET_EVENT_EXCEPT) && FD_ISSET(obj->handle, &exceptSet)) obj->eventStatus |= kSOCKET_EVENT_EXCEPT;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
|
|
kFx(kStatus) xkSocket_Peek(kSocket sockt, void* buffer, kSize size, kSize* read)
|
|
{
|
|
return xkSocket_ReadImpl(sockt, buffer, size, read, xkSOCKET_MSG_PEEK);
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Read(kSocket sockt, void* buffer, kSize size, kSize* read)
|
|
{
|
|
return xkSocket_ReadImpl(sockt, buffer, size, read, 0);
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_ReadImpl(kSocket sockt, void* buffer, kSize size, kSize* read, k32s options)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
kSSize result;
|
|
k32s socketError;
|
|
kStatus opStatus = kOK;
|
|
|
|
kCheck(obj->status);
|
|
|
|
if (size == 0)
|
|
{
|
|
return kOK;
|
|
}
|
|
|
|
result = xkSocket_Recv(sockt, buffer, size, options);
|
|
|
|
if (result > 0)
|
|
{
|
|
if (!kIsNull(read))
|
|
{
|
|
*read = (kSize)result;
|
|
}
|
|
}
|
|
else if (result == 0)
|
|
{
|
|
opStatus = kERROR_CLOSED;
|
|
obj->status = kERROR_CLOSED;
|
|
}
|
|
else
|
|
{
|
|
socketError = xkSocket_GetLastError();
|
|
|
|
if ((socketError == xkSOCKET_TIMEDOUT) || (socketError == xkSOCKET_WOULD_BLOCK))
|
|
{
|
|
if (!obj->isBlocking)
|
|
{
|
|
opStatus = kERROR_BUSY;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_TIMEOUT;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_STREAM;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_STREAM;
|
|
}
|
|
}
|
|
}
|
|
|
|
return opStatus;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_ReadFrom(kSocket sockt, kIpEndPoint *endPoint, void* buffer, kSize size, kSize* read)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct sockaddr_in sockAddr;
|
|
kSSize result;
|
|
k32s socketError;
|
|
kStatus opStatus = kOK;
|
|
|
|
kCheck(obj->status);
|
|
|
|
result = xkSocket_RecvFrom(sockt, buffer, size, &sockAddr);
|
|
|
|
if (result > 0)
|
|
{
|
|
if (!kIsNull(endPoint))
|
|
{
|
|
kCheck(xkIpAddress_FromSockAddr(&sockAddr, &endPoint->address, &endPoint->port));
|
|
}
|
|
if (!kIsNull(read))
|
|
{
|
|
*read = (kSize) result;
|
|
}
|
|
}
|
|
else if (result == 0)
|
|
{
|
|
opStatus = kERROR_CLOSED;
|
|
obj->status = kERROR_CLOSED;
|
|
}
|
|
else
|
|
{
|
|
socketError = xkSocket_GetLastError();
|
|
|
|
if ((socketError == xkSOCKET_TIMEDOUT) || (socketError == xkSOCKET_WOULD_BLOCK))
|
|
{
|
|
if (!obj->isBlocking)
|
|
{
|
|
opStatus = kERROR_BUSY;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_TIMEOUT;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
else if (socketError == xkSOCKET_MSGSIZE)
|
|
{
|
|
opStatus = kERROR_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_STREAM;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_STREAM;
|
|
}
|
|
}
|
|
}
|
|
|
|
return opStatus;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_ReadFromEx(kSocket sockt, kIpEndPoint *endPoint, kSize* interfaceIndex, void* buffer, kSize size, kSize* read)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct sockaddr_in sockAddr;
|
|
kSSize result;
|
|
k32s socketError;
|
|
kStatus opStatus = kOK;
|
|
|
|
kCheck(obj->status);
|
|
kCheckTrue(obj->socketType == kSOCKET_TYPE_UDP, kERROR_STATE);
|
|
|
|
result = xkSocket_RecvMessage(sockt, buffer, size, &sockAddr, interfaceIndex);
|
|
|
|
if (result > 0)
|
|
{
|
|
if (!kIsNull(endPoint))
|
|
{
|
|
kCheck(xkIpAddress_FromSockAddr(&sockAddr, &endPoint->address, &endPoint->port));
|
|
}
|
|
if (!kIsNull(read))
|
|
{
|
|
*read = (kSize)result;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
socketError = xkSocket_GetLastError();
|
|
|
|
if ((socketError == xkSOCKET_TIMEDOUT) || (socketError == xkSOCKET_WOULD_BLOCK))
|
|
{
|
|
if (!obj->isBlocking)
|
|
{
|
|
opStatus = kERROR_BUSY;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_TIMEOUT;
|
|
}
|
|
}
|
|
else if (socketError == xkSOCKET_MSGSIZE)
|
|
{
|
|
opStatus = kERROR_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_STREAM;
|
|
}
|
|
}
|
|
|
|
return opStatus;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Write(kSocket sockt, const void* buffer, kSize size, kSize* written)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
kSSize result;
|
|
k32s socketError;
|
|
kStatus opStatus = kOK;
|
|
|
|
kCheck(obj->status);
|
|
|
|
*written = 0;
|
|
|
|
do
|
|
{
|
|
result = xkSocket_Send(sockt, (const kByte*)buffer + *written, size - *written);
|
|
|
|
if (result >= 0)
|
|
{
|
|
*written += (kSize) result;
|
|
}
|
|
else
|
|
{
|
|
socketError = xkSocket_GetLastError();
|
|
|
|
if ((socketError == xkSOCKET_TIMEDOUT) || (socketError == xkSOCKET_WOULD_BLOCK))
|
|
{
|
|
if (!obj->isBlocking)
|
|
{
|
|
opStatus = kERROR_BUSY;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_TIMEOUT;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_STREAM;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_STREAM;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (obj->isBlocking && !kIsError(opStatus) && (*written < size));
|
|
|
|
return opStatus;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_WriteTo(kSocket sockt, kIpAddress address, k32u port, const void* buffer, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct sockaddr_in sockAddr;
|
|
kSSize result;
|
|
k32s socketError;
|
|
kStatus opStatus = kOK;
|
|
|
|
kCheck(obj->status);
|
|
kCheckArgs(address.version == kIP_VERSION_4);
|
|
|
|
kCheck(xkIpAddress_ToSockAddr(address, port, &sockAddr));
|
|
|
|
result = xkSocket_SendTo(sockt, buffer, size, &sockAddr);
|
|
|
|
if (result < 0)
|
|
{
|
|
socketError = xkSocket_GetLastError();
|
|
|
|
if ((socketError == xkSOCKET_TIMEDOUT) || (socketError == xkSOCKET_WOULD_BLOCK))
|
|
{
|
|
if (!obj->isBlocking)
|
|
{
|
|
opStatus = kERROR_BUSY;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_TIMEOUT;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
else if (socketError == xkSOCKET_MSGSIZE)
|
|
{
|
|
opStatus = kERROR_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
opStatus = kERROR_STREAM;
|
|
|
|
if (obj->socketType == kSOCKET_TYPE_TCP)
|
|
{
|
|
obj->status = kERROR_STREAM;
|
|
}
|
|
}
|
|
}
|
|
else if ((kSize)result != size)
|
|
{
|
|
opStatus = kERROR_INCOMPLETE;
|
|
}
|
|
|
|
return opStatus;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_EnableBroadcast(kSocket sockt, kBool broadcast)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
kBool value = (broadcast) ? kTRUE : kFALSE;
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetNoDelay(kSocket sockt, kBool noDelay)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
kBool value = noDelay;
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetLingerTime(kSocket sockt, k64u lingerTime)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct linger linger;
|
|
|
|
kCheck(obj->status);
|
|
|
|
linger.l_onoff = 1;
|
|
linger.l_linger = (unsigned short) ((lingerTime + 999999)/1000000);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_LINGER, (char*) &linger, sizeof(struct linger)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_SetWriteLowWater(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) size;
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_SNDLOWAT, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_SetReadLowWater(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVLOWAT, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kSize) xkSocket_WriteBuffer(kSocket sockt)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int optValue = 0;
|
|
socklen_t optLen = sizeof(optValue);
|
|
|
|
if (getsockopt(obj->handle, SOL_SOCKET, SO_SNDBUF, (char*)&optValue, &optLen) == xkSOCKET_ERROR)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return (kSize)optValue;
|
|
}
|
|
|
|
kFx(kSize) xkSocket_ReadBuffer(kSocket sockt)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int optValue = 0;
|
|
socklen_t optLen = sizeof(optValue);
|
|
|
|
if (getsockopt(obj->handle, SOL_SOCKET, SO_RCVBUF, (char*)&optValue, &optLen) == xkSOCKET_ERROR)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return (kSize)optValue;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_LocalEndPoint(kSocket sockt, kIpEndPoint* endPoint)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct sockaddr_in sockAddr;
|
|
socklen_t sockAddrLength = (socklen_t) sizeof(sockAddr);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(getsockname(obj->handle, (struct sockaddr*)&sockAddr, &sockAddrLength) != xkSOCKET_ERROR);
|
|
kCheck(xkIpAddress_FromSockAddr(&sockAddr, &endPoint->address, &endPoint->port));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_RemoteEndPoint(kSocket sockt, kIpEndPoint* endPoint)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct sockaddr_in sockAddr;
|
|
socklen_t sockAddrLength = (socklen_t) sizeof(sockAddr);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(getpeername(obj->handle, (struct sockaddr*)&sockAddr, &sockAddrLength) == 0);
|
|
kCheck(xkIpAddress_FromSockAddr(&sockAddr,&endPoint->address, &endPoint->port));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_Status(kSocket sockt)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return obj->status;
|
|
}
|
|
|
|
kFx(kSize) kSocket_Handle(kSocket sockt)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return (kSize) obj->handle;
|
|
}
|
|
|
|
#if defined(K_WINDOWS)
|
|
|
|
kFx(kStatus) kSocket_EnableReuseAddress(kSocket sockt, kBool reuse)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int option = reuse;
|
|
|
|
kCheck(obj->status);
|
|
|
|
//FSS-1243: according to semi-credible internet gossip (and backed up by informal testing) Windows
|
|
//appears to ignore existing connections in the TIME_WAIT state by default when binding. as such, there
|
|
//doesn't appear to be any valid reason to use SO_REUSEADDR on Windows with TCP sockets (we don't consider
|
|
//"port stealing" to be valid). for UDP sockets, SO_REUSEADDR is required for broacast/multicast
|
|
//receive, so it's valid to use
|
|
if (obj->socketType == kSOCKET_TYPE_UDP)
|
|
{
|
|
kCheckState(setsockopt(obj->handle, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option)) != xkSOCKET_ERROR);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Recv(kSocket sockt, void* buffer, kSize size, k32s options)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return recv(obj->handle, (char*)buffer, (int)kMin_(size, xkSOCKET_MAX_IO_SIZE), options);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvFrom(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int sockAddrLength = sizeof(struct sockaddr_in);
|
|
|
|
return recvfrom(obj->handle, (char*)buffer, (int) kMin_(size, xkSOCKET_MAX_IO_SIZE), 0, (struct sockaddr*)from, &sockAddrLength);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvMessage(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from, kSize* interfaceIndex)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
GUID WSARecvMsgGUID = WSAID_WSARECVMSG;
|
|
WSACMSGHDR* messageHeader = kNULL;
|
|
LPFN_WSARECVMSG recvMsgFx = kNULL;
|
|
WSABUF iovec[1] = { 0 };
|
|
WSAMSG message = { 0 };
|
|
DWORD bytesRead = 0;
|
|
kChar pktbuf[1024];
|
|
|
|
*interfaceIndex = kSIZE_NULL;
|
|
|
|
if (WSAIoctl(obj->handle, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
|
&WSARecvMsgGUID, sizeof(WSARecvMsgGUID),
|
|
&recvMsgFx, sizeof(recvMsgFx),
|
|
&bytesRead, NULL, NULL) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
from->sin_family = AF_INET;
|
|
from->sin_port = htons(0);
|
|
|
|
iovec[0].buf = (kChar*)buffer;
|
|
iovec[0].len = (ULONG)size;
|
|
|
|
message.name = (struct sockaddr*)from;
|
|
message.namelen = sizeof(sockaddr_in);
|
|
message.lpBuffers = &iovec[0];
|
|
message.dwBufferCount = 1;
|
|
message.Control.buf = pktbuf;
|
|
message.Control.len = kCountOf(pktbuf);
|
|
message.dwFlags = 0;
|
|
|
|
if (recvMsgFx(obj->handle, &message, &bytesRead, NULL, NULL) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
messageHeader = WSA_CMSG_FIRSTHDR(&message);
|
|
if (!kIsNull(messageHeader) && (messageHeader->cmsg_type == IP_PKTINFO))
|
|
{
|
|
IN_PKTINFO* pktInfo = (IN_PKTINFO*)WSA_CMSG_DATA(messageHeader);
|
|
*interfaceIndex = pktInfo->ipi_ifindex;
|
|
}
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Send(kSocket sockt, const void* buffer, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return send(obj->handle, (const char*)buffer, (int) kMin_(size, xkSOCKET_MAX_IO_SIZE), 0);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_SendTo(kSocket sockt, const void* buffer, kSize size, const struct sockaddr_in* to)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int sockAddrLength = sizeof(struct sockaddr_in);
|
|
|
|
return sendto(obj->handle, (const char*)buffer, (int) kMin_(size, xkSOCKET_MAX_IO_SIZE), 0, (const struct sockaddr*)to, sockAddrLength);
|
|
}
|
|
|
|
kFx(k32s) xkSocket_GetLastError()
|
|
{
|
|
return WSAGetLastError();
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_ShutdownHandle(xkSocketHandle socktHandle)
|
|
{
|
|
shutdown(socktHandle, SD_BOTH);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_CloseHandle(xkSocketHandle socktHandle)
|
|
{
|
|
if (closesocket(socktHandle) != 0)
|
|
{
|
|
return kERROR_NETWORK;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetBlocking(kSocket sockt, kBool isBlocking)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
unsigned long nonBlocking = (unsigned long) !isBlocking;
|
|
|
|
kCheck(ioctlsocket(obj->handle, FIONBIO, &nonBlocking) == 0);
|
|
|
|
obj->isBlocking = isBlocking;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_SNDBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) (timeout + 999)/1000;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_SNDTIMEO, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) (timeout + 999)/1000;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVTIMEO, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_EnablePacketInfo(kSocket sockt, kBool enabled)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
kCheck(obj->status);
|
|
kCheckTrue(obj->socketType == kSOCKET_TYPE_UDP, kERROR_STATE);
|
|
|
|
kCheck(setsockopt(obj->handle, IPPROTO_IP, IP_PKTINFO, (char*)&enabled, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_BindToDevice(kSocket socket, const kChar* interfaceName)
|
|
{
|
|
//not supported; has no effect
|
|
return kOK;
|
|
}
|
|
|
|
#elif defined (K_TI_BIOS)
|
|
|
|
kFx(kStatus) kSocket_EnableReuseAddress(kSocket sockt, kBool reuse)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
kBool value = reuse;
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheckState(setsockopt(obj->handle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Recv(kSocket sockt, void* buffer, kSize size, k32s options)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
return recv(obj->handle, buffer, size, options);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvFrom(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
socklen_t sockAddrLength = sizeof(struct sockaddr_in);
|
|
|
|
return recvfrom(obj->handle, buffer, size, 0, (struct sockaddr*)from, &sockAddrLength);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvMessage(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from, kSize* interfaceIndex)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
*interfaceIndex = 2; //convention
|
|
|
|
return xkSocket_RecvFrom(sockt, buffer, size, from);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Send(kSocket sockt, const void* buffer, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return send(obj->handle, (void*)buffer, size, 0);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_SendTo(kSocket sockt, const void* buffer, kSize size, const struct sockaddr_in* to)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
socklen_t sockAddrLength = (socklen_t)sizeof(struct sockaddr_in);
|
|
|
|
return sendto(obj->handle, (void*)buffer, size, 0, (struct sockaddr*)to, sockAddrLength);
|
|
}
|
|
|
|
kFx(k32s) xkSocket_GetLastError()
|
|
{
|
|
return fdError();
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_ShutdownHandle(xkSocketHandle socktHandle)
|
|
{
|
|
struct linger linger;
|
|
|
|
linger.l_onoff = 1;
|
|
linger.l_linger = 0;
|
|
|
|
//SYS/BIOS doesn't seem to implement shutdown properly, so we set the linger time to zero to force connection reset
|
|
setsockopt(socktHandle, SOL_SOCKET, SO_LINGER, (char*) &linger, sizeof(struct linger));
|
|
|
|
shutdown(socktHandle, SHUT_RDWR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_CloseHandle(xkSocketHandle socktHandle)
|
|
{
|
|
fdClose(socktHandle);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetBlocking(kSocket sockt, kBool isBlocking)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int blockingOpt = (int) isBlocking;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_BLOCKING, &blockingOpt, sizeof(blockingOpt)) != xkSOCKET_ERROR);
|
|
|
|
obj->isBlocking = isBlocking;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
//using this option with a UDP socket generates "00000.553 mmFree: Double Free" condition in NDK stack (1.93)
|
|
if (obj->socketType != kSOCKET_TYPE_UDP)
|
|
{
|
|
kCheckTrue(setsockopt(obj->handle, SOL_SOCKET, SO_SNDBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR, kERROR_OS);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
//using this option with a UDP socket generates "00000.553 mmFree: Double Free" condition in NDK stack (1.93)
|
|
if (obj->socketType != kSOCKET_TYPE_UDP)
|
|
{
|
|
kCheckTrue(setsockopt(obj->handle, SOL_SOCKET, SO_RCVBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR, kERROR_OS);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct timeval tv;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_SNDTIMEO, xkSocket_FormatTimeVal(&tv, timeout), sizeof(tv)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct timeval tv;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVTIMEO, xkSocket_FormatTimeVal(&tv, timeout), sizeof(tv)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_EnablePacketInfo(kSocket sockt, kBool enabled)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_BindToDevice(kSocket socket, const kChar* interfaceName)
|
|
{
|
|
//not supported; has no effect
|
|
return kOK;
|
|
}
|
|
|
|
#elif defined (K_VX_KERNEL)
|
|
|
|
kFx(kStatus) kSocket_EnableReuseAddress(kSocket sockt, kBool reuse)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
kBool value = reuse;
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheckState(setsockopt(obj->handle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Recv(kSocket sockt, void* buffer, kSize size, k32s options)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
return recv(obj->handle, buffer, size, options);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvFrom(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int sockAddrLength = (int) sizeof(struct sockaddr_in);
|
|
|
|
return recvfrom(obj->handle, buffer, size, 0, (struct sockaddr*)from, &sockAddrLength);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvMessage(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from, kSize* interfaceIndex)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct msghdr message;
|
|
struct iovec iovec[1];
|
|
kChar pktbuf[1024];
|
|
kSSize read = 0;
|
|
struct cmsghdr* cmptr = kNULL;
|
|
|
|
*interfaceIndex = kSIZE_NULL;
|
|
|
|
iovec[0].iov_base = (kChar*)buffer;
|
|
iovec[0].iov_len = size;
|
|
|
|
message.msg_name = from;
|
|
message.msg_namelen = sizeof(struct sockaddr_in);
|
|
message.msg_iov = iovec;
|
|
message.msg_iovlen = 1;
|
|
message.msg_control = pktbuf;
|
|
message.msg_controllen = kCountOf(pktbuf);
|
|
|
|
read = recvmsg(obj->handle, &message, 0);
|
|
|
|
cmptr = CMSG_FIRSTHDR(&message);
|
|
if (!kIsNull(cmptr) && (cmptr->cmsg_type == IP_PKTINFO))
|
|
{
|
|
struct in_pktinfo* pktInfo = (struct in_pktinfo*)CMSG_DATA(cmptr);
|
|
*interfaceIndex = pktInfo->ipi_ifindex;
|
|
}
|
|
|
|
return read;
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Send(kSocket sockt, const void* buffer, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return send(obj->handle, buffer, size, 0);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_SendTo(kSocket sockt, const void* buffer, kSize size, const struct sockaddr_in* to)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int sockAddrLength = sizeof(struct sockaddr_in);
|
|
|
|
return sendto(obj->handle, buffer, size, 0, (const struct sockaddr*)to, sockAddrLength);
|
|
}
|
|
|
|
kFx(k32s) xkSocket_GetLastError()
|
|
{
|
|
return errno;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_ShutdownHandle(xkSocketHandle socktHandle)
|
|
{
|
|
shutdown(socktHandle, SHUT_RDWR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_CloseHandle(xkSocketHandle socktHandle)
|
|
{
|
|
close(socktHandle);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetBlocking(kSocket sockt, kBool isBlocking)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int nonBlocking = !isBlocking;
|
|
|
|
if (ioctl(obj->handle, FIONBIO, (int)&nonBlocking) == ERROR)
|
|
{
|
|
return kERROR_OS;
|
|
}
|
|
|
|
obj->isBlocking = isBlocking;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_SNDBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
/*
|
|
kObj(kSocket, sockt);
|
|
struct timeval tv;
|
|
int result = setsockopt(obj->handle, SOL_SOCKET, SO_SNDTIMEO, xkSocket_FormatTimeVal(&tv, timeout), sizeof(tv));
|
|
|
|
kCheck(result != xkSOCKET_ERROR);
|
|
*/
|
|
|
|
/*
|
|
* TODO: This option doesn't appear to be implemented in VxWorks 6.9; code above compiles, but always return an error.
|
|
* For now, we'll just return kOK; should file a bug report with Wind River, or just remove kSocket timeout feature if not needed.
|
|
*/
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct timeval tv;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVTIMEO, xkSocket_FormatTimeVal(&tv, timeout), sizeof(tv)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_EnablePacketInfo(kSocket sockt, kBool enabled)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
kCheck(obj->status);
|
|
kCheckTrue(obj->socketType == kSOCKET_TYPE_UDP, kERROR_STATE);
|
|
|
|
kCheck(setsockopt(obj->handle, IPPROTO_IP, IP_PKTINFO, (char*)&enabled, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
#elif defined (K_POSIX)
|
|
|
|
kFx(kStatus) kSocket_EnableReuseAddress(kSocket sockt, kBool reuse)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
kBool value = reuse;
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheckState(setsockopt(obj->handle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Recv(kSocket sockt, void* buffer, kSize size, k32s options)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
return recv(obj->handle, buffer, size, options);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvFrom(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
unsigned int sockAddrLength = sizeof(struct sockaddr_in);
|
|
|
|
return recvfrom(obj->handle, buffer, size, 0, (struct sockaddr*)from, &sockAddrLength);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_RecvMessage(kSocket sockt, void* buffer, kSize size, struct sockaddr_in* from, kSize* interfaceIndex)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct msghdr message;
|
|
struct iovec iovec[1];
|
|
kChar pktbuf[1024];
|
|
kSSize read = 0;
|
|
struct cmsghdr* cmptr = kNULL;
|
|
|
|
*interfaceIndex = kSIZE_NULL;
|
|
|
|
iovec[0].iov_base = buffer;
|
|
iovec[0].iov_len = size;
|
|
|
|
message.msg_name = from;
|
|
message.msg_namelen = sizeof(struct sockaddr_in);
|
|
message.msg_iov = iovec;
|
|
message.msg_iovlen = 1;
|
|
message.msg_control = pktbuf;
|
|
message.msg_controllen = kCountOf(pktbuf);
|
|
|
|
read = recvmsg(obj->handle, &message, 0);
|
|
|
|
cmptr = CMSG_FIRSTHDR(&message);
|
|
if (!kIsNull(cmptr) && (cmptr->cmsg_type == IP_PKTINFO))
|
|
{
|
|
struct in_pktinfo* pktInfo = (struct in_pktinfo*)CMSG_DATA(cmptr);
|
|
*interfaceIndex = pktInfo->ipi_ifindex;
|
|
}
|
|
|
|
return read;
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_Send(kSocket sockt, const void* buffer, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
return send(obj->handle, buffer, size, MSG_NOSIGNAL);
|
|
}
|
|
|
|
kFx(kSSize) xkSocket_SendTo(kSocket sockt, const void* buffer, kSize size, const struct sockaddr_in* to)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int sockAddrLength = sizeof(struct sockaddr_in);
|
|
|
|
#if defined(K_QNX)
|
|
return sendto(obj->handle, buffer, (int)kMin_(size, xkSOCKET_MAX_BUFFER), MSG_DONTROUTE, (const struct sockaddr*)to, sockAddrLength);
|
|
#else
|
|
return sendto(obj->handle, buffer, size, MSG_NOSIGNAL, (const struct sockaddr*)to, sockAddrLength);
|
|
#endif
|
|
}
|
|
|
|
kFx(k32s) xkSocket_GetLastError()
|
|
{
|
|
return errno;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_ShutdownHandle(xkSocketHandle socktHandle)
|
|
{
|
|
shutdown(socktHandle, SHUT_RDWR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkSocket_CloseHandle(xkSocketHandle socktHandle)
|
|
{
|
|
close(socktHandle);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetBlocking(kSocket sockt, kBool isBlocking)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int flags = fcntl(obj->handle, F_GETFL);
|
|
|
|
if (isBlocking)
|
|
{
|
|
fcntl(obj->handle, F_SETFL, flags &~O_NONBLOCK);
|
|
}
|
|
else
|
|
{
|
|
fcntl(obj->handle, F_SETFL, O_NONBLOCK);
|
|
}
|
|
|
|
obj->isBlocking = isBlocking;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_SNDBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadBuffer(kSocket sockt, kSize size)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
int value = (int) kMin_(size, xkSOCKET_MAX_BUFFER);
|
|
|
|
kCheck(obj->status);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVBUF, (char*)&value, sizeof(int)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetWriteTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct timeval tv;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_SNDTIMEO, xkSocket_FormatTimeVal(&tv, timeout), sizeof(tv)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_SetReadTimeout(kSocket sockt, k64u timeout)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct timeval tv;
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_RCVTIMEO, xkSocket_FormatTimeVal(&tv, timeout), sizeof(tv)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kSocket_EnablePacketInfo(kSocket sockt, kBool enabled)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
|
|
kCheck(obj->status);
|
|
kCheckTrue(obj->socketType == kSOCKET_TYPE_UDP, kERROR_STATE);
|
|
|
|
kCheck(setsockopt(obj->handle, IPPROTO_IP, IP_PKTINFO, (char*)&enabled, sizeof(kBool)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
#if !defined(K_DARWIN)
|
|
|
|
kFx(kStatus) kSocket_BindToDevice(kSocket sockt, const kChar* interfaceName)
|
|
{
|
|
kObj(kSocket, sockt);
|
|
struct ifreq sender;
|
|
|
|
kCheckArgs(interfaceName[0] != 0);
|
|
|
|
kMemSet(&sender, 0, sizeof(sender));
|
|
kStrCopy(sender.ifr_name, kCountOf(sender.ifr_name), interfaceName);
|
|
|
|
kCheck(setsockopt(obj->handle, SOL_SOCKET, SO_BINDTODEVICE, &sender, sizeof(sender)) != xkSOCKET_ERROR);
|
|
|
|
return kOK;
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|