Răsfoiți Sursa

Bunch of work in progress on new socket multiplexer and plumbing for TCP jailbreaking... Don't try to build, won't yet.

Adam Ierymenko 11 ani în urmă
părinte
comite
7e80d7e551
7 a modificat fișierele cu 1012 adăugiri și 66 ștergeri
  1. 139 0
      node/Socket.hpp
  2. 410 0
      node/SocketManager.cpp
  3. 205 0
      node/SocketManager.hpp
  4. 113 0
      node/TcpSocket.hpp
  5. 27 66
      node/UdpSocket.hpp
  6. 0 0
      node/UdpSocket_old.cpp
  7. 118 0
      node/UdpSocket_old.hpp

+ 139 - 0
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

+ 410 - 0
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

+ 205 - 0
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

+ 113 - 0
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

+ 27 - 66
node/UdpSocket.hpp

@@ -28,91 +28,52 @@
 #ifndef ZT_UDPSOCKET_HPP
 #ifndef ZT_UDPSOCKET_HPP
 #define 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 {
 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:
 private:
-	Thread _thread;
-	void (*_packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int);
-	void *_arg;
-	int _localPort;
 #ifdef __WINDOWS__
 #ifdef __WINDOWS__
-	volatile SOCKET _sock;
+	UdpSocket(Type t,SOCKET sock) :
 #else
 #else
-	volatile int _sock;
+	UdpSocket(Type t,int sock) :
 #endif
 #endif
-	bool _v6;
-	Mutex _sendLock;
+		Socket(t,sock)
+	{
+	}
 };
 };
 
 
-} // namespace ZeroTier
+}; // namespace ZeroTier
 
 
 #endif
 #endif

+ 0 - 0
node/UdpSocket.cpp → node/UdpSocket_old.cpp


+ 118 - 0
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