diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2015-11-30 15:17:31 -0800 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2015-11-30 15:17:31 -0800 |
commit | 7e28161638a3c64754f55fe84d88917741b49019 (patch) | |
tree | d6debc90b571e0d32e4d8f30a0803a47b0762eaa /osdep | |
parent | ef4472e1857b86b5a45493a3c3566c7761260dad (diff) | |
parent | 40a4ba6e39bbc59b320eef2f6839cf30c3f7c8b1 (diff) | |
download | infinitytier-7e28161638a3c64754f55fe84d88917741b49019.tar.gz infinitytier-7e28161638a3c64754f55fe84d88917741b49019.zip |
Merge dev
Diffstat (limited to 'osdep')
-rw-r--r-- | osdep/LinuxEthernetTap.cpp | 11 | ||||
-rw-r--r-- | osdep/PortMapper.cpp | 320 | ||||
-rw-r--r-- | osdep/PortMapper.hpp (renamed from osdep/UPNPClient.hpp) | 29 | ||||
-rw-r--r-- | osdep/UPNPClient.cpp | 198 |
4 files changed, 340 insertions, 218 deletions
diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 73eb0cc5..f250abd3 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -210,8 +210,8 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip) long cpid = (long)vfork(); if (cpid == 0) { OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); - ::execl("/sbin/ip","/sbin/ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); - ::execl("/usr/sbin/ip","/usr/sbin/ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); + setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1); + ::execlp("ip","ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); ::_exit(-1); } else { int exitcode = -1; @@ -238,12 +238,11 @@ bool LinuxEthernetTap::addIp(const InetAddress &ip) long cpid = (long)vfork(); if (cpid == 0) { OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); + setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1); if (ip.isV4()) { - ::execl("/sbin/ip","/sbin/ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0); - ::execl("/usr/sbin/ip","/usr/sbin/ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0); + ::execlp("ip","ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0); } else { - ::execl("/sbin/ip","/sbin/ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); - ::execl("/usr/sbin/ip","/usr/sbin/ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); + ::execlp("ip","ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); } ::_exit(-1); } else if (cpid > 0) { diff --git a/osdep/PortMapper.cpp b/osdep/PortMapper.cpp new file mode 100644 index 00000000..5c017931 --- /dev/null +++ b/osdep/PortMapper.cpp @@ -0,0 +1,320 @@ +/* + * 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 <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/ + */ + +#ifdef ZT_USE_MINIUPNPC + +// Uncomment to dump debug messages +#define ZT_PORTMAPPER_TRACE 1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <string> + +#include "../node/Utils.hpp" +#include "OSUtils.hpp" +#include "PortMapper.hpp" + +#ifdef __WINDOWS__ +#ifndef MINIUPNP_STATICLIB +#define MINIUPNP_STATICLIB +#endif +#endif + +#include "../ext/miniupnpc/miniupnpc.h" +#include "../ext/miniupnpc/upnpcommands.h" +#include "../ext/libnatpmp/natpmp.h" + +namespace ZeroTier { + +class PortMapperImpl +{ +public: + PortMapperImpl(int localUdpPortToMap,const char *un) : + run(true), + localPort(localUdpPortToMap), + uniqueName(un) + { + } + + ~PortMapperImpl() {} + + void threadMain() + throw() + { + int mode = 0; // 0 == NAT-PMP, 1 == UPnP + +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: started for UDP port %d"ZT_EOL_S,localPort); +#endif + + while (run) { + + // --------------------------------------------------------------------- + // NAT-PMP mode (preferred) + // --------------------------------------------------------------------- + if (mode == 0) { + natpmp_t natpmp; + natpmpresp_t response; + int r = 0; + + bool natPmpSuccess = false; + for(int tries=0;tries<60;++tries) { + int tryPort = (int)localPort + tries; + if (tryPort >= 65535) + tryPort = (tryPort - 65535) + 1025; + + memset(&natpmp,0,sizeof(natpmp)); + memset(&response,0,sizeof(response)); + + if (initnatpmp(&natpmp,0,0) != 0) { + mode = 1; +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: NAT-PMP: init failed, switching to UPnP mode"ZT_EOL_S); +#endif + break; + } + + InetAddress publicAddress; + sendpublicaddressrequest(&natpmp); + uint64_t myTimeout = OSUtils::now() + 5000; + do { + fd_set fds; + struct timeval timeout; + FD_ZERO(&fds); + FD_SET(natpmp.s, &fds); + getnatpmprequesttimeout(&natpmp, &timeout); + select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + r = readnatpmpresponseorretry(&natpmp, &response); + if (OSUtils::now() >= myTimeout) + break; + } while (r == NATPMP_TRYAGAIN); + if (r == 0) { + publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0); + } else { +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: NAT-PMP: request for external address failed, aborting..."ZT_EOL_S); +#endif + closenatpmp(&natpmp); + break; + } + + sendnewportmappingrequest(&natpmp,NATPMP_PROTOCOL_UDP,localPort,tryPort,(ZT_PORTMAPPER_REFRESH_DELAY * 2) / 1000); + myTimeout = OSUtils::now() + 10000; + do { + fd_set fds; + struct timeval timeout; + FD_ZERO(&fds); + FD_SET(natpmp.s, &fds); + getnatpmprequesttimeout(&natpmp, &timeout); + select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + r = readnatpmpresponseorretry(&natpmp, &response); + if (OSUtils::now() >= myTimeout) + break; + } while (r == NATPMP_TRYAGAIN); + if (r == 0) { + publicAddress.setPort(response.pnu.newportmapping.mappedpublicport); +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: NAT-PMP: mapped %u to %s"ZT_EOL_S,(unsigned int)localPort,publicAddress.toString().c_str()); +#endif + Mutex::Lock sl(surface_l); + surface.clear(); + surface.push_back(publicAddress); + natPmpSuccess = true; + closenatpmp(&natpmp); + break; + } else { + closenatpmp(&natpmp); + // continue + } + } + + if (!natPmpSuccess) { + mode = 1; +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: NAT-PMP: request failed, switching to UPnP mode"ZT_EOL_S); +#endif + } + } + // --------------------------------------------------------------------- + + // --------------------------------------------------------------------- + // UPnP mode + // --------------------------------------------------------------------- + if (mode == 1) { + char lanaddr[4096]; + char externalip[4096]; // no range checking? so make these buffers larger than any UDP packet a uPnP server could send us as a precaution :P + char inport[16]; + char outport[16]; + struct UPNPUrls urls; + struct IGDdatas data; + + int upnpError = 0; + UPNPDev *devlist = upnpDiscoverAll(5000,(const char *)0,(const char *)0,0,0,2,&upnpError); + if (devlist) { + +#ifdef ZT_PORTMAPPER_TRACE + { + UPNPDev *dev = devlist; + while (dev) { + fprintf(stderr,"PortMapper: found UPnP device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st); + dev = dev->pNext; + } + } +#endif + + memset(lanaddr,0,sizeof(lanaddr)); + memset(externalip,0,sizeof(externalip)); + memset(&urls,0,sizeof(urls)); + memset(&data,0,sizeof(data)); + Utils::snprintf(inport,sizeof(inport),"%d",localPort); + + if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) { +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: UPnP: my LAN IP address: %s"ZT_EOL_S,lanaddr); +#endif + if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) { +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: UPnP: my external IP address: %s"ZT_EOL_S,externalip); +#endif + + for(int tries=0;tries<60;++tries) { + int tryPort = (int)localPort + tries; + if (tryPort >= 65535) + tryPort = (tryPort - 65535) + 1025; + Utils::snprintf(outport,sizeof(outport),"%u",tryPort); + + // First check and see if this port is already mapped to the + // same unique name. If so, keep this mapping and don't try + // to map again since this can break buggy routers. But don't + // fail if this command fails since not all routers support it. + { + char haveIntClient[128]; // 128 == big enough for all these as per miniupnpc "documentation" + char haveIntPort[128]; + char haveDesc[128]; + char haveEnabled[128]; + char haveLeaseDuration[128]; + memset(haveIntClient,0,sizeof(haveIntClient)); + memset(haveIntPort,0,sizeof(haveIntPort)); + memset(haveDesc,0,sizeof(haveDesc)); + memset(haveEnabled,0,sizeof(haveEnabled)); + memset(haveLeaseDuration,0,sizeof(haveLeaseDuration)); + if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) { +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: UPnP: reusing previously reserved external port: %s"ZT_EOL_S,outport); +#endif + Mutex::Lock sl(surface_l); + surface.clear(); + InetAddress tmp(externalip); + tmp.setPort(tryPort); + surface.push_back(tmp); + break; + } + } + + // Try to map this port + int mapResult = 0; + if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) { +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: UPnP: reserved external port: %s"ZT_EOL_S,outport); +#endif + Mutex::Lock sl(surface_l); + surface.clear(); + InetAddress tmp(externalip); + tmp.setPort(tryPort); + surface.push_back(tmp); + break; + } else { +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult); +#endif + Thread::sleep(1000); + } + } + + } else { + mode = 0; +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode"ZT_EOL_S); +#endif + } + } else { + mode = 0; +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode"ZT_EOL_S); +#endif + } + + freeUPNPDevlist(devlist); + + } else { + mode = 0; +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d"ZT_EOL_S,upnpError); +#endif + } + } + // --------------------------------------------------------------------- + +#ifdef ZT_PORTMAPPER_TRACE + fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY); +#endif + Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY); + } + + delete this; + } + + volatile bool run; + int localPort; + std::string uniqueName; + + Mutex surface_l; + std::vector<InetAddress> surface; +}; + +PortMapper::PortMapper(int localUdpPortToMap,const char *uniqueName) +{ + _impl = new PortMapperImpl(localUdpPortToMap,uniqueName); + Thread::start(_impl); +} + +PortMapper::~PortMapper() +{ + _impl->run = false; +} + +std::vector<InetAddress> PortMapper::get() const +{ + Mutex::Lock _l(_impl->surface_l); + return _impl->surface; +} + +} // namespace ZeroTier + +#endif // ZT_USE_MINIUPNPC diff --git a/osdep/UPNPClient.hpp b/osdep/PortMapper.hpp index a6b05b5f..3643445b 100644 --- a/osdep/UPNPClient.hpp +++ b/osdep/PortMapper.hpp @@ -25,11 +25,11 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef ZT_UPNPCLIENT_HPP -#define ZT_UPNPCLIENT_HPP - #ifdef ZT_USE_MINIUPNPC +#ifndef ZT_PORTMAPPER_HPP +#define ZT_PORTMAPPER_HPP + #include <vector> #include "../node/Constants.hpp" @@ -40,28 +40,29 @@ /** * How frequently should we refresh our UPNP/NAT-PnP/whatever state? */ -#define ZT_UPNP_CLIENT_REFRESH_DELAY 600000 +#define ZT_PORTMAPPER_REFRESH_DELAY 300000 namespace ZeroTier { -class UPNPClientImpl; +class PortMapperImpl; /** - * UPnP/NAT-PnP daemon thread + * UPnP/NAT-PnP port mapping "daemon" */ -class UPNPClient +class PortMapper { - friend class UPNPClientImpl; + friend class PortMapperImpl; public: /** - * Create and start UPNP client service + * Create and start port mapper service * * @param localUdpPortToMap Port we want visible to the outside world + * @param name Unique name of this endpoint (based on ZeroTier address) */ - UPNPClient(int localUdpPortToMap); + PortMapper(int localUdpPortToMap,const char *uniqueName); - ~UPNPClient(); + ~PortMapper(); /** * @return All current external mappings for our port @@ -69,11 +70,11 @@ public: std::vector<InetAddress> get() const; private: - UPNPClientImpl *_impl; + PortMapperImpl *_impl; }; } // namespace ZeroTier -#endif // ZT_USE_MINIUPNPC - #endif + +#endif // ZT_USE_MINIUPNPC diff --git a/osdep/UPNPClient.cpp b/osdep/UPNPClient.cpp deleted file mode 100644 index b7c7e768..00000000 --- a/osdep/UPNPClient.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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 <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/ - */ - -#ifdef ZT_USE_MINIUPNPC - -// Uncomment to dump debug messages -//#define ZT_UPNP_TRACE 1 - -// Uncomment to build a main() for ad-hoc testing -//#define ZT_UPNP_TEST 1 - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "../node/Utils.hpp" -#include "UPNPClient.hpp" - -#ifdef __WINDOWS__ -#ifndef MINIUPNP_STATICLIB -#define MINIUPNP_STATICLIB -#endif -#endif - -#include "../ext/miniupnpc/miniupnpc.h" -#include "../ext/miniupnpc/upnpcommands.h" - -namespace ZeroTier { - -class UPNPClientImpl -{ -public: - UPNPClientImpl(int localUdpPortToMap) : - run(true), - localPort(localUdpPortToMap) - { - } - - void threadMain() - throw() - { - char lanaddr[4096]; - char externalip[4096]; // no range checking? so make these buffers larger than any UDP packet a uPnP server could send us as a precaution :P - char inport[16]; - char outport[16]; - struct UPNPUrls urls; - struct IGDdatas data; - -#ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: started for UDP port %d"ZT_EOL_S,localPort); -#endif - - unsigned int tryPortStart = 0; - Utils::getSecureRandom(&tryPortStart,sizeof(tryPortStart)); - tryPortStart = (tryPortStart % (65535 - 1025)) + 1025; - - while (run) { - { - int upnpError = 0; - UPNPDev *devlist = upnpDiscover(2000,(const char *)0,(const char *)0,0,0,0,&upnpError); - if (devlist) { -#ifdef ZT_UPNP_TRACE - { - UPNPDev *dev = devlist; - while (dev) { - fprintf(stderr,"UPNPClient: found device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st); - dev = dev->pNext; - } - } -#endif - - memset(lanaddr,0,sizeof(lanaddr)); - memset(externalip,0,sizeof(externalip)); - memset(&urls,0,sizeof(urls)); - memset(&data,0,sizeof(data)); - Utils::snprintf(inport,sizeof(inport),"%d",localPort); - - if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) { -#ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: my LAN IP address: %s"ZT_EOL_S,lanaddr); -#endif - if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) { -#ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: my external IP address: %s"ZT_EOL_S,externalip); -#endif - - for(int tries=0;tries<64;++tries) { - int tryPort = (int)tryPortStart + tries; - if (tryPort >= 65535) - tryPort = (tryPort - 65535) + 1025; - Utils::snprintf(outport,sizeof(outport),"%u",tryPort); - - int mapResult = 0; - if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,"ZeroTier","UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) { - #ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: reserved external port: %s"ZT_EOL_S,outport); - #endif - { - Mutex::Lock sl(surface_l); - surface.clear(); - InetAddress tmp(externalip); - tmp.setPort(tryPort); - surface.push_back(tmp); - } - break; - } else { - #ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: UPNP_AddAnyPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult); - #endif - Thread::sleep(1000); - } - } - } else { -#ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: UPNP_GetExternalIPAddress failed"ZT_EOL_S); -#endif - } - } else { -#ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: UPNP_GetValidIGD failed"ZT_EOL_S); -#endif - } - - freeUPNPDevlist(devlist); - } else { -#ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: upnpDiscover error code: %d"ZT_EOL_S,upnpError); -#endif - } - } - -#ifdef ZT_UPNP_TRACE - fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_UPNP_CLIENT_REFRESH_DELAY); -#endif - Thread::sleep(ZT_UPNP_CLIENT_REFRESH_DELAY); - } - delete this; - } - - volatile bool run; - int localPort; - Mutex surface_l; - std::vector<InetAddress> surface; -}; - -UPNPClient::UPNPClient(int localUdpPortToMap) -{ - _impl = new UPNPClientImpl(localUdpPortToMap); - Thread::start(_impl); -} - -UPNPClient::~UPNPClient() -{ - _impl->run = false; -} - -std::vector<InetAddress> UPNPClient::get() const -{ - Mutex::Lock _l(_impl->surface_l); - return _impl->surface; -} - -} // namespace ZeroTier - -#ifdef ZT_UPNP_TEST -int main(int argc,char **argv) -{ - ZeroTier::UPNPClient *client = new ZeroTier::UPNPClient(12345); - ZeroTier::Thread::sleep(0xffffffff); // wait forever - return 0; -} -#endif - -#endif // ZT_USE_MINIUPNPC |