ipv6.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. "use strict";
  2. /* eslint-disable prefer-destructuring */
  3. /* eslint-disable no-param-reassign */
  4. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  5. if (k2 === undefined) k2 = k;
  6. var desc = Object.getOwnPropertyDescriptor(m, k);
  7. if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
  8. desc = { enumerable: true, get: function() { return m[k]; } };
  9. }
  10. Object.defineProperty(o, k2, desc);
  11. }) : (function(o, m, k, k2) {
  12. if (k2 === undefined) k2 = k;
  13. o[k2] = m[k];
  14. }));
  15. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  16. Object.defineProperty(o, "default", { enumerable: true, value: v });
  17. }) : function(o, v) {
  18. o["default"] = v;
  19. });
  20. var __importStar = (this && this.__importStar) || function (mod) {
  21. if (mod && mod.__esModule) return mod;
  22. var result = {};
  23. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  24. __setModuleDefault(result, mod);
  25. return result;
  26. };
  27. Object.defineProperty(exports, "__esModule", { value: true });
  28. exports.Address6 = void 0;
  29. const common = __importStar(require("./common"));
  30. const constants4 = __importStar(require("./v4/constants"));
  31. const constants6 = __importStar(require("./v6/constants"));
  32. const helpers = __importStar(require("./v6/helpers"));
  33. const ipv4_1 = require("./ipv4");
  34. const regular_expressions_1 = require("./v6/regular-expressions");
  35. const address_error_1 = require("./address-error");
  36. const jsbn_1 = require("jsbn");
  37. const sprintf_js_1 = require("sprintf-js");
  38. function assert(condition) {
  39. if (!condition) {
  40. throw new Error('Assertion failed.');
  41. }
  42. }
  43. function addCommas(number) {
  44. const r = /(\d+)(\d{3})/;
  45. while (r.test(number)) {
  46. number = number.replace(r, '$1,$2');
  47. }
  48. return number;
  49. }
  50. function spanLeadingZeroes4(n) {
  51. n = n.replace(/^(0{1,})([1-9]+)$/, '<span class="parse-error">$1</span>$2');
  52. n = n.replace(/^(0{1,})(0)$/, '<span class="parse-error">$1</span>$2');
  53. return n;
  54. }
  55. /*
  56. * A helper function to compact an array
  57. */
  58. function compact(address, slice) {
  59. const s1 = [];
  60. const s2 = [];
  61. let i;
  62. for (i = 0; i < address.length; i++) {
  63. if (i < slice[0]) {
  64. s1.push(address[i]);
  65. }
  66. else if (i > slice[1]) {
  67. s2.push(address[i]);
  68. }
  69. }
  70. return s1.concat(['compact']).concat(s2);
  71. }
  72. function paddedHex(octet) {
  73. return (0, sprintf_js_1.sprintf)('%04x', parseInt(octet, 16));
  74. }
  75. function unsignByte(b) {
  76. // eslint-disable-next-line no-bitwise
  77. return b & 0xff;
  78. }
  79. /**
  80. * Represents an IPv6 address
  81. * @class Address6
  82. * @param {string} address - An IPv6 address string
  83. * @param {number} [groups=8] - How many octets to parse
  84. * @example
  85. * var address = new Address6('2001::/32');
  86. */
  87. class Address6 {
  88. constructor(address, optionalGroups) {
  89. this.addressMinusSuffix = '';
  90. this.parsedSubnet = '';
  91. this.subnet = '/128';
  92. this.subnetMask = 128;
  93. this.v4 = false;
  94. this.zone = '';
  95. // #region Attributes
  96. /**
  97. * Returns true if the given address is in the subnet of the current address
  98. * @memberof Address6
  99. * @instance
  100. * @returns {boolean}
  101. */
  102. this.isInSubnet = common.isInSubnet;
  103. /**
  104. * Returns true if the address is correct, false otherwise
  105. * @memberof Address6
  106. * @instance
  107. * @returns {boolean}
  108. */
  109. this.isCorrect = common.isCorrect(constants6.BITS);
  110. if (optionalGroups === undefined) {
  111. this.groups = constants6.GROUPS;
  112. }
  113. else {
  114. this.groups = optionalGroups;
  115. }
  116. this.address = address;
  117. const subnet = constants6.RE_SUBNET_STRING.exec(address);
  118. if (subnet) {
  119. this.parsedSubnet = subnet[0].replace('/', '');
  120. this.subnetMask = parseInt(this.parsedSubnet, 10);
  121. this.subnet = `/${this.subnetMask}`;
  122. if (Number.isNaN(this.subnetMask) ||
  123. this.subnetMask < 0 ||
  124. this.subnetMask > constants6.BITS) {
  125. throw new address_error_1.AddressError('Invalid subnet mask.');
  126. }
  127. address = address.replace(constants6.RE_SUBNET_STRING, '');
  128. }
  129. else if (/\//.test(address)) {
  130. throw new address_error_1.AddressError('Invalid subnet mask.');
  131. }
  132. const zone = constants6.RE_ZONE_STRING.exec(address);
  133. if (zone) {
  134. this.zone = zone[0];
  135. address = address.replace(constants6.RE_ZONE_STRING, '');
  136. }
  137. this.addressMinusSuffix = address;
  138. this.parsedAddress = this.parse(this.addressMinusSuffix);
  139. }
  140. static isValid(address) {
  141. try {
  142. // eslint-disable-next-line no-new
  143. new Address6(address);
  144. return true;
  145. }
  146. catch (e) {
  147. return false;
  148. }
  149. }
  150. /**
  151. * Convert a BigInteger to a v6 address object
  152. * @memberof Address6
  153. * @static
  154. * @param {BigInteger} bigInteger - a BigInteger to convert
  155. * @returns {Address6}
  156. * @example
  157. * var bigInteger = new BigInteger('1000000000000');
  158. * var address = Address6.fromBigInteger(bigInteger);
  159. * address.correctForm(); // '::e8:d4a5:1000'
  160. */
  161. static fromBigInteger(bigInteger) {
  162. const hex = bigInteger.toString(16).padStart(32, '0');
  163. const groups = [];
  164. let i;
  165. for (i = 0; i < constants6.GROUPS; i++) {
  166. groups.push(hex.slice(i * 4, (i + 1) * 4));
  167. }
  168. return new Address6(groups.join(':'));
  169. }
  170. /**
  171. * Convert a URL (with optional port number) to an address object
  172. * @memberof Address6
  173. * @static
  174. * @param {string} url - a URL with optional port number
  175. * @example
  176. * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');
  177. * addressAndPort.address.correctForm(); // 'ffff::'
  178. * addressAndPort.port; // 8080
  179. */
  180. static fromURL(url) {
  181. let host;
  182. let port = null;
  183. let result;
  184. // If we have brackets parse them and find a port
  185. if (url.indexOf('[') !== -1 && url.indexOf(']:') !== -1) {
  186. result = constants6.RE_URL_WITH_PORT.exec(url);
  187. if (result === null) {
  188. return {
  189. error: 'failed to parse address with port',
  190. address: null,
  191. port: null,
  192. };
  193. }
  194. host = result[1];
  195. port = result[2];
  196. // If there's a URL extract the address
  197. }
  198. else if (url.indexOf('/') !== -1) {
  199. // Remove the protocol prefix
  200. url = url.replace(/^[a-z0-9]+:\/\//, '');
  201. // Parse the address
  202. result = constants6.RE_URL.exec(url);
  203. if (result === null) {
  204. return {
  205. error: 'failed to parse address from URL',
  206. address: null,
  207. port: null,
  208. };
  209. }
  210. host = result[1];
  211. // Otherwise just assign the URL to the host and let the library parse it
  212. }
  213. else {
  214. host = url;
  215. }
  216. // If there's a port convert it to an integer
  217. if (port) {
  218. port = parseInt(port, 10);
  219. // squelch out of range ports
  220. if (port < 0 || port > 65536) {
  221. port = null;
  222. }
  223. }
  224. else {
  225. // Standardize `undefined` to `null`
  226. port = null;
  227. }
  228. return {
  229. address: new Address6(host),
  230. port,
  231. };
  232. }
  233. /**
  234. * Create an IPv6-mapped address given an IPv4 address
  235. * @memberof Address6
  236. * @static
  237. * @param {string} address - An IPv4 address string
  238. * @returns {Address6}
  239. * @example
  240. * var address = Address6.fromAddress4('192.168.0.1');
  241. * address.correctForm(); // '::ffff:c0a8:1'
  242. * address.to4in6(); // '::ffff:192.168.0.1'
  243. */
  244. static fromAddress4(address) {
  245. const address4 = new ipv4_1.Address4(address);
  246. const mask6 = constants6.BITS - (constants4.BITS - address4.subnetMask);
  247. return new Address6(`::ffff:${address4.correctForm()}/${mask6}`);
  248. }
  249. /**
  250. * Return an address from ip6.arpa form
  251. * @memberof Address6
  252. * @static
  253. * @param {string} arpaFormAddress - an 'ip6.arpa' form address
  254. * @returns {Adress6}
  255. * @example
  256. * var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)
  257. * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'
  258. */
  259. static fromArpa(arpaFormAddress) {
  260. // remove ending ".ip6.arpa." or just "."
  261. let address = arpaFormAddress.replace(/(\.ip6\.arpa)?\.$/, '');
  262. const semicolonAmount = 7;
  263. // correct ip6.arpa form with ending removed will be 63 characters
  264. if (address.length !== 63) {
  265. throw new address_error_1.AddressError("Invalid 'ip6.arpa' form.");
  266. }
  267. const parts = address.split('.').reverse();
  268. for (let i = semicolonAmount; i > 0; i--) {
  269. const insertIndex = i * 4;
  270. parts.splice(insertIndex, 0, ':');
  271. }
  272. address = parts.join('');
  273. return new Address6(address);
  274. }
  275. /**
  276. * Return the Microsoft UNC transcription of the address
  277. * @memberof Address6
  278. * @instance
  279. * @returns {String} the Microsoft UNC transcription of the address
  280. */
  281. microsoftTranscription() {
  282. return (0, sprintf_js_1.sprintf)('%s.ipv6-literal.net', this.correctForm().replace(/:/g, '-'));
  283. }
  284. /**
  285. * Return the first n bits of the address, defaulting to the subnet mask
  286. * @memberof Address6
  287. * @instance
  288. * @param {number} [mask=subnet] - the number of bits to mask
  289. * @returns {String} the first n bits of the address as a string
  290. */
  291. mask(mask = this.subnetMask) {
  292. return this.getBitsBase2(0, mask);
  293. }
  294. /**
  295. * Return the number of possible subnets of a given size in the address
  296. * @memberof Address6
  297. * @instance
  298. * @param {number} [size=128] - the subnet size
  299. * @returns {String}
  300. */
  301. // TODO: probably useful to have a numeric version of this too
  302. possibleSubnets(subnetSize = 128) {
  303. const availableBits = constants6.BITS - this.subnetMask;
  304. const subnetBits = Math.abs(subnetSize - constants6.BITS);
  305. const subnetPowers = availableBits - subnetBits;
  306. if (subnetPowers < 0) {
  307. return '0';
  308. }
  309. return addCommas(new jsbn_1.BigInteger('2', 10).pow(subnetPowers).toString(10));
  310. }
  311. /**
  312. * Helper function getting start address.
  313. * @memberof Address6
  314. * @instance
  315. * @returns {BigInteger}
  316. */
  317. _startAddress() {
  318. return new jsbn_1.BigInteger(this.mask() + '0'.repeat(constants6.BITS - this.subnetMask), 2);
  319. }
  320. /**
  321. * The first address in the range given by this address' subnet
  322. * Often referred to as the Network Address.
  323. * @memberof Address6
  324. * @instance
  325. * @returns {Address6}
  326. */
  327. startAddress() {
  328. return Address6.fromBigInteger(this._startAddress());
  329. }
  330. /**
  331. * The first host address in the range given by this address's subnet ie
  332. * the first address after the Network Address
  333. * @memberof Address6
  334. * @instance
  335. * @returns {Address6}
  336. */
  337. startAddressExclusive() {
  338. const adjust = new jsbn_1.BigInteger('1');
  339. return Address6.fromBigInteger(this._startAddress().add(adjust));
  340. }
  341. /**
  342. * Helper function getting end address.
  343. * @memberof Address6
  344. * @instance
  345. * @returns {BigInteger}
  346. */
  347. _endAddress() {
  348. return new jsbn_1.BigInteger(this.mask() + '1'.repeat(constants6.BITS - this.subnetMask), 2);
  349. }
  350. /**
  351. * The last address in the range given by this address' subnet
  352. * Often referred to as the Broadcast
  353. * @memberof Address6
  354. * @instance
  355. * @returns {Address6}
  356. */
  357. endAddress() {
  358. return Address6.fromBigInteger(this._endAddress());
  359. }
  360. /**
  361. * The last host address in the range given by this address's subnet ie
  362. * the last address prior to the Broadcast Address
  363. * @memberof Address6
  364. * @instance
  365. * @returns {Address6}
  366. */
  367. endAddressExclusive() {
  368. const adjust = new jsbn_1.BigInteger('1');
  369. return Address6.fromBigInteger(this._endAddress().subtract(adjust));
  370. }
  371. /**
  372. * Return the scope of the address
  373. * @memberof Address6
  374. * @instance
  375. * @returns {String}
  376. */
  377. getScope() {
  378. let scope = constants6.SCOPES[this.getBits(12, 16).intValue()];
  379. if (this.getType() === 'Global unicast' && scope !== 'Link local') {
  380. scope = 'Global';
  381. }
  382. return scope || 'Unknown';
  383. }
  384. /**
  385. * Return the type of the address
  386. * @memberof Address6
  387. * @instance
  388. * @returns {String}
  389. */
  390. getType() {
  391. for (const subnet of Object.keys(constants6.TYPES)) {
  392. if (this.isInSubnet(new Address6(subnet))) {
  393. return constants6.TYPES[subnet];
  394. }
  395. }
  396. return 'Global unicast';
  397. }
  398. /**
  399. * Return the bits in the given range as a BigInteger
  400. * @memberof Address6
  401. * @instance
  402. * @returns {BigInteger}
  403. */
  404. getBits(start, end) {
  405. return new jsbn_1.BigInteger(this.getBitsBase2(start, end), 2);
  406. }
  407. /**
  408. * Return the bits in the given range as a base-2 string
  409. * @memberof Address6
  410. * @instance
  411. * @returns {String}
  412. */
  413. getBitsBase2(start, end) {
  414. return this.binaryZeroPad().slice(start, end);
  415. }
  416. /**
  417. * Return the bits in the given range as a base-16 string
  418. * @memberof Address6
  419. * @instance
  420. * @returns {String}
  421. */
  422. getBitsBase16(start, end) {
  423. const length = end - start;
  424. if (length % 4 !== 0) {
  425. throw new Error('Length of bits to retrieve must be divisible by four');
  426. }
  427. return this.getBits(start, end)
  428. .toString(16)
  429. .padStart(length / 4, '0');
  430. }
  431. /**
  432. * Return the bits that are set past the subnet mask length
  433. * @memberof Address6
  434. * @instance
  435. * @returns {String}
  436. */
  437. getBitsPastSubnet() {
  438. return this.getBitsBase2(this.subnetMask, constants6.BITS);
  439. }
  440. /**
  441. * Return the reversed ip6.arpa form of the address
  442. * @memberof Address6
  443. * @param {Object} options
  444. * @param {boolean} options.omitSuffix - omit the "ip6.arpa" suffix
  445. * @instance
  446. * @returns {String}
  447. */
  448. reverseForm(options) {
  449. if (!options) {
  450. options = {};
  451. }
  452. const characters = Math.floor(this.subnetMask / 4);
  453. const reversed = this.canonicalForm()
  454. .replace(/:/g, '')
  455. .split('')
  456. .slice(0, characters)
  457. .reverse()
  458. .join('.');
  459. if (characters > 0) {
  460. if (options.omitSuffix) {
  461. return reversed;
  462. }
  463. return (0, sprintf_js_1.sprintf)('%s.ip6.arpa.', reversed);
  464. }
  465. if (options.omitSuffix) {
  466. return '';
  467. }
  468. return 'ip6.arpa.';
  469. }
  470. /**
  471. * Return the correct form of the address
  472. * @memberof Address6
  473. * @instance
  474. * @returns {String}
  475. */
  476. correctForm() {
  477. let i;
  478. let groups = [];
  479. let zeroCounter = 0;
  480. const zeroes = [];
  481. for (i = 0; i < this.parsedAddress.length; i++) {
  482. const value = parseInt(this.parsedAddress[i], 16);
  483. if (value === 0) {
  484. zeroCounter++;
  485. }
  486. if (value !== 0 && zeroCounter > 0) {
  487. if (zeroCounter > 1) {
  488. zeroes.push([i - zeroCounter, i - 1]);
  489. }
  490. zeroCounter = 0;
  491. }
  492. }
  493. // Do we end with a string of zeroes?
  494. if (zeroCounter > 1) {
  495. zeroes.push([this.parsedAddress.length - zeroCounter, this.parsedAddress.length - 1]);
  496. }
  497. const zeroLengths = zeroes.map((n) => n[1] - n[0] + 1);
  498. if (zeroes.length > 0) {
  499. const index = zeroLengths.indexOf(Math.max(...zeroLengths));
  500. groups = compact(this.parsedAddress, zeroes[index]);
  501. }
  502. else {
  503. groups = this.parsedAddress;
  504. }
  505. for (i = 0; i < groups.length; i++) {
  506. if (groups[i] !== 'compact') {
  507. groups[i] = parseInt(groups[i], 16).toString(16);
  508. }
  509. }
  510. let correct = groups.join(':');
  511. correct = correct.replace(/^compact$/, '::');
  512. correct = correct.replace(/^compact|compact$/, ':');
  513. correct = correct.replace(/compact/, '');
  514. return correct;
  515. }
  516. /**
  517. * Return a zero-padded base-2 string representation of the address
  518. * @memberof Address6
  519. * @instance
  520. * @returns {String}
  521. * @example
  522. * var address = new Address6('2001:4860:4001:803::1011');
  523. * address.binaryZeroPad();
  524. * // '0010000000000001010010000110000001000000000000010000100000000011
  525. * // 0000000000000000000000000000000000000000000000000001000000010001'
  526. */
  527. binaryZeroPad() {
  528. return this.bigInteger().toString(2).padStart(constants6.BITS, '0');
  529. }
  530. // TODO: Improve the semantics of this helper function
  531. parse4in6(address) {
  532. const groups = address.split(':');
  533. const lastGroup = groups.slice(-1)[0];
  534. const address4 = lastGroup.match(constants4.RE_ADDRESS);
  535. if (address4) {
  536. this.parsedAddress4 = address4[0];
  537. this.address4 = new ipv4_1.Address4(this.parsedAddress4);
  538. for (let i = 0; i < this.address4.groups; i++) {
  539. if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) {
  540. throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.", address.replace(constants4.RE_ADDRESS, this.address4.parsedAddress.map(spanLeadingZeroes4).join('.')));
  541. }
  542. }
  543. this.v4 = true;
  544. groups[groups.length - 1] = this.address4.toGroup6();
  545. address = groups.join(':');
  546. }
  547. return address;
  548. }
  549. // TODO: Make private?
  550. parse(address) {
  551. address = this.parse4in6(address);
  552. const badCharacters = address.match(constants6.RE_BAD_CHARACTERS);
  553. if (badCharacters) {
  554. throw new address_error_1.AddressError((0, sprintf_js_1.sprintf)('Bad character%s detected in address: %s', badCharacters.length > 1 ? 's' : '', badCharacters.join('')), address.replace(constants6.RE_BAD_CHARACTERS, '<span class="parse-error">$1</span>'));
  555. }
  556. const badAddress = address.match(constants6.RE_BAD_ADDRESS);
  557. if (badAddress) {
  558. throw new address_error_1.AddressError((0, sprintf_js_1.sprintf)('Address failed regex: %s', badAddress.join('')), address.replace(constants6.RE_BAD_ADDRESS, '<span class="parse-error">$1</span>'));
  559. }
  560. let groups = [];
  561. const halves = address.split('::');
  562. if (halves.length === 2) {
  563. let first = halves[0].split(':');
  564. let last = halves[1].split(':');
  565. if (first.length === 1 && first[0] === '') {
  566. first = [];
  567. }
  568. if (last.length === 1 && last[0] === '') {
  569. last = [];
  570. }
  571. const remaining = this.groups - (first.length + last.length);
  572. if (!remaining) {
  573. throw new address_error_1.AddressError('Error parsing groups');
  574. }
  575. this.elidedGroups = remaining;
  576. this.elisionBegin = first.length;
  577. this.elisionEnd = first.length + this.elidedGroups;
  578. groups = groups.concat(first);
  579. for (let i = 0; i < remaining; i++) {
  580. groups.push('0');
  581. }
  582. groups = groups.concat(last);
  583. }
  584. else if (halves.length === 1) {
  585. groups = address.split(':');
  586. this.elidedGroups = 0;
  587. }
  588. else {
  589. throw new address_error_1.AddressError('Too many :: groups found');
  590. }
  591. groups = groups.map((group) => (0, sprintf_js_1.sprintf)('%x', parseInt(group, 16)));
  592. if (groups.length !== this.groups) {
  593. throw new address_error_1.AddressError('Incorrect number of groups found');
  594. }
  595. return groups;
  596. }
  597. /**
  598. * Return the canonical form of the address
  599. * @memberof Address6
  600. * @instance
  601. * @returns {String}
  602. */
  603. canonicalForm() {
  604. return this.parsedAddress.map(paddedHex).join(':');
  605. }
  606. /**
  607. * Return the decimal form of the address
  608. * @memberof Address6
  609. * @instance
  610. * @returns {String}
  611. */
  612. decimal() {
  613. return this.parsedAddress.map((n) => (0, sprintf_js_1.sprintf)('%05d', parseInt(n, 16))).join(':');
  614. }
  615. /**
  616. * Return the address as a BigInteger
  617. * @memberof Address6
  618. * @instance
  619. * @returns {BigInteger}
  620. */
  621. bigInteger() {
  622. return new jsbn_1.BigInteger(this.parsedAddress.map(paddedHex).join(''), 16);
  623. }
  624. /**
  625. * Return the last two groups of this address as an IPv4 address string
  626. * @memberof Address6
  627. * @instance
  628. * @returns {Address4}
  629. * @example
  630. * var address = new Address6('2001:4860:4001::1825:bf11');
  631. * address.to4().correctForm(); // '24.37.191.17'
  632. */
  633. to4() {
  634. const binary = this.binaryZeroPad().split('');
  635. return ipv4_1.Address4.fromHex(new jsbn_1.BigInteger(binary.slice(96, 128).join(''), 2).toString(16));
  636. }
  637. /**
  638. * Return the v4-in-v6 form of the address
  639. * @memberof Address6
  640. * @instance
  641. * @returns {String}
  642. */
  643. to4in6() {
  644. const address4 = this.to4();
  645. const address6 = new Address6(this.parsedAddress.slice(0, 6).join(':'), 6);
  646. const correct = address6.correctForm();
  647. let infix = '';
  648. if (!/:$/.test(correct)) {
  649. infix = ':';
  650. }
  651. return correct + infix + address4.address;
  652. }
  653. /**
  654. * Return an object containing the Teredo properties of the address
  655. * @memberof Address6
  656. * @instance
  657. * @returns {Object}
  658. */
  659. inspectTeredo() {
  660. /*
  661. - Bits 0 to 31 are set to the Teredo prefix (normally 2001:0000::/32).
  662. - Bits 32 to 63 embed the primary IPv4 address of the Teredo server that
  663. is used.
  664. - Bits 64 to 79 can be used to define some flags. Currently only the
  665. higher order bit is used; it is set to 1 if the Teredo client is
  666. located behind a cone NAT, 0 otherwise. For Microsoft's Windows Vista
  667. and Windows Server 2008 implementations, more bits are used. In those
  668. implementations, the format for these 16 bits is "CRAAAAUG AAAAAAAA",
  669. where "C" remains the "Cone" flag. The "R" bit is reserved for future
  670. use. The "U" bit is for the Universal/Local flag (set to 0). The "G" bit
  671. is Individual/Group flag (set to 0). The A bits are set to a 12-bit
  672. randomly generated number chosen by the Teredo client to introduce
  673. additional protection for the Teredo node against IPv6-based scanning
  674. attacks.
  675. - Bits 80 to 95 contains the obfuscated UDP port number. This is the
  676. port number that is mapped by the NAT to the Teredo client with all
  677. bits inverted.
  678. - Bits 96 to 127 contains the obfuscated IPv4 address. This is the
  679. public IPv4 address of the NAT with all bits inverted.
  680. */
  681. const prefix = this.getBitsBase16(0, 32);
  682. const udpPort = this.getBits(80, 96).xor(new jsbn_1.BigInteger('ffff', 16)).toString();
  683. const server4 = ipv4_1.Address4.fromHex(this.getBitsBase16(32, 64));
  684. const client4 = ipv4_1.Address4.fromHex(this.getBits(96, 128).xor(new jsbn_1.BigInteger('ffffffff', 16)).toString(16));
  685. const flags = this.getBits(64, 80);
  686. const flagsBase2 = this.getBitsBase2(64, 80);
  687. const coneNat = flags.testBit(15);
  688. const reserved = flags.testBit(14);
  689. const groupIndividual = flags.testBit(8);
  690. const universalLocal = flags.testBit(9);
  691. const nonce = new jsbn_1.BigInteger(flagsBase2.slice(2, 6) + flagsBase2.slice(8, 16), 2).toString(10);
  692. return {
  693. prefix: (0, sprintf_js_1.sprintf)('%s:%s', prefix.slice(0, 4), prefix.slice(4, 8)),
  694. server4: server4.address,
  695. client4: client4.address,
  696. flags: flagsBase2,
  697. coneNat,
  698. microsoft: {
  699. reserved,
  700. universalLocal,
  701. groupIndividual,
  702. nonce,
  703. },
  704. udpPort,
  705. };
  706. }
  707. /**
  708. * Return an object containing the 6to4 properties of the address
  709. * @memberof Address6
  710. * @instance
  711. * @returns {Object}
  712. */
  713. inspect6to4() {
  714. /*
  715. - Bits 0 to 15 are set to the 6to4 prefix (2002::/16).
  716. - Bits 16 to 48 embed the IPv4 address of the 6to4 gateway that is used.
  717. */
  718. const prefix = this.getBitsBase16(0, 16);
  719. const gateway = ipv4_1.Address4.fromHex(this.getBitsBase16(16, 48));
  720. return {
  721. prefix: (0, sprintf_js_1.sprintf)('%s', prefix.slice(0, 4)),
  722. gateway: gateway.address,
  723. };
  724. }
  725. /**
  726. * Return a v6 6to4 address from a v6 v4inv6 address
  727. * @memberof Address6
  728. * @instance
  729. * @returns {Address6}
  730. */
  731. to6to4() {
  732. if (!this.is4()) {
  733. return null;
  734. }
  735. const addr6to4 = [
  736. '2002',
  737. this.getBitsBase16(96, 112),
  738. this.getBitsBase16(112, 128),
  739. '',
  740. '/16',
  741. ].join(':');
  742. return new Address6(addr6to4);
  743. }
  744. /**
  745. * Return a byte array
  746. * @memberof Address6
  747. * @instance
  748. * @returns {Array}
  749. */
  750. toByteArray() {
  751. const byteArray = this.bigInteger().toByteArray();
  752. // work around issue where `toByteArray` returns a leading 0 element
  753. if (byteArray.length === 17 && byteArray[0] === 0) {
  754. return byteArray.slice(1);
  755. }
  756. return byteArray;
  757. }
  758. /**
  759. * Return an unsigned byte array
  760. * @memberof Address6
  761. * @instance
  762. * @returns {Array}
  763. */
  764. toUnsignedByteArray() {
  765. return this.toByteArray().map(unsignByte);
  766. }
  767. /**
  768. * Convert a byte array to an Address6 object
  769. * @memberof Address6
  770. * @static
  771. * @returns {Address6}
  772. */
  773. static fromByteArray(bytes) {
  774. return this.fromUnsignedByteArray(bytes.map(unsignByte));
  775. }
  776. /**
  777. * Convert an unsigned byte array to an Address6 object
  778. * @memberof Address6
  779. * @static
  780. * @returns {Address6}
  781. */
  782. static fromUnsignedByteArray(bytes) {
  783. const BYTE_MAX = new jsbn_1.BigInteger('256', 10);
  784. let result = new jsbn_1.BigInteger('0', 10);
  785. let multiplier = new jsbn_1.BigInteger('1', 10);
  786. for (let i = bytes.length - 1; i >= 0; i--) {
  787. result = result.add(multiplier.multiply(new jsbn_1.BigInteger(bytes[i].toString(10), 10)));
  788. multiplier = multiplier.multiply(BYTE_MAX);
  789. }
  790. return Address6.fromBigInteger(result);
  791. }
  792. /**
  793. * Returns true if the address is in the canonical form, false otherwise
  794. * @memberof Address6
  795. * @instance
  796. * @returns {boolean}
  797. */
  798. isCanonical() {
  799. return this.addressMinusSuffix === this.canonicalForm();
  800. }
  801. /**
  802. * Returns true if the address is a link local address, false otherwise
  803. * @memberof Address6
  804. * @instance
  805. * @returns {boolean}
  806. */
  807. isLinkLocal() {
  808. // Zeroes are required, i.e. we can't check isInSubnet with 'fe80::/10'
  809. if (this.getBitsBase2(0, 64) ===
  810. '1111111010000000000000000000000000000000000000000000000000000000') {
  811. return true;
  812. }
  813. return false;
  814. }
  815. /**
  816. * Returns true if the address is a multicast address, false otherwise
  817. * @memberof Address6
  818. * @instance
  819. * @returns {boolean}
  820. */
  821. isMulticast() {
  822. return this.getType() === 'Multicast';
  823. }
  824. /**
  825. * Returns true if the address is a v4-in-v6 address, false otherwise
  826. * @memberof Address6
  827. * @instance
  828. * @returns {boolean}
  829. */
  830. is4() {
  831. return this.v4;
  832. }
  833. /**
  834. * Returns true if the address is a Teredo address, false otherwise
  835. * @memberof Address6
  836. * @instance
  837. * @returns {boolean}
  838. */
  839. isTeredo() {
  840. return this.isInSubnet(new Address6('2001::/32'));
  841. }
  842. /**
  843. * Returns true if the address is a 6to4 address, false otherwise
  844. * @memberof Address6
  845. * @instance
  846. * @returns {boolean}
  847. */
  848. is6to4() {
  849. return this.isInSubnet(new Address6('2002::/16'));
  850. }
  851. /**
  852. * Returns true if the address is a loopback address, false otherwise
  853. * @memberof Address6
  854. * @instance
  855. * @returns {boolean}
  856. */
  857. isLoopback() {
  858. return this.getType() === 'Loopback';
  859. }
  860. // #endregion
  861. // #region HTML
  862. /**
  863. * @returns {String} the address in link form with a default port of 80
  864. */
  865. href(optionalPort) {
  866. if (optionalPort === undefined) {
  867. optionalPort = '';
  868. }
  869. else {
  870. optionalPort = (0, sprintf_js_1.sprintf)(':%s', optionalPort);
  871. }
  872. return (0, sprintf_js_1.sprintf)('http://[%s]%s/', this.correctForm(), optionalPort);
  873. }
  874. /**
  875. * @returns {String} a link suitable for conveying the address via a URL hash
  876. */
  877. link(options) {
  878. if (!options) {
  879. options = {};
  880. }
  881. if (options.className === undefined) {
  882. options.className = '';
  883. }
  884. if (options.prefix === undefined) {
  885. options.prefix = '/#address=';
  886. }
  887. if (options.v4 === undefined) {
  888. options.v4 = false;
  889. }
  890. let formFunction = this.correctForm;
  891. if (options.v4) {
  892. formFunction = this.to4in6;
  893. }
  894. if (options.className) {
  895. return (0, sprintf_js_1.sprintf)('<a href="%1$s%2$s" class="%3$s">%2$s</a>', options.prefix, formFunction.call(this), options.className);
  896. }
  897. return (0, sprintf_js_1.sprintf)('<a href="%1$s%2$s">%2$s</a>', options.prefix, formFunction.call(this));
  898. }
  899. /**
  900. * Groups an address
  901. * @returns {String}
  902. */
  903. group() {
  904. if (this.elidedGroups === 0) {
  905. // The simple case
  906. return helpers.simpleGroup(this.address).join(':');
  907. }
  908. assert(typeof this.elidedGroups === 'number');
  909. assert(typeof this.elisionBegin === 'number');
  910. // The elided case
  911. const output = [];
  912. const [left, right] = this.address.split('::');
  913. if (left.length) {
  914. output.push(...helpers.simpleGroup(left));
  915. }
  916. else {
  917. output.push('');
  918. }
  919. const classes = ['hover-group'];
  920. for (let i = this.elisionBegin; i < this.elisionBegin + this.elidedGroups; i++) {
  921. classes.push((0, sprintf_js_1.sprintf)('group-%d', i));
  922. }
  923. output.push((0, sprintf_js_1.sprintf)('<span class="%s"></span>', classes.join(' ')));
  924. if (right.length) {
  925. output.push(...helpers.simpleGroup(right, this.elisionEnd));
  926. }
  927. else {
  928. output.push('');
  929. }
  930. if (this.is4()) {
  931. assert(this.address4 instanceof ipv4_1.Address4);
  932. output.pop();
  933. output.push(this.address4.groupForV6());
  934. }
  935. return output.join(':');
  936. }
  937. // #endregion
  938. // #region Regular expressions
  939. /**
  940. * Generate a regular expression string that can be used to find or validate
  941. * all variations of this address
  942. * @memberof Address6
  943. * @instance
  944. * @param {boolean} substringSearch
  945. * @returns {string}
  946. */
  947. regularExpressionString(substringSearch = false) {
  948. let output = [];
  949. // TODO: revisit why this is necessary
  950. const address6 = new Address6(this.correctForm());
  951. if (address6.elidedGroups === 0) {
  952. // The simple case
  953. output.push((0, regular_expressions_1.simpleRegularExpression)(address6.parsedAddress));
  954. }
  955. else if (address6.elidedGroups === constants6.GROUPS) {
  956. // A completely elided address
  957. output.push((0, regular_expressions_1.possibleElisions)(constants6.GROUPS));
  958. }
  959. else {
  960. // A partially elided address
  961. const halves = address6.address.split('::');
  962. if (halves[0].length) {
  963. output.push((0, regular_expressions_1.simpleRegularExpression)(halves[0].split(':')));
  964. }
  965. assert(typeof address6.elidedGroups === 'number');
  966. output.push((0, regular_expressions_1.possibleElisions)(address6.elidedGroups, halves[0].length !== 0, halves[1].length !== 0));
  967. if (halves[1].length) {
  968. output.push((0, regular_expressions_1.simpleRegularExpression)(halves[1].split(':')));
  969. }
  970. output = [output.join(':')];
  971. }
  972. if (!substringSearch) {
  973. output = [
  974. '(?=^|',
  975. regular_expressions_1.ADDRESS_BOUNDARY,
  976. '|[^\\w\\:])(',
  977. ...output,
  978. ')(?=[^\\w\\:]|',
  979. regular_expressions_1.ADDRESS_BOUNDARY,
  980. '|$)',
  981. ];
  982. }
  983. return output.join('');
  984. }
  985. /**
  986. * Generate a regular expression that can be used to find or validate all
  987. * variations of this address.
  988. * @memberof Address6
  989. * @instance
  990. * @param {boolean} substringSearch
  991. * @returns {RegExp}
  992. */
  993. regularExpression(substringSearch = false) {
  994. return new RegExp(this.regularExpressionString(substringSearch), 'i');
  995. }
  996. }
  997. exports.Address6 = Address6;
  998. //# sourceMappingURL=ipv6.js.map