mirror of
https://github.com/debauchee/barrier.git
synced 2026-02-08 04:45:03 +08:00
Many tools strip trailing whitespaces by default, so after editing a file with
whitespace errors we end up with a bunch of unrelated hunks that need to be
reverted locally.
This could be fixed by configuring each tool to not do this (at least for the
barrier repo), or, simpler, we just sed the problem away and make barrier
whitespace-compliant.
sed commands run:
sed -i 's/[ \t]\+$//' **/*.(cpp|h) **/*CMakeLists.txt
Verified with git diff --ignore-space-change, this shows the empty diff.
1010 lines
23 KiB
C++
1010 lines
23 KiB
C++
/*
|
|
* barrier -- mouse and keyboard sharing utility
|
|
* Copyright (C) 2012-2016 Symless Ltd.
|
|
* 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 LICENSE 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "arch/unix/ArchNetworkBSD.h"
|
|
|
|
#include "arch/unix/ArchMultithreadPosix.h"
|
|
#include "arch/unix/XArchUnix.h"
|
|
#include "arch/Arch.h"
|
|
|
|
#if HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#if !defined(TCP_NODELAY)
|
|
# include <netinet/tcp.h>
|
|
#endif
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#if HAVE_POLL
|
|
# include <poll.h>
|
|
#else
|
|
# if HAVE_SYS_SELECT_H
|
|
# include <sys/select.h>
|
|
# endif
|
|
# if HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
# endif
|
|
#endif
|
|
|
|
#if !HAVE_INET_ATON
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
static const int s_family[] = {
|
|
PF_UNSPEC,
|
|
PF_INET,
|
|
PF_INET6,
|
|
};
|
|
static const int s_type[] = {
|
|
SOCK_DGRAM,
|
|
SOCK_STREAM
|
|
};
|
|
|
|
#if !HAVE_INET_ATON
|
|
// parse dotted quad addresses. we don't bother with the weird BSD'ism
|
|
// of handling octal and hex and partial forms.
|
|
static
|
|
in_addr_t
|
|
inet_aton(const char* cp, struct in_addr* inp)
|
|
{
|
|
unsigned int a, b, c, d;
|
|
if (sscanf(cp, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
|
|
return 0;
|
|
}
|
|
if (a >= 256 || b >= 256 || c >= 256 || d >= 256) {
|
|
return 0;
|
|
}
|
|
unsigned char* incp = (unsigned char*)inp;
|
|
incp[0] = (unsigned char)(a & 0xffu);
|
|
incp[1] = (unsigned char)(b & 0xffu);
|
|
incp[2] = (unsigned char)(c & 0xffu);
|
|
incp[3] = (unsigned char)(d & 0xffu);
|
|
return inp->s_addr;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// ArchNetworkBSD
|
|
//
|
|
|
|
ArchNetworkBSD::ArchNetworkBSD()
|
|
{
|
|
}
|
|
|
|
ArchNetworkBSD::~ArchNetworkBSD()
|
|
{
|
|
ARCH->closeMutex(m_mutex);
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::init()
|
|
{
|
|
// create mutex to make some calls thread safe
|
|
m_mutex = ARCH->newMutex();
|
|
}
|
|
|
|
ArchSocket
|
|
ArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type)
|
|
{
|
|
// create socket
|
|
int fd = socket(s_family[family], s_type[type], 0);
|
|
if (fd == -1) {
|
|
throwError(errno);
|
|
}
|
|
try {
|
|
setBlockingOnSocket(fd, false);
|
|
if (family == kINET6) {
|
|
int flag = 0;
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) != 0)
|
|
throwError(errno);
|
|
}
|
|
}
|
|
catch (...) {
|
|
close(fd);
|
|
throw;
|
|
}
|
|
|
|
// allocate socket object
|
|
ArchSocketImpl* newSocket = new ArchSocketImpl;
|
|
newSocket->m_fd = fd;
|
|
newSocket->m_refCount = 1;
|
|
return newSocket;
|
|
}
|
|
|
|
ArchSocket
|
|
ArchNetworkBSD::copySocket(ArchSocket s)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
// ref the socket and return it
|
|
ARCH->lockMutex(m_mutex);
|
|
++s->m_refCount;
|
|
ARCH->unlockMutex(m_mutex);
|
|
return s;
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::closeSocket(ArchSocket 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) {
|
|
if (close(s->m_fd) == -1) {
|
|
// close failed. restore the last ref and throw.
|
|
int err = errno;
|
|
ARCH->lockMutex(m_mutex);
|
|
++s->m_refCount;
|
|
ARCH->unlockMutex(m_mutex);
|
|
throwError(err);
|
|
}
|
|
delete s;
|
|
}
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::closeSocketForRead(ArchSocket s)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
if (shutdown(s->m_fd, 0) == -1) {
|
|
if (errno != ENOTCONN) {
|
|
throwError(errno);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::closeSocketForWrite(ArchSocket s)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
if (shutdown(s->m_fd, 1) == -1) {
|
|
if (errno != ENOTCONN) {
|
|
throwError(errno);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::bindSocket(ArchSocket s, ArchNetAddress addr)
|
|
{
|
|
assert(s != NULL);
|
|
assert(addr != NULL);
|
|
|
|
if (bind(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
|
|
throwError(errno);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::listenOnSocket(ArchSocket s)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
// hardcoding backlog
|
|
if (listen(s->m_fd, 3) == -1) {
|
|
throwError(errno);
|
|
}
|
|
}
|
|
|
|
ArchSocket
|
|
ArchNetworkBSD::acceptSocket(ArchSocket s, ArchNetAddress* addr)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
// if user passed NULL in addr then use scratch space
|
|
ArchNetAddress dummy;
|
|
if (addr == NULL) {
|
|
addr = &dummy;
|
|
}
|
|
|
|
// create new socket and address
|
|
ArchSocketImpl* newSocket = new ArchSocketImpl;
|
|
*addr = new ArchNetAddressImpl;
|
|
|
|
// accept on socket
|
|
ACCEPT_TYPE_ARG3 len = (ACCEPT_TYPE_ARG3)((*addr)->m_len);
|
|
int fd = accept(s->m_fd, TYPED_ADDR(struct sockaddr, (*addr)), &len);
|
|
(*addr)->m_len = (socklen_t)len;
|
|
if (fd == -1) {
|
|
int err = errno;
|
|
delete newSocket;
|
|
delete *addr;
|
|
*addr = NULL;
|
|
if (err == EAGAIN) {
|
|
return NULL;
|
|
}
|
|
throwError(err);
|
|
}
|
|
|
|
try {
|
|
setBlockingOnSocket(fd, false);
|
|
}
|
|
catch (...) {
|
|
close(fd);
|
|
delete newSocket;
|
|
delete *addr;
|
|
*addr = NULL;
|
|
throw;
|
|
}
|
|
|
|
// initialize socket
|
|
newSocket->m_fd = fd;
|
|
newSocket->m_refCount = 1;
|
|
|
|
// discard address if not requested
|
|
if (addr == &dummy) {
|
|
ARCH->closeAddr(dummy);
|
|
}
|
|
|
|
return newSocket;
|
|
}
|
|
|
|
bool
|
|
ArchNetworkBSD::connectSocket(ArchSocket s, ArchNetAddress addr)
|
|
{
|
|
assert(s != NULL);
|
|
assert(addr != NULL);
|
|
|
|
if (connect(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
|
|
if (errno == EISCONN) {
|
|
return true;
|
|
}
|
|
if (errno == EINPROGRESS) {
|
|
return false;
|
|
}
|
|
throwError(errno);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if HAVE_POLL
|
|
|
|
int
|
|
ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
|
|
{
|
|
assert(pe != NULL || num == 0);
|
|
|
|
// return if nothing to do
|
|
if (num == 0) {
|
|
if (timeout > 0.0) {
|
|
ARCH->sleep(timeout);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// allocate space for translated query
|
|
struct pollfd* pfd = new struct pollfd[1 + num];
|
|
|
|
// translate query
|
|
for (int i = 0; i < num; ++i) {
|
|
pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd;
|
|
pfd[i].events = 0;
|
|
if ((pe[i].m_events & kPOLLIN) != 0) {
|
|
pfd[i].events |= POLLIN;
|
|
}
|
|
if ((pe[i].m_events & kPOLLOUT) != 0) {
|
|
pfd[i].events |= POLLOUT;
|
|
}
|
|
}
|
|
int n = num;
|
|
|
|
// add the unblock pipe
|
|
const int* unblockPipe = getUnblockPipe();
|
|
if (unblockPipe != NULL) {
|
|
pfd[n].fd = unblockPipe[0];
|
|
pfd[n].events = POLLIN;
|
|
++n;
|
|
}
|
|
|
|
// prepare timeout
|
|
int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
|
|
|
|
// do the poll
|
|
n = poll(pfd, n, t);
|
|
|
|
// reset the unblock pipe
|
|
if (n > 0 && unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) {
|
|
// the unblock event was signalled. flush the pipe.
|
|
char dummy[100];
|
|
int ignore;
|
|
|
|
do {
|
|
ignore = read(unblockPipe[0], dummy, sizeof(dummy));
|
|
} while (errno != EAGAIN);
|
|
|
|
// don't count this unblock pipe in return value
|
|
--n;
|
|
}
|
|
|
|
// handle results
|
|
if (n == -1) {
|
|
if (errno == EINTR) {
|
|
// interrupted system call
|
|
ARCH->testCancelThread();
|
|
delete[] pfd;
|
|
return 0;
|
|
}
|
|
delete[] pfd;
|
|
throwError(errno);
|
|
}
|
|
|
|
// translate back
|
|
for (int i = 0; i < num; ++i) {
|
|
pe[i].m_revents = 0;
|
|
if ((pfd[i].revents & POLLIN) != 0) {
|
|
pe[i].m_revents |= kPOLLIN;
|
|
}
|
|
if ((pfd[i].revents & POLLOUT) != 0) {
|
|
pe[i].m_revents |= kPOLLOUT;
|
|
}
|
|
if ((pfd[i].revents & POLLERR) != 0) {
|
|
pe[i].m_revents |= kPOLLERR;
|
|
}
|
|
if ((pfd[i].revents & POLLNVAL) != 0) {
|
|
pe[i].m_revents |= kPOLLNVAL;
|
|
}
|
|
}
|
|
|
|
delete[] pfd;
|
|
return n;
|
|
}
|
|
|
|
#else
|
|
|
|
int
|
|
ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
|
|
{
|
|
int i, n;
|
|
|
|
// prepare sets for select
|
|
n = 0;
|
|
fd_set readSet, writeSet, errSet;
|
|
fd_set* readSetP = NULL;
|
|
fd_set* writeSetP = NULL;
|
|
fd_set* errSetP = NULL;
|
|
FD_ZERO(&readSet);
|
|
FD_ZERO(&writeSet);
|
|
FD_ZERO(&errSet);
|
|
for (i = 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;
|
|
}
|
|
|
|
int fdi = pe[i].m_socket->m_fd;
|
|
if (pe[i].m_events & kPOLLIN) {
|
|
FD_SET(pe[i].m_socket->m_fd, &readSet);
|
|
readSetP = &readSet;
|
|
if (fdi > n) {
|
|
n = fdi;
|
|
}
|
|
}
|
|
if (pe[i].m_events & kPOLLOUT) {
|
|
FD_SET(pe[i].m_socket->m_fd, &writeSet);
|
|
writeSetP = &writeSet;
|
|
if (fdi > n) {
|
|
n = fdi;
|
|
}
|
|
}
|
|
if (true) {
|
|
FD_SET(pe[i].m_socket->m_fd, &errSet);
|
|
errSetP = &errSet;
|
|
if (fdi > n) {
|
|
n = fdi;
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the unblock pipe
|
|
const int* unblockPipe = getUnblockPipe();
|
|
if (unblockPipe != NULL) {
|
|
FD_SET(unblockPipe[0], &readSet);
|
|
readSetP = &readSet;
|
|
if (unblockPipe[0] > n) {
|
|
n = unblockPipe[0];
|
|
}
|
|
}
|
|
|
|
// if there are no sockets then don't block forever
|
|
if (n == 0 && timeout < 0.0) {
|
|
timeout = 0.0;
|
|
}
|
|
|
|
// prepare timeout for select
|
|
struct timeval timeout2;
|
|
struct timeval* timeout2P;
|
|
if (timeout < 0.0) {
|
|
timeout2P = NULL;
|
|
}
|
|
else {
|
|
timeout2P = &timeout2;
|
|
timeout2.tv_sec = static_cast<int>(timeout);
|
|
timeout2.tv_usec = static_cast<int>(1.0e+6 *
|
|
(timeout - timeout2.tv_sec));
|
|
}
|
|
|
|
// do the select
|
|
n = select((SELECT_TYPE_ARG1) n + 1,
|
|
SELECT_TYPE_ARG234 readSetP,
|
|
SELECT_TYPE_ARG234 writeSetP,
|
|
SELECT_TYPE_ARG234 errSetP,
|
|
SELECT_TYPE_ARG5 timeout2P);
|
|
|
|
// reset the unblock pipe
|
|
if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) {
|
|
// the unblock event was signalled. flush the pipe.
|
|
char dummy[100];
|
|
do {
|
|
read(unblockPipe[0], dummy, sizeof(dummy));
|
|
} while (errno != EAGAIN);
|
|
}
|
|
|
|
// handle results
|
|
if (n == -1) {
|
|
if (errno == EINTR) {
|
|
// interrupted system call
|
|
ARCH->testCancelThread();
|
|
return 0;
|
|
}
|
|
throwError(errno);
|
|
}
|
|
n = 0;
|
|
for (i = 0; i < num; ++i) {
|
|
if (pe[i].m_socket != NULL) {
|
|
if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) {
|
|
pe[i].m_revents |= kPOLLIN;
|
|
}
|
|
if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) {
|
|
pe[i].m_revents |= kPOLLOUT;
|
|
}
|
|
if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) {
|
|
pe[i].m_revents |= kPOLLERR;
|
|
}
|
|
}
|
|
if (pe[i].m_revents != 0) {
|
|
++n;
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
ArchNetworkBSD::unblockPollSocket(ArchThread thread)
|
|
{
|
|
const int* unblockPipe = getUnblockPipeForThread(thread);
|
|
if (unblockPipe != NULL) {
|
|
char dummy = 0;
|
|
int ignore;
|
|
|
|
ignore = write(unblockPipe[1], &dummy, 1);
|
|
}
|
|
}
|
|
|
|
size_t
|
|
ArchNetworkBSD::readSocket(ArchSocket s, void* buf, size_t len)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
ssize_t n = read(s->m_fd, buf, len);
|
|
if (n == -1) {
|
|
if (errno == EINTR || errno == EAGAIN) {
|
|
return 0;
|
|
}
|
|
throwError(errno);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
size_t
|
|
ArchNetworkBSD::writeSocket(ArchSocket s, const void* buf, size_t len)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
ssize_t n = write(s->m_fd, buf, len);
|
|
if (n == -1) {
|
|
if (errno == EINTR || errno == EAGAIN) {
|
|
return 0;
|
|
}
|
|
throwError(errno);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::throwErrorOnSocket(ArchSocket s)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
// get the error from the socket layer
|
|
int err = 0;
|
|
socklen_t size = (socklen_t)sizeof(err);
|
|
if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR,
|
|
(optval_t*)&err, &size) == -1) {
|
|
err = errno;
|
|
}
|
|
|
|
// throw if there's an error
|
|
if (err != 0) {
|
|
throwError(err);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking)
|
|
{
|
|
assert(fd != -1);
|
|
|
|
int mode = fcntl(fd, F_GETFL, 0);
|
|
if (mode == -1) {
|
|
throwError(errno);
|
|
}
|
|
if (blocking) {
|
|
mode &= ~O_NONBLOCK;
|
|
}
|
|
else {
|
|
mode |= O_NONBLOCK;
|
|
}
|
|
if (fcntl(fd, F_SETFL, mode) == -1) {
|
|
throwError(errno);
|
|
}
|
|
}
|
|
|
|
bool
|
|
ArchNetworkBSD::setNoDelayOnSocket(ArchSocket s, bool noDelay)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
// get old state
|
|
int oflag;
|
|
socklen_t size = (socklen_t)sizeof(oflag);
|
|
if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
|
|
(optval_t*)&oflag, &size) == -1) {
|
|
throwError(errno);
|
|
}
|
|
|
|
int flag = noDelay ? 1 : 0;
|
|
size = (socklen_t)sizeof(flag);
|
|
if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
|
|
(optval_t*)&flag, size) == -1) {
|
|
throwError(errno);
|
|
}
|
|
|
|
return (oflag != 0);
|
|
}
|
|
|
|
bool
|
|
ArchNetworkBSD::setReuseAddrOnSocket(ArchSocket s, bool reuse)
|
|
{
|
|
assert(s != NULL);
|
|
|
|
// get old state
|
|
int oflag;
|
|
socklen_t size = (socklen_t)sizeof(oflag);
|
|
if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
|
|
(optval_t*)&oflag, &size) == -1) {
|
|
throwError(errno);
|
|
}
|
|
|
|
int flag = reuse ? 1 : 0;
|
|
size = (socklen_t)sizeof(flag);
|
|
if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
|
|
(optval_t*)&flag, size) == -1) {
|
|
throwError(errno);
|
|
}
|
|
|
|
return (oflag != 0);
|
|
}
|
|
|
|
std::string
|
|
ArchNetworkBSD::getHostName()
|
|
{
|
|
char name[256];
|
|
if (gethostname(name, sizeof(name)) == -1) {
|
|
name[0] = '\0';
|
|
}
|
|
else {
|
|
name[sizeof(name) - 1] = '\0';
|
|
}
|
|
return name;
|
|
}
|
|
|
|
ArchNetAddress
|
|
ArchNetworkBSD::newAnyAddr(EAddressFamily family)
|
|
{
|
|
// allocate address
|
|
ArchNetAddressImpl* addr = new ArchNetAddressImpl;
|
|
|
|
// fill it in
|
|
switch (family) {
|
|
case kINET: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
|
memset(ipAddr, 0, sizeof(struct sockaddr_in));
|
|
ipAddr->sin_family = AF_INET;
|
|
ipAddr->sin_addr.s_addr = INADDR_ANY;
|
|
addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
|
|
break;
|
|
}
|
|
|
|
case kINET6: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
|
memset(ipAddr, 0, sizeof(struct sockaddr_in6));
|
|
ipAddr->sin6_family = AF_INET6;
|
|
memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
|
|
addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
|
|
break;
|
|
}
|
|
default:
|
|
delete addr;
|
|
assert(0 && "invalid family");
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
ArchNetAddress
|
|
ArchNetworkBSD::copyAddr(ArchNetAddress addr)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
// allocate and copy address
|
|
return new ArchNetAddressImpl(*addr);
|
|
}
|
|
|
|
ArchNetAddress
|
|
ArchNetworkBSD::nameToAddr(const std::string& name)
|
|
{
|
|
// allocate address
|
|
ArchNetAddressImpl* addr = new ArchNetAddressImpl;
|
|
|
|
struct addrinfo hints;
|
|
struct addrinfo *p;
|
|
int ret;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
ARCH->lockMutex(m_mutex);
|
|
if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &p)) != 0) {
|
|
ARCH->unlockMutex(m_mutex);
|
|
delete addr;
|
|
throwNameError(ret);
|
|
}
|
|
|
|
if (p->ai_family == AF_INET) {
|
|
addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
|
|
} else {
|
|
addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
|
|
}
|
|
|
|
memcpy(&addr->m_addr, p->ai_addr, addr->m_len);
|
|
freeaddrinfo(p);
|
|
ARCH->unlockMutex(m_mutex);
|
|
|
|
return addr;
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::closeAddr(ArchNetAddress addr)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
delete addr;
|
|
}
|
|
|
|
std::string
|
|
ArchNetworkBSD::addrToName(ArchNetAddress addr)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
// mutexed name lookup (ugh)
|
|
ARCH->lockMutex(m_mutex);
|
|
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) {
|
|
ARCH->unlockMutex(m_mutex);
|
|
throwNameError(ret);
|
|
}
|
|
|
|
// save (primary) name
|
|
std::string name = host;
|
|
|
|
// done with static buffer
|
|
ARCH->unlockMutex(m_mutex);
|
|
|
|
return name;
|
|
}
|
|
|
|
std::string
|
|
ArchNetworkBSD::addrToString(ArchNetAddress addr)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
switch (getAddrFamily(addr)) {
|
|
case kINET: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
|
ARCH->lockMutex(m_mutex);
|
|
std::string s = inet_ntoa(ipAddr->sin_addr);
|
|
ARCH->unlockMutex(m_mutex);
|
|
return s;
|
|
}
|
|
|
|
case kINET6: {
|
|
char strAddr[INET6_ADDRSTRLEN];
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
|
ARCH->lockMutex(m_mutex);
|
|
inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
|
|
ARCH->unlockMutex(m_mutex);
|
|
return strAddr;
|
|
}
|
|
|
|
default:
|
|
assert(0 && "unknown address family");
|
|
return "";
|
|
}
|
|
}
|
|
|
|
IArchNetwork::EAddressFamily
|
|
ArchNetworkBSD::getAddrFamily(ArchNetAddress addr)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
switch (addr->m_addr.ss_family) {
|
|
case AF_INET:
|
|
return kINET;
|
|
case AF_INET6:
|
|
return kINET6;
|
|
|
|
default:
|
|
return kUNKNOWN;
|
|
}
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
switch (getAddrFamily(addr)) {
|
|
case kINET: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
|
ipAddr->sin_port = htons(port);
|
|
break;
|
|
}
|
|
|
|
case kINET6: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
|
ipAddr->sin6_port = htons(port);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
assert(0 && "unknown address family");
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
ArchNetworkBSD::getAddrPort(ArchNetAddress addr)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
switch (getAddrFamily(addr)) {
|
|
case kINET: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
|
return ntohs(ipAddr->sin_port);
|
|
}
|
|
|
|
case kINET6: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
|
return ntohs(ipAddr->sin6_port);
|
|
}
|
|
|
|
default:
|
|
assert(0 && "unknown address family");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool
|
|
ArchNetworkBSD::isAnyAddr(ArchNetAddress addr)
|
|
{
|
|
assert(addr != NULL);
|
|
|
|
switch (getAddrFamily(addr)) {
|
|
case kINET: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
|
return (ipAddr->sin_addr.s_addr == INADDR_ANY &&
|
|
addr->m_len == (socklen_t)sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
case kINET6: {
|
|
auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
|
return (memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
|
|
addr->m_len == (socklen_t)sizeof(struct sockaddr_in6));
|
|
}
|
|
|
|
default:
|
|
assert(0 && "unknown address family");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
ArchNetworkBSD::isEqualAddr(ArchNetAddress a, ArchNetAddress b)
|
|
{
|
|
return (a->m_len == b->m_len &&
|
|
memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0);
|
|
}
|
|
|
|
const int*
|
|
ArchNetworkBSD::getUnblockPipe()
|
|
{
|
|
ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance();
|
|
ArchThread thread = mt->newCurrentThread();
|
|
const int* p = getUnblockPipeForThread(thread);
|
|
ARCH->closeThread(thread);
|
|
return p;
|
|
}
|
|
|
|
const int*
|
|
ArchNetworkBSD::getUnblockPipeForThread(ArchThread thread)
|
|
{
|
|
ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance();
|
|
int* unblockPipe = (int*)mt->getNetworkDataForThread(thread);
|
|
if (unblockPipe == NULL) {
|
|
unblockPipe = new int[2];
|
|
if (pipe(unblockPipe) != -1) {
|
|
try {
|
|
setBlockingOnSocket(unblockPipe[0], false);
|
|
mt->setNetworkDataForCurrentThread(unblockPipe);
|
|
}
|
|
catch (...) {
|
|
delete[] unblockPipe;
|
|
unblockPipe = NULL;
|
|
}
|
|
}
|
|
else {
|
|
delete[] unblockPipe;
|
|
unblockPipe = NULL;
|
|
}
|
|
}
|
|
return unblockPipe;
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::throwError(int err)
|
|
{
|
|
switch (err) {
|
|
case EINTR:
|
|
ARCH->testCancelThread();
|
|
throw XArchNetworkInterrupted(new XArchEvalUnix(err));
|
|
|
|
case EACCES:
|
|
case EPERM:
|
|
throw XArchNetworkAccess(new XArchEvalUnix(err));
|
|
|
|
case ENFILE:
|
|
case EMFILE:
|
|
case ENODEV:
|
|
case ENOBUFS:
|
|
case ENOMEM:
|
|
case ENETDOWN:
|
|
#if defined(ENOSR)
|
|
case ENOSR:
|
|
#endif
|
|
throw XArchNetworkResource(new XArchEvalUnix(err));
|
|
|
|
case EPROTOTYPE:
|
|
case EPROTONOSUPPORT:
|
|
case EAFNOSUPPORT:
|
|
case EPFNOSUPPORT:
|
|
case ESOCKTNOSUPPORT:
|
|
case EINVAL:
|
|
case ENOPROTOOPT:
|
|
case EOPNOTSUPP:
|
|
case ESHUTDOWN:
|
|
#if defined(ENOPKG)
|
|
case ENOPKG:
|
|
#endif
|
|
throw XArchNetworkSupport(new XArchEvalUnix(err));
|
|
|
|
case EIO:
|
|
throw XArchNetworkIO(new XArchEvalUnix(err));
|
|
|
|
case EADDRNOTAVAIL:
|
|
throw XArchNetworkNoAddress(new XArchEvalUnix(err));
|
|
|
|
case EADDRINUSE:
|
|
throw XArchNetworkAddressInUse(new XArchEvalUnix(err));
|
|
|
|
case EHOSTUNREACH:
|
|
case ENETUNREACH:
|
|
throw XArchNetworkNoRoute(new XArchEvalUnix(err));
|
|
|
|
case ENOTCONN:
|
|
throw XArchNetworkNotConnected(new XArchEvalUnix(err));
|
|
|
|
case EPIPE:
|
|
throw XArchNetworkShutdown(new XArchEvalUnix(err));
|
|
|
|
case ECONNABORTED:
|
|
case ECONNRESET:
|
|
throw XArchNetworkDisconnected(new XArchEvalUnix(err));
|
|
|
|
case ECONNREFUSED:
|
|
throw XArchNetworkConnectionRefused(new XArchEvalUnix(err));
|
|
|
|
case EHOSTDOWN:
|
|
case ETIMEDOUT:
|
|
throw XArchNetworkTimedOut(new XArchEvalUnix(err));
|
|
|
|
default:
|
|
throw XArchNetwork(new XArchEvalUnix(err));
|
|
}
|
|
}
|
|
|
|
void
|
|
ArchNetworkBSD::throwNameError(int err)
|
|
{
|
|
static const char* s_msg[] = {
|
|
"The specified host is unknown",
|
|
"The requested name is valid but does not have an IP address",
|
|
"A non-recoverable name server error occurred",
|
|
"A temporary error occurred on an authoritative name server",
|
|
"An unknown name server error occurred"
|
|
};
|
|
|
|
switch (err) {
|
|
case HOST_NOT_FOUND:
|
|
throw XArchNetworkNameUnknown(s_msg[0]);
|
|
|
|
case NO_DATA:
|
|
throw XArchNetworkNameNoAddress(s_msg[1]);
|
|
|
|
case NO_RECOVERY:
|
|
throw XArchNetworkNameFailure(s_msg[2]);
|
|
|
|
case TRY_AGAIN:
|
|
throw XArchNetworkNameUnavailable(s_msg[3]);
|
|
|
|
default:
|
|
throw XArchNetworkName(s_msg[4]);
|
|
}
|
|
}
|