ipv6.js 33 KB

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