• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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