index.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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_NETCONF_SERVICE_VERSION = "ncver";
  29. var ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES = "et";
  30. var ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID = "nwid";
  31. var ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP = "ts";
  32. var ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO = "id";
  33. var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS = "mpb";
  34. var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH = "md";
  35. var ZT_NETWORKCONFIG_DICT_KEY_ARP_CACHE_TTL = "cARP";
  36. var ZT_NETWORKCONFIG_DICT_KEY_NDP_CACHE_TTL = "cNDP";
  37. var ZT_NETWORKCONFIG_DICT_KEY_EMULATE_ARP = "eARP";
  38. var ZT_NETWORKCONFIG_DICT_KEY_EMULATE_NDP = "eNDP";
  39. var ZT_NETWORKCONFIG_DICT_KEY_IS_OPEN = "o";
  40. var ZT_NETWORKCONFIG_DICT_KEY_NAME = "name";
  41. var ZT_NETWORKCONFIG_DICT_KEY_DESC = "desc";
  42. var ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC = "v4s";
  43. var ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC = "v6s";
  44. var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES = "mr";
  45. var ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP = "com";
  46. // Path to zerotier-idtool binary, invoked to enerate certificates of membership
  47. var ZEROTIER_IDTOOL = '/usr/local/bin/zerotier-idtool';
  48. // Connect to redis, assuming database 0 and no auth (for now)
  49. var redis = require('redis');
  50. var DB = redis.createClient();
  51. DB.on("error",function(err) {
  52. console.error('redis query error: '+err);
  53. });
  54. // Global variables -- these are initialized on startup or netconf-init message
  55. var netconfSigningIdentity = null; // identity of netconf master, with private key portion
  56. function ztDbTrue(v) { return ((v === '1')||(v === 'true')||(v > 0)); }
  57. function csvToArray(csv) { return (((typeof csv === 'string')&&(csv.length > 0)) ? csv.split(',') : []); }
  58. function arrayToCsv(a) { return ((Array.isArray(a)) ? ((a.length > 0) ? a.join(',') : '') : (((a !== null)&&(typeof a !== 'undefined')) ? a.toString() : '')); }
  59. //
  60. // ZeroTier One Dictionary -- encoding-compatible with Dictionary in C++ code base
  61. //
  62. function Dictionary(fromStr)
  63. {
  64. var thiz = this;
  65. this.data = {};
  66. this._esc = function(data) {
  67. var es = '';
  68. for(var i=0;i<data.length;++i) {
  69. var c = data.charAt(i);
  70. switch(c) {
  71. case '\0': es += '\\0'; break;
  72. case '\r': es += '\\r'; break;
  73. case '\n': es += '\\n'; break;
  74. case '\\': es += '\\\\'; break;
  75. case '=': es += '\\='; break;
  76. default: es += c; break;
  77. }
  78. }
  79. return es;
  80. };
  81. this._unesc = function(s) {
  82. if (typeof s !== 'string')
  83. return '';
  84. var uns = '';
  85. var escapeState = false;
  86. for(var i=0;i<s.length;++i) {
  87. var c = s.charAt(i);
  88. if (escapeState) {
  89. escapeState = false;
  90. switch(c) {
  91. case '0': uns += '\0'; break;
  92. case 'r': uns += '\r'; break;
  93. case 'n': uns += '\n'; break;
  94. default: uns += c; break;
  95. }
  96. } else{
  97. if ((c !== '\r')&&(c !== '\n')&&(c !== '\0')) {
  98. if (c === '\\')
  99. escapeState = true;
  100. else uns += c;
  101. }
  102. }
  103. }
  104. return uns;
  105. };
  106. this.toString = function() {
  107. var str = '';
  108. for(var key in thiz.data) {
  109. str += thiz._esc(key);
  110. str += '=';
  111. var value = thiz.data[key];
  112. if (value)
  113. str += thiz._esc(value.toString());
  114. str += '\n';
  115. }
  116. return str;
  117. };
  118. this.fromString = function(str) {
  119. thiz.data = {};
  120. if (typeof str !== 'string')
  121. return thiz;
  122. var lines = str.split('\n');
  123. for(var l=0;l<lines.length;++l) {
  124. var escapeState = false;
  125. var eqAt = 0;
  126. for(;eqAt<lines[l].length;++eqAt) {
  127. var c = lines[l].charAt(eqAt);
  128. if (escapeState)
  129. escapeState = false;
  130. else if (c === '\\')
  131. escapeState = true;
  132. else if (c === '=')
  133. break;
  134. }
  135. var k = thiz._unesc(lines[l].substr(0,eqAt));
  136. ++eqAt;
  137. if ((k)&&(k.length > 0))
  138. thiz.data[k] = thiz._unesc((eqAt < lines[l].length) ? lines[l].substr(eqAt) : '');
  139. }
  140. return thiz;
  141. };
  142. if ((typeof fromStr === 'string')&&(fromStr.length > 0))
  143. thiz.fromString(fromStr);
  144. };
  145. //
  146. // Identity implementation using zerotier-idtool as subprocess to do actual crypto work
  147. //
  148. function Identity(idstr)
  149. {
  150. var thiz = this;
  151. this.str = '';
  152. this.fields = [];
  153. this.toString = function() {
  154. return thiz.str;
  155. };
  156. this.address = function() {
  157. return ((thiz.fields.length > 0) ? thiz.fields[0] : '0000000000');
  158. };
  159. this.fromString = function(str) {
  160. thiz.str = '';
  161. thiz.fields = [];
  162. if (typeof str !== 'string')
  163. return;
  164. for(var i=0;i<str.length;++i) {
  165. if ("0123456789abcdef:ABCDEF".indexOf(str.charAt(i)) < 0)
  166. return; // invalid character in identity
  167. }
  168. var fields = str.split(':');
  169. if ((fields.length < 3)||(fields[0].length !== 10)||(fields[1] !== '0'))
  170. return;
  171. thiz.fields = fields;
  172. };
  173. this.isValid = function() {
  174. if ((thiz.fields.length < 3)||(thiz.fields[0].length !== 10)||(thiz.fields[1] !== '0'))
  175. return true;
  176. return false;
  177. };
  178. this.hasPrivate = function() {
  179. return ((thiz.isValid())&&(thiz.fields.length >= 4));
  180. };
  181. if (typeof idstr === 'string')
  182. thiz.fromString(idstr);
  183. };
  184. //
  185. // Message handler for messages over ZeroTier One service bus
  186. //
  187. function handleMessage(dictStr)
  188. {
  189. var message = new Dictionary(dictStr);
  190. if (!('type' in message.data)) {
  191. console.error('ignored message without request type field');
  192. return;
  193. }
  194. if (message.data['type'] === 'netconf-init') {
  195. netconfSigningIdentity = new Identity(message.data['netconfId']);
  196. if (!netconfSigningIdentity.hasPrivate()) {
  197. netconfSigningIdentity = null;
  198. console.error('got invalid netconf signing identity');
  199. }
  200. } else if (message.data['type'] === 'netconf-request') {
  201. if ((!netconfSigningIdentity)||(!netconfSigningIdentity.hasPrivate())) {
  202. console.error('got netconf-request before netconf-init, ignored');
  203. return;
  204. }
  205. // Get required fields
  206. var peerId = new Identity(message.data['peerId']);
  207. var fromIpAndPort = message.data['from'];
  208. var nwid = message.data['nwid'];
  209. var requestId = message.data['requestId'];
  210. if ((!peerId)||(!peerId.isValid())||(!fromIpAndPort)||(!nwid)||(nwid.length !== 16)||(!requestId))
  211. return;
  212. // Get optional fields
  213. var meta = new Dictionary(message.data['meta']);
  214. var clientVersion = message.data['clientVersion'];
  215. var clientOs = message.data['clientOs'];
  216. var network = null;
  217. var member = null;
  218. var authorized = false;
  219. var v4NeedAssign = false;
  220. var v6NeedAssign = false;
  221. var v4Assignments = [];
  222. var v6Assignments = [];
  223. async.series([function(next) { // network lookup
  224. DB.hgetall('zt1:network:'+nwid+':~',function(err,obj) {
  225. network = obj;
  226. return next(err);
  227. });
  228. },function(next) { // member record lookup, unless public network
  229. if ((!network)||(!('nwid' in network)||(network['nwid'] !== nwid))
  230. return next(null);
  231. var memberKey = 'zt1:network:'+nwid+':member:'+peerId.address()+':~';
  232. DB.hgetall(memberKey,function(err,obj) {
  233. if (err)
  234. return next(err);
  235. else if (obj) {
  236. // Update member object
  237. member = obj;
  238. authorized = (ztDbTrue(network['private']) || ztDbTrue(member['authorized']));
  239. DB.hmset(memberKey,{
  240. 'lastSeen': Date.now(),
  241. 'lastAt': fromIpAndPort,
  242. 'clientVersion': (clientVersion) ? clientVersion : '?.?.?',
  243. 'clientOs': (clientOs) ? clientOs : '?'
  244. },next);
  245. } else {
  246. // Add member object for new and unauthorized member
  247. authorized = false;
  248. member = {
  249. 'id': peerId.address(),
  250. 'nwid': nwid,
  251. 'authorized': 0,
  252. 'identity': peerId.toString(),
  253. 'firstSeen': Date.now(),
  254. 'lastSeen': Date.now(),
  255. 'lastAt': fromIpAndPort,
  256. 'clientVersion': (clientVersion) ? clientVersion : '?.?.?',
  257. 'clientOs': (clientOs) ? clientOs : '?'
  258. };
  259. DB.hmset(memberKey,member,next);
  260. }
  261. });
  262. },function(next) { // IP address auto-assignment, if needed
  263. if (!authorized)
  264. return next(null);
  265. v4NeedAssign = (network['v4AssignMode'] === 'zt');
  266. v6NeedAssign = (network['v6AssignMode'] === 'zt');
  267. var ipa = csvToArray(member['ipAssignments']);
  268. for(var i=0;i<ipa.length;++i) {
  269. if ((ipa[i].indexOf('.') > 0)&&(v4NeedAssign))
  270. v4Assignments.push(ipa[i]);
  271. else if ((ipa[i].indexOf(':') > 0)&&(v6NeedAssign))
  272. v6Assignments.push(ipa[i]);
  273. }
  274. return next(null);
  275. },function(next) { // assign IPv4 if needed
  276. if ((!authorized)||(!v4NeedAssign))
  277. return next(null);
  278. var ipAssignmentAttempts = 0; // for sanity-checking
  279. var v4pool = network['v4AssignPool'];
  280. var ztaddr = peerId.address();
  281. var network = 0;
  282. var netmask = 0;
  283. var netmaskBits = 0;
  284. if (v4pool) {
  285. var v4poolSplit = v4Pool.split('/');
  286. if (v4poolSplit.length === 2) {
  287. var networkSplit = v4poolSplit[0].split('.');
  288. if (networkSplit.length === 4) {
  289. network |= (parseInt(networkSplit[0],10) << 24) & 0xff000000;
  290. network |= (parseInt(networkSplit[1],10) << 16) & 0x00ff0000;
  291. network |= (parseInt(networkSplit[2],10) << 8) & 0x0000ff00;
  292. network |= parseInt(networkSplit[3],10) & 0x000000ff;
  293. netmaskBits = parseInt(v4poolSplit[1],10);
  294. if (netmaskBits > 32)
  295. netmaskBits = 32; // sanity check
  296. for(var i=0;i<netmaskBits;++i)
  297. netmask |= (0x80000000 >> i);
  298. }
  299. }
  300. }
  301. var invmask = netmask ^ 0xffffffff;
  302. var abcd = 0;
  303. var assignment = null;
  304. var ipAssignmentsKey = 'zt1:network:'+nwid+':ipAssignments';
  305. var memberKey = 'zt1:network:'+nwid+':member:'+ztaddr+':~';
  306. async.whilst(
  307. function() { return ((v4NeedAssign)&&(v4Assignments.length === 0)&&(network !== 0)&&(netmask !== 0xffffffff)&&(ipAssignmentAttempts < 1000)); },
  308. function(next2) {
  309. ++ipAssignmentAttempts;
  310. // Generate or increment IP address
  311. if (abcd === 0) {
  312. var a = parseInt(ztaddr.substr(2,2),16) & 0xff;
  313. var b = parseInt(ztaddr.substr(4,2),16) & 0xff;
  314. var c = parseInt(ztaddr.substr(6,2),16) & 0xff;
  315. var d = parseInt(ztaddr.substr(8,2),16) & 0xff;
  316. abcd = (a << 24) | (b << 16) | (c << 8) | d;
  317. } else ++abcd;
  318. if ((abcd & 0xff) === 0)
  319. abcd |= 1;
  320. // Derive an IP to test and generate assignment ip/bits string
  321. var ip = (abcd & invmask) | (network & netmask);
  322. assignment = ((ip >> 24) & 0xff).toString(10) + '.' + ((ip >> 16) & 0xff).toString(10) + '.' + ((ip >> 8) & 0xff).toString(10) + '.' + (ip & 0xff).toString(10) + '/' + netmaskBits.toString(10);
  323. DB.hget(ipAssignmentsKey,assignment,function(err,value) {
  324. if (err)
  325. return next2(err);
  326. if ((value)&&(value !== ztaddr))
  327. return next2(null); // if someone's already got this IP, keep looking
  328. v4Assignments.push(assignment);
  329. // Save assignment to :ipAssignments hash
  330. DB.hset(ipAssignmentsKey,assignment,ztaddr,function(err) {
  331. if (err)
  332. return next2(err);
  333. // Save updated CSV list of assignments to member record
  334. var ipAssignments = member['ipAssignments'];
  335. if (!ipAssignments)
  336. ipAssignments = '';
  337. if (ipAssignments.length > 0)
  338. ipAssignments += ',';
  339. ipAssignments += assignment;
  340. member['ipAssignments'] = ipAssignments;
  341. DB.hset(memberKey,'ipAssignments',ipAssignments,next2);
  342. });
  343. });
  344. },
  345. next
  346. );
  347. },function(next) { // assign IPv6 if needed -- TODO
  348. if ((!authorized)||(!v6NeedAssign))
  349. return next(null);
  350. return next(null);
  351. }],function(err) {
  352. if (err) {
  353. console.log('error composing response for '+peerId.address()+': '+err);
  354. return;
  355. } else if (authorized) {
  356. // TODO: COM!!!
  357. var certificateOfMembership = null;
  358. var netconf = new Dictionary();
  359. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NETCONF_SERVICE_VERSION] = '0.0.0';
  360. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = network['etherTypes'];
  361. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = nwid;
  362. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = Date.now().toString();
  363. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = peerId.address();
  364. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS] = 0;
  365. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH] = 0;
  366. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = '';
  367. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ARP_CACHE_TTL] = 0;
  368. //netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NDP_CACHE_TTL] = 0;
  369. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_EMULATE_ARP] = '0';
  370. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_EMULATE_NDP] = '0';
  371. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IS_OPEN] = ztDbTrue(network['private']) ? '0' : '1';
  372. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NAME] = network['name'];
  373. if (network['desc'])
  374. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_DESC] = network['desc'];
  375. if (v4NeedAssign)
  376. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = (v4Assignments.length > 0) ? v4Assignments.join(',') : '';
  377. if (v6NeedAssign)
  378. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = (v6Assignments.length > 0) ? v6Assignments.join(',') : '';
  379. if (certificateOfMembership !== null)
  380. netconf.data[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = certificateOfMembership;
  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. response.data['netconf'] = netconf.toString();
  387. process.stdout.write(response.toString()+'\n');
  388. return;
  389. } else {
  390. }
  391. });
  392. } else {
  393. console.error('ignored unrecognized message type: '+message.data['type']);
  394. }
  395. };
  396. //
  397. // Read stream of double-CR-terminated dictionaries from stdin until close/EOF
  398. //
  399. var stdinReadBuffer = '';
  400. process.stdin.on('readable',function() {
  401. var chunk = process.stdin.read();
  402. if (chunk)
  403. stdinReadBuffer += chunk;
  404. if ((stdinReadBuffer.length >= 2)&&(stdinReadBuffer.substr(stdinReadBuffer.length - 2) === '\n\n')) {
  405. handleMessage(stdinReadBuffer);
  406. stdinReadBuffer = '';
  407. }
  408. });
  409. process.stdin.on('end',function() {
  410. process.exit(0);
  411. });
  412. process.stdin.on('close',function() {
  413. process.exit(0);
  414. });
  415. process.stdin.on('error',function() {
  416. process.exit(0);
  417. });
  418. // Tell ZeroTier One that the service is running, solicit netconf-init
  419. process.stdout.write('type=ready\n\n');