/* * ZeroTier One - Network Virtualization Everywhere * Copyright (C) 2011-2015 ZeroTier, Inc. * * 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 . * * -- * * 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_WIRE_HPP #define ZT_WIRE_HPP #include #include #ifdef __WINDOWS__ #include #include #include #else #include #include #include #include #include #include #include #include #include #include #endif #if defined(_WIN32) || defined(_WIN64) #define ZT_SELECTWIRE_SOCKFD_TYPE SOCKET #define ZT_SELECTWIRE_SOCKFD_NULL (INVALID_SOCKET) #define ZT_SELECTWIRE_SOCKFD_VALID(s) ((s) != INVALID_SOCKET) #define ZT_SELECTWIRE_CLOSE_SOCKET(s) ::closesocket(s) #define ZT_SELECTWIRE_MAX_SOCKETS (FD_SETSIZE) #define ZT_SELECTWIRE_SOCKADDR_STORAGE_TYPE struct sockaddr_storage #else #define ZT_SELECTWIRE_SOCKFD_TYPE int #define ZT_SELECTWIRE_SOCKFD_NULL (-1) #define ZT_SELECTWIRE_SOCKFD_VALID(s) ((s) > -1) #define ZT_SELECTWIRE_CLOSE_SOCKET(s) ::close(s) #define ZT_SELECTWIRE_MAX_SOCKETS (FD_SETSIZE) #define ZT_SELECTWIRE_SOCKADDR_STORAGE_TYPE struct sockaddr_storage #endif namespace ZeroTier { /** * Wire implementation using select() for *nix or Windows * * This implementation takes four functions or function objects as template * paramters: * * ON_DATAGRAM_FUNCTION(const void *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) * ON_TCP_CONNECT_FUNCTION(const void *sock,void **uptr,bool success) * ON_TCP_ACCEPT_FUNCTION(const void *sockL,const void *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) * ON_TCP_CLOSE_FUNCTION(const void *sock,void *uptr) * ON_TCP_DATA_FUNCTION(const void *sock,void **uptr,void *data,unsigned long len) * ON_TCP_WRITABLE_FUNCTION(const void *sock,void **uptr) * * These templates typically refer to function objects. Templates are used to * avoid the call overhead of indirection. */ template < typename ON_DATAGRAM_FUNCTION, typename ON_TCP_CONNECT_FUNCTION, typename ON_TCP_ACCEPT_FUNCTION, typename ON_TCP_CLOSE_FUNCTION, typename ON_TCP_DATA_FUNCTION, typename ON_TCP_WRITABLE_FUNCTION > class Wire { public: /** * @param dgHandler Function or function object to handle UDP or RAW datagrams * @param tcpConnectHandler Handler for outgoing TCP connection attempts (success or failure) * @param tcpAcceptHandler Handler for incoming TCP connections * @param tcpDataHandler Handler for incoming TCP data * @param tcpWritableHandler Handler to be called when TCP sockets are writable (if notification is on) * @param noDelay If true, disable Nagle algorithm on new TCP sockets */ Wire( ON_DATAGRAM_FUNCTION dgHandler, ON_TCP_CONNECT_FUNCTION tcpConnectHandler, ON_TCP_ACCEPT_FUNCTION tcpAcceptHandler, ON_TCP_CLOSE_FUNCTION tcpCloseHandler, ON_TCP_DATA_FUNCTION tcpDataHandler, ON_TCP_WRITABLE_FUNCTION tcpWritableHandler, bool noDelay) : _dgHandler(dgHandler), _tcpConnectHandler(tcpConnectHandler), _tcpAcceptHandler(tcpAcceptHandler), _tcpCloseHandler(tcpCloseHandler), _tcpDataHandler(tcpDataHandler), _tcpWritableHandler(tcpWritableHandler) { for(unsigned lont i=0;i pipes[1]) ? (long)pipes[0] : (long)pipes[1]; _whackReceiveSocket = pipes[0]; _whackSendSocket = pipes[1]; _noDelay = noDelay; } ~Wire() { for(unsigned long i=0;i<_nsocks;++i) { if (_socks[i].type != ZT_WIRE_SOCKET_NULL) this->close(_socks[i],true); } ZT_SELECTWIRE_CLOSE_SOCKET(_whackReceiveSocket); ZT_SELECTWIRE_CLOSE_SOCKET(_whackSendSocket); } /** * Cause poll() to stop waiting immediately */ inline void whack() { #if defined(_WIN32) || defined(_WIN64) ::send(_whackSendSocket,(const char *)this,1,0); #else ::write(_whackSendSocket,(const void *)this,1); #endif } /** * Bind a UDP socket * * @param localAddress Local endpoint address and port * @param uptr Initial value of user pointer associated with this socket * @return Socket (as opaque pointer) or NULL on failure */ inline const void *udpBind(const struct sockaddr *localAddress,void *uptr) { ZT_SELECTWIRE_SOCKFD_TYPE s = ::socket(AF_INET6,SOCK_DGRAM,0); if (!ZT_SELECTWIRE_SOCKFD_VALID(s)) return (const void *)0; int bs = 262144; while (bs >= 65536) { int tmpbs = bs; if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) break; bs -= 16384; } bs = 262144; while (bs >= 65536) { int tmpbs = bs; if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) break; bs -= 16384; } #if defined(_WIN32) || defined(_WIN64) BOOL f; if (localAddress->ss_family == AF_INET6) { f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f)); } f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f)); #else // not Windows int f; if (localAddress->ss_family == AF_INET6) { f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); #ifdef IPV6_MTU_DISCOVER f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); #endif } f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f)); #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 #endif // Windows or not if (::bind(s,localAddress,(localAddress->ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { ZT_SELECTWIRE_CLOSE_SOCKET(s); return (const void *)0; } #if defined(_WIN32) || defined(_WIN64) u_long iMode=1; ioctlsocket(s,FIONBIO,&iMode); #else fcntl(s,F_SETFL,O_NONBLOCK); #endif for(unsigned long i=0;i _nfds) _nfds = (long)s; FD_SET(s,&_readfds); _socks[i].type = ZT_WIRE_SOCKET_UDP; _socks[i].sock = s; _socks[i].uptr = uptr; memcpy(&(_socks[i].saddr),localAddress,sizeof(struct sockaddr_storage)); return (const void *)&(_socks[i]); } } ZT_SELECTWIRE_CLOSE_SOCKET(s); return (const void *)0; } /** * Send a UDP packet * * @param sock UDP socket * @param addr Destination address (must be correct type for socket) * @param addrlen Length of sockaddr_X structure * @param data Data to send * @param len Length of packet * @return True if packet appears to have been sent successfully */ inline bool udpSend(const void *sock,const struct sockaddr *addr,unsigned int addrlen,const void *data,unsigned long len) { WireSocket &sws = *(const_cast (reinterpret_cast(sock))); return ((long)::sendto(sws.sock,data,len,0,addr,(socklen_t)addrlen) == (long)len); } inline const void *tcpListen(const struct sockaddr *localAddress,void *uptr) { } inline const void *tcpConnect(const struct sockaddr *remoteAddress,void *uptr) { } inline unsigned long tcpSend(const void *sock,const void *data,unsigned long len) { WireSocket &sws = *(const_cast (reinterpret_cast(sock))); long n = ::send(sws.sock,data,len,0); return ((n > 0) ? (unsigned long)n : 0); } /** * Set whether we want to be notified via the TCP writability handler when a socket is writable * * @param sock TCP connection socket (other types are not valid) * @param notifyWritable Want writable notifications? */ inline const void tcpSetNotifyWritable(const void *sock,bool notifyWritable) { WireSocket &sws = *(const_cast (reinterpret_cast(sock))); if (notifyWritable) { FD_SET(sws.sock,&_writefds); } else { FD_CLR(sws.sock,&_writefds); } } inline void poll(unsigned long timeout) { char buf[131072]; struct sockaddr_storage ss; struct timeval tv; fd_set rfds,wfds,efds; memcpy(&rfds,&_readfds,sizeof(rfds)); memcpy(&wfds,&_writefds,sizeof(wfds)); memcpy(&efds,&_exceptfds,sizeof(efds)); tv.tv_sec = (long)(timeout / 1000); tv.tv_usec = (long)((timeout % 1000) * 1000); select((int)_nfds + 1,&rfds,&wfds,&efds,(timeout > 0) ? &tv : (struct timeval *)0); if (FD_ISSET(_whackReceiveSocket,&rfds)) { char tmp[16]; #if defined(_WIN32) || defined(_WIN64) ::recv(_whackReceiveSocket,tmp,16,0); #else ::read(_whackReceiveSocket,tmp,16); #endif } for(unsigned long i=0;iclose((const void *)&(_socks[i]),true); else if (FD_ISSET(_socks[i].sock,&wfds)) { socklen_t slen = sizeof(ss); if (::getpeername(_socks[i].sock,(strut sockaddr *)&ss,&slen) != 0) this->close((const void *)&(_socks[i]),true); else { _socks[i].type = ZT_WIRE_SOCKET_TCP_OUT_CONNECTED; FD_SET(_socks[i].sock,&_readfds); FD_CLR(_socks[i].sock,&_writefds); FD_CLR(_socks[i].sock,&_exceptfds); try { _tcpConnectHandler((const void *)&(_socks[i]),&(_socks[i].uptr),true); } catch ( ... ) {} } } break; case ZT_WIRE_SOCKET_TCP_OUT_CONNECTED: case ZT_WIRE_SOCKET_TCP_IN: if (FD_ISSET(_socks[i].sock,&rfds)) { long n = (long)::recv(_socks[i].sock,buf,sizeof(buf),0); if (n <= 0) { this->close((const void *)&(_socks[i]),true); } else { try { _tcpDataHandler((const void *)&(_socks[i]),&(_socks[i].uptr),(void *)buf,(unsigned long)n); } catch ( ... ) {} } } if ((FD_ISSET(_socks[i].sock,&wfds))&&(FD_ISSET(_socks[i].sock,&_writefds))) { try { _tcpWritableHandler((const void *)&(_socks[i]),&(_socks[i].uptr)); } catch ( ... ) {} } break; case ZT_WIRE_SOCKET_TCP_LISTEN: if (FD_ISSET(_socks[i].sock,&rfds)) { memset(&ss,0,sizeof(ss)); socklen_t slen = sizeof(ss); ZT_SELECTWIRE_SOCKFD_TYPE s = ::accept(_socks[i].sock,(struct sockaddr *)&ss,&slen); if (ZT_SELECTWIRE_SOCKFD_VALID(s)) { #if defined(_WIN32) || defined(_WIN64) if (_noDelay) { BOOL f = TRUE; setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } u_long iMode=1; ioctlsocket(s,FIONBIO,&iMode); #else if (_noDelay) { int f = 1; setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } fcntl(s,F_SETFL,O_NONBLOCK); #endif bool haveSlot = false; for(unsigned long k=0;k _nfds) _nfds = (long)s; FD_SET(s,&_readfds); _socks[k].type = ZT_WIRE_SOCKET_UDP; _socks[k].sock = s; _socks[k].uptr = (void *)0; memcpy(&(_socks[k].saddr),&ss,sizeof(struct sockaddr_storage)); try { _tcpAcceptHandler((const void *)&(_socks[i]),(const void *)&(_socks[k]),&(_socks[i].uptr),&(_socks[k].uptr),(const struct sockaddr *)&(_socks[k].saddr)); } catch ( ... ) {} break; } } if (!haveSlot) ZT_SELECTWIRE_CLOSE_SOCKET(s); } } break; case ZT_WIRE_SOCKET_UDP: if (FD_ISSET(_socks[i].sock,&rfds)) { memset(&ss,0,sizeof(ss)); socklen_t slen = sizeof(ss); long n = (long)::recvfrom(_socks[i].sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen); if (n > 0) { try { _dgHandler((const void *)&(_socks[i]),&(_socks[i].uptr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n); } catch ( ... ) {} } } break; default: break; } } } inline void close(const void *sock,bool callHandlers) { WireSocket &sws = *(const_cast (reinterpret_cast(sock))); const WireSocketType oldType = sws.type; sws.type = ZT_WIRE_SOCKET_NULL; FD_CLR(sws.sock,&_readfds); FD_CLR(sws.sock,&_writefds); FD_CLR(sws.sock,&_exceptfds); ZT_SELECTWIRE_CLOSE_SOCKET(sws.sock); switch(oldType) { case ZT_WIRE_SOCKET_TCP_OUT_PENDING: if (callHandlers) { try { _tcpConnectHandler((const void *)&sws,&(sws.uptr),false); } catch ( ... ) {} } break; case ZT_WIRE_SOCKET_TCP_OUT_CONNECTED: case ZT_WIRE_SOCKET_TCP_IN: if (callHandlers) { try { _tcpCloseHandler((const void *)&sws,sws.uptr); } catch ( ... ) {} } break; default: break; } if ((long)sws.sock >= _nfds) { long nfds = (long)_whackSendSocket; for(unsigned long i=0;i nfds)) nfds = (long)_socks[i].sock; } _nfds = nfds; } } private: #if defined(_WIN32) || defined(_WIN64) // hack copied from StackOverflow, behaves a bit like pipe() on *nix systems inline void _winPipeHack(SOCKET fds[2]) const { struct sockaddr_in inaddr; struct sockaddr addr; SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); if (lst == INVALID_SOCKET) throw std::runtime_error("unable to create pipes for select() abort"); 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); if (fds[0] == INVALID_SOCKET) throw std::runtime_error("unable to create pipes for select() abort"); connect(fds[0],&addr,len); fds[1]=accept(lst,0,0); closesocket(lst); } #endif enum WireSocketType { ZT_WIRE_SOCKET_TCP_OUT_PENDING = 0x00, ZT_WIRE_SOCKET_TCP_OUT_CONNECTED = 0x01, ZT_WIRE_SOCKET_TCP_IN = 0x02, ZT_WIRE_SOCKET_TCP_LISTEN = 0x03, // isTCP() == ((type & 0x03) != 0) ZT_WIRE_SOCKET_RAW = 0x04, ZT_WIRE_SOCKET_UDP = 0x05, ZT_WIRE_SOCKET_NULL = 0x06 }; struct WireSocket { WireSocketType type; ZT_SELECTWIRE_SOCKFD_TYPE sock; void *uptr; // user-settable pointer ZT_SELECTWIRE_SOCKADDR_STORAGE_TYPE saddr; // from address for TCP_IN, local address otherwise }; inline bool _isTCP(const WireSocket &sws) const throw() { return ((((unsigned int)sws.type) & 0x03) != 0); } ON_DATAGRAM_FUNCTION _dgHandler; ON_TCP_CONNECT_FUNCTION _tcpConnectHandler; ON_TCP_ACCEPT_FUNCTION _tcpAcceptHandler; ON_TCP_CLOSE_FUNCTION _tcpCloseHandler; ON_TCP_DATA_FUNCTION _tcpDataHandler; ON_TCP_WRITABLE_FUNCTION _tcpWritableHandler; WireSocket _socks[ZT_SELECTWIRE_MAX_SOCKETS]; fd_set _readfds,_writefds,_exceptfds; long _nfds; ZT_SELECTWIRE_SOCKFD_TYPE _whackReceiveSocket; ZT_SELECTWIRE_SOCKFD_TYPE _whackSendSocket; bool _noDelay; }; } // namespace ZeroTier #endif