Imported Upstream version 1.1.14

This commit is contained in:
NAStools 2016-11-01 16:45:16 -07:00
commit bb232b9d52
557 changed files with 115164 additions and 0 deletions

View file

@ -0,0 +1,160 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
*
* 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/>.
*/
#ifndef ZT_CLUSTERDEFINITION_HPP
#define ZT_CLUSTERDEFINITION_HPP
#ifdef ZT_ENABLE_CLUSTER
#include <vector>
#include <algorithm>
#include "../node/Constants.hpp"
#include "../node/Utils.hpp"
#include "../node/NonCopyable.hpp"
#include "../osdep/OSUtils.hpp"
#include "ClusterGeoIpService.hpp"
namespace ZeroTier {
/**
* Parser for cluster definition file
*/
class ClusterDefinition : NonCopyable
{
public:
struct MemberDefinition
{
MemberDefinition() : id(0),x(0),y(0),z(0) { name[0] = (char)0; }
unsigned int id;
int x,y,z;
char name[256];
InetAddress clusterEndpoint;
std::vector<InetAddress> zeroTierEndpoints;
};
/**
* Load and initialize cluster definition and GeoIP data if any
*
* @param myAddress My ZeroTier address
* @param pathToClusterFile Path to cluster definition file
* @throws std::runtime_error Invalid cluster definition or unable to load data
*/
ClusterDefinition(uint64_t myAddress,const char *pathToClusterFile)
{
std::string cf;
if (!OSUtils::readFile(pathToClusterFile,cf))
return;
char myAddressStr[64];
Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
std::vector<std::string> lines(Utils::split(cf.c_str(),"\r\n","",""));
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
std::vector<std::string> fields(Utils::split(l->c_str()," \t","",""));
if ((fields.size() < 5)||(fields[0][0] == '#')||(fields[0] != myAddressStr))
continue;
// <address> geo <CSV path> <ip start column> <ip end column> <latitutde column> <longitude column>
if (fields[1] == "geo") {
if ((fields.size() >= 7)&&(OSUtils::fileExists(fields[2].c_str()))) {
int ipStartColumn = Utils::strToInt(fields[3].c_str());
int ipEndColumn = Utils::strToInt(fields[4].c_str());
int latitudeColumn = Utils::strToInt(fields[5].c_str());
int longitudeColumn = Utils::strToInt(fields[6].c_str());
if (_geo.load(fields[2].c_str(),ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn) <= 0)
throw std::runtime_error(std::string("failed to load geo-ip data from ")+fields[2]);
}
continue;
}
// <address> <ID> <name> <backplane IP/port(s)> <ZT frontplane IP/port(s)> <x,y,z>
int id = Utils::strToUInt(fields[1].c_str());
if ((id < 0)||(id > ZT_CLUSTER_MAX_MEMBERS))
throw std::runtime_error(std::string("invalid cluster member ID: ")+fields[1]);
MemberDefinition &md = _md[id];
md.id = (unsigned int)id;
if (fields.size() >= 6) {
std::vector<std::string> xyz(Utils::split(fields[5].c_str(),",","",""));
md.x = (xyz.size() > 0) ? Utils::strToInt(xyz[0].c_str()) : 0;
md.y = (xyz.size() > 1) ? Utils::strToInt(xyz[1].c_str()) : 0;
md.z = (xyz.size() > 2) ? Utils::strToInt(xyz[2].c_str()) : 0;
}
Utils::scopy(md.name,sizeof(md.name),fields[2].c_str());
md.clusterEndpoint.fromString(fields[3]);
if (!md.clusterEndpoint)
continue;
std::vector<std::string> zips(Utils::split(fields[4].c_str(),",","",""));
for(std::vector<std::string>::iterator zip(zips.begin());zip!=zips.end();++zip) {
InetAddress i;
i.fromString(*zip);
if (i)
md.zeroTierEndpoints.push_back(i);
}
_ids.push_back((unsigned int)id);
}
std::sort(_ids.begin(),_ids.end());
}
/**
* @return All member definitions in this cluster by ID (ID is array index)
*/
inline const MemberDefinition &operator[](unsigned int id) const throw() { return _md[id]; }
/**
* @return Number of members in this cluster
*/
inline unsigned int size() const throw() { return (unsigned int)_ids.size(); }
/**
* @return IDs of members in this cluster sorted by ID
*/
inline const std::vector<unsigned int> &ids() const throw() { return _ids; }
/**
* @return GeoIP service for this cluster
*/
inline ClusterGeoIpService &geo() throw() { return _geo; }
/**
* @return A vector (new copy) containing all cluster members
*/
inline std::vector<MemberDefinition> members() const
{
std::vector<MemberDefinition> m;
for(std::vector<unsigned int>::const_iterator i(_ids.begin());i!=_ids.end();++i)
m.push_back(_md[*i]);
return m;
}
private:
MemberDefinition _md[ZT_CLUSTER_MAX_MEMBERS];
std::vector<unsigned int> _ids;
ClusterGeoIpService _geo;
};
} // namespace ZeroTier
#endif // ZT_ENABLE_CLUSTER
#endif

View file

@ -0,0 +1,235 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
*
* 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/>.
*/
#ifdef ZT_ENABLE_CLUSTER
#include <math.h>
#include <cmath>
#include "ClusterGeoIpService.hpp"
#include "../node/Utils.hpp"
#include "../osdep/OSUtils.hpp"
#define ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY 10000
namespace ZeroTier {
ClusterGeoIpService::ClusterGeoIpService() :
_pathToCsv(),
_ipStartColumn(-1),
_ipEndColumn(-1),
_latitudeColumn(-1),
_longitudeColumn(-1),
_lastFileCheckTime(0),
_csvModificationTime(0),
_csvFileSize(0)
{
}
ClusterGeoIpService::~ClusterGeoIpService()
{
}
bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z)
{
Mutex::Lock _l(_lock);
if ((_pathToCsv.length() > 0)&&((OSUtils::now() - _lastFileCheckTime) > ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY)) {
_lastFileCheckTime = OSUtils::now();
if ((_csvFileSize != OSUtils::getFileSize(_pathToCsv.c_str()))||(_csvModificationTime != OSUtils::getLastModified(_pathToCsv.c_str())))
_load(_pathToCsv.c_str(),_ipStartColumn,_ipEndColumn,_latitudeColumn,_longitudeColumn);
}
/* We search by looking up the upper bound of the sorted vXdb vectors
* and then iterating down for a matching IP range. We stop when we hit
* the beginning or an entry whose start and end are before the IP we
* are searching. */
if ((ip.ss_family == AF_INET)&&(_v4db.size() > 0)) {
_V4E key;
key.start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr));
std::vector<_V4E>::const_iterator i(std::upper_bound(_v4db.begin(),_v4db.end(),key));
while (i != _v4db.begin()) {
--i;
if ((key.start >= i->start)&&(key.start <= i->end)) {
x = i->x;
y = i->y;
z = i->z;
//printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
return true;
} else if ((key.start > i->start)&&(key.start > i->end))
break;
}
} else if ((ip.ss_family == AF_INET6)&&(_v6db.size() > 0)) {
_V6E key;
memcpy(key.start,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
std::vector<_V6E>::const_iterator i(std::upper_bound(_v6db.begin(),_v6db.end(),key));
while (i != _v6db.begin()) {
--i;
const int s_vs_s = memcmp(key.start,i->start,16);
const int s_vs_e = memcmp(key.start,i->end,16);
if ((s_vs_s >= 0)&&(s_vs_e <= 0)) {
x = i->x;
y = i->y;
z = i->z;
//printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
return true;
} else if ((s_vs_s > 0)&&(s_vs_e > 0))
break;
}
}
return false;
}
void ClusterGeoIpService::_parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
{
std::vector<std::string> ls(Utils::split(line,",\t","\\","\"'"));
if ( ((ipStartColumn >= 0)&&(ipStartColumn < (int)ls.size()))&&
((ipEndColumn >= 0)&&(ipEndColumn < (int)ls.size()))&&
((latitudeColumn >= 0)&&(latitudeColumn < (int)ls.size()))&&
((longitudeColumn >= 0)&&(longitudeColumn < (int)ls.size())) ) {
InetAddress ipStart(ls[ipStartColumn].c_str(),0);
InetAddress ipEnd(ls[ipEndColumn].c_str(),0);
const double lat = strtod(ls[latitudeColumn].c_str(),(char **)0);
const double lon = strtod(ls[longitudeColumn].c_str(),(char **)0);
if ((ipStart.ss_family == ipEnd.ss_family)&&(ipStart)&&(ipEnd)&&(std::isfinite(lat))&&(std::isfinite(lon))) {
const double latRadians = lat * 0.01745329251994; // PI / 180
const double lonRadians = lon * 0.01745329251994; // PI / 180
const double cosLat = cos(latRadians);
const int x = (int)round((-6371.0) * cosLat * cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers
const int y = (int)round(6371.0 * sin(latRadians));
const int z = (int)round(6371.0 * cosLat * sin(lonRadians));
if (ipStart.ss_family == AF_INET) {
v4db.push_back(_V4E());
v4db.back().start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipStart)->sin_addr.s_addr));
v4db.back().end = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipEnd)->sin_addr.s_addr));
v4db.back().lat = (float)lat;
v4db.back().lon = (float)lon;
v4db.back().x = x;
v4db.back().y = y;
v4db.back().z = z;
//printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
} else if (ipStart.ss_family == AF_INET6) {
v6db.push_back(_V6E());
memcpy(v6db.back().start,reinterpret_cast<const struct sockaddr_in6 *>(&ipStart)->sin6_addr.s6_addr,16);
memcpy(v6db.back().end,reinterpret_cast<const struct sockaddr_in6 *>(&ipEnd)->sin6_addr.s6_addr,16);
v6db.back().lat = (float)lat;
v6db.back().lon = (float)lon;
v6db.back().x = x;
v6db.back().y = y;
v6db.back().z = z;
//printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
}
}
}
}
long ClusterGeoIpService::_load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
{
// assumes _lock is locked
FILE *f = fopen(pathToCsv,"rb");
if (!f)
return -1;
std::vector<_V4E> v4db;
std::vector<_V6E> v6db;
v4db.reserve(16777216);
v6db.reserve(16777216);
char buf[4096];
char linebuf[1024];
unsigned int lineptr = 0;
for(;;) {
int n = (int)fread(buf,1,sizeof(buf),f);
if (n <= 0)
break;
for(int i=0;i<n;++i) {
if ((buf[i] == '\r')||(buf[i] == '\n')||(buf[i] == (char)0)) {
if (lineptr) {
linebuf[lineptr] = (char)0;
_parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
}
lineptr = 0;
} else if (lineptr < (unsigned int)sizeof(linebuf))
linebuf[lineptr++] = buf[i];
}
}
if (lineptr) {
linebuf[lineptr] = (char)0;
_parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
}
fclose(f);
if ((v4db.size() > 0)||(v6db.size() > 0)) {
std::sort(v4db.begin(),v4db.end());
std::sort(v6db.begin(),v6db.end());
_pathToCsv = pathToCsv;
_ipStartColumn = ipStartColumn;
_ipEndColumn = ipEndColumn;
_latitudeColumn = latitudeColumn;
_longitudeColumn = longitudeColumn;
_lastFileCheckTime = OSUtils::now();
_csvModificationTime = OSUtils::getLastModified(pathToCsv);
_csvFileSize = OSUtils::getFileSize(pathToCsv);
_v4db.swap(v4db);
_v6db.swap(v6db);
return (long)(_v4db.size() + _v6db.size());
} else {
return 0;
}
}
} // namespace ZeroTier
#endif // ZT_ENABLE_CLUSTER
/*
int main(int argc,char **argv)
{
char buf[1024];
ZeroTier::ClusterGeoIpService gip;
printf("loading...\n");
gip.load("/Users/api/Code/ZeroTier/Infrastructure/root-servers/zerotier-one/cluster-geoip.csv",0,1,5,6);
printf("... done!\n"); fflush(stdout);
while (gets(buf)) { // unsafe, testing only
ZeroTier::InetAddress addr(buf,0);
printf("looking up: %s\n",addr.toString().c_str()); fflush(stdout);
int x = 0,y = 0,z = 0;
if (gip.locate(addr,x,y,z)) {
//printf("%s: %d,%d,%d\n",addr.toString().c_str(),x,y,z); fflush(stdout);
} else {
printf("%s: not found!\n",addr.toString().c_str()); fflush(stdout);
}
}
return 0;
}
*/

View file

@ -0,0 +1,143 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
*
* 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/>.
*/
#ifndef ZT_CLUSTERGEOIPSERVICE_HPP
#define ZT_CLUSTERGEOIPSERVICE_HPP
#ifdef ZT_ENABLE_CLUSTER
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include <algorithm>
#include "../node/Constants.hpp"
#include "../node/Mutex.hpp"
#include "../node/NonCopyable.hpp"
#include "../node/InetAddress.hpp"
namespace ZeroTier {
/**
* Loads a GeoIP CSV into memory for fast lookup, reloading as needed
*
* This was designed around the CSV from https://db-ip.com but can be used
* with any similar GeoIP CSV database that is presented in the form of an
* IP range and lat/long coordinates.
*
* It loads the whole database into memory, which can be kind of large. If
* the CSV file changes, the changes are loaded automatically.
*/
class ClusterGeoIpService : NonCopyable
{
public:
ClusterGeoIpService();
~ClusterGeoIpService();
/**
* Load or reload CSV file
*
* CSV column indexes start at zero. CSVs can be quoted with single or
* double quotes. Whitespace before or after commas is ignored. Backslash
* may be used for escaping whitespace as well.
*
* @param pathToCsv Path to (uncompressed) CSV file
* @param ipStartColumn Column with IP range start
* @param ipEndColumn Column with IP range end (inclusive)
* @param latitudeColumn Column with latitude
* @param longitudeColumn Column with longitude
* @return Number of valid records loaded or -1 on error (invalid file, not found, etc.)
*/
inline long load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
{
Mutex::Lock _l(_lock);
return _load(pathToCsv,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
}
/**
* Attempt to locate an IP
*
* This returns true if x, y, and z are set. If the return value is false
* the values of x, y, and z are undefined.
*
* @param ip IPv4 or IPv6 address
* @param x Reference to variable to receive X
* @param y Reference to variable to receive Y
* @param z Reference to variable to receive Z
* @return True if coordinates were set
*/
bool locate(const InetAddress &ip,int &x,int &y,int &z);
/**
* @return True if IP database/service is available for queries (otherwise locate() will always be false)
*/
inline bool available() const
{
Mutex::Lock _l(_lock);
return ((_v4db.size() + _v6db.size()) > 0);
}
private:
struct _V4E
{
uint32_t start;
uint32_t end;
float lat,lon;
int16_t x,y,z;
inline bool operator<(const _V4E &e) const { return (start < e.start); }
};
struct _V6E
{
uint8_t start[16];
uint8_t end[16];
float lat,lon;
int16_t x,y,z;
inline bool operator<(const _V6E &e) const { return (memcmp(start,e.start,16) < 0); }
};
static void _parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);
long _load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);
std::string _pathToCsv;
int _ipStartColumn;
int _ipEndColumn;
int _latitudeColumn;
int _longitudeColumn;
uint64_t _lastFileCheckTime;
uint64_t _csvModificationTime;
int64_t _csvFileSize;
std::vector<_V4E> _v4db;
std::vector<_V6E> _v6db;
Mutex _lock;
};
} // namespace ZeroTier
#endif // ZT_ENABLE_CLUSTER
#endif

628
service/ControlPlane.cpp Normal file
View file

@ -0,0 +1,628 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
*
* 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/>.
*/
#include "ControlPlane.hpp"
#include "OneService.hpp"
#include "../version.h"
#include "../include/ZeroTierOne.h"
#ifdef ZT_USE_SYSTEM_HTTP_PARSER
#include <http_parser.h>
#else
#include "../ext/http-parser/http_parser.h"
#endif
#ifdef ZT_USE_SYSTEM_JSON_PARSER
#include <json-parser/json.h>
#else
#include "../ext/json-parser/json.h"
#endif
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
#include "../controller/SqliteNetworkController.hpp"
#endif
#include "../node/InetAddress.hpp"
#include "../node/Node.hpp"
#include "../node/Utils.hpp"
#include "../osdep/OSUtils.hpp"
namespace ZeroTier {
static std::string _jsonEscape(const char *s)
{
std::string buf;
for(const char *p=s;(*p);++p) {
switch(*p) {
case '\t': buf.append("\\t"); break;
case '\b': buf.append("\\b"); break;
case '\r': buf.append("\\r"); break;
case '\n': buf.append("\\n"); break;
case '\f': buf.append("\\f"); break;
case '"': buf.append("\\\""); break;
case '\\': buf.append("\\\\"); break;
case '/': buf.append("\\/"); break;
default: buf.push_back(*p); break;
}
}
return buf;
}
static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); }
static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int count)
{
std::string buf;
buf.push_back('[');
for(unsigned int i=0;i<count;++i) {
if (i > 0)
buf.push_back(',');
buf.push_back('"');
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(ss[i]))->toString()));
buf.push_back('"');
}
buf.push_back(']');
return buf;
}
static std::string _jsonEnumerate(const ZT_VirtualNetworkRoute *routes,unsigned int count)
{
std::string buf;
buf.push_back('[');
for(unsigned int i=0;i<count;++i) {
if (i > 0)
buf.push_back(',');
buf.append("{\"target\":\"");
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString()));
buf.append("\",\"via\":");
if (routes[i].via.ss_family == routes[i].target.ss_family) {
buf.push_back('"');
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString()));
buf.append("\",");
} else buf.append("null,");
char tmp[1024];
Utils::snprintf(tmp,sizeof(tmp),"\"flags\":%u,\"metric\":%u}",(unsigned int)routes[i].flags,(unsigned int)routes[i].metric);
buf.append(tmp);
}
buf.push_back(']');
return buf;
}
static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings)
{
char json[4096];
char prefix[32];
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
return;
for(unsigned int i=0;i<depth;++i)
prefix[i] = '\t';
prefix[depth] = '\0';
const char *nstatus = "",*ntype = "";
switch(nc->status) {
case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: nstatus = "REQUESTING_CONFIGURATION"; break;
case ZT_NETWORK_STATUS_OK: nstatus = "OK"; break;
case ZT_NETWORK_STATUS_ACCESS_DENIED: nstatus = "ACCESS_DENIED"; break;
case ZT_NETWORK_STATUS_NOT_FOUND: nstatus = "NOT_FOUND"; break;
case ZT_NETWORK_STATUS_PORT_ERROR: nstatus = "PORT_ERROR"; break;
case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: nstatus = "CLIENT_TOO_OLD"; break;
}
switch(nc->type) {
case ZT_NETWORK_TYPE_PRIVATE: ntype = "PRIVATE"; break;
case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
}
Utils::snprintf(json,sizeof(json),
"%s{\n"
"%s\t\"nwid\": \"%.16llx\",\n"
"%s\t\"mac\": \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
"%s\t\"name\": \"%s\",\n"
"%s\t\"status\": \"%s\",\n"
"%s\t\"type\": \"%s\",\n"
"%s\t\"mtu\": %u,\n"
"%s\t\"dhcp\": %s,\n"
"%s\t\"bridge\": %s,\n"
"%s\t\"broadcastEnabled\": %s,\n"
"%s\t\"portError\": %d,\n"
"%s\t\"netconfRevision\": %lu,\n"
"%s\t\"assignedAddresses\": %s,\n"
"%s\t\"routes\": %s,\n"
"%s\t\"portDeviceName\": \"%s\",\n"
"%s\t\"allowManaged\": %s,\n"
"%s\t\"allowGlobal\": %s,\n"
"%s\t\"allowDefault\": %s\n"
"%s}",
prefix,
prefix,nc->nwid,
prefix,(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff),
prefix,_jsonEscape(nc->name).c_str(),
prefix,nstatus,
prefix,ntype,
prefix,nc->mtu,
prefix,(nc->dhcp == 0) ? "false" : "true",
prefix,(nc->bridge == 0) ? "false" : "true",
prefix,(nc->broadcastEnabled == 0) ? "false" : "true",
prefix,nc->portError,
prefix,nc->netconfRevision,
prefix,_jsonEnumerate(nc->assignedAddresses,nc->assignedAddressCount).c_str(),
prefix,_jsonEnumerate(nc->routes,nc->routeCount).c_str(),
prefix,_jsonEscape(portDeviceName).c_str(),
prefix,(localSettings.allowManaged) ? "true" : "false",
prefix,(localSettings.allowGlobal) ? "true" : "false",
prefix,(localSettings.allowDefault) ? "true" : "false",
prefix);
buf.append(json);
}
static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *pp,unsigned int count)
{
char json[1024];
char prefix[32];
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
return std::string();
for(unsigned int i=0;i<depth;++i)
prefix[i] = '\t';
prefix[depth] = '\0';
std::string buf;
for(unsigned int i=0;i<count;++i) {
if (i > 0)
buf.push_back(',');
Utils::snprintf(json,sizeof(json),
"{\n"
"%s\t\"address\": \"%s\",\n"
"%s\t\"lastSend\": %llu,\n"
"%s\t\"lastReceive\": %llu,\n"
"%s\t\"active\": %s,\n"
"%s\t\"preferred\": %s,\n"
"%s\t\"trustedPathId\": %llu\n"
"%s}",
prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
prefix,pp[i].lastSend,
prefix,pp[i].lastReceive,
prefix,(pp[i].active == 0) ? "false" : "true",
prefix,(pp[i].preferred == 0) ? "false" : "true",
prefix,pp[i].trustedPathId,
prefix);
buf.append(json);
}
return buf;
}
static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
{
char json[1024];
char prefix[32];
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
return;
for(unsigned int i=0;i<depth;++i)
prefix[i] = '\t';
prefix[depth] = '\0';
const char *prole = "";
switch(peer->role) {
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
case ZT_PEER_ROLE_RELAY: prole = "RELAY"; break;
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
}
Utils::snprintf(json,sizeof(json),
"%s{\n"
"%s\t\"address\": \"%.10llx\",\n"
"%s\t\"lastUnicastFrame\": %llu,\n"
"%s\t\"lastMulticastFrame\": %llu,\n"
"%s\t\"versionMajor\": %d,\n"
"%s\t\"versionMinor\": %d,\n"
"%s\t\"versionRev\": %d,\n"
"%s\t\"version\": \"%d.%d.%d\",\n"
"%s\t\"latency\": %u,\n"
"%s\t\"role\": \"%s\",\n"
"%s\t\"paths\": [%s]\n"
"%s}",
prefix,
prefix,peer->address,
prefix,peer->lastUnicastFrame,
prefix,peer->lastMulticastFrame,
prefix,peer->versionMajor,
prefix,peer->versionMinor,
prefix,peer->versionRev,
prefix,peer->versionMajor,peer->versionMinor,peer->versionRev,
prefix,peer->latency,
prefix,prole,
prefix,_jsonEnumerate(depth+1,peer->paths,peer->pathCount).c_str(),
prefix);
buf.append(json);
}
ControlPlane::ControlPlane(OneService *svc,Node *n,const char *uiStaticPath) :
_svc(svc),
_node(n),
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
_controller((SqliteNetworkController *)0),
#endif
_uiStaticPath((uiStaticPath) ? uiStaticPath : "")
{
}
ControlPlane::~ControlPlane()
{
}
unsigned int ControlPlane::handleRequest(
const InetAddress &fromAddress,
unsigned int httpMethod,
const std::string &path,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType)
{
char json[8194];
unsigned int scode = 404;
std::vector<std::string> ps(Utils::split(path.c_str(),"/","",""));
std::map<std::string,std::string> urlArgs;
Mutex::Lock _l(_lock);
if (!((fromAddress.ipsEqual(InetAddress::LO4))||(fromAddress.ipsEqual(InetAddress::LO6))))
return 403; // Forbidden: we only allow access from localhost right now
/* Note: this is kind of restricted in what it'll take. It does not support
* URL encoding, and /'s in URL args will screw it up. But the only URL args
* it really uses in ?jsonp=funcionName, and otherwise it just takes simple
* paths to simply-named resources. */
if (ps.size() > 0) {
std::size_t qpos = ps[ps.size() - 1].find('?');
if (qpos != std::string::npos) {
std::string args(ps[ps.size() - 1].substr(qpos + 1));
ps[ps.size() - 1] = ps[ps.size() - 1].substr(0,qpos);
std::vector<std::string> asplit(Utils::split(args.c_str(),"&","",""));
for(std::vector<std::string>::iterator a(asplit.begin());a!=asplit.end();++a) {
std::size_t eqpos = a->find('=');
if (eqpos == std::string::npos)
urlArgs[*a] = "";
else urlArgs[a->substr(0,eqpos)] = a->substr(eqpos + 1);
}
}
} else {
ps.push_back(std::string("index.html"));
}
bool isAuth = false;
{
std::map<std::string,std::string>::const_iterator ah(headers.find("x-zt1-auth"));
if ((ah != headers.end())&&(_authTokens.count(ah->second) > 0)) {
isAuth = true;
} else {
ah = urlArgs.find("auth");
if ((ah != urlArgs.end())&&(_authTokens.count(ah->second) > 0))
isAuth = true;
}
}
if (httpMethod == HTTP_GET) {
std::string ext;
std::size_t dotIdx = ps[0].find_last_of('.');
if (dotIdx != std::string::npos)
ext = ps[0].substr(dotIdx);
if ((ps.size() == 1)&&(ext.length() >= 2)&&(ext[0] == '.')) {
/* Static web pages can be served without authentication to enable a simple web
* UI. This is still only allowed from approved IP addresses. Anything with a
* dot in the first path element (e.g. foo.html) is considered a static page,
* as nothing in the API is so named. */
if (_uiStaticPath.length() > 0) {
if (ext == ".html")
responseContentType = "text/html";
else if (ext == ".js")
responseContentType = "application/javascript";
else if (ext == ".jsx")
responseContentType = "text/jsx";
else if (ext == ".json")
responseContentType = "application/json";
else if (ext == ".css")
responseContentType = "text/css";
else if (ext == ".png")
responseContentType = "image/png";
else if (ext == ".jpg")
responseContentType = "image/jpeg";
else if (ext == ".gif")
responseContentType = "image/gif";
else if (ext == ".txt")
responseContentType = "text/plain";
else if (ext == ".xml")
responseContentType = "text/xml";
else if (ext == ".svg")
responseContentType = "image/svg+xml";
else responseContentType = "application/octet-stream";
scode = OSUtils::readFile((_uiStaticPath + ZT_PATH_SEPARATOR_S + ps[0]).c_str(),responseBody) ? 200 : 404;
} else {
scode = 404;
}
} else if (isAuth) {
/* Things that require authentication -- a.k.a. everything but static web app pages. */
if (ps[0] == "status") {
responseContentType = "application/json";
ZT_NodeStatus status;
_node->status(&status);
std::string clusterJson;
#ifdef ZT_ENABLE_CLUSTER
{
ZT_ClusterStatus cs;
_node->clusterStatus(&cs);
if (cs.clusterSize >= 1) {
char t[1024];
Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members\": [",cs.myId,cs.clusterSize);
clusterJson.append(t);
for(unsigned int i=0;i<cs.clusterSize;++i) {
Utils::snprintf(t,sizeof(t),"%s\t\t\t{\n\t\t\t\t\"id\": %u,\n\t\t\t\t\"msSinceLastHeartbeat\": %u,\n\t\t\t\t\"alive\": %s,\n\t\t\t\t\"x\": %d,\n\t\t\t\t\"y\": %d,\n\t\t\t\t\"z\": %d,\n\t\t\t\t\"load\": %llu,\n\t\t\t\t\"peers\": %llu\n\t\t\t}",
((i == 0) ? "\n" : ",\n"),
cs.members[i].id,
cs.members[i].msSinceLastHeartbeat,
(cs.members[i].alive != 0) ? "true" : "false",
cs.members[i].x,
cs.members[i].y,
cs.members[i].z,
cs.members[i].load,
cs.members[i].peers);
clusterJson.append(t);
}
clusterJson.append(" ]\n\t\t}");
}
}
#endif
Utils::snprintf(json,sizeof(json),
"{\n"
"\t\"address\": \"%.10llx\",\n"
"\t\"publicIdentity\": \"%s\",\n"
"\t\"worldId\": %llu,\n"
"\t\"worldTimestamp\": %llu,\n"
"\t\"online\": %s,\n"
"\t\"tcpFallbackActive\": %s,\n"
"\t\"versionMajor\": %d,\n"
"\t\"versionMinor\": %d,\n"
"\t\"versionRev\": %d,\n"
"\t\"version\": \"%d.%d.%d\",\n"
"\t\"clock\": %llu,\n"
"\t\"cluster\": %s\n"
"}\n",
status.address,
status.publicIdentity,
status.worldId,
status.worldTimestamp,
(status.online) ? "true" : "false",
(_svc->tcpFallbackActive()) ? "true" : "false",
ZEROTIER_ONE_VERSION_MAJOR,
ZEROTIER_ONE_VERSION_MINOR,
ZEROTIER_ONE_VERSION_REVISION,
ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,
(unsigned long long)OSUtils::now(),
((clusterJson.length() > 0) ? clusterJson.c_str() : "null"));
responseBody = json;
scode = 200;
} else if (ps[0] == "config") {
responseContentType = "application/json";
responseBody = "{}"; // TODO
scode = 200;
} else if (ps[0] == "network") {
ZT_VirtualNetworkList *nws = _node->networks();
if (nws) {
if (ps.size() == 1) {
// Return [array] of all networks
responseContentType = "application/json";
responseBody = "[\n";
for(unsigned long i=0;i<nws->networkCount;++i) {
if (i > 0)
responseBody.append(",");
OneService::NetworkSettings localSettings;
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
_jsonAppend(1,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
}
responseBody.append("\n]\n");
scode = 200;
} else if (ps.size() == 2) {
// Return a single network by ID or 404 if not found
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
for(unsigned long i=0;i<nws->networkCount;++i) {
if (nws->networks[i].nwid == wantnw) {
responseContentType = "application/json";
OneService::NetworkSettings localSettings;
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
responseBody.push_back('\n');
scode = 200;
break;
}
}
} // else 404
_node->freeQueryResult((void *)nws);
} else scode = 500;
} else if (ps[0] == "peer") {
ZT_PeerList *pl = _node->peers();
if (pl) {
if (ps.size() == 1) {
// Return [array] of all peers
responseContentType = "application/json";
responseBody = "[\n";
for(unsigned long i=0;i<pl->peerCount;++i) {
if (i > 0)
responseBody.append(",\n");
_jsonAppend(1,responseBody,&(pl->peers[i]));
}
responseBody.append("\n]\n");
scode = 200;
} else if (ps.size() == 2) {
// Return a single peer by ID or 404 if not found
uint64_t wantp = Utils::hexStrToU64(ps[1].c_str());
for(unsigned long i=0;i<pl->peerCount;++i) {
if (pl->peers[i].address == wantp) {
responseContentType = "application/json";
_jsonAppend(0,responseBody,&(pl->peers[i]));
responseBody.push_back('\n');
scode = 200;
break;
}
}
} // else 404
_node->freeQueryResult((void *)pl);
} else scode = 500;
} else if (ps[0] == "newIdentity") {
// Return a newly generated ZeroTier identity -- this is primarily for debugging
// and testing to make it easy for automated test scripts to generate test IDs.
Identity newid;
newid.generate();
responseBody = newid.toString(true);
responseContentType = "text/plain";
scode = 200;
} else {
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
if (_controller)
scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
#else
scode = 404;
#endif
}
} else scode = 401; // isAuth == false
} else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
if (isAuth) {
if (ps[0] == "config") {
// TODO
} else if (ps[0] == "network") {
if (ps.size() == 2) {
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
_node->join(wantnw,(void *)0); // does nothing if we are a member
ZT_VirtualNetworkList *nws = _node->networks();
if (nws) {
for(unsigned long i=0;i<nws->networkCount;++i) {
if (nws->networks[i].nwid == wantnw) {
OneService::NetworkSettings localSettings;
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
json_value *j = json_parse(body.c_str(),body.length());
if (j) {
if (j->type == json_object) {
for(unsigned int k=0;k<j->u.object.length;++k) {
if (!strcmp(j->u.object.values[k].name,"allowManaged")) {
if (j->u.object.values[k].value->type == json_boolean)
localSettings.allowManaged = (j->u.object.values[k].value->u.boolean != 0);
} else if (!strcmp(j->u.object.values[k].name,"allowGlobal")) {
if (j->u.object.values[k].value->type == json_boolean)
localSettings.allowGlobal = (j->u.object.values[k].value->u.boolean != 0);
} else if (!strcmp(j->u.object.values[k].name,"allowDefault")) {
if (j->u.object.values[k].value->type == json_boolean)
localSettings.allowDefault = (j->u.object.values[k].value->u.boolean != 0);
}
}
}
json_value_free(j);
}
_svc->setNetworkSettings(nws->networks[i].nwid,localSettings);
responseContentType = "application/json";
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
responseBody.push_back('\n');
scode = 200;
break;
}
}
_node->freeQueryResult((void *)nws);
} else scode = 500;
}
} else {
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
if (_controller)
scode = _controller->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
#else
scode = 404;
#endif
}
} else scode = 401; // isAuth == false
} else if (httpMethod == HTTP_DELETE) {
if (isAuth) {
if (ps[0] == "config") {
// TODO
} else if (ps[0] == "network") {
ZT_VirtualNetworkList *nws = _node->networks();
if (nws) {
if (ps.size() == 2) {
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
for(unsigned long i=0;i<nws->networkCount;++i) {
if (nws->networks[i].nwid == wantnw) {
_node->leave(wantnw,(void **)0);
responseBody = "true";
responseContentType = "application/json";
scode = 200;
break;
}
}
} // else 404
_node->freeQueryResult((void *)nws);
} else scode = 500;
} else {
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
if (_controller)
scode = _controller->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
#else
scode = 404;
#endif
}
} else {
scode = 401; // isAuth = false
}
} else {
scode = 400;
responseBody = "Method not supported.";
}
// Wrap result in jsonp function call if the user included a jsonp= url argument.
// Also double-check isAuth since forbidding this without auth feels safer.
std::map<std::string,std::string>::const_iterator jsonp(urlArgs.find("jsonp"));
if ((isAuth)&&(jsonp != urlArgs.end())&&(responseContentType == "application/json")) {
if (responseBody.length() > 0)
responseBody = jsonp->second + "(" + responseBody + ");";
else responseBody = jsonp->second + "(null);";
responseContentType = "application/javascript";
}
return scode;
}
} // namespace ZeroTier

102
service/ControlPlane.hpp Normal file
View file

@ -0,0 +1,102 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
*
* 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/>.
*/
#ifndef ZT_ONE_CONTROLPLANE_HPP
#define ZT_ONE_CONTROLPLANE_HPP
#include <string>
#include <map>
#include <set>
#include "../include/ZeroTierOne.h"
#include "../node/Mutex.hpp"
namespace ZeroTier {
class OneService;
class Node;
class SqliteNetworkController;
struct InetAddress;
/**
* HTTP control plane and static web server
*/
class ControlPlane
{
public:
ControlPlane(OneService *svc,Node *n,const char *uiStaticPath);
~ControlPlane();
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
/**
* Set controller, which will be available under /controller
*
* @param c Network controller instance
*/
inline void setController(SqliteNetworkController *c)
{
Mutex::Lock _l(_lock);
_controller = c;
}
#endif
/**
* Add an authentication token for API access
*/
inline void addAuthToken(const char *tok)
{
Mutex::Lock _l(_lock);
_authTokens.insert(std::string(tok));
}
/**
* Handle HTTP request
*
* @param fromAddress Originating IP address of request
* @param httpMethod HTTP method (as defined in ext/http-parser/http_parser.h)
* @param path Request path
* @param headers Request headers
* @param body Request body
* @param responseBody Result parameter: fill with response data
* @param responseContentType Result parameter: fill with content type
* @return HTTP response code
*/
unsigned int handleRequest(
const InetAddress &fromAddress,
unsigned int httpMethod,
const std::string &path,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType);
private:
OneService *const _svc;
Node *const _node;
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
SqliteNetworkController *_controller;
#endif
std::string _uiStaticPath;
std::set<std::string> _authTokens;
Mutex _lock;
};
} // namespace ZeroTier
#endif

1993
service/OneService.cpp Normal file

File diff suppressed because it is too large Load diff

187
service/OneService.hpp Normal file
View file

@ -0,0 +1,187 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
*
* 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/>.
*/
#ifndef ZT_ONESERVICE_HPP
#define ZT_ONESERVICE_HPP
#include <string>
namespace ZeroTier {
/**
* Local service for ZeroTier One as system VPN/NFV provider
*
* If built with ZT_ENABLE_NETWORK_CONTROLLER defined, this includes and
* runs controller/SqliteNetworkController with a database called
* controller.db in the specified home directory.
*
* If built with ZT_AUTO_UPDATE, an official ZeroTier update URL is
* periodically checked and updates are automatically downloaded, verified
* against a built-in list of update signing keys, and installed. This is
* only supported for certain platforms.
*
* If built with ZT_ENABLE_CLUSTER, a 'cluster' file is checked and if
* present is read to determine the identity of other cluster members.
*/
class OneService
{
public:
/**
* Returned by node main if/when it terminates
*/
enum ReasonForTermination
{
/**
* Instance is still running
*/
ONE_STILL_RUNNING = 0,
/**
* Normal shutdown
*/
ONE_NORMAL_TERMINATION = 1,
/**
* A serious unrecoverable error has occurred
*/
ONE_UNRECOVERABLE_ERROR = 2,
/**
* Your identity has collided with another
*/
ONE_IDENTITY_COLLISION = 3
};
/**
* Local settings for each network
*/
struct NetworkSettings
{
/**
* Allow this network to configure IP addresses and routes?
*/
bool allowManaged;
/**
* Allow configuration of IPs and routes within global (Internet) IP space?
*/
bool allowGlobal;
/**
* Allow overriding of system default routes for "full tunnel" operation?
*/
bool allowDefault;
};
/**
* @return Platform default home path or empty string if this platform doesn't have one
*/
static std::string platformDefaultHomePath();
/**
* @return Auto-update URL or empty string if auto-updates unsupported or not enabled
*/
static std::string autoUpdateUrl();
/**
* Create a new instance of the service
*
* Once created, you must call the run() method to actually start
* processing.
*
* The port is saved to a file in the home path called zerotier-one.port,
* which is used by the CLI and can be used to see which port was chosen if
* 0 (random port) is picked.
*
* @param hp Home path
* @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
*/
static OneService *newInstance(
const char *hp,
unsigned int port);
virtual ~OneService();
/**
* Execute the service main I/O loop until terminated
*
* The terminate() method may be called from a signal handler or another
* thread to terminate execution. Otherwise this will not return unless
* another condition terminates execution such as a fatal error.
*/
virtual ReasonForTermination run() = 0;
/**
* @return Reason for terminating or ONE_STILL_RUNNING if running
*/
virtual ReasonForTermination reasonForTermination() const = 0;
/**
* @return Fatal error message or empty string if none
*/
virtual std::string fatalErrorMessage() const = 0;
/**
* @return System device name corresponding with a given ZeroTier network ID or empty string if not opened yet or network ID not found
*/
virtual std::string portDeviceName(uint64_t nwid) const = 0;
/**
* @return True if TCP fallback is currently active
*/
virtual bool tcpFallbackActive() const = 0;
/**
* Terminate background service (can be called from other threads)
*/
virtual void terminate() = 0;
/**
* Get local settings for a network
*
* @param nwid Network ID
* @param settings Buffer to fill with local network settings
* @return True if network was found and settings is filled
*/
virtual bool getNetworkSettings(const uint64_t nwid,NetworkSettings &settings) const = 0;
/**
* Set local settings for a network
*
* @param nwid Network ID
* @param settings New network local settings
* @return True if network was found and setting modified
*/
virtual bool setNetworkSettings(const uint64_t nwid,const NetworkSettings &settings) = 0;
/**
* @return True if service is still running
*/
inline bool isRunning() const { return (this->reasonForTermination() == ONE_STILL_RUNNING); }
protected:
OneService() {}
private:
OneService(const OneService &one) {}
inline OneService &operator=(const OneService &one) { return *this; }
};
} // namespace ZeroTier
#endif

122
service/README.md Normal file
View file

@ -0,0 +1,122 @@
ZeroTier One Network Virtualization Service
======
This is the common background service implementation for ZeroTier One, the VPN-like OS-level network virtualization service.
It provides a ready-made core I/O loop and a local HTTP-based JSON control bus for controlling the service. This control bus HTTP server can also serve the files in ui/ if this folder's contents are installed in the ZeroTier home folder. The ui/ implements a React-based HTML5 user interface which is then wrappered for various platforms via MacGap, Windows .NET WebControl, etc. It can also be used locally from scripts or via *curl*.
### Network Virtualization Service API
The JSON API supports GET, POST/PUT, and DELETE. PUT is treated as a synonym for POST. Other methods including HEAD are not supported.
Values POSTed to the JSON API are *extremely* type sensitive. Things *must* be of the indicated type, otherwise they will be ignored or will generate an error. Anything quoted is a string so booleans and integers must lack quotes. Booleans must be *true* or *false* and nothing else. Integers cannot contain decimal points or they are floats (and vice versa). If something seems to be getting ignored or set to a strange value, or if you receive errors, check the type of all JSON fields you are submitting against the types listed below. Unrecognized fields in JSON objects are also ignored.
API requests must be authenticated via an authentication token. ZeroTier One saves this token in the *authtoken.secret* file in its working directory. This token may be supplied via the *auth* URL parameter (e.g. '?auth=...') or via the *X-ZT1-Auth* HTTP request header. Static UI pages are the only thing the server will allow without authentication.
A *jsonp* URL argument may be supplied to request JSONP encapsulation. A JSONP response is sent as a script with its JSON response payload wrapped in a call to the function name supplied as the argument to *jsonp*.
#### /status
* Purpose: Get running node status and addressing info
* Methods: GET
* Returns: { object }
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>address</td><td>string</td><td>10-digit hexadecimal ZeroTier address of this node</td><td>no</td></tr>
<tr><td>publicIdentity</td><td>string</td><td>Full public ZeroTier identity of this node</td><td>no</td></tr>
<tr><td>online</td><td>boolean</td><td>Does this node appear to have upstream network access?</td><td>no</td></tr>
<tr><td>tcpFallbackActive</td><td>boolean</td><td>Is TCP fallback mode active?</td><td>no</td></tr>
<tr><td>versionMajor</td><td>integer</td><td>ZeroTier major version</td><td>no</td></tr>
<tr><td>versionMinor</td><td>integer</td><td>ZeroTier minor version</td><td>no</td></tr>
<tr><td>versionRev</td><td>integer</td><td>ZeroTier revision</td><td>no</td></tr>
<tr><td>version</td><td>string</td><td>Version in major.minor.rev format</td><td>no</td></tr>
<tr><td>clock</td><td>integer</td><td>Node system clock in ms since epoch</td><td>no</td></tr>
</table>
#### /config
* Purpose: Get or set local configuration
* Methods: GET, POST
* Returns: { object }
No local configuration options are exposed yet.
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
</table>
#### /network
* Purpose: Get all network memberships
* Methods: GET
* Returns: [ {object}, ... ]
Getting /network returns an array of all networks that this node has joined. See below for network object format.
#### /network/\<network ID\>
* Purpose: Get, join, or leave a network
* Methods: GET, POST, DELETE
* Returns: { object }
To join a network, POST to it. Since networks have no mandatory writable parameters, POST data is optional and may be omitted. Example: POST to /network/8056c2e21c000001 to join the public "Earth" network. To leave a network, DELETE it e.g. DELETE /network/8056c2e21c000001.
Most network settings are not writable, as they are defined by the network controller.
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>nwid</td><td>string</td><td>16-digit hex network ID</td><td>no</td></tr>
<tr><td>mac</td><td>string</td><td>Ethernet MAC address of virtual network port</td><td>no</td></tr>
<tr><td>name</td><td>string</td><td>Network short name as configured on network controller</td><td>no</td></tr>
<tr><td>status</td><td>string</td><td>Network status: OK, ACCESS_DENIED, PORT_ERROR, etc.</td><td>no</td></tr>
<tr><td>type</td><td>string</td><td>Network type, currently PUBLIC or PRIVATE</td><td>no</td></tr>
<tr><td>mtu</td><td>integer</td><td>Ethernet MTU</td><td>no</td></tr>
<tr><td>dhcp</td><td>boolean</td><td>If true, DHCP may be used to obtain an IP address</td><td>no</td></tr>
<tr><td>bridge</td><td>boolean</td><td>If true, this node may bridge in other Ethernet devices</td><td>no</td></tr>
<tr><td>broadcastEnabled</td><td>boolean</td><td>Is Ethernet broadcast (ff:ff:ff:ff:ff:ff) allowed?</td><td>no</td></tr>
<tr><td>portError</td><td>integer</td><td>Error code (if any) returned by underlying OS "tap" driver</td><td>no</td></tr>
<tr><td>netconfRevision</td><td>integer</td><td>Network configuration revision ID</td><td>no</td></tr>
<tr><td>multicastSubscriptions</td><td>[string]</td><td>Multicast memberships as array of MAC/ADI tuples</td><td>no</td></tr>
<tr><td>assignedAddresses</td><td>[string]</td><td>ZeroTier-managed IP address assignments as array of IP/netmask bits tuples</td><td>no</td></tr>
<tr><td>portDeviceName</td><td>string</td><td>OS-specific network device name (if available)</td><td>no</td></tr>
</table>
#### /peer
* Purpose: Get all peers
* Methods: GET
* Returns: [ {object}, ... ]
Getting /peer returns an array of peer objects for all current peers. See below for peer object format.
#### /peer/\<address\>
* Purpose: Get information about a peer
* Methods: GET
* Returns: { object }
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>address</td><td>string</td><td>10-digit hex ZeroTier address</td><td>no</td></tr>
<tr><td>lastUnicastFrame</td><td>integer</td><td>Time of last unicast frame in ms since epoch</td><td>no</td></tr>
<tr><td>lastMulticastFrame</td><td>integer</td><td>Time of last multicast frame in ms since epoch</td><td>no</td></tr>
<tr><td>versionMajor</td><td>integer</td><td>Major version of remote if known</td><td>no</td></tr>
<tr><td>versionMinor</td><td>integer</td><td>Minor version of remote if known</td><td>no</td></tr>
<tr><td>versionRev</td><td>integer</td><td>Revision of remote if known</td><td>no</td></tr>
<tr><td>version</td><td>string</td><td>Version in major.minor.rev format</td><td>no</td></tr>
<tr><td>latency</td><td>integer</td><td>Latency in milliseconds if known</td><td>no</td></tr>
<tr><td>role</td><td>string</td><td>LEAF, HUB, or ROOTSERVER</td><td>no</td></tr>
<tr><td>paths</td><td>[object]</td><td>Array of path objects (see below)</td><td>no</td></tr>
</table>
Path objects describe direct physical paths to peer. If no path objects are listed, peer is only reachable via indirect relay fallback. Path object format is:
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>address</td><td>string</td><td>Physical socket address e.g. IP/port for UDP</td><td>no</td></tr>
<tr><td>lastSend</td><td>integer</td><td>Last send via this path in ms since epoch</td><td>no</td></tr>
<tr><td>lastReceive</td><td>integer</td><td>Last receive via this path in ms since epoch</td><td>no</td></tr>
<tr><td>fixed</td><td>boolean</td><td>If true, this is a statically-defined "fixed" path</td><td>no</td></tr>
<tr><td>preferred</td><td>boolean</td><td>If true, this is the current preferred path</td><td>no</td></tr>
</table>