barrier/lib/arch/CArchNetworkWinsock.cpp
2004-02-28 12:19:49 +00:00

938 lines
24 KiB
C++

/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CArchNetworkWinsock.h"
#include "CArch.h"
#include "IArchMultithread.h"
#include "XArchWindows.h"
#include <malloc.h>
static const int s_family[] = {
PF_UNSPEC,
PF_INET
};
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 = NULL;
static
FARPROC
netGetProcAddress(HMODULE module, LPCSTR name)
{
FARPROC func = ::GetProcAddress(module, name);
if (!func) {
throw XArchNetworkSupport("");
}
return func;
}
CArchNetAddressImpl*
CArchNetAddressImpl::alloc(size_t size)
{
size_t totalSize = size + ADDR_HDR_SIZE;
CArchNetAddressImpl* addr = (CArchNetAddressImpl*)malloc(totalSize);
addr->m_len = size;
return addr;
}
//
// CArchNetworkWinsock
//
CArchNetworkWinsock::CArchNetworkWinsock()
{
static const char* s_library[] = { "ws2_32.dll" };
assert(WSACleanup_winsock == NULL);
assert(s_networkModule == NULL);
// try each winsock library
for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) {
try {
init((HMODULE)::LoadLibrary(s_library[i]));
m_mutex = ARCH->newMutex();
return;
}
catch (XArchNetwork&) {
// ignore
}
}
// can't initialize any library
throw XArchNetworkSupport("Cannot load winsock library");
}
CArchNetworkWinsock::~CArchNetworkWinsock()
{
if (s_networkModule != NULL) {
WSACleanup_winsock();
::FreeLibrary(s_networkModule);
WSACleanup_winsock = NULL;
s_networkModule = NULL;
}
ARCH->closeMutex(m_mutex);
}
void
CArchNetworkWinsock::init(HMODULE module)
{
if (module == NULL) {
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*/, 0 /*minor*/);
WSADATA data;
int err = startup(version, &data);
if (data.wVersion != version) {
throw XArchNetworkSupport(new XArchEvalWinsock(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;
}
CArchSocket
CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
{
// allocate socket object
CArchSocketImpl* socket = new CArchSocketImpl;
// create socket
SOCKET fd = socket_winsock(s_family[family], s_type[type], 0);
if (fd == INVALID_SOCKET) {
throwError(getsockerror_winsock());
}
socket->m_socket = fd;
socket->m_connected = false;
socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = false;
return socket;
}
CArchSocket
CArchNetworkWinsock::copySocket(CArchSocket s)
{
assert(s != NULL);
// ref the socket and return it
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
return s;
}
void
CArchNetworkWinsock::closeSocket(CArchSocket s)
{
assert(s != NULL);
// 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) {
do {
if (close_winsock(s->m_socket) == SOCKET_ERROR) {
// close failed
int err = getsockerror_winsock();
if (err == WSAEINTR) {
// interrupted system call
ARCH->testCancelThread();
continue;
}
// restore the last ref and throw
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
throwError(err);
}
} while (false);
WSACloseEvent_winsock(s->m_event);
delete s;
}
}
void
CArchNetworkWinsock::closeSocketForRead(CArchSocket s)
{
assert(s != NULL);
if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) {
if (getsockerror_winsock() != WSAENOTCONN) {
throwError(getsockerror_winsock());
}
}
}
void
CArchNetworkWinsock::closeSocketForWrite(CArchSocket s)
{
assert(s != NULL);
if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) {
if (getsockerror_winsock() != WSAENOTCONN) {
throwError(getsockerror_winsock());
}
}
}
void
CArchNetworkWinsock::bindSocket(CArchSocket s, CArchNetAddress addr)
{
assert(s != NULL);
assert(addr != NULL);
if (bind_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
void
CArchNetworkWinsock::listenOnSocket(CArchSocket s)
{
assert(s != NULL);
// hardcoding backlog
if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
CArchSocket
CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr)
{
assert(s != NULL);
// create new socket and temporary address
CArchSocketImpl* socket = new CArchSocketImpl;
CArchNetAddress tmp = CArchNetAddressImpl::alloc(sizeof(struct sockaddr));
// accept on socket
SOCKET fd;
do {
fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len);
if (fd == INVALID_SOCKET) {
int err = getsockerror_winsock();
delete socket;
free(tmp);
*addr = NULL;
if (err == WSAEINTR) {
// interrupted system call
ARCH->testCancelThread();
return NULL;
}
throwError(err);
}
} while (false);
// initialize socket
socket->m_socket = fd;
socket->m_connected = true;
socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = true;
// copy address if requested
if (addr != NULL) {
*addr = ARCH->copyAddr(tmp);
}
free(tmp);
return socket;
}
void
CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr)
{
assert(s != NULL);
assert(addr != NULL);
do {
if (connect_winsock(s->m_socket, &addr->m_addr,
addr->m_len) == SOCKET_ERROR) {
if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call
ARCH->testCancelThread();
continue;
}
if (getsockerror_winsock() == WSAEISCONN) {
// already connected
break;
}
if (getsockerror_winsock() == WSAEWOULDBLOCK) {
// connecting
throw XArchNetworkConnecting(new XArchEvalWinsock(
getsockerror_winsock()));
}
throwError(getsockerror_winsock());
}
} while (false);
ARCH->lockMutex(m_mutex);
s->m_connected = true;
ARCH->unlockMutex(m_mutex);
}
int
CArchNetworkWinsock::pollSocket(CPollEntry 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 == NULL) {
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
CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
CArchThread thread = mt->newCurrentThread();
WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
if (unblockEvent == NULL) {
unblockEvent = new WSAEVENT;
*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 == NULL ||
(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
CArchNetworkWinsock::unblockPollSocket(CArchThread thread)
{
// set the unblock event
CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
if (unblockEvent != NULL) {
WSASetEvent_winsock(*unblockEvent);
}
}
size_t
CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len)
{
assert(s != NULL);
int n;
do {
n = recv_winsock(s->m_socket, buf, len, 0);
if (n == SOCKET_ERROR) {
if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call
n = 0;
break;
}
else if (getsockerror_winsock() == WSAEWOULDBLOCK) {
n = 0;
break;
}
throwError(getsockerror_winsock());
}
} while (false);
ARCH->testCancelThread();
return static_cast<size_t>(n);
}
size_t
CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len)
{
assert(s != NULL);
int n;
do {
n = send_winsock(s->m_socket, buf, len, 0);
if (n == SOCKET_ERROR) {
if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call
n = 0;
break;
}
else if (getsockerror_winsock() == WSAEWOULDBLOCK) {
s->m_pollWrite = true;
n = 0;
break;
}
throwError(getsockerror_winsock());
}
} while (false);
ARCH->testCancelThread();
return static_cast<size_t>(n);
}
void
CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s)
{
assert(s != NULL);
// 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);
}
}
bool
CArchNetworkWinsock::setBlockingOnSocket(CArchSocket s, bool blocking)
{
assert(s != NULL);
int flag = blocking ? 0 : 1;
if (ioctl_winsock(s->m_socket, FIONBIO, &flag) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
// FIXME -- can't get the current blocking state of socket?
return true;
}
bool
CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay)
{
assert(s != NULL);
// 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);
}
std::string
CArchNetworkWinsock::getHostName()
{
char name[256];
if (gethostname_winsock(name, sizeof(name)) == -1) {
name[0] = '\0';
}
else {
name[sizeof(name) - 1] = '\0';
}
return name;
}
CArchNetAddress
CArchNetworkWinsock::newAnyAddr(EAddressFamily family)
{
CArchNetAddressImpl* addr = NULL;
switch (family) {
case kINET: {
addr = CArchNetAddressImpl::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;
}
default:
assert(0 && "invalid family");
}
return addr;
}
CArchNetAddress
CArchNetworkWinsock::copyAddr(CArchNetAddress addr)
{
assert(addr != NULL);
CArchNetAddressImpl* copy = CArchNetAddressImpl::alloc(addr->m_len);
memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
return copy;
}
CArchNetAddress
CArchNetworkWinsock::nameToAddr(const std::string& name)
{
// allocate address
CArchNetAddressImpl* addr = NULL;
// try to convert assuming an IPv4 dot notation address
struct sockaddr_in inaddr;
memset(&inaddr, 0, sizeof(inaddr));
inaddr.sin_family = AF_INET;
inaddr.sin_port = 0;
inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str());
if (inaddr.sin_addr.s_addr != INADDR_NONE) {
// it's a dot notation address
addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len);
}
else {
// address lookup
struct hostent* info = gethostbyname_winsock(name.c_str());
if (info == NULL) {
delete addr;
throwNameError(getsockerror_winsock());
}
addr = CArchNetAddressImpl::alloc(info->h_length);
memcpy(TYPED_ADDR(void, addr), info->h_addr_list[0], info->h_length);
}
return addr;
}
void
CArchNetworkWinsock::closeAddr(CArchNetAddress addr)
{
assert(addr != NULL);
free(addr);
}
std::string
CArchNetworkWinsock::addrToName(CArchNetAddress addr)
{
assert(addr != NULL);
// name lookup
struct hostent* info = gethostbyaddr_winsock(
reinterpret_cast<const char FAR*>(&addr->m_addr),
addr->m_len, addr->m_addr.sa_family);
if (info == NULL) {
throwNameError(getsockerror_winsock());
}
// return (primary) name
return info->h_name;
}
std::string
CArchNetworkWinsock::addrToString(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return inet_ntoa_winsock(ipAddr->sin_addr);
}
default:
assert(0 && "unknown address family");
return "";
}
}
IArchNetwork::EAddressFamily
CArchNetworkWinsock::getAddrFamily(CArchNetAddress addr)
{
assert(addr != NULL);
switch (addr->m_addr.sa_family) {
case AF_INET:
return kINET;
default:
return kUNKNOWN;
}
}
void
CArchNetworkWinsock::setAddrPort(CArchNetAddress addr, int port)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
ipAddr->sin_port = htons_winsock(static_cast<u_short>(port));
break;
}
default:
assert(0 && "unknown address family");
break;
}
}
int
CArchNetworkWinsock::getAddrPort(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return ntohs_winsock(ipAddr->sin_port);
}
default:
assert(0 && "unknown address family");
return 0;
}
}
bool
CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return (addr->m_len == sizeof(struct sockaddr_in) &&
ipAddr->sin_addr.s_addr == INADDR_ANY);
}
default:
assert(0 && "unknown address family");
return true;
}
}
bool
CArchNetworkWinsock::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
{
return (a == b || (a->m_len == b->m_len &&
memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
}
void
CArchNetworkWinsock::throwError(int err)
{
switch (err) {
case WSAEWOULDBLOCK:
throw XArchNetworkWouldBlock(new XArchEvalWinsock(err));
case WSAEACCES:
throw XArchNetworkAccess(new XArchEvalWinsock(err));
case WSAEMFILE:
case WSAENOBUFS:
case WSAENETDOWN:
throw XArchNetworkResource(new XArchEvalWinsock(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(new XArchEvalWinsock(err));
case WSAEADDRNOTAVAIL:
throw XArchNetworkNoAddress(new XArchEvalWinsock(err));
case WSAEADDRINUSE:
throw XArchNetworkAddressInUse(new XArchEvalWinsock(err));
case WSAEHOSTUNREACH:
case WSAENETUNREACH:
throw XArchNetworkNoRoute(new XArchEvalWinsock(err));
case WSAENOTCONN:
throw XArchNetworkNotConnected(new XArchEvalWinsock(err));
case WSAEDISCON:
throw XArchNetworkShutdown(new XArchEvalWinsock(err));
case WSAENETRESET:
case WSAECONNABORTED:
case WSAECONNRESET:
throw XArchNetworkDisconnected(new XArchEvalWinsock(err));
case WSAECONNREFUSED:
throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err));
case WSAEINPROGRESS:
case WSAEALREADY:
throw XArchNetworkConnecting(new XArchEvalWinsock(err));
case WSAEHOSTDOWN:
case WSAETIMEDOUT:
throw XArchNetworkTimedOut(new XArchEvalWinsock(err));
case WSAHOST_NOT_FOUND:
throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
case WSANO_DATA:
throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
case WSANO_RECOVERY:
throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
case WSATRY_AGAIN:
throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
default:
throw XArchNetwork(new XArchEvalWinsock(err));
}
}
void
CArchNetworkWinsock::throwNameError(int err)
{
switch (err) {
case WSAHOST_NOT_FOUND:
throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
case WSANO_DATA:
throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
case WSANO_RECOVERY:
throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
case WSATRY_AGAIN:
throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
default:
throw XArchNetworkName(new XArchEvalWinsock(err));
}
}