Merge branch 'adamierymenko-dev' into android-jni

This commit is contained in:
Grant Limberg 2015-06-26 17:44:09 -07:00
commit 9c26d10ea6
32 changed files with 314 additions and 783 deletions

View File

@ -8,6 +8,9 @@
## Contributors
* A number of fixes and improvements to the new controller, other stuff.<br>
Kees Bos / https://github.com/keesbos
* Debugging and testing, OpenWRT support fixes.<br>
Moritz Warning / moritzwarning@web.de

View File

@ -155,7 +155,6 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
||(sqlite3_prepare_v2(_db,"UPDATE Node SET lastAt = ?,lastSeen = ? WHERE id = ?",-1,&_sUpdateNode,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Node SET lastSeen = ? WHERE id = ?",-1,&_sUpdateNode2,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT etherType FROM Rule WHERE networkId = ? AND \"action\" = 'accept'",-1,&_sGetEtherTypesFromRuleTable,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT mgMac,mgAdi,preload,maxBalance,accrual FROM MulticastRate WHERE networkId = ?",-1,&_sGetMulticastRates,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT nodeId FROM Member WHERE networkId = ? AND activeBridge > 0 AND authorized > 0",-1,&_sGetActiveBridges,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND ipVersion = ?",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ipNetwork,ipNetmaskBits FROM IpAssignmentPool WHERE networkId = ? AND ipVersion = ?",-1,&_sGetIpAssignmentPools,(const char **)0) != SQLITE_OK)
@ -178,6 +177,8 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignmentPool WHERE networkId = ?",-1,&_sDeleteIpAssignmentPoolsForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Rule WHERE networkId = ?",-1,&_sDeleteRulesForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignmentPool (networkId,ipNetwork,ipNetmaskBits,ipVersion) VALUES (?,?,?,?)",-1,&_sCreateIpAssignmentPool,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ? WHERE rowid = ?",-1,&_sUpdateMemberAuthorized,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ? WHERE rowid = ?",-1,&_sUpdateMemberActiveBridge,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sDeleteMember,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Network WHERE id = ?",-1,&_sDeleteNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ip,ipVersion,metric FROM Gateway WHERE networkId = ? ORDER BY metric ASC",-1,&_sGetGateways,(const char **)0) != SQLITE_OK)
@ -202,7 +203,6 @@ SqliteNetworkController::~SqliteNetworkController()
sqlite3_finalize(_sUpdateNode);
sqlite3_finalize(_sUpdateNode2);
sqlite3_finalize(_sGetEtherTypesFromRuleTable);
sqlite3_finalize(_sGetMulticastRates);
sqlite3_finalize(_sGetActiveBridges);
sqlite3_finalize(_sGetIpAssignmentsForNode);
sqlite3_finalize(_sGetIpAssignmentPools);
@ -225,6 +225,9 @@ SqliteNetworkController::~SqliteNetworkController()
sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork);
sqlite3_finalize(_sDeleteRulesForNetwork);
sqlite3_finalize(_sCreateIpAssignmentPool);
sqlite3_finalize(_sUpdateMemberAuthorized);
sqlite3_finalize(_sUpdateMemberActiveBridge);
sqlite3_finalize(_sDeleteMember);
sqlite3_finalize(_sDeleteNetwork);
sqlite3_finalize(_sGetGateways);
sqlite3_finalize(_sDeleteGateways);
@ -342,7 +345,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
sqlite3_reset(_sCreateMember);
sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC);
sqlite3_bind_text(_sCreateMember,2,member.nodeId,10,SQLITE_STATIC);
sqlite3_bind_int(_sCreateMember,3,(member.authorized ? 0 : 1));
sqlite3_bind_int(_sCreateMember,3,(member.authorized ? 1 : 0));
if (sqlite3_step(_sCreateMember) != SQLITE_DONE) {
netconf["error"] = "unable to create new member record";
return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
@ -398,26 +401,10 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = allowedEtherTypesCsv;
}
{
std::string multicastRates;
sqlite3_reset(_sGetMulticastRates);
sqlite3_bind_text(_sGetMulticastRates,1,network.id,16,SQLITE_STATIC);
while (sqlite3_step(_sGetMulticastRates) == SQLITE_ROW) {
const char *mac = (const char *)sqlite3_column_text(_sGetMulticastRates,0);
if ((mac)&&(strlen(mac) == 12)) {
unsigned long adi = ((unsigned long)sqlite3_column_int64(_sGetMulticastRates,1)) & 0xffffffff;
char tmp[256];
Utils::snprintf(tmp,sizeof(tmp),"%s/%.4lx=%x,%x,%x\n",mac,adi,sqlite3_column_int(_sGetMulticastRates,2),sqlite3_column_int(_sGetMulticastRates,3),sqlite3_column_int(_sGetMulticastRates,4));
multicastRates.append(tmp);
}
}
if (multicastRates.length() > 0)
netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = multicastRates;
if (network.multicastLimit > 0) {
char ml[16];
Utils::snprintf(ml,sizeof(ml),"%lx",(unsigned long)network.multicastLimit);
netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT] = ml;
}
if (network.multicastLimit > 0) {
char ml[16];
Utils::snprintf(ml,sizeof(ml),"%lx",(unsigned long)network.multicastLimit);
netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT] = ml;
}
{
@ -519,7 +506,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,1);
if ((ip)&&(sqlite3_column_bytes(_sGetIpAssignmentsForNode,0) >= 4)&&(ipNetmaskBits > 0)&&(ipNetmaskBits <= 32)) {
char tmp[32];
Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)ip[0],(int)ip[1],(int)ip[2],(int)ip[3],ipNetmaskBits);
Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)ip[12+0],(int)ip[12+1],(int)ip[12+2],(int)ip[12+3],ipNetmaskBits);
if (v4s.length())
v4s.push_back(',');
v4s.append(tmp);
@ -680,29 +667,27 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
if (!strcmp(j->u.object.values[k].name,"authorized")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_stmt *stmt = (sqlite3_stmt *)0;
if (sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ? WHERE rowid = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int64(stmt,2,memberRowId);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_reset(_sUpdateMemberAuthorized);
sqlite3_bind_int(_sUpdateMemberAuthorized,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int64(_sUpdateMemberAuthorized,2,memberRowId);
if (sqlite3_step(_sUpdateMemberAuthorized) != SQLITE_DONE)
return 500;
}
} else if (!strcmp(j->u.object.values[k].name,"activeBridge")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_stmt *stmt = (sqlite3_stmt *)0;
if (sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ? WHERE rowid = ?",-1,&stmt,(const char **)0) == SQLITE_OK) {
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int64(stmt,2,memberRowId);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
sqlite3_reset(_sUpdateMemberActiveBridge);
sqlite3_bind_int(_sUpdateMemberActiveBridge,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int64(_sUpdateMemberActiveBridge,2,memberRowId);
if (sqlite3_step(_sUpdateMemberActiveBridge) != SQLITE_DONE)
return 500;
}
} else if (!strcmp(j->u.object.values[k].name,"ipAssignments")) {
if (j->u.object.values[k].value->type == json_array) {
sqlite3_reset(_sDeleteIpAllocations);
sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC);
sqlite3_step(_sDeleteIpAllocations);
if (sqlite3_step(_sDeleteIpAllocations) != SQLITE_DONE)
return 500;
for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
json_value *ipalloc = j->u.object.values[k].value->u.array.values[kk];
if (ipalloc->type == json_string) {
@ -731,7 +716,8 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
sqlite3_bind_blob(_sAllocateIp,3,(const void *)ipBlob,16,SQLITE_STATIC);
sqlite3_bind_int(_sAllocateIp,4,(int)a.netmaskBits());
sqlite3_bind_int(_sAllocateIp,5,ipVersion);
sqlite3_step(_sAllocateIp);
if (sqlite3_step(_sAllocateIp) != SQLITE_DONE)
return 500;
}
}
}
@ -1172,7 +1158,14 @@ unsigned int SqliteNetworkController::_doCPGet(
sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC);
bool firstIp = true;
while (sqlite3_step(_sGetIpAssignmentsForNode2) == SQLITE_ROW) {
InetAddress ip((const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),(sqlite3_column_int(_sGetIpAssignmentsForNode2,2) == 6) ? 16 : 4,(unsigned int)sqlite3_column_int(_sGetIpAssignmentPools2,1));
int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode2,2);
char ipBlob[16];
memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),16);
InetAddress ip(
(const void *)(ipversion == 6 ? ipBlob : &ipBlob[12]),
(ipversion == 6 ? 16 : 4),
(unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode2,1)
);
responseBody.append(firstIp ? "\"" : ",\"");
firstIp = false;
responseBody.append(_jsonEscape(ip.toString()));

View File

@ -99,7 +99,6 @@ private:
sqlite3_stmt *_sUpdateNode;
sqlite3_stmt *_sUpdateNode2;
sqlite3_stmt *_sGetEtherTypesFromRuleTable;
sqlite3_stmt *_sGetMulticastRates;
sqlite3_stmt *_sGetActiveBridges;
sqlite3_stmt *_sGetIpAssignmentsForNode;
sqlite3_stmt *_sGetIpAssignmentPools;
@ -122,6 +121,8 @@ private:
sqlite3_stmt *_sDeleteIpAssignmentPoolsForNetwork;
sqlite3_stmt *_sDeleteRulesForNetwork;
sqlite3_stmt *_sCreateIpAssignmentPool;
sqlite3_stmt *_sUpdateMemberAuthorized;
sqlite3_stmt *_sUpdateMemberActiveBridge;
sqlite3_stmt *_sDeleteMember;
sqlite3_stmt *_sDeleteNetwork;
sqlite3_stmt *_sGetGateways;

View File

@ -64,17 +64,6 @@ CREATE TABLE Member (
CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);
CREATE TABLE MulticastRate (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
mgMac char(12) NOT NULL,
mgAdi integer NOT NULL DEFAULT(0),
preload integer NOT NULL,
maxBalance integer NOT NULL,
accrual integer NOT NULL
);
CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);
CREATE TABLE Relay (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,

View File

@ -65,17 +65,6 @@
"\n"\
"CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\
"\n"\
"CREATE TABLE MulticastRate (\n"\
" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
" mgMac char(12) NOT NULL,\n"\
" mgAdi integer NOT NULL DEFAULT(0),\n"\
" preload integer NOT NULL,\n"\
" maxBalance integer NOT NULL,\n"\
" accrual integer NOT NULL\n"\
");\n"\
"\n"\
"CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);\n"\
"\n"\
"CREATE TABLE Relay (\n"\
" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
" nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"\

View File

@ -559,8 +559,8 @@ typedef struct
*/
enum ZT1_PeerRole {
ZT1_PEER_ROLE_LEAF = 0, // ordinary node
ZT1_PEER_ROLE_HUB = 1, // locally federated hub
ZT1_PEER_ROLE_ROOTSERVER = 2 // planetary rootserver
ZT1_PEER_ROLE_RELAY = 1, // relay node
ZT1_PEER_ROLE_ROOT = 2 // root server
};
/**

View File

@ -1,58 +0,0 @@
'use strict'
function convertType(v,t)
{
if (Array.isArray(t)) {
var r = v;
if (t.length !== 0) {
if (Array.isArray(v)) {
r = [];
for(var i=0;i<v.length;++i)
r.push(convertType(v[i],t[0]));
} else r = [ convertType(v,t[0]) ];
} else r = [ v ];
return r;
} else if (t === 'string') {
if (typeof v === 'string')
return v;
else if ((typeof v === 'boolean')||(typeof v === 'number'))
return v.toString();
else if (Array.isArray(v)||(typeof v === 'object'))
return JSON.stringify(v);
else return '';
} else if (t === 'integer') {
if (typeof v === 'number')
return Math.round(v);
else if (typeof v === 'string')
return parseInt(v);
else if (typeof v === 'boolean')
return ((v) ? 1 : 0);
else return 0;
} else if (t === 'number') {
if (typeof v === 'number')
return v;
else if (typeof v === 'string')
return parseFloat(v);
else if (typeof v === 'boolean')
return ((v) ? 1 : 0);
else return 0;
} else if (t === 'boolean') {
return ((v) ? true : false);
} else if (typeof t === 'object') {
if ((v !== null)&&(typeof v === 'object'))
return constrainTypes(v,t);
else return {};
} else return v;
}
function constrainTypes(obj,typeMap)
{
var r = {};
for(var k in obj) {
var t = typeMap[k];
r[k] = convertType(v,t);
}
return r;
}
exports = constrainTypes;

View File

@ -1,231 +0,0 @@
'use strict'
var request = require('request');
var constrainTypes = require('./constrain-types.js');
// Types that fields must be in submissions -- used with constrainTypes to
// ensure that submitted JSON objects are correctly typed since the JSON
// API is very sensitive to this. This only includes writable fields since
// non-writable and unknown fields are ignored.
var REQUEST_TYPE_MAPS = {
'controller/network/*/relay': {
'address': 'string',
'phyAddress': 'string'
},
'controller/network/*/rule': {
'ruleId': 'integer',
'nodeId': 'string',
'vlanId': 'integer',
'vlanPcp': 'integer',
'etherType': 'integer',
'macSource': 'string',
'macDest': 'string',
'ipSource': 'string',
'ipDest': 'string',
'ipTos': 'integer',
'ipProtocol': 'integer',
'ipSourcePort': 'integer',
'ipDestPort': 'integer',
'flags': 'integer',
'invFlags': 'integer',
'action': 'string'
},
'controller/network/*/ipAssignmentPool': {
'network': 'string',
'netmaskBits': 'integer'
},
'controller/network/*/member': {
'authorized': 'boolean',
'activeBridge': 'boolean',
'ipAssignments': [ 'string' ]
},
'controller/network/*': {
'name': 'string',
'private': 'boolean',
'enableBroadcast': 'boolean',
'allowPassiveBridging': 'boolean',
'v4AssignMode': 'string',
'v6AssignMode': 'string',
'multicastLimit': 'integer',
'relays': [ this['controller/network/*/relay'] ],
'ipAssignmentPools': [ this['controller/network/*/ipAssignmentPool'] ],
'rules': [ this['controller/network/*/rule'] ]
}
};
// URL must end with trailing slash e.g. http://127.0.0.1:9993/
function ZT1ApiClient(url,authToken)
{
this.url = url;
this.authToken = authToken;
}
// Simple JSON URI getter, for internal use.
ZT1ApiClient.prototype._jsonGet = function(getPath,callback)
{
request({
url: this.url + getPath,
method: 'GET',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(error,response,body) {
if (error)
return callback(error,null);
if (response.statusCode !== 200)
return callback(new Error('server responded with error: '+response.statusCode),null);
return callback(null,(typeof body === 'string') ? JSON.parse(body) : null);
});
};
// Generate new ZeroTier identity -- mostly for testing
ZT1ApiClient.prototype.newIdentity = function(callback)
{
request({
url: this.url + 'newIdentity',
method: 'GET',
json: false,
headers: {
'X-ZT1-Auth': this.authToken
}
},function(error,response,body) {
if (error)
return callback(error,null);
if (response.statusCode === 200)
return callback(null,body);
return callback(new Error('server responded with error: '+response.statusCode),'');
});
}
// Get node status -- returns a combination of regular status and (if present) controller info
ZT1ApiClient.prototype.status = function(callback)
{
request({
url: this.url + 'controller',
method: 'GET',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(error,response,body) {
if (error)
return callback(error,null);
var controllerStatus = {};
if ((typeof body === 'string')&&(response.statusCode === 200))
controllerStatus = JSON.parse(body);
request({
url: this.url + 'status',
method: 'GET',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(error,response,body) {
if (error)
return callback(error,{});
if (response.statusCode !== 200)
return callback(new Error('server responded with '+response.statusCode),{});
var nodeStatus = JSON.parse(body);
for(var k in controllerStatus)
nodeStatus[k] = controllerStatus[k];
return callback(null,nodeStatus);
}.bind(this));
}.bind(this));
};
ZT1ApiClient.prototype.getNetworks = function(callback)
{
this._jsonGet('network',callback);
};
ZT1ApiClient.prototype.getPeers = function(callback)
{
this._jsonGet('peer',callback);
};
ZT1ApiClient.prototype.listControllerNetworks = function(callback)
{
this._jsonGet('controller/network',callback);
};
ZT1ApiClient.prototype.getControllerNetwork = function(nwid,callback)
{
this._jsonGet('controller/network/' + nwid,callback);
};
// If NWID is the special ##########______ format, a new NWID will
// be generated server side and filled in in returned object.
ZT1ApiClient.prototype.saveControllerNetwork = function(network,callback)
{
request({
url: this.url + 'controller/network/' + n.nwid,
method: 'POST',
json: true,
body: constrainTypes(network,REQUEST_TYPE_MAPS['controller/network/*']),
headers: {
'X-ZT1-Auth': this.authToken
}
},function(err,response,body) {
if (err)
return callback(err,null);
if (response.statusCode !== 200)
return callback(new Error('server responded with error: '+response.statusCode),null);
return callback(null,(typeof body === 'string') ? JSON.parse(body) : body);
});
};
ZT1ApiClient.prototype.deleteControllerNetwork = function(nwid,callback) {
request({
url: this.url + 'controller/network/'+ nwid,
method: 'DELETE',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(err,response,body) {
if (err)
return callback(err);
else if (response.statusCode === 200)
return callback(null);
else return callback(new Error('server responded with error: '+response.statusCode));
});
};
ZT1ApiClient.prototype.getControllerNetworkMember = function(nwid,address,callback) {
this._jsonGet('controller/network/' + nwid + '/member/' + address,callback);
};
ZT1ApiClient.prototype.saveControllerNetworkMember = function(nwid,member,callback) {
var m = constrainTypes(member,REQUEST_TYPE_MAPS['controller/network/*/member']);
m.nwid = nwid;
request({
url: this.url + 'controller/network' + nwid + '/member/' + member.address,
method: 'POST',
json: true,
body: m,
headers: {
'X-ZT1-Auth': this.authToken
}
},function(err,response,body) {
if (err)
return callback(err,null);
if (response.statusCode !== 200)
return callback(new Error('server responded with error: '+response.statusCode),null);
return callback(null,(typeof body === 'string') ? JSON.parse(body) : body);
});
};
ZT1ApiClient.prototype.deleteControllerNetworkMember = function(nwid,address,callback) {
request({
url: this.url + 'controller/network/' + nwid + '/member/' + address,
method: 'DELETE',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(err,response,body) {
if (err)
return callback(err);
else if (response.statusCode === 200)
return callback(null);
else return callback(new Error('server responded with error: '+response.statusCode));
});
};
exports.ZT1ApiClient = ZT1ApiClient;

View File

@ -1,15 +0,0 @@
{
"name": "zt1-api-client",
"version": "0.0.1",
"description": "ZeroTier One JSON API Client",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ZeroTier, Inc.",
"license": "BSD",
"dependencies": {
"async": "^0.9.0",
"request": "^2.55.0"
}
}

View File

@ -254,7 +254,7 @@
/**
* Delay between scans of the topology active peer DB for peers that need ping
*
* This is also how often pings will be retried to upstream peers (rootservers)
* This is also how often pings will be retried to upstream peers (relays, roots)
* constantly until something is heard.
*/
#define ZT_PING_CHECK_INVERVAL 6250
@ -279,9 +279,9 @@
*
* When we send something (including frames), we generally expect a response.
* Switching relays if no response in a short period of time causes more
* rapid failover if a rootserver goes down or becomes unreachable. In the
* rapid failover if a root server goes down or becomes unreachable. In the
* mistaken case, little harm is done as it'll pick the next-fastest
* rootserver and will switch back eventually.
* root server and will switch back eventually.
*/
#define ZT_PEER_RELAY_CONVERSATION_LATENCY_THRESHOLD 10000

View File

@ -110,7 +110,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
case Packet::ERROR_OBJ_NOT_FOUND:
if (inReVerb == Packet::VERB_WHOIS) {
if (RR->topology->isRootserver(peer->address()))
if (RR->topology->isRoot(peer->identity()))
RR->sw->cancelWhoisRequest(Address(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH));
} else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
@ -128,7 +128,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
break;
case Packet::ERROR_IDENTITY_COLLISION:
if (RR->topology->isRootserver(peer->address()))
if (RR->topology->isRoot(peer->identity()))
RR->node->postEvent(ZT1_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
break;
@ -268,7 +268,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision);
bool trusted = false;
if (RR->topology->isRootserver(id.address())) {
if (RR->topology->isRoot(id)) {
RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision);
trusted = true;
}
@ -353,7 +353,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
bool trusted = false;
if (RR->topology->isRootserver(peer->address())) {
if (RR->topology->isRoot(peer->identity())) {
RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision);
trusted = true;
}
@ -362,10 +362,10 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
} break;
case Packet::VERB_WHOIS: {
// Right now only rootservers are allowed to send OK(WHOIS) to prevent
// poisoning attacks. Further decentralization will require some other
// kind of trust mechanism.
if (RR->topology->isRootserver(peer->address())) {
/* Right now only root servers are allowed to send OK(WHOIS) to prevent
* poisoning attacks. Further decentralization will require some other
* kind of trust mechanism. */
if (RR->topology->isRoot(peer->identity())) {
const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
if (id.locallyValidate())
RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR->identity,id))));

View File

@ -216,7 +216,7 @@ void Multicaster::send(
if ((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY) {
gs.lastExplicitGather = now;
SharedPtr<Peer> sn(RR->topology->getBestRootserver());
SharedPtr<Peer> sn(RR->topology->getBestRoot());
if (sn) {
TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
@ -269,51 +269,6 @@ void Multicaster::send(
// Free allocated memory buffer if any
if (indexes != idxbuf)
delete [] indexes;
#ifdef ZT_SUPPORT_LEGACY_MULTICAST
// This sends a P5 multicast up to our rootserver, who then
// redistributes it manually down to all <1.0.0 peers for
// legacy support. These peers don't support the new multicast
// frame type, so even if they receive it they will ignore it.
{
SharedPtr<Peer> sn(RR->topology->getBestRootserver());
if (sn) {
uint32_t rn = RR->prng->next32();
Packet outp(sn->address(),RR->identity.address(),Packet::VERB_P5_MULTICAST_FRAME);
outp.append((uint16_t)0xffff); // do not forward
outp.append((unsigned char)0,320 + 1024); // empty queue and bloom filter
outp.append((unsigned char)((com) ? ZT_PROTO_VERB_P5_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE : 0));
outp.append((uint64_t)nwid);
outp.append((uint16_t)0);
outp.append((unsigned char)0);
outp.append((unsigned char)0);
RR->identity.address().appendTo(outp);
outp.append((const void *)&rn,3); // random multicast ID
if (src)
src.appendTo(outp);
else MAC(RR->identity.address(),nwid).appendTo(outp);
mg.mac().appendTo(outp);
outp.append((uint32_t)mg.adi());
outp.append((uint16_t)etherType);
outp.append((uint16_t)len);
outp.append(data,len);
unsigned int signedPortionLen = outp.size() - ZT_PROTO_VERB_P5_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION;
C25519::Signature sig(RR->identity.sign(outp.field(ZT_PROTO_VERB_P5_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION,signedPortionLen),signedPortionLen));
outp.append((uint16_t)sig.size());
outp.append(sig.data,(unsigned int)sig.size());
if (com) com->serialize(outp);
outp.compress();
outp.armor(sn->key(),true);
sn->send(RR,outp.data(),outp.size(),now);
}
}
#endif // ZT_SUPPORT_LEGACY_MULTICAST
}
void Multicaster::clean(uint64_t now)
@ -372,9 +327,9 @@ void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,Multi
//TRACE("..MC %s joined multicast group %.16llx/%s via %s",member.toString().c_str(),nwid,mg.toString().c_str(),((learnedFrom) ? learnedFrom.toString().c_str() : "(direct)"));
for(std::list<OutboundMulticast>::iterator tx(gs.txQueue.begin());tx!=gs.txQueue.end();) {
if (tx->atLimit()) {
if (tx->atLimit())
gs.txQueue.erase(tx++);
} else {
else {
tx->sendIfNew(RR,member);
if (tx->atLimit())
gs.txQueue.erase(tx++);

View File

@ -286,18 +286,28 @@ void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool
return;
}
SharedPtr<Peer> signer(RR->topology->getPeer(cert.signedBy()));
if (cert.signedBy() == RR->identity.address()) {
// We are the controller: RR->identity.address() == controller() == cert.signedBy()
// So, verify that we signed th cert ourself
if (!cert.verify(RR->identity)) {
TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str());
return;
}
} else {
if (!signer) {
// This would be rather odd, since this is our controller... could happen
// if we get packets before we've gotten config.
RR->sw->requestWhois(cert.signedBy());
return;
}
SharedPtr<Peer> signer(RR->topology->getPeer(cert.signedBy()));
if (!cert.verify(signer->identity())) {
TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str());
return;
if (!signer) {
// This would be rather odd, since this is our controller... could happen
// if we get packets before we've gotten config.
RR->sw->requestWhois(cert.signedBy());
return;
}
if (!cert.verify(signer->identity())) {
TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str());
return;
}
}
}
@ -357,20 +367,6 @@ void Network::clean()
}
}
bool Network::updateAndCheckMulticastBalance(const MulticastGroup &mg,unsigned int bytes)
{
const uint64_t now = RR->node->now();
Mutex::Lock _l(_lock);
if (!_config)
return false;
std::map< MulticastGroup,BandwidthAccount >::iterator bal(_multicastRateAccounts.find(mg));
if (bal == _multicastRateAccounts.end()) {
NetworkConfig::MulticastRate r(_config->multicastRate(mg));
bal = _multicastRateAccounts.insert(std::pair< MulticastGroup,BandwidthAccount >(mg,BandwidthAccount(r.preload,r.maxBalance,r.accrual,now))).first;
}
return bal->second.deduct(bytes,now);
}
void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
{
Mutex::Lock _l(_lock);
@ -518,13 +514,13 @@ public:
RR(renv),
_now(renv->node->now()),
_network(nw),
_rootserverAddresses(renv->topology->rootserverAddresses()),
_rootAddresses(renv->topology->rootAddresses()),
_allMulticastGroups(nw->_allMulticastGroups())
{}
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
if ( ( (p->hasActiveDirectPath(_now)) && (_network->_isAllowed(p->address())) ) || (std::find(_rootserverAddresses.begin(),_rootserverAddresses.end(),p->address()) != _rootserverAddresses.end()) ) {
if ( ( (p->hasActiveDirectPath(_now)) && (_network->_isAllowed(p->address())) ) || (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ) {
Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
for(std::vector<MulticastGroup>::iterator mg(_allMulticastGroups.begin());mg!=_allMulticastGroups.end();++mg) {
@ -551,7 +547,7 @@ private:
const RuntimeEnvironment *RR;
uint64_t _now;
Network *_network;
std::vector<Address> _rootserverAddresses;
std::vector<Address> _rootAddresses;
std::vector<MulticastGroup> _allMulticastGroups;
};

View File

@ -47,7 +47,6 @@
#include "MulticastGroup.hpp"
#include "MAC.hpp"
#include "Dictionary.hpp"
#include "BandwidthAccount.hpp"
#include "Multicaster.hpp"
#include "NetworkConfig.hpp"
#include "CertificateOfMembership.hpp"
@ -237,15 +236,6 @@ public:
_externalConfig(ec);
}
/**
* Update and check multicast rate balance for a multicast group
*
* @param mg Multicast group
* @param bytes Size of packet
* @return True if packet is within budget
*/
bool updateAndCheckMulticastBalance(const MulticastGroup &mg,unsigned int bytes);
/**
* Get current network config or throw exception
*
@ -370,7 +360,6 @@ private:
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to including those behind us (updated periodically)
std::map< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups bridged to us and when we last saw activity on each
std::map< MulticastGroup,BandwidthAccount > _multicastRateAccounts;
std::map<MAC,Address> _remoteBridgeRoutes; // remote addresses where given MACs are reachable

View File

@ -32,10 +32,6 @@
namespace ZeroTier {
// This is fast enough for things like Apple's mDNS spam, so it should serve
// as a good default for your average network.
const NetworkConfig::MulticastRate NetworkConfig::DEFAULT_MULTICAST_RATE(40000,60000,80);
SharedPtr<NetworkConfig> NetworkConfig::createTestNetworkConfig(const Address &self)
{
SharedPtr<NetworkConfig> nc(new NetworkConfig());
@ -85,18 +81,6 @@ std::vector<unsigned int> NetworkConfig::allowedEtherTypes() const
return ets;
}
const NetworkConfig::MulticastRate &NetworkConfig::multicastRate(const MulticastGroup &mg) const
throw()
{
std::map<MulticastGroup,MulticastRate>::const_iterator r(_multicastRates.find(mg));
if (r == _multicastRates.end()) {
r = _multicastRates.find(MulticastGroup()); // zero MG signifies network's default rate
if (r == _multicastRates.end())
return DEFAULT_MULTICAST_RATE; // neither specific nor default found in network config
}
return r->second;
}
void NetworkConfig::_fromDictionary(const Dictionary &d)
{
static const std::string zero("0");
@ -181,13 +165,6 @@ void NetworkConfig::_fromDictionary(const Dictionary &d)
std::sort(_activeBridges.begin(),_activeBridges.end());
std::unique(_activeBridges.begin(),_activeBridges.end());
Dictionary multicastRateEntries(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES,std::string()));
for(Dictionary::const_iterator i(multicastRateEntries.begin());i!=multicastRateEntries.end();++i) {
std::vector<std::string> params(Utils::split(i->second.c_str(),",","",""));
if (params.size() >= 3)
_multicastRates[MulticastGroup(i->first)] = MulticastRate(Utils::hexStrToUInt(params[0].c_str()),Utils::hexStrToUInt(params[1].c_str()),Utils::hexStrToUInt(params[2].c_str()));
}
std::vector<std::string> relaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS,"").c_str(),",","",""));
for(std::vector<std::string>::const_iterator r(relaysSplit.begin());r!=relaysSplit.end();++r) {
std::size_t semi(r->find(';')); // address;ip/port,...
@ -221,14 +198,6 @@ bool NetworkConfig::operator==(const NetworkConfig &nc) const
if (_gateways != nc._gateways) return false;
if (_activeBridges != nc._activeBridges) return false;
if (_relays != nc._relays) return false;
if (_multicastRates.size() == nc._multicastRates.size()) {
// uclibc++ doesn't seem to implement map<> != map<> correctly, so do
// it ourselves. Note that this depends on the maps being sorted.
for(std::map<MulticastGroup,MulticastRate>::const_iterator a(_multicastRates.begin()),b(nc._multicastRates.begin());a!=_multicastRates.end();++a,++b) {
if ((a->first != b->first)||(a->second != b->second))
return false;
}
} else return false;
if (_com != nc._com) return false;
return true;
}

View File

@ -68,9 +68,6 @@ namespace ZeroTier {
// integer(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml"
// dictionary of one or more of: MAC/ADI=preload,maxbalance,accrual
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES "mr"
// 0/1
#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p"
@ -114,27 +111,6 @@ class NetworkConfig
friend class SharedPtr<NetworkConfig>;
public:
/**
* Tuple of multicast rate parameters
*/
struct MulticastRate
{
MulticastRate() throw() {}
MulticastRate(uint32_t pl,uint32_t maxb,uint32_t acc) throw() : preload(pl),maxBalance(maxb),accrual(acc) {}
uint32_t preload;
uint32_t maxBalance;
uint32_t accrual;
inline bool operator==(const MulticastRate &mr) const { return ((preload == mr.preload)&&(maxBalance == mr.maxBalance)&&(accrual == mr.accrual)); }
inline bool operator!=(const MulticastRate &mr) const { return (!(*this == mr)); }
};
/**
* A hard-coded default multicast rate for networks that don't specify
*/
static const MulticastRate DEFAULT_MULTICAST_RATE;
/**
* Create an instance of a NetworkConfig for the test network ID
*
@ -176,7 +152,6 @@ public:
inline uint64_t revision() const throw() { return _revision; }
inline const Address &issuedTo() const throw() { return _issuedTo; }
inline unsigned int multicastLimit() const throw() { return _multicastLimit; }
inline const std::map<MulticastGroup,MulticastRate> &multicastRates() const throw() { return _multicastRates; }
inline bool allowPassiveBridging() const throw() { return _allowPassiveBridging; }
inline bool isPublic() const throw() { return (!_private); }
inline bool isPrivate() const throw() { return _private; }
@ -198,13 +173,6 @@ public:
return ( (_allowPassiveBridging) || (std::find(_activeBridges.begin(),_activeBridges.end(),fromPeer) != _activeBridges.end()) );
}
/**
* @param mg Multicast group
* @return Multicast rate or DEFAULT_MULTICAST_RATE if not set
*/
const MulticastRate &multicastRate(const MulticastGroup &mg) const
throw();
bool operator==(const NetworkConfig &nc) const;
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
@ -229,7 +197,6 @@ private:
std::vector<InetAddress> _gateways;
std::vector<Address> _activeBridges;
std::vector< std::pair<Address,InetAddress> > _relays;
std::map<MulticastGroup,MulticastRate> _multicastRates;
CertificateOfMembership _com;
AtomicCounter __refCount;

View File

@ -133,9 +133,7 @@ Node::Node(
if (!rt.size())
rt.fromString(ZT_DEFAULTS.defaultRootTopology);
}
Dictionary rootservers(rt.get("rootservers",""));
rootservers.update(rt.get("supernodes",""));
RR->topology->setRootservers(rootservers);
RR->topology->setRootServers(Dictionary(rt.get("rootservers","")));
postEvent(ZT1_EVENT_UP);
}
@ -143,7 +141,7 @@ Node::Node(
Node::~Node()
{
Mutex::Lock _l(_networks_m);
_networks.clear();
_networks.clear(); // ensure that networks are destroyed before shutdown
delete RR->sa;
delete RR->topology;
delete RR->antiRec;
@ -191,7 +189,7 @@ public:
RR(renv),
_now(now),
_relays(relays),
_rootservers(RR->topology->rootserverAddresses())
_rootAddresses(RR->topology->rootAddresses())
{
}
@ -207,7 +205,7 @@ public:
}
}
if ((isRelay)||(std::find(_rootservers.begin(),_rootservers.end(),p->address()) != _rootservers.end())) {
if ((isRelay)||(std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end())) {
p->doPingAndKeepalive(RR,_now);
if (p->lastReceive() > lastReceiveFromUpstream)
lastReceiveFromUpstream = p->lastReceive();
@ -221,7 +219,7 @@ private:
const RuntimeEnvironment *RR;
uint64_t _now;
const std::vector< std::pair<Address,InetAddress> > &_relays;
std::vector<Address> _rootservers;
std::vector<Address> _rootAddresses;
};
ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
@ -238,7 +236,7 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *next
std::vector< SharedPtr<Network> > needConfig;
{
Mutex::Lock _l(_networks_m);
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
SharedPtr<NetworkConfig> nc(n->second->config2());
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!nc))
needConfig.push_back(n->second);
@ -262,7 +260,7 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *next
}
}
// Ping living or rootserver/relay peers
// Ping living or root server/relay peers
_PingPeersThatNeedPing pfunc(RR,now,networkRelays);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
@ -312,20 +310,22 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *next
ZT1_ResultCode Node::join(uint64_t nwid)
{
Mutex::Lock _l(_networks_m);
SharedPtr<Network> &nwe = _networks[nwid];
if (!nwe)
nwe = SharedPtr<Network>(new Network(RR,nwid));
SharedPtr<Network> nw = _network(nwid);
if(!nw)
_networks.push_back(std::pair< uint64_t,SharedPtr<Network> >(nwid,SharedPtr<Network>(new Network(RR,nwid))));
std::sort(_networks.begin(),_networks.end()); // will sort by nwid since it's the first in a pair<>
return ZT1_RESULT_OK;
}
ZT1_ResultCode Node::leave(uint64_t nwid)
{
std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn;
Mutex::Lock _l(_networks_m);
std::map< uint64_t,SharedPtr<Network> >::iterator nw(_networks.find(nwid));
if (nw != _networks.end()) {
nw->second->destroy();
_networks.erase(nw);
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
if (n->first != nwid)
newn.push_back(*n);
}
_networks.swap(newn);
return ZT1_RESULT_OK;
}
@ -386,7 +386,7 @@ ZT1_PeerList *Node::peers() const
p->versionRev = -1;
}
p->latency = pi->second->latency();
p->role = RR->topology->isRootserver(pi->second->address()) ? ZT1_PEER_ROLE_ROOTSERVER : ZT1_PEER_ROLE_LEAF;
p->role = RR->topology->isRoot(pi->second->identity()) ? ZT1_PEER_ROLE_ROOT : ZT1_PEER_ROLE_LEAF;
std::vector<Path> paths(pi->second->paths());
Path *bestPath = pi->second->getBestPath(_now);
@ -408,10 +408,10 @@ ZT1_PeerList *Node::peers() const
ZT1_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.find(nwid));
if (nw != _networks.end()) {
SharedPtr<Network> nw = _network(nwid);
if(nw) {
ZT1_VirtualNetworkConfig *nc = (ZT1_VirtualNetworkConfig *)::malloc(sizeof(ZT1_VirtualNetworkConfig));
nw->second->externalConfig(nc);
nw->externalConfig(nc);
return nc;
}
return (ZT1_VirtualNetworkConfig *)0;
@ -428,7 +428,7 @@ ZT1_VirtualNetworkList *Node::networks() const
nl->networks = (ZT1_VirtualNetworkConfig *)(buf + sizeof(ZT1_VirtualNetworkList));
nl->networkCount = 0;
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n)
n->second->externalConfig(&(nl->networks[nl->networkCount++]));
return nl;

View File

@ -155,19 +155,19 @@ public:
len);
}
inline SharedPtr<Network> network(uint64_t nwid)
inline SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
std::map< uint64_t,SharedPtr<Network> >::iterator nw(_networks.find(nwid));
return ((nw == _networks.end()) ? SharedPtr<Network>() : nw->second);
return _network(nwid);
}
inline std::vector< SharedPtr<Network> > allNetworks() const
{
Mutex::Lock _l(_networks_m);
std::vector< SharedPtr<Network> > nw;
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
nw.push_back(n->second);
Mutex::Lock _l(_networks_m);
nw.reserve(_networks.size());
for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i)
nw.push_back(i->second);
return nw;
}
@ -208,6 +208,16 @@ public:
#endif
private:
inline SharedPtr<Network> _network(uint64_t nwid) const
{
// assumes _networks_m is locked
for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
if (i->first == nwid)
return i->second;
}
return SharedPtr<Network>();
}
RuntimeEnvironment _RR;
RuntimeEnvironment *RR;
@ -223,7 +233,7 @@ private:
//Dictionary _localConfig; // persisted as local.conf
//Mutex _localConfig_m;
std::map< uint64_t,SharedPtr<Network> > _networks;
std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
Mutex _networks_m;
Mutex _backgroundTasksLock;

View File

@ -626,7 +626,7 @@ public:
* [... additional tuples of network/address/adi ...]
*
* LIKEs are sent to peers with whom you have a direct peer to peer
* connection, and always including rootservers.
* connection, and always including root servers.
*
* OK/ERROR are not generated.
*/

View File

@ -122,16 +122,16 @@ void Peer::received(
/* Announce multicast groups of interest to direct peers if they are
* considered authorized members of a given network. Also announce to
* rootservers and network controllers. */
* root servers and network controllers. */
if ((pathIsConfirmed)&&((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000))) {
_lastAnnouncedTo = now;
const bool isRootserver = RR->topology->isRootserver(_id.address());
const bool isRoot = RR->topology->isRoot(_id);
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n) {
if ( (isRootserver) || ((*n)->isAllowed(_id.address())) ) {
if ( (isRoot) || ((*n)->isAllowed(_id.address())) ) {
const std::vector<MulticastGroup> mgs((*n)->allMulticastGroups());
for(std::vector<MulticastGroup>::const_iterator mg(mgs.begin());mg!=mgs.end();++mg) {
if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) {

View File

@ -118,7 +118,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi
// For all peers for whom we forgot an address, send a packet indirectly if
// they are still considered alive so that we will re-establish direct links.
SharedPtr<Peer> sn(RR->topology->getBestRootserver());
SharedPtr<Peer> sn(RR->topology->getBestRoot());
if (sn) {
Path *snp = sn->getBestPath(now);
if (snp) {

View File

@ -145,12 +145,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if (fromBridged)
network->learnBridgedMulticastGroup(mg,RR->node->now());
// Check multicast/broadcast bandwidth quotas and reject if quota exceeded
if (!network->updateAndCheckMulticastBalance(mg,len)) {
TRACE("%.16llx: didn't multicast %u bytes, quota exceeded for multicast group %s",network->id(),len,mg.toString().c_str());
return;
}
//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),len);
RR->mc->send(
@ -320,8 +314,8 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
* P2 in randomized order in terms of which gets sent first. This is done
* since in a few cases NAT-t can be sensitive to slight timing differences
* in terms of when the two peers initiate. Normally this is accounted for
* by the nearly-simultaneous RENDEZVOUS kickoff from the rootserver, but
* given that rootservers are hosted on cloud providers this can in some
* by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but
* given that relay are hosted on cloud providers this can in some
* cases have a few ms of latency between packet departures. By randomizing
* the order we make each attempted NAT-t favor one or the other going
* first, meaning if it doesn't succeed the first time it might the second
@ -565,8 +559,8 @@ void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,const void
// It wouldn't hurt anything, just redundant and unnecessary.
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) {
// Don't know peer or no direct path -- so relay via rootserver
relayTo = RR->topology->getBestRootserver();
// Don't know peer or no direct path -- so relay via root server
relayTo = RR->topology->getBestRoot();
if (relayTo)
relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now());
}
@ -641,8 +635,8 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,const void *dat
if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) {
unite(source,destination,false);
} else {
// Don't know peer or no direct path -- so relay via rootserver
relayTo = RR->topology->getBestRootserver(&source,1,true);
// Don't know peer or no direct path -- so relay via root server
relayTo = RR->topology->getBestRoot(&source,1,true);
if (relayTo)
relayTo->send(RR,packet->data(),packet->size(),RR->node->now());
}
@ -712,13 +706,13 @@ void Switch::_handleBeacon(const InetAddress &fromAddr,const Buffer<ZT_PROTO_BEA
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
{
SharedPtr<Peer> rootserver(RR->topology->getBestRootserver(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
if (rootserver) {
Packet outp(rootserver->address(),RR->identity.address(),Packet::VERB_WHOIS);
SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
if (root) {
Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
addr.appendTo(outp);
outp.armor(rootserver->key(),true);
if (rootserver->send(RR,outp.data(),outp.size(),RR->node->now()))
return rootserver->address();
outp.armor(root->key(),true);
if (root->send(RR,outp.data(),outp.size(),RR->node->now()))
return root->address();
}
return Address();
}
@ -752,7 +746,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
}
if (!relay)
relay = RR->topology->getBestRootserver();
relay = RR->topology->getBestRoot();
if (!(relay)||(!(viaPath = relay->getBestPath(now))))
return false;

View File

@ -36,7 +36,7 @@ namespace ZeroTier {
Topology::Topology(const RuntimeEnvironment *renv) :
RR(renv),
_amRootserver(false)
_amRoot(false)
{
}
@ -44,16 +44,16 @@ Topology::~Topology()
{
}
void Topology::setRootservers(const std::map< Identity,std::vector<InetAddress> > &sn)
void Topology::setRootServers(const std::map< Identity,std::vector<InetAddress> > &sn)
{
Mutex::Lock _l(_lock);
if (_rootservers == sn)
if (_roots == sn)
return; // no change
_rootservers = sn;
_rootserverAddresses.clear();
_rootserverPeers.clear();
_roots = sn;
_rootAddresses.clear();
_rootPeers.clear();
const uint64_t now = RR->node->now();
for(std::map< Identity,std::vector<InetAddress> >::const_iterator i(sn.begin());i!=sn.end();++i) {
@ -64,17 +64,17 @@ void Topology::setRootservers(const std::map< Identity,std::vector<InetAddress>
for(std::vector<InetAddress>::const_iterator j(i->second.begin());j!=i->second.end();++j)
p->addPath(Path(*j,true));
p->use(now);
_rootserverPeers.push_back(p);
_rootPeers.push_back(p);
}
_rootserverAddresses.push_back(i->first.address());
_rootAddresses.push_back(i->first.address());
}
std::sort(_rootserverAddresses.begin(),_rootserverAddresses.end());
std::sort(_rootAddresses.begin(),_rootAddresses.end());
_amRootserver = (_rootservers.find(RR->identity) != _rootservers.end());
_amRoot = (_roots.find(RR->identity) != _roots.end());
}
void Topology::setRootservers(const Dictionary &sn)
void Topology::setRootServers(const Dictionary &sn)
{
std::map< Identity,std::vector<InetAddress> > m;
for(Dictionary::const_iterator d(sn.begin());d!=sn.end();++d) {
@ -86,11 +86,11 @@ void Topology::setRootservers(const Dictionary &sn)
if (udp.length() > 0)
a.push_back(InetAddress(udp));
} catch ( ... ) {
TRACE("rootserver list contained invalid entry for: %s",d->first.c_str());
TRACE("root server list contained invalid entry for: %s",d->first.c_str());
}
}
}
this->setRootservers(m);
this->setRootServers(m);
}
SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
@ -141,28 +141,28 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
return SharedPtr<Peer>();
}
SharedPtr<Peer> Topology::getBestRootserver(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
{
SharedPtr<Peer> bestRootserver;
SharedPtr<Peer> bestRoot;
const uint64_t now = RR->node->now();
Mutex::Lock _l(_lock);
if (_amRootserver) {
/* If I am a rootserver, the "best" rootserver is the one whose address
if (_amRoot) {
/* If I am a root server, the "best" root server is the one whose address
* is numerically greater than mine (with wrap at top of list). This
* causes packets searching for a route to pretty much literally
* circumnavigate the globe rather than bouncing between just two. */
if (_rootserverAddresses.size() > 1) { // gotta be one other than me for this to work
std::vector<Address>::const_iterator sna(std::find(_rootserverAddresses.begin(),_rootserverAddresses.end(),RR->identity.address()));
if (sna != _rootserverAddresses.end()) { // sanity check -- _amRootserver should've been false in this case
if (_rootAddresses.size() > 1) { // gotta be one other than me for this to work
std::vector<Address>::const_iterator sna(std::find(_rootAddresses.begin(),_rootAddresses.end(),RR->identity.address()));
if (sna != _rootAddresses.end()) { // sanity check -- _amRoot should've been false in this case
for(;;) {
if (++sna == _rootserverAddresses.end())
sna = _rootserverAddresses.begin(); // wrap around at end
if (++sna == _rootAddresses.end())
sna = _rootAddresses.begin(); // wrap around at end
if (*sna != RR->identity.address()) { // pick one other than us -- starting from me+1 in sorted set order
std::map< Address,SharedPtr<Peer> >::const_iterator p(_activePeers.find(*sna));
if ((p != _activePeers.end())&&(p->second->hasActiveDirectPath(now))) {
bestRootserver = p->second;
bestRoot = p->second;
break;
}
}
@ -170,80 +170,87 @@ SharedPtr<Peer> Topology::getBestRootserver(const Address *avoid,unsigned int av
}
}
} else {
/* If I am not a rootserver, the best rootserver is the active one with
/* If I am not a root server, the best root server is the active one with
* the lowest latency. */
unsigned int l,bestRootserverLatency = 65536;
unsigned int l,bestLatency = 65536;
uint64_t lds,ldr;
// First look for a best rootserver by comparing latencies, but exclude
// rootservers that have not responded to direct messages in order to
// First look for a best root by comparing latencies, but exclude
// root servers that have not responded to direct messages in order to
// try to exclude any that are dead or unreachable.
for(std::vector< SharedPtr<Peer> >::const_iterator sn(_rootserverPeers.begin());sn!=_rootserverPeers.end();) {
for(std::vector< SharedPtr<Peer> >::const_iterator sn(_rootPeers.begin());sn!=_rootPeers.end();) {
// Skip explicitly avoided relays
for(unsigned int i=0;i<avoidCount;++i) {
if (avoid[i] == (*sn)->address())
goto keep_searching_for_rootservers;
goto keep_searching_for_roots;
}
// Skip possibly comatose or unreachable relays
lds = (*sn)->lastDirectSend();
ldr = (*sn)->lastDirectReceive();
if ((lds)&&(lds > ldr)&&((lds - ldr) > ZT_PEER_RELAY_CONVERSATION_LATENCY_THRESHOLD))
goto keep_searching_for_rootservers;
goto keep_searching_for_roots;
if ((*sn)->hasActiveDirectPath(now)) {
l = (*sn)->latency();
if (bestRootserver) {
if ((l)&&(l < bestRootserverLatency)) {
bestRootserverLatency = l;
bestRootserver = *sn;
if (bestRoot) {
if ((l)&&(l < bestLatency)) {
bestLatency = l;
bestRoot = *sn;
}
} else {
if (l)
bestRootserverLatency = l;
bestRootserver = *sn;
bestLatency = l;
bestRoot = *sn;
}
}
keep_searching_for_rootservers:
keep_searching_for_roots:
++sn;
}
if (bestRootserver) {
bestRootserver->use(now);
return bestRootserver;
if (bestRoot) {
bestRoot->use(now);
return bestRoot;
} else if (strictAvoid)
return SharedPtr<Peer>();
// If we have nothing from above, just pick one without avoidance criteria.
for(std::vector< SharedPtr<Peer> >::const_iterator sn=_rootserverPeers.begin();sn!=_rootserverPeers.end();++sn) {
for(std::vector< SharedPtr<Peer> >::const_iterator sn=_rootPeers.begin();sn!=_rootPeers.end();++sn) {
if ((*sn)->hasActiveDirectPath(now)) {
unsigned int l = (*sn)->latency();
if (bestRootserver) {
if ((l)&&(l < bestRootserverLatency)) {
bestRootserverLatency = l;
bestRootserver = *sn;
if (bestRoot) {
if ((l)&&(l < bestLatency)) {
bestLatency = l;
bestRoot = *sn;
}
} else {
if (l)
bestRootserverLatency = l;
bestRootserver = *sn;
bestLatency = l;
bestRoot = *sn;
}
}
}
}
if (bestRootserver)
bestRootserver->use(now);
return bestRootserver;
if (bestRoot)
bestRoot->use(now);
return bestRoot;
}
bool Topology::isRoot(const Identity &id) const
throw()
{
Mutex::Lock _l(_lock);
return (_roots.count(id) != 0);
}
void Topology::clean(uint64_t now)
{
Mutex::Lock _l(_lock);
for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();) {
if (((now - p->second->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootserverAddresses.begin(),_rootserverAddresses.end(),p->first) == _rootserverAddresses.end())) {
if (((now - p->second->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),p->first) == _rootAddresses.end())) {
_activePeers.erase(p++);
} else ++p;
}

View File

@ -59,21 +59,19 @@ public:
~Topology();
/**
* Set up rootservers for this network
*
* @param sn Rootservers for this network
* @param sn Root server identities and addresses
*/
void setRootservers(const std::map< Identity,std::vector<InetAddress> > &sn);
void setRootServers(const std::map< Identity,std::vector<InetAddress> > &sn);
/**
* Set up rootservers for this network
* Set up root servers for this network
*
* This performs no signature verification of any kind. The caller must
* check the signature of the root topology dictionary first.
*
* @param sn Rootservers dictionary from root-topology
* @param sn 'rootservers' key from root-topology Dictionary (deserialized as Dictionary)
*/
void setRootservers(const Dictionary &sn);
void setRootServers(const Dictionary &sn);
/**
* Add a peer to database
@ -95,65 +93,52 @@ public:
SharedPtr<Peer> getPeer(const Address &zta);
/**
* @return Vector of peers that are rootservers
* @return Vector of peers that are root servers
*/
inline std::vector< SharedPtr<Peer> > rootserverPeers() const
inline std::vector< SharedPtr<Peer> > rootPeers() const
{
Mutex::Lock _l(_lock);
return _rootserverPeers;
return _rootPeers;
}
/**
* @return Number of rootservers
*/
inline unsigned int numRootservers() const
{
Mutex::Lock _l(_lock);
return (unsigned int)_rootserverPeers.size();
}
/**
* Get the current favorite rootserver
* Get the current favorite root server
*
* @return Rootserver with lowest latency or NULL if none
* @return Root server with lowest latency or NULL if none
*/
inline SharedPtr<Peer> getBestRootserver()
inline SharedPtr<Peer> getBestRoot()
{
return getBestRootserver((const Address *)0,0,false);
return getBestRoot((const Address *)0,0,false);
}
/**
* Get the best rootserver, avoiding rootservers listed in an array
* Get the best root server, avoiding root servers listed in an array
*
* This will get the best rootserver (lowest latency, etc.) but will
* try to avoid the listed rootservers, only using them if no others
* This will get the best root server (lowest latency, etc.) but will
* try to avoid the listed root servers, only using them if no others
* are available.
*
* @param avoid Nodes to avoid
* @param avoidCount Number of nodes to avoid
* @param strictAvoid If false, consider avoided rootservers anyway if no non-avoid rootservers are available
* @return Rootserver or NULL if none
* @param strictAvoid If false, consider avoided root servers anyway if no non-avoid root servers are available
* @return Root server or NULL if none available
*/
SharedPtr<Peer> getBestRootserver(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
SharedPtr<Peer> getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
/**
* @param zta ZeroTier address
* @return True if this is a designated rootserver
* @param id Identity to check
* @return True if this is a designated root server
*/
inline bool isRootserver(const Address &zta) const
throw()
{
Mutex::Lock _l(_lock);
return (std::find(_rootserverAddresses.begin(),_rootserverAddresses.end(),zta) != _rootserverAddresses.end());
}
bool isRoot(const Identity &id) const
throw();
/**
* @return Vector of rootserver addresses
* @return Vector of root server addresses
*/
inline std::vector<Address> rootserverAddresses() const
inline std::vector<Address> rootAddresses() const
{
Mutex::Lock _l(_lock);
return _rootserverAddresses;
return _rootAddresses;
}
/**
@ -206,13 +191,13 @@ private:
const RuntimeEnvironment *RR;
std::map< Address,SharedPtr<Peer> > _activePeers;
std::map< Identity,std::vector<InetAddress> > _rootservers;
std::vector< Address > _rootserverAddresses;
std::vector< SharedPtr<Peer> > _rootserverPeers;
std::map< Identity,std::vector<InetAddress> > _roots;
std::vector< Address > _rootAddresses;
std::vector< SharedPtr<Peer> > _rootPeers;
Mutex _lock;
bool _amRootserver;
bool _amRoot;
};
} // namespace ZeroTier

View File

@ -1,98 +1,90 @@
static unsigned char ZT_DEFAULT_ROOT_TOPOLOGY[] = {
0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x3d, 0x37, 0x65, 0x31, 0x39, 0x38,
0x37, 0x36, 0x61, 0x62, 0x61, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x37, 0x65, 0x31,
0x39, 0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x3a, 0x30, 0x3a, 0x32, 0x61, 0x36, 0x65, 0x32, 0x62,
0x32, 0x33, 0x31, 0x38, 0x39, 0x33, 0x30, 0x66, 0x36, 0x30, 0x65, 0x62, 0x30, 0x39, 0x37, 0x66,
0x37, 0x30, 0x64, 0x30, 0x66, 0x34, 0x62, 0x30, 0x32, 0x38, 0x62, 0x32, 0x63, 0x64, 0x36, 0x64,
0x33, 0x64, 0x30, 0x63, 0x36, 0x33, 0x63, 0x30, 0x31, 0x34, 0x62, 0x39, 0x30, 0x33, 0x39, 0x66,
0x66, 0x33, 0x35, 0x33, 0x39, 0x30, 0x65, 0x34, 0x31, 0x31, 0x38, 0x31, 0x66, 0x32, 0x31, 0x36,
0x66, 0x62, 0x32, 0x65, 0x36, 0x66, 0x61, 0x38, 0x64, 0x39, 0x35, 0x63, 0x31, 0x65, 0x65, 0x39,
0x36, 0x36, 0x37, 0x31, 0x35, 0x36, 0x34, 0x31, 0x31, 0x39, 0x30, 0x35, 0x63, 0x33, 0x64, 0x63,
0x63, 0x66, 0x65, 0x61, 0x37, 0x38, 0x64, 0x38, 0x63, 0x36, 0x64, 0x66, 0x61, 0x66, 0x62, 0x61,
0x36, 0x38, 0x38, 0x31, 0x37, 0x30, 0x62, 0x33, 0x66, 0x61, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70,
0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, 0x32,
0x32, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c,
0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, 0x32, 0x32, 0x30, 0x2f,
0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, 0x61,
0x6e, 0x20, 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2c, 0x20, 0x43, 0x61, 0x6c,
0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x2c, 0x20, 0x55, 0x53, 0x41, 0x5c, 0x5c, 0x6e, 0x64,
0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x6e, 0x79, 0x61, 0x72, 0x6c, 0x61, 0x74, 0x68, 0x6f, 0x74,
0x65, 0x70, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x5c,
0x5c, 0x6e, 0x5c, 0x6e, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x5c, 0x3d,
0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65,
0x3a, 0x30, 0x3a, 0x62, 0x62, 0x31, 0x64, 0x33, 0x31, 0x66, 0x32, 0x63, 0x33, 0x32, 0x33, 0x65,
0x32, 0x36, 0x34, 0x65, 0x39, 0x65, 0x36, 0x34, 0x31, 0x37, 0x32, 0x63, 0x31, 0x61, 0x37, 0x34,
0x66, 0x37, 0x37, 0x38, 0x39, 0x39, 0x35, 0x35, 0x35, 0x65, 0x64, 0x31, 0x30, 0x37, 0x35, 0x31,
0x63, 0x64, 0x35, 0x36, 0x65, 0x38, 0x36, 0x34, 0x30, 0x35, 0x63, 0x64, 0x65, 0x31, 0x31, 0x38,
0x64, 0x30, 0x32, 0x64, 0x66, 0x66, 0x65, 0x35, 0x35, 0x35, 0x64, 0x34, 0x36, 0x32, 0x63, 0x63,
0x66, 0x36, 0x61, 0x38, 0x35, 0x62, 0x35, 0x36, 0x33, 0x31, 0x63, 0x31, 0x32, 0x33, 0x35, 0x30,
0x63, 0x38, 0x64, 0x35, 0x64, 0x63, 0x34, 0x30, 0x39, 0x62, 0x61, 0x31, 0x30, 0x62, 0x39, 0x30,
0x32, 0x35, 0x64, 0x30, 0x66, 0x34, 0x34, 0x35, 0x63, 0x66, 0x34, 0x34, 0x39, 0x64, 0x39, 0x32,
0x62, 0x31, 0x63, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37,
0x2e, 0x31, 0x39, 0x31, 0x2e, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33,
0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31, 0x39,
0x31, 0x2e, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64,
0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x50, 0x61, 0x72, 0x69, 0x73, 0x2c, 0x20, 0x46, 0x72,
0x61, 0x6e, 0x63, 0x65, 0x5c, 0x5c, 0x6e, 0x64, 0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x73, 0x68,
0x6f, 0x67, 0x67, 0x6f, 0x74, 0x68, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e,
0x63, 0x6f, 0x6d, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39, 0x66,
0x65, 0x33, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35,
0x39, 0x66, 0x65, 0x33, 0x3a, 0x30, 0x3a, 0x34, 0x38, 0x32, 0x66, 0x36, 0x65, 0x65, 0x35, 0x64,
0x66, 0x65, 0x39, 0x30, 0x32, 0x33, 0x31, 0x39, 0x62, 0x34, 0x31, 0x39, 0x64, 0x65, 0x35, 0x62,
0x64, 0x63, 0x37, 0x36, 0x35, 0x32, 0x30, 0x39, 0x63, 0x30, 0x65, 0x63, 0x64, 0x61, 0x33, 0x38,
0x63, 0x34, 0x64, 0x36, 0x65, 0x34, 0x66, 0x63, 0x66, 0x30, 0x64, 0x33, 0x33, 0x36, 0x35, 0x38,
0x33, 0x39, 0x38, 0x62, 0x34, 0x35, 0x32, 0x37, 0x64, 0x63, 0x64, 0x32, 0x32, 0x66, 0x39, 0x33,
0x31, 0x31, 0x32, 0x66, 0x62, 0x39, 0x62, 0x65, 0x66, 0x64, 0x30, 0x32, 0x66, 0x64, 0x37, 0x38,
0x62, 0x66, 0x37, 0x32, 0x36, 0x31, 0x62, 0x33, 0x33, 0x33, 0x66, 0x63, 0x31, 0x30, 0x35, 0x64,
0x31, 0x39, 0x32, 0x61, 0x36, 0x32, 0x33, 0x63, 0x61, 0x39, 0x65, 0x35, 0x30, 0x66, 0x63, 0x36,
0x30, 0x62, 0x33, 0x37, 0x34, 0x61, 0x35, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c,
0x3d, 0x31, 0x36, 0x32, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f,
0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x36,
0x32, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x34, 0x34, 0x33,
0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x4e, 0x65, 0x77, 0x20, 0x59,
0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x55,
0x53, 0x41, 0x5c, 0x5c, 0x6e, 0x64, 0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x63, 0x74, 0x68, 0x75,
0x6c, 0x68, 0x75, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d,
0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, 0x66, 0x33, 0x5c,
0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, 0x66,
0x33, 0x3a, 0x30, 0x3a, 0x30, 0x31, 0x66, 0x30, 0x39, 0x32, 0x32, 0x61, 0x39, 0x38, 0x65, 0x33,
0x62, 0x33, 0x34, 0x65, 0x62, 0x63, 0x62, 0x66, 0x66, 0x33, 0x33, 0x33, 0x32, 0x36, 0x39, 0x64,
0x63, 0x32, 0x36, 0x35, 0x64, 0x37, 0x61, 0x30, 0x32, 0x30, 0x61, 0x61, 0x62, 0x36, 0x39, 0x64,
0x37, 0x32, 0x62, 0x65, 0x34, 0x64, 0x34, 0x61, 0x63, 0x63, 0x39, 0x63, 0x38, 0x63, 0x39, 0x32,
0x39, 0x34, 0x37, 0x38, 0x35, 0x37, 0x37, 0x31, 0x32, 0x35, 0x36, 0x63, 0x64, 0x31, 0x64, 0x39,
0x34, 0x32, 0x61, 0x39, 0x30, 0x64, 0x31, 0x62, 0x64, 0x31, 0x64, 0x32, 0x64, 0x63, 0x61, 0x33,
0x65, 0x61, 0x38, 0x34, 0x65, 0x66, 0x37, 0x64, 0x38, 0x35, 0x61, 0x66, 0x65, 0x36, 0x36, 0x31,
0x31, 0x66, 0x62, 0x34, 0x33, 0x66, 0x66, 0x30, 0x62, 0x37, 0x34, 0x31, 0x32, 0x36, 0x64, 0x39,
0x30, 0x61, 0x36, 0x65, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x32,
0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x39, 0x39,
0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x32, 0x38, 0x2e,
0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x34, 0x34, 0x33, 0x5c,
0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, 0x69, 0x6e, 0x67, 0x61, 0x70,
0x6f, 0x72, 0x65, 0x5c, 0x5c, 0x6e, 0x64, 0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x6d, 0x69, 0x2d,
0x67, 0x6f, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x5c,
0x5c, 0x6e, 0x5c, 0x6e, 0x0a, 0x7e, 0x21, 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x3d, 0x62,
0x37, 0x34, 0x39, 0x33, 0x66, 0x35, 0x61, 0x34, 0x62, 0x37, 0x39, 0x61, 0x31, 0x64, 0x63, 0x63,
0x34, 0x32, 0x33, 0x66, 0x64, 0x32, 0x35, 0x64, 0x32, 0x64, 0x38, 0x61, 0x61, 0x38, 0x64, 0x36,
0x32, 0x39, 0x33, 0x63, 0x34, 0x39, 0x30, 0x61, 0x31, 0x32, 0x63, 0x65, 0x62, 0x36, 0x33, 0x39,
0x35, 0x34, 0x31, 0x37, 0x64, 0x64, 0x35, 0x38, 0x36, 0x38, 0x63, 0x31, 0x37, 0x62, 0x66, 0x62,
0x63, 0x65, 0x65, 0x36, 0x38, 0x35, 0x64, 0x65, 0x35, 0x38, 0x30, 0x31, 0x39, 0x64, 0x32, 0x31,
0x66, 0x39, 0x32, 0x35, 0x37, 0x36, 0x61, 0x37, 0x38, 0x61, 0x34, 0x35, 0x32, 0x33, 0x35, 0x64,
0x33, 0x34, 0x32, 0x65, 0x66, 0x61, 0x32, 0x61, 0x30, 0x30, 0x61, 0x35, 0x34, 0x34, 0x64, 0x65,
0x64, 0x33, 0x34, 0x37, 0x36, 0x36, 0x64, 0x64, 0x33, 0x32, 0x64, 0x36, 0x66, 0x30, 0x65, 0x31,
0x31, 0x38, 0x30, 0x39, 0x31, 0x39, 0x37, 0x66, 0x39, 0x62, 0x61, 0x65, 0x65, 0x64, 0x66, 0x34,
0x63, 0x36, 0x61, 0x30, 0x65, 0x38, 0x64, 0x32, 0x64, 0x36, 0x35, 0x37, 0x64, 0x32, 0x38, 0x30,
0x61, 0x35, 0x37, 0x39, 0x66, 0x32, 0x66, 0x32, 0x34, 0x37, 0x38, 0x62, 0x32, 0x66, 0x37, 0x63,
0x37, 0x61, 0x30, 0x38, 0x30, 0x38, 0x39, 0x61, 0x35, 0x30, 0x31, 0x36, 0x62, 0x35, 0x35, 0x0a,
0x7e, 0x21, 0x73, 0x69, 0x67, 0x69, 0x64, 0x3d, 0x37, 0x37, 0x37, 0x39, 0x32, 0x62, 0x31, 0x63,
0x30, 0x32, 0x3a, 0x30, 0x3a, 0x62, 0x35, 0x63, 0x33, 0x36, 0x31, 0x65, 0x38, 0x65, 0x39, 0x63,
0x32, 0x31, 0x35, 0x34, 0x65, 0x38, 0x32, 0x63, 0x33, 0x65, 0x39, 0x30, 0x32, 0x66, 0x64, 0x66,
0x63, 0x33, 0x33, 0x37, 0x34, 0x36, 0x38, 0x62, 0x30, 0x39, 0x32, 0x61, 0x37, 0x63, 0x34, 0x64,
0x38, 0x64, 0x63, 0x36, 0x38, 0x35, 0x63, 0x33, 0x37, 0x65, 0x62, 0x31, 0x30, 0x65, 0x65, 0x34,
0x66, 0x33, 0x63, 0x31, 0x37, 0x63, 0x63, 0x30, 0x62, 0x62, 0x31, 0x64, 0x30, 0x32, 0x34, 0x31,
0x36, 0x37, 0x65, 0x38, 0x63, 0x62, 0x30, 0x38, 0x32, 0x34, 0x64, 0x31, 0x32, 0x32, 0x36, 0x33,
0x34, 0x32, 0x38, 0x33, 0x37, 0x33, 0x35, 0x38, 0x32, 0x64, 0x61, 0x33, 0x64, 0x30, 0x61, 0x39,
0x61, 0x31, 0x34, 0x62, 0x33, 0x36, 0x65, 0x34, 0x35, 0x34, 0x36, 0x63, 0x33, 0x31, 0x37, 0x65,
0x38, 0x31, 0x31, 0x65, 0x36, 0x0a, 0x7e, 0x21, 0x73, 0x69, 0x67, 0x74, 0x73, 0x3d, 0x31, 0x34,
0x61, 0x65, 0x34, 0x32, 0x64, 0x30, 0x33, 0x31, 0x34, 0x0a
0x72, 0x6f, 0x6f, 0x74, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3d, 0x37, 0x65, 0x31, 0x39,
0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x37, 0x65,
0x31, 0x39, 0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x3a, 0x30, 0x3a, 0x32, 0x61, 0x36, 0x65, 0x32,
0x62, 0x32, 0x33, 0x31, 0x38, 0x39, 0x33, 0x30, 0x66, 0x36, 0x30, 0x65, 0x62, 0x30, 0x39, 0x37,
0x66, 0x37, 0x30, 0x64, 0x30, 0x66, 0x34, 0x62, 0x30, 0x32, 0x38, 0x62, 0x32, 0x63, 0x64, 0x36,
0x64, 0x33, 0x64, 0x30, 0x63, 0x36, 0x33, 0x63, 0x30, 0x31, 0x34, 0x62, 0x39, 0x30, 0x33, 0x39,
0x66, 0x66, 0x33, 0x35, 0x33, 0x39, 0x30, 0x65, 0x34, 0x31, 0x31, 0x38, 0x31, 0x66, 0x32, 0x31,
0x36, 0x66, 0x62, 0x32, 0x65, 0x36, 0x66, 0x61, 0x38, 0x64, 0x39, 0x35, 0x63, 0x31, 0x65, 0x65,
0x39, 0x36, 0x36, 0x37, 0x31, 0x35, 0x36, 0x34, 0x31, 0x31, 0x39, 0x30, 0x35, 0x63, 0x33, 0x64,
0x63, 0x63, 0x66, 0x65, 0x61, 0x37, 0x38, 0x64, 0x38, 0x63, 0x36, 0x64, 0x66, 0x61, 0x66, 0x62,
0x61, 0x36, 0x38, 0x38, 0x31, 0x37, 0x30, 0x62, 0x33, 0x66, 0x61, 0x5c, 0x5c, 0x6e, 0x75, 0x64,
0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e,
0x32, 0x32, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c,
0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, 0x32, 0x32, 0x30,
0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53,
0x61, 0x6e, 0x20, 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2c, 0x20, 0x43, 0x61,
0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x2c, 0x20, 0x55, 0x53, 0x41, 0x5c, 0x5c, 0x6e,
0x5c, 0x6e, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x5c, 0x3d, 0x69, 0x64,
0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x3a, 0x30,
0x3a, 0x62, 0x62, 0x31, 0x64, 0x33, 0x31, 0x66, 0x32, 0x63, 0x33, 0x32, 0x33, 0x65, 0x32, 0x36,
0x34, 0x65, 0x39, 0x65, 0x36, 0x34, 0x31, 0x37, 0x32, 0x63, 0x31, 0x61, 0x37, 0x34, 0x66, 0x37,
0x37, 0x38, 0x39, 0x39, 0x35, 0x35, 0x35, 0x65, 0x64, 0x31, 0x30, 0x37, 0x35, 0x31, 0x63, 0x64,
0x35, 0x36, 0x65, 0x38, 0x36, 0x34, 0x30, 0x35, 0x63, 0x64, 0x65, 0x31, 0x31, 0x38, 0x64, 0x30,
0x32, 0x64, 0x66, 0x66, 0x65, 0x35, 0x35, 0x35, 0x64, 0x34, 0x36, 0x32, 0x63, 0x63, 0x66, 0x36,
0x61, 0x38, 0x35, 0x62, 0x35, 0x36, 0x33, 0x31, 0x63, 0x31, 0x32, 0x33, 0x35, 0x30, 0x63, 0x38,
0x64, 0x35, 0x64, 0x63, 0x34, 0x30, 0x39, 0x62, 0x61, 0x31, 0x30, 0x62, 0x39, 0x30, 0x32, 0x35,
0x64, 0x30, 0x66, 0x34, 0x34, 0x35, 0x63, 0x66, 0x34, 0x34, 0x39, 0x64, 0x39, 0x32, 0x62, 0x31,
0x63, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31,
0x39, 0x31, 0x2e, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c,
0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31, 0x39, 0x31, 0x2e,
0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73,
0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x50, 0x61, 0x72, 0x69, 0x73, 0x2c, 0x20, 0x46, 0x72, 0x61, 0x6e,
0x63, 0x65, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39, 0x66, 0x65,
0x33, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39,
0x66, 0x65, 0x33, 0x3a, 0x30, 0x3a, 0x34, 0x38, 0x32, 0x66, 0x36, 0x65, 0x65, 0x35, 0x64, 0x66,
0x65, 0x39, 0x30, 0x32, 0x33, 0x31, 0x39, 0x62, 0x34, 0x31, 0x39, 0x64, 0x65, 0x35, 0x62, 0x64,
0x63, 0x37, 0x36, 0x35, 0x32, 0x30, 0x39, 0x63, 0x30, 0x65, 0x63, 0x64, 0x61, 0x33, 0x38, 0x63,
0x34, 0x64, 0x36, 0x65, 0x34, 0x66, 0x63, 0x66, 0x30, 0x64, 0x33, 0x33, 0x36, 0x35, 0x38, 0x33,
0x39, 0x38, 0x62, 0x34, 0x35, 0x32, 0x37, 0x64, 0x63, 0x64, 0x32, 0x32, 0x66, 0x39, 0x33, 0x31,
0x31, 0x32, 0x66, 0x62, 0x39, 0x62, 0x65, 0x66, 0x64, 0x30, 0x32, 0x66, 0x64, 0x37, 0x38, 0x62,
0x66, 0x37, 0x32, 0x36, 0x31, 0x62, 0x33, 0x33, 0x33, 0x66, 0x63, 0x31, 0x30, 0x35, 0x64, 0x31,
0x39, 0x32, 0x61, 0x36, 0x32, 0x33, 0x63, 0x61, 0x39, 0x65, 0x35, 0x30, 0x66, 0x63, 0x36, 0x30,
0x62, 0x33, 0x37, 0x34, 0x61, 0x35, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d,
0x31, 0x36, 0x32, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x39,
0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x36, 0x32,
0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x34, 0x34, 0x33, 0x5c,
0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f,
0x72, 0x6b, 0x2c, 0x20, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x55, 0x53,
0x41, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, 0x66, 0x33,
0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39,
0x66, 0x33, 0x3a, 0x30, 0x3a, 0x30, 0x31, 0x66, 0x30, 0x39, 0x32, 0x32, 0x61, 0x39, 0x38, 0x65,
0x33, 0x62, 0x33, 0x34, 0x65, 0x62, 0x63, 0x62, 0x66, 0x66, 0x33, 0x33, 0x33, 0x32, 0x36, 0x39,
0x64, 0x63, 0x32, 0x36, 0x35, 0x64, 0x37, 0x61, 0x30, 0x32, 0x30, 0x61, 0x61, 0x62, 0x36, 0x39,
0x64, 0x37, 0x32, 0x62, 0x65, 0x34, 0x64, 0x34, 0x61, 0x63, 0x63, 0x39, 0x63, 0x38, 0x63, 0x39,
0x32, 0x39, 0x34, 0x37, 0x38, 0x35, 0x37, 0x37, 0x31, 0x32, 0x35, 0x36, 0x63, 0x64, 0x31, 0x64,
0x39, 0x34, 0x32, 0x61, 0x39, 0x30, 0x64, 0x31, 0x62, 0x64, 0x31, 0x64, 0x32, 0x64, 0x63, 0x61,
0x33, 0x65, 0x61, 0x38, 0x34, 0x65, 0x66, 0x37, 0x64, 0x38, 0x35, 0x61, 0x66, 0x65, 0x36, 0x36,
0x31, 0x31, 0x66, 0x62, 0x34, 0x33, 0x66, 0x66, 0x30, 0x62, 0x37, 0x34, 0x31, 0x32, 0x36, 0x64,
0x39, 0x30, 0x61, 0x36, 0x65, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31,
0x32, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x39,
0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x32, 0x38,
0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x34, 0x34, 0x33,
0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, 0x69, 0x6e, 0x67, 0x61,
0x70, 0x6f, 0x72, 0x65, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x0a, 0x7e, 0x21, 0x65, 0x64, 0x32, 0x35,
0x35, 0x31, 0x39, 0x3d, 0x38, 0x33, 0x32, 0x62, 0x33, 0x35, 0x64, 0x61, 0x64, 0x64, 0x37, 0x66,
0x35, 0x36, 0x66, 0x66, 0x33, 0x38, 0x31, 0x66, 0x61, 0x37, 0x32, 0x31, 0x64, 0x65, 0x37, 0x64,
0x35, 0x62, 0x65, 0x34, 0x63, 0x65, 0x62, 0x66, 0x63, 0x63, 0x63, 0x32, 0x30, 0x30, 0x32, 0x30,
0x38, 0x33, 0x38, 0x30, 0x64, 0x33, 0x30, 0x38, 0x34, 0x66, 0x36, 0x34, 0x38, 0x65, 0x32, 0x63,
0x31, 0x61, 0x35, 0x63, 0x66, 0x34, 0x33, 0x65, 0x35, 0x39, 0x66, 0x39, 0x32, 0x61, 0x36, 0x36,
0x35, 0x64, 0x66, 0x34, 0x64, 0x62, 0x63, 0x62, 0x38, 0x33, 0x37, 0x38, 0x38, 0x66, 0x36, 0x62,
0x64, 0x36, 0x37, 0x37, 0x66, 0x30, 0x32, 0x62, 0x32, 0x31, 0x30, 0x65, 0x35, 0x30, 0x63, 0x61,
0x66, 0x65, 0x66, 0x64, 0x32, 0x65, 0x66, 0x31, 0x38, 0x39, 0x62, 0x62, 0x66, 0x34, 0x38, 0x31,
0x62, 0x64, 0x30, 0x32, 0x63, 0x64, 0x63, 0x39, 0x38, 0x34, 0x35, 0x33, 0x38, 0x37, 0x64, 0x38,
0x34, 0x39, 0x62, 0x63, 0x35, 0x36, 0x66, 0x39, 0x63, 0x37, 0x32, 0x35, 0x31, 0x65, 0x35, 0x64,
0x30, 0x65, 0x61, 0x34, 0x34, 0x34, 0x66, 0x66, 0x63, 0x66, 0x38, 0x66, 0x37, 0x32, 0x32, 0x63,
0x32, 0x66, 0x65, 0x62, 0x38, 0x39, 0x36, 0x30, 0x33, 0x61, 0x30, 0x65, 0x35, 0x62, 0x61, 0x32,
0x39, 0x35, 0x66, 0x63, 0x0a, 0x7e, 0x21, 0x73, 0x69, 0x67, 0x69, 0x64, 0x3d, 0x37, 0x37, 0x37,
0x39, 0x32, 0x62, 0x31, 0x63, 0x30, 0x32, 0x3a, 0x30, 0x3a, 0x62, 0x35, 0x63, 0x33, 0x36, 0x31,
0x65, 0x38, 0x65, 0x39, 0x63, 0x32, 0x31, 0x35, 0x34, 0x65, 0x38, 0x32, 0x63, 0x33, 0x65, 0x39,
0x30, 0x32, 0x66, 0x64, 0x66, 0x63, 0x33, 0x33, 0x37, 0x34, 0x36, 0x38, 0x62, 0x30, 0x39, 0x32,
0x61, 0x37, 0x63, 0x34, 0x64, 0x38, 0x64, 0x63, 0x36, 0x38, 0x35, 0x63, 0x33, 0x37, 0x65, 0x62,
0x31, 0x30, 0x65, 0x65, 0x34, 0x66, 0x33, 0x63, 0x31, 0x37, 0x63, 0x63, 0x30, 0x62, 0x62, 0x31,
0x64, 0x30, 0x32, 0x34, 0x31, 0x36, 0x37, 0x65, 0x38, 0x63, 0x62, 0x30, 0x38, 0x32, 0x34, 0x64,
0x31, 0x32, 0x32, 0x36, 0x33, 0x34, 0x32, 0x38, 0x33, 0x37, 0x33, 0x35, 0x38, 0x32, 0x64, 0x61,
0x33, 0x64, 0x30, 0x61, 0x39, 0x61, 0x31, 0x34, 0x62, 0x33, 0x36, 0x65, 0x34, 0x35, 0x34, 0x36,
0x63, 0x33, 0x31, 0x37, 0x65, 0x38, 0x31, 0x31, 0x65, 0x36, 0x0a, 0x7e, 0x21, 0x73, 0x69, 0x67,
0x74, 0x73, 0x3d, 0x31, 0x34, 0x65, 0x30, 0x63, 0x62, 0x62, 0x39, 0x38, 0x64, 0x36, 0x0a
};
#define ZT_DEFAULT_ROOT_TOPOLOGY_LEN 1514
#define ZT_DEFAULT_ROOT_TOPOLOGY_LEN 1391

View File

@ -1,4 +1,4 @@
rootservers=7e19876aba\=id\\\=7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa\\nudp\\\=198.199.97.220/9993\\ntcp\\\=198.199.97.220/443\\ndesc\\\=San Francisco, California, USA\\ndns\\\=nyarlathotep.zerotier.com\\n\n8841408a2e\=id\\\=8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c\\nudp\\\=107.191.46.210/9993\\ntcp\\\=107.191.46.210/443\\ndesc\\\=Paris, France\\ndns\\\=shoggoth.zerotier.com\\n\n8acf059fe3\=id\\\=8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5\\nudp\\\=162.243.77.111/9993\\ntcp\\\=162.243.77.111/443\\ndesc\\\=New York, New York, USA\\ndns\\\=cthulhu.zerotier.com\\n\n9d219039f3\=id\\\=9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e\\nudp\\\=128.199.197.217/9993\\ntcp\\\=128.199.197.217/443\\ndesc\\\=Singapore\\ndns\\\=mi-go.zerotier.com\\n\n
~!ed25519=b7493f5a4b79a1dcc423fd25d2d8aa8d6293c490a12ceb6395417dd5868c17bfbcee685de58019d21f92576a78a45235d342efa2a00a544ded34766dd32d6f0e11809197f9baeedf4c6a0e8d2d657d280a579f2f2478b2f7c7a08089a5016b55
rootservers=7e19876aba\=id\\\=7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa\\nudp\\\=198.199.97.220/9993\\ntcp\\\=198.199.97.220/443\\ndesc\\\=San Francisco, California, USA\\n\n8841408a2e\=id\\\=8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c\\nudp\\\=107.191.46.210/9993\\ntcp\\\=107.191.46.210/443\\ndesc\\\=Paris, France\\n\n8acf059fe3\=id\\\=8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5\\nudp\\\=162.243.77.111/9993\\ntcp\\\=162.243.77.111/443\\ndesc\\\=New York, New York, USA\\n\n9d219039f3\=id\\\=9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e\\nudp\\\=128.199.197.217/9993\\ntcp\\\=128.199.197.217/443\\ndesc\\\=Singapore\\n\n
~!ed25519=832b35dadd7f56ff381fa721de7d5be4cebfccc200208380d3084f648e2c1a5cf43e59f92a665df4dbcb83788f6bd677f02b210e50cafefd2ef189bbf481bd02cdc9845387d849bc56f9c7251e5d0ea444ffcf8f722c2feb89603a0e5ba295fc
~!sigid=77792b1c02:0:b5c361e8e9c2154e82c3e902fdfc337468b092a7c4d8dc685c37eb10ee4f3c17cc0bb1d024167e8cb0824d12263428373582da3d0a9a14b36e4546c317e811e6
~!sigts=14ae42d0314
~!sigts=14e0cbb98d6

View File

@ -2,4 +2,3 @@ id=7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e411
udp=198.199.97.220/9993
tcp=198.199.97.220/443
desc=San Francisco, California, USA
dns=nyarlathotep.zerotier.com

View File

@ -2,4 +2,3 @@ id=8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02
udp=107.191.46.210/9993
tcp=107.191.46.210/443
desc=Paris, France
dns=shoggoth.zerotier.com

View File

@ -2,4 +2,3 @@ id=8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b452
udp=162.243.77.111/9993
tcp=162.243.77.111/443
desc=New York, New York, USA
dns=cthulhu.zerotier.com

View File

@ -2,4 +2,3 @@ id=9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c929478
udp=128.199.197.217/9993
tcp=128.199.197.217/443
desc=Singapore
dns=mi-go.zerotier.com

View File

@ -211,9 +211,9 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT1_Peer *peer
const char *prole = "";
switch(peer->role) {
case ZT1_PEER_ROLE_LEAF: prole = "LEAF"; break;
case ZT1_PEER_ROLE_HUB: prole = "HUB"; break;
case ZT1_PEER_ROLE_ROOTSERVER: prole = "ROOT"; break;
case ZT1_PEER_ROLE_LEAF: prole = "LEAF"; break;
case ZT1_PEER_ROLE_RELAY: prole = "RELAY"; break;
case ZT1_PEER_ROLE_ROOT: prole = "ROOT"; break;
}
Utils::snprintf(json,sizeof(json),