This commit is contained in:
Joseph Henry 2018-01-10 12:43:18 -08:00
commit 6e1823ac81
46 changed files with 106 additions and 3377 deletions

View File

@ -1,525 +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/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifndef _WIN32
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#endif
#include "JSONDB.hpp"
#include "EmbeddedNetworkController.hpp"
namespace ZeroTier {
static const nlohmann::json _EMPTY_JSON(nlohmann::json::object());
JSONDB::JSONDB(const std::string &basePath,EmbeddedNetworkController *parent) :
_parent(parent),
_basePath(basePath),
_rawInput(-1),
_rawOutput(-1),
_summaryThreadRun(true),
_dataReady(false)
{
#ifndef __WINDOWS__
if (_basePath == "-") {
// If base path is "-" we run in Central harnessed mode. We read pseudo-http-requests from stdin and write
// them to stdout.
_rawInput = STDIN_FILENO;
_rawOutput = STDOUT_FILENO;
fcntl(_rawInput,F_SETFL,O_NONBLOCK);
} else {
#endif
// Default mode of operation is to store files in the filesystem
OSUtils::mkdir(_basePath.c_str());
OSUtils::lockDownFile(_basePath.c_str(),true); // networks might contain auth tokens, etc., so restrict directory permissions
#ifndef __WINDOWS__
}
#endif
_networks_m.lock(); // locked until data is loaded, etc.
if (_rawInput < 0) {
_load(basePath);
_dataReady = true;
_networks_m.unlock();
} else {
// In harnessed mode we leave the lock locked and wait for our initial DB from Central.
_summaryThread = Thread::start(this);
}
}
JSONDB::~JSONDB()
{
Thread t;
{
Mutex::Lock _l(_summaryThread_m);
_summaryThreadRun = false;
t = _summaryThread;
}
if (t)
Thread::join(t);
}
bool JSONDB::writeRaw(const std::string &n,const std::string &obj)
{
if (_rawOutput >= 0) {
#ifndef __WINDOWS__
if (obj.length() > 0) {
Mutex::Lock _l(_rawLock);
//fprintf(stderr,"%s\n",obj.c_str());
if ((long)write(_rawOutput,obj.data(),obj.length()) == (long)obj.length()) {
if (write(_rawOutput,"\n",1) == 1)
return true;
}
} else return true;
#endif
return false;
} else {
const std::string path(_genPath(n,true));
if (!path.length())
return false;
return OSUtils::writeFile(path.c_str(),obj);
}
}
bool JSONDB::hasNetwork(const uint64_t networkId) const
{
Mutex::Lock _l(_networks_m);
return (_networks.find(networkId) != _networks.end());
}
bool JSONDB::getNetwork(const uint64_t networkId,nlohmann::json &config) const
{
Mutex::Lock _l(_networks_m);
const std::unordered_map<uint64_t,_NW>::const_iterator i(_networks.find(networkId));
if (i == _networks.end())
return false;
config = nlohmann::json::from_msgpack(i->second.config);
return true;
}
bool JSONDB::getNetworkSummaryInfo(const uint64_t networkId,NetworkSummaryInfo &ns) const
{
Mutex::Lock _l(_networks_m);
const std::unordered_map<uint64_t,_NW>::const_iterator i(_networks.find(networkId));
if (i == _networks.end())
return false;
ns = i->second.summaryInfo;
return true;
}
int JSONDB::getNetworkAndMember(const uint64_t networkId,const uint64_t nodeId,nlohmann::json &networkConfig,nlohmann::json &memberConfig,NetworkSummaryInfo &ns) const
{
Mutex::Lock _l(_networks_m);
const std::unordered_map<uint64_t,_NW>::const_iterator i(_networks.find(networkId));
if (i == _networks.end())
return 0;
const std::unordered_map< uint64_t,std::vector<uint8_t> >::const_iterator j(i->second.members.find(nodeId));
if (j == i->second.members.end())
return 1;
networkConfig = nlohmann::json::from_msgpack(i->second.config);
memberConfig = nlohmann::json::from_msgpack(j->second);
ns = i->second.summaryInfo;
return 3;
}
bool JSONDB::getNetworkMember(const uint64_t networkId,const uint64_t nodeId,nlohmann::json &memberConfig) const
{
Mutex::Lock _l(_networks_m);
const std::unordered_map<uint64_t,_NW>::const_iterator i(_networks.find(networkId));
if (i == _networks.end())
return false;
const std::unordered_map< uint64_t,std::vector<uint8_t> >::const_iterator j(i->second.members.find(nodeId));
if (j == i->second.members.end())
return false;
memberConfig = nlohmann::json::from_msgpack(j->second);
return true;
}
void JSONDB::saveNetwork(const uint64_t networkId,const nlohmann::json &networkConfig)
{
char n[64];
OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId);
writeRaw(n,OSUtils::jsonDump(networkConfig,-1));
{
Mutex::Lock _l(_networks_m);
_NW &nw = _networks[networkId];
nw.config = nlohmann::json::to_msgpack(networkConfig);
}
_recomputeSummaryInfo(networkId);
}
void JSONDB::saveNetworkMember(const uint64_t networkId,const uint64_t nodeId,const nlohmann::json &memberConfig)
{
char n[256];
OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId);
writeRaw(n,OSUtils::jsonDump(memberConfig,-1));
{
Mutex::Lock _l(_networks_m);
std::vector<uint8_t> &m = _networks[networkId].members[nodeId];
m = nlohmann::json::to_msgpack(memberConfig);
_members[nodeId].insert(networkId);
}
_recomputeSummaryInfo(networkId);
}
nlohmann::json JSONDB::eraseNetwork(const uint64_t networkId)
{
if (_rawOutput >= 0) {
// In harnessed mode, DB deletes occur in the Central database and we do
// not need to erase files.
} else {
std::vector<uint64_t> memberIds;
{
Mutex::Lock _l(_networks_m);
const std::unordered_map<uint64_t,_NW>::iterator i(_networks.find(networkId));
if (i == _networks.end())
return _EMPTY_JSON;
for(std::unordered_map< uint64_t,std::vector<uint8_t> >::iterator m(i->second.members.begin());m!=i->second.members.end();++m)
memberIds.push_back(m->first);
}
for(std::vector<uint64_t>::iterator m(memberIds.begin());m!=memberIds.end();++m)
eraseNetworkMember(networkId,*m,false);
char n[256];
OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId);
const std::string path(_genPath(n,false));
if (path.length())
OSUtils::rm(path.c_str());
}
// This also erases all members from the memory cache
{
Mutex::Lock _l(_networks_m);
std::unordered_map<uint64_t,_NW>::iterator i(_networks.find(networkId));
if (i == _networks.end())
return _EMPTY_JSON; // sanity check, shouldn't happen
nlohmann::json tmp(nlohmann::json::from_msgpack(i->second.config));
_networks.erase(i);
return tmp;
}
}
nlohmann::json JSONDB::eraseNetworkMember(const uint64_t networkId,const uint64_t nodeId,bool recomputeSummaryInfo)
{
if (_rawOutput >= 0) {
// In harnessed mode, DB deletes occur in Central and we do not remove files.
} else {
char n[256];
OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId);
const std::string path(_genPath(n,false));
if (path.length())
OSUtils::rm(path.c_str());
}
{
Mutex::Lock _l(_networks_m);
_members[nodeId].erase(networkId);
std::unordered_map<uint64_t,_NW>::iterator i(_networks.find(networkId));
if (i == _networks.end())
return _EMPTY_JSON;
std::unordered_map< uint64_t,std::vector<uint8_t> >::iterator j(i->second.members.find(nodeId));
if (j == i->second.members.end())
return _EMPTY_JSON;
nlohmann::json tmp(j->second);
i->second.members.erase(j);
if (recomputeSummaryInfo)
_recomputeSummaryInfo(networkId);
return tmp;
}
}
void JSONDB::threadMain()
throw()
{
#ifndef __WINDOWS__
fd_set readfds,nullfds;
char *const readbuf = (_rawInput >= 0) ? (new char[1048576]) : (char *)0;
std::string rawInputBuf;
FD_ZERO(&readfds);
FD_ZERO(&nullfds);
struct timeval tv;
#endif
std::vector<uint64_t> todo;
while (_summaryThreadRun) {
#ifndef __WINDOWS__
if (_rawInput < 0) {
Thread::sleep(25);
} else {
// In IPC mode we wait but also select() on STDIN to read database updates
FD_SET(_rawInput,&readfds);
tv.tv_sec = 0;
tv.tv_usec = 25000;
select(_rawInput+1,&readfds,&nullfds,&nullfds,&tv);
if (FD_ISSET(_rawInput,&readfds)) {
const long rn = (long)read(_rawInput,readbuf,1048576);
bool gotMessage = false;
for(long i=0;i<rn;++i) {
if ((readbuf[i] != '\n')&&(readbuf[i] != '\r')&&(readbuf[i] != 0)) { // compatible with nodeJS IPC
rawInputBuf.push_back(readbuf[i]);
} else if (rawInputBuf.length() > 0) {
try {
const nlohmann::json obj(OSUtils::jsonParse(rawInputBuf));
gotMessage = true;
if (!_dataReady) {
_dataReady = true;
_networks_m.unlock();
}
if (obj.is_array()) {
for(unsigned long i=0;i<obj.size();++i)
_addOrUpdate(obj[i]);
} else if (obj.is_object()) {
_addOrUpdate(obj);
}
} catch ( ... ) {} // ignore malformed JSON
rawInputBuf.clear();
}
}
if (!gotMessage) // select() again immediately until we get at least one full message
continue;
}
}
#else
Thread::sleep(25);
#endif
{
Mutex::Lock _l(_summaryThread_m);
if (_summaryThreadToDo.empty())
continue;
else _summaryThreadToDo.swap(todo);
}
if (!_dataReady) { // sanity check
_dataReady = true;
_networks_m.unlock();
}
const int64_t now = OSUtils::now();
try {
Mutex::Lock _l(_networks_m);
for(std::vector<uint64_t>::iterator ii(todo.begin());ii!=todo.end();++ii) {
const uint64_t networkId = *ii;
std::unordered_map<uint64_t,_NW>::iterator n(_networks.find(networkId));
if (n != _networks.end()) {
NetworkSummaryInfo &ns = n->second.summaryInfo;
ns.activeBridges.clear();
ns.allocatedIps.clear();
ns.authorizedMemberCount = 0;
ns.activeMemberCount = 0;
ns.totalMemberCount = 0;
ns.mostRecentDeauthTime = 0;
for(std::unordered_map< uint64_t,std::vector<uint8_t> >::const_iterator m(n->second.members.begin());m!=n->second.members.end();++m) {
try {
nlohmann::json member(nlohmann::json::from_msgpack(m->second));
if (OSUtils::jsonBool(member["authorized"],false)) {
++ns.authorizedMemberCount;
try {
const nlohmann::json &mlog = member["recentLog"];
if ((mlog.is_array())&&(mlog.size() > 0)) {
const nlohmann::json &mlog1 = mlog[0];
if (mlog1.is_object()) {
if ((now - OSUtils::jsonInt(mlog1["ts"],0ULL)) < (ZT_NETWORK_AUTOCONF_DELAY * 2))
++ns.activeMemberCount;
}
}
} catch ( ... ) {}
try {
if (OSUtils::jsonBool(member["activeBridge"],false))
ns.activeBridges.push_back(Address(m->first));
} catch ( ... ) {}
try {
const nlohmann::json &mips = member["ipAssignments"];
if (mips.is_array()) {
for(unsigned long i=0;i<mips.size();++i) {
InetAddress mip(OSUtils::jsonString(mips[i],"").c_str());
if ((mip.ss_family == AF_INET)||(mip.ss_family == AF_INET6))
ns.allocatedIps.push_back(mip);
}
}
} catch ( ... ) {}
} else {
try {
ns.mostRecentDeauthTime = std::max(ns.mostRecentDeauthTime,(int64_t)OSUtils::jsonInt(member["lastDeauthorizedTime"],0LL));
} catch ( ... ) {}
}
++ns.totalMemberCount;
} catch ( ... ) {}
}
std::sort(ns.activeBridges.begin(),ns.activeBridges.end());
std::sort(ns.allocatedIps.begin(),ns.allocatedIps.end());
n->second.summaryInfoLastComputed = now;
}
}
} catch ( ... ) {}
todo.clear();
}
if (!_dataReady) // sanity check
_networks_m.unlock();
#ifndef __WINDOWS__
delete [] readbuf;
#endif
}
bool JSONDB::_addOrUpdate(const nlohmann::json &j)
{
try {
if (j.is_object()) {
std::string id(OSUtils::jsonString(j["id"],"0"));
const std::string objtype(OSUtils::jsonString(j["objtype"],""));
if ((id.length() == 16)&&(objtype == "network")) {
const uint64_t nwid = Utils::hexStrToU64(id.c_str());
if (nwid) {
bool update;
{
Mutex::Lock _l(_networks_m);
_NW &nw = _networks[nwid];
update = !nw.config.empty();
nw.config = nlohmann::json::to_msgpack(j);
}
if (update)
_parent->onNetworkUpdate(nwid);
_recomputeSummaryInfo(nwid);
return true;
}
} else if ((id.length() == 10)&&(objtype == "member")) {
const uint64_t mid = Utils::hexStrToU64(id.c_str());
const uint64_t nwid = Utils::hexStrToU64(OSUtils::jsonString(j["nwid"],"0").c_str());
if ((mid)&&(nwid)) {
bool update = false;
bool deauth = false;
{
Mutex::Lock _l(_networks_m);
std::vector<uint8_t> &m = _networks[nwid].members[mid];
if (!m.empty()) {
update = true;
nlohmann::json oldm(nlohmann::json::from_msgpack(m));
deauth = ((OSUtils::jsonBool(oldm["authorized"],false))&&(!OSUtils::jsonBool(j["authorized"],false)));
}
m = nlohmann::json::to_msgpack(j);
_members[mid].insert(nwid);
}
if (update) {
_parent->onNetworkMemberUpdate(nwid,mid);
if (deauth)
_parent->onNetworkMemberDeauthorize(nwid,mid);
}
_recomputeSummaryInfo(nwid);
return true;
}
} else if (objtype == "_delete") { // pseudo-object-type, only used in Central harnessed mode
const std::string deleteType(OSUtils::jsonString(j["deleteType"],""));
id = OSUtils::jsonString(j["deleteId"],"");
if ((deleteType == "network")&&(id.length() == 16)) {
eraseNetwork(Utils::hexStrToU64(id.c_str()));
} else if ((deleteType == "member")&&(id.length() == 10)) {
const std::string networkId(OSUtils::jsonString(j["deleteNetworkId"],""));
const uint64_t nwid = Utils::hexStrToU64(networkId.c_str());
const uint64_t mid = Utils::hexStrToU64(id.c_str());
if (networkId.length() == 16)
eraseNetworkMember(nwid,mid,true);
_parent->onNetworkMemberDeauthorize(nwid,mid);
}
}
}
} catch ( ... ) {}
return false;
}
bool JSONDB::_load(const std::string &p)
{
// This is not used in stdin/stdout mode. Instead data is populated by
// sending it all to stdin.
std::vector<std::string> dl(OSUtils::listDirectory(p.c_str(),true));
for(std::vector<std::string>::const_iterator di(dl.begin());di!=dl.end();++di) {
if ((di->length() > 5)&&(di->substr(di->length() - 5) == ".json")) {
std::string buf;
if (OSUtils::readFile((p + ZT_PATH_SEPARATOR_S + *di).c_str(),buf)) {
try {
_addOrUpdate(OSUtils::jsonParse(buf));
} catch ( ... ) {}
}
} else {
this->_load((p + ZT_PATH_SEPARATOR_S + *di));
}
}
return true;
}
void JSONDB::_recomputeSummaryInfo(const uint64_t networkId)
{
Mutex::Lock _l(_summaryThread_m);
if (std::find(_summaryThreadToDo.begin(),_summaryThreadToDo.end(),networkId) == _summaryThreadToDo.end())
_summaryThreadToDo.push_back(networkId);
if (!_summaryThread)
_summaryThread = Thread::start(this);
}
std::string JSONDB::_genPath(const std::string &n,bool create)
{
std::vector<std::string> pt(OSUtils::split(n.c_str(),"/","",""));
if (pt.size() == 0)
return std::string();
std::string p(_basePath);
if (create) OSUtils::mkdir(p.c_str());
for(unsigned long i=0,j=(unsigned long)(pt.size()-1);i<j;++i) {
p.push_back(ZT_PATH_SEPARATOR);
p.append(pt[i]);
if (create) OSUtils::mkdir(p.c_str());
}
p.push_back(ZT_PATH_SEPARATOR);
p.append(pt[pt.size()-1]);
p.append(".json");
return p;
}
} // namespace ZeroTier

View File

@ -1,192 +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/>.
*/
#ifndef ZT_JSONDB_HPP
#define ZT_JSONDB_HPP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <map>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include "../node/Constants.hpp"
#include "../node/Utils.hpp"
#include "../node/InetAddress.hpp"
#include "../node/Mutex.hpp"
#include "../ext/json/json.hpp"
#include "../osdep/OSUtils.hpp"
#include "../osdep/Thread.hpp"
namespace ZeroTier {
class EmbeddedNetworkController;
/**
* Hierarchical JSON store that persists into the filesystem or via HTTP
*/
class JSONDB
{
public:
struct NetworkSummaryInfo
{
NetworkSummaryInfo() : authorizedMemberCount(0),activeMemberCount(0),totalMemberCount(0),mostRecentDeauthTime(0) {}
std::vector<Address> activeBridges;
std::vector<InetAddress> allocatedIps;
unsigned long authorizedMemberCount;
unsigned long activeMemberCount;
unsigned long totalMemberCount;
int64_t mostRecentDeauthTime;
};
JSONDB(const std::string &basePath,EmbeddedNetworkController *parent);
~JSONDB();
/**
* Write a JSON object to the data store
*
* It's important that obj contain a valid JSON object with no newlines (jsonDump with -1
* for indentation), since newline-delimited JSON is what nodeJS's IPC speaks and this
* is important in Central-harnessed mode.
*
* @param n Path name of object
* @param obj Object in single-line no-CRs JSON object format (OSUtils::jsonDump(obj,-1))
* @return True if write appears successful
*/
bool writeRaw(const std::string &n,const std::string &obj);
bool hasNetwork(const uint64_t networkId) const;
bool getNetwork(const uint64_t networkId,nlohmann::json &config) const;
bool getNetworkSummaryInfo(const uint64_t networkId,NetworkSummaryInfo &ns) const;
/**
* @return Bit mask: 0 == none, 1 == network only, 3 == network and member
*/
int getNetworkAndMember(const uint64_t networkId,const uint64_t nodeId,nlohmann::json &networkConfig,nlohmann::json &memberConfig,NetworkSummaryInfo &ns) const;
bool getNetworkMember(const uint64_t networkId,const uint64_t nodeId,nlohmann::json &memberConfig) const;
void saveNetwork(const uint64_t networkId,const nlohmann::json &networkConfig);
void saveNetworkMember(const uint64_t networkId,const uint64_t nodeId,const nlohmann::json &memberConfig);
nlohmann::json eraseNetwork(const uint64_t networkId);
nlohmann::json eraseNetworkMember(const uint64_t networkId,const uint64_t nodeId,bool recomputeSummaryInfo = true);
std::vector<uint64_t> networkIds() const
{
std::vector<uint64_t> r;
Mutex::Lock _l(_networks_m);
for(std::unordered_map<uint64_t,_NW>::const_iterator n(_networks.begin());n!=_networks.end();++n)
r.push_back(n->first);
return r;
}
inline unsigned long memberCount(const uint64_t networkId)
{
Mutex::Lock _l(_networks_m);
std::unordered_map<uint64_t,_NW>::const_iterator i(_networks.find(networkId));
if (i != _networks.end())
return (unsigned long)i->second.members.size();
return 0;
}
template<typename F>
inline void eachMember(const uint64_t networkId,F func)
{
Mutex::Lock _l(_networks_m);
std::unordered_map<uint64_t,_NW>::const_iterator i(_networks.find(networkId));
if (i != _networks.end()) {
for(std::unordered_map< uint64_t,std::vector<uint8_t> >::const_iterator m(i->second.members.begin());m!=i->second.members.end();++m) {
try {
func(networkId,m->first,nlohmann::json::from_msgpack(m->second));
} catch ( ... ) {}
}
}
}
template<typename F>
inline void eachId(F func)
{
Mutex::Lock _l(_networks_m);
for(std::unordered_map<uint64_t,_NW>::const_iterator i(_networks.begin());i!=_networks.end();++i) {
for(std::unordered_map< uint64_t,std::vector<uint8_t> >::const_iterator m(i->second.members.begin());m!=i->second.members.end();++m) {
try {
func(i->first,m->first);
} catch ( ... ) {}
}
}
}
inline std::vector<uint64_t> networksForMember(const uint64_t nodeId)
{
Mutex::Lock _l(_networks_m);
std::unordered_map< uint64_t,std::unordered_set< uint64_t > >::const_iterator m(_members.find(nodeId));
if (m != _members.end()) {
return std::vector<uint64_t>(m->second.begin(),m->second.end());
} else {
return std::vector<uint64_t>();
}
}
void threadMain()
throw();
private:
bool _addOrUpdate(const nlohmann::json &j);
bool _load(const std::string &p);
void _recomputeSummaryInfo(const uint64_t networkId);
std::string _genPath(const std::string &n,bool create);
EmbeddedNetworkController *const _parent;
std::string _basePath;
int _rawInput,_rawOutput;
Mutex _rawLock;
Thread _summaryThread;
std::vector<uint64_t> _summaryThreadToDo;
volatile bool _summaryThreadRun;
Mutex _summaryThread_m;
struct _NW
{
_NW() : summaryInfoLastComputed(0) {}
std::vector<uint8_t> config;
NetworkSummaryInfo summaryInfo;
uint64_t summaryInfoLastComputed;
std::unordered_map< uint64_t,std::vector<uint8_t> > members;
};
std::unordered_map< uint64_t,_NW > _networks;
std::unordered_map< uint64_t,std::unordered_set< uint64_t > > _members;
bool _dataReady;
Mutex _networks_m;
};
} // namespace ZeroTier
#endif

View File

@ -1,57 +0,0 @@
The new ZeroTier CLI!
====
With this update we've expanded upon the previous CLI's functionality, so things should seem pretty familiar. Here are some of the new features we've introduced:
- Create and administer networks on ZeroTier Central directly from the console.
- Service configurations, allows you to control local/remote instances of ZeroTier One
- Identity generation and management is now part of the same CLI tool
***
## Configurations
Configurations are a way for you to nickname and logically organize the control of ZeroTier services running locally or remotely (this includes ZeroTier Central!). They're merely groupings of service API url's and auth tokens. The CLI's settings data is contained within `.zerotierCliSettings`.
For instance, you can control your local instance of ZeroTier One via the `@local` config. By default it is represented as follows:
```
"local": {
"auth": "7tyqRoFytajf21j2l2t9QPm5",
"type": "one",
"url": "http://127.0.0.1:9993/"
}
```
As an example, if you issue the command `zerotier ls` is it implicitly stating `zerotier @local ls`.
With the same line of thinking, you could create a `@my.zerotier.com` which would allow for something like `zerotier @my.zerotier.com net-create` which talks to our hosted ZeroTier Central to create a new network.
## Command families
- `cli-` is for configuring the settings data for the CLI itself, such as adding/removing `@thing` configurations, variables, etc.
- `net-` is for operating on a *ZeroTier Central* service such as `https://my.zerotier.com`
- `id-` is for handling ZeroTier identities.
And those commands with no prefix are there to allow you to operate ZeroTier One instances either local or remote.
***
## Useful command examples
*Add a ZeroTier One configuration:*
- `zerotier cli-add-zt MyLocalConfigName https://127.0.0.1:9993/ <authtoken>`
*Add a ZeroTier Central configuration:*
- `zerotier cli-add-central MyZTCentralConfigName https://my.zerotier.com/ <centralAPIAuthtoken>`
*Set a default ZeroTier One instance:*
- `zerotier cli-set defaultOne MyLocalConfigName`
*Set a default ZeroTier Central:*
- `zerotier cli-set defaultCentral MyZTCentralConfigName`

View File

@ -1,957 +0,0 @@
/*
* 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/>.
*/
// Note: unlike the rest of ZT's code base, this requires C++11 due to
// the JSON library it uses and other things.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "../node/Constants.hpp"
#include "../node/Identity.hpp"
#include "../version.h"
#include "../osdep/OSUtils.hpp"
#include "../ext/offbase/json/json.hpp"
#ifdef __WINDOWS__
#include <WinSock2.h>
#include <windows.h>
#include <tchar.h>
#include <wchar.h>
#else
#include <ctype.h>
#include <unistd.h>
#endif
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <tuple>
#include <regex>
#include <curl/curl.h>
using json = nlohmann::json;
using namespace ZeroTier;
#define ZT_CLI_FLAG_VERBOSE 'v'
#define ZT_CLI_FLAG_UNSAFE_SSL 'X'
#define REQ_GET 0
#define REQ_POST 1
#define REQ_DEL 2
#define OK_STR "[OK ]: "
#define FAIL_STR "[FAIL]: "
#define WARN_STR "[WARN]: "
#define INVALID_ARGS_STR "Invalid args. Usage: "
struct CLIState
{
std::string atname;
std::string command;
std::string url;
std::map<std::string,std::string> reqHeaders;
std::vector<std::string> args;
std::map<char,std::string> opts;
json settings;
};
namespace {
static Identity getIdFromArg(char *arg)
{
Identity id;
if ((strlen(arg) > 32)&&(arg[10] == ':')) { // identity is a literal on the command line
if (id.fromString(arg))
return id;
} else { // identity is to be read from a file
std::string idser;
if (OSUtils::readFile(arg,idser)) {
if (id.fromString(idser))
return id;
}
}
return Identity();
}
static std::string trimString(const std::string &s)
{
unsigned long end = (unsigned long)s.length();
while (end) {
char c = s[end - 1];
if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
--end;
else break;
}
unsigned long start = 0;
while (start < end) {
char c = s[start];
if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
++start;
else break;
}
return s.substr(start,end - start);
}
static inline std::string getSettingsFilePath()
{
#ifdef __WINDOWS__
#else
const char *home = getenv("HOME");
if (!home)
home = "/";
return (std::string(home) + "/.zerotierCliSettings");
#endif
}
static bool saveSettingsBackup(CLIState &state)
{
std::string sfp(getSettingsFilePath().c_str());
if(state.settings.find("generateBackupConfig") != state.settings.end()
&& state.settings["generateBackupConfig"].get<std::string>() == "true") {
std::string backup_file = getSettingsFilePath() + ".bak";
if(!OSUtils::writeFile(sfp.c_str(), state.settings.dump(2))) {
OSUtils::lockDownFile(sfp.c_str(),false);
std::cout << WARN_STR << "unable to write backup config file" << std::endl;
return false;
}
return true;
}
return false;
}
static bool saveSettings(CLIState &state)
{
std::string sfp(getSettingsFilePath().c_str());
if(OSUtils::writeFile(sfp.c_str(), state.settings.dump(2))) {
OSUtils::lockDownFile(sfp.c_str(),false);
std::cout << OK_STR << "changes saved." << std::endl;
return true;
}
std::cout << FAIL_STR << "unable to write to " << sfp << std::endl;
return false;
}
static void dumpHelp()
{
std::cout << "ZeroTier Newer-Spiffier CLI " << ZEROTIER_ONE_VERSION_MAJOR << "." << ZEROTIER_ONE_VERSION_MINOR << "." << ZEROTIER_ONE_VERSION_REVISION << std::endl;
std::cout << "(c)2016 ZeroTier, Inc. / Licensed under the GNU GPL v3" << std::endl;
std::cout << std::endl;
std::cout << "Configuration path: " << getSettingsFilePath() << std::endl;
std::cout << std::endl;
std::cout << "Usage: zerotier [-option] [@name] <command> [<command options>]" << std::endl;
std::cout << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -verbose - Verbose JSON output" << std::endl;
std::cout << " -X - Do not check SSL certs (CAUTION!)" << std::endl;
std::cout << std::endl;
std::cout << "CLI Configuration Commands:" << std::endl;
std::cout << " cli-set <setting> <value> - Set a CLI option ('cli-set help')" << std::endl;
std::cout << " cli-unset <setting> <value> - Un-sets a CLI option ('cli-unset help')" << std::endl;
std::cout << " cli-ls - List configured @things" << std::endl;
std::cout << " cli-rm @name - Remove a configured @thing" << std::endl;
std::cout << " cli-add-zt @name <url> <auth> - Add a ZeroTier service" << std::endl;
std::cout << " cli-add-central @name <url> <auth> - Add ZeroTier Central instance" << std::endl;
std::cout << std::endl;
std::cout << "ZeroTier One Service Commands:" << std::endl;
std::cout << " -v / -version - Displays default local instance's version'" << std::endl;
std::cout << " ls - List currently joined networks" << std::endl;
std::cout << " join <network> [opt=value ...] - Join a network" << std::endl;
std::cout << " leave <network> - Leave a network" << std::endl;
std::cout << " peers - List ZeroTier VL1 peers" << std::endl;
std::cout << " show [<network/peer address>] - Get info about self or object" << std::endl;
std::cout << std::endl;
std::cout << "Network Controller Commands:" << std::endl;
std::cout << " net-create - Create a new network" << std::endl;
std::cout << " net-rm <network> - Delete a network (CAUTION!)" << std::endl;
std::cout << " net-ls - List administered networks" << std::endl;
std::cout << " net-members <network> - List members of a network" << std::endl;
std::cout << " net-show <network> [<address>] - Get network or member info" << std::endl;
std::cout << " net-auth <network> <address> - Authorize a member" << std::endl;
std::cout << " net-unauth <network> <address> - De-authorize a member" << std::endl;
std::cout << " net-set <path> <value> - See 'net-set help'" << std::endl;
std::cout << std::endl;
std::cout << "Identity Commands:" << std::endl;
std::cout << " id-generate [<vanity prefix>] - Generate a ZeroTier identity" << std::endl;
std::cout << " id-validate <identity> - Locally validate an identity" << std::endl;
std::cout << " id-sign <identity> <file> - Sign a file" << std::endl;
std::cout << " id-verify <secret> <file> <sig> - Verify a file's signature" << std::endl;
std::cout << " id-getpublic <secret> - Get full identity's public portion" << std::endl;
std::cout << std::endl;
}
static size_t _curlStringAppendCallback(void *contents,size_t size,size_t nmemb,void *stdstring)
{
size_t totalSize = size * nmemb;
reinterpret_cast<std::string *>(stdstring)->append((const char *)contents,totalSize);
return totalSize;
}
static std::tuple<int,std::string> REQUEST(int requestType, CLIState &state, const std::map<std::string,std::string> &headers, const std::string &postfield, const std::string &url)
{
std::string body;
char errbuf[CURL_ERROR_SIZE];
char urlbuf[4096];
CURL *curl;
curl = curl_easy_init();
if (!curl) {
std::cerr << "FATAL: curl_easy_init() failed" << std::endl;
exit(-1);
}
Utils::scopy(urlbuf,sizeof(urlbuf),url.c_str());
curl_easy_setopt(curl,CURLOPT_URL,urlbuf);
struct curl_slist *hdrs = (struct curl_slist *)0;
for(std::map<std::string,std::string>::const_iterator i(headers.begin());i!=headers.end();++i) {
std::string htmp(i->first);
htmp.append(": ");
htmp.append(i->second);
hdrs = curl_slist_append(hdrs,htmp.c_str());
}
if (hdrs)
curl_easy_setopt(curl,CURLOPT_HTTPHEADER,hdrs);
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl,CURLOPT_WRITEDATA,(void *)&body);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curlStringAppendCallback);
if(std::find(state.args.begin(), state.args.end(), "-X") == state.args.end())
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(state.opts.count(ZT_CLI_FLAG_UNSAFE_SSL) > 0) ? 0L : 1L);
if(requestType == REQ_POST) {
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfield.c_str());
}
if(requestType == REQ_DEL)
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
if(requestType == REQ_GET) {
curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf);
curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,0L);
}
curl_easy_setopt(curl,CURLOPT_USERAGENT,"ZeroTier-CLI");
CURLcode res = curl_easy_perform(curl);
errbuf[CURL_ERROR_SIZE-1] = (char)0; // sanity check
if (res != CURLE_OK)
return std::make_tuple(-1,std::string(errbuf));
long response_code;
int rc = (int)curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &response_code);
if(response_code == 401) { std::cout << FAIL_STR << response_code << "Unauthorized." << std::endl; exit(0); }
else if(response_code == 403) { std::cout << FAIL_STR << response_code << "Forbidden." << std::endl; exit(0); }
else if(response_code == 404) { std::cout << FAIL_STR << response_code << "Not found." << std::endl; exit(0); }
else if(response_code == 408) { std::cout << FAIL_STR << response_code << "Request timed out." << std::endl; exit(0); }
else if(response_code != 200) { std::cout << FAIL_STR << response_code << "Unable to process request." << std::endl; exit(0); }
curl_easy_cleanup(curl);
if (hdrs)
curl_slist_free_all(hdrs);
return std::make_tuple(response_code,body);
}
} // anonymous namespace
//////////////////////////////////////////////////////////////////////////////
// Check for user-specified @thing config
// Make sure it @thing makes sense
// Apply appropriate request headers
static void checkForThing(CLIState &state, std::string thingType, bool warnNoThingProvided)
{
std::string configName;
if(state.atname.length()) {
configName = state.atname.erase(0,1);
// make sure specified @thing makes sense in the context of the command
if(thingType == "one" && state.settings["things"][configName]["type"].get<std::string>() != "one") {
std::cout << FAIL_STR << "A ZeroTier Central config was specified for a ZeroTier One command." << std::endl;
exit(0);
}
if(thingType == "central" && state.settings["things"][configName]["type"].get<std::string>() != "central") {
std::cout << FAIL_STR << "A ZeroTier One config was specified for a ZeroTier Central command." << std::endl;
exit(0);
}
}
else { // no @thing specified, check for defaults depending on type
if(thingType == "one") {
if(state.settings.find("defaultOne") != state.settings.end()) {
if(warnNoThingProvided)
std::cout << WARN_STR << "No @thing specified, assuming default for ZeroTier One command: " << state.settings["defaultOne"].get<std::string>().c_str() << std::endl;
configName = state.settings["defaultOne"].get<std::string>().erase(0,1); // get default
}
else {
std::cout << WARN_STR << "No @thing specified, and no default is known." << std::endl;
std::cout << "HELP: To set a default: zerotier cli-set defaultOne @my_default_thing" << std::endl;
exit(0);
}
}
if(thingType == "central") {
if(state.settings.find("defaultCentral") != state.settings.end()) {
if(warnNoThingProvided)
std::cout << WARN_STR << "No @thing specified, assuming default for ZeroTier Central command: " << state.settings["defaultCentral"].get<std::string>().c_str() << std::endl;
configName = state.settings["defaultCentral"].get<std::string>().erase(0,1); // get default
}
else {
std::cout << WARN_STR << "No @thing specified, and no default is known." << std::endl;
std::cout << "HELP: To set a default: zerotier cli-set defaultCentral @my_default_thing" << std::endl;
exit(0);
}
}
}
// Apply headers
if(thingType == "one") {
state.reqHeaders["X-ZT1-Auth"] = state.settings["things"][configName]["auth"];
}
if(thingType == "central"){
state.reqHeaders["Content-Type"] = "application/json";
state.reqHeaders["Authorization"] = "Bearer " + state.settings["things"][configName]["auth"].get<std::string>();
state.reqHeaders["Accept"] = "application/json";
}
state.url = state.settings["things"][configName]["url"];
}
static bool checkURL(std::string url)
{
// TODO
return true;
}
static std::string getLocalVersion(CLIState &state)
{
json result;
std::tuple<int,std::string> res;
checkForThing(state,"one",false);
res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "/status");
if(std::get<0>(res) == 200) {
result = json::parse(std::get<1>(res));
return result["version"].get<std::string>();
}
return "---";
}
#ifdef __WINDOWS__
int _tmain(int argc, _TCHAR* argv[])
#else
int main(int argc,char **argv)
#endif
{
#ifdef __WINDOWS__
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
}
#endif
curl_global_init(CURL_GLOBAL_DEFAULT);
CLIState state;
std::string arg1, arg2, authToken;
for(int i=1;i<argc;++i) {
if (argv[i][0] == '@') {
state.atname = argv[i];
}
else if (state.command.length() == 0) {
if (argv[i][0] == '-') {
if (!argv[i][1]) {
dumpHelp();
return -1;
} else if (argv[i][2]) {
state.opts[argv[i][1]] = argv[i] + 2;
} else {
state.opts[argv[i][1]] = "";
}
} else {
state.command = argv[i];
}
}
else {
state.args.push_back(std::string(argv[i]));
}
}
{
std::string buf;
if (OSUtils::readFile(getSettingsFilePath().c_str(),buf))
state.settings = json::parse(buf);
if (state.settings.empty()) {
// Default settings
state.settings = {
{ "configVersion", 1 },
{ "things", {
{ "my.zerotier.com", {
{ "type", "central" },
{ "url", "https://my.zerotier.com/" },
{ "auth", "" }
}},
{ "local", {
{ "type", "one" },
{ "url", "" },
{ "auth", "" }
}}
}},
{ "defaultController", "@my.zerotier.com" },
{ "defaultOne", "@local" }
};
std::string oneHome(OSUtils::platformDefaultHomePath());
std::string portStr;
bool initSuccess = false;
std::string path = oneHome + ZT_PATH_SEPARATOR_S ;
if (OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),authToken)&&OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr)) {
portStr = trimString(portStr);
authToken = trimString(authToken);
int port = Utils::strToInt(portStr.c_str());
if (((port > 0)&&(port < 65536))&&(authToken.length() > 0)) {
state.settings["things"]["local"]["url"] = (std::string("http://127.0.0.1:") + portStr + "/");
state.settings["things"]["local"]["auth"] = authToken;
initSuccess = true;
}
}
if (!saveSettings(state)) {
std::cerr << "FATAL: unable to write " << getSettingsFilePath() << std::endl;
exit(-1);
}
if (initSuccess) {
std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << std::endl;
} else {
std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << " but could not auto-init local ZeroTier One service config from " << oneHome << " -- you will need to set local service URL and port manually if you want to control a local instance of ZeroTier One. (This happens if you are not root/administrator.)" << std::endl;
}
}
}
// PRE-REQUEST SETUP
json result;
std::tuple<int,std::string> res;
std::string url = "";
// META
if ((state.command.length() == 0)||(state.command == "help")) {
dumpHelp();
return -1;
}
// zerotier version
else if (state.command == "v" || state.command == "version") {
std::cout << getLocalVersion(state) << std::endl;
return 1;
}
// zerotier cli-set <setting> <value>
else if (state.command == "cli-set") {
if(argc != 4) {
std::cerr << INVALID_ARGS_STR << "zerotier cli-set <setting> <value>" << std::endl;
return 1;
}
std::string settingName, settingValue;
if(state.atname.length()) { // User provided @thing erroneously, we will ignore it and adjust argument indices
settingName = argv[3];
settingValue = argv[4];
}
else {
settingName = argv[2];
settingValue = argv[3];
}
saveSettingsBackup(state);
state.settings[settingName] = settingValue; // changes
saveSettings(state);
}
// zerotier cli-unset <setting>
else if (state.command == "cli-unset") {
if(argc != 3) {
std::cerr << INVALID_ARGS_STR << "zerotier cli-unset <setting>" << std::endl;
return 1;
}
std::string settingName;
if(state.atname.length()) // User provided @thing erroneously, we will ignore it and adjust argument indices
settingName = argv[3];
else
settingName = argv[2];
saveSettingsBackup(state);
state.settings.erase(settingName); // changes
saveSettings(state);
}
// zerotier @thing_to_remove cli-rm --- removes the configuration
else if (state.command == "cli-rm") {
if(argc != 3) {
std::cerr << INVALID_ARGS_STR << "zerotier cli-rm <@thing>" << std::endl;
return 1;
}
if(state.settings["things"].find(state.atname) != state.settings["things"].end()) {
if(state.settings["defaultOne"] == state.atname) {
std::cout << "WARNING: The config you're trying to remove is currently set as your default. Set a new default first!" << std::endl;
std::cout << " | Usage: zerotier set defaultOne @your_other_thing" << std::endl;
}
else {
state.settings["things"].erase(state.atname.c_str());
saveSettings(state);
}
}
}
// zerotier cli-add-zt <shortname> <url> <auth>
// TODO: Check for malformed urls/auth
else if (state.command == "cli-add-zt") {
if(argc != 5) {
std::cerr << INVALID_ARGS_STR << "zerotier cli-add-zt <shortname> <url> <authToken>" << std::endl;
return 1;
}
std::string thing_name = argv[2], url = argv[3], auth = argv[4];
if(!checkURL(url)) {
std::cout << FAIL_STR << "Malformed URL" << std::endl;
return 1;
}
if(state.settings.find(thing_name) != state.settings.end()) {
std::cout << "WARNING: A @thing with the shortname " << thing_name.c_str()
<< " already exists. Choose another name or rename the old @thing" << std::endl;
std::cout << " | Usage: To rename a @thing: zerotier cli-rename @old_thing_name @new_thing_name" << std::endl;
}
else {
result = json::parse("{ \"auth\": \"" + auth + "\", \"type\": \"" + "one" + "\", \"url\": \"" + url + "\" }");
saveSettingsBackup(state);
// TODO: Handle cases where user may or may not prepend an @
state.settings["things"][thing_name] = result; // changes
saveSettings(state);
}
}
// zerotier cli-add-central <shortname> <url> <auth>
// TODO: Check for malformed urls/auth
else if (state.command == "cli-add-central") {
if(argc != 5) {
std::cerr << INVALID_ARGS_STR << "zerotier cli-add-central <shortname> <url> <authToken>" << std::endl;
return 1;
}
std::string thing_name = argv[2], url = argv[3], auth = argv[4];
if(!checkURL(url)) {
std::cout << FAIL_STR << "Malformed URL" << std::endl;
return 1;
}
if(state.settings.find(thing_name) != state.settings.end()) {
std::cout << "WARNING: A @thing with the shortname " << thing_name.c_str()
<< " already exists. Choose another name or rename the old @thing" << std::endl;
std::cout << " | Usage: To rename a @thing: zerotier cli-rename @old_thing_name @new_thing_name" << std::endl;
}
else {
result = json::parse("{ \"auth\": \"" + auth + "\", \"type\": \"" + "central" + "\", \"url\": \"" + url + "\" }");
saveSettingsBackup(state);
// TODO: Handle cases where user may or may not prepend an @
state.settings["things"]["@" + thing_name] = result; // changes
saveSettings(state);
}
}
// ONE SERVICE
// zerotier ls --- display all networks currently joined
else if (state.command == "ls" || state.command == "listnetworks") {
if(argc != 2) {
std::cerr << INVALID_ARGS_STR << "zerotier ls" << std::endl;
return 1;
}
checkForThing(state,"one",true);
url = state.url + "network";
res = REQUEST(REQ_GET,state,state.reqHeaders,"",(const std::string)url);
if(std::get<0>(res) == 200) {
std::cout << "listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" << std::endl;
auto j = json::parse(std::get<1>(res).c_str());
if (j.type() == json::value_t::array) {
for(int i=0;i<j.size();i++){
std::string nwid = j[i]["nwid"].get<std::string>();
std::string name = j[i]["name"].get<std::string>();
std::string mac = j[i]["mac"].get<std::string>();
std::string status = j[i]["status"].get<std::string>();
std::string type = j[i]["type"].get<std::string>();
std::string addrs;
for(int m=0; m<j[i]["assignedAddresses"].size(); m++) {
addrs += j[i]["assignedAddresses"][m].get<std::string>() + " ";
}
std::string dev = j[i]["portDeviceName"].get<std::string>();
std::cout << "listnetworks " << nwid << " " << name << " " << mac << " " << status << " " << type << " " << dev << " " << addrs << std::endl;
}
}
}
}
// zerotier join <nwid> --- joins a network
else if (state.command == "join") {
if(argc != 3) {
std::cerr << INVALID_ARGS_STR << "zerotier join <nwid>" << std::endl;
return 1;
}
checkForThing(state,"one",true);
res = REQUEST(REQ_POST,state,state.reqHeaders,"{}",state.url + "/network/" + state.args[0]);
if(std::get<0>(res) == 200) {
std::cout << OK_STR << "connected to " << state.args[0] << std::endl;
}
}
// zerotier leave <nwid> --- leaves a network
else if (state.command == "leave") {
if(argc != 3) {
std::cerr << INVALID_ARGS_STR << "zerotier leave <nwid>" << std::endl;
return 1;
}
checkForThing(state,"one",true);
res = REQUEST(REQ_DEL,state,state.reqHeaders,"{}",state.url + "/network/" + state.args[0]);
if(std::get<0>(res) == 200) {
std::cout << OK_STR << "disconnected from " << state.args[0] << std::endl;
}
}
// zerotier peers --- display address and role of all peers
else if (state.command == "peers") {
if(argc != 2) {
std::cerr << INVALID_ARGS_STR << "zerotier peers" << std::endl;
return 1;
}
checkForThing(state,"one",true);
res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "/peer");
if(std::get<0>(res) == 200) {
json result = json::parse(std::get<1>(res));
for(int i=0; i<result.size(); i++) {
std::cout << result[i]["address"] << " " << result[i]["role"] << std::endl;
}
}
}
// zerotier show --- display status of local instance
else if (state.command == "show" || state.command == "status") {
if(argc != 2) {
std::cerr << INVALID_ARGS_STR << "zerotier show" << std::endl;
return 1;
}
checkForThing(state,"one",true);
res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "/status");
if(std::get<0>(res) == 200) {
result = json::parse(std::get<1>(res));
std::string status_str = result["online"].get<bool>() ? "ONLINE" : "OFFLINE";
std::cout << "info " << result["address"].get<std::string>()
<< " " << status_str << " " << result["version"].get<std::string>() << std::endl;
}
}
// REMOTE
// zerotier @thing net-create --- creates a new network
else if (state.command == "net-create") {
if(argc > 3 || (argc == 3 && !state.atname.length())) {
std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-create" << std::endl;
return 1;
}
checkForThing(state,"central",true);
res = REQUEST(REQ_POST,state,state.reqHeaders,"",state.url + "api/network");
if(std::get<0>(res) == 200) {
json result = json::parse(std::get<1>(res));
std::cout << OK_STR << "created network " << result["config"]["nwid"].get<std::string>() << std::endl;
}
}
// zerotier @thing net-rm <nwid> --- deletes a network
else if (state.command == "net-rm") {
if(argc > 4 || (argc == 4 && !state.atname.length())) {
std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-rm <nwid>" << std::endl;
return 1;
}
checkForThing(state,"central",true);
if(!state.args.size()) {
std::cout << "Argument error: No network specified." << std::endl;
std::cout << " | Usage: zerotier net-rm <nwid>" << std::endl;
}
else {
std::string nwid = state.args[0];
res = REQUEST(REQ_DEL,state,state.reqHeaders,"",state.url + "api/network/" + nwid);
if(std::get<0>(res) == 200) {
std::cout << "deleted network " << nwid << std::endl;
}
}
}
// zerotier @thing net-ls --- lists all networks
else if (state.command == "net-ls") {
if(argc > 3 || (argc == 3 && !state.atname.length())) {
std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-ls" << std::endl;
return 1;
}
checkForThing(state,"central",true);
res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network");
if(std::get<0>(res) == 200) {
json result = json::parse(std::get<1>(res));
for(int m=0;m<result.size(); m++) {
std::cout << "network " << result[m]["id"].get<std::string>() << std::endl;
}
}
}
// zerotier @thing net-members <nwid> --- show all members of a network
else if (state.command == "net-members") {
if(argc > 4 || (argc == 4 && !state.atname.length())) {
std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-members <nwid>" << std::endl;
return 1;
}
checkForThing(state,"central",true);
if(!state.args.size()) {
std::cout << FAIL_STR << "Argument error: No network specified." << std::endl;
std::cout << " | Usage: zerotier net-members <nwid>" << std::endl;
}
else {
std::string nwid = state.args[0];
res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network/" + nwid + "/member");
json result = json::parse(std::get<1>(res));
std::cout << "Members of " << nwid << ":" << std::endl;
for (json::iterator it = result.begin(); it != result.end(); ++it) {
std::cout << it.key() << std::endl;
}
}
}
// zerotier @thing net-show <nwid> <devID> --- show info about a device on a specific network
else if (state.command == "net-show") {
if(argc > 5 || (argc == 5 && !state.atname.length())) {
std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-show <nwid> <devID>" << std::endl;
return 1;
}
checkForThing(state,"central",true);
if(state.args.size() < 2) {
std::cout << FAIL_STR << "Argument error: Too few arguments." << std::endl;
std::cout << " | Usage: zerotier net-show <nwid> <devID>" << std::endl;
}
else {
std::string nwid = state.args[0];
std::string devid = state.args[1];
res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network/" + nwid + "/member/" + devid);
// TODO: More info, what would we like to show exactly?
if(std::get<0>(res) == 200) {
json result = json::parse(std::get<1>(res));
std::cout << "Assigned IP: " << std::endl;
for(int m=0; m<result["config"]["ipAssignments"].size();m++) {
std::cout << "\t" << result["config"]["ipAssignments"][m].get<std::string>() << std::endl;
}
}
}
}
// zerotier @thing net-auth <nwid> <devID> --- authorize a device on a network
else if (state.command == "net-auth") {
if(argc > 5 || (argc == 5 && !state.atname.length())) {
std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-auth <nwid> <devID>" << std::endl;
return 1;
}
checkForThing(state,"central",true);
if(state.args.size() != 2) {
std::cout << FAIL_STR << "Argument error: Network and/or device ID not specified." << std::endl;
std::cout << " | Usage: zerotier net-auth <nwid> <devID>" << std::endl;
}
std::string nwid = state.args[0];
std::string devid = state.args[1];
url = state.url + "api/network/" + nwid + "/member/" + devid;
// Add device to network
res = REQUEST(REQ_POST,state,state.reqHeaders,"",(const std::string)url);
if(std::get<0>(res) == 200) {
result = json::parse(std::get<1>(res));
res = REQUEST(REQ_GET,state,state.reqHeaders,"",(const std::string)url);
result = json::parse(std::get<1>(res));
result["config"]["authorized"] = "true";
std::string newconfig = result.dump();
res = REQUEST(REQ_POST,state,state.reqHeaders,newconfig,(const std::string)url);
if(std::get<0>(res) == 200)
std::cout << OK_STR << devid << " authorized on " << nwid << std::endl;
else
std::cout << FAIL_STR << "There was a problem authorizing that device." << std::endl;
}
}
// zerotier @thing net-unauth <nwid> <devID>
else if (state.command == "net-unauth") {
if(argc > 5 || (argc == 5 && !state.atname.length())) {
std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-unauth <nwid> <devID>" << std::endl;
return 1;
}
checkForThing(state,"central",true);
if(state.args.size() != 2) {
std::cout << FAIL_STR << "Bad argument. No network and/or device ID specified." << std::endl;
std::cout << " | Usage: zerotier net-unauth <nwid> <devID>" << std::endl;
}
std::string nwid = state.args[0];
std::string devid = state.args[1];
// If successful, get member config
res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network/" + nwid + "/member/" + devid);
result = json::parse(std::get<1>(res));
// modify auth field and re-POST
result["config"]["authorized"] = "false";
std::string newconfig = result.dump();
res = REQUEST(REQ_POST,state,state.reqHeaders,newconfig,state.url + "api/network/" + nwid + "/member/" + devid);
if(std::get<0>(res) == 200)
std::cout << OK_STR << devid << " de-authorized from " << nwid << std::endl;
else
std::cout << FAIL_STR << "There was a problem de-authorizing that device." << std::endl;
}
// zerotier @thing net-set
else if (state.command == "net-set") {
}
// ID
// zerotier id-generate [<vanity prefix>]
else if (state.command == "id-generate") {
if(argc != 3) {
std::cerr << INVALID_ARGS_STR << "zerotier id-generate [<vanity prefix>]" << std::endl;
return 1;
}
uint64_t vanity = 0;
int vanityBits = 0;
if (argc >= 5) {
vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL;
vanityBits = 4 * strlen(argv[4]);
if (vanityBits > 40)
vanityBits = 40;
}
ZeroTier::Identity id;
for(;;) {
id.generate();
if ((id.address().toInt() >> (40 - vanityBits)) == vanity) {
if (vanityBits > 0) {
fprintf(stderr,"vanity address: found %.10llx !\n",(unsigned long long)id.address().toInt());
}
break;
} else {
fprintf(stderr,"vanity address: tried %.10llx looking for first %d bits of %.10llx\n",(unsigned long long)id.address().toInt(),vanityBits,(unsigned long long)(vanity << (40 - vanityBits)));
}
}
std::string idser = id.toString(true);
if (argc >= 3) {
if (!OSUtils::writeFile(argv[2],idser)) {
std::cerr << "Error writing to " << argv[2] << std::endl;
return 1;
} else std::cout << argv[2] << " written" << std::endl;
if (argc >= 4) {
idser = id.toString(false);
if (!OSUtils::writeFile(argv[3],idser)) {
std::cerr << "Error writing to " << argv[3] << std::endl;
return 1;
} else std::cout << argv[3] << " written" << std::endl;
}
} else std::cout << idser << std::endl;
}
// zerotier id-validate <identity>
else if (state.command == "id-validate") {
if(argc != 3) {
std::cerr << INVALID_ARGS_STR << "zerotier id-validate <identity>" << std::endl;
return 1;
}
Identity id = getIdFromArg(argv[2]);
if (!id) {
std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
return 1;
}
if (!id.locallyValidate()) {
std::cerr << argv[2] << " FAILED validation." << std::endl;
return 1;
} else std::cout << argv[2] << "is a valid identity" << std::endl;
}
// zerotier id-sign <identity> <file>
else if (state.command == "id-sign") {
if(argc != 4) {
std::cerr << INVALID_ARGS_STR << "zerotier id-sign <identity> <file>" << std::endl;
return 1;
}
Identity id = getIdFromArg(argv[2]);
if (!id) {
std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
return 1;
}
if (!id.hasPrivate()) {
std::cerr << argv[2] << " does not contain a private key (must use private to sign)" << std::endl;
return 1;
}
std::string inf;
if (!OSUtils::readFile(argv[3],inf)) {
std::cerr << argv[3] << " is not readable" << std::endl;
return 1;
}
C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length());
std::cout << Utils::hex(signature.data,(unsigned int)signature.size()) << std::endl;
}
// zerotier id-verify <secret> <file> <sig>
else if (state.command == "id-verify") {
if(argc != 4) {
std::cerr << INVALID_ARGS_STR << "zerotier id-verify <secret> <file> <sig>" << std::endl;
return 1;
}
Identity id = getIdFromArg(argv[2]);
if (!id) {
std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
return 1;
}
std::string inf;
if (!OSUtils::readFile(argv[3],inf)) {
std::cerr << argv[3] << " is not readable" << std::endl;
return 1;
}
std::string signature(Utils::unhex(argv[4]));
if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
std::cout << argv[3] << " signature valid" << std::endl;
} else {
std::cerr << argv[3] << " signature check FAILED" << std::endl;
return 1;
}
}
// zerotier id-getpublic <secret>
else if (state.command == "id-getpublic") {
if(argc != 3) {
std::cerr << INVALID_ARGS_STR << "zerotier id-getpublic <secret>" << std::endl;
return 1;
}
Identity id = getIdFromArg(argv[2]);
if (!id) {
std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
return 1;
}
std::cerr << id.toString(false) << std::endl;
}
//
else {
dumpHelp();
return -1;
}
if(std::find(state.args.begin(), state.args.end(), "-verbose") != state.args.end())
std::cout << "\n\nAPI response = " << std::get<1>(res) << std::endl;
curl_global_cleanup();
return 0;
}

View File

@ -1,8 +0,0 @@
Dockerized Linux Build Farm
======
This subfolder contains Dockerfiles and a script to build Linux packages for a variety of Linux distributions. It's also an excellent way to test your CPU fans and stress test your disk.
Running `build.sh` with no arguments builds everything. You can run `build.sh` with the name of a distro (e.g. centos-7) to only build that. Both 32 and 64 bit packages are built except where no 32-bit version of the distribution exists.
The `make-apt-repos.sh` and `make-rpm-repos.sh` scripts build repositories. They may require some editing for outside-of-ZeroTier use, and be careful with the apt one if you have an existing *aptly* configuration.

View File

@ -1,13 +0,0 @@
#FROM ambakshi/amazon-linux:2016.03
#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
#RUN yum update -y
#RUN yum install -y epel-release
#RUN yum install -y make development-tools rpmdevtools clang gcc-c++ ruby ruby-devel
#RUN gem install ronn
FROM zerotier/zt1-build-amazon-2016.03-x64-base
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
ADD zt1-src.tar.gz /

View File

@ -1,69 +0,0 @@
#!/bin/bash
export PATH=/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin
subdirs=$*
if [ ! -n "$subdirs" ]; then
subdirs=`find . -type d -name '*-*' -printf '%f '`
fi
if [ ! -d ./ubuntu-trusty ]; then
echo 'Must run from linux-build-farm subfolder.'
exit 1
fi
rm -f zt1-src.tar.gz
cd ..
git archive --format=tar.gz --prefix=ZeroTierOne/ -o linux-build-farm/zt1-src.tar.gz HEAD
cd linux-build-farm
# Note that --privileged is used so we can bind mount VM shares when building in a VM.
# It has no other impact or purpose, but probably doesn't matter here in any case.
for distro in $subdirs; do
echo
echo "--- BUILDING FOR $distro ---"
echo
cd $distro
if [ -d x64 ]; then
cd x64
mv ../../zt1-src.tar.gz .
docker build -t zt1-build-${distro}-x64 .
mv zt1-src.tar.gz ../..
cd ..
fi
if [ -d x86 ]; then
cd x86
mv ../../zt1-src.tar.gz .
docker build -t zt1-build-${distro}-x86 .
mv zt1-src.tar.gz ../..
cd ..
fi
rm -f *.deb *.rpm
# exit 0
if [ ! -n "`echo $distro | grep -F debian`" -a ! -n "`echo $distro | grep -F ubuntu`" ]; then
if [ -d x64 ]; then
docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x64 /bin/bash -c 'cd /ZeroTierOne ; make redhat ; cd .. ; cp `find /root/rpmbuild -type f -name *.rpm` /artifacts ; ls -l /artifacts'
fi
if [ -d x86 ]; then
docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x86 /bin/bash -c 'cd /ZeroTierOne ; make redhat ; cd .. ; cp `find /root/rpmbuild -type f -name *.rpm` /artifacts ; ls -l /artifacts'
fi
else
if [ -d x64 ]; then
docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x64 /bin/bash -c 'cd /ZeroTierOne ; make debian ; cd .. ; cp *.deb /artifacts ; ls -l /artifacts'
fi
if [ -d x86 ]; then
docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x86 /bin/bash -c 'cd /ZeroTierOne ; make debian ; cd .. ; cp *.deb /artifacts ; ls -l /artifacts'
fi
fi
cd ..
done
rm -f zt1-src.tar.gz

View File

@ -1,13 +0,0 @@
FROM centos:6
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN yum update -y
RUN yum install -y epel-release
RUN yum install -y make development-tools rpmdevtools clang gcc-c++ tar
RUN yum install -y nodejs npm
# Stop use of http-parser-devel which is installed by nodejs/npm
RUN rm -f /usr/include/http_parser.h
ADD zt1-src.tar.gz /

View File

@ -1,13 +0,0 @@
FROM toopher/centos-i386:centos6
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN yum update -y
RUN yum install -y epel-release
RUN yum install -y make development-tools rpmdevtools clang gcc-c++ tar
RUN yum install -y nodejs npm
# Stop use of http-parser-devel which is installed by nodejs/npm
RUN rm -f /usr/include/http_parser.h
ADD zt1-src.tar.gz /

View File

@ -1,10 +0,0 @@
FROM centos:7
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN yum update -y
RUN yum install -y epel-release
RUN yum install -y make development-tools rpmdevtools clang gcc-c++ ruby ruby-devel
RUN gem install ronn
ADD zt1-src.tar.gz /

View File

@ -1,22 +0,0 @@
#FROM zerotier/centos7-32bit
#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
#RUN echo 'i686-redhat-linux' >/etc/rpm/platform
#RUN yum update -y
#RUN yum install -y make development-tools rpmdevtools http-parser-devel lz4-devel libnatpmp-devel
#RUN yum install -y gcc-c++
#RUN rpm --install --force https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
#RUN rpm --install --force ftp://rpmfind.net/linux/centos/6.8/os/i386/Packages/libffi-3.0.5-3.2.el6.i686.rpm
#RUN yum install -y clang
FROM zerotier/zt1-build-centos-7-x86-base
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN yum install -y ruby ruby-devel
RUN gem install ronn
#RUN rpm --erase http-parser-devel lz4-devel libnatpmp-devel
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM debian:jessie
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.5
RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM 32bit/debian:jessie
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.5
RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM debian:stretch
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM mcandre/docker-debian-32bit:stretch
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM debian:wheezy
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /
RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules

View File

@ -1,15 +0,0 @@
#FROM tubia/debian:wheezy
#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
#RUN apt-get update
#RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
FROM zerotier/zt1-build-debian-wheezy-x86-base
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /
RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules

View File

@ -1,10 +0,0 @@
FROM fedora:22
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN yum update -y
RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
RUN rpm --erase http-parser-devel
RUN yum install -y rubygem-ronn ruby
ADD zt1-src.tar.gz /

View File

@ -1,19 +0,0 @@
#FROM nickcis/fedora-32:22
#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
#RUN mkdir -p /etc/dnf/vars
#RUN echo 'i386' >/etc/dnf/vars/basearch
#RUN echo 'i386' >/etc/dnf/vars/arch
#RUN yum update -y
#RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
FROM zerotier/zt1-build-fedora-22-x86-base
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN echo 'i686-redhat-linux' >/etc/rpm/platform
RUN rpm --erase http-parser-devel
RUN yum install -y rubygem-ronn ruby
ADD zt1-src.tar.gz /

View File

@ -1,16 +0,0 @@
#!/bin/bash
# This builds a series of Debian repositories for each distribution.
export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
for distro in debian-* ubuntu-*; do
if [ -n "`find ${distro} -name '*.deb' -type f`" ]; then
arches=`ls ${distro}/*.deb | cut -d _ -f 3 | cut -d . -f 1 | xargs | sed 's/ /,/g'`
distro_name=`echo $distro | cut -d '-' -f 2`
echo '---' $distro / $distro_name / $arches
aptly repo create -architectures=${arches} -comment="ZeroTier, Inc. Debian Packages" -component="main" -distribution=${distro_name} zt-release-${distro_name}
aptly repo add zt-release-${distro_name} ${distro}/*.deb
aptly publish repo zt-release-${distro_name} $distro_name
fi
done

View File

@ -1,64 +0,0 @@
#!/bin/bash
export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
GPG_KEY=contact@zerotier.com
rm -rf /tmp/zt-rpm-repo
mkdir /tmp/zt-rpm-repo
for distro in centos-* fedora-* amazon-*; do
dname=`echo $distro | cut -d '-' -f 1`
if [ "$dname" = "centos" ]; then
dname=el
fi
if [ "$dname" = "fedora" ]; then
dname=fc
fi
if [ "$dname" = "amazon" ]; then
dname=amzn1
fi
dvers=`echo $distro | cut -d '-' -f 2`
mkdir -p /tmp/zt-rpm-repo/$dname/$dvers
cp -v $distro/*.rpm /tmp/zt-rpm-repo/$dname/$dvers
done
rpmsign --resign --key-id=$GPG_KEY --digest-algo=sha256 `find /tmp/zt-rpm-repo -type f -name '*.rpm'`
for db in `find /tmp/zt-rpm-repo -mindepth 2 -maxdepth 2 -type d`; do
createrepo --database $db
done
# Stupid RHEL stuff
cd /tmp/zt-rpm-repo/el
ln -sf 6 6Client
ln -sf 6 6Workstation
ln -sf 6 6Server
ln -sf 6 6.0
ln -sf 6 6.1
ln -sf 6 6.2
ln -sf 6 6.3
ln -sf 6 6.4
ln -sf 6 6.5
ln -sf 6 6.6
ln -sf 6 6.7
ln -sf 6 6.8
ln -sf 6 6.9
ln -sf 7 7Client
ln -sf 7 7Workstation
ln -sf 7 7Server
ln -sf 7 7.0
ln -sf 7 7.1
ln -sf 7 7.2
ln -sf 7 7.3
ln -sf 7 7.4
ln -sf 7 7.5
ln -sf 7 7.6
ln -sf 7 7.7
ln -sf 7 7.8
ln -sf 7 7.9
echo
echo Repo created in /tmp/zt-rpm-repo

View File

@ -1,12 +0,0 @@
FROM ubuntu:14.04
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.6
RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM 32bit/ubuntu:14.04
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.6
RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM ubuntu:wily
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.7
RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,12 +0,0 @@
FROM daald/ubuntu32:wily
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.7
RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,14 +0,0 @@
FROM ubuntu:xenial
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
RUN rm -f /usr/bin/clang++ /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,14 +0,0 @@
FROM f69m/ubuntu32:xenial
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
RUN apt-get update
RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
RUN rm -f /usr/bin/clang++ /usr/bin/clang
RUN dpkg --purge libhttp-parser-dev
ADD zt1-src.tar.gz /

View File

@ -1,112 +0,0 @@
CREATE TABLE Config (
k varchar(16) PRIMARY KEY NOT NULL,
v varchar(1024) NOT NULL
);
CREATE TABLE Network (
id char(16) PRIMARY KEY NOT NULL,
name varchar(128) NOT NULL,
private integer NOT NULL DEFAULT(1),
enableBroadcast integer NOT NULL DEFAULT(1),
allowPassiveBridging integer NOT NULL DEFAULT(0),
multicastLimit integer NOT NULL DEFAULT(32),
creationTime integer NOT NULL DEFAULT(0),
revision integer NOT NULL DEFAULT(1),
memberRevisionCounter integer NOT NULL DEFAULT(1),
flags integer NOT NULL DEFAULT(0)
);
CREATE TABLE AuthToken (
id integer PRIMARY KEY NOT NULL,
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
authMode integer NOT NULL DEFAULT(1),
useCount integer NOT NULL DEFAULT(0),
maxUses integer NOT NULL DEFAULT(0),
expiresAt integer NOT NULL DEFAULT(0),
token varchar(256) NOT NULL
);
CREATE INDEX AuthToken_networkId_token ON AuthToken(networkId,token);
CREATE TABLE Node (
id char(10) PRIMARY KEY NOT NULL,
identity varchar(4096) NOT NULL
);
CREATE TABLE IpAssignment (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
nodeId char(10) REFERENCES Node(id) ON DELETE CASCADE,
type integer NOT NULL DEFAULT(0),
ip blob(16) NOT NULL,
ipNetmaskBits integer NOT NULL DEFAULT(0),
ipVersion integer NOT NULL DEFAULT(4)
);
CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);
CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);
CREATE TABLE IpAssignmentPool (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
ipRangeStart blob(16) NOT NULL,
ipRangeEnd blob(16) NOT NULL,
ipVersion integer NOT NULL DEFAULT(4)
);
CREATE UNIQUE INDEX IpAssignmentPool_networkId_ipRangeStart ON IpAssignmentPool (networkId,ipRangeStart);
CREATE TABLE Member (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,
authorized integer NOT NULL DEFAULT(0),
activeBridge integer NOT NULL DEFAULT(0),
memberRevision integer NOT NULL DEFAULT(0),
flags integer NOT NULL DEFAULT(0),
lastRequestTime integer NOT NULL DEFAULT(0),
lastPowDifficulty integer NOT NULL DEFAULT(0),
lastPowTime integer NOT NULL DEFAULT(0),
recentHistory blob,
PRIMARY KEY (networkId, nodeId)
);
CREATE INDEX Member_networkId_nodeId ON Member(networkId,nodeId);
CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);
CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);
CREATE INDEX Member_networkId_lastRequestTime ON Member(networkId, lastRequestTime);
CREATE TABLE Route (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
target blob(16) NOT NULL,
via blob(16),
targetNetmaskBits integer NOT NULL,
ipVersion integer NOT NULL,
flags integer NOT NULL,
metric integer NOT NULL
);
CREATE INDEX Route_networkId ON Route (networkId);
CREATE TABLE Rule (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
capId integer,
ruleNo integer NOT NULL,
ruleType integer NOT NULL DEFAULT(0),
"addr" blob(16),
"int1" integer,
"int2" integer,
"int3" integer,
"int4" integer
);
CREATE INDEX Rule_networkId_capId ON Rule (networkId,capId);
CREATE TABLE MemberTC (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,
tagId integer,
tagValue integer,
capId integer,
capMaxCustodyChainLength integer NOT NULL DEFAULT(1)
);
CREATE INDEX MemberTC_networkId_nodeId ON MemberTC (networkId,nodeId);

View File

@ -1,134 +0,0 @@
#!/bin/bash
# This script builds the installer for *nix systems. Windows must do everything
# completely differently, as usual.
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
if [ ! -f zerotier-one ]; then
echo "Could not find 'zerotier-one' binary, please build before running this script."
exit 2
fi
machine=`uname -m`
system=`uname -s`
vmajor=`cat version.h | grep -F ZEROTIER_ONE_VERSION_MAJOR | cut -d ' ' -f 3`
vminor=`cat version.h | grep -F ZEROTIER_ONE_VERSION_MINOR | cut -d ' ' -f 3`
revision=`cat version.h | grep -F ZEROTIER_ONE_VERSION_REVISION | cut -d ' ' -f 3`
if [ -z "$vmajor" -o -z "$vminor" -o -z "$revision" ]; then
echo "Unable to extract version info from version.h, aborting installer build."
exit 2
fi
rm -rf build-installer
mkdir build-installer
case "$system" in
Linux)
# Canonicalize $machine for some architectures... we use x86
# and x64 for Intel stuff. ARM and others should be fine if
# we ever ship officially for those.
debian_arch=$machine
case "$machine" in
i386|i486|i586|i686)
machine="x86"
debian_arch="i386"
;;
x86_64|amd64|x64)
machine="x64"
debian_arch="amd64"
;;
armv6l|arm|armhf|arm7l|armv7l)
machine="armv6l"
debian_arch="armhf"
;;
esac
echo "Assembling Linux installer for $machine and version $vmajor.$vminor.$revision"
mkdir -p 'build-installer/var/lib/zerotier-one/ui'
cp -fp 'ext/installfiles/linux/uninstall.sh' 'build-installer/var/lib/zerotier-one'
cp -fp 'zerotier-one' 'build-installer/var/lib/zerotier-one'
for f in ui/*.html ui/*.js ui/*.css ui/*.jsx ; do
cp -fp "$f" 'build-installer/var/lib/zerotier-one/ui'
done
mkdir -p 'build-installer/tmp'
cp -fp 'ext/installfiles/linux/init.d/zerotier-one' 'build-installer/tmp/init.d_zerotier-one'
cp -fp 'ext/installfiles/linux/systemd/zerotier-one.service' 'build-installer/tmp/systemd_zerotier-one.service'
targ="ZeroTierOneInstaller-linux-${machine}-${vmajor}_${vminor}_${revision}"
# Use gzip in Linux since some minimal Linux systems do not have bunzip2
rm -f build-installer-tmp.tar.gz
cd build-installer
tar -cf - * | gzip -9 >../build-installer-tmp.tar.gz
cd ..
rm -f $targ
cat ext/installfiles/linux/install.tmpl.sh build-installer-tmp.tar.gz >$targ
chmod 0755 $targ
rm -f build-installer-tmp.tar.gz
ls -l $targ
if [ -f /usr/bin/dpkg-deb -a "$UID" -eq 0 ]; then
echo
echo Found dpkg-deb and you are root, trying to build Debian package.
rm -rf build-installer-deb
debbase="build-installer-deb/zerotier-one_${vmajor}.${vminor}.${revision}_$debian_arch"
debfolder="${debbase}/DEBIAN"
mkdir -p $debfolder
cat 'ext/installfiles/linux/DEBIAN/control.in' | sed "s/__VERSION__/${vmajor}.${vminor}.${revision}/" | sed "s/__ARCH__/${debian_arch}/" >$debfolder/control
cat $debfolder/control
cp -f 'ext/installfiles/linux/DEBIAN/conffiles' "${debfolder}/conffiles"
mkdir -p "${debbase}/var/lib/zerotier-one/updates.d"
cp -f $targ "${debbase}/var/lib/zerotier-one/updates.d"
rm -f "${debfolder}/postinst" "${debfolder}/prerm"
echo '#!/bin/bash' >${debfolder}/postinst
echo "/var/lib/zerotier-one/updates.d/${targ} >>/dev/null 2>&1" >>${debfolder}/postinst
echo "/bin/rm -f /var/lib/zerotier-one/updates.d/*" >>${debfolder}/postinst
chmod a+x ${debfolder}/postinst
echo '#!/bin/bash' >${debfolder}/prerm
echo 'export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin' >>${debfolder}/prerm
echo 'if [ "$1" != "upgrade" ]; then' >>${debfolder}/prerm
echo ' /var/lib/zerotier-one/uninstall.sh >>/dev/null 2>&1' >>${debfolder}/prerm
echo 'fi' >>${debfolder}/prerm
chmod a+x ${debfolder}/prerm
dpkg-deb --build $debbase
mv -f build-installer-deb/*.deb .
rm -rf build-installer-deb
fi
if [ -f /usr/bin/rpmbuild ]; then
echo
echo Found rpmbuild, trying to build RedHat/CentOS package.
rm -f /tmp/zerotier-one.spec
curr_dir=`pwd`
cat ext/installfiles/linux/RPM/zerotier-one.spec.in | sed "s/__VERSION__/${vmajor}.${vminor}.${revision}/g" | sed "s/__INSTALLER__/${targ}/g" >/tmp/zerotier-one.spec
rpmbuild -ba /tmp/zerotier-one.spec
rm -f /tmp/zerotier-one.spec
fi
;;
*)
echo "Unsupported platform: $system"
exit 2
esac
rm -rf build-installer
exit 0

View File

@ -1,182 +0,0 @@
#!/bin/bash
export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
shopt -s expand_aliases
dryRun=0
echo "*** ZeroTier One install/update ***"
echo
if [ "$UID" -ne 0 ]; then
echo "Not running as root so doing dry run (no modifications to system)..."
dryRun=1
fi
if [ $dryRun -gt 0 ]; then
alias ln="echo '>> ln'"
alias rm="echo '>> rm'"
alias mv="echo '>> mv'"
alias cp="echo '>> cp'"
alias chown="echo '>> chown'"
alias chgrp="echo '>> chgrp'"
alias chmod="echo '>> chmod'"
alias chkconfig="echo '>> chkconfig'"
alias zerotier-cli="echo '>> zerotier-cli'"
alias service="echo '>> service'"
alias systemctl="echo '>> systemctl'"
fi
scriptPath="`dirname "$0"`/`basename "$0"`"
if [ ! -r "$scriptPath" ]; then
scriptPath="$0"
if [ ! -r "$scriptPath" ]; then
echo "Installer cannot determine its own path; $scriptPath is not readable."
exit 2
fi
fi
# Check for systemd vs. old school SysV init
SYSTEMDUNITDIR=
if [ -e /bin/systemctl -o -e /usr/bin/systemctl -o -e /usr/local/bin/systemctl -o -e /sbin/systemctl -o -e /usr/sbin/systemctl ]; then
# Second check: test if systemd appears to actually be running. Apparently Ubuntu
# thought it was a good idea to ship with systemd installed but not used. Issue #133
if [ -d /var/run/systemd/system -o -d /run/systemd/system ]; then
if [ -e /usr/bin/pkg-config ]; then
SYSTEMDUNITDIR=`/usr/bin/pkg-config systemd --variable=systemdsystemunitdir`
fi
if [ -z "$SYSTEMDUNITDIR" -o ! -d "$SYSTEMDUNITDIR" ]; then
if [ -d /usr/lib/systemd/system ]; then
SYSTEMDUNITDIR=/usr/lib/systemd/system
fi
if [ -d /etc/systemd/system ]; then
SYSTEMDUNITDIR=/etc/systemd/system
fi
fi
fi
fi
# Find the end of this script, which is where we have appended binary data.
endMarkerIndex=`grep -a -b -E '^################' "$scriptPath" | head -c 16 | cut -d : -f 1`
if [ "$endMarkerIndex" -le 100 ]; then
echo 'Internal error: unable to find end of script / start of binary data marker.'
exit 2
fi
blobStart=`expr $endMarkerIndex + 17`
if [ "$blobStart" -le "$endMarkerIndex" ]; then
echo 'Internal error: unable to find end of script / start of binary data marker.'
exit 2
fi
echo -n 'Getting version of existing install... '
origVersion=NONE
if [ -x /var/lib/zerotier-one/zerotier-one ]; then
origVersion=`/var/lib/zerotier-one/zerotier-one -v`
fi
echo $origVersion
echo 'Extracting files...'
if [ $dryRun -gt 0 ]; then
echo ">> tail -c +$blobStart \"$scriptPath\" | gunzip -c | tar -xvop -C / -f -"
tail -c +$blobStart "$scriptPath" | gunzip -c | tar -t -f - | sed 's/^/>> /'
else
tail -c +$blobStart "$scriptPath" | gunzip -c | tar -xvop --no-overwrite-dir -C / -f -
fi
if [ $dryRun -eq 0 -a ! -x "/var/lib/zerotier-one/zerotier-one" ]; then
echo 'Archive extraction failed, cannot find zerotier-one binary in "/var/lib/zerotier-one".'
exit 2
fi
echo -n 'Getting version of new install... '
newVersion=`/var/lib/zerotier-one/zerotier-one -v`
echo $newVersion
echo 'Creating symlinks...'
rm -f /usr/bin/zerotier-cli /usr/bin/zerotier-idtool
ln -sf /var/lib/zerotier-one/zerotier-one /usr/bin/zerotier-cli
ln -sf /var/lib/zerotier-one/zerotier-one /usr/bin/zerotier-idtool
echo 'Installing zerotier-one service...'
if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" ]; then
# SYSTEMD
# If this was updated or upgraded from an init.d based system, clean up the old
# init.d stuff before installing directly via systemd.
if [ -f /etc/init.d/zerotier-one ]; then
if [ -e /sbin/chkconfig -o -e /usr/sbin/chkconfig -o -e /bin/chkconfig -o -e /usr/bin/chkconfig ]; then
chkconfig zerotier-one off
fi
rm -f /etc/init.d/zerotier-one
fi
cp -f /tmp/systemd_zerotier-one.service "$SYSTEMDUNITDIR/zerotier-one.service"
chown 0 "$SYSTEMDUNITDIR/zerotier-one.service"
chgrp 0 "$SYSTEMDUNITDIR/zerotier-one.service"
chmod 0644 "$SYSTEMDUNITDIR/zerotier-one.service"
rm -f /tmp/systemd_zerotier-one.service /tmp/init.d_zerotier-one
systemctl enable zerotier-one.service
echo
echo 'Done! Installed and service configured to start at system boot.'
echo
echo "To start now or restart the service if it's already running:"
echo ' sudo systemctl restart zerotier-one.service'
else
# SYSV INIT -- also covers upstart which supports SysVinit backward compatibility
cp -f /tmp/init.d_zerotier-one /etc/init.d/zerotier-one
chmod 0755 /etc/init.d/zerotier-one
rm -f /tmp/systemd_zerotier-one.service /tmp/init.d_zerotier-one
if [ -f /sbin/chkconfig -o -f /usr/sbin/chkconfig -o -f /usr/bin/chkconfig -o -f /bin/chkconfig ]; then
chkconfig zerotier-one on
else
# Yes Virginia, some systems lack chkconfig.
if [ -d /etc/rc0.d ]; then
rm -f /etc/rc0.d/???zerotier-one
ln -sf /etc/init.d/zerotier-one /etc/rc0.d/K89zerotier-one
fi
if [ -d /etc/rc1.d ]; then
rm -f /etc/rc1.d/???zerotier-one
ln -sf /etc/init.d/zerotier-one /etc/rc1.d/K89zerotier-one
fi
if [ -d /etc/rc2.d ]; then
rm -f /etc/rc2.d/???zerotier-one
ln -sf /etc/init.d/zerotier-one /etc/rc2.d/S11zerotier-one
fi
if [ -d /etc/rc3.d ]; then
rm -f /etc/rc3.d/???zerotier-one
ln -sf /etc/init.d/zerotier-one /etc/rc3.d/S11zerotier-one
fi
if [ -d /etc/rc4.d ]; then
rm -f /etc/rc4.d/???zerotier-one
ln -sf /etc/init.d/zerotier-one /etc/rc4.d/S11zerotier-one
fi
if [ -d /etc/rc5.d ]; then
rm -f /etc/rc5.d/???zerotier-one
ln -sf /etc/init.d/zerotier-one /etc/rc5.d/S11zerotier-one
fi
if [ -d /etc/rc6.d ]; then
rm -f /etc/rc6.d/???zerotier-one
ln -sf /etc/init.d/zerotier-one /etc/rc6.d/K89zerotier-one
fi
fi
echo
echo 'Done! Installed and service configured to start at system boot.'
echo
echo "To start now or restart the service if it's already running:"
echo ' sudo service zerotier-one restart'
fi
exit 0
# Do not remove the last line or add a carriage return to it! The installer
# looks for an unterminated line beginning with 16 #'s in itself to find
# the binary blob data, which is appended after it.
################

View File

@ -1,76 +0,0 @@
#!/bin/bash
export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
if [ "$UID" -ne 0 ]; then
echo "Must be run as root; try: sudo $0"
exit 1
fi
# Detect systemd vs. regular init
SYSTEMDUNITDIR=
if [ -e /bin/systemctl -o -e /usr/bin/systemctl -o -e /usr/local/bin/systemctl -o -e /sbin/systemctl -o -e /usr/sbin/systemctl ]; then
if [ -e /usr/bin/pkg-config ]; then
SYSTEMDUNITDIR=`/usr/bin/pkg-config systemd --variable=systemdsystemunitdir`
fi
if [ -z "$SYSTEMDUNITDIR" -o ! -d "$SYSTEMDUNITDIR" ]; then
if [ -d /usr/lib/systemd/system ]; then
SYSTEMDUNITDIR=/usr/lib/systemd/system
fi
if [ -d /etc/systemd/system ]; then
SYSTEMDUNITDIR=/etc/systemd/system
fi
fi
fi
echo "Killing any running zerotier-one service..."
if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" ]; then
systemctl stop zerotier-one.service
systemctl disable zerotier-one.service
else
if [ -f /sbin/service -o -f /usr/sbin/service -o -f /bin/service -o -f /usr/bin/service ]; then
service zerotier-one stop
fi
fi
sleep 1
if [ -f /var/lib/zerotier-one/zerotier-one.pid ]; then
kill -TERM `cat /var/lib/zerotier-one/zerotier-one.pid`
sleep 1
fi
if [ -f /var/lib/zerotier-one/zerotier-one.pid ]; then
kill -KILL `cat /var/lib/zerotier-one/zerotier-one.pid`
fi
if [ -f /etc/init.d/zerotier-one ]; then
echo "Removing SysV init items..."
if [ -f /sbin/chkconfig -o -f /usr/sbin/chkconfig -o -f /bin/chkconfig -o -f /usr/bin/chkconfig ]; then
chkconfig zerotier-one off
fi
rm -f /etc/init.d/zerotier-one
find /etc/rc*.d -type f -name '???zerotier-one' -print0 | xargs -0 rm -f
fi
if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" -a -f "$SYSTEMDUNITDIR/zerotier-one.service" ]; then
echo "Removing systemd service..."
rm -f "$SYSTEMDUNITDIR/zerotier-one.service"
fi
echo "Erasing binary and support files..."
if [ -d /var/lib/zerotier-one ]; then
cd /var/lib/zerotier-one
rm -rf zerotier-one *.persist identity.public *.log *.pid *.sh updates.d networks.d iddb.d root-topology ui
fi
echo "Erasing anything installed into system bin directories..."
rm -f /usr/local/bin/zerotier-cli /usr/bin/zerotier-cli /usr/local/bin/zerotier-idtool /usr/bin/zerotier-idtool
echo "Done."
echo
echo "Your ZeroTier One identity is still preserved in /var/lib/zerotier-one"
echo "as identity.secret and can be manually deleted if you wish. Save it if"
echo "you wish to re-use the address of this node, as it cannot be regenerated."
echo
exit 0

View File

@ -1,8 +0,0 @@
Root Server Watcher
======
This is a small daemon written in NodeJS that watches a set of root servers and records peer status information into a Postgres database.
To use type `npm install` to install modules. Then edit `config.json.example` and rename to `config.json`. For each of your roots you will need to configure a way for this script to reach it. You will also need to use `schema.sql` to initialize a Postgres database to contain your logs and set it up in `config.json` as well.
This doesn't (yet) include any software for reading the log database and doing anything useful with the information inside, though given that it's a simple SQL database it should not be hard to compose queries to show interesting statistics.

View File

@ -1,30 +0,0 @@
{
"interval": 30000,
"dbSaveInterval": 60000,
"peerTimeout": 600000,
"db": {
"database": "ztr",
"user": "postgres",
"password": "s00p3rs3kr3t",
"host": "127.0.0.1",
"port": 5432,
"max": 16,
"idleTimeoutMillis": 30000
},
"roots": {
"my-root-01": {
"id": 1,
"ip": "10.0.0.1",
"port": 9993,
"authToken": "foobarbaz",
"peers": "/peer"
},
"my-root-02": {
"id": 2,
"ip": "10.0.0.2",
"port": 9993,
"authToken": "lalafoo",
"peers": "/peer"
}
}
}

View File

@ -1,16 +0,0 @@
{
"name": "zerotier-root-watcher",
"version": "1.0.0",
"description": "Simple background service to watch a cluster of roots and record peer info into a database",
"main": "zerotier-root-watcher.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ZeroTier, Inc. <contact@zerotier.com>",
"license": "GPL-3.0",
"dependencies": {
"async": "^2.3.0",
"pg": "^6.1.5",
"zlib": "^1.0.5"
}
}

View File

@ -1,20 +0,0 @@
/* Schema for ZeroTier root watcher log database */
CREATE TABLE "Peer"
(
"ztAddress" BIGINT NOT NULL,
"timestamp" BIGINT NOT NULL,
"versionMajor" INTEGER NOT NULL,
"versionMinor" INTEGER NOT NULL,
"versionRev" INTEGER NOT NULL,
"rootId" INTEGER NOT NULL,
"phyPort" INTEGER NOT NULL,
"phyLinkQuality" REAL NOT NULL,
"phyLastReceive" BIGINT NOT NULL,
"phyAddress" INET NOT NULL
);
CREATE INDEX "Peer_ztAddress" ON "Peer" ("ztAddress");
CREATE INDEX "Peer_timestamp" ON "Peer" ("timestamp");
CREATE INDEX "Peer_rootId" ON "Peer" ("rootId");
CREATE INDEX "Peer_phyAddress" ON "Peer" ("phyAddress");

View File

@ -1,235 +0,0 @@
'use strict';
const pg = require('pg');
const zlib = require('zlib');
const http = require('http');
const fs = require('fs');
const async = require('async');
const config = JSON.parse(fs.readFileSync('./config.json'));
const roots = config.roots||{};
const db = new pg.Pool(config.db);
process.on('uncaughtException',function(err) {
console.error('ERROR: uncaught exception: '+err);
if (err.stack)
console.error(err.stack);
});
function httpRequest(host,port,authToken,method,path,args,callback)
{
var responseBody = [];
var postData = (args) ? JSON.stringify(args) : null;
var req = http.request({
host: host,
port: port,
path: path,
method: method,
headers: {
'X-ZT1-Auth': (authToken||''),
'Content-Length': (postData) ? postData.length : 0
}
},function(res) {
res.on('data',function(chunk) {
if ((chunk)&&(chunk.length > 0))
responseBody.push(chunk);
});
res.on('timeout',function() {
try {
if (typeof callback === 'function') {
var cb = callback;
callback = null;
cb(new Error('connection timed out'),null);
}
req.abort();
} catch (e) {}
});
res.on('error',function(e) {
try {
if (typeof callback === 'function') {
var cb = callback;
callback = null;
cb(new Error('connection timed out'),null);
}
req.abort();
} catch (e) {}
});
res.on('end',function() {
if (typeof callback === 'function') {
var cb = callback;
callback = null;
if (responseBody.length === 0) {
return cb(null,{});
} else {
responseBody = Buffer.concat(responseBody);
if (responseBody.length < 2) {
return cb(null,{});
}
if ((responseBody.readUInt8(0,true) === 0x1f)&&(responseBody.readUInt8(1,true) === 0x8b)) {
try {
responseBody = zlib.gunzipSync(responseBody);
} catch (e) {
return cb(e,null);
}
}
try {
return cb(null,JSON.parse(responseBody));
} catch (e) {
return cb(e,null);
}
}
}
});
}).on('error',function(e) {
try {
if (typeof callback === 'function') {
var cb = callback;
callback = null;
cb(e,null);
}
req.abort();
} catch (e) {}
}).on('timeout',function() {
try {
if (typeof callback === 'function') {
var cb = callback;
callback = null;
cb(new Error('connection timed out'),null);
}
req.abort();
} catch (e) {}
});
req.setTimeout(30000);
req.setNoDelay(true);
if (postData !== null)
req.end(postData);
else req.end();
};
var peerStatus = {};
function saveToDb()
{
db.connect(function(err,client,clientDone) {
if (err) {
console.log('WARNING: database error writing peers: '+err.toString());
clientDone();
return setTimeout(saveToDb,config.dbSaveInterval||60000);
}
client.query('BEGIN',function(err) {
if (err) {
console.log('WARNING: database error writing peers: '+err.toString());
clientDone();
return setTimeout(saveToDb,config.dbSaveInterval||60000);
}
let timeout = Date.now() - (config.peerTimeout||600000);
let wtotal = 0;
async.eachSeries(Object.keys(peerStatus),function(address,nextAddress) {
let s = peerStatus[address];
if (s[1] <= timeout) {
delete peerStatus[address];
return process.nextTick(nextAddress);
} else {
++wtotal;
client.query('INSERT INTO "Peer" ("ztAddress","timestamp","versionMajor","versionMinor","versionRev","rootId","phyPort","phyLinkQuality","phyLastReceive","phyAddress") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)',s,nextAddress);
}
},function(err) {
if (err)
console.log('WARNING database error writing peers: '+err.toString());
console.log(Date.now().toString()+' '+wtotal);
client.query('COMMIT',function(err,result) {
clientDone();
return setTimeout(saveToDb,config.dbSaveInterval||60000);
});
});
});
});
};
function doRootUpdate(name,id,ip,port,peersPath,authToken,interval)
{
httpRequest(ip,port,authToken,"GET",peersPath,null,function(err,res) {
if (err) {
console.log('WARNING: cannot reach '+name+peersPath+' (will try again in 1s): '+err.toString());
return setTimeout(function() { doRootUpdate(name,id,ip,port,peersPath,authToken,interval); },1000);
}
if (!Array.isArray(res)) {
console.log('WARNING: cannot reach '+name+peersPath+' (will try again in 1s): response is not an array of peers');
return setTimeout(function() { doRootUpdate(name,id,ip,port,peersPath,authToken,interval); },1000);
}
//console.log(name+': '+res.length+' peer entries.');
let now = Date.now();
let count = 0;
for(let pi=0;pi<res.length;++pi) {
let peer = res[pi];
let address = peer.address;
let ztAddress = parseInt(address,16)||0;
if (!ztAddress)
continue;
let paths = peer.paths;
if ((Array.isArray(paths))&&(paths.length > 0)) {
let bestPath = null;
for(let i=0;i<paths.length;++i) {
if (paths[i].active) {
let lr = paths[i].lastReceive;
if ((lr > 0)&&((!bestPath)||(bestPath.lastReceive < lr)))
bestPath = paths[i];
}
}
if (bestPath) {
let a = bestPath.address;
if (typeof a === 'string') {
let a2 = a.split('/');
if (a2.length === 2) {
let vmaj = peer.versionMajor;
if ((typeof vmaj === 'undefined')||(vmaj === null)) vmaj = -1;
let vmin = peer.versionMinor;
if ((typeof vmin === 'undefined')||(vmin === null)) vmin = -1;
let vrev = peer.versionRev;
if ((typeof vrev === 'undefined')||(vrev === null)) vrev = -1;
let lr = parseInt(bestPath.lastReceive)||0;
let s = peerStatus[address];
if ((!s)||(s[8] < lr)) {
peerStatus[address] = [
ztAddress,
now,
vmaj,
vmin,
vrev,
id,
parseInt(a2[1])||0,
parseFloat(bestPath.linkQuality)||1.0,
lr,
a2[0]
];
}
++count;
}
}
}
}
}
console.log(name+': '+count+' peers with active direct paths.');
return setTimeout(function() { doRootUpdate(name,id,ip,port,peersPath,authToken,interval); },interval);
});
};
for(var r in roots) {
var rr = roots[r];
if (rr.peers)
doRootUpdate(r,rr.id,rr.ip,rr.port,rr.peers,rr.authToken||null,config.interval||60000);
}
return setTimeout(saveToDb,config.dbSaveInterval||60000);

View File

@ -1,7 +0,0 @@
CXX=$(shell which clang++ g++ c++ 2>/dev/null | head -n 1)
all:
$(CXX) -O3 -fno-rtti -o tcp-proxy tcp-proxy.cpp
clean:
rm -f *.o tcp-proxy *.dSYM

View File

@ -1,4 +0,0 @@
TCP Proxy Server
======
This is the TCP proxy server we run for TCP tunneling from peers behind fascist NATs. Regular users won't have much use for this.

View File

@ -1,317 +0,0 @@
/*
* 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/>.
*/
// HACK! Will eventually use epoll() or something in Phy<> instead of select().
// Also be sure to change ulimit -n and fs.file-max in /etc/sysctl.conf on relays.
#if defined(__linux__) || defined(__LINUX__) || defined(__LINUX) || defined(LINUX)
#include <linux/posix_types.h>
#include <bits/types.h>
#undef __FD_SETSIZE
#define __FD_SETSIZE 1048576
#undef FD_SETSIZE
#define FD_SETSIZE 1048576
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <map>
#include <set>
#include <string>
#include <algorithm>
#include <vector>
#include "../osdep/Phy.hpp"
#define ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS 300
#define ZT_TCP_PROXY_TCP_PORT 443
using namespace ZeroTier;
/*
* ZeroTier TCP Proxy Server
*
* This implements a simple packet encapsulation that is designed to look like
* a TLS connection. It's not a TLS connection, but it sends TLS format record
* headers. It could be extended in the future to implement a fake TLS
* handshake.
*
* At the moment, each packet is just made to look like TLS application data:
* <[1] TLS content type> - currently 0x17 for "application data"
* <[1] TLS major version> - currently 0x03 for TLS 1.2
* <[1] TLS minor version> - currently 0x03 for TLS 1.2
* <[2] payload length> - 16-bit length of payload in bytes
* <[...] payload> - Message payload
*
* TCP is inherently inefficient for encapsulating Ethernet, since TCP and TCP
* like protocols over TCP lead to double-ACKs. So this transport is only used
* to enable access when UDP or other datagram protocols are not available.
*
* Clients send a greeting, which is a four-byte message that contains:
* <[1] ZeroTier major version>
* <[1] minor version>
* <[2] revision>
*
* If a client has sent a greeting, it uses the new version of this protocol
* in which every encapsulated ZT packet is prepended by an IP address where
* it should be forwarded (or where it came from for replies). This causes
* this proxy to act as a remote UDP socket similar to a socks proxy, which
* will allow us to move this function off the rootservers and onto dedicated
* proxy nodes.
*
* Older ZT clients that do not send this message get their packets relayed
* to/from 127.0.0.1:9993, which will allow them to talk to and relay via
* the ZT node on the same machine as the proxy. We'll only support this for
* as long as such nodes appear to be in the wild.
*/
struct TcpProxyService;
struct TcpProxyService
{
Phy<TcpProxyService *> *phy;
int udpPortCounter;
struct Client
{
char tcpReadBuf[131072];
char tcpWriteBuf[131072];
unsigned long tcpWritePtr;
unsigned long tcpReadPtr;
PhySocket *tcp;
PhySocket *udp;
time_t lastActivity;
bool newVersion;
};
std::map< PhySocket *,Client > clients;
PhySocket *getUnusedUdp(void *uptr)
{
for(int i=0;i<65535;++i) {
++udpPortCounter;
if (udpPortCounter > 0xfffe)
udpPortCounter = 1024;
struct sockaddr_in laddr;
memset(&laddr,0,sizeof(struct sockaddr_in));
laddr.sin_family = AF_INET;
laddr.sin_port = htons((uint16_t)udpPortCounter);
PhySocket *udp = phy->udpBind(reinterpret_cast<struct sockaddr *>(&laddr),uptr);
if (udp)
return udp;
}
return (PhySocket *)0;
}
void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
{
if (!*uptr)
return;
if ((from->sa_family == AF_INET)&&(len >= 16)&&(len < 2048)) {
Client &c = *((Client *)*uptr);
c.lastActivity = time((time_t *)0);
unsigned long mlen = len;
if (c.newVersion)
mlen += 7; // new clients get IP info
if ((c.tcpWritePtr + 5 + mlen) <= sizeof(c.tcpWriteBuf)) {
if (!c.tcpWritePtr)
phy->setNotifyWritable(c.tcp,true);
c.tcpWriteBuf[c.tcpWritePtr++] = 0x17; // look like TLS data
c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2
c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2
c.tcpWriteBuf[c.tcpWritePtr++] = (char)((mlen >> 8) & 0xff);
c.tcpWriteBuf[c.tcpWritePtr++] = (char)(mlen & 0xff);
if (c.newVersion) {
c.tcpWriteBuf[c.tcpWritePtr++] = (char)4; // IPv4
*((uint32_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_addr.s_addr;
c.tcpWritePtr += 4;
*((uint16_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_port;
c.tcpWritePtr += 2;
}
for(unsigned long i=0;i<len;++i)
c.tcpWriteBuf[c.tcpWritePtr++] = ((const char *)data)[i];
}
//printf("<< UDP %s:%d -> %.16llx\n",inet_ntoa(reinterpret_cast<const struct sockaddr_in *>(from)->sin_addr),(int)ntohs(reinterpret_cast<const struct sockaddr_in *>(from)->sin_port),(unsigned long long)&c);
}
}
void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
{
// unused, we don't initiate outbound connections
}
void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
{
Client &c = clients[sockN];
PhySocket *udp = getUnusedUdp((void *)&c);
if (!udp) {
phy->close(sockN);
clients.erase(sockN);
//printf("** TCP rejected, no more UDP ports to assign\n");
return;
}
c.tcpWritePtr = 0;
c.tcpReadPtr = 0;
c.tcp = sockN;
c.udp = udp;
c.lastActivity = time((time_t *)0);
c.newVersion = false;
*uptrN = (void *)&c;
//printf("<< TCP from %s -> %.16llx\n",inet_ntoa(reinterpret_cast<const struct sockaddr_in *>(from)->sin_addr),(unsigned long long)&c);
}
void phyOnTcpClose(PhySocket *sock,void **uptr)
{
if (!*uptr)
return;
Client &c = *((Client *)*uptr);
phy->close(c.udp);
clients.erase(sock);
//printf("** TCP %.16llx closed\n",(unsigned long long)*uptr);
}
void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
{
Client &c = *((Client *)*uptr);
c.lastActivity = time((time_t *)0);
for(unsigned long i=0;i<len;++i) {
if (c.tcpReadPtr >= sizeof(c.tcpReadBuf)) {
phy->close(sock);
return;
}
c.tcpReadBuf[c.tcpReadPtr++] = ((const char *)data)[i];
if (c.tcpReadPtr >= 5) {
unsigned long mlen = ( ((((unsigned long)c.tcpReadBuf[3]) & 0xff) << 8) | (((unsigned long)c.tcpReadBuf[4]) & 0xff) );
if (c.tcpReadPtr >= (mlen + 5)) {
if (mlen == 4) {
// Right now just sending this means the client is 'new enough' for the IP header
c.newVersion = true;
//printf("<< TCP %.16llx HELLO\n",(unsigned long long)*uptr);
} else if (mlen >= 7) {
char *payload = c.tcpReadBuf + 5;
unsigned long payloadLen = mlen;
struct sockaddr_in dest;
memset(&dest,0,sizeof(dest));
if (c.newVersion) {
if (*payload == (char)4) {
// New clients tell us where their packets go.
++payload;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = *((uint32_t *)payload);
payload += 4;
dest.sin_port = *((uint16_t *)payload); // will be in network byte order already
payload += 2;
payloadLen -= 7;
}
} else {
// For old clients we will just proxy everything to a local ZT instance. The
// fact that this will come from 127.0.0.1 will in turn prevent that instance
// from doing unite() with us. It'll just forward. There will not be many of
// these.
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1
dest.sin_port = htons(9993);
}
// Note: we do not relay to privileged ports... just an abuse prevention rule.
if ((ntohs(dest.sin_port) > 1024)&&(payloadLen >= 16)) {
phy->udpSend(c.udp,(const struct sockaddr *)&dest,payload,payloadLen);
//printf(">> TCP %.16llx to %s:%d\n",(unsigned long long)*uptr,inet_ntoa(dest.sin_addr),(int)ntohs(dest.sin_port));
}
}
memmove(c.tcpReadBuf,c.tcpReadBuf + (mlen + 5),c.tcpReadPtr -= (mlen + 5));
}
}
}
}
void phyOnTcpWritable(PhySocket *sock,void **uptr)
{
Client &c = *((Client *)*uptr);
if (c.tcpWritePtr) {
long n = phy->streamSend(sock,c.tcpWriteBuf,c.tcpWritePtr);
if (n > 0) {
memmove(c.tcpWriteBuf,c.tcpWriteBuf + n,c.tcpWritePtr -= (unsigned long)n);
if (!c.tcpWritePtr)
phy->setNotifyWritable(sock,false);
}
} else phy->setNotifyWritable(sock,false);
}
void doHousekeeping()
{
std::vector<PhySocket *> toClose;
time_t now = time((time_t *)0);
for(std::map< PhySocket *,Client >::iterator c(clients.begin());c!=clients.end();++c) {
if ((now - c->second.lastActivity) >= ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS) {
toClose.push_back(c->first);
toClose.push_back(c->second.udp);
}
}
for(std::vector<PhySocket *>::iterator s(toClose.begin());s!=toClose.end();++s)
phy->close(*s);
}
};
int main(int argc,char **argv)
{
signal(SIGPIPE,SIG_IGN);
signal(SIGHUP,SIG_IGN);
srand(time((time_t *)0));
TcpProxyService svc;
Phy<TcpProxyService *> phy(&svc,false,true);
svc.phy = &phy;
svc.udpPortCounter = 1023;
{
struct sockaddr_in laddr;
memset(&laddr,0,sizeof(laddr));
laddr.sin_family = AF_INET;
laddr.sin_port = htons(ZT_TCP_PROXY_TCP_PORT);
if (!phy.tcpListen((const struct sockaddr *)&laddr)) {
fprintf(stderr,"%s: fatal error: unable to bind TCP port %d\n",argv[0],ZT_TCP_PROXY_TCP_PORT);
return 1;
}
}
time_t lastDidHousekeeping = time((time_t *)0);
for(;;) {
phy.poll(120000);
time_t now = time((time_t *)0);
if ((now - lastDidHousekeeping) > 120) {
lastDidHousekeeping = now;
svc.doHousekeeping();
}
}
return 0;
}

View File

@ -27,6 +27,76 @@ using json = nlohmann::json;
namespace ZeroTier { namespace ZeroTier {
void DB::initNetwork(nlohmann::json &network)
{
if (!network.count("private")) network["private"] = true;
if (!network.count("creationTime")) network["creationTime"] = OSUtils::now();
if (!network.count("name")) network["name"] = "";
if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32;
if (!network.count("enableBroadcast")) network["enableBroadcast"] = true;
if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}};
if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}};
if (!network.count("authTokens")) network["authTokens"] = {{}};
if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array();
if (!network.count("tags")) network["tags"] = nlohmann::json::array();
if (!network.count("routes")) network["routes"] = nlohmann::json::array();
if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array();
if (!network.count("anchors")) network["anchors"] = nlohmann::json::array();
if (!network.count("mtu")) network["mtu"] = ZT_DEFAULT_MTU;
if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json();
if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0;
if (!network.count("rules")) {
// If unspecified, rules are set to allow anything and behave like a flat L2 segment
network["rules"] = {{
{ "not",false },
{ "or", false },
{ "type","ACTION_ACCEPT" }
}};
}
network["objtype"] = "network";
}
void DB::initMember(nlohmann::json &member)
{
if (!member.count("authorized")) member["authorized"] = false;
if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array();
if (!member.count("activeBridge")) member["activeBridge"] = false;
if (!member.count("tags")) member["tags"] = nlohmann::json::array();
if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array();
if (!member.count("creationTime")) member["creationTime"] = OSUtils::now();
if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false;
if (!member.count("revision")) member["revision"] = 0ULL;
if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL;
if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json();
if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json();
if (!member.count("vMajor")) member["vMajor"] = -1;
if (!member.count("vMinor")) member["vMinor"] = -1;
if (!member.count("vRev")) member["vRev"] = -1;
if (!member.count("vProto")) member["vProto"] = -1;
if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json();
if (!member.count("removeTraceLevel")) member["remoteTraceLevel"] = 0;
member["objtype"] = "member";
}
void DB::cleanNetwork(nlohmann::json &network)
{
network.erase("clock");
network.erase("authorizedMemberCount");
network.erase("activeMemberCount");
network.erase("totalMemberCount");
network.erase("lastModified");
}
void DB::cleanMember(nlohmann::json &member)
{
member.erase("clock");
member.erase("physicalAddr");
member.erase("recentLog");
member.erase("lastModified");
member.erase("lastRequestMetaData");
}
DB::DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) : DB::DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
_controller(nc), _controller(nc),
_myId(myId), _myId(myId),

View File

@ -58,6 +58,26 @@ public:
int64_t mostRecentDeauthTime; int64_t mostRecentDeauthTime;
}; };
/**
* Ensure that all network fields are present
*/
static void initNetwork(nlohmann::json &network);
/**
* Ensure that all member fields are present
*/
static void initMember(nlohmann::json &member);
/**
* Remove old and temporary network fields
*/
static void cleanNetwork(nlohmann::json &network);
/**
* Remove old and temporary member fields
*/
static void cleanMember(nlohmann::json &member);
DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path); DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
virtual ~DB(); virtual ~DB();

View File

@ -648,7 +648,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
json member,network; json member,network;
_db->get(nwid,network,address,member); _db->get(nwid,network,address,member);
json origMember(member); // for detecting changes json origMember(member); // for detecting changes
_initMember(member); DB::initMember(member);
try { try {
if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false); if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false);
@ -734,7 +734,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
member["address"] = addrs; // legacy member["address"] = addrs; // legacy
member["nwid"] = nwids; member["nwid"] = nwids;
_cleanMember(member); DB::cleanMember(member);
_db->save(&origMember,member); _db->save(&origMember,member);
responseBody = OSUtils::jsonDump(member); responseBody = OSUtils::jsonDump(member);
responseContentType = "application/json"; responseContentType = "application/json";
@ -767,7 +767,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
json network; json network;
_db->get(nwid,network); _db->get(nwid,network);
json origNetwork(network); // for detecting changes json origNetwork(network); // for detecting changes
_initNetwork(network); DB::initNetwork(network);
try { try {
if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],""); if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],"");
@ -981,7 +981,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
network["id"] = nwids; network["id"] = nwids;
network["nwid"] = nwids; // legacy network["nwid"] = nwids; // legacy
_cleanNetwork(network); DB::cleanNetwork(network);
_db->save(&origNetwork,network); _db->save(&origNetwork,network);
responseBody = OSUtils::jsonDump(network); responseBody = OSUtils::jsonDump(network);
@ -1183,7 +1183,7 @@ void EmbeddedNetworkController::_request(
} }
origMember = member; origMember = member;
const bool newMember = ((!member.is_object())||(member.size() == 0)); const bool newMember = ((!member.is_object())||(member.size() == 0));
_initMember(member); DB::initMember(member);
{ {
const std::string haveIdStr(OSUtils::jsonString(member["identity"],"")); const std::string haveIdStr(OSUtils::jsonString(member["identity"],""));
@ -1281,7 +1281,7 @@ void EmbeddedNetworkController::_request(
} }
} else { } else {
// If they are not authorized, STOP! // If they are not authorized, STOP!
_cleanMember(member); DB::cleanMember(member);
_db->save(&origMember,member); _db->save(&origMember,member);
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
return; return;
@ -1646,7 +1646,7 @@ void EmbeddedNetworkController::_request(
return; return;
} }
_cleanMember(member); DB::cleanMember(member);
_db->save(&origMember,member); _db->save(&origMember,member);
_sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); _sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6);
} }

View File

@ -105,73 +105,6 @@ private:
void _request(uint64_t nwid,const InetAddress &fromAddr,uint64_t requestPacketId,const Identity &identity,const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData); void _request(uint64_t nwid,const InetAddress &fromAddr,uint64_t requestPacketId,const Identity &identity,const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData);
void _startThreads(); void _startThreads();
// These init objects with default and static/informational fields
inline void _initMember(nlohmann::json &member)
{
if (!member.count("authorized")) member["authorized"] = false;
if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array();
if (!member.count("activeBridge")) member["activeBridge"] = false;
if (!member.count("tags")) member["tags"] = nlohmann::json::array();
if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array();
if (!member.count("creationTime")) member["creationTime"] = OSUtils::now();
if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false;
if (!member.count("revision")) member["revision"] = 0ULL;
if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL;
if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json();
if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json();
if (!member.count("vMajor")) member["vMajor"] = -1;
if (!member.count("vMinor")) member["vMinor"] = -1;
if (!member.count("vRev")) member["vRev"] = -1;
if (!member.count("vProto")) member["vProto"] = -1;
if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json();
if (!member.count("removeTraceLevel")) member["remoteTraceLevel"] = 0;
member["objtype"] = "member";
}
inline void _initNetwork(nlohmann::json &network)
{
if (!network.count("private")) network["private"] = true;
if (!network.count("creationTime")) network["creationTime"] = OSUtils::now();
if (!network.count("name")) network["name"] = "";
if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32;
if (!network.count("enableBroadcast")) network["enableBroadcast"] = true;
if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}};
if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}};
if (!network.count("authTokens")) network["authTokens"] = {{}};
if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array();
if (!network.count("tags")) network["tags"] = nlohmann::json::array();
if (!network.count("routes")) network["routes"] = nlohmann::json::array();
if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array();
if (!network.count("mtu")) network["mtu"] = ZT_DEFAULT_MTU;
if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json();
if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0;
if (!network.count("rules")) {
// If unspecified, rules are set to allow anything and behave like a flat L2 segment
network["rules"] = {{
{ "not",false },
{ "or", false },
{ "type","ACTION_ACCEPT" }
}};
}
network["objtype"] = "network";
}
inline void _cleanNetwork(nlohmann::json &network)
{
network.erase("clock");
network.erase("authorizedMemberCount");
network.erase("activeMemberCount");
network.erase("totalMemberCount");
network.erase("lastModified");
}
inline void _cleanMember(nlohmann::json &member)
{
member.erase("clock");
member.erase("physicalAddr");
member.erase("recentLog");
member.erase("lastModified");
member.erase("lastRequestMetaData");
}
struct _RQEntry struct _RQEntry
{ {
uint64_t nwid; uint64_t nwid;

View File

@ -102,11 +102,6 @@
*/ */
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
/**
* Device can send CIRCUIT_TESTs for this network
*/
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER 0x0000080000000000ULL
namespace ZeroTier { namespace ZeroTier {
// Dictionary capacity needed for max size network config // Dictionary capacity needed for max size network config
@ -344,21 +339,6 @@ public:
return false; return false;
} }
/**
* @param byPeer Address to check
* @return True if this peer is allowed to do circuit tests on this network (controller is always true)
*/
inline bool circuitTestingAllowed(const Address &byPeer) const
{
if (byPeer.toInt() == ((networkId >> 24) & 0xffffffffffULL))
return true;
for(unsigned int i=0;i<specialistCount;++i) {
if ((byPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER) != 0))
return true;
}
return false;
}
/** /**
* @return True if this network config is non-NULL * @return True if this network config is non-NULL
*/ */

View File

@ -40,8 +40,9 @@
#include "Constants.hpp" #include "Constants.hpp"
// So it's 2017 and this still helps on most Linux versions. It shouldn't but it does. Go figure. #ifdef __LINUX__
#if defined(__LINUX__) && ((defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)))
#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
#include <emmintrin.h> #include <emmintrin.h>
static inline void ZT_FAST_MEMCPY(void *a,const void *b,unsigned long k) static inline void ZT_FAST_MEMCPY(void *a,const void *b,unsigned long k)
{ {
@ -74,6 +75,10 @@ static inline void ZT_FAST_MEMCPY(void *a,const void *b,unsigned long k)
#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c) #define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
#endif #endif
#else
#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
#endif
namespace ZeroTier { namespace ZeroTier {
/** /**

View File

@ -43,6 +43,7 @@
#include "../node/MulticastGroup.hpp" #include "../node/MulticastGroup.hpp"
#include "../node/Mutex.hpp" #include "../node/Mutex.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
#include "../osdep/OSUtils.hpp"
namespace ZeroTier { namespace ZeroTier {
@ -66,7 +67,7 @@ public:
_enabled(true) _enabled(true)
{ {
char tmp[32]; char tmp[32];
Utils::snprintf(tmp,sizeof(tmp),"%.16llx",(unsigned long long)_nwid); OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",(unsigned long long)_nwid);
_dev.append(tmp); _dev.append(tmp);
#ifdef ZT_TEST_TAP_REPORT_TO #ifdef ZT_TEST_TAP_REPORT_TO
_reportTo.fromString(ZT_TEST_TAP_REPORT_TO); _reportTo.fromString(ZT_TEST_TAP_REPORT_TO);