|
|
|
|
@ -48,39 +48,67 @@
|
|
|
|
|
#include <netinet/tcp.h> |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#include <list> |
|
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) |
|
|
|
|
#define ZT_SELECTWIRE_SOCKFD_TYPE SOCKET |
|
|
|
|
#define ZT_SELECTWIRE_SOCKFD_NULL (INVALID_SOCKET) |
|
|
|
|
#define ZT_SELECTWIRE_SOCKFD_VALID(s) ((s) != INVALID_SOCKET) |
|
|
|
|
#define ZT_SELECTWIRE_CLOSE_SOCKET(s) ::closesocket(s) |
|
|
|
|
#define ZT_SELECTWIRE_MAX_SOCKETS (FD_SETSIZE) |
|
|
|
|
#define ZT_SELECTWIRE_SOCKADDR_STORAGE_TYPE struct sockaddr_storage |
|
|
|
|
#define ZT_WIRE_SOCKFD_TYPE SOCKET |
|
|
|
|
#define ZT_WIRE_SOCKFD_NULL (INVALID_SOCKET) |
|
|
|
|
#define ZT_WIRE_SOCKFD_VALID(s) ((s) != INVALID_SOCKET) |
|
|
|
|
#define ZT_WIRE_CLOSE_SOCKET(s) ::closesocket(s) |
|
|
|
|
#define ZT_WIRE_MAX_SOCKETS (FD_SETSIZE) |
|
|
|
|
#define ZT_WIRE_SOCKADDR_STORAGE_TYPE struct sockaddr_storage |
|
|
|
|
#else |
|
|
|
|
#define ZT_SELECTWIRE_SOCKFD_TYPE int |
|
|
|
|
#define ZT_SELECTWIRE_SOCKFD_NULL (-1) |
|
|
|
|
#define ZT_SELECTWIRE_SOCKFD_VALID(s) ((s) > -1) |
|
|
|
|
#define ZT_SELECTWIRE_CLOSE_SOCKET(s) ::close(s) |
|
|
|
|
#define ZT_SELECTWIRE_MAX_SOCKETS (FD_SETSIZE) |
|
|
|
|
#define ZT_SELECTWIRE_SOCKADDR_STORAGE_TYPE struct sockaddr_storage |
|
|
|
|
#define ZT_WIRE_SOCKFD_TYPE int |
|
|
|
|
#define ZT_WIRE_SOCKFD_NULL (-1) |
|
|
|
|
#define ZT_WIRE_SOCKFD_VALID(s) ((s) > -1) |
|
|
|
|
#define ZT_WIRE_CLOSE_SOCKET(s) ::close(s) |
|
|
|
|
#define ZT_WIRE_MAX_SOCKETS (FD_SETSIZE) |
|
|
|
|
#define ZT_WIRE_SOCKADDR_STORAGE_TYPE struct sockaddr_storage |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
namespace ZeroTier { |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Wire implementation using select() for *nix or Windows |
|
|
|
|
* Opaque socket type |
|
|
|
|
*/ |
|
|
|
|
typedef const void * WireSocket; |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Simple templated non-blocking sockets implementation |
|
|
|
|
* |
|
|
|
|
* Yes there is boost::asio and libuv, but I like small binaries and I hate |
|
|
|
|
* build dependencies. |
|
|
|
|
* |
|
|
|
|
* This implementation takes four functions or function objects as template |
|
|
|
|
* paramters: |
|
|
|
|
* |
|
|
|
|
* ON_DATAGRAM_FUNCTION(const void *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) |
|
|
|
|
* ON_TCP_CONNECT_FUNCTION(const void *sock,void **uptr,bool success) |
|
|
|
|
* ON_TCP_ACCEPT_FUNCTION(const void *sockL,const void *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) |
|
|
|
|
* ON_TCP_CLOSE_FUNCTION(const void *sock,void *uptr) |
|
|
|
|
* ON_TCP_DATA_FUNCTION(const void *sock,void **uptr,void *data,unsigned long len) |
|
|
|
|
* ON_TCP_WRITABLE_FUNCTION(const void *sock,void **uptr) |
|
|
|
|
* ON_DATAGRAM_FUNCTION(WireSocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) |
|
|
|
|
* ON_TCP_CONNECT_FUNCTION(WireSocket *sock,void **uptr,bool success) |
|
|
|
|
* ON_TCP_ACCEPT_FUNCTION(WireSocket *sockL,WireSocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) |
|
|
|
|
* ON_TCP_CLOSE_FUNCTION(WireSocket *sock,void **uptr) |
|
|
|
|
* ON_TCP_DATA_FUNCTION(WireSocket *sock,void **uptr,void *data,unsigned long len) |
|
|
|
|
* ON_TCP_WRITABLE_FUNCTION(WireSocket *sock,void **uptr) |
|
|
|
|
* |
|
|
|
|
* These templates typically refer to function objects. Templates are used to |
|
|
|
|
* avoid the call overhead of indirection. |
|
|
|
|
* avoid the call overhead of indirection, which is surprisingly high for high |
|
|
|
|
* bandwidth applications pushing a lot of packets. |
|
|
|
|
* |
|
|
|
|
* The 'sock' pointer above is an opaque pointer to a socket. Each socket |
|
|
|
|
* has a 'uptr' user-settable/modifiable pointer associated with it, which |
|
|
|
|
* can be set on bind/connect calls and is passed as a void ** to permit |
|
|
|
|
* resetting at any time. The ACCEPT handler takes two sets of sock and |
|
|
|
|
* uptr: sockL and uptrL for the listen socket, and sockN and uptrN for |
|
|
|
|
* the new TCP connection socket that has just been created. |
|
|
|
|
* |
|
|
|
|
* Handlers are always called. On outgoing TCP connection, CONNECT is always |
|
|
|
|
* called on either success or failure followed by DATA and/or WRITABLE as |
|
|
|
|
* indicated. On socket close, handlers are called unless close() is told |
|
|
|
|
* explicitly not to call handlers. It is safe to close a socket within a |
|
|
|
|
* handler, and in that case close() can be told not to call handlers to |
|
|
|
|
* prevent recursion. |
|
|
|
|
* |
|
|
|
|
* This isn't thread-safe with the exception of whack(), which is safe to |
|
|
|
|
* call from another thread to abort poll(). |
|
|
|
|
*/ |
|
|
|
|
template |
|
|
|
|
< |
|
|
|
|
@ -117,36 +145,31 @@ public:
|
|
|
|
|
_tcpDataHandler(tcpDataHandler), |
|
|
|
|
_tcpWritableHandler(tcpWritableHandler) |
|
|
|
|
{ |
|
|
|
|
for(unsigned lont i=0;i<ZT_SELECTWIRE_MAX_SOCKETS;++i) |
|
|
|
|
_socks[i].type = ZT_WIRE_SOCKET_NULL; |
|
|
|
|
|
|
|
|
|
FD_ZERO(&_readfds); |
|
|
|
|
FD_ZERO(&_writefds); |
|
|
|
|
FD_ZERO(&_exceptfds); |
|
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) |
|
|
|
|
SOCKET pipes[2]; |
|
|
|
|
_winPipeHack(pipes); |
|
|
|
|
this->_winPipeHack(pipes); |
|
|
|
|
#else |
|
|
|
|
int pipes[2]; |
|
|
|
|
if (::pipe(pipes)) |
|
|
|
|
throw std::runtime_error("unable to create pipes for select() abort"); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
_nfds = (pipes[0] > pipes[1]) ? (long)pipes[0] : (long)pipes[1]; |
|
|
|
|
_whackReceiveSocket = pipes[0]; |
|
|
|
|
_whackSendSocket = pipes[1]; |
|
|
|
|
|
|
|
|
|
_noDelay = noDelay; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
~Wire() |
|
|
|
|
{ |
|
|
|
|
for(unsigned long i=0;i<_nsocks;++i) { |
|
|
|
|
if (_socks[i].type != ZT_WIRE_SOCKET_NULL) |
|
|
|
|
this->close(_socks[i],true); |
|
|
|
|
} |
|
|
|
|
ZT_SELECTWIRE_CLOSE_SOCKET(_whackReceiveSocket); |
|
|
|
|
ZT_SELECTWIRE_CLOSE_SOCKET(_whackSendSocket); |
|
|
|
|
while (!_socks.empty()) |
|
|
|
|
this->close((WireSocket *)&(_socks.front()),true); |
|
|
|
|
ZT_WIRE_CLOSE_SOCKET(_whackReceiveSocket); |
|
|
|
|
ZT_WIRE_CLOSE_SOCKET(_whackSendSocket); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -157,90 +180,112 @@ public:
|
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) |
|
|
|
|
::send(_whackSendSocket,(const char *)this,1,0); |
|
|
|
|
#else |
|
|
|
|
::write(_whackSendSocket,(const void *)this,1); |
|
|
|
|
::write(_whackSendSocket,(WireSocket *)this,1); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return Number of open sockets |
|
|
|
|
*/ |
|
|
|
|
inline unsigned long count() const throw() { return _socks.size(); } |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return Maximum number of sockets allowed |
|
|
|
|
*/ |
|
|
|
|
inline unsigned long maxCount() const throw() { return ZT_WIRE_MAX_SOCKETS; } |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bind a UDP socket |
|
|
|
|
* |
|
|
|
|
* @param localAddress Local endpoint address and port |
|
|
|
|
* @param uptr Initial value of user pointer associated with this socket |
|
|
|
|
* @return Socket (as opaque pointer) or NULL on failure |
|
|
|
|
* @param bufferSize Desired socket receive/send buffer size -- will set as close to this as possible (0 to accept default) |
|
|
|
|
* @return Socket or NULL on failure to bind |
|
|
|
|
*/ |
|
|
|
|
inline const void *udpBind(const struct sockaddr *localAddress,void *uptr) |
|
|
|
|
inline WireSocket *udpBind(const struct sockaddr *localAddress,void *uptr,int bufferSize) |
|
|
|
|
{ |
|
|
|
|
ZT_SELECTWIRE_SOCKFD_TYPE s = ::socket(AF_INET6,SOCK_DGRAM,0); |
|
|
|
|
if (!ZT_SELECTWIRE_SOCKFD_VALID(s)) |
|
|
|
|
return (const void *)0; |
|
|
|
|
|
|
|
|
|
int bs = 262144; |
|
|
|
|
while (bs >= 65536) { |
|
|
|
|
int tmpbs = bs; |
|
|
|
|
if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) |
|
|
|
|
break; |
|
|
|
|
bs -= 16384; |
|
|
|
|
} |
|
|
|
|
bs = 262144; |
|
|
|
|
while (bs >= 65536) { |
|
|
|
|
int tmpbs = bs; |
|
|
|
|
if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) |
|
|
|
|
break; |
|
|
|
|
bs -= 16384; |
|
|
|
|
if (_socks.size() >= ZT_WIRE_MAX_SOCKETS) |
|
|
|
|
return (WireSocket *)0; |
|
|
|
|
|
|
|
|
|
ZT_WIRE_SOCKFD_TYPE s = ::socket(localAddress->sa_family,SOCK_DGRAM,0); |
|
|
|
|
if (!ZT_WIRE_SOCKFD_VALID(s)) |
|
|
|
|
return (WireSocket *)0; |
|
|
|
|
|
|
|
|
|
if (bufferSize > 0) { |
|
|
|
|
int bs = bufferSize; |
|
|
|
|
while (bs >= 65536) { |
|
|
|
|
int tmpbs = bs; |
|
|
|
|
if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) |
|
|
|
|
break; |
|
|
|
|
bs -= 16384; |
|
|
|
|
} |
|
|
|
|
bs = bufferSize; |
|
|
|
|
while (bs >= 65536) { |
|
|
|
|
int tmpbs = bs; |
|
|
|
|
if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) |
|
|
|
|
break; |
|
|
|
|
bs -= 16384; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) |
|
|
|
|
BOOL f; |
|
|
|
|
if (localAddress->ss_family == AF_INET6) { |
|
|
|
|
f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); |
|
|
|
|
f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f)); |
|
|
|
|
{ |
|
|
|
|
BOOL f; |
|
|
|
|
if (localAddress->sa_family == AF_INET6) { |
|
|
|
|
f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); |
|
|
|
|
f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f)); |
|
|
|
|
} |
|
|
|
|
f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); |
|
|
|
|
f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f)); |
|
|
|
|
} |
|
|
|
|
f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); |
|
|
|
|
f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f)); |
|
|
|
|
#else // not Windows
|
|
|
|
|
int f; |
|
|
|
|
if (localAddress->ss_family == AF_INET6) { |
|
|
|
|
f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); |
|
|
|
|
{ |
|
|
|
|
int f; |
|
|
|
|
if (localAddress->sa_family == AF_INET6) { |
|
|
|
|
f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); |
|
|
|
|
#ifdef IPV6_MTU_DISCOVER |
|
|
|
|
f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); |
|
|
|
|
f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); |
|
|
|
|
f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f)); |
|
|
|
|
} |
|
|
|
|
f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); |
|
|
|
|
f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f)); |
|
|
|
|
#ifdef IP_DONTFRAG |
|
|
|
|
f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); |
|
|
|
|
f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); |
|
|
|
|
#endif |
|
|
|
|
#ifdef IP_MTU_DISCOVER |
|
|
|
|
f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); |
|
|
|
|
f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
#endif // Windows or not
|
|
|
|
|
|
|
|
|
|
if (::bind(s,localAddress,(localAddress->ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { |
|
|
|
|
ZT_SELECTWIRE_CLOSE_SOCKET(s); |
|
|
|
|
return (const void *)0; |
|
|
|
|
if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { |
|
|
|
|
ZT_WIRE_CLOSE_SOCKET(s); |
|
|
|
|
return (WireSocket *)0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) |
|
|
|
|
u_long iMode=1; |
|
|
|
|
ioctlsocket(s,FIONBIO,&iMode); |
|
|
|
|
{ u_long iMode=1; ioctlsocket(s,FIONBIO,&iMode); } |
|
|
|
|
#else |
|
|
|
|
fcntl(s,F_SETFL,O_NONBLOCK); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
for(unsigned long i=0;i<ZT_SELECTWIRE_MAX_SOCKETS;++i) { |
|
|
|
|
if (_socks[i].type == ZT_WIRE_SOCKET_NULL) { |
|
|
|
|
if ((long)s > _nfds) |
|
|
|
|
_nfds = (long)s; |
|
|
|
|
FD_SET(s,&_readfds); |
|
|
|
|
_socks[i].type = ZT_WIRE_SOCKET_UDP; |
|
|
|
|
_socks[i].sock = s; |
|
|
|
|
_socks[i].uptr = uptr; |
|
|
|
|
memcpy(&(_socks[i].saddr),localAddress,sizeof(struct sockaddr_storage)); |
|
|
|
|
return (const void *)&(_socks[i]); |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
_socks.push_back(WireSocketImpl()); |
|
|
|
|
} catch ( ... ) { |
|
|
|
|
ZT_WIRE_CLOSE_SOCKET(s); |
|
|
|
|
return (WireSocket *)0; |
|
|
|
|
} |
|
|
|
|
WireSocketImpl &sws = _socks.back(); |
|
|
|
|
|
|
|
|
|
ZT_SELECTWIRE_CLOSE_SOCKET(s); |
|
|
|
|
return (const void *)0; |
|
|
|
|
if ((long)s > _nfds) |
|
|
|
|
_nfds = (long)s; |
|
|
|
|
FD_SET(s,&_readfds); |
|
|
|
|
sws.type = ZT_WIRE_SOCKET_UDP; |
|
|
|
|
sws.sock = s; |
|
|
|
|
sws.uptr = uptr; |
|
|
|
|
memcpy(&(sws.saddr),localAddress,sizeof(struct sockaddr_storage)); |
|
|
|
|
|
|
|
|
|
return (WireSocket *)&sws; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -253,23 +298,53 @@ public:
|
|
|
|
|
* @param len Length of packet |
|
|
|
|
* @return True if packet appears to have been sent successfully |
|
|
|
|
*/ |
|
|
|
|
inline bool udpSend(const void *sock,const struct sockaddr *addr,unsigned int addrlen,const void *data,unsigned long len) |
|
|
|
|
inline bool udpSend(WireSocket *sock,const struct sockaddr *addr,unsigned int addrlen,WireSocket *data,unsigned long len) |
|
|
|
|
{ |
|
|
|
|
WireSocket &sws = *(const_cast <WireSocket *>(reinterpret_cast<const WireSocket *>(sock))); |
|
|
|
|
WireSocketImpl &sws = *(const_cast <WireSocketImpl *>(reinterpret_cast<const WireSocketImpl *>(sock))); |
|
|
|
|
return ((long)::sendto(sws.sock,data,len,0,addr,(socklen_t)addrlen) == (long)len); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline const void *tcpListen(const struct sockaddr *localAddress,void *uptr) |
|
|
|
|
/**
|
|
|
|
|
* Bind a local listen socket to listen for new TCP connections |
|
|
|
|
* |
|
|
|
|
* @param localAddress Local address and port |
|
|
|
|
* @param uptr Initial value of uptr for new socket |
|
|
|
|
* @return Socket or NULL on failure to bind |
|
|
|
|
*/ |
|
|
|
|
inline WireSocket *tcpListen(const struct sockaddr *localAddress,void *uptr) |
|
|
|
|
{ |
|
|
|
|
if (_socks.size() >= ZT_WIRE_MAX_SOCKETS) |
|
|
|
|
return (WireSocket *)0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline const void *tcpConnect(const struct sockaddr *remoteAddress,void *uptr) |
|
|
|
|
/**
|
|
|
|
|
* Start a non-blocking connect; CONNECT handler is called on success or failure |
|
|
|
|
* |
|
|
|
|
* Note that if NULL is returned here, the handler is not called. Such |
|
|
|
|
* a return would indicate failure to allocate the socket, too many |
|
|
|
|
* open sockets, etc. |
|
|
|
|
* |
|
|
|
|
* @param remoteAddress Remote address |
|
|
|
|
* @param uptr Initial value of uptr for new socket |
|
|
|
|
* @return New socket or NULL on failure |
|
|
|
|
*/ |
|
|
|
|
inline WireSocket *tcpConnect(const struct sockaddr *remoteAddress,void *uptr) |
|
|
|
|
{ |
|
|
|
|
if (_socks.size() >= ZT_WIRE_MAX_SOCKETS) |
|
|
|
|
return (WireSocket *)0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline unsigned long tcpSend(const void *sock,const void *data,unsigned long len) |
|
|
|
|
/**
|
|
|
|
|
* Attempt to send data to a TCP connection (non-blocking) |
|
|
|
|
* |
|
|
|
|
* @param sock An open TCP socket (other socket types will fail) |
|
|
|
|
* @param data Data to send |
|
|
|
|
* @param len Length of data |
|
|
|
|
* @return Number of bytes actually sent or 0 on failure |
|
|
|
|
*/ |
|
|
|
|
inline unsigned long tcpSend(WireSocket *sock,WireSocket *data,unsigned long len) |
|
|
|
|
{ |
|
|
|
|
WireSocket &sws = *(const_cast <WireSocket *>(reinterpret_cast<const WireSocket *>(sock))); |
|
|
|
|
WireSocketImpl &sws = *(const_cast <WireSocketImpl *>(reinterpret_cast<const WireSocketImpl *>(sock))); |
|
|
|
|
long n = ::send(sws.sock,data,len,0); |
|
|
|
|
return ((n > 0) ? (unsigned long)n : 0); |
|
|
|
|
} |
|
|
|
|
@ -277,12 +352,16 @@ public:
|
|
|
|
|
/**
|
|
|
|
|
* Set whether we want to be notified via the TCP writability handler when a socket is writable |
|
|
|
|
* |
|
|
|
|
* Call whack() if this is being done from another thread and you want |
|
|
|
|
* it to take effect immediately. Otherwise it is only guaranteed to |
|
|
|
|
* take effect on the next poll(). |
|
|
|
|
* |
|
|
|
|
* @param sock TCP connection socket (other types are not valid) |
|
|
|
|
* @param notifyWritable Want writable notifications? |
|
|
|
|
*/ |
|
|
|
|
inline const void tcpSetNotifyWritable(const void *sock,bool notifyWritable) |
|
|
|
|
inline const void tcpSetNotifyWritable(WireSocket *sock,bool notifyWritable) |
|
|
|
|
{ |
|
|
|
|
WireSocket &sws = *(const_cast <WireSocket *>(reinterpret_cast<const WireSocket *>(sock))); |
|
|
|
|
WireSocketImpl &sws = *(const_cast <WireSocketImpl *>(reinterpret_cast<const WireSocketImpl *>(sock))); |
|
|
|
|
if (notifyWritable) { |
|
|
|
|
FD_SET(sws.sock,&_writefds); |
|
|
|
|
} else { |
|
|
|
|
@ -290,6 +369,15 @@ public:
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Wait for activity and handle one or more events |
|
|
|
|
* |
|
|
|
|
* Note that this is not guaranteed to wait up to 'timeout' even |
|
|
|
|
* if nothing happens, as whack() or other events such as signals |
|
|
|
|
* may cause premature termination. |
|
|
|
|
* |
|
|
|
|
* @param timeout Timeout in milliseconds or 0 for none (forever) |
|
|
|
|
*/ |
|
|
|
|
inline void poll(unsigned long timeout) |
|
|
|
|
{ |
|
|
|
|
char buf[131072]; |
|
|
|
|
@ -314,89 +402,83 @@ public:
|
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for(unsigned long i=0;i<ZT_SELECTWIRE_MAX_SOCKETS;++i) { |
|
|
|
|
switch (_socks[i].type) { |
|
|
|
|
for(std::list<WireSocketImpl>::iterator s(_socks.begin());s!=_socks.end();++s) { |
|
|
|
|
switch (s->type) { |
|
|
|
|
case ZT_WIRE_SOCKET_TCP_OUT_PENDING: |
|
|
|
|
if (FD_ISSET(_socks[i].sock,&efds)) |
|
|
|
|
this->close((const void *)&(_socks[i]),true); |
|
|
|
|
else if (FD_ISSET(_socks[i].sock,&wfds)) { |
|
|
|
|
if (FD_ISSET(s->sock,&efds)) |
|
|
|
|
this->close((WireSocket *)&(_socks[i]),true); |
|
|
|
|
else if (FD_ISSET(s->sock,&wfds)) { |
|
|
|
|
socklen_t slen = sizeof(ss); |
|
|
|
|
if (::getpeername(_socks[i].sock,(strut sockaddr *)&ss,&slen) != 0) |
|
|
|
|
this->close((const void *)&(_socks[i]),true); |
|
|
|
|
if (::getpeername(s->sock,(strut sockaddr *)&ss,&slen) != 0) |
|
|
|
|
this->close((WireSocket *)&(_socks[i]),true); |
|
|
|
|
else { |
|
|
|
|
_socks[i].type = ZT_WIRE_SOCKET_TCP_OUT_CONNECTED; |
|
|
|
|
FD_SET(_socks[i].sock,&_readfds); |
|
|
|
|
FD_CLR(_socks[i].sock,&_writefds); |
|
|
|
|
FD_CLR(_socks[i].sock,&_exceptfds); |
|
|
|
|
s->type = ZT_WIRE_SOCKET_TCP_OUT_CONNECTED; |
|
|
|
|
FD_SET(s->sock,&_readfds); |
|
|
|
|
FD_CLR(s->sock,&_writefds); |
|
|
|
|
FD_CLR(s->sock,&_exceptfds); |
|
|
|
|
try { |
|
|
|
|
_tcpConnectHandler((const void *)&(_socks[i]),&(_socks[i].uptr),true); |
|
|
|
|
_tcpConnectHandler((WireSocket *)&(_socks[i]),&(s->uptr),true); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ZT_WIRE_SOCKET_TCP_OUT_CONNECTED: |
|
|
|
|
case ZT_WIRE_SOCKET_TCP_IN: |
|
|
|
|
if (FD_ISSET(_socks[i].sock,&rfds)) { |
|
|
|
|
long n = (long)::recv(_socks[i].sock,buf,sizeof(buf),0); |
|
|
|
|
if (FD_ISSET(s->sock,&rfds)) { |
|
|
|
|
long n = (long)::recv(s->sock,buf,sizeof(buf),0); |
|
|
|
|
if (n <= 0) { |
|
|
|
|
this->close((const void *)&(_socks[i]),true); |
|
|
|
|
this->close((WireSocket *)&(_socks[i]),true); |
|
|
|
|
} else { |
|
|
|
|
try { |
|
|
|
|
_tcpDataHandler((const void *)&(_socks[i]),&(_socks[i].uptr),(void *)buf,(unsigned long)n); |
|
|
|
|
_tcpDataHandler((WireSocket *)&(_socks[i]),&(s->uptr),(void *)buf,(unsigned long)n); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if ((FD_ISSET(_socks[i].sock,&wfds))&&(FD_ISSET(_socks[i].sock,&_writefds))) { |
|
|
|
|
if ((FD_ISSET(s->sock,&wfds))&&(FD_ISSET(s->sock,&_writefds))) { |
|
|
|
|
try { |
|
|
|
|
_tcpWritableHandler((const void *)&(_socks[i]),&(_socks[i].uptr)); |
|
|
|
|
_tcpWritableHandler((WireSocket *)&(_socks[i]),&(s->uptr)); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ZT_WIRE_SOCKET_TCP_LISTEN: |
|
|
|
|
if (FD_ISSET(_socks[i].sock,&rfds)) { |
|
|
|
|
if (FD_ISSET(s->sock,&rfds)) { |
|
|
|
|
memset(&ss,0,sizeof(ss)); |
|
|
|
|
socklen_t slen = sizeof(ss); |
|
|
|
|
ZT_SELECTWIRE_SOCKFD_TYPE s = ::accept(_socks[i].sock,(struct sockaddr *)&ss,&slen); |
|
|
|
|
if (ZT_SELECTWIRE_SOCKFD_VALID(s)) { |
|
|
|
|
ZT_WIRE_SOCKFD_TYPE newSock = ::accept(s->sock,(struct sockaddr *)&ss,&slen); |
|
|
|
|
if (ZT_WIRE_SOCKFD_VALID(newSock)) { |
|
|
|
|
if (_socks.size() >= ZT_WIRE_MAX_SOCKETS) { |
|
|
|
|
ZT_WIRE_CLOSE_SOCKET(newSock); |
|
|
|
|
} else { |
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) |
|
|
|
|
if (_noDelay) { BOOL f = TRUE; setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } |
|
|
|
|
u_long iMode=1; |
|
|
|
|
ioctlsocket(s,FIONBIO,&iMode); |
|
|
|
|
{ BOOL f = (_noDelay ? TRUE : FALSE); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } |
|
|
|
|
{ u_long iMode=1; ioctlsocket(newSock,FIONBIO,&iMode); } |
|
|
|
|
#else |
|
|
|
|
if (_noDelay) { int f = 1; setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } |
|
|
|
|
fcntl(s,F_SETFL,O_NONBLOCK); |
|
|
|
|
{ int f = (_noDelay ? 1 : 0); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } |
|
|
|
|
fcntl(newSock,F_SETFL,O_NONBLOCK); |
|
|
|
|
#endif |
|
|
|
|
bool haveSlot = false; |
|
|
|
|
for(unsigned long k=0;k<ZT_SELECTWIRE_MAX_SOCKETS;++k) { |
|
|
|
|
if (_socks[k].type == ZT_WIRE_SOCKET_NULL) { |
|
|
|
|
FD_SET(s,&_readfds); |
|
|
|
|
haveSlot = true; |
|
|
|
|
if ((long)s > _nfds) |
|
|
|
|
_nfds = (long)s; |
|
|
|
|
FD_SET(s,&_readfds); |
|
|
|
|
_socks[k].type = ZT_WIRE_SOCKET_UDP; |
|
|
|
|
_socks[k].sock = s; |
|
|
|
|
_socks[k].uptr = (void *)0; |
|
|
|
|
memcpy(&(_socks[k].saddr),&ss,sizeof(struct sockaddr_storage)); |
|
|
|
|
try { |
|
|
|
|
_tcpAcceptHandler((const void *)&(_socks[i]),(const void *)&(_socks[k]),&(_socks[i].uptr),&(_socks[k].uptr),(const struct sockaddr *)&(_socks[k].saddr)); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!haveSlot) |
|
|
|
|
ZT_SELECTWIRE_CLOSE_SOCKET(s); |
|
|
|
|
_socks.push_back(WireSocketImpl()); |
|
|
|
|
WireSocketImpl &sws = _socks.back(); |
|
|
|
|
FD_SET(newSock,&_readfds); |
|
|
|
|
if ((long)newSock > _nfds) |
|
|
|
|
_nfds = (long)newSock; |
|
|
|
|
sws.type = ZT_WIRE_SOCKET_UDP; |
|
|
|
|
sws.sock = s; |
|
|
|
|
sws.uptr = (void *)0; |
|
|
|
|
memcpy(&(sws.saddr),&ss,sizeof(struct sockaddr_storage)); |
|
|
|
|
try { |
|
|
|
|
_tcpAcceptHandler((WireSocket *)&(*s),(WireSocket *)&(_socks.back()),&(s->uptr),&(sws.uptr),(const struct sockaddr *)&(sws.saddr)); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ZT_WIRE_SOCKET_UDP: |
|
|
|
|
if (FD_ISSET(_socks[i].sock,&rfds)) { |
|
|
|
|
if (FD_ISSET(s->sock,&rfds)) { |
|
|
|
|
memset(&ss,0,sizeof(ss)); |
|
|
|
|
socklen_t slen = sizeof(ss); |
|
|
|
|
long n = (long)::recvfrom(_socks[i].sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen); |
|
|
|
|
long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen); |
|
|
|
|
if (n > 0) { |
|
|
|
|
try { |
|
|
|
|
_dgHandler((const void *)&(_socks[i]),&(_socks[i].uptr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n); |
|
|
|
|
_dgHandler((WireSocket *)&(_socks[i]),&(s->uptr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -407,24 +489,23 @@ public:
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline void close(const void *sock,bool callHandlers) |
|
|
|
|
inline void close(WireSocket *sock,bool callHandlers) |
|
|
|
|
{ |
|
|
|
|
WireSocket &sws = *(const_cast <WireSocket *>(reinterpret_cast<const WireSocket *>(sock))); |
|
|
|
|
|
|
|
|
|
const WireSocketType oldType = sws.type; |
|
|
|
|
sws.type = ZT_WIRE_SOCKET_NULL; |
|
|
|
|
if (!sock) |
|
|
|
|
return; |
|
|
|
|
WireSocketImpl &sws = *(const_cast <WireSocketImpl *>(reinterpret_cast<const WireSocketImpl *>(sock))); |
|
|
|
|
|
|
|
|
|
FD_CLR(sws.sock,&_readfds); |
|
|
|
|
FD_CLR(sws.sock,&_writefds); |
|
|
|
|
FD_CLR(sws.sock,&_exceptfds); |
|
|
|
|
|
|
|
|
|
ZT_SELECTWIRE_CLOSE_SOCKET(sws.sock); |
|
|
|
|
ZT_WIRE_CLOSE_SOCKET(sws.sock); |
|
|
|
|
|
|
|
|
|
switch(oldType) { |
|
|
|
|
switch(sws.type) { |
|
|
|
|
case ZT_WIRE_SOCKET_TCP_OUT_PENDING: |
|
|
|
|
if (callHandlers) { |
|
|
|
|
try { |
|
|
|
|
_tcpConnectHandler((const void *)&sws,&(sws.uptr),false); |
|
|
|
|
_tcpConnectHandler(sock,&(sws.uptr),false); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
@ -432,7 +513,7 @@ public:
|
|
|
|
|
case ZT_WIRE_SOCKET_TCP_IN: |
|
|
|
|
if (callHandlers) { |
|
|
|
|
try { |
|
|
|
|
_tcpCloseHandler((const void *)&sws,sws.uptr); |
|
|
|
|
_tcpCloseHandler(sock,&(sws.uptr)); |
|
|
|
|
} catch ( ... ) {} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
@ -442,12 +523,21 @@ public:
|
|
|
|
|
|
|
|
|
|
if ((long)sws.sock >= _nfds) { |
|
|
|
|
long nfds = (long)_whackSendSocket; |
|
|
|
|
for(unsigned long i=0;i<ZT_SELECTWIRE_MAX_SOCKETS;++i) { |
|
|
|
|
if ((_socks[i].type != ZT_WIRE_SOCKET_NULL)&&((long)_socks[i].sock > nfds)) |
|
|
|
|
nfds = (long)_socks[i].sock; |
|
|
|
|
if ((long)_whackReceiveSocket > nfds) |
|
|
|
|
nfds = (long)_whackReceiveSocket; |
|
|
|
|
for(std::list<WireSocketImpl>::iterator s(_socks.begin());s!=_socks.end();++s) { |
|
|
|
|
if ((long)s->sock > nfds) |
|
|
|
|
nfds = (long)s->sock; |
|
|
|
|
} |
|
|
|
|
_nfds = nfds; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for(std::list<WireSocketImpl>::iterator s(_socks.begin());s!=_socks.end();++s) { |
|
|
|
|
if (&(*s) == sock) { |
|
|
|
|
_socks.erase(s); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
@ -487,19 +577,18 @@ private:
|
|
|
|
|
ZT_WIRE_SOCKET_TCP_IN = 0x02, |
|
|
|
|
ZT_WIRE_SOCKET_TCP_LISTEN = 0x03, // isTCP() == ((type & 0x03) != 0)
|
|
|
|
|
ZT_WIRE_SOCKET_RAW = 0x04, |
|
|
|
|
ZT_WIRE_SOCKET_UDP = 0x05, |
|
|
|
|
ZT_WIRE_SOCKET_NULL = 0x06 |
|
|
|
|
ZT_WIRE_SOCKET_UDP = 0x05 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct WireSocket |
|
|
|
|
struct WireSocketImpl |
|
|
|
|
{ |
|
|
|
|
WireSocketType type; |
|
|
|
|
ZT_SELECTWIRE_SOCKFD_TYPE sock; |
|
|
|
|
ZT_WIRE_SOCKFD_TYPE sock; |
|
|
|
|
void *uptr; // user-settable pointer
|
|
|
|
|
ZT_SELECTWIRE_SOCKADDR_STORAGE_TYPE saddr; // from address for TCP_IN, local address otherwise
|
|
|
|
|
ZT_WIRE_SOCKADDR_STORAGE_TYPE saddr; // from address for TCP_IN, local address otherwise
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
inline bool _isTCP(const WireSocket &sws) const throw() { return ((((unsigned int)sws.type) & 0x03) != 0); } |
|
|
|
|
inline bool _isTCP(const WireSocketImpl &sws) const throw() { return ((((unsigned int)sws.type) & 0x03) != 0); } |
|
|
|
|
|
|
|
|
|
ON_DATAGRAM_FUNCTION _dgHandler; |
|
|
|
|
ON_TCP_CONNECT_FUNCTION _tcpConnectHandler; |
|
|
|
|
@ -508,13 +597,12 @@ private:
|
|
|
|
|
ON_TCP_DATA_FUNCTION _tcpDataHandler; |
|
|
|
|
ON_TCP_WRITABLE_FUNCTION _tcpWritableHandler; |
|
|
|
|
|
|
|
|
|
WireSocket _socks[ZT_SELECTWIRE_MAX_SOCKETS]; |
|
|
|
|
|
|
|
|
|
std::list<WireSocketImpl> _socks; |
|
|
|
|
fd_set _readfds,_writefds,_exceptfds; |
|
|
|
|
long _nfds; |
|
|
|
|
|
|
|
|
|
ZT_SELECTWIRE_SOCKFD_TYPE _whackReceiveSocket; |
|
|
|
|
ZT_SELECTWIRE_SOCKFD_TYPE _whackSendSocket; |
|
|
|
|
ZT_WIRE_SOCKFD_TYPE _whackReceiveSocket; |
|
|
|
|
ZT_WIRE_SOCKFD_TYPE _whackSendSocket; |
|
|
|
|
|
|
|
|
|
bool _noDelay; |
|
|
|
|
}; |
|
|
|
|
|