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 ## 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> * Debugging and testing, OpenWRT support fixes.<br>
Moritz Warning / moritzwarning@web.de 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 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,"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 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 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 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) ||(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 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,"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,"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 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,"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) ||(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(_sUpdateNode);
sqlite3_finalize(_sUpdateNode2); sqlite3_finalize(_sUpdateNode2);
sqlite3_finalize(_sGetEtherTypesFromRuleTable); sqlite3_finalize(_sGetEtherTypesFromRuleTable);
sqlite3_finalize(_sGetMulticastRates);
sqlite3_finalize(_sGetActiveBridges); sqlite3_finalize(_sGetActiveBridges);
sqlite3_finalize(_sGetIpAssignmentsForNode); sqlite3_finalize(_sGetIpAssignmentsForNode);
sqlite3_finalize(_sGetIpAssignmentPools); sqlite3_finalize(_sGetIpAssignmentPools);
@ -225,6 +225,9 @@ SqliteNetworkController::~SqliteNetworkController()
sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork); sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork);
sqlite3_finalize(_sDeleteRulesForNetwork); sqlite3_finalize(_sDeleteRulesForNetwork);
sqlite3_finalize(_sCreateIpAssignmentPool); sqlite3_finalize(_sCreateIpAssignmentPool);
sqlite3_finalize(_sUpdateMemberAuthorized);
sqlite3_finalize(_sUpdateMemberActiveBridge);
sqlite3_finalize(_sDeleteMember);
sqlite3_finalize(_sDeleteNetwork); sqlite3_finalize(_sDeleteNetwork);
sqlite3_finalize(_sGetGateways); sqlite3_finalize(_sGetGateways);
sqlite3_finalize(_sDeleteGateways); sqlite3_finalize(_sDeleteGateways);
@ -342,7 +345,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
sqlite3_reset(_sCreateMember); sqlite3_reset(_sCreateMember);
sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC); sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC);
sqlite3_bind_text(_sCreateMember,2,member.nodeId,10,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) { if (sqlite3_step(_sCreateMember) != SQLITE_DONE) {
netconf["error"] = "unable to create new member record"; netconf["error"] = "unable to create new member record";
return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; 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; netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = allowedEtherTypesCsv;
} }
{ if (network.multicastLimit > 0) {
std::string multicastRates; char ml[16];
sqlite3_reset(_sGetMulticastRates); Utils::snprintf(ml,sizeof(ml),"%lx",(unsigned long)network.multicastLimit);
sqlite3_bind_text(_sGetMulticastRates,1,network.id,16,SQLITE_STATIC); netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT] = ml;
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;
}
} }
{ {
@ -519,7 +506,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,1); int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,1);
if ((ip)&&(sqlite3_column_bytes(_sGetIpAssignmentsForNode,0) >= 4)&&(ipNetmaskBits > 0)&&(ipNetmaskBits <= 32)) { if ((ip)&&(sqlite3_column_bytes(_sGetIpAssignmentsForNode,0) >= 4)&&(ipNetmaskBits > 0)&&(ipNetmaskBits <= 32)) {
char tmp[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()) if (v4s.length())
v4s.push_back(','); v4s.push_back(',');
v4s.append(tmp); v4s.append(tmp);
@ -680,29 +667,27 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
if (!strcmp(j->u.object.values[k].name,"authorized")) { if (!strcmp(j->u.object.values[k].name,"authorized")) {
if (j->u.object.values[k].value->type == json_boolean) { if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_stmt *stmt = (sqlite3_stmt *)0; sqlite3_reset(_sUpdateMemberAuthorized);
if (sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ? WHERE rowid = ?",-1,&stmt,(const char **)0) == SQLITE_OK) sqlite3_bind_int(_sUpdateMemberAuthorized,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); sqlite3_bind_int64(_sUpdateMemberAuthorized,2,memberRowId);
sqlite3_bind_int64(stmt,2,memberRowId); if (sqlite3_step(_sUpdateMemberAuthorized) != SQLITE_DONE)
sqlite3_step(stmt); return 500;
sqlite3_finalize(stmt);
} }
} else if (!strcmp(j->u.object.values[k].name,"activeBridge")) { } else if (!strcmp(j->u.object.values[k].name,"activeBridge")) {
if (j->u.object.values[k].value->type == json_boolean) { if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_stmt *stmt = (sqlite3_stmt *)0; sqlite3_reset(_sUpdateMemberActiveBridge);
if (sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ? WHERE rowid = ?",-1,&stmt,(const char **)0) == SQLITE_OK) { sqlite3_bind_int(_sUpdateMemberActiveBridge,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); sqlite3_bind_int64(_sUpdateMemberActiveBridge,2,memberRowId);
sqlite3_bind_int64(stmt,2,memberRowId); if (sqlite3_step(_sUpdateMemberActiveBridge) != SQLITE_DONE)
sqlite3_step(stmt); return 500;
sqlite3_finalize(stmt);
}
} }
} else if (!strcmp(j->u.object.values[k].name,"ipAssignments")) { } else if (!strcmp(j->u.object.values[k].name,"ipAssignments")) {
if (j->u.object.values[k].value->type == json_array) { if (j->u.object.values[k].value->type == json_array) {
sqlite3_reset(_sDeleteIpAllocations); sqlite3_reset(_sDeleteIpAllocations);
sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC); sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,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) { 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]; json_value *ipalloc = j->u.object.values[k].value->u.array.values[kk];
if (ipalloc->type == json_string) { 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_blob(_sAllocateIp,3,(const void *)ipBlob,16,SQLITE_STATIC);
sqlite3_bind_int(_sAllocateIp,4,(int)a.netmaskBits()); sqlite3_bind_int(_sAllocateIp,4,(int)a.netmaskBits());
sqlite3_bind_int(_sAllocateIp,5,ipVersion); 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); sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC);
bool firstIp = true; bool firstIp = true;
while (sqlite3_step(_sGetIpAssignmentsForNode2) == SQLITE_ROW) { 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 ? "\"" : ",\""); responseBody.append(firstIp ? "\"" : ",\"");
firstIp = false; firstIp = false;
responseBody.append(_jsonEscape(ip.toString())); responseBody.append(_jsonEscape(ip.toString()));

View File

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

View File

@ -64,17 +64,6 @@ CREATE TABLE Member (
CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge); 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 ( CREATE TABLE Relay (
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE, nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,

View File

@ -65,17 +65,6 @@
"\n"\ "\n"\
"CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\ "CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\
"\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"\ "CREATE TABLE Relay (\n"\
" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\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"\ " nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"\

View File

@ -559,8 +559,8 @@ typedef struct
*/ */
enum ZT1_PeerRole { enum ZT1_PeerRole {
ZT1_PEER_ROLE_LEAF = 0, // ordinary node ZT1_PEER_ROLE_LEAF = 0, // ordinary node
ZT1_PEER_ROLE_HUB = 1, // locally federated hub ZT1_PEER_ROLE_RELAY = 1, // relay node
ZT1_PEER_ROLE_ROOTSERVER = 2 // planetary rootserver 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 * 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. * constantly until something is heard.
*/ */
#define ZT_PING_CHECK_INVERVAL 6250 #define ZT_PING_CHECK_INVERVAL 6250
@ -279,9 +279,9 @@
* *
* When we send something (including frames), we generally expect a response. * When we send something (including frames), we generally expect a response.
* Switching relays if no response in a short period of time causes more * 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 * 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 #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: case Packet::ERROR_OBJ_NOT_FOUND:
if (inReVerb == Packet::VERB_WHOIS) { 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)); 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) { } else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); 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; break;
case Packet::ERROR_IDENTITY_COLLISION: 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); RR->node->postEvent(ZT1_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
break; break;
@ -268,7 +268,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision);
bool trusted = false; bool trusted = false;
if (RR->topology->isRootserver(id.address())) { if (RR->topology->isRoot(id)) {
RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision); RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision);
trusted = true; trusted = true;
} }
@ -353,7 +353,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
bool trusted = false; bool trusted = false;
if (RR->topology->isRootserver(peer->address())) { if (RR->topology->isRoot(peer->identity())) {
RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision); RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision);
trusted = true; trusted = true;
} }
@ -362,10 +362,10 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
} break; } break;
case Packet::VERB_WHOIS: { case Packet::VERB_WHOIS: {
// Right now only rootservers are allowed to send OK(WHOIS) to prevent /* Right now only root servers are allowed to send OK(WHOIS) to prevent
// poisoning attacks. Further decentralization will require some other * poisoning attacks. Further decentralization will require some other
// kind of trust mechanism. * kind of trust mechanism. */
if (RR->topology->isRootserver(peer->address())) { if (RR->topology->isRoot(peer->identity())) {
const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY); const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
if (id.locallyValidate()) if (id.locallyValidate())
RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR->identity,id)))); 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) { if ((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY) {
gs.lastExplicitGather = now; gs.lastExplicitGather = now;
SharedPtr<Peer> sn(RR->topology->getBestRootserver()); SharedPtr<Peer> sn(RR->topology->getBestRoot());
if (sn) { if (sn) {
TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str()); 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 // Free allocated memory buffer if any
if (indexes != idxbuf) if (indexes != idxbuf)
delete [] indexes; 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) 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)")); //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();) { for(std::list<OutboundMulticast>::iterator tx(gs.txQueue.begin());tx!=gs.txQueue.end();) {
if (tx->atLimit()) { if (tx->atLimit())
gs.txQueue.erase(tx++); gs.txQueue.erase(tx++);
} else { else {
tx->sendIfNew(RR,member); tx->sendIfNew(RR,member);
if (tx->atLimit()) if (tx->atLimit())
gs.txQueue.erase(tx++); gs.txQueue.erase(tx++);

View File

@ -286,18 +286,28 @@ void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool
return; 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) { SharedPtr<Peer> signer(RR->topology->getPeer(cert.signedBy()));
// 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())) { if (!signer) {
TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); // This would be rather odd, since this is our controller... could happen
return; // 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) void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
@ -518,13 +514,13 @@ public:
RR(renv), RR(renv),
_now(renv->node->now()), _now(renv->node->now()),
_network(nw), _network(nw),
_rootserverAddresses(renv->topology->rootserverAddresses()), _rootAddresses(renv->topology->rootAddresses()),
_allMulticastGroups(nw->_allMulticastGroups()) _allMulticastGroups(nw->_allMulticastGroups())
{} {}
inline void operator()(Topology &t,const SharedPtr<Peer> &p) 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); Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
for(std::vector<MulticastGroup>::iterator mg(_allMulticastGroups.begin());mg!=_allMulticastGroups.end();++mg) { for(std::vector<MulticastGroup>::iterator mg(_allMulticastGroups.begin());mg!=_allMulticastGroups.end();++mg) {
@ -551,7 +547,7 @@ private:
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;
uint64_t _now; uint64_t _now;
Network *_network; Network *_network;
std::vector<Address> _rootserverAddresses; std::vector<Address> _rootAddresses;
std::vector<MulticastGroup> _allMulticastGroups; std::vector<MulticastGroup> _allMulticastGroups;
}; };

View File

@ -47,7 +47,6 @@
#include "MulticastGroup.hpp" #include "MulticastGroup.hpp"
#include "MAC.hpp" #include "MAC.hpp"
#include "Dictionary.hpp" #include "Dictionary.hpp"
#include "BandwidthAccount.hpp"
#include "Multicaster.hpp" #include "Multicaster.hpp"
#include "NetworkConfig.hpp" #include "NetworkConfig.hpp"
#include "CertificateOfMembership.hpp" #include "CertificateOfMembership.hpp"
@ -237,15 +236,6 @@ public:
_externalConfig(ec); _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 * 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::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,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 std::map<MAC,Address> _remoteBridgeRoutes; // remote addresses where given MACs are reachable

View File

@ -32,10 +32,6 @@
namespace ZeroTier { 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> NetworkConfig::createTestNetworkConfig(const Address &self)
{ {
SharedPtr<NetworkConfig> nc(new NetworkConfig()); SharedPtr<NetworkConfig> nc(new NetworkConfig());
@ -85,18 +81,6 @@ std::vector<unsigned int> NetworkConfig::allowedEtherTypes() const
return ets; 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) void NetworkConfig::_fromDictionary(const Dictionary &d)
{ {
static const std::string zero("0"); static const std::string zero("0");
@ -181,13 +165,6 @@ void NetworkConfig::_fromDictionary(const Dictionary &d)
std::sort(_activeBridges.begin(),_activeBridges.end()); std::sort(_activeBridges.begin(),_activeBridges.end());
std::unique(_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(),",","","")); 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) { for(std::vector<std::string>::const_iterator r(relaysSplit.begin());r!=relaysSplit.end();++r) {
std::size_t semi(r->find(';')); // address;ip/port,... 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 (_gateways != nc._gateways) return false;
if (_activeBridges != nc._activeBridges) return false; if (_activeBridges != nc._activeBridges) return false;
if (_relays != nc._relays) 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; if (_com != nc._com) return false;
return true; return true;
} }

View File

@ -68,9 +68,6 @@ namespace ZeroTier {
// integer(hex) // integer(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml" #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 // 0/1
#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p" #define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p"
@ -114,27 +111,6 @@ class NetworkConfig
friend class SharedPtr<NetworkConfig>; friend class SharedPtr<NetworkConfig>;
public: 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 * 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 uint64_t revision() const throw() { return _revision; }
inline const Address &issuedTo() const throw() { return _issuedTo; } inline const Address &issuedTo() const throw() { return _issuedTo; }
inline unsigned int multicastLimit() const throw() { return _multicastLimit; } 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 allowPassiveBridging() const throw() { return _allowPassiveBridging; }
inline bool isPublic() const throw() { return (!_private); } inline bool isPublic() const throw() { return (!_private); }
inline bool isPrivate() 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()) ); 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; bool operator==(const NetworkConfig &nc) const;
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); } inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
@ -229,7 +197,6 @@ private:
std::vector<InetAddress> _gateways; std::vector<InetAddress> _gateways;
std::vector<Address> _activeBridges; std::vector<Address> _activeBridges;
std::vector< std::pair<Address,InetAddress> > _relays; std::vector< std::pair<Address,InetAddress> > _relays;
std::map<MulticastGroup,MulticastRate> _multicastRates;
CertificateOfMembership _com; CertificateOfMembership _com;
AtomicCounter __refCount; AtomicCounter __refCount;

View File

@ -133,9 +133,7 @@ Node::Node(
if (!rt.size()) if (!rt.size())
rt.fromString(ZT_DEFAULTS.defaultRootTopology); rt.fromString(ZT_DEFAULTS.defaultRootTopology);
} }
Dictionary rootservers(rt.get("rootservers","")); RR->topology->setRootServers(Dictionary(rt.get("rootservers","")));
rootservers.update(rt.get("supernodes",""));
RR->topology->setRootservers(rootservers);
postEvent(ZT1_EVENT_UP); postEvent(ZT1_EVENT_UP);
} }
@ -143,7 +141,7 @@ Node::Node(
Node::~Node() Node::~Node()
{ {
Mutex::Lock _l(_networks_m); Mutex::Lock _l(_networks_m);
_networks.clear(); _networks.clear(); // ensure that networks are destroyed before shutdown
delete RR->sa; delete RR->sa;
delete RR->topology; delete RR->topology;
delete RR->antiRec; delete RR->antiRec;
@ -191,7 +189,7 @@ public:
RR(renv), RR(renv),
_now(now), _now(now),
_relays(relays), _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); p->doPingAndKeepalive(RR,_now);
if (p->lastReceive() > lastReceiveFromUpstream) if (p->lastReceive() > lastReceiveFromUpstream)
lastReceiveFromUpstream = p->lastReceive(); lastReceiveFromUpstream = p->lastReceive();
@ -221,7 +219,7 @@ private:
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;
uint64_t _now; uint64_t _now;
const std::vector< std::pair<Address,InetAddress> > &_relays; 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) 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; std::vector< SharedPtr<Network> > needConfig;
{ {
Mutex::Lock _l(_networks_m); 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()); SharedPtr<NetworkConfig> nc(n->second->config2());
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!nc)) if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!nc))
needConfig.push_back(n->second); 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); _PingPeersThatNeedPing pfunc(RR,now,networkRelays);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc); 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) ZT1_ResultCode Node::join(uint64_t nwid)
{ {
Mutex::Lock _l(_networks_m); Mutex::Lock _l(_networks_m);
SharedPtr<Network> &nwe = _networks[nwid]; SharedPtr<Network> nw = _network(nwid);
if (!nwe) if(!nw)
nwe = SharedPtr<Network>(new Network(RR,nwid)); _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; return ZT1_RESULT_OK;
} }
ZT1_ResultCode Node::leave(uint64_t nwid) ZT1_ResultCode Node::leave(uint64_t nwid)
{ {
std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn;
Mutex::Lock _l(_networks_m); Mutex::Lock _l(_networks_m);
std::map< uint64_t,SharedPtr<Network> >::iterator nw(_networks.find(nwid)); for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
if (nw != _networks.end()) { if (n->first != nwid)
nw->second->destroy(); newn.push_back(*n);
_networks.erase(nw);
} }
_networks.swap(newn);
return ZT1_RESULT_OK; return ZT1_RESULT_OK;
} }
@ -386,7 +386,7 @@ ZT1_PeerList *Node::peers() const
p->versionRev = -1; p->versionRev = -1;
} }
p->latency = pi->second->latency(); 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()); std::vector<Path> paths(pi->second->paths());
Path *bestPath = pi->second->getBestPath(_now); Path *bestPath = pi->second->getBestPath(_now);
@ -408,10 +408,10 @@ ZT1_PeerList *Node::peers() const
ZT1_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const ZT1_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
{ {
Mutex::Lock _l(_networks_m); Mutex::Lock _l(_networks_m);
std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.find(nwid)); SharedPtr<Network> nw = _network(nwid);
if (nw != _networks.end()) { if(nw) {
ZT1_VirtualNetworkConfig *nc = (ZT1_VirtualNetworkConfig *)::malloc(sizeof(ZT1_VirtualNetworkConfig)); ZT1_VirtualNetworkConfig *nc = (ZT1_VirtualNetworkConfig *)::malloc(sizeof(ZT1_VirtualNetworkConfig));
nw->second->externalConfig(nc); nw->externalConfig(nc);
return nc; return nc;
} }
return (ZT1_VirtualNetworkConfig *)0; return (ZT1_VirtualNetworkConfig *)0;
@ -428,7 +428,7 @@ ZT1_VirtualNetworkList *Node::networks() const
nl->networks = (ZT1_VirtualNetworkConfig *)(buf + sizeof(ZT1_VirtualNetworkList)); nl->networks = (ZT1_VirtualNetworkConfig *)(buf + sizeof(ZT1_VirtualNetworkList));
nl->networkCount = 0; 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++])); n->second->externalConfig(&(nl->networks[nl->networkCount++]));
return nl; return nl;

View File

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

View File

@ -626,7 +626,7 @@ public:
* [... additional tuples of network/address/adi ...] * [... additional tuples of network/address/adi ...]
* *
* LIKEs are sent to peers with whom you have a direct peer to peer * 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. * 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 /* Announce multicast groups of interest to direct peers if they are
* considered authorized members of a given network. Also announce to * 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))) { if ((pathIsConfirmed)&&((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000))) {
_lastAnnouncedTo = now; _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); Packet outp(_id.address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks()); const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n) { 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()); const std::vector<MulticastGroup> mgs((*n)->allMulticastGroups());
for(std::vector<MulticastGroup>::const_iterator mg(mgs.begin());mg!=mgs.end();++mg) { for(std::vector<MulticastGroup>::const_iterator mg(mgs.begin());mg!=mgs.end();++mg) {
if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) { 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 // 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. // 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) { if (sn) {
Path *snp = sn->getBestPath(now); Path *snp = sn->getBestPath(now);
if (snp) { if (snp) {

View File

@ -145,12 +145,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if (fromBridged) if (fromBridged)
network->learnBridgedMulticastGroup(mg,RR->node->now()); 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); //TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),len);
RR->mc->send( 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 * 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 * 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 * in terms of when the two peers initiate. Normally this is accounted for
* by the nearly-simultaneous RENDEZVOUS kickoff from the rootserver, but * by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but
* given that rootservers are hosted on cloud providers this can in some * given that relay are hosted on cloud providers this can in some
* cases have a few ms of latency between packet departures. By randomizing * 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 * 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 * 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. // It wouldn't hurt anything, just redundant and unnecessary.
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination); SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) { if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) {
// Don't know peer or no direct path -- so relay via rootserver // Don't know peer or no direct path -- so relay via root server
relayTo = RR->topology->getBestRootserver(); relayTo = RR->topology->getBestRoot();
if (relayTo) if (relayTo)
relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()); 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())))) { if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) {
unite(source,destination,false); unite(source,destination,false);
} else { } else {
// Don't know peer or no direct path -- so relay via rootserver // Don't know peer or no direct path -- so relay via root server
relayTo = RR->topology->getBestRootserver(&source,1,true); relayTo = RR->topology->getBestRoot(&source,1,true);
if (relayTo) if (relayTo)
relayTo->send(RR,packet->data(),packet->size(),RR->node->now()); 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) Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
{ {
SharedPtr<Peer> rootserver(RR->topology->getBestRootserver(peersAlreadyConsulted,numPeersAlreadyConsulted,false)); SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
if (rootserver) { if (root) {
Packet outp(rootserver->address(),RR->identity.address(),Packet::VERB_WHOIS); Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
addr.appendTo(outp); addr.appendTo(outp);
outp.armor(rootserver->key(),true); outp.armor(root->key(),true);
if (rootserver->send(RR,outp.data(),outp.size(),RR->node->now())) if (root->send(RR,outp.data(),outp.size(),RR->node->now()))
return rootserver->address(); return root->address();
} }
return Address(); return Address();
} }
@ -752,7 +746,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
} }
if (!relay) if (!relay)
relay = RR->topology->getBestRootserver(); relay = RR->topology->getBestRoot();
if (!(relay)||(!(viaPath = relay->getBestPath(now)))) if (!(relay)||(!(viaPath = relay->getBestPath(now))))
return false; return false;

View File

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

View File

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

View File

@ -1,98 +1,90 @@
static unsigned char ZT_DEFAULT_ROOT_TOPOLOGY[] = { static unsigned char ZT_DEFAULT_ROOT_TOPOLOGY[] = {
0x73, 0x75, 0x70, 0x65, 0x72, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x3d, 0x37, 0x65, 0x31, 0x39, 0x38, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3d, 0x37, 0x65, 0x31, 0x39,
0x37, 0x36, 0x61, 0x62, 0x61, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x37, 0x65, 0x31, 0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x37, 0x65,
0x39, 0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x3a, 0x30, 0x3a, 0x32, 0x61, 0x36, 0x65, 0x32, 0x62, 0x31, 0x39, 0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x3a, 0x30, 0x3a, 0x32, 0x61, 0x36, 0x65, 0x32,
0x32, 0x33, 0x31, 0x38, 0x39, 0x33, 0x30, 0x66, 0x36, 0x30, 0x65, 0x62, 0x30, 0x39, 0x37, 0x66, 0x62, 0x32, 0x33, 0x31, 0x38, 0x39, 0x33, 0x30, 0x66, 0x36, 0x30, 0x65, 0x62, 0x30, 0x39, 0x37,
0x37, 0x30, 0x64, 0x30, 0x66, 0x34, 0x62, 0x30, 0x32, 0x38, 0x62, 0x32, 0x63, 0x64, 0x36, 0x64, 0x66, 0x37, 0x30, 0x64, 0x30, 0x66, 0x34, 0x62, 0x30, 0x32, 0x38, 0x62, 0x32, 0x63, 0x64, 0x36,
0x33, 0x64, 0x30, 0x63, 0x36, 0x33, 0x63, 0x30, 0x31, 0x34, 0x62, 0x39, 0x30, 0x33, 0x39, 0x66, 0x64, 0x33, 0x64, 0x30, 0x63, 0x36, 0x33, 0x63, 0x30, 0x31, 0x34, 0x62, 0x39, 0x30, 0x33, 0x39,
0x66, 0x33, 0x35, 0x33, 0x39, 0x30, 0x65, 0x34, 0x31, 0x31, 0x38, 0x31, 0x66, 0x32, 0x31, 0x36, 0x66, 0x66, 0x33, 0x35, 0x33, 0x39, 0x30, 0x65, 0x34, 0x31, 0x31, 0x38, 0x31, 0x66, 0x32, 0x31,
0x66, 0x62, 0x32, 0x65, 0x36, 0x66, 0x61, 0x38, 0x64, 0x39, 0x35, 0x63, 0x31, 0x65, 0x65, 0x39, 0x36, 0x66, 0x62, 0x32, 0x65, 0x36, 0x66, 0x61, 0x38, 0x64, 0x39, 0x35, 0x63, 0x31, 0x65, 0x65,
0x36, 0x36, 0x37, 0x31, 0x35, 0x36, 0x34, 0x31, 0x31, 0x39, 0x30, 0x35, 0x63, 0x33, 0x64, 0x63, 0x39, 0x36, 0x36, 0x37, 0x31, 0x35, 0x36, 0x34, 0x31, 0x31, 0x39, 0x30, 0x35, 0x63, 0x33, 0x64,
0x63, 0x66, 0x65, 0x61, 0x37, 0x38, 0x64, 0x38, 0x63, 0x36, 0x64, 0x66, 0x61, 0x66, 0x62, 0x61, 0x63, 0x63, 0x66, 0x65, 0x61, 0x37, 0x38, 0x64, 0x38, 0x63, 0x36, 0x64, 0x66, 0x61, 0x66, 0x62,
0x36, 0x38, 0x38, 0x31, 0x37, 0x30, 0x62, 0x33, 0x66, 0x61, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x61, 0x36, 0x38, 0x38, 0x31, 0x37, 0x30, 0x62, 0x33, 0x66, 0x61, 0x5c, 0x5c, 0x6e, 0x75, 0x64,
0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, 0x32, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e,
0x32, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x32, 0x32, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c,
0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, 0x32, 0x32, 0x30, 0x2f, 0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, 0x32, 0x32, 0x30,
0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, 0x61, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53,
0x6e, 0x20, 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2c, 0x20, 0x43, 0x61, 0x6c, 0x61, 0x6e, 0x20, 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2c, 0x20, 0x43, 0x61,
0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x2c, 0x20, 0x55, 0x53, 0x41, 0x5c, 0x5c, 0x6e, 0x64, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x2c, 0x20, 0x55, 0x53, 0x41, 0x5c, 0x5c, 0x6e,
0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x6e, 0x79, 0x61, 0x72, 0x6c, 0x61, 0x74, 0x68, 0x6f, 0x74, 0x5c, 0x6e, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x5c, 0x3d, 0x69, 0x64,
0x65, 0x70, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x5c, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x3a, 0x30,
0x5c, 0x6e, 0x5c, 0x6e, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x5c, 0x3d, 0x3a, 0x62, 0x62, 0x31, 0x64, 0x33, 0x31, 0x66, 0x32, 0x63, 0x33, 0x32, 0x33, 0x65, 0x32, 0x36,
0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x34, 0x65, 0x39, 0x65, 0x36, 0x34, 0x31, 0x37, 0x32, 0x63, 0x31, 0x61, 0x37, 0x34, 0x66, 0x37,
0x3a, 0x30, 0x3a, 0x62, 0x62, 0x31, 0x64, 0x33, 0x31, 0x66, 0x32, 0x63, 0x33, 0x32, 0x33, 0x65, 0x37, 0x38, 0x39, 0x39, 0x35, 0x35, 0x35, 0x65, 0x64, 0x31, 0x30, 0x37, 0x35, 0x31, 0x63, 0x64,
0x32, 0x36, 0x34, 0x65, 0x39, 0x65, 0x36, 0x34, 0x31, 0x37, 0x32, 0x63, 0x31, 0x61, 0x37, 0x34, 0x35, 0x36, 0x65, 0x38, 0x36, 0x34, 0x30, 0x35, 0x63, 0x64, 0x65, 0x31, 0x31, 0x38, 0x64, 0x30,
0x66, 0x37, 0x37, 0x38, 0x39, 0x39, 0x35, 0x35, 0x35, 0x65, 0x64, 0x31, 0x30, 0x37, 0x35, 0x31, 0x32, 0x64, 0x66, 0x66, 0x65, 0x35, 0x35, 0x35, 0x64, 0x34, 0x36, 0x32, 0x63, 0x63, 0x66, 0x36,
0x63, 0x64, 0x35, 0x36, 0x65, 0x38, 0x36, 0x34, 0x30, 0x35, 0x63, 0x64, 0x65, 0x31, 0x31, 0x38, 0x61, 0x38, 0x35, 0x62, 0x35, 0x36, 0x33, 0x31, 0x63, 0x31, 0x32, 0x33, 0x35, 0x30, 0x63, 0x38,
0x64, 0x30, 0x32, 0x64, 0x66, 0x66, 0x65, 0x35, 0x35, 0x35, 0x64, 0x34, 0x36, 0x32, 0x63, 0x63, 0x64, 0x35, 0x64, 0x63, 0x34, 0x30, 0x39, 0x62, 0x61, 0x31, 0x30, 0x62, 0x39, 0x30, 0x32, 0x35,
0x66, 0x36, 0x61, 0x38, 0x35, 0x62, 0x35, 0x36, 0x33, 0x31, 0x63, 0x31, 0x32, 0x33, 0x35, 0x30, 0x64, 0x30, 0x66, 0x34, 0x34, 0x35, 0x63, 0x66, 0x34, 0x34, 0x39, 0x64, 0x39, 0x32, 0x62, 0x31,
0x63, 0x38, 0x64, 0x35, 0x64, 0x63, 0x34, 0x30, 0x39, 0x62, 0x61, 0x31, 0x30, 0x62, 0x39, 0x30, 0x63, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31,
0x32, 0x35, 0x64, 0x30, 0x66, 0x34, 0x34, 0x35, 0x63, 0x66, 0x34, 0x34, 0x39, 0x64, 0x39, 0x32, 0x39, 0x31, 0x2e, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c,
0x62, 0x31, 0x63, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31, 0x39, 0x31, 0x2e,
0x2e, 0x31, 0x39, 0x31, 0x2e, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73,
0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31, 0x39, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x50, 0x61, 0x72, 0x69, 0x73, 0x2c, 0x20, 0x46, 0x72, 0x61, 0x6e,
0x31, 0x2e, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x63, 0x65, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39, 0x66, 0x65,
0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x50, 0x61, 0x72, 0x69, 0x73, 0x2c, 0x20, 0x46, 0x72, 0x33, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39,
0x61, 0x6e, 0x63, 0x65, 0x5c, 0x5c, 0x6e, 0x64, 0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x73, 0x68, 0x66, 0x65, 0x33, 0x3a, 0x30, 0x3a, 0x34, 0x38, 0x32, 0x66, 0x36, 0x65, 0x65, 0x35, 0x64, 0x66,
0x6f, 0x67, 0x67, 0x6f, 0x74, 0x68, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e, 0x65, 0x39, 0x30, 0x32, 0x33, 0x31, 0x39, 0x62, 0x34, 0x31, 0x39, 0x64, 0x65, 0x35, 0x62, 0x64,
0x63, 0x6f, 0x6d, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39, 0x66, 0x63, 0x37, 0x36, 0x35, 0x32, 0x30, 0x39, 0x63, 0x30, 0x65, 0x63, 0x64, 0x61, 0x33, 0x38, 0x63,
0x65, 0x33, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x34, 0x64, 0x36, 0x65, 0x34, 0x66, 0x63, 0x66, 0x30, 0x64, 0x33, 0x33, 0x36, 0x35, 0x38, 0x33,
0x39, 0x66, 0x65, 0x33, 0x3a, 0x30, 0x3a, 0x34, 0x38, 0x32, 0x66, 0x36, 0x65, 0x65, 0x35, 0x64, 0x39, 0x38, 0x62, 0x34, 0x35, 0x32, 0x37, 0x64, 0x63, 0x64, 0x32, 0x32, 0x66, 0x39, 0x33, 0x31,
0x66, 0x65, 0x39, 0x30, 0x32, 0x33, 0x31, 0x39, 0x62, 0x34, 0x31, 0x39, 0x64, 0x65, 0x35, 0x62, 0x31, 0x32, 0x66, 0x62, 0x39, 0x62, 0x65, 0x66, 0x64, 0x30, 0x32, 0x66, 0x64, 0x37, 0x38, 0x62,
0x64, 0x63, 0x37, 0x36, 0x35, 0x32, 0x30, 0x39, 0x63, 0x30, 0x65, 0x63, 0x64, 0x61, 0x33, 0x38, 0x66, 0x37, 0x32, 0x36, 0x31, 0x62, 0x33, 0x33, 0x33, 0x66, 0x63, 0x31, 0x30, 0x35, 0x64, 0x31,
0x63, 0x34, 0x64, 0x36, 0x65, 0x34, 0x66, 0x63, 0x66, 0x30, 0x64, 0x33, 0x33, 0x36, 0x35, 0x38, 0x39, 0x32, 0x61, 0x36, 0x32, 0x33, 0x63, 0x61, 0x39, 0x65, 0x35, 0x30, 0x66, 0x63, 0x36, 0x30,
0x33, 0x39, 0x38, 0x62, 0x34, 0x35, 0x32, 0x37, 0x64, 0x63, 0x64, 0x32, 0x32, 0x66, 0x39, 0x33, 0x62, 0x33, 0x37, 0x34, 0x61, 0x35, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d,
0x31, 0x31, 0x32, 0x66, 0x62, 0x39, 0x62, 0x65, 0x66, 0x64, 0x30, 0x32, 0x66, 0x64, 0x37, 0x38, 0x31, 0x36, 0x32, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x39,
0x62, 0x66, 0x37, 0x32, 0x36, 0x31, 0x62, 0x33, 0x33, 0x33, 0x66, 0x63, 0x31, 0x30, 0x35, 0x64, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x36, 0x32,
0x31, 0x39, 0x32, 0x61, 0x36, 0x32, 0x33, 0x63, 0x61, 0x39, 0x65, 0x35, 0x30, 0x66, 0x63, 0x36, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x34, 0x34, 0x33, 0x5c,
0x30, 0x62, 0x33, 0x37, 0x34, 0x61, 0x35, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f,
0x3d, 0x31, 0x36, 0x32, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x72, 0x6b, 0x2c, 0x20, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x55, 0x53,
0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x36, 0x41, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, 0x66, 0x33,
0x32, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39,
0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x66, 0x33, 0x3a, 0x30, 0x3a, 0x30, 0x31, 0x66, 0x30, 0x39, 0x32, 0x32, 0x61, 0x39, 0x38, 0x65,
0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x55, 0x33, 0x62, 0x33, 0x34, 0x65, 0x62, 0x63, 0x62, 0x66, 0x66, 0x33, 0x33, 0x33, 0x32, 0x36, 0x39,
0x53, 0x41, 0x5c, 0x5c, 0x6e, 0x64, 0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x63, 0x74, 0x68, 0x75, 0x64, 0x63, 0x32, 0x36, 0x35, 0x64, 0x37, 0x61, 0x30, 0x32, 0x30, 0x61, 0x61, 0x62, 0x36, 0x39,
0x6c, 0x68, 0x75, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x64, 0x37, 0x32, 0x62, 0x65, 0x34, 0x64, 0x34, 0x61, 0x63, 0x63, 0x39, 0x63, 0x38, 0x63, 0x39,
0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, 0x66, 0x33, 0x5c, 0x32, 0x39, 0x34, 0x37, 0x38, 0x35, 0x37, 0x37, 0x31, 0x32, 0x35, 0x36, 0x63, 0x64, 0x31, 0x64,
0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, 0x66, 0x39, 0x34, 0x32, 0x61, 0x39, 0x30, 0x64, 0x31, 0x62, 0x64, 0x31, 0x64, 0x32, 0x64, 0x63, 0x61,
0x33, 0x3a, 0x30, 0x3a, 0x30, 0x31, 0x66, 0x30, 0x39, 0x32, 0x32, 0x61, 0x39, 0x38, 0x65, 0x33, 0x33, 0x65, 0x61, 0x38, 0x34, 0x65, 0x66, 0x37, 0x64, 0x38, 0x35, 0x61, 0x66, 0x65, 0x36, 0x36,
0x62, 0x33, 0x34, 0x65, 0x62, 0x63, 0x62, 0x66, 0x66, 0x33, 0x33, 0x33, 0x32, 0x36, 0x39, 0x64, 0x31, 0x31, 0x66, 0x62, 0x34, 0x33, 0x66, 0x66, 0x30, 0x62, 0x37, 0x34, 0x31, 0x32, 0x36, 0x64,
0x63, 0x32, 0x36, 0x35, 0x64, 0x37, 0x61, 0x30, 0x32, 0x30, 0x61, 0x61, 0x62, 0x36, 0x39, 0x64, 0x39, 0x30, 0x61, 0x36, 0x65, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31,
0x37, 0x32, 0x62, 0x65, 0x34, 0x64, 0x34, 0x61, 0x63, 0x63, 0x39, 0x63, 0x38, 0x63, 0x39, 0x32, 0x32, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x39,
0x39, 0x34, 0x37, 0x38, 0x35, 0x37, 0x37, 0x31, 0x32, 0x35, 0x36, 0x63, 0x64, 0x31, 0x64, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x32, 0x38,
0x34, 0x32, 0x61, 0x39, 0x30, 0x64, 0x31, 0x62, 0x64, 0x31, 0x64, 0x32, 0x64, 0x63, 0x61, 0x33, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x34, 0x34, 0x33,
0x65, 0x61, 0x38, 0x34, 0x65, 0x66, 0x37, 0x64, 0x38, 0x35, 0x61, 0x66, 0x65, 0x36, 0x36, 0x31, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, 0x69, 0x6e, 0x67, 0x61,
0x31, 0x66, 0x62, 0x34, 0x33, 0x66, 0x66, 0x30, 0x62, 0x37, 0x34, 0x31, 0x32, 0x36, 0x64, 0x39, 0x70, 0x6f, 0x72, 0x65, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x0a, 0x7e, 0x21, 0x65, 0x64, 0x32, 0x35,
0x30, 0x61, 0x36, 0x65, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x32, 0x35, 0x31, 0x39, 0x3d, 0x38, 0x33, 0x32, 0x62, 0x33, 0x35, 0x64, 0x61, 0x64, 0x64, 0x37, 0x66,
0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x39, 0x39, 0x35, 0x36, 0x66, 0x66, 0x33, 0x38, 0x31, 0x66, 0x61, 0x37, 0x32, 0x31, 0x64, 0x65, 0x37, 0x64,
0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x32, 0x38, 0x2e, 0x35, 0x62, 0x65, 0x34, 0x63, 0x65, 0x62, 0x66, 0x63, 0x63, 0x63, 0x32, 0x30, 0x30, 0x32, 0x30,
0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x38, 0x33, 0x38, 0x30, 0x64, 0x33, 0x30, 0x38, 0x34, 0x66, 0x36, 0x34, 0x38, 0x65, 0x32, 0x63,
0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, 0x69, 0x6e, 0x67, 0x61, 0x70, 0x31, 0x61, 0x35, 0x63, 0x66, 0x34, 0x33, 0x65, 0x35, 0x39, 0x66, 0x39, 0x32, 0x61, 0x36, 0x36,
0x6f, 0x72, 0x65, 0x5c, 0x5c, 0x6e, 0x64, 0x6e, 0x73, 0x5c, 0x5c, 0x5c, 0x3d, 0x6d, 0x69, 0x2d, 0x35, 0x64, 0x66, 0x34, 0x64, 0x62, 0x63, 0x62, 0x38, 0x33, 0x37, 0x38, 0x38, 0x66, 0x36, 0x62,
0x67, 0x6f, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x74, 0x69, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x5c, 0x64, 0x36, 0x37, 0x37, 0x66, 0x30, 0x32, 0x62, 0x32, 0x31, 0x30, 0x65, 0x35, 0x30, 0x63, 0x61,
0x5c, 0x6e, 0x5c, 0x6e, 0x0a, 0x7e, 0x21, 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x3d, 0x62, 0x66, 0x65, 0x66, 0x64, 0x32, 0x65, 0x66, 0x31, 0x38, 0x39, 0x62, 0x62, 0x66, 0x34, 0x38, 0x31,
0x37, 0x34, 0x39, 0x33, 0x66, 0x35, 0x61, 0x34, 0x62, 0x37, 0x39, 0x61, 0x31, 0x64, 0x63, 0x63, 0x62, 0x64, 0x30, 0x32, 0x63, 0x64, 0x63, 0x39, 0x38, 0x34, 0x35, 0x33, 0x38, 0x37, 0x64, 0x38,
0x34, 0x32, 0x33, 0x66, 0x64, 0x32, 0x35, 0x64, 0x32, 0x64, 0x38, 0x61, 0x61, 0x38, 0x64, 0x36, 0x34, 0x39, 0x62, 0x63, 0x35, 0x36, 0x66, 0x39, 0x63, 0x37, 0x32, 0x35, 0x31, 0x65, 0x35, 0x64,
0x32, 0x39, 0x33, 0x63, 0x34, 0x39, 0x30, 0x61, 0x31, 0x32, 0x63, 0x65, 0x62, 0x36, 0x33, 0x39, 0x30, 0x65, 0x61, 0x34, 0x34, 0x34, 0x66, 0x66, 0x63, 0x66, 0x38, 0x66, 0x37, 0x32, 0x32, 0x63,
0x35, 0x34, 0x31, 0x37, 0x64, 0x64, 0x35, 0x38, 0x36, 0x38, 0x63, 0x31, 0x37, 0x62, 0x66, 0x62, 0x32, 0x66, 0x65, 0x62, 0x38, 0x39, 0x36, 0x30, 0x33, 0x61, 0x30, 0x65, 0x35, 0x62, 0x61, 0x32,
0x63, 0x65, 0x65, 0x36, 0x38, 0x35, 0x64, 0x65, 0x35, 0x38, 0x30, 0x31, 0x39, 0x64, 0x32, 0x31, 0x39, 0x35, 0x66, 0x63, 0x0a, 0x7e, 0x21, 0x73, 0x69, 0x67, 0x69, 0x64, 0x3d, 0x37, 0x37, 0x37,
0x66, 0x39, 0x32, 0x35, 0x37, 0x36, 0x61, 0x37, 0x38, 0x61, 0x34, 0x35, 0x32, 0x33, 0x35, 0x64, 0x39, 0x32, 0x62, 0x31, 0x63, 0x30, 0x32, 0x3a, 0x30, 0x3a, 0x62, 0x35, 0x63, 0x33, 0x36, 0x31,
0x33, 0x34, 0x32, 0x65, 0x66, 0x61, 0x32, 0x61, 0x30, 0x30, 0x61, 0x35, 0x34, 0x34, 0x64, 0x65, 0x65, 0x38, 0x65, 0x39, 0x63, 0x32, 0x31, 0x35, 0x34, 0x65, 0x38, 0x32, 0x63, 0x33, 0x65, 0x39,
0x64, 0x33, 0x34, 0x37, 0x36, 0x36, 0x64, 0x64, 0x33, 0x32, 0x64, 0x36, 0x66, 0x30, 0x65, 0x31, 0x30, 0x32, 0x66, 0x64, 0x66, 0x63, 0x33, 0x33, 0x37, 0x34, 0x36, 0x38, 0x62, 0x30, 0x39, 0x32,
0x31, 0x38, 0x30, 0x39, 0x31, 0x39, 0x37, 0x66, 0x39, 0x62, 0x61, 0x65, 0x65, 0x64, 0x66, 0x34, 0x61, 0x37, 0x63, 0x34, 0x64, 0x38, 0x64, 0x63, 0x36, 0x38, 0x35, 0x63, 0x33, 0x37, 0x65, 0x62,
0x63, 0x36, 0x61, 0x30, 0x65, 0x38, 0x64, 0x32, 0x64, 0x36, 0x35, 0x37, 0x64, 0x32, 0x38, 0x30, 0x31, 0x30, 0x65, 0x65, 0x34, 0x66, 0x33, 0x63, 0x31, 0x37, 0x63, 0x63, 0x30, 0x62, 0x62, 0x31,
0x61, 0x35, 0x37, 0x39, 0x66, 0x32, 0x66, 0x32, 0x34, 0x37, 0x38, 0x62, 0x32, 0x66, 0x37, 0x63, 0x64, 0x30, 0x32, 0x34, 0x31, 0x36, 0x37, 0x65, 0x38, 0x63, 0x62, 0x30, 0x38, 0x32, 0x34, 0x64,
0x37, 0x61, 0x30, 0x38, 0x30, 0x38, 0x39, 0x61, 0x35, 0x30, 0x31, 0x36, 0x62, 0x35, 0x35, 0x0a, 0x31, 0x32, 0x32, 0x36, 0x33, 0x34, 0x32, 0x38, 0x33, 0x37, 0x33, 0x35, 0x38, 0x32, 0x64, 0x61,
0x7e, 0x21, 0x73, 0x69, 0x67, 0x69, 0x64, 0x3d, 0x37, 0x37, 0x37, 0x39, 0x32, 0x62, 0x31, 0x63, 0x33, 0x64, 0x30, 0x61, 0x39, 0x61, 0x31, 0x34, 0x62, 0x33, 0x36, 0x65, 0x34, 0x35, 0x34, 0x36,
0x30, 0x32, 0x3a, 0x30, 0x3a, 0x62, 0x35, 0x63, 0x33, 0x36, 0x31, 0x65, 0x38, 0x65, 0x39, 0x63, 0x63, 0x33, 0x31, 0x37, 0x65, 0x38, 0x31, 0x31, 0x65, 0x36, 0x0a, 0x7e, 0x21, 0x73, 0x69, 0x67,
0x32, 0x31, 0x35, 0x34, 0x65, 0x38, 0x32, 0x63, 0x33, 0x65, 0x39, 0x30, 0x32, 0x66, 0x64, 0x66, 0x74, 0x73, 0x3d, 0x31, 0x34, 0x65, 0x30, 0x63, 0x62, 0x62, 0x39, 0x38, 0x64, 0x36, 0x0a
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
}; };
#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 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=b7493f5a4b79a1dcc423fd25d2d8aa8d6293c490a12ceb6395417dd5868c17bfbcee685de58019d21f92576a78a45235d342efa2a00a544ded34766dd32d6f0e11809197f9baeedf4c6a0e8d2d657d280a579f2f2478b2f7c7a08089a5016b55 ~!ed25519=832b35dadd7f56ff381fa721de7d5be4cebfccc200208380d3084f648e2c1a5cf43e59f92a665df4dbcb83788f6bd677f02b210e50cafefd2ef189bbf481bd02cdc9845387d849bc56f9c7251e5d0ea444ffcf8f722c2feb89603a0e5ba295fc
~!sigid=77792b1c02:0:b5c361e8e9c2154e82c3e902fdfc337468b092a7c4d8dc685c37eb10ee4f3c17cc0bb1d024167e8cb0824d12263428373582da3d0a9a14b36e4546c317e811e6 ~!sigid=77792b1c02:0:b5c361e8e9c2154e82c3e902fdfc337468b092a7c4d8dc685c37eb10ee4f3c17cc0bb1d024167e8cb0824d12263428373582da3d0a9a14b36e4546c317e811e6
~!sigts=14ae42d0314 ~!sigts=14e0cbb98d6

View File

@ -2,4 +2,3 @@ id=7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e411
udp=198.199.97.220/9993 udp=198.199.97.220/9993
tcp=198.199.97.220/443 tcp=198.199.97.220/443
desc=San Francisco, California, USA 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 udp=107.191.46.210/9993
tcp=107.191.46.210/443 tcp=107.191.46.210/443
desc=Paris, France desc=Paris, France
dns=shoggoth.zerotier.com

View File

@ -2,4 +2,3 @@ id=8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b452
udp=162.243.77.111/9993 udp=162.243.77.111/9993
tcp=162.243.77.111/443 tcp=162.243.77.111/443
desc=New York, New York, USA 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 udp=128.199.197.217/9993
tcp=128.199.197.217/443 tcp=128.199.197.217/443
desc=Singapore 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 = ""; const char *prole = "";
switch(peer->role) { switch(peer->role) {
case ZT1_PEER_ROLE_LEAF: prole = "LEAF"; break; case ZT1_PEER_ROLE_LEAF: prole = "LEAF"; break;
case ZT1_PEER_ROLE_HUB: prole = "HUB"; break; case ZT1_PEER_ROLE_RELAY: prole = "RELAY"; break;
case ZT1_PEER_ROLE_ROOTSERVER: prole = "ROOT"; break; case ZT1_PEER_ROLE_ROOT: prole = "ROOT"; break;
} }
Utils::snprintf(json,sizeof(json), Utils::snprintf(json,sizeof(json),