deskflow/src/lib/arch/win32/ArchNetworkWinsock.cpp
Povilas Kanapickas 314899ac7d chore: arch, Remove support for lazy error code to string evaluations
This functionality is not needed because in all code paths the error
code is immediately converted to string without actually using the lazy
evaluation functionality.

based-on: 53dff5b803
ported and expanded by sithlord48
2025-05-28 14:00:18 +01:00

939 lines
26 KiB
C++

/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "arch/win32/ArchNetworkWinsock.h"
#include "arch/Arch.h"
#include "arch/IArchMultithread.h"
#include "arch/XArch.h"
#include "arch/win32/ArchMultithreadWindows.h"
#include "arch/win32/XArchWindows.h"
#include <malloc.h>
static const int s_family[] = {
PF_UNSPEC,
PF_INET,
PF_INET6,
};
static const int s_type[] = {SOCK_DGRAM, SOCK_STREAM};
static SOCKET(PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen);
static int(PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen);
static int(PASCAL FAR *close_winsock)(SOCKET s);
static int(PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen);
static int(PASCAL FAR *gethostname_winsock)(char FAR *name, int namelen);
static int(PASCAL FAR *getsockerror_winsock)(void);
static int(PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR *optval, int FAR *optlen);
static u_short(PASCAL FAR *htons_winsock)(u_short v);
static char FAR *(PASCAL FAR *inet_ntoa_winsock)(struct in_addr in);
static unsigned long(PASCAL FAR *inet_addr_winsock)(const char FAR *cp);
static int(PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR *data);
static int(PASCAL FAR *listen_winsock)(SOCKET s, int backlog);
static u_short(PASCAL FAR *ntohs_winsock)(u_short v);
static int(PASCAL FAR *recv_winsock)(SOCKET s, void FAR *buf, int len, int flags);
static int(PASCAL FAR *select_winsock)(
int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout
);
static int(PASCAL FAR *send_winsock)(SOCKET s, const void FAR *buf, int len, int flags);
static int(PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR *optval, int optlen);
static int(PASCAL FAR *shutdown_winsock)(SOCKET s, int how);
static SOCKET(PASCAL FAR *socket_winsock)(int af, int type, int protocol);
static struct hostent FAR *(PASCAL FAR *gethostbyaddr_winsock)(const char FAR *addr, int len, int type);
static struct hostent FAR *(PASCAL FAR *gethostbyname_winsock)(const char FAR *name);
static int(PASCAL FAR *WSACleanup_winsock)(void);
static int(PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR *fdset);
static WSAEVENT(PASCAL FAR *WSACreateEvent_winsock)(void);
static BOOL(PASCAL FAR *WSACloseEvent_winsock)(WSAEVENT);
static BOOL(PASCAL FAR *WSASetEvent_winsock)(WSAEVENT);
static BOOL(PASCAL FAR *WSAResetEvent_winsock)(WSAEVENT);
static int(PASCAL FAR *WSAEventSelect_winsock)(SOCKET, WSAEVENT, long);
static DWORD(PASCAL FAR *WSAWaitForMultipleEvents_winsock)(DWORD, const WSAEVENT FAR *, BOOL, DWORD, BOOL);
static int(PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS);
#undef FD_ISSET
#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set))
#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name)
static HMODULE s_networkModule = nullptr;
static FARPROC netGetProcAddress(HMODULE module, LPCSTR name)
{
FARPROC func = ::GetProcAddress(module, name);
if (!func) {
throw XArchNetworkSupport("");
}
return func;
}
ArchNetAddressImpl *ArchNetAddressImpl::alloc(size_t size)
{
size_t totalSize = size + ADDR_HDR_SIZE;
ArchNetAddressImpl *addr = (ArchNetAddressImpl *)malloc(totalSize);
addr->m_len = (int)size;
return addr;
}
//
// ArchNetworkWinsock
//
ArchNetworkWinsock::ArchNetworkWinsock() : m_mutex(nullptr)
{
}
ArchNetworkWinsock::~ArchNetworkWinsock()
{
if (s_networkModule != nullptr) {
WSACleanup_winsock();
::FreeLibrary(s_networkModule);
WSACleanup_winsock = nullptr;
s_networkModule = nullptr;
}
if (m_mutex != nullptr) {
ARCH->closeMutex(m_mutex);
}
EventList::iterator it;
for (it = m_unblockEvents.begin(); it != m_unblockEvents.end(); it++) {
delete *it;
}
}
void ArchNetworkWinsock::init()
{
static const char *s_library[] = {"ws2_32.dll"};
assert(WSACleanup_winsock == nullptr);
assert(s_networkModule == nullptr);
// try each winsock library
for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) {
try {
initModule((HMODULE)::LoadLibrary(s_library[i]));
m_mutex = ARCH->newMutex();
return;
} catch (XArchNetwork &) {
// ignore
}
}
// can't initialize any library
throw XArchNetworkSupport("Cannot load winsock library");
}
void ArchNetworkWinsock::initModule(HMODULE module)
{
if (module == nullptr) {
throw XArchNetworkSupport("");
}
// get startup function address
int(PASCAL FAR * startup)(WORD, LPWSADATA);
setfunc(startup, WSAStartup, int(PASCAL FAR *)(WORD, LPWSADATA));
// startup network library
WORD version = MAKEWORD(2 /*major*/, 2 /*minor*/);
WSADATA data;
int err = startup(version, &data);
if (data.wVersion != version) {
throw XArchNetworkSupport(winsockErrorToString(err));
}
if (err != 0) {
// some other initialization error
throwError(err);
}
// get function addresses
setfunc(accept_winsock, accept, SOCKET(PASCAL FAR *)(SOCKET s, struct sockaddr FAR * addr, int FAR *addrlen));
setfunc(bind_winsock, bind, int(PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen));
setfunc(close_winsock, closesocket, int(PASCAL FAR *)(SOCKET s));
setfunc(connect_winsock, connect, int(PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen));
setfunc(gethostname_winsock, gethostname, int(PASCAL FAR *)(char FAR *name, int namelen));
setfunc(getsockerror_winsock, WSAGetLastError, int(PASCAL FAR *)(void));
setfunc(
getsockopt_winsock, getsockopt,
int(PASCAL FAR *)(SOCKET s, int level, int optname, void FAR *optval, int FAR *optlen)
);
setfunc(htons_winsock, htons, u_short(PASCAL FAR *)(u_short v));
setfunc(inet_ntoa_winsock, inet_ntoa, char FAR *(PASCAL FAR *)(struct in_addr in));
setfunc(inet_addr_winsock, inet_addr, unsigned long(PASCAL FAR *)(const char FAR *cp));
setfunc(ioctl_winsock, ioctlsocket, int(PASCAL FAR *)(SOCKET s, int cmd, void FAR *));
setfunc(listen_winsock, listen, int(PASCAL FAR *)(SOCKET s, int backlog));
setfunc(ntohs_winsock, ntohs, u_short(PASCAL FAR *)(u_short v));
setfunc(recv_winsock, recv, int(PASCAL FAR *)(SOCKET s, void FAR *buf, int len, int flags));
setfunc(
select_winsock, select,
int(PASCAL FAR *)(
int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout
)
);
setfunc(send_winsock, send, int(PASCAL FAR *)(SOCKET s, const void FAR *buf, int len, int flags));
setfunc(
setsockopt_winsock, setsockopt,
int(PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR *optval, int optlen)
);
setfunc(shutdown_winsock, shutdown, int(PASCAL FAR *)(SOCKET s, int how));
setfunc(socket_winsock, socket, SOCKET(PASCAL FAR *)(int af, int type, int protocol));
setfunc(
gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR *addr, int len, int type)
);
setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR *name));
setfunc(WSACleanup_winsock, WSACleanup, int(PASCAL FAR *)(void));
setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int(PASCAL FAR *)(SOCKET, fd_set FAR *));
setfunc(WSACreateEvent_winsock, WSACreateEvent, WSAEVENT(PASCAL FAR *)(void));
setfunc(WSACloseEvent_winsock, WSACloseEvent, BOOL(PASCAL FAR *)(WSAEVENT));
setfunc(WSASetEvent_winsock, WSASetEvent, BOOL(PASCAL FAR *)(WSAEVENT));
setfunc(WSAResetEvent_winsock, WSAResetEvent, BOOL(PASCAL FAR *)(WSAEVENT));
setfunc(WSAEventSelect_winsock, WSAEventSelect, int(PASCAL FAR *)(SOCKET, WSAEVENT, long));
setfunc(
WSAWaitForMultipleEvents_winsock, WSAWaitForMultipleEvents,
DWORD(PASCAL FAR *)(DWORD, const WSAEVENT FAR *, BOOL, DWORD, BOOL)
);
setfunc(WSAEnumNetworkEvents_winsock, WSAEnumNetworkEvents, int(PASCAL FAR *)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
s_networkModule = module;
}
ArchSocket ArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
{
// create socket
SOCKET fd = socket_winsock(s_family[family], s_type[type], 0);
if (fd == INVALID_SOCKET) {
throwError(getsockerror_winsock());
}
try {
setBlockingOnSocket(fd, false);
BOOL flag = 0;
int size = sizeof(flag);
if (setsockopt_winsock(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
} catch (...) {
close_winsock(fd);
throw;
}
// allocate socket object
ArchSocketImpl *socket = new ArchSocketImpl;
socket->m_socket = fd;
socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = true;
return socket;
}
ArchSocket ArchNetworkWinsock::copySocket(ArchSocket s)
{
assert(s != nullptr);
// ref the socket and return it
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
return s;
}
void ArchNetworkWinsock::closeSocket(ArchSocket s)
{
assert(s != nullptr);
// unref the socket and note if it should be released
ARCH->lockMutex(m_mutex);
const bool doClose = (--s->m_refCount == 0);
ARCH->unlockMutex(m_mutex);
// close the socket if necessary
if (doClose) {
if (close_winsock(s->m_socket) == SOCKET_ERROR) {
// close failed. restore the last ref and throw.
int err = getsockerror_winsock();
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
throwError(err);
}
WSACloseEvent_winsock(s->m_event);
delete s;
}
}
void ArchNetworkWinsock::closeSocketForRead(ArchSocket s)
{
assert(s != nullptr);
if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) {
if (getsockerror_winsock() != WSAENOTCONN) {
throwError(getsockerror_winsock());
}
}
}
void ArchNetworkWinsock::closeSocketForWrite(ArchSocket s)
{
assert(s != nullptr);
if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) {
if (getsockerror_winsock() != WSAENOTCONN) {
throwError(getsockerror_winsock());
}
}
}
void ArchNetworkWinsock::bindSocket(ArchSocket s, ArchNetAddress addr)
{
assert(s != nullptr);
assert(addr != nullptr);
if (bind_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
void ArchNetworkWinsock::listenOnSocket(ArchSocket s)
{
assert(s != nullptr);
// hardcoding backlog
if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
ArchSocket ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress *const addr)
{
assert(s != nullptr);
// create new socket and temporary address
ArchSocketImpl *socket = new ArchSocketImpl;
ArchNetAddress tmp = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
// accept on socket
SOCKET fd = accept_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, tmp), &tmp->m_len);
if (fd == INVALID_SOCKET) {
int err = getsockerror_winsock();
delete socket;
free(tmp);
if (addr) {
*addr = nullptr;
}
if (err == WSAEWOULDBLOCK) {
return nullptr;
}
throwError(err);
}
try {
setBlockingOnSocket(fd, false);
} catch (...) {
close_winsock(fd);
delete socket;
free(tmp);
if (addr) {
*addr = nullptr;
}
throw;
}
// initialize socket
socket->m_socket = fd;
socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = true;
// copy address if requested
if (addr != nullptr) {
*addr = ARCH->copyAddr(tmp);
}
free(tmp);
return socket;
}
bool ArchNetworkWinsock::connectSocket(ArchSocket s, ArchNetAddress addr)
{
assert(s != nullptr);
assert(addr != nullptr);
if (connect_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == SOCKET_ERROR) {
if (getsockerror_winsock() == WSAEISCONN) {
return true;
}
if (getsockerror_winsock() == WSAEWOULDBLOCK) {
return false;
}
throwError(getsockerror_winsock());
}
return true;
}
int ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
{
int i;
DWORD n;
// prepare sockets and wait list
bool canWrite = false;
WSAEVENT *events = (WSAEVENT *)alloca((num + 1) * sizeof(WSAEVENT));
for (i = 0, n = 0; i < num; ++i) {
// reset return flags
pe[i].m_revents = 0;
// set invalid flag if socket is bogus then go to next socket
if (pe[i].m_socket == nullptr) {
pe[i].m_revents |= kPOLLNVAL;
continue;
}
// select desired events
long socketEvents = 0;
if ((pe[i].m_events & kPOLLIN) != 0) {
socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE;
}
if ((pe[i].m_events & kPOLLOUT) != 0) {
socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE;
// if m_pollWrite is false then we assume the socket is
// writable. winsock doesn't signal writability except
// when the state changes from unwritable.
if (!pe[i].m_socket->m_pollWrite) {
canWrite = true;
pe[i].m_revents |= kPOLLOUT;
}
}
// if no events then ignore socket
if (socketEvents == 0) {
continue;
}
// select socket for desired events
WSAEventSelect_winsock(pe[i].m_socket->m_socket, pe[i].m_socket->m_event, socketEvents);
// add socket event to wait list
events[n++] = pe[i].m_socket->m_event;
}
// if no sockets then return immediately
if (n == 0) {
return 0;
}
// add the unblock event
ArchMultithreadWindows *mt = ArchMultithreadWindows::getInstance();
ArchThread thread = mt->newCurrentThread();
WSAEVENT *unblockEvent = (WSAEVENT *)mt->getNetworkDataForThread(thread);
ARCH->closeThread(thread);
if (unblockEvent == nullptr) {
unblockEvent = new WSAEVENT;
m_unblockEvents.push_back(unblockEvent);
*unblockEvent = WSACreateEvent_winsock();
mt->setNetworkDataForCurrentThread(unblockEvent);
}
events[n++] = *unblockEvent;
// prepare timeout
DWORD t = (timeout < 0.0) ? INFINITE : (DWORD)(1000.0 * timeout);
if (canWrite) {
// if we know we can write then don't block
t = 0;
}
// wait
DWORD result = WSAWaitForMultipleEvents_winsock(n, events, FALSE, t, FALSE);
// reset the unblock event
WSAResetEvent_winsock(*unblockEvent);
// handle results
if (result == WSA_WAIT_FAILED) {
if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call
ARCH->testCancelThread();
return 0;
}
throwError(getsockerror_winsock());
}
if (result == WSA_WAIT_TIMEOUT && !canWrite) {
return 0;
}
if (result == WSA_WAIT_EVENT_0 + n - 1) {
// the unblock event was signalled
return 0;
}
for (i = 0, n = 0; i < num; ++i) {
// skip events we didn't check
if (pe[i].m_socket == nullptr || (pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) {
continue;
}
// get events
WSANETWORKEVENTS info;
if (WSAEnumNetworkEvents_winsock(pe[i].m_socket->m_socket, pe[i].m_socket->m_event, &info) == SOCKET_ERROR) {
continue;
}
if ((info.lNetworkEvents & FD_READ) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((info.lNetworkEvents & FD_ACCEPT) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((info.lNetworkEvents & FD_WRITE) != 0) {
pe[i].m_revents |= kPOLLOUT;
// socket is now writable so don't bothing polling for
// writable until it becomes unwritable.
pe[i].m_socket->m_pollWrite = false;
}
if ((info.lNetworkEvents & FD_CONNECT) != 0) {
if (info.iErrorCode[FD_CONNECT_BIT] != 0) {
pe[i].m_revents |= kPOLLERR;
} else {
pe[i].m_revents |= kPOLLOUT;
pe[i].m_socket->m_pollWrite = false;
}
}
if ((info.lNetworkEvents & FD_CLOSE) != 0) {
if (info.iErrorCode[FD_CLOSE_BIT] != 0) {
pe[i].m_revents |= kPOLLERR;
} else {
if ((pe[i].m_events & kPOLLIN) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((pe[i].m_events & kPOLLOUT) != 0) {
pe[i].m_revents |= kPOLLOUT;
}
}
}
if (pe[i].m_revents != 0) {
++n;
}
}
return (int)n;
}
void ArchNetworkWinsock::unblockPollSocket(ArchThread thread)
{
// set the unblock event
ArchMultithreadWindows *mt = ArchMultithreadWindows::getInstance();
WSAEVENT *unblockEvent = (WSAEVENT *)mt->getNetworkDataForThread(thread);
if (unblockEvent != nullptr) {
WSASetEvent_winsock(*unblockEvent);
}
}
size_t ArchNetworkWinsock::readSocket(ArchSocket s, void *buf, size_t len)
{
assert(s != nullptr);
int n = recv_winsock(s->m_socket, buf, (int)len, 0);
if (n == SOCKET_ERROR) {
int err = getsockerror_winsock();
if (err == WSAEINTR || err == WSAEWOULDBLOCK) {
return 0;
}
throwError(err);
}
return static_cast<size_t>(n);
}
size_t ArchNetworkWinsock::writeSocket(ArchSocket s, const void *buf, size_t len)
{
assert(s != nullptr);
int n = send_winsock(s->m_socket, buf, (int)len, 0);
if (n == SOCKET_ERROR) {
int err = getsockerror_winsock();
if (err == WSAEINTR) {
return 0;
}
if (err == WSAEWOULDBLOCK) {
s->m_pollWrite = true;
return 0;
}
throwError(err);
}
return static_cast<size_t>(n);
}
void ArchNetworkWinsock::throwErrorOnSocket(ArchSocket s)
{
assert(s != nullptr);
// get the error from the socket layer
int err = 0;
int size = sizeof(err);
if (getsockopt_winsock(s->m_socket, SOL_SOCKET, SO_ERROR, &err, &size) == SOCKET_ERROR) {
err = getsockerror_winsock();
}
// throw if there's an error
if (err != 0) {
throwError(err);
}
}
void ArchNetworkWinsock::setBlockingOnSocket(SOCKET s, bool blocking)
{
assert(s != 0);
int flag = blocking ? 0 : 1;
if (ioctl_winsock(s, FIONBIO, &flag) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
bool ArchNetworkWinsock::setNoDelayOnSocket(ArchSocket s, bool noDelay)
{
assert(s != nullptr);
// get old state
BOOL oflag;
int size = sizeof(oflag);
if (getsockopt_winsock(s->m_socket, IPPROTO_TCP, TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
// set new state
BOOL flag = noDelay ? 1 : 0;
size = sizeof(flag);
if (setsockopt_winsock(s->m_socket, IPPROTO_TCP, TCP_NODELAY, &flag, size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
return (oflag != 0);
}
bool ArchNetworkWinsock::setReuseAddrOnSocket(ArchSocket s, bool reuse)
{
assert(s != nullptr);
// get old state
BOOL oflag;
int size = sizeof(oflag);
if (getsockopt_winsock(s->m_socket, SOL_SOCKET, SO_REUSEADDR, &oflag, &size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
// set new state
BOOL flag = reuse ? 1 : 0;
size = sizeof(flag);
if (setsockopt_winsock(s->m_socket, SOL_SOCKET, SO_REUSEADDR, &flag, size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
return (oflag != 0);
}
std::string ArchNetworkWinsock::getHostName()
{
char name[256];
if (gethostname_winsock(name, sizeof(name)) == -1) {
name[0] = '\0';
} else {
name[sizeof(name) - 1] = '\0';
}
return name;
}
ArchNetAddress ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
{
ArchNetAddressImpl *addr = nullptr;
switch (family) {
case kINET: {
addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
ipAddr->sin_family = AF_INET;
ipAddr->sin_port = 0;
ipAddr->sin_addr.s_addr = INADDR_ANY;
break;
}
case kINET6: {
addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
ipAddr->sin6_family = AF_INET6;
ipAddr->sin6_port = 0;
memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
break;
}
default:
assert(0 && "invalid family");
}
return addr;
}
ArchNetAddress ArchNetworkWinsock::copyAddr(ArchNetAddress addr)
{
assert(addr != nullptr);
ArchNetAddressImpl *copy = ArchNetAddressImpl::alloc(addr->m_len);
memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
return copy;
}
std::vector<ArchNetAddress> ArchNetworkWinsock::nameToAddr(const std::string &name)
{
// allocate address
std::vector<ArchNetAddressImpl *> addresses;
struct addrinfo hints;
struct addrinfo *pResult;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
int ret = -1;
ARCH->lockMutex(m_mutex);
if ((ret = getaddrinfo(name.c_str(), nullptr, &hints, &pResult)) != 0) {
ARCH->unlockMutex(m_mutex);
throwNameError(ret);
}
for (auto address = pResult; address != nullptr; address = address->ai_next) {
addresses.push_back(new ArchNetAddressImpl);
if (address->ai_family == AF_INET) {
addresses.back()->m_len = (socklen_t)sizeof(struct sockaddr_in);
} else {
addresses.back()->m_len = (socklen_t)sizeof(struct sockaddr_in6);
}
memcpy(&addresses.back()->m_addr, address->ai_addr, addresses.back()->m_len);
}
freeaddrinfo(pResult);
ARCH->unlockMutex(m_mutex);
return addresses;
}
void ArchNetworkWinsock::closeAddr(ArchNetAddress addr)
{
assert(addr != nullptr);
free(addr);
}
std::string ArchNetworkWinsock::addrToName(ArchNetAddress addr)
{
assert(addr != nullptr);
char host[1024];
char service[20];
int ret =
getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host, sizeof(host), service, sizeof(service), 0);
if (ret != 0) {
throwNameError(ret);
}
// return (primary) name
std::string name = host;
return name;
}
std::string ArchNetworkWinsock::addrToString(ArchNetAddress addr)
{
assert(addr != nullptr);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
return inet_ntoa_winsock(ipAddr->sin_addr);
}
case kINET6: {
char strAddr[INET6_ADDRSTRLEN];
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
return strAddr;
}
default:
assert(0 && "unknown address family");
return "";
}
}
IArchNetwork::EAddressFamily ArchNetworkWinsock::getAddrFamily(ArchNetAddress addr)
{
assert(addr != nullptr);
switch (addr->m_addr.ss_family) {
case AF_INET:
return kINET;
case AF_INET6:
return kINET6;
default:
return kUNKNOWN;
}
}
void ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port)
{
assert(addr != nullptr);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
ipAddr->sin_port = htons_winsock(static_cast<u_short>(port));
break;
}
case kINET6: {
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
ipAddr->sin6_port = htons_winsock(static_cast<u_short>(port));
break;
}
default:
assert(0 && "unknown address family");
break;
}
}
int ArchNetworkWinsock::getAddrPort(ArchNetAddress addr)
{
assert(addr != nullptr);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
return ntohs_winsock(ipAddr->sin_port);
}
case kINET6: {
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
return ntohs_winsock(ipAddr->sin6_port);
}
default:
assert(0 && "unknown address family");
return 0;
}
}
bool ArchNetworkWinsock::isAnyAddr(ArchNetAddress addr)
{
assert(addr != nullptr);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
return (addr->m_len == sizeof(struct sockaddr_in) && ipAddr->sin_addr.s_addr == INADDR_ANY);
}
case kINET6: {
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
return (
addr->m_len == sizeof(struct sockaddr_in) && memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0
);
}
default:
assert(0 && "unknown address family");
return true;
}
}
bool ArchNetworkWinsock::isEqualAddr(ArchNetAddress a, ArchNetAddress b)
{
return (a == b || (a->m_len == b->m_len && memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
}
void ArchNetworkWinsock::throwError(int err)
{
switch (err) {
case WSAEACCES:
throw XArchNetworkAccess(winsockErrorToString(err));
case WSAEMFILE:
case WSAENOBUFS:
case WSAENETDOWN:
throw XArchNetworkResource(winsockErrorToString(err));
case WSAEPROTOTYPE:
case WSAEPROTONOSUPPORT:
case WSAEAFNOSUPPORT:
case WSAEPFNOSUPPORT:
case WSAESOCKTNOSUPPORT:
case WSAEINVAL:
case WSAENOPROTOOPT:
case WSAEOPNOTSUPP:
case WSAESHUTDOWN:
case WSANOTINITIALISED:
case WSAVERNOTSUPPORTED:
case WSASYSNOTREADY:
throw XArchNetworkSupport(winsockErrorToString(err));
case WSAEADDRNOTAVAIL:
throw XArchNetworkNoAddress(winsockErrorToString(err));
case WSAEADDRINUSE:
throw XArchNetworkAddressInUse(winsockErrorToString(err));
case WSAEHOSTUNREACH:
case WSAENETUNREACH:
throw XArchNetworkNoRoute(winsockErrorToString(err));
case WSAENOTCONN:
throw XArchNetworkNotConnected(winsockErrorToString(err));
case WSAEDISCON:
throw XArchNetworkShutdown(winsockErrorToString(err));
case WSAENETRESET:
case WSAECONNABORTED:
case WSAECONNRESET:
throw XArchNetworkDisconnected(winsockErrorToString(err));
case WSAECONNREFUSED:
throw XArchNetworkConnectionRefused(winsockErrorToString(err));
case WSAEHOSTDOWN:
case WSAETIMEDOUT:
throw XArchNetworkTimedOut(winsockErrorToString(err));
case WSAHOST_NOT_FOUND:
throw XArchNetworkNameUnknown(winsockErrorToString(err));
case WSANO_DATA:
throw XArchNetworkNameNoAddress(winsockErrorToString(err));
case WSANO_RECOVERY:
throw XArchNetworkNameFailure(winsockErrorToString(err));
case WSATRY_AGAIN:
throw XArchNetworkNameUnavailable(winsockErrorToString(err));
default:
throw XArchNetwork(winsockErrorToString(err));
}
}
void ArchNetworkWinsock::throwNameError(int err)
{
switch (err) {
case WSAHOST_NOT_FOUND:
throw XArchNetworkNameUnknown(winsockErrorToString(err));
case WSANO_DATA:
throw XArchNetworkNameNoAddress(winsockErrorToString(err));
case WSANO_RECOVERY:
throw XArchNetworkNameFailure(winsockErrorToString(err));
case WSATRY_AGAIN:
throw XArchNetworkNameUnavailable(winsockErrorToString(err));
default:
throw XArchNetworkName(winsockErrorToString(err));
}
}