Source: net/netaddress.js

  1. /*!
  2. * netaddress.js - network address object for hsd
  3. * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License).
  4. * https://github.com/handshake-org/hsd
  5. */
  6. 'use strict';
  7. const assert = require('bsert');
  8. const bio = require('bufio');
  9. const IP = require('binet');
  10. const base32 = require('bcrypto/lib/encoding/base32');
  11. const Network = require('../protocol/network');
  12. const util = require('../utils/util');
  13. const common = require('./common');
  14. /** @typedef {import('net').Socket} NetSocket */
  15. /** @typedef {import('../types').NetworkType} NetworkType */
  16. /** @typedef {import('../types').BufioWriter} BufioWriter */
  17. /*
  18. * Constants
  19. */
  20. const ZERO_KEY = Buffer.alloc(33, 0x00);
  21. /**
  22. * Net Address
  23. * Represents a network address.
  24. * @alias module:net.NetAddress
  25. * @property {Host} host
  26. * @property {Number} port
  27. * @property {Number} services
  28. * @property {Number} time
  29. */
  30. class NetAddress extends bio.Struct {
  31. /**
  32. * Create a network address.
  33. * @constructor
  34. * @param {Object} [options]
  35. * @param {Number?} options.time - Timestamp.
  36. * @param {Number?} options.services - Service bits.
  37. * @param {String?} options.host - IP address (IPv6 or IPv4).
  38. * @param {Number?} options.port - Port.
  39. */
  40. constructor(options) {
  41. super();
  42. this.host = '0.0.0.0';
  43. this.port = 0;
  44. this.services = 0;
  45. this.time = 0;
  46. this.hostname = '0.0.0.0:0';
  47. this.raw = IP.ZERO_IPV4;
  48. this.key = ZERO_KEY;
  49. if (options)
  50. this.fromOptions(options);
  51. }
  52. /**
  53. * Inject properties from options object.
  54. * @param {Object} options
  55. */
  56. fromOptions(options) {
  57. assert(typeof options.host === 'string',
  58. 'NetAddress requires host string.');
  59. assert(typeof options.port === 'number',
  60. 'NetAddress requires port number.');
  61. assert(options.port >= 0 && options.port <= 0xffff,
  62. 'port number is incorrect.');
  63. this.raw = IP.toBuffer(options.host);
  64. this.host = IP.toString(this.raw);
  65. this.port = options.port;
  66. if (options.services) {
  67. assert(typeof options.services === 'number',
  68. 'services must be a number.');
  69. this.services = options.services;
  70. }
  71. if (options.time) {
  72. assert(typeof options.time === 'number',
  73. 'time must be a number.');
  74. this.time = options.time;
  75. }
  76. if (options.key) {
  77. assert(Buffer.isBuffer(options.key), 'key must be a buffer.');
  78. assert(options.key.length === 33, 'key length must be 33.');
  79. this.key = options.key;
  80. }
  81. this.hostname = IP.toHostname(this.host, this.port, this.key);
  82. return this;
  83. }
  84. /**
  85. * Test whether required services are available.
  86. * @param {Number} services
  87. * @returns {Boolean}
  88. */
  89. hasServices(services) {
  90. return (this.services & services) === services;
  91. }
  92. /**
  93. * Test whether the address is IPv4.
  94. * @returns {Boolean}
  95. */
  96. isIPv4() {
  97. return IP.isIPv4(this.raw);
  98. }
  99. /**
  100. * Test whether the address is IPv6.
  101. * @returns {Boolean}
  102. */
  103. isIPv6() {
  104. return IP.isIPv6(this.raw);
  105. }
  106. /**
  107. * Test whether the address is RFC3964.
  108. * @returns {Boolean}
  109. */
  110. isRFC3964() {
  111. return IP.isRFC3964(this.raw);
  112. }
  113. /**
  114. * Test whether the address is RFC4380.
  115. * @returns {Boolean}
  116. */
  117. isRFC4380() {
  118. return IP.isRFC4380(this.raw);
  119. }
  120. /**
  121. * Test whether the address is RFC6052.
  122. * @returns {Boolean}
  123. */
  124. isRFC6052() {
  125. return IP.isRFC6052(this.raw);
  126. }
  127. /**
  128. * Test whether the address is RFC6145.
  129. * @returns {Boolean}
  130. */
  131. isRFC6145() {
  132. return IP.isRFC6145(this.raw);
  133. }
  134. /**
  135. * Test whether the host is null.
  136. * @returns {Boolean}
  137. */
  138. isNull() {
  139. return IP.isNull(this.raw);
  140. }
  141. /**
  142. * Test whether the host is a local address.
  143. * @returns {Boolean}
  144. */
  145. isLocal() {
  146. return IP.isLocal(this.raw);
  147. }
  148. /**
  149. * Test whether the host is valid.
  150. * @returns {Boolean}
  151. */
  152. isValid() {
  153. return IP.isValid(this.raw);
  154. }
  155. /**
  156. * Test whether the host is routable.
  157. * @returns {Boolean}
  158. */
  159. isRoutable() {
  160. return IP.isRoutable(this.raw);
  161. }
  162. /**
  163. * Test whether the host is an onion address.
  164. * @returns {Boolean}
  165. */
  166. isOnion() {
  167. return IP.isOnion(this.raw);
  168. }
  169. /**
  170. * Test whether the peer has a key.
  171. * @returns {Boolean}
  172. */
  173. hasKey() {
  174. return !this.key.equals(ZERO_KEY);
  175. }
  176. /**
  177. * Compare against another network address.
  178. * @param {NetAddress} addr
  179. * @returns {Boolean}
  180. */
  181. equal(addr) {
  182. return this.compare(addr) === 0;
  183. }
  184. /**
  185. * Compare against another network address.
  186. * @param {NetAddress} addr
  187. * @returns {Number}
  188. */
  189. compare(addr) {
  190. const cmp = this.raw.compare(addr.raw);
  191. if (cmp !== 0)
  192. return cmp;
  193. return this.port - addr.port;
  194. }
  195. /**
  196. * Get reachable score to destination.
  197. * @param {NetAddress} dest
  198. * @returns {Number}
  199. */
  200. getReachability(dest) {
  201. return IP.getReachability(this.raw, dest.raw);
  202. }
  203. /**
  204. * Get the canonical identifier of our network group
  205. * @returns {Buffer}
  206. */
  207. getGroup() {
  208. return groupKey(this);
  209. }
  210. /**
  211. * Set null host.
  212. */
  213. setNull() {
  214. this.raw = IP.ZERO_IPV4;
  215. this.host = '0.0.0.0';
  216. this.key = ZERO_KEY;
  217. this.hostname = IP.toHostname(this.host, this.port, this.key);
  218. }
  219. /**
  220. * Set host.
  221. * @param {String} host
  222. */
  223. setHost(host) {
  224. this.raw = IP.toBuffer(host);
  225. this.host = IP.toString(this.raw);
  226. this.hostname = IP.toHostname(this.host, this.port, this.key);
  227. }
  228. /**
  229. * Set port.
  230. * @param {Number} port
  231. */
  232. setPort(port) {
  233. assert(port >= 0 && port <= 0xffff);
  234. this.port = port;
  235. this.hostname = IP.toHostname(this.host, this.port, this.key);
  236. }
  237. /**
  238. * Set key.
  239. * @param {Buffer} key
  240. */
  241. setKey(key) {
  242. if (key == null)
  243. key = ZERO_KEY;
  244. assert(Buffer.isBuffer(key) && key.length === 33);
  245. this.key = key;
  246. this.hostname = IP.toHostname(this.host, this.port, this.key);
  247. }
  248. /**
  249. * Get key.
  250. * @param {String} enc
  251. * @returns {String|Buffer}
  252. */
  253. getKey(enc) {
  254. if (!this.hasKey())
  255. return null;
  256. if (enc === 'base32')
  257. return base32.encode(this.key);
  258. if (enc === 'hex')
  259. return this.key.toString('hex');
  260. return this.key;
  261. }
  262. /**
  263. * Inject properties from host, port, and network.
  264. * @param {String} host
  265. * @param {Number} port
  266. * @param {Buffer} [key]
  267. * @param {(Network|NetworkType)?} [network]
  268. */
  269. fromHost(host, port, key, network) {
  270. network = Network.get(network);
  271. assert(port >= 0 && port <= 0xffff);
  272. assert(!key || Buffer.isBuffer(key));
  273. assert(!key || key.length === 33);
  274. this.raw = IP.toBuffer(host);
  275. this.host = IP.toString(this.raw);
  276. this.port = port;
  277. this.services = NetAddress.DEFAULT_SERVICES;
  278. this.time = network.now();
  279. this.key = key || ZERO_KEY;
  280. this.hostname = IP.toHostname(this.host, this.port, this.key);
  281. return this;
  282. }
  283. /**
  284. * Instantiate a network address
  285. * from a host and port.
  286. * @param {String} host
  287. * @param {Number} port
  288. * @param {Buffer} [key]
  289. * @param {(Network|NetworkType)?} [network]
  290. * @returns {NetAddress}
  291. */
  292. static fromHost(host, port, key, network) {
  293. return new this().fromHost(host, port, key, network);
  294. }
  295. /**
  296. * Inject properties from hostname and network.
  297. * @param {String} hostname
  298. * @param {(Network|NetworkType)?} [network]
  299. */
  300. fromHostname(hostname, network) {
  301. network = Network.get(network);
  302. const addr = IP.fromHostname(hostname);
  303. if (addr.port === 0)
  304. addr.port = addr.key ? network.brontidePort : network.port;
  305. return this.fromHost(addr.host, addr.port, addr.key, network);
  306. }
  307. /**
  308. * Instantiate a network address
  309. * from a hostname (i.e. 127.0.0.1:8333).
  310. * @param {String} hostname
  311. * @param {(Network|NetworkType)?} [network]
  312. * @returns {NetAddress}
  313. */
  314. static fromHostname(hostname, network) {
  315. return new this().fromHostname(hostname, network);
  316. }
  317. /**
  318. * Inject properties from socket.
  319. * @param {NetSocket} socket
  320. * @param {(Network|NetworkType)?} [network]
  321. */
  322. fromSocket(socket, network) {
  323. const host = socket.remoteAddress;
  324. const port = socket.remotePort;
  325. assert(typeof host === 'string');
  326. assert(typeof port === 'number');
  327. return this.fromHost(IP.normalize(host), port, null, network);
  328. }
  329. /**
  330. * Instantiate a network address
  331. * from a socket.
  332. * @param {NetSocket} socket
  333. * @param {(Network|NetworkType)?} [network]
  334. * @returns {NetAddress}
  335. */
  336. static fromSocket(socket, network) {
  337. return new this().fromSocket(socket, network);
  338. }
  339. /**
  340. * Calculate serialization size of address.
  341. * @returns {Number}
  342. */
  343. getSize() {
  344. return 88;
  345. }
  346. /**
  347. * Write network address to a buffer writer.
  348. * @param {BufioWriter} bw
  349. * @returns {BufioWriter}
  350. */
  351. write(bw) {
  352. bw.writeU64(this.time);
  353. bw.writeU32(this.services);
  354. bw.writeU32(0);
  355. bw.writeU8(0);
  356. bw.writeBytes(this.raw);
  357. bw.fill(0, 20); // reserved
  358. bw.writeU16(this.port);
  359. bw.writeBytes(this.key);
  360. return bw;
  361. }
  362. /**
  363. * Inject properties from buffer reader.
  364. * @param {bio.BufferReader} br
  365. */
  366. read(br) {
  367. this.time = br.readU64();
  368. this.services = br.readU32();
  369. // Note: hi service bits
  370. // are currently unused.
  371. br.readU32();
  372. if (br.readU8() === 0) {
  373. this.raw = br.readBytes(16);
  374. br.seek(20);
  375. } else {
  376. this.raw = Buffer.alloc(16, 0x00);
  377. br.seek(36);
  378. }
  379. this.port = br.readU16();
  380. this.key = br.readBytes(33);
  381. this.host = IP.toString(this.raw);
  382. this.hostname = IP.toHostname(this.host, this.port, this.key);
  383. return this;
  384. }
  385. /**
  386. * Convert net address to json-friendly object.
  387. * @returns {Object}
  388. */
  389. getJSON() {
  390. return {
  391. host: this.host,
  392. port: this.port,
  393. services: this.services,
  394. time: this.time,
  395. key: this.key.toString('hex')
  396. };
  397. }
  398. /**
  399. * Inject properties from json object.
  400. * @param {Object} json
  401. * @returns {this}
  402. */
  403. fromJSON(json) {
  404. assert((json.port & 0xffff) === json.port);
  405. assert((json.services >>> 0) === json.services);
  406. assert((json.time >>> 0) === json.time);
  407. assert(typeof json.key === 'string');
  408. this.raw = IP.toBuffer(json.host);
  409. this.host = json.host;
  410. this.port = json.port;
  411. this.services = json.services;
  412. this.time = json.time;
  413. this.key = Buffer.from(json.key, 'hex');
  414. this.hostname = IP.toHostname(this.host, this.port, this.key);
  415. return this;
  416. }
  417. /**
  418. * Inspect the network address.
  419. * @returns {Object}
  420. */
  421. format() {
  422. return '<NetAddress:'
  423. + ` hostname=${this.hostname}`
  424. + ` services=${this.services.toString(2)}`
  425. + ` date=${util.date(this.time)}`
  426. + '>';
  427. }
  428. }
  429. /**
  430. * Default services for
  431. * unknown outbound peers.
  432. * @const {Number}
  433. * @default
  434. */
  435. NetAddress.DEFAULT_SERVICES = 0
  436. | common.services.NETWORK
  437. | common.services.BLOOM;
  438. /*
  439. * Helpers
  440. */
  441. /**
  442. * @param {NetAddress} addr
  443. * @returns {Buffer}
  444. */
  445. function groupKey(addr) {
  446. const raw = addr.raw;
  447. // See: https://github.com/bitcoin/bitcoin/blob/e258ce7/src/netaddress.cpp#L413
  448. // Todo: Use IP->ASN mapping, see:
  449. // https://github.com/bitcoin/bitcoin/blob/adea5e1/src/addrman.h#L274
  450. let type = IP.networks.INET6; // NET_IPV6
  451. let start = 0;
  452. let bits = 16;
  453. let i = 0;
  454. if (addr.isLocal()) {
  455. type = 255; // NET_LOCAL
  456. bits = 0;
  457. } else if (!addr.isRoutable()) {
  458. type = IP.networks.NONE; // NET_UNROUTABLE
  459. bits = 0;
  460. } else if (addr.isIPv4() || addr.isRFC6145() || addr.isRFC6052()) {
  461. type = IP.networks.INET4; // NET_IPV4
  462. start = 12;
  463. } else if (addr.isRFC3964()) {
  464. type = IP.networks.INET4; // NET_IPV4
  465. start = 2;
  466. } else if (addr.isRFC4380()) {
  467. const buf = Buffer.alloc(3);
  468. buf[0] = IP.networks.INET4; // NET_IPV4
  469. buf[1] = raw[12] ^ 0xff;
  470. buf[2] = raw[13] ^ 0xff;
  471. return buf;
  472. } else if (addr.isOnion()) {
  473. type = IP.networks.ONION; // NET_ONION
  474. start = 6;
  475. bits = 4;
  476. } else if (raw[0] === 0x20
  477. && raw[1] === 0x01
  478. && raw[2] === 0x04
  479. && raw[3] === 0x70) {
  480. bits = 36;
  481. } else {
  482. bits = 32;
  483. }
  484. const out = Buffer.alloc(1 + ((bits + 7) >>> 3));
  485. out[i++] = type;
  486. while (bits >= 8) {
  487. out[i++] = raw[start++];
  488. bits -= 8;
  489. }
  490. if (bits > 0)
  491. out[i++] = raw[start] | ((1 << (8 - bits)) - 1);
  492. assert(i === out.length);
  493. return out;
  494. }
  495. /*
  496. * Expose
  497. */
  498. module.exports = NetAddress;