diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2014-03-17 09:05:43 -0700 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2014-03-17 09:05:43 -0700 |
commit | 7e80d7e551a0a2edc3fa3e6b6eb7379863c3ac04 (patch) | |
tree | 02da2dfacfda5b6965e12d4e30b69d52851743cd | |
parent | ce09d00679f22e4e71219bb1af2da6b974e04573 (diff) | |
download | infinitytier-7e80d7e551a0a2edc3fa3e6b6eb7379863c3ac04.tar.gz infinitytier-7e80d7e551a0a2edc3fa3e6b6eb7379863c3ac04.zip |
Bunch of work in progress on new socket multiplexer and plumbing for TCP jailbreaking... Don't try to build, won't yet.
-rw-r--r-- | node/Socket.hpp | 139 | ||||
-rw-r--r-- | node/SocketManager.cpp | 410 | ||||
-rw-r--r-- | node/SocketManager.hpp | 205 | ||||
-rw-r--r-- | node/TcpSocket.hpp | 113 | ||||
-rw-r--r-- | node/UdpSocket.hpp | 93 | ||||
-rw-r--r-- | node/UdpSocket_old.cpp (renamed from node/UdpSocket.cpp) | 0 | ||||
-rw-r--r-- | node/UdpSocket_old.hpp | 118 |
7 files changed, 1012 insertions, 66 deletions
diff --git a/node/Socket.hpp b/node/Socket.hpp new file mode 100644 index 00000000..687834a2 --- /dev/null +++ b/node/Socket.hpp @@ -0,0 +1,139 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2014 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_SOCKET_HPP +#define ZT_SOCKET_HPP + +#include <list> + +#include "Constants.hpp" +#include "InetAddress.hpp" +#include "AtomicCounter.hpp" +#include "SharedPtr.hpp" +#include "NonCopyable.hpp" + +#ifdef __WINDOWS__ +#include <WinSock2.h> +#include <WS2tcpip.h> +#include <Windows.h> +#endif + +/** + * Maximum discrete message length supported by all socket types + */ +#define ZT_SOCKET_MAX_MESSAGE_LEN 4096 + +namespace ZeroTier { + +class SocketManager; + +/** + * Base class of all socket types + * + * Socket implementations are tightly bound to SocketManager. + */ +class Socket : NonCopyable +{ + friend class SharedPtr<Socket>; + friend class SocketManager; + +public: + enum Type + { + ZT_SOCKET_TYPE_UDP_V4, + ZT_SOCKET_TYPE_UDP_V6, + ZT_SOCKET_TYPE_TCP + }; + + virtual ~Socket() {} + + /** + * @return Socket type + */ + inline Type type() const + throw() + { + return _type; + } + + /** + * @return True if this is a TCP socket + */ + inline bool isTCP() const + throw() + { + return (_type == ZT_SOCKET_TYPE_TCP); + } + + /** + * @return True if socket is available for sending/receiving of data + */ + virtual bool isOpen() const = 0; + + /** + * Send a ZeroTier message packet + * + * @param to Destination address (ignored in connected TCP sockets) + * @param msg Message data + * @param msglen Message length (max 16384 bytes) + * @return True if send appears successful on our end, false if e.g. address type unreachable from this socket + */ + virtual bool send(const InetAddress &to,const void *msg,unsigned int msglen) = 0; + +protected: + // Called only by SocketManager, should return false if socket is no longer open/valid (e.g. connection drop or other fatal error) + virtual bool notifyAvailableForRead(const SharedPtr<Socket> &self,SocketManager *sm) = 0; + virtual bool notifyAvailableForWrite(const SharedPtr<Socket> &self,SocketManager *sm) = 0; + +private: +#ifdef __WINDOWS__ + Socket(Type t,SOCKET sock) : + _sock(sock), + _type(t) + { + } +#else + Socket(Type t,int sock) : + _sock(sock), + _type(t) + { + } +#endif + +#ifdef __WINDOWS__ + SOCKET _sock; +#else + int _sock; +#endif + Type _type; + + AtomicCounter __refCount; +}; + +}; // namespace ZeroTier + +#endif diff --git a/node/SocketManager.cpp b/node/SocketManager.cpp new file mode 100644 index 00000000..87bbf066 --- /dev/null +++ b/node/SocketManager.cpp @@ -0,0 +1,410 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2014 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <time.h> +#include <sys/types.h> + +#include "SocketManager.hpp" + +#ifndef __WINDOWS__ +#include <unistd.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <signal.h> +#endif + +// Allow us to use the same value on Windows and *nix +#ifndef INVALID_SOCKET +#define INVALID_SOCKET 0 +#endif + +namespace ZeroTier { + +#ifdef __WINDOWS__ +// hack from StackOverflow, behaves a bit like pipe() on *nix systems +static inline void __winpipe(SOCKET fds[2]) +{ + struct sockaddr_in inaddr; + struct sockaddr addr; + SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); + memset(&inaddr, 0, sizeof(inaddr)); + memset(&addr, 0, sizeof(addr)); + inaddr.sin_family = AF_INET; + inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + inaddr.sin_port = 0; + int yes=1; + setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)); + bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)); + listen(lst,1); + int len=sizeof(inaddr); + getsockname(lst, &addr,&len); + fds[0]=::socket(AF_INET, SOCK_STREAM,0); + connect(fds[0],&addr,len); + fds[1]=accept(lst,0,0); + closesocket(lst); +} +#endif + +SocketManager::SocketManager(int localUdpPort,int localTcpPort,void (*packetHandler)(const SharedPtr<Socket> &,void *,const InetAddress &,const void *,unsigned int),void *arg) : + _whackSendPipe(INVALID_SOCKET), + _whackReceivePipe(INVALID_SOCKET), + _tcpV4ListenSocket(INVALID_SOCKET), + _tcpV6ListenSocket(INVALID_SOCKET), + _nfds(0), + _packetHandler(packetHandler), + _arg(arg) +{ + FD_ZERO(&_readfds); + FD_ZERO(&_writefds); + +#ifdef __WINDOWS__ + { + SOCKET tmps[2] = { INVALID_SOCKET,INVALID_SOCKET }; + __winpipe(tmps); + _whackSendPipe = tmps[0]; + _whackReceivePipe = tmps[1]; + } +#else + { + int tmpfds[2]; + if (::pipe(tmpfds,0)) + throw std::runtime_error("pipe() failed"); + _whackSendPipe = tmpfds[1]; + _whackReceivePipe = tmpfds[0]; + } +#endif + FD_SET(_whackReceivePipe,&_readfds); + + if (localTcpPort > 0) { + if (localTcpPort > 0xffff) { + _closeSockets(); + throw std::runtime_error("invalid local TCP port number"); + } + + { // bind TCP IPv6 + _tcpV6ListenSocket = ::socket(AF_INET6,SOCK_STREAM,0); +#ifdef __WINDOWS__ + if (_tcpV6ListenSocket == INVALID_SOCKET) { + _closeSockets(); + throw std::runtime_error("unable to create IPv6 SOCK_STREAM socket"); + } +#else + if (_tcpV6ListenSocket <= 0) { + _closeSockets(); + throw std::runtime_error("unable to create IPv6 SOCK_STREAM socket"); + } +#endif + +#ifdef __WINDOWS__ + { + BOOL f; + f = TRUE; ::setsockopt(_tcpV6ListenSocket,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); + f = TRUE; ::setsockopt(_tcpV6ListenSocket,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); + } +#else + { + int f; + f = 1; ::setsockopt(_tcpV6ListenSocket,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); + f = 1; ::setsockopt(_tcpV6ListenSocket,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); + } +#endif + + struct sockaddr_in6 sin6; + memset(&sin6,0,sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(localTcpPort); + memcpy(&(sin6.sin6_addr),&in6addr_any,sizeof(struct in6_addr)); + if (::bind(_tcpV6ListenSocket,(const struct sockaddr *)&sin6,sizeof(sin6))) { + _closeSockets(); + throw std::runtime_error("unable to bind to local TCP port"); + } + + if (::listen(_tcpV6ListenSocket,16)) { + _closeSockets(); + throw std::runtime_error("listen() failed"); + } + + FD_SET(_tcpV6ListenSocket,&_readfds); + } + + { // bind TCP IPv4 + _tcpV4ListenSocket = ::socket(AF_INET,SOCK_STREAM,0); +#ifdef __WINDOWS__ + if (_tcpV4ListenSocket == INVALID_SOCKET) { + _closeSockets(); + throw std::runtime_error("unable to create IPv4 SOCK_STREAM socket"); + } +#else + if (_tcpV4ListenSocket <= 0) { + _closeSockets(); + throw std::runtime_error("unable to create IPv4 SOCK_STREAM socket"); + } +#endif + +#ifdef __WINDOWS__ + { + BOOL f = TRUE; ::setsockopt(_tcpV4ListenSocket,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); + } +#else + { + int f = 1; ::setsockopt(_tcpV4ListenSocket,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); + } +#endif + + struct sockaddr_in sin4; + memset(&sin4,0,sizeof(sin4)); + sin4.sin_family = AF_INET; + sin4.sin_port = htons(localTcpPort); + sin4.sin_addr.s_addr = INADDR_ANY; + if (::bind(_tcpV4ListenSocket,(const struct sockaddr *)&sin4,sizeof(sin4))) { + _closeSockets(); + throw std::runtime_error("unable to bind to local TCP port"); + } + + if (::listen(_tcpV4ListenSocket,16)) { + _closeSockets(); + throw std::runtime_error("listen() failed"); + } + + FD_SET(_tcpV4ListenSocket,&_readfds); + } + } + + if (localUdpPort > 0) { + if (localUdpPort > 0xffff) { + _closeSockets(); + throw std::runtime_error("invalid local UDP port number"); + } + + { // bind UDP IPv6 +#ifdef __WINDOWS__ + SOCKET s = ::socket(AF_INET6,SOCK_DGRAM,0); + if (s == INVALID_SOCKET) { + _closeSockets(); + throw std::runtime_error("unable to create IPv6 SOCK_DGRAM socket"); + } +#else + int s = ::socket(AF_INET6,SOCK_DGRAM,0); + if (s <= 0) { + _closeSockets(); + throw std::runtime_error("unable to create IPv6 SOCK_DGRAM socket"); + } +#endif + + { +#ifdef __WINDOWS__ + BOOL f; + f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); + f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); + f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f)); +#else + int f; + f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); + f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); +#ifdef IP_DONTFRAG + 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)); +#endif +#ifdef IPV6_MTU_DISCOVER + f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); +#endif +#endif + } + + struct sockaddr_in6 sin6; + memset(&sin6,0,sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(localUdpPort); + memcpy(&(sin6.sin6_addr),&in6addr_any,sizeof(struct in6_addr)); + if (::bind(s,(const struct sockaddr *)&sin6,sizeof(sin6))) { +#ifdef __WINDOWS__ + ::closesocket(s); +#else + ::close(s); +#endif + _closeSockets(); + throw std::runtime_error("unable to bind to port"); + } + + FD_SET(s,&_readfds); + _udpV6Socket = SharedPtr<Socket>(new UdpSocket(Socket::ZT_SOCKET_TYPE_UDP_V6,s)); + } + + { // bind UDP IPv4 +#ifdef __WINDOWS__ + SOCKET s = ::socket(AF_INET,SOCK_DGRAM,0); + if (s == INVALID_SOCKET) { + _closeSockets(); + throw std::runtime_error("unable to create IPv4 SOCK_DGRAM socket"); + } +#else + int s = ::socket(AF_INET,SOCK_DGRAM,0); + if (s <= 0) { + _closeSockets(); + throw std::runtime_error("unable to create IPv4 SOCK_DGRAM socket"); + } +#endif + + { +#ifdef __WINDOWS__ + BOOL f; + f = FALSE; setsockopt(_sock,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); + f = FALSE; setsockopt(_sock,IPPROTO_IP,IP_DONTFRAGMENT,(const char *)&f,sizeof(f)); +#else + int f; + f = 0; setsockopt(_sock,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); +#ifdef IP_DONTFRAG + f = 0; setsockopt(_sock,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); +#endif +#ifdef IP_MTU_DISCOVER + f = 0; setsockopt(_sock,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); +#endif +#endif + } + + struct sockaddr_in sin4; + memset(&sin4,0,sizeof(sin4)); + sin4.sin_family = AF_INET; + sin4.sin_port = htons(localUdpPort); + sin4.sin_addr.s_addr = INADDR_ANY; + if (::bind(s,(const struct sockaddr *)&sin4,sizeof(sin4))) { +#ifdef __WINDOWS__ + ::closesocket(s); +#else + ::close(s); +#endif + throw std::runtime_error("unable to bind to port"); + } + + FD_SET(s,&_readfds); + _udpV4Socket = SharedPtr<Socket>(new UdpSocket(Socket::ZT_SOCKET_TYPE_UDP_V4,s)); + } + } +} + +SocketManager::~SocketManager() +{ + Mutex::Lock _l(_pollLock); + _closeSockets(); +} + +bool SocketManager::send(const InetAddress &to,bool tcp,const void *msg,unsigned int msglen) +{ + if (tcp) { + } else if (to.isV4()) { + if (_udpV4Socket) + return _udpV4Socket->send(to,msg,msglen); + } else if (to.isV6()) { + if (_udpV6Socket) + return _udpV6Socket->send(to,msg,msglen); + } + return false; +} + +bool SocketManager::sendFirewallOpener(const InetAddress &to,int hopLimit) +{ + if (to.isV4()) { + if (_udpV4Socket) + return _udpV4Socket->sendWithHopLimit(to,msg,msglen,hopLimit); + } else if (to.isV6()) { + if (_udpV6Socket) + return _udpV6Socket->sendWithHopLimit(to,msg,msglen,hopLimit); + } + return false; +} + +void SocketManager::poll(unsigned long timeout) +{ + fd_set rfds,wfds,nfds; + struct timeval tv; + + Mutex::Lock _l(_pollLock); + + _fdSetLock.lock(); + memcpy(&rfds,&_readfds,sizeof(rfds)); + memcpy(&wfds,&_writefds,sizeof(wfds)); + _fdSetLock.unlock(); + FD_ZERO(&nfds); + + tv.tv_sec = (long)(timeout / 1000); + tv.tv_usec = (long)((timeout % 1000) * 1000); + select(_nfds,&rfds,&wfds,&nfds,(timeout > 0) ? &tv : (struct timeval *)0); + + if (FD_ISSET(_whackReceivePipe,&rfds)) { + char tmp[32]; +#ifdef __WINDOWS__ + ::recv(_whackReceivePipe,tmp,sizeof(tmp),0); +#else + ::read(_whackReceivePipe,tmp,sizeof(tmp)); +#endif + } + + if ((_tcpV4ListenSocket != INVALID_SOCKET)&&(FD_ISSET(_tcpV4ListenSocket,&rfds))) { + } + if ((_tcpV6ListenSocket != INVALID_SOCKET)&&(FD_ISSET(_tcpV6ListenSocket,&rfds))) { + } + + if ((_udpV4Socket)&&(FD_ISSET(_udpV4Socket->_sock,&rfds))) + _udpV4Socket->notifyAvailableForRead(_udpV4Socket,this); + if ((_udpV6Socket)&&(FD_ISSET(_udpV6Socket->_sock,&rfds))) + _udpV6Socket->notifyAvailableForRead(_udpV6Socket,this); + + std::vector< SharedPtr<Socket> > ts; + { + Mutex::Lock _l2(_tcpSockets_m); + if (_tcpSockets.size()) { + ts.reserve(_tcpSockets.size()); + for(std::map< InetAddress,SharedPtr<Socket> >::iterator s(_tcpSockets.begin());s!=_tcpSockets.end();++s) + ts.push_back(s->second); + } + } + for(std::vector< SharedPtr<Socket> >::iterator s(ts.begin());s!=ts.end();++s) { + if (FD_ISSET((*s)->_sock,&rfds)) + s->notifyAvailableForRead(*s,this); + if (FD_ISSET((*s)->_sock,&wfds)) + s->notifyAvailableForWrite(*s,this); + } +} + +void SocketManager::whack() +{ + _whackSendPipe_m.lock(); +#ifdef __WINDOWS__ + ::send(_whackSendPipe,(const void *)this,1,0); +#else + ::write(_whackSendPipe,(const void *)this,1); // data is arbitrary, just send a byte +#endif + _whackSendPipe_m.unlock(); +} + +} // namespace ZeroTier diff --git a/node/SocketManager.hpp b/node/SocketManager.hpp new file mode 100644 index 00000000..5ea11080 --- /dev/null +++ b/node/SocketManager.hpp @@ -0,0 +1,205 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2014 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_SOCKETMANAGER_HPP +#define ZT_SOCKETMANAGER_HPP + +#include <stdio.h> +#include <stdlib.h> +#ifdef __WINDOWS__ +#include <WinSock2.h> +#include <WS2tcpip.h> +#include <Windows.h> +#else +#include <unistd.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#endif + +#include <map> +#include <stdexcept> + +#include "Constants.hpp" +#include "SharedPtr.hpp" +#include "InetAddress.hpp" +#include "Socket.hpp" +#include "TcpSocket.hpp" +#include "UdpSocket.hpp" +#include "Mutex.hpp" +#include "NonCopyable.hpp" +#include "Buffer.hpp" + +namespace ZeroTier { + +/** + * Socket I/O multiplexer + * + * This wraps select(), epoll(), etc. and handles creation of Sockets. + */ +class SocketManager : NonCopyable +{ + friend class Socket; + friend class UdpSocket; + friend class TcpSocket; + +public: + /** + * @param localUdpPort Local UDP port to bind or 0 for no UDP support + * @param localTcpPort Local TCP port to listen to or 0 for no incoming TCP connect support + * @param packetHandler Function to call when packets are received by a socket + * @param arg Second argument to packetHandler() + * @throws std::runtime_error Could not bind local port(s) or open socket(s) + */ + SocketManager(int localUdpPort,int localTcpPort,void (*packetHandler)(const SharedPtr<Socket> &,void *,const InetAddress &,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &),void *arg); + + ~SocketManager(); + + /** + * Send a message to a remote peer + * + * If 'tcp' is true an existing TCP socket will be used or an attempt will + * be made to connect if one is not available. The message will be placed + * in the connecting TCP socket's outgoing queue, so if the connection + * succeeds the message will be sent. Otherwise it will be dropped. + * + * @param to Destination address + * @param tcp Use TCP? + * @param msg Message to send + * @param msglen Length of message + */ + bool send(const InetAddress &to,bool tcp,const void *msg,unsigned int msglen); + + /** + * Send a UDP packet with a limited IP TTL + * + * @param to Destination address + * @param hopLimit IP TTL + */ + bool sendFirewallOpener(const InetAddress &to,int hopLimit); + + /** + * Perform I/O polling operation (e.g. select()) + * + * If called concurrently, one will block until the other completes. + * + * @param timeout Timeout in milliseconds, may return sooner if whack() is called + */ + void poll(unsigned long timeout); + + /** + * Cause current or next blocking poll() operation to timeout immediately + */ + void whack(); + +private: + // Called by socket implementations when a packet is received + inline void handleReceivedPacket(const SharedPtr<Socket> &sock,const InetAddress &from,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &data) + throw() + { + try { + _packetHandler(sock,_arg,from,data); + } catch ( ... ) {} // handlers shouldn't throw + } + + // Called by socket implementations to register or unregister for available-for-write notification on underlying _sock + inline void startNotifyWrite(const Socket *sock) + throw() + { + _fdSetLock.lock(); + FD_SET(sock->_sock,&_writefds); + _fdSetLock.unlock(); + } + inline void stopNotifyWrite(const Socket *sock) + throw() + { + _fdSetLock.lock(); + FD_CLR(sock->_sock,&_writefds); + _fdSetLock.unlock(); + } + + inline void _closeSockets() + throw() + { +#ifdef __WINDOWS__ + if (_whackSendPipe != INVALID_SOCKET) + ::closesocket(_whackSendPipe); + if (_whackReceivePipe != INVALID_SOCKET) + ::closesocket(_whackReceivePipe); + if (_tcpV4ListenSocket != INVALID_SOCKET) + ::closesocket(s); + if (_tcpV6ListenSocket != INVALID_SOCKET) + ::closesocket(s); +#else + if (_whackSendPipe > 0) + ::close(_whackSendPipe); + if (_whackReceivePipe > 0) + ::close(_whackReceivePipe); + if (_tcpV4ListenSocket > 0) + ::close(_tcpV4ListenSocket); + if (_tcpV4ListenSocket > 0) + ::close(_tcpV6ListenSocket); +#endif + } + +#ifdef __WINDOWS__ + SOCKET _whackSendPipe; + SOCKET _whackReceivePipe; +#else + int _whackSendPipe; + int _whackReceivePipe; +#endif + Mutex::Lock _whackSendPipe_m; + +#ifdef __WINDOWS__ + SOCKET _tcpV4ListenSocket; + SOCKET _tcpV6ListenSocket; +#else + int _tcpV4ListenSocket; + int _tcpV6ListenSocket; +#endif + + SharedPtr<Socket> _udpV4Socket; + SharedPtr<Socket> _udpV6Socket; + + fd_set _readfds; + fd_set _writefds; + int _nfds; + Mutex _fdSetLock; + + std::map< InetAddress,SharedPtr<Socket> > _tcpSockets; + Mutex _tcpSockets_m; + + void (*_packetHandler)(const SharedPtr<Socket> &,void *,const InetAddress &,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &); + void *_arg; + + Mutex _pollLock; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/TcpSocket.hpp b/node/TcpSocket.hpp new file mode 100644 index 00000000..0161fa27 --- /dev/null +++ b/node/TcpSocket.hpp @@ -0,0 +1,113 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2014 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_TCPSOCKET_HPP +#define ZT_TCPSOCKET_HPP + +#include <stdint.h> + +#include "Socket.hpp" +#include "Mutex.hpp" +#include "Utils.hpp" + +namespace ZeroTier { + +class SocketManager; + +#define ZT_TCP_SENDQ_LENGTH 16384 +#define ZT_TCP_MAX_MESSAGE_LENGTH 2048 + +/** + * A TCP socket encapsulating ZeroTier packets over a TCP stream connection + * + * This implements a simple packet encapsulation that is designed to look like + * a TLS connection. It's not a TLS connection, but it sends TLS format record + * headers. It could be extended in the future to implement a fake TLS + * handshake. + * + * At the moment, each packet is just made to look like TLS application data: + * <[1] TLS content type> - currently 0x17 for "application data" + * <[1] TLS major version> - currently 0x03 for TLS 1.2 + * <[1] TLS minor version> - currently 0x03 for TLS 1.2 + * <[2] payload length> - 16-bit length of payload in bytes + * <[...] payload> - Message payload + * + * The primary purpose of TCP sockets is to work over ports like HTTPS(443), + * allowing users behind particularly fascist firewalls to at least reach + * ZeroTier's supernodes. UDP is the preferred method of communication as + * encapsulating L2 and L3 protocols over TCP is inherently inefficient + * due to double-ACKs. So TCP is only used as a fallback. + */ +class TcpSocket : public Socket +{ + friend class SharedPtr<Socket>; + friend class SocketManager; + +public: + virtual ~TcpSocket(); + + virtual bool isOpen() const; + virtual bool send(const InetAddress &to,const void *msg,unsigned int msglen); + + /** + * @return Remote TCP endpoint address + */ + inline const InetAddress &remote() const { return _remote; } + +protected: + virtual bool notifyAvailableForRead(const SharedPtr<Socket> &self,SocketManager *sm); + virtual bool notifyAvailableForWrite(const SharedPtr<Socket> &self,SocketManager *sm); + +private: +#ifdef __WINDOWS__ + TcpSocket(SOCKET sock,bool connecting,const InetAddress &remote) : +#endif + TcpSocket(int sock,bool connecting,const InetAddress &remote) : +#else + Socket(ZT_SOCKET_TYPE_TCP,sock), + _lastReceivedData(Utils::now()), + _inptr(0), + _outptr(0), + _connecting(connecting), + _remote(remote), + _lock() + { + } + + unsigned char _outbuf[ZT_TCP_SENDQ_LENGTH]; + unsigned char _inbuf[ZT_TCP_MAX_MESSAGE_LENGTH]; + uint64_t _lastReceivedData; // updated whenever data is received, checked directly by SocketManager for stale TCP cleanup + unsigned int _inptr; + unsigned int _outptr; + bool _connecting; // manipulated directly by SocketManager, true if connect() is in progress + InetAddress _remote; + Mutex _lock; +}; + +}; // namespace ZeroTier + +#endif diff --git a/node/UdpSocket.hpp b/node/UdpSocket.hpp index b48dc759..9a6cf68b 100644 --- a/node/UdpSocket.hpp +++ b/node/UdpSocket.hpp @@ -28,91 +28,52 @@ #ifndef ZT_UDPSOCKET_HPP #define ZT_UDPSOCKET_HPP -#include <stdexcept> - -#include "Constants.hpp" -#include "Thread.hpp" -#include "InetAddress.hpp" -#include "Mutex.hpp" - -#ifdef __WINDOWS__ -#include <WinSock2.h> -#endif +#include "Socket.hpp" namespace ZeroTier { +class SocketManager; + /** - * A local UDP socket - * - * The socket listens in a background thread and sends packets to Switch. + * Locally bound UDP socket */ -class UdpSocket +class TcpSocket : public Socket { -public: - /** - * Create and bind a local UDP socket - * - * @param localOnly If true, bind to loopback address only - * @param localPort Local port to listen to - * @param ipv6 If true, bind this as an IPv6 socket, otherwise IPv4 - * @param packetHandler Function to call when packets are read - * @param arg First argument (after self) to function - * @throws std::runtime_error Unable to bind - */ - UdpSocket( - bool localOnly, - int localPort, - bool ipv6, - void (*packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int), - void *arg) - throw(std::runtime_error); + friend class SharedPtr<Socket>; + friend class SocketManager; - ~UdpSocket(); +public: + virtual ~UdpSocket(); - /** - * @return Locally bound port - */ - inline int localPort() const throw() { return _localPort; } + virtual bool isOpen() const; + virtual bool send(const InetAddress &to,const void *msg,unsigned int msglen); /** - * @return True if this is an IPv6 socket - */ - inline bool v6() const throw() { return _v6; } - - /** - * Send a packet - * - * Attempt to send V6 on a V4 or V4 on a V6 socket will return false. + * Send UDP packet with IP max hops set (<= 0 for default/infinite) * - * @param to Destination IP/port - * @param data Data to send - * @param len Length of data in bytes - * @param hopLimit IP hop limit for UDP packet or -1 for max (max: 255) - * @return True if packet successfully sent to link layer + * @param to Destination address + * @param msg Message data + * @param msglen Message length + * @param hopLimit IP TTL / max hops + * @return True if packet appears sent */ - bool send(const InetAddress &to,const void *data,unsigned int len,int hopLimit) - throw(); + bool sendWithHopLimit(const InetAddress &to,const void *msg,unsigned int msglen,int hopLimit); - /** - * Thread main method; do not call elsewhere - */ - void threadMain() - throw(); +protected: + virtual bool notifyAvailableForRead(const SharedPtr<Socket> &self,SocketManager *sm); + virtual bool notifyAvailableForWrite(const SharedPtr<Socket> &self,SocketManager *sm); private: - Thread _thread; - void (*_packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int); - void *_arg; - int _localPort; #ifdef __WINDOWS__ - volatile SOCKET _sock; + UdpSocket(Type t,SOCKET sock) : #else - volatile int _sock; + UdpSocket(Type t,int sock) : #endif - bool _v6; - Mutex _sendLock; + Socket(t,sock) + { + } }; -} // namespace ZeroTier +}; // namespace ZeroTier #endif diff --git a/node/UdpSocket.cpp b/node/UdpSocket_old.cpp index a3f551b3..a3f551b3 100644 --- a/node/UdpSocket.cpp +++ b/node/UdpSocket_old.cpp diff --git a/node/UdpSocket_old.hpp b/node/UdpSocket_old.hpp new file mode 100644 index 00000000..b48dc759 --- /dev/null +++ b/node/UdpSocket_old.hpp @@ -0,0 +1,118 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2014 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_UDPSOCKET_HPP +#define ZT_UDPSOCKET_HPP + +#include <stdexcept> + +#include "Constants.hpp" +#include "Thread.hpp" +#include "InetAddress.hpp" +#include "Mutex.hpp" + +#ifdef __WINDOWS__ +#include <WinSock2.h> +#endif + +namespace ZeroTier { + +/** + * A local UDP socket + * + * The socket listens in a background thread and sends packets to Switch. + */ +class UdpSocket +{ +public: + /** + * Create and bind a local UDP socket + * + * @param localOnly If true, bind to loopback address only + * @param localPort Local port to listen to + * @param ipv6 If true, bind this as an IPv6 socket, otherwise IPv4 + * @param packetHandler Function to call when packets are read + * @param arg First argument (after self) to function + * @throws std::runtime_error Unable to bind + */ + UdpSocket( + bool localOnly, + int localPort, + bool ipv6, + void (*packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int), + void *arg) + throw(std::runtime_error); + + ~UdpSocket(); + + /** + * @return Locally bound port + */ + inline int localPort() const throw() { return _localPort; } + + /** + * @return True if this is an IPv6 socket + */ + inline bool v6() const throw() { return _v6; } + + /** + * Send a packet + * + * Attempt to send V6 on a V4 or V4 on a V6 socket will return false. + * + * @param to Destination IP/port + * @param data Data to send + * @param len Length of data in bytes + * @param hopLimit IP hop limit for UDP packet or -1 for max (max: 255) + * @return True if packet successfully sent to link layer + */ + bool send(const InetAddress &to,const void *data,unsigned int len,int hopLimit) + throw(); + + /** + * Thread main method; do not call elsewhere + */ + void threadMain() + throw(); + +private: + Thread _thread; + void (*_packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int); + void *_arg; + int _localPort; +#ifdef __WINDOWS__ + volatile SOCKET _sock; +#else + volatile int _sock; +#endif + bool _v6; + Mutex _sendLock; +}; + +} // namespace ZeroTier + +#endif |