/*
* A JavaScript class based model for the ZeroTier controller microservice API
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
'use strict';
/**
* @param {string} IP with optional /netmask|port section
* @return 4, 6, or 0 if invalid
*/
function ipClassify(ip)
{
if ((!ip)||(typeof ip !== 'string'))
return 0;
let ips = ip.split('/');
if (ips.length > 0) {
if (ips.length > 1) {
if (ips[1].length === 0)
return 0;
for(let i=0;i 0) {
for(let i=0;i 0) {
for(let i=0;i= 0) {
r += c;
if (r.length === l)
break;
}
}
while (r.length < l)
r = '0' + r;
return r;
};
exports.formatZeroTierIdentifier = formatZeroTierIdentifier;
/**
* Goes through a rule set array and makes sure it's valid, returning a canonicalized version
*
* @param {array[object]} rules Array of ZeroTier rules
* @return New array of canonicalized rules
* @throws {Error} Rule set is invalid
*/
function formatRuleSetArray(rules)
{
let r = [];
if ((rules)&&(Array.isArray(rules))) {
for(let a=0;a= 0)
this._authTokens[k] = exp;
}
}
return this._authTokens;
}
get capabilities() { return this._capabilities; }
set capabilities(c)
{
let ca = [];
let ids = {};
if ((c)&&(Array.isArray(c))) {
for(let a=0;a= 0)&&(capId <= 0xffffffff)&&(!ids[capId])) {
ids[capId] = true;
let capDefault = !!cap['default'];
let capRules = formatRuleSetArray(cap.rules);
ca.push({
id: capId,
'default': capDefault,
rules: capRules
});
}
}
}
}
ca.sort(function(a,b) {
a = a.id;
b = b.id;
return ((a > b) ? 1 : ((a < b) ? -1 : 0));
});
this._capabilities = ca;
return ca;
}
get ipAssignmentPools() { return this._ipAssignmentPools; }
set ipAssignmentPools(ipp)
{
let pa = [];
let ranges = {};
if ((ipp)&&(Array.isArray(ipp))) {
for(let a=0;a 0)&&(stype === ipClassify(end))&&(!ranges[start+'_'+end])) {
ranges[start+'_'+end] = true;
pa.push({ ipRangeStart: start, ipRangeEnd: end });
}
}
}
}
}
pa.sort(function(a,b) { return a.ipRangeStart.localeCompare(b.ipRangeStart); });
this._ipAssignmentPools = pa;
return pa;
}
get multicastLimit() { return this._multicastLimit; }
set multicastLimit(n)
{
try {
let nn = parseInt(n)||0;
this._multicastLimit = (nn >= 0) ? nn : 0;
} catch (e) {
this._multicastLimit = 0;
}
return this._multicastLimit;
}
get routes() { return this._routes; }
set routes(r)
{
let ra = [];
let targets = {};
if ((r)&&(Array.isArray(r))) {
for(let a=0;a 0)&&((routeVia === null)||(ipClassify(routeVia) === rtt))&&(!targets[routeTarget])) {
targets[routeTarget] = true;
ra.push({ target: routeTarget, via: routeVia });
}
}
}
}
ra.sort(function(a,b) { return a.routeTarget.localeCompare(b.routeTarget); });
this._routes = ra;
return ra;
}
get tags() { return this._tags; }
set tags(t)
{
let ta = [];
if ((t)&&(Array.isArray(t))) {
for(let a=0;a= 0)||(tagId <= 0xffffffff)) {
let tagDefault = tag.default;
if (typeof tagDefault !== 'number')
tagDefault = parseInt(tagDefault)||null;
if ((tagDefault < 0)||(tagDefault > 0xffffffff))
tagDefault = null;
ta.push({ 'id': tagId, 'default': tagDefault });
}
}
}
}
ta.sort(function(a,b) {
a = a.id;
b = b.id;
return ((a > b) ? 1 : ((a < b) ? -1 : 0));
});
this._tags = ta;
return ta;
}
get v4AssignMode() { return this._v4AssignMode; }
set v4AssignMode(m)
{
if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) {
this._v4AssignMode.zt = m.zt;
} else if (m === 'zt') { // legacy
this._v4AssignMode.zt = true;
} else {
this._v4AssignMode.zt = false;
}
}
get v6AssignMode() { return this._v6AssignMode; }
set v6AssignMode(m)
{
if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) {
this._v6AssignMode.zt = m.zt;
this._v6AssignMode.rfc4193 = m.rfc4193;
this._v6AssignMode['6plane'] = m['6plane'];
} else if (typeof m === 'string') { // legacy
let ms = m.split(',');
this._v6AssignMode.zt = false;
this._v6AssignMode.rfc4193 = false;
this._v6AssignMode['6plane'] = false;
for(let i=0;i= 10000) mtu = 10000; // maximum as per ZT spec
this._mtu = mtu;
}
get name() { return this._name; }
set name(n)
{
if (typeof n === 'string')
this._name = n;
else if (typeof n === 'number')
this._name = n.toString();
else this._name = '';
}
get private() { return this._private; }
set private(b)
{
// This is really meaningful for security, so make true unless explicitly set to false.
this._private = (b !== false);
}
get activeMemberCount() { return this.__activeMemberCount; }
get authorizedMemberCount() { return this.__authorizedMemberCount; }
get totalMemberCount() { return this.__totalMemberCount; }
get clock() { return this.__clock; }
get creationTime() { return this.__creationTime; }
get revision() { return this.__revision; }
toJSONExcludeControllerGenerated()
{
return {
id: this.id,
objtype: 'network',
nwid: this.nwid,
authTokens: this.authTokens,
capabilities: this.capabilities,
ipAssignmentPools: this.ipAssignmentPools,
multicastLimit: this.multicastLimit,
routes: this.routes,
tags: this.tags,
v4AssignMode: this._v4AssignMode.toJSON(),
v6AssignMode: this._v6AssignMode.toJSON(),
rules: this.rules,
enableBroadcast: this.enableBroadcast,
mtu: this.mtu,
name: this.name,
'private': this['private']
};
}
toJSON()
{
var j = this.toJSONExcludeControllerGenerated();
j.activeMemberCount = this.activeMemberCount;
j.authorizedMemberCount = this.authorizedMemberCount;
j.totalMemberCount = this.totalMemberCount;
j.clock = this.clock;
j.creationTime = this.creationTime;
j.revision = this.revision;
return j;
}
clear()
{
this._id = '';
this._authTokens = {};
this._capabilities = [];
this._ipAssignmentPools = [];
this._multicastLimit = 32;
this._routes = [];
this._tags = [];
this._v4AssignMode = new _V4AssignMode();
this._v6AssignMode = new _v6AssignMode();
this._rules = [];
this._enableBroadcast = true;
this._mtu = 2800;
this._name = '';
this._private = true;
this.__activeMemberCount = 0;
this.__authorizedMemberCount = 0;
this.__totalMemberCount = 0;
this.__clock = 0;
this.__creationTime = 0;
this.__revision = 0;
}
patch(obj)
{
if (obj instanceof Network)
obj = obj.toJSON();
if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) {
for(var k in obj) {
try {
switch(k) {
case 'id':
case 'authTokens':
case 'capabilities':
case 'ipAssignmentPools':
case 'multicastLimit':
case 'routes':
case 'tags':
case 'rules':
case 'enableBroadcast':
case 'mtu':
case 'name':
case 'private':
case 'v4AssignMode':
case 'v6AssignMode':
this[k] = obj[k];
break;
case 'activeMemberCount':
case 'authorizedMemberCount':
case 'totalMemberCount':
case 'clock':
case 'creationTime':
case 'revision':
this['__'+k] = parseInt(obj[k])||0;
break;
}
} catch (e) {}
}
}
}
};
exports.Network = Network;
class Member
{
constructor(obj)
{
this.clear();
this.patch(obj);
}
get objtype() { return 'member'; }
get id() { return this._id; }
set id(x) { this._id = formatZeroTierIdentifier((typeof x === 'number') ? x.toString(16) : x,10); }
get address() { return this._id; } // legacy
get nwid() { return this._nwid; }
set nwid(x) { this._nwid = formatZeroTierIdentifier(x,16); }
get controllerId() { return this.nwid.substr(0,10); }
get authorized() { return this._authorized; }
set authorized(b) { this._authorized = (b === true); } // security critical so require explicit set to true
get activeBridge() { return this._activeBridge; }
set activeBridge(b) { this._activeBridge = !!b; }
get capabilities() { return this._capabilities; }
set capabilities(c)
{
let caps = {};
let ca = [];
if ((c)&&(Array.isArray(c))) {
for(let a=0;a= 0)&&(capId <= 0xffffffff)&&(!caps[capId])) {
caps[capId] = true;
ca.push(capId);
}
}
}
ca.sort();
this._capabilities = ca;
return ca;
}
get identity() { return this._identity; }
set identity(istr)
{
if ((istr)&&(typeof istr === 'string'))
this._identity = istr;
else this._identity = null;
}
get ipAssignments() { return this._ipAssignments; }
set ipAssignments(ipa)
{
let ips = {};
if ((ipa)&&(Array.isArray(ipa))) {
for(let a=0;a 0)
ips[ip] = true;
}
}
this._ipAssignments = Object.keys(ips);
this._ipAssignments.sort();
return this._ipAssignments;
}
get noAutoAssignIps() { return this._noAutoAssignIps; }
set noAutoAssignIps(b) { this._noAutoAssignIps = !!b; }
get tags() { return this._tags; }
set tags(t)
{
let ta = [];
let pairs = {};
if ((t)&&(Array.isArray(t))) {
for(let a=0;a= 0)&&(tagId <= 0xffffffff)&&(tagValue >= 0)&&(tagValue <= 0xffffffff)&&(!pairs[pk])) {
pairs[pk] = true;
ta.push([ tagId,tagValue ]);
}
}
}
}
ta.sort(function(a,b) {
return ((a[0] < b[0]) ? -1 : ((a[0] > b[0]) ? 1 : 0));
});
this._tags = ta;
return ta;
}
get creationTime() { return this.__creationTime; }
get lastAuthorizedTime() { return this.__lastAuthorizedTime; }
get lastAuthorizedCredentialType() { return this.__lastAuthorizedCredentialType; }
get lastAuthorizedCredential() { return this.__lastAuthorizedCredential; }
get lastDeauthorizedTime() { return this.__lastDeauthorizedTime; }
get physicalAddr() { return this.__physicalAddr; }
get revision() { return this.__revision; }
get vMajor() { return this.__vMajor; }
get vMinor() { return this.__vMinor; }
get vRev() { return this.__vRev; }
get vProto() { return this.__vProto; }
toJSONExcludeControllerGenerated()
{
return {
id: this.id,
nwid: this.nwid,
objtype: 'member',
address: this.id,
authorized: this.authorized,
activeBridge: this.activeBridge,
capabilities: this.capabilities,
identity: this.identity,
ipAssignments: this.ipAssignments,
noAutoAssignIps: this.noAutoAssignIps,
tags: this.tags
};
}
toJSON()
{
let j = this.toJSONExcludeControllerGenerated();
j.creationTime = this.creationTime;
j.lastAuthorizedTime = this.lastAuthorizedTime;
j.lastAuthorizedCredentialType = this.lastAuthorizedCredentialType;
j.lastAuthorizedCredential = this.lastAuthorizedCredential;
j.lastDeauthorizedTime = this.lastDeauthorizedTime;
j.physicalAddr = this.physicalAddr;
j.revision = this.revision;
j.vMajor = this.vMajor;
j.vMinor = this.vMinor;
j.vRev = this.vRev;
j.vProto = this.vProto;
return j;
}
clear()
{
this._id = '';
this._nwid = '';
this._authorized = false;
this._activeBridge = false;
this._capabilities = [];
this._identity = '';
this._ipAssignments = [];
this._noAutoAssignIps = false;
this._tags = [];
this.__creationTime = 0;
this.__lastAuthorizedTime = 0;
this.__lastAuthorizedCredentialType = null;
this.__lastAuthorizedCredential = null;
this.__lastDeauthorizedTime = 0;
this.__physicalAddr = '';
this.__revision = 0;
this.__vMajor = 0;
this.__vMinor = 0;
this.__vRev = 0;
this.__vProto = 0;
}
patch(obj)
{
if (obj instanceof Member)
obj = obj.toJSON();
if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) {
for(var k in obj) {
try {
switch(k) {
case 'id':
case 'nwid':
case 'authorized':
case 'activeBridge':
case 'capabilities':
case 'identity':
case 'ipAssignments':
case 'noAutoAssignIps':
case 'tags':
this[k] = obj[k];
break;
case 'creationTime':
case 'lastAuthorizedTime':
case 'lastAuthorizedCredentialType':
case 'lastAuthorizedCredential':
case 'lastDeauthorizedTime':
case 'physicalAddr':
case 'revision':
case 'vMajor':
case 'vMinor':
case 'vRev':
case 'vProto':
this['__'+k] = parseInt(obj[k])||0;
break;
}
} catch (e) {}
}
}
}
};
exports.Member = Member;