index.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. //
  2. // ZeroTier One - Global Peer to Peer Ethernet
  3. // Copyright (C) 2011-2014 ZeroTier Networks LLC
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. //
  18. // --
  19. //
  20. // ZeroTier may be used and distributed under the terms of the GPLv3, which
  21. // are available at: http://www.gnu.org/licenses/gpl-3.0.html
  22. //
  23. // If you would like to embed ZeroTier into a commercial application or
  24. // redistribute it in a modified binary form, please contact ZeroTier Networks
  25. // LLC. Start here: http://www.zerotier.com/
  26. //
  27. // Fields in netconf response dictionary
  28. var ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES = "et";
  29. var ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID = "nwid";
  30. var ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP = "ts";
  31. var ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO = "id";
  32. var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS = "mpb";
  33. var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH = "md";
  34. var ZT_NETWORKCONFIG_DICT_KEY_PRIVATE = "p";
  35. var ZT_NETWORKCONFIG_DICT_KEY_NAME = "n";
  36. var ZT_NETWORKCONFIG_DICT_KEY_DESC = "d";
  37. var ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC = "v4s";
  38. var ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC = "v6s";
  39. var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES = "mr";
  40. var ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP = "com";
  41. // Path to zerotier-idtool binary, invoked to enerate certificates of membership
  42. var ZEROTIER_IDTOOL = '/usr/local/bin/zerotier-idtool';
  43. // From Constants.hpp in node/
  44. var ZT_NETWORK_AUTOCONF_DELAY = 60000;
  45. // Connect to redis, assuming database 0 and no auth (for now)
  46. var redis = require('redis');
  47. var DB = redis.createClient();
  48. DB.on("error",function(err) {
  49. console.error('redis query error: '+err);
  50. });
  51. // Global variables -- these are initialized on startup or netconf-init message
  52. var netconfSigningIdentity = null; // identity of netconf master, with private key portion
  53. var spawn = require('child_process').spawn;
  54. function ztDbTrue(v) { return ((v === '1')||(v === 'true')||(v > 0)); }
  55. function csvToArray(csv) { return (((typeof csv === 'string')&&(csv.length > 0)) ? csv.split(',') : []); }
  56. function arrayToCsv(a) { return ((Array.isArray(a)) ? ((a.length > 0) ? a.join(',') : '') : (((a !== null)&&(typeof a !== 'undefined')) ? a.toString() : '')); }
  57. //
  58. // ZeroTier One Dictionary -- encoding-compatible with Dictionary in C++ code base
  59. //
  60. function Dictionary(fromStr)
  61. {
  62. var thiz = this;
  63. this.data = {};
  64. this._esc = function(data) {
  65. var es = '';
  66. for(var i=0;i<data.length;++i) {
  67. var c = data.charAt(i);
  68. switch(c) {
  69. case '\0': es += '\\0'; break;
  70. case '\r': es += '\\r'; break;
  71. case '\n': es += '\\n'; break;
  72. case '\\': es += '\\\\'; break;
  73. case '=': es += '\\='; break;
  74. default: es += c; break;
  75. }
  76. }
  77. return es;
  78. };
  79. this._unesc = function(s) {
  80. if (typeof s !== 'string')
  81. return '';
  82. var uns = '';
  83. var escapeState = false;
  84. for(var i=0;i<s.length;++i) {
  85. var c = s.charAt(i);
  86. if (escapeState) {
  87. escapeState = false;
  88. switch(c) {
  89. case '0': uns += '\0'; break;
  90. case 'r': uns += '\r'; break;
  91. case 'n': uns += '\n'; break;
  92. default: uns += c; break;
  93. }
  94. } else{
  95. if ((c !== '\r')&&(c !== '\n')&&(c !== '\0')) {
  96. if (c === '\\')
  97. escapeState = true;
  98. else uns += c;
  99. }
  100. }
  101. }
  102. return uns;
  103. };
  104. this.toString = function() {
  105. var str = '';
  106. for(var key in thiz.data) {
  107. str += thiz._esc(key);
  108. str += '=';
  109. var value = thiz.data[key];
  110. if (value)
  111. str += thiz._esc(value.toString());
  112. str += '\n';
  113. }
  114. return str;
  115. };
  116. this.fromString = function(str) {
  117. thiz.data = {};
  118. if (typeof str !== 'string')
  119. return thiz;
  120. var lines = str.split('\n');
  121. for(var l=0;l<lines.length;++l) {
  122. var escapeState = false;
  123. var eqAt = 0;
  124. for(;eqAt<lines[l].length;++eqAt) {
  125. var c = lines[l].charAt(eqAt);
  126. if (escapeState)
  127. escapeState = false;
  128. else if (c === '\\')
  129. escapeState = true;
  130. else if (c === '=')
  131. break;
  132. }
  133. var k = thiz._unesc(lines[l].substr(0,eqAt));
  134. ++eqAt;
  135. if ((k)&&(k.length > 0))
  136. thiz.data[k] = thiz._unesc((eqAt < lines[l].length) ? lines[l].substr(eqAt) : '');
  137. }
  138. return thiz;
  139. };
  140. if ((typeof fromStr === 'string')&&(fromStr.length > 0))
  141. thiz.fromString(fromStr);
  142. };
  143. //
  144. // Identity implementation using zerotier-idtool as subprocess to do actual crypto work
  145. //
  146. function Identity(idstr)
  147. {
  148. var thiz = this;
  149. this.str = '';
  150. this.fields = [];
  151. this.toString = function() {
  152. return thiz.str;
  153. };
  154. this.address = function() {
  155. return ((thiz.fields.length > 0) ? thiz.fields[0] : '0000000000');
  156. };
  157. this.fromString = function(str) {
  158. thiz.str = '';
  159. thiz.fields = [];
  160. if (typeof str !== 'string')
  161. return;
  162. for(var i=0;i<str.length;++i) {
  163. if ("0123456789abcdef:ABCDEF".indexOf(str.charAt(i)) < 0)
  164. return; // invalid character in identity
  165. }
  166. var fields = str.split(':');
  167. if ((fields.length < 3)||(fields[0].length !== 10)||(fields[1] !== '0'))
  168. return;
  169. thiz.fields = fields;
  170. };
  171. this.isValid = function() {
  172. if ((thiz.fields.length < 3)||(thiz.fields[0].length !== 10)||(thiz.fields[1] !== '0'))
  173. return true;
  174. return false;
  175. };
  176. this.hasPrivate = function() {
  177. return ((thiz.isValid())&&(thiz.fields.length >= 4));
  178. };
  179. if (typeof idstr === 'string')
  180. thiz.fromString(idstr);
  181. };
  182. //
  183. // Invokes zerotier-idtool to generate certificates for private networks
  184. //
  185. function generateCertificateOfMembership(nwid,peerAddress,callback)
  186. {
  187. var comTimestamp = '0,' + Date.now().toString(16) + ',' + (ZT_NETWORK_AUTOCONF_DELAY * 4).toString(16);
  188. var comNwid = '1,' + nwid + ',0';
  189. var comIssuedTo = '2,' + peerAddress + ',ffffffffffffffff';
  190. var cert = '';
  191. var certErr = '';
  192. var idtool = spawn(ZEROTIER_IDTOOL,[ 'mkcom',netconfSigningIdentity,comTimestamp,comNwid,comIssuedTo ]);
  193. idtool.stdout.on('data',function(data) {
  194. cert += data;
  195. });
  196. idtool.stderr.on('data',function(data) {
  197. certErr += data;
  198. });
  199. idtool.on('close',function(exitCode) {
  200. if (certErr.length > 0)
  201. console.error('zerotier-idtool stderr returned: '+certErr);
  202. return callback((cert.length > 0) ? cert : null,exitCode);
  203. });
  204. }
  205. //
  206. // Message handler for messages over ZeroTier One service bus
  207. //
  208. function doNetconfInit(message)
  209. {
  210. netconfSigningIdentity = new Identity(message.data['netconfId']);
  211. if (!netconfSigningIdentity.hasPrivate()) {
  212. netconfSigningIdentity = null;
  213. console.error('got invalid netconf signing identity in netconf-init');
  214. }
  215. }
  216. function doNetconfRequest(message)
  217. {
  218. if ((!netconfSigningIdentity)||(!netconfSigningIdentity.hasPrivate())) {
  219. console.error('got netconf-request before netconf-init, ignored');
  220. return;
  221. }
  222. // Get required fields
  223. var peerId = new Identity(message.data['peerId']);
  224. var fromIpAndPort = message.data['from'];
  225. var nwid = message.data['nwid'];
  226. var requestId = message.data['requestId'];
  227. if ((!peerId)||(!peerId.isValid())||(!fromIpAndPort)||(!nwid)||(nwid.length !== 16)||(!requestId))
  228. return;
  229. var network = null;
  230. var member = null;
  231. var authorized = false;
  232. var v4NeedAssign = false;
  233. var v6NeedAssign = false;
  234. var v4Assignments = [];
  235. var v6Assignments = [];
  236. var ipAssignments = []; // both v4 and v6
  237. async.series([function(next) {
  238. // network lookup
  239. DB.hgetall('zt1:network:'+nwid+':~',function(err,obj) {
  240. network = obj;
  241. return next(err);
  242. });
  243. },function(next) {
  244. // member record lookup, unless public network
  245. if ((!network)||(!('nwid' in network))||(network['nwid'] !== nwid))
  246. return next(null);
  247. var memberKey = 'zt1:network:'+nwid+':member:'+peerId.address()+':~';
  248. DB.hgetall(memberKey,function(err,obj) {
  249. if (err)
  250. return next(err);
  251. if (obj) {
  252. // Update existing member record with new last seen time, etc.
  253. member = obj;
  254. authorized = (ztDbTrue(network['private']) || ztDbTrue(member['authorized']));
  255. DB.hmset(memberKey,{
  256. 'lastSeen': Date.now(),
  257. 'lastAt': fromIpAndPort,
  258. 'clientVersion': (clientVersion) ? clientVersion : '?.?.?',
  259. 'clientOs': (clientOs) ? clientOs : '?'
  260. },next);
  261. } else {
  262. // Add member record to network for newly seen peer
  263. authorized = ztDbTrue(network['private']) ? false : true; // public networks authorize everyone by default
  264. var now = Date.now().toString();
  265. member = {
  266. 'id': peerId.address(),
  267. 'nwid': nwid,
  268. 'authorized': authorized ? '1' : '0',
  269. 'identity': peerId.toString(),
  270. 'firstSeen': now,
  271. 'lastSeen': now,
  272. 'lastAt': fromIpAndPort,
  273. 'clientVersion': (message.data['clientVersion']) ? message.data['clientVersion'] : '?.?.?',
  274. 'clientOs': (message.data['clientOs']) ? message.data['clientOs'] : '?'
  275. };
  276. DB.hmset(memberKey,member,next);
  277. }
  278. });
  279. },function(next) {
  280. // Figure out which IP address auto-assignments we need to look up or make
  281. if (!authorized)
  282. return next(null);
  283. v4NeedAssign = (network['v4AssignMode'] === 'zt');
  284. v6NeedAssign = (network['v6AssignMode'] === 'zt');
  285. var ipa = csvToArray(member['ipAssignments']);
  286. for(var i=0;i<ipa.length;++i) {
  287. if (ipa[i])
  288. ipAssignments.push(ipa[i]);
  289. if ((ipa[i].indexOf('.') > 0)&&(v4NeedAssign)) {
  290. v4Assignments.push(ipa[i]);
  291. } else if ((ipa[i].indexOf(':') > 0)&&(v6NeedAssign)) {
  292. v6Assignments.push(ipa[i]);
  293. }
  294. }
  295. return next(null);
  296. },function(next) {
  297. // assign IPv4 if needed
  298. if ((!authorized)||(!v4NeedAssign)||(v4Assignments.length > 0))
  299. return next(null);
  300. var ipAssignmentAttempts = 0;
  301. var v4pool = network['v4AssignPool']; // technically csv but only one netblock currently supported
  302. var peerAddress = peerId.address();
  303. var network = 0;
  304. var netmask = 0;
  305. var netmaskBits = 0;
  306. if (v4pool) {
  307. var v4poolSplit = v4Pool.split('/');
  308. if (v4poolSplit.length === 2) {
  309. var networkSplit = v4poolSplit[0].split('.');
  310. if (networkSplit.length === 4) {
  311. network |= (parseInt(networkSplit[0],10) << 24) & 0xff000000;
  312. network |= (parseInt(networkSplit[1],10) << 16) & 0x00ff0000;
  313. network |= (parseInt(networkSplit[2],10) << 8) & 0x0000ff00;
  314. network |= parseInt(networkSplit[3],10) & 0x000000ff;
  315. netmaskBits = parseInt(v4poolSplit[1],10);
  316. if (netmaskBits > 32)
  317. netmaskBits = 32; // sanity check
  318. for(var i=0;i<netmaskBits;++i)
  319. netmask |= (0x80000000 >> i);
  320. netmask &= 0xffffffff;
  321. }
  322. }
  323. }
  324. if ((network === 0)||(netmask === 0xffffffff))
  325. return next(null);
  326. var invmask = netmask ^ 0xffffffff;
  327. var abcd = 0;
  328. var assignment = null;
  329. var ipAssignmentsKey = 'zt1:network:'+nwid+':ipAssignments';
  330. var memberKey = 'zt1:network:'+nwid+':member:'+peerAddress+':~';
  331. async.whilst(
  332. function() { return ((v4Assignments.length === 0)&&(ipAssignmentAttempts < 1000)); },
  333. function(next2) {
  334. ++ipAssignmentAttempts;
  335. // Generate or increment IP address source bits
  336. if (abcd === 0) {
  337. var a = parseInt(peerAddress.substr(2,2),16) & 0xff;
  338. var b = parseInt(peerAddress.substr(4,2),16) & 0xff;
  339. var c = parseInt(peerAddress.substr(6,2),16) & 0xff;
  340. var d = parseInt(peerAddress.substr(8,2),16) & 0xff;
  341. abcd = (a << 24) | (b << 16) | (c << 8) | d;
  342. } else ++abcd;
  343. if ((abcd & 0xff) === 0)
  344. abcd |= 1;
  345. abcd &= 0xffffffff;
  346. // Derive an IP to test and generate assignment ip/bits string
  347. var ip = (abcd & invmask) | (network & netmask);
  348. assignment = ((ip >> 24) & 0xff).toString(10) + '.' + ((ip >> 16) & 0xff).toString(10) + '.' + ((ip >> 8) & 0xff).toString(10) + '.' + (ip & 0xff).toString(10) + '/' + netmaskBits.toString(10);
  349. // Check :ipAssignments to see if this IP is already taken
  350. DB.hget(ipAssignmentsKey,assignment,function(err,value) {
  351. if (err)
  352. return next2(err);
  353. // IP is already taken, try again via async.whilst()
  354. if ((value)&&(value !== peerAddress))
  355. return next2(null); // if someone's already got this IP, keep looking
  356. v4Assignments.push(assignment);
  357. ipAssignments.push(assignment);
  358. // Save assignment to :ipAssignments hash
  359. DB.hset(ipAssignmentsKey,assignment,peerAddress,function(err) {
  360. if (err)
  361. return next2(err);
  362. // Save updated CSV list of assignments to member record
  363. var ipacsv = ipAssignments.join(',');
  364. member['ipAssignments'] = ipacsv;
  365. DB.hset(memberKey,'ipAssignments',ipacsv,next2);
  366. });
  367. });
  368. },
  369. next
  370. );
  371. },function(next) {
  372. // assign IPv6 if needed -- TODO
  373. if ((!authorized)||(!v6NeedAssign)||(v6Assignments.length > 0))
  374. return next(null);
  375. return next(null);
  376. }],function(err) {
  377. if (err) {
  378. console.log('error composing response for '+peerId.address()+': '+err);
  379. return;
  380. }
  381. var response = new Dictionary();
  382. response.data['peer'] = peerId.address();
  383. response.data['nwid'] = nwid;
  384. response.data['type'] = 'netconf-response';
  385. response.data['requestId'] = requestId;
  386. if (authorized) {
  387. var certificateOfMembership = null;
  388. var privateNetwork = ztDbTrue(network['private']);
  389. async.series([function(next) {
  390. // Generate certificate of membership if necessary
  391. if (privateNetwork) {
  392. generateCertificateOfMembership(nwid,peerId.address(),function(cert,exitCode) {
  393. if (cert) {
  394. certificateOfMembership = cert;
  395. return next(null);
  396. } else return next(new Error('zerotier-idtool returned '+exitCode));
  397. });
  398. } else return next(null);
  399. }],function(err) {
  400. if (err) {
  401. console.error('unable to generate certificate for peer '+peerId.address()+' on network '+nwid+': '+err);
  402. response.data['error'] = 'ACCESS_DENIED'; // unable to generate certificate
  403. } else {
  404. var netconf = new Dictionary();
  405. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = network['etherTypes'];
  406. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = nwid;
  407. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = Date.now().toString(16);
  408. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = peerId.address();
  409. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS] = 0;
  410. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH] = 0;
  411. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = '';
  412. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = privateNetwork ? '1' : '0';
  413. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NAME] = network['name'];
  414. if (network['desc'])
  415. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_DESC] = network['desc'];
  416. if ((v4NeedAssign)&&(v4Assignments.length > 0))
  417. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4Assignments.join(',');
  418. if ((v6NeedAssign)&&(v6Assignments.length > 0))
  419. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = v6Assignments.join(',');
  420. if (certificateOfMembership !== null)
  421. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = certificateOfMembership;
  422. response.data['netconf'] = netconf.toString();
  423. }
  424. process.stdout.write(response.toString()+'\n');
  425. });
  426. } else {
  427. // Peer not authorized to join network
  428. response.data['error'] = 'ACCESS_DENIED';
  429. process.stdout.write(response.toString()+'\n');
  430. }
  431. });
  432. }
  433. function handleMessage(dictStr)
  434. {
  435. var message = new Dictionary(dictStr);
  436. if (!('type' in message.data)) {
  437. console.error('ignored message without request type field');
  438. return;
  439. } else if (message.data['type'] === 'netconf-init') {
  440. doNetconfInit(message);
  441. } else if (message.data['type'] === 'netconf-request') {
  442. doNetconfRequest(message);
  443. } else {
  444. console.error('ignored unrecognized message type: '+message.data['type']);
  445. }
  446. };
  447. //
  448. // Read stream of double-CR-terminated dictionaries from stdin until close/EOF
  449. //
  450. var stdinReadBuffer = '';
  451. process.stdin.on('readable',function() {
  452. var chunk = process.stdin.read();
  453. if (chunk)
  454. stdinReadBuffer += chunk;
  455. if ((stdinReadBuffer.length >= 2)&&(stdinReadBuffer.substr(stdinReadBuffer.length - 2) === '\n\n')) {
  456. handleMessage(stdinReadBuffer);
  457. stdinReadBuffer = '';
  458. }
  459. });
  460. process.stdin.on('end',function() {
  461. process.exit(0);
  462. });
  463. process.stdin.on('close',function() {
  464. process.exit(0);
  465. });
  466. process.stdin.on('error',function() {
  467. process.exit(0);
  468. });
  469. // Tell ZeroTier One that the service is running, solicit netconf-init
  470. process.stdout.write('type=ready\n\n');