1"use strict"; 2/* eslint-disable prefer-destructuring */ 3/* eslint-disable no-param-reassign */ 4var __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})); 15var __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}); 20var __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}; 27Object.defineProperty(exports, "__esModule", { value: true }); 28exports.Address6 = void 0; 29const common = __importStar(require("./common")); 30const constants4 = __importStar(require("./v4/constants")); 31const constants6 = __importStar(require("./v6/constants")); 32const helpers = __importStar(require("./v6/helpers")); 33const ipv4_1 = require("./ipv4"); 34const regular_expressions_1 = require("./v6/regular-expressions"); 35const address_error_1 = require("./address-error"); 36const jsbn_1 = require("jsbn"); 37const sprintf_js_1 = require("sprintf-js"); 38function assert(condition) { 39 if (!condition) { 40 throw new Error('Assertion failed.'); 41 } 42} 43function 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} 50function 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 */ 58function 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} 72function paddedHex(octet) { 73 return (0, sprintf_js_1.sprintf)('%04x', parseInt(octet, 16)); 74} 75function 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 */ 87class 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} 997exports.Address6 = Address6; 998//# sourceMappingURL=ipv6.js.map