Merge dev

This commit is contained in:
Adam Ierymenko 2015-11-30 15:17:31 -08:00
commit 7e28161638
39 changed files with 3797 additions and 320 deletions

View file

@ -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) {

320
osdep/PortMapper.cpp Normal file
View file

@ -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

View file

@ -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

View file

@ -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