Browse Source

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 years ago
parent
commit
7e80d7e551
7 changed files with 1012 additions and 66 deletions
  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