| 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 |
|---|