diff options
Diffstat (limited to 'assets')
| -rw-r--r-- | assets/css/pre_overlay.css | 11 | ||||
| -rw-r--r-- | assets/js/bundle.js | 2064 | ||||
| -rw-r--r-- | assets/js/main.js | 1117 |
3 files changed, 0 insertions, 3192 deletions
diff --git a/assets/css/pre_overlay.css b/assets/css/pre_overlay.css deleted file mode 100644 index efa291a..0000000 --- a/assets/css/pre_overlay.css +++ /dev/null @@ -1,11 +0,0 @@ -.pre_container { - position: relative; -} -.pre_buttons { - position: absolute; - top: 5px; - right: 5px; -} -.pre_buttons button:focus { - outline: none; -} diff --git a/assets/js/bundle.js b/assets/js/bundle.js deleted file mode 100644 index da8f679..0000000 --- a/assets/js/bundle.js +++ /dev/null @@ -1,2064 +0,0 @@ -require=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AddressError = void 0; -class AddressError extends Error { - constructor(message, parseMessage) { - super(message); - this.name = 'AddressError'; - this.parseMessage = parseMessage; - } -} -exports.AddressError = AddressError; - -},{}],2:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isInSubnet = isInSubnet; -exports.isCorrect = isCorrect; -exports.prefixLengthFromMask = prefixLengthFromMask; -exports.numberToPaddedHex = numberToPaddedHex; -exports.stringToPaddedHex = stringToPaddedHex; -exports.testBit = testBit; -const address_error_1 = require("./address-error"); -function isInSubnet(address) { - if (this.subnetMask < address.subnetMask) { - return false; - } - if (this.mask(address.subnetMask) === address.mask()) { - return true; - } - return false; -} -function isCorrect(defaultBits) { - return function () { - if (this.addressMinusSuffix !== this.correctForm()) { - return false; - } - if (this.subnetMask === defaultBits && !this.parsedSubnet) { - return true; - } - return this.parsedSubnet === String(this.subnetMask); - }; -} -/** - * Returns the prefix length (number of leading 1 bits) of a contiguous - * subnet mask. Throws `AddressError` if the mask is non-contiguous (e.g. - * `255.0.255.0`). - */ -function prefixLengthFromMask(value, totalBits) { - const binary = value.toString(2).padStart(totalBits, '0'); - if (binary.length > totalBits) { - throw new address_error_1.AddressError('Invalid subnet mask.'); - } - const firstZero = binary.indexOf('0'); - if (firstZero === -1) { - return totalBits; - } - if (binary.slice(firstZero).includes('1')) { - throw new address_error_1.AddressError('Invalid subnet mask.'); - } - return firstZero; -} -function numberToPaddedHex(number) { - return number.toString(16).padStart(2, '0'); -} -function stringToPaddedHex(numberString) { - return numberToPaddedHex(parseInt(numberString, 10)); -} -/** - * @param binaryValue Binary representation of a value (e.g. `10`) - * @param position Byte position, where 0 is the least significant bit - */ -function testBit(binaryValue, position) { - const { length } = binaryValue; - if (position > length) { - return false; - } - const positionInString = length - position; - return binaryValue.substring(positionInString, positionInString + 1) === '1'; -} - -},{"./address-error":1}],3:[function(require,module,exports){ -"use strict"; -/* eslint-disable no-param-reassign */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Address4 = void 0; -const common = __importStar(require("./common")); -const constants = __importStar(require("./v4/constants")); -const address_error_1 = require("./address-error"); -const isCorrect4 = common.isCorrect(constants.BITS); -/** - * Represents an IPv4 address - * @param {string} address - An IPv4 address string - */ -class Address4 { - constructor(address) { - this.groups = constants.GROUPS; - this.parsedAddress = []; - this.parsedSubnet = ''; - this.subnet = '/32'; - this.subnetMask = 32; - this.v4 = true; - /** - * Returns true if the address is correct, false otherwise - * @returns {Boolean} - */ - this.isCorrect = isCorrect4; - /** - * Returns true if the given address is in the subnet of the current address - * @returns {boolean} - */ - this.isInSubnet = common.isInSubnet; - this.address = address; - const subnet = constants.RE_SUBNET_STRING.exec(address); - if (subnet) { - this.parsedSubnet = subnet[0].replace('/', ''); - this.subnetMask = parseInt(this.parsedSubnet, 10); - this.subnet = `/${this.subnetMask}`; - if (this.subnetMask < 0 || this.subnetMask > constants.BITS) { - throw new address_error_1.AddressError('Invalid subnet mask.'); - } - address = address.replace(constants.RE_SUBNET_STRING, ''); - } - this.addressMinusSuffix = address; - this.parsedAddress = this.parse(address); - } - /** - * Returns true if the given string is a valid IPv4 address (with optional - * CIDR subnet), false otherwise. Host bits in the subnet portion are - * allowed (e.g. `192.168.1.5/24` is valid); for strict network-address - * validation compare `correctForm()` to `startAddress().correctForm()`, - * or use `networkForm()`. - */ - static isValid(address) { - try { - // eslint-disable-next-line no-new - new Address4(address); - return true; - } - catch (e) { - return false; - } - } - /** - * Parses an IPv4 address string into its four octet groups and stores the - * result on `this.parsedAddress`. Called automatically by the constructor; - * you typically don't need to call it directly. Throws `AddressError` if - * the input is not a valid IPv4 address. - */ - parse(address) { - const groups = address.split('.'); - if (!address.match(constants.RE_ADDRESS)) { - throw new address_error_1.AddressError('Invalid IPv4 address.'); - } - return groups; - } - /** - * Returns the address in correct form: octets joined with `.` and any - * leading zeros stripped (e.g. `192.168.1.1`). For IPv4 this matches the - * canonical dotted-decimal representation. - */ - correctForm() { - return this.parsedAddress.map((part) => parseInt(part, 10)).join('.'); - } - /** - * Construct an `Address4` from an address and a dotted-decimal subnet - * mask given as separate strings (e.g. as returned by Node's - * `os.networkInterfaces()`). Throws `AddressError` if the mask is - * non-contiguous (e.g. `255.0.255.0`). - * @example - * var address = Address4.fromAddressAndMask('192.168.1.1', '255.255.255.0'); - * address.subnetMask; // 24 - */ - static fromAddressAndMask(address, mask) { - const bits = common.prefixLengthFromMask(new Address4(mask).bigInt(), constants.BITS); - return new Address4(`${address}/${bits}`); - } - /** - * Construct an `Address4` from an address and a Cisco-style wildcard mask - * given as separate strings (e.g. `0.0.0.255` for a `/24`). The wildcard - * mask is the bitwise inverse of the subnet mask. Throws `AddressError` - * if the mask is non-contiguous (e.g. `0.255.0.255`). - * @example - * var address = Address4.fromAddressAndWildcardMask('10.0.0.1', '0.0.0.255'); - * address.subnetMask; // 24 - */ - static fromAddressAndWildcardMask(address, wildcardMask) { - const wildcard = new Address4(wildcardMask).bigInt(); - const allOnes = (BigInt(1) << BigInt(constants.BITS)) - BigInt(1); - // eslint-disable-next-line no-bitwise - const mask = wildcard ^ allOnes; - const bits = common.prefixLengthFromMask(mask, constants.BITS); - return new Address4(`${address}/${bits}`); - } - /** - * Construct an `Address4` from a wildcard pattern with trailing `*` - * octets. The number of trailing wildcards determines the prefix - * length: each `*` represents 8 bits. - * - * Only trailing whole-octet wildcards are supported. Partial-octet - * wildcards (e.g. `192.168.0.1*`) and interior wildcards (e.g. - * `192.*.0.1`) throw `AddressError`. - * @example - * Address4.fromWildcard('192.168.0.*').subnet; // '/24' - * Address4.fromWildcard('192.168.*.*').subnet; // '/16' - * Address4.fromWildcard('*.*.*.*').subnet; // '/0' - */ - static fromWildcard(input) { - const groups = input.split('.'); - if (groups.length !== constants.GROUPS) { - throw new address_error_1.AddressError('Wildcard pattern must have 4 octets'); - } - let firstWildcard = -1; - for (let i = 0; i < groups.length; i++) { - if (groups[i] === '*') { - if (firstWildcard === -1) { - firstWildcard = i; - } - } - else if (firstWildcard !== -1) { - throw new address_error_1.AddressError('Wildcard `*` must only appear in trailing octets (e.g. `192.168.0.*`)'); - } - } - const trailing = firstWildcard === -1 ? 0 : groups.length - firstWildcard; - const replaced = groups.map((g) => (g === '*' ? '0' : g)); - const subnetBits = constants.BITS - trailing * 8; - return new Address4(`${replaced.join('.')}/${subnetBits}`); - } - /** - * Converts a hex string to an IPv4 address object. Accepts 8 hex digits - * with optional `:` separators (e.g. `'7f000001'` or `'7f:00:00:01'`). - * Throws `AddressError` for any other length or for non-hex characters. - * @param {string} hex - a hex string to convert - * @returns {Address4} - */ - static fromHex(hex) { - const stripped = hex.replace(/:/g, ''); - if (!/^[0-9a-fA-F]{8}$/.test(stripped)) { - throw new address_error_1.AddressError('IPv4 hex must be exactly 8 hex digits'); - } - const groups = []; - for (let i = 0; i < 8; i += 2) { - groups.push(parseInt(stripped.slice(i, i + 2), 16)); - } - return new Address4(groups.join('.')); - } - /** - * Converts an integer into a IPv4 address object. The integer must be a - * non-negative safe integer in the range `[0, 2**32 - 1]`; otherwise - * `AddressError` is thrown. - * @param {integer} integer - a number to convert - * @returns {Address4} - */ - static fromInteger(integer) { - if (!Number.isInteger(integer) || integer < 0 || integer > 0xffffffff) { - throw new address_error_1.AddressError('IPv4 integer must be in the range 0 to 2**32 - 1'); - } - return Address4.fromHex(integer.toString(16).padStart(8, '0')); - } - /** - * Return an address from in-addr.arpa form - * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address - * @returns {Adress4} - * @example - * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.) - * address.correctForm(); // '192.0.2.42' - */ - static fromArpa(arpaFormAddress) { - // remove ending ".in-addr.arpa." or just "." - const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, ''); - const address = leader.split('.').reverse().join('.'); - return new Address4(address); - } - /** - * Converts an IPv4 address object to a hex string - * @returns {String} - */ - toHex() { - return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':'); - } - /** - * Converts an IPv4 address object to an array of bytes. - * - * To get a Node.js `Buffer`, wrap the result: `Buffer.from(address.toArray())`. - * @returns {Array} - */ - toArray() { - return this.parsedAddress.map((part) => parseInt(part, 10)); - } - /** - * Converts an IPv4 address object to an IPv6 address group - * @returns {String} - */ - toGroup6() { - const output = []; - let i; - for (i = 0; i < constants.GROUPS; i += 2) { - output.push(`${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(this.parsedAddress[i + 1])}`); - } - return output.join(':'); - } - /** - * Returns the address as a `bigint` - * @returns {bigint} - */ - bigInt() { - return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join('')}`); - } - /** - * Helper function getting start address. - * @returns {bigint} - */ - _startAddress() { - return BigInt(`0b${this.mask() + '0'.repeat(constants.BITS - this.subnetMask)}`); - } - /** - * The first address in the range given by this address' subnet. - * Often referred to as the Network Address. - * @returns {Address4} - */ - startAddress() { - return Address4.fromBigInt(this._startAddress()); - } - /** - * The first host address in the range given by this address's subnet ie - * the first address after the Network Address - * @returns {Address4} - */ - startAddressExclusive() { - const adjust = BigInt('1'); - return Address4.fromBigInt(this._startAddress() + adjust); - } - /** - * Helper function getting end address. - * @returns {bigint} - */ - _endAddress() { - return BigInt(`0b${this.mask() + '1'.repeat(constants.BITS - this.subnetMask)}`); - } - /** - * The last address in the range given by this address' subnet - * Often referred to as the Broadcast - * @returns {Address4} - */ - endAddress() { - return Address4.fromBigInt(this._endAddress()); - } - /** - * The last host address in the range given by this address's subnet ie - * the last address prior to the Broadcast Address - * @returns {Address4} - */ - endAddressExclusive() { - const adjust = BigInt('1'); - return Address4.fromBigInt(this._endAddress() - adjust); - } - /** - * The dotted-decimal form of the subnet mask, e.g. `255.255.240.0` for - * a `/20`. Returns an `Address4`; call `.correctForm()` for the string. - * @returns {Address4} - */ - subnetMaskAddress() { - return Address4.fromBigInt(BigInt(`0b${'1'.repeat(this.subnetMask)}${'0'.repeat(constants.BITS - this.subnetMask)}`)); - } - /** - * The Cisco-style wildcard mask, e.g. `0.0.0.255` for a `/24`. This is - * the bitwise inverse of `subnetMaskAddress()`. Returns an `Address4`; - * call `.correctForm()` for the string. - * @returns {Address4} - */ - wildcardMask() { - return Address4.fromBigInt(BigInt(`0b${'0'.repeat(this.subnetMask)}${'1'.repeat(constants.BITS - this.subnetMask)}`)); - } - /** - * The network address in CIDR string form, e.g. `192.168.1.0/24` for - * `192.168.1.5/24`. For an address with no explicit subnet the prefix is - * `/32`, e.g. `networkForm()` on `192.168.1.5` returns `192.168.1.5/32`. - * @returns {string} - */ - networkForm() { - return `${this.startAddress().correctForm()}/${this.subnetMask}`; - } - /** - * Converts a BigInt to a v4 address object. The value must be in the - * range `[0, 2**32 - 1]`; otherwise `AddressError` is thrown. - * @param {bigint} bigInt - a BigInt to convert - * @returns {Address4} - */ - static fromBigInt(bigInt) { - if (bigInt < 0n || bigInt > 0xffffffffn) { - throw new address_error_1.AddressError('IPv4 BigInt must be in the range 0 to 2**32 - 1'); - } - return Address4.fromHex(bigInt.toString(16).padStart(8, '0')); - } - /** - * Convert a byte array to an Address4 object. - * - * To convert from a Node.js `Buffer`, spread it: `Address4.fromByteArray([...buf])`. - * @param {Array<number>} bytes - an array of 4 bytes (0-255) - * @returns {Address4} - */ - static fromByteArray(bytes) { - if (bytes.length !== 4) { - throw new address_error_1.AddressError('IPv4 addresses require exactly 4 bytes'); - } - // Validate that all bytes are within valid range (0-255) - for (let i = 0; i < bytes.length; i++) { - if (!Number.isInteger(bytes[i]) || bytes[i] < 0 || bytes[i] > 255) { - throw new address_error_1.AddressError('All bytes must be integers between 0 and 255'); - } - } - return this.fromUnsignedByteArray(bytes); - } - /** - * Convert an unsigned byte array to an Address4 object - * @param {Array<number>} bytes - an array of 4 unsigned bytes (0-255) - * @returns {Address4} - */ - static fromUnsignedByteArray(bytes) { - if (bytes.length !== 4) { - throw new address_error_1.AddressError('IPv4 addresses require exactly 4 bytes'); - } - const address = bytes.join('.'); - return new Address4(address); - } - /** - * Returns the first n bits of the address, defaulting to the - * subnet mask - * @returns {String} - */ - mask(mask) { - if (mask === undefined) { - mask = this.subnetMask; - } - return this.getBitsBase2(0, mask); - } - /** - * Returns the bits in the given range as a base-2 string - * @returns {string} - */ - getBitsBase2(start, end) { - return this.binaryZeroPad().slice(start, end); - } - /** - * Return the reversed ip6.arpa form of the address - * @param {Object} options - * @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix - * @returns {String} - */ - reverseForm(options) { - if (!options) { - options = {}; - } - const reversed = this.correctForm().split('.').reverse().join('.'); - if (options.omitSuffix) { - return reversed; - } - return `${reversed}.in-addr.arpa.`; - } - /** - * Returns true if the given address is a multicast address - * @returns {boolean} - */ - isMulticast() { - return this.isInSubnet(MULTICAST_V4); - } - /** - * Returns true if the address is in one of the [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) private address ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`). - * @returns {boolean} - */ - isPrivate() { - return PRIVATE_V4.some((subnet) => this.isInSubnet(subnet)); - } - /** - * Returns true if the address is in the loopback range `127.0.0.0/8` ([RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122)). - * @returns {boolean} - */ - isLoopback() { - return this.isInSubnet(LOOPBACK_V4); - } - /** - * Returns true if the address is in the link-local range `169.254.0.0/16` ([RFC 3927](https://datatracker.ietf.org/doc/html/rfc3927)). - * @returns {boolean} - */ - isLinkLocal() { - return this.isInSubnet(LINK_LOCAL_V4); - } - /** - * Returns true if the address is the unspecified address `0.0.0.0`. - * @returns {boolean} - */ - isUnspecified() { - return this.isInSubnet(UNSPECIFIED_V4); - } - /** - * Returns true if the address is the limited broadcast address `255.255.255.255` ([RFC 919](https://datatracker.ietf.org/doc/html/rfc919)). - * @returns {boolean} - */ - isBroadcast() { - return this.isInSubnet(BROADCAST_V4); - } - /** - * Returns true if the address is in the carrier-grade NAT range `100.64.0.0/10` ([RFC 6598](https://datatracker.ietf.org/doc/html/rfc6598)). - * @returns {boolean} - */ - isCGNAT() { - return this.isInSubnet(CGNAT_V4); - } - /** - * Returns a zero-padded base-2 string representation of the address - * @returns {string} - */ - binaryZeroPad() { - if (this._binaryZeroPad === undefined) { - this._binaryZeroPad = this.bigInt().toString(2).padStart(constants.BITS, '0'); - } - return this._binaryZeroPad; - } - /** - * Groups an IPv4 address for inclusion at the end of an IPv6 address - * @returns {String} - */ - groupForV6() { - const segments = this.parsedAddress; - return this.address.replace(constants.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments - .slice(0, 2) - .join('.')}</span>.<span class="hover-group group-v4 group-7">${segments - .slice(2, 4) - .join('.')}</span>`); - } -} -exports.Address4 = Address4; -const MULTICAST_V4 = new Address4('224.0.0.0/4'); -const PRIVATE_V4 = [ - new Address4('10.0.0.0/8'), - new Address4('172.16.0.0/12'), - new Address4('192.168.0.0/16'), -]; -const LOOPBACK_V4 = new Address4('127.0.0.0/8'); -const LINK_LOCAL_V4 = new Address4('169.254.0.0/16'); -const UNSPECIFIED_V4 = new Address4('0.0.0.0/32'); -const BROADCAST_V4 = new Address4('255.255.255.255/32'); -const CGNAT_V4 = new Address4('100.64.0.0/10'); - -},{"./address-error":1,"./common":2,"./v4/constants":5}],4:[function(require,module,exports){ -"use strict"; -/* eslint-disable prefer-destructuring */ -/* eslint-disable no-param-reassign */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Address6 = void 0; -const common = __importStar(require("./common")); -const constants4 = __importStar(require("./v4/constants")); -const constants6 = __importStar(require("./v6/constants")); -const helpers = __importStar(require("./v6/helpers")); -const ipv4_1 = require("./ipv4"); -const regular_expressions_1 = require("./v6/regular-expressions"); -const address_error_1 = require("./address-error"); -const common_1 = require("./common"); -const isCorrect6 = common.isCorrect(constants6.BITS); -function assert(condition) { - if (!condition) { - throw new Error('Assertion failed.'); - } -} -function addCommas(number) { - const r = /(\d+)(\d{3})/; - while (r.test(number)) { - number = number.replace(r, '$1,$2'); - } - return number; -} -function spanLeadingZeroes4(n) { - n = n.replace(/^(0{1,})([1-9]+)$/, '<span class="parse-error">$1</span>$2'); - n = n.replace(/^(0{1,})(0)$/, '<span class="parse-error">$1</span>$2'); - return n; -} -/* - * A helper function to compact an array - */ -function compact(address, slice) { - const s1 = []; - const s2 = []; - let i; - for (i = 0; i < address.length; i++) { - if (i < slice[0]) { - s1.push(address[i]); - } - else if (i > slice[1]) { - s2.push(address[i]); - } - } - return s1.concat(['compact']).concat(s2); -} -function paddedHex(octet) { - return parseInt(octet, 16).toString(16).padStart(4, '0'); -} -function unsignByte(b) { - // eslint-disable-next-line no-bitwise - return b & 0xff; -} -/** - * Represents an IPv6 address - * @param {string} address - An IPv6 address string - * @param {number} [groups=8] - How many octets to parse - * @example - * var address = new Address6('2001::/32'); - */ -class Address6 { - constructor(address, optionalGroups) { - this.addressMinusSuffix = ''; - this.parsedSubnet = ''; - this.subnet = '/128'; - this.subnetMask = 128; - this.v4 = false; - this.zone = ''; - // #region Attributes - /** - * Returns true if the given address is in the subnet of the current address - * @returns {boolean} - */ - this.isInSubnet = common.isInSubnet; - /** - * Returns true if the address is correct, false otherwise - * @returns {boolean} - */ - this.isCorrect = isCorrect6; - if (optionalGroups === undefined) { - this.groups = constants6.GROUPS; - } - else { - this.groups = optionalGroups; - } - this.address = address; - const subnet = constants6.RE_SUBNET_STRING.exec(address); - if (subnet) { - this.parsedSubnet = subnet[0].replace('/', ''); - this.subnetMask = parseInt(this.parsedSubnet, 10); - this.subnet = `/${this.subnetMask}`; - if (Number.isNaN(this.subnetMask) || - this.subnetMask < 0 || - this.subnetMask > constants6.BITS) { - throw new address_error_1.AddressError('Invalid subnet mask.'); - } - address = address.replace(constants6.RE_SUBNET_STRING, ''); - } - else if (/\//.test(address)) { - throw new address_error_1.AddressError('Invalid subnet mask.'); - } - const zone = constants6.RE_ZONE_STRING.exec(address); - if (zone) { - this.zone = zone[0]; - address = address.replace(constants6.RE_ZONE_STRING, ''); - } - this.addressMinusSuffix = address; - this.parsedAddress = this.parse(this.addressMinusSuffix); - } - /** - * Returns true if the given string is a valid IPv6 address (with optional - * CIDR subnet and zone identifier), false otherwise. Host bits in the - * subnet portion are allowed (e.g. `2001:db8::1/32` is valid); for strict - * network-address validation compare `correctForm()` to - * `startAddress().correctForm()`, or use `networkForm()`. - */ - static isValid(address) { - try { - // eslint-disable-next-line no-new - new Address6(address); - return true; - } - catch (e) { - return false; - } - } - /** - * Convert a BigInt to a v6 address object. The value must be in the - * range `[0, 2**128 - 1]`; otherwise `AddressError` is thrown. - * @param {bigint} bigInt - a BigInt to convert - * @returns {Address6} - * @example - * var bigInt = BigInt('1000000000000'); - * var address = Address6.fromBigInt(bigInt); - * address.correctForm(); // '::e8:d4a5:1000' - */ - static fromBigInt(bigInt) { - if (bigInt < 0n || bigInt > (1n << BigInt(constants6.BITS)) - 1n) { - throw new address_error_1.AddressError('IPv6 BigInt must be in the range 0 to 2**128 - 1'); - } - const hex = bigInt.toString(16).padStart(32, '0'); - const groups = []; - for (let i = 0; i < constants6.GROUPS; i++) { - groups.push(hex.slice(i * 4, (i + 1) * 4)); - } - return new Address6(groups.join(':')); - } - /** - * Parse a URL (with optional bracketed host and port) into an address and - * port. Returns either `{ address, port }` on success or - * `{ error, address: null, port: null }` if the URL could not be parsed. - * Ports are returned as numbers (or `null` if absent or out of range). - * @example - * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/'); - * addressAndPort.address.correctForm(); // 'ffff::' - * addressAndPort.port; // 8080 - */ - static fromURL(url) { - let host; - let port = null; - let result; - // If we have brackets parse them and find a port - if (url.indexOf('[') !== -1 && url.indexOf(']:') !== -1) { - result = constants6.RE_URL_WITH_PORT.exec(url); - if (result === null) { - return { - error: 'failed to parse address with port', - address: null, - port: null, - }; - } - host = result[1]; - port = result[2]; - // If there's a URL extract the address - } - else if (url.indexOf('/') !== -1) { - // Remove the protocol prefix - url = url.replace(/^[a-z0-9]+:\/\//, ''); - // Parse the address - result = constants6.RE_URL.exec(url); - if (result === null) { - return { - error: 'failed to parse address from URL', - address: null, - port: null, - }; - } - host = result[1]; - // Otherwise just assign the URL to the host and let the library parse it - } - else { - host = url; - } - // If there's a port convert it to an integer - if (port) { - port = parseInt(port, 10); - // squelch out of range ports - if (port < 0 || port > 65536) { - port = null; - } - } - else { - // Standardize `undefined` to `null` - port = null; - } - return { - address: new Address6(host), - port, - }; - } - /** - * Construct an `Address6` from an address and a hex subnet mask given as - * separate strings (e.g. as returned by Node's `os.networkInterfaces()`). - * Throws `AddressError` if the mask is non-contiguous (e.g. - * `ffff::ffff`). - * @example - * var address = Address6.fromAddressAndMask('fe80::1', 'ffff:ffff:ffff:ffff::'); - * address.subnetMask; // 64 - */ - static fromAddressAndMask(address, mask) { - const bits = common.prefixLengthFromMask(new Address6(mask).bigInt(), constants6.BITS); - return new Address6(`${address}/${bits}`); - } - /** - * Construct an `Address6` from an address and a Cisco-style wildcard mask - * given as separate strings (e.g. `::ffff:ffff:ffff:ffff` for a `/64`). - * The wildcard mask is the bitwise inverse of the subnet mask. Throws - * `AddressError` if the mask is non-contiguous. - * @example - * var address = Address6.fromAddressAndWildcardMask('fe80::1', '::ffff:ffff:ffff:ffff'); - * address.subnetMask; // 64 - */ - static fromAddressAndWildcardMask(address, wildcardMask) { - const wildcard = new Address6(wildcardMask).bigInt(); - const allOnes = (BigInt(1) << BigInt(constants6.BITS)) - BigInt(1); - // eslint-disable-next-line no-bitwise - const mask = wildcard ^ allOnes; - const bits = common.prefixLengthFromMask(mask, constants6.BITS); - return new Address6(`${address}/${bits}`); - } - /** - * Construct an `Address6` from a wildcard pattern with trailing `*` - * groups. The number of trailing wildcards determines the prefix - * length: each `*` represents 16 bits. `::` is expanded to zero groups - * (not wildcards) before evaluating trailing wildcards. - * - * Only trailing whole-group wildcards are supported. Partial-group - * wildcards (e.g. `2001:db8::0*`) and interior wildcards (e.g. - * `*::1`) throw `AddressError`. - * @example - * Address6.fromWildcard('2001:db8:*:*:*:*:*:*').subnet; // '/32' - * Address6.fromWildcard('2001:db8::*').subnet; // '/112' - * Address6.fromWildcard('*:*:*:*:*:*:*:*').subnet; // '/0' - */ - static fromWildcard(input) { - if (input.includes('%') || input.includes('/')) { - throw new address_error_1.AddressError('Wildcard pattern must not include a zone or CIDR suffix'); - } - const halves = input.split('::'); - if (halves.length > 2) { - throw new address_error_1.AddressError("Wildcard pattern cannot contain more than one '::'"); - } - let groups; - if (halves.length === 2) { - const left = halves[0] === '' ? [] : halves[0].split(':'); - const right = halves[1] === '' ? [] : halves[1].split(':'); - const remaining = constants6.GROUPS - left.length - right.length; - if (remaining < 1) { - throw new address_error_1.AddressError("Wildcard pattern with '::' has too many groups"); - } - groups = [...left, ...new Array(remaining).fill('0'), ...right]; - } - else { - groups = input.split(':'); - } - if (groups.length !== constants6.GROUPS) { - throw new address_error_1.AddressError('Wildcard pattern must have 8 groups'); - } - let firstWildcard = -1; - for (let i = 0; i < groups.length; i++) { - if (groups[i] === '*') { - if (firstWildcard === -1) { - firstWildcard = i; - } - } - else if (firstWildcard !== -1) { - throw new address_error_1.AddressError('Wildcard `*` must only appear in trailing groups (e.g. `2001:db8:*:*:*:*:*:*`)'); - } - } - const trailing = firstWildcard === -1 ? 0 : groups.length - firstWildcard; - const replaced = groups.map((g) => (g === '*' ? '0' : g)); - const subnetBits = constants6.BITS - trailing * 16; - return new Address6(`${replaced.join(':')}/${subnetBits}`); - } - /** - * Create an IPv6-mapped address given an IPv4 address - * @param {string} address - An IPv4 address string - * @returns {Address6} - * @example - * var address = Address6.fromAddress4('192.168.0.1'); - * address.correctForm(); // '::ffff:c0a8:1' - * address.to4in6(); // '::ffff:192.168.0.1' - */ - static fromAddress4(address) { - const address4 = new ipv4_1.Address4(address); - const mask6 = constants6.BITS - (constants4.BITS - address4.subnetMask); - return new Address6(`::ffff:${address4.correctForm()}/${mask6}`); - } - /** - * Return an address from ip6.arpa form - * @param {string} arpaFormAddress - an 'ip6.arpa' form address - * @returns {Adress6} - * @example - * 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.) - * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe' - */ - static fromArpa(arpaFormAddress) { - // remove ending ".ip6.arpa." or just "." - let address = arpaFormAddress.replace(/(\.ip6\.arpa)?\.$/, ''); - const semicolonAmount = 7; - // correct ip6.arpa form with ending removed will be 63 characters - if (address.length !== 63) { - throw new address_error_1.AddressError("Invalid 'ip6.arpa' form."); - } - const parts = address.split('.').reverse(); - for (let i = semicolonAmount; i > 0; i--) { - const insertIndex = i * 4; - parts.splice(insertIndex, 0, ':'); - } - address = parts.join(''); - return new Address6(address); - } - /** - * Return the Microsoft UNC transcription of the address - * @returns {String} the Microsoft UNC transcription of the address - */ - microsoftTranscription() { - return `${this.correctForm().replace(/:/g, '-')}.ipv6-literal.net`; - } - /** - * Return the first n bits of the address, defaulting to the subnet mask - * @param {number} [mask=subnet] - the number of bits to mask - * @returns {String} the first n bits of the address as a string - */ - mask(mask = this.subnetMask) { - return this.getBitsBase2(0, mask); - } - /** - * Return the number of possible subnets of a given size in the address - * @param {number} [subnetSize=128] - the subnet size - * @returns {String} - */ - // TODO: probably useful to have a numeric version of this too - possibleSubnets(subnetSize = 128) { - const availableBits = constants6.BITS - this.subnetMask; - const subnetBits = Math.abs(subnetSize - constants6.BITS); - const subnetPowers = availableBits - subnetBits; - if (subnetPowers < 0) { - return '0'; - } - return addCommas((BigInt('2') ** BigInt(subnetPowers)).toString(10)); - } - /** - * Helper function getting start address. - * @returns {bigint} - */ - _startAddress() { - return BigInt(`0b${this.mask() + '0'.repeat(constants6.BITS - this.subnetMask)}`); - } - /** - * The first address in the range given by this address' subnet - * Often referred to as the Network Address. - * @returns {Address6} - */ - startAddress() { - return Address6.fromBigInt(this._startAddress()); - } - /** - * The first host address in the range given by this address's subnet ie - * the first address after the Network Address - * @returns {Address6} - */ - startAddressExclusive() { - const adjust = BigInt('1'); - return Address6.fromBigInt(this._startAddress() + adjust); - } - /** - * Helper function getting end address. - * @returns {bigint} - */ - _endAddress() { - return BigInt(`0b${this.mask() + '1'.repeat(constants6.BITS - this.subnetMask)}`); - } - /** - * The last address in the range given by this address' subnet - * Often referred to as the Broadcast - * @returns {Address6} - */ - endAddress() { - return Address6.fromBigInt(this._endAddress()); - } - /** - * The last host address in the range given by this address's subnet ie - * the last address prior to the Broadcast Address - * @returns {Address6} - */ - endAddressExclusive() { - const adjust = BigInt('1'); - return Address6.fromBigInt(this._endAddress() - adjust); - } - /** - * The hex form of the subnet mask, e.g. `ffff:ffff:ffff:ffff::` for a - * `/64`. Returns an `Address6`; call `.correctForm()` for the string. - * @returns {Address6} - */ - subnetMaskAddress() { - return Address6.fromBigInt(BigInt(`0b${'1'.repeat(this.subnetMask)}${'0'.repeat(constants6.BITS - this.subnetMask)}`)); - } - /** - * The Cisco-style wildcard mask, e.g. `::ffff:ffff:ffff:ffff` for a - * `/64`. This is the bitwise inverse of `subnetMaskAddress()`. Returns - * an `Address6`; call `.correctForm()` for the string. - * @returns {Address6} - */ - wildcardMask() { - return Address6.fromBigInt(BigInt(`0b${'0'.repeat(this.subnetMask)}${'1'.repeat(constants6.BITS - this.subnetMask)}`)); - } - /** - * The network address in CIDR string form, e.g. `2001:db8::/32` for - * `2001:db8::1/32`. For an address with no explicit subnet the prefix - * is `/128`, e.g. `networkForm()` on `2001:db8::1` returns - * `2001:db8::1/128`. - * @returns {string} - */ - networkForm() { - return `${this.startAddress().correctForm()}/${this.subnetMask}`; - } - /** - * Return the scope of the address. The 4-bit scope field - * ([RFC 4291 §2.7](https://datatracker.ietf.org/doc/html/rfc4291#section-2.7)) - * is only defined for multicast addresses; for unicast addresses the scope - * is derived from the address type per - * [RFC 4007 §6](https://datatracker.ietf.org/doc/html/rfc4007#section-6). - * @returns {String} - */ - getScope() { - const type = this.getType(); - if (type === 'Multicast' || type.startsWith('Multicast ')) { - const scope = constants6.SCOPES[parseInt(this.getBits(12, 16).toString(10), 10)]; - return scope || 'Unknown'; - } - // RFC 4291 §2.5.3: the loopback address is treated as having Link-Local - // scope. (Multicast scope 1, "Interface-Local", is a different concept - // used only for loopback transmission of multicast.) - if (type === 'Link-local unicast' || type === 'Loopback') { - return 'Link local'; - } - // RFC 4007 §6: the unspecified address has no scope. - if (type === 'Unspecified') { - return 'Unknown'; - } - return 'Global'; - } - /** - * Return the type of the address - * @returns {String} - */ - getType() { - for (let i = 0; i < TYPE_SUBNETS.length; i++) { - const entry = TYPE_SUBNETS[i]; - if (this.isInSubnet(entry[0])) { - return entry[1]; - } - } - return 'Global unicast'; - } - /** - * Return the bits in the given range as a BigInt - * @returns {bigint} - */ - getBits(start, end) { - return BigInt(`0b${this.getBitsBase2(start, end)}`); - } - /** - * Return the bits in the given range as a base-2 string - * @returns {String} - */ - getBitsBase2(start, end) { - return this.binaryZeroPad().slice(start, end); - } - /** - * Return the bits in the given range as a base-16 string - * @returns {String} - */ - getBitsBase16(start, end) { - const length = end - start; - if (length % 4 !== 0) { - throw new Error('Length of bits to retrieve must be divisible by four'); - } - return this.getBits(start, end) - .toString(16) - .padStart(length / 4, '0'); - } - /** - * Return the bits that are set past the subnet mask length - * @returns {String} - */ - getBitsPastSubnet() { - return this.getBitsBase2(this.subnetMask, constants6.BITS); - } - /** - * Return the reversed ip6.arpa form of the address - * @param {Object} options - * @param {boolean} options.omitSuffix - omit the "ip6.arpa" suffix - * @returns {String} - */ - reverseForm(options) { - if (!options) { - options = {}; - } - const characters = Math.floor(this.subnetMask / 4); - const reversed = this.canonicalForm() - .replace(/:/g, '') - .split('') - .slice(0, characters) - .reverse() - .join('.'); - if (characters > 0) { - if (options.omitSuffix) { - return reversed; - } - return `${reversed}.ip6.arpa.`; - } - if (options.omitSuffix) { - return ''; - } - return 'ip6.arpa.'; - } - /** - * Returns the address in correct form, per - * [RFC 5952](https://datatracker.ietf.org/doc/html/rfc5952): leading zeros - * stripped, the longest run of zero groups collapsed to `::`, and hex digits - * lowercased (e.g. `2001:db8::1`). This is the recommended form for display. - */ - correctForm() { - let i; - let groups = []; - let zeroCounter = 0; - const zeroes = []; - for (i = 0; i < this.parsedAddress.length; i++) { - const value = parseInt(this.parsedAddress[i], 16); - if (value === 0) { - zeroCounter++; - } - if (value !== 0 && zeroCounter > 0) { - if (zeroCounter > 1) { - zeroes.push([i - zeroCounter, i - 1]); - } - zeroCounter = 0; - } - } - // Do we end with a string of zeroes? - if (zeroCounter > 1) { - zeroes.push([this.parsedAddress.length - zeroCounter, this.parsedAddress.length - 1]); - } - const zeroLengths = zeroes.map((n) => n[1] - n[0] + 1); - if (zeroes.length > 0) { - const index = zeroLengths.indexOf(Math.max(...zeroLengths)); - groups = compact(this.parsedAddress, zeroes[index]); - } - else { - groups = this.parsedAddress; - } - for (i = 0; i < groups.length; i++) { - if (groups[i] !== 'compact') { - groups[i] = parseInt(groups[i], 16).toString(16); - } - } - let correct = groups.join(':'); - correct = correct.replace(/^compact$/, '::'); - correct = correct.replace(/(^compact)|(compact$)/, ':'); - correct = correct.replace(/compact/, ''); - return correct; - } - /** - * Return a zero-padded base-2 string representation of the address - * @returns {String} - * @example - * var address = new Address6('2001:4860:4001:803::1011'); - * address.binaryZeroPad(); - * // '0010000000000001010010000110000001000000000000010000100000000011 - * // 0000000000000000000000000000000000000000000000000001000000010001' - */ - binaryZeroPad() { - if (this._binaryZeroPad === undefined) { - this._binaryZeroPad = this.bigInt().toString(2).padStart(constants6.BITS, '0'); - } - return this._binaryZeroPad; - } - /** - * Parses a v4-in-v6 string (e.g. `::ffff:192.168.0.1`) by extracting the - * trailing IPv4 address into `this.address4` / `this.parsedAddress4` and - * returning the address with the v4 portion converted to two v6 groups. - * Used internally by `parse()`. - */ - // TODO: Improve the semantics of this helper function - parse4in6(address) { - if (address.indexOf('.') === -1) { - return address; - } - const groups = address.split(':'); - const lastGroup = groups.slice(-1)[0]; - const address4 = lastGroup.match(constants4.RE_ADDRESS); - if (address4) { - this.parsedAddress4 = address4[0]; - this.address4 = new ipv4_1.Address4(this.parsedAddress4); - for (let i = 0; i < this.address4.groups; i++) { - if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) { - // The prefix groups haven't been through the bad-character check - // yet, so escape them before including in the error HTML. - const highlighted = this.address4.parsedAddress.map(spanLeadingZeroes4).join('.'); - const prefix = groups.slice(0, -1).map(helpers.escapeHtml).join(':'); - const separator = groups.length > 1 ? ':' : ''; - throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.", `${prefix}${separator}${highlighted}`); - } - } - this.v4 = true; - groups[groups.length - 1] = this.address4.toGroup6(); - address = groups.join(':'); - } - return address; - } - /** - * Parses an IPv6 address string into its 8 hexadecimal groups (expanding - * any `::` elision and any trailing v4-in-v6 portion) and stores the result - * on `this.parsedAddress`. Called automatically by the constructor; you - * typically don't need to call it directly. Throws `AddressError` if the - * input is malformed. - */ - // TODO: Make private? - parse(address) { - address = this.parse4in6(address); - const badCharacters = address.match(constants6.RE_BAD_CHARACTERS); - if (badCharacters) { - 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>')); - } - const badAddress = address.match(constants6.RE_BAD_ADDRESS); - if (badAddress) { - throw new address_error_1.AddressError(`Address failed regex: ${badAddress.join('')}`, address.replace(constants6.RE_BAD_ADDRESS, '<span class="parse-error">$1</span>')); - } - let groups = []; - const halves = address.split('::'); - if (halves.length === 2) { - let first = halves[0].split(':'); - let last = halves[1].split(':'); - if (first.length === 1 && first[0] === '') { - first = []; - } - if (last.length === 1 && last[0] === '') { - last = []; - } - const remaining = this.groups - (first.length + last.length); - if (!remaining) { - throw new address_error_1.AddressError('Error parsing groups'); - } - this.elidedGroups = remaining; - this.elisionBegin = first.length; - this.elisionEnd = first.length + this.elidedGroups; - groups = groups.concat(first); - for (let i = 0; i < remaining; i++) { - groups.push('0'); - } - groups = groups.concat(last); - } - else if (halves.length === 1) { - groups = address.split(':'); - this.elidedGroups = 0; - } - else { - throw new address_error_1.AddressError('Too many :: groups found'); - } - groups = groups.map((group) => parseInt(group, 16).toString(16)); - if (groups.length !== this.groups) { - throw new address_error_1.AddressError('Incorrect number of groups found'); - } - return groups; - } - /** - * Returns the canonical (fully expanded) form of the address: all 8 groups, - * each padded to 4 hex digits, with no `::` collapsing - * (e.g. `2001:0db8:0000:0000:0000:0000:0000:0001`). Useful for sorting and - * byte-exact comparison. - */ - canonicalForm() { - return this.parsedAddress.map(paddedHex).join(':'); - } - /** - * Return the decimal form of the address - * @returns {String} - */ - decimal() { - return this.parsedAddress.map((n) => parseInt(n, 16).toString(10).padStart(5, '0')).join(':'); - } - /** - * Return the address as a BigInt - * @returns {bigint} - */ - bigInt() { - return BigInt(`0x${this.parsedAddress.map(paddedHex).join('')}`); - } - /** - * Return the last two groups of this address as an IPv4 address string - * @returns {Address4} - * @example - * var address = new Address6('2001:4860:4001::1825:bf11'); - * address.to4().correctForm(); // '24.37.191.17' - */ - to4() { - const binary = this.binaryZeroPad().split(''); - return ipv4_1.Address4.fromHex(BigInt(`0b${binary.slice(96, 128).join('')}`).toString(16).padStart(8, '0')); - } - /** - * Return the v4-in-v6 form of the address - * @returns {String} - */ - to4in6() { - const address4 = this.to4(); - const address6 = new Address6(this.parsedAddress.slice(0, 6).join(':'), 6); - const correct = address6.correctForm(); - let infix = ''; - if (!/:$/.test(correct)) { - infix = ':'; - } - return correct + infix + address4.address; - } - /** - * Decodes the Teredo tunneling fields embedded in this address. Returns the - * Teredo prefix, server IPv4, client IPv4, raw flag bits, cone-NAT flag, - * UDP port, and Microsoft-format flag breakdown (reserved, universal/local, - * group/individual, nonce). Only meaningful for addresses in `2001::/32`. - */ - inspectTeredo() { - /* - - Bits 0 to 31 are set to the Teredo prefix (normally 2001:0000::/32). - - Bits 32 to 63 embed the primary IPv4 address of the Teredo server that - is used. - - Bits 64 to 79 can be used to define some flags. Currently only the - higher order bit is used; it is set to 1 if the Teredo client is - located behind a cone NAT, 0 otherwise. For Microsoft's Windows Vista - and Windows Server 2008 implementations, more bits are used. In those - implementations, the format for these 16 bits is "CRAAAAUG AAAAAAAA", - where "C" remains the "Cone" flag. The "R" bit is reserved for future - use. The "U" bit is for the Universal/Local flag (set to 0). The "G" bit - is Individual/Group flag (set to 0). The A bits are set to a 12-bit - randomly generated number chosen by the Teredo client to introduce - additional protection for the Teredo node against IPv6-based scanning - attacks. - - Bits 80 to 95 contains the obfuscated UDP port number. This is the - port number that is mapped by the NAT to the Teredo client with all - bits inverted. - - Bits 96 to 127 contains the obfuscated IPv4 address. This is the - public IPv4 address of the NAT with all bits inverted. - */ - const prefix = this.getBitsBase16(0, 32); - const bitsForUdpPort = this.getBits(80, 96); - // eslint-disable-next-line no-bitwise - const udpPort = (bitsForUdpPort ^ BigInt('0xffff')).toString(); - const server4 = ipv4_1.Address4.fromHex(this.getBitsBase16(32, 64)); - const bitsForClient4 = this.getBits(96, 128); - // eslint-disable-next-line no-bitwise - const client4 = ipv4_1.Address4.fromHex((bitsForClient4 ^ BigInt('0xffffffff')).toString(16).padStart(8, '0')); - const flagsBase2 = this.getBitsBase2(64, 80); - const coneNat = (0, common_1.testBit)(flagsBase2, 15); - const reserved = (0, common_1.testBit)(flagsBase2, 14); - const groupIndividual = (0, common_1.testBit)(flagsBase2, 8); - const universalLocal = (0, common_1.testBit)(flagsBase2, 9); - const nonce = BigInt(`0b${flagsBase2.slice(2, 6) + flagsBase2.slice(8, 16)}`).toString(10); - return { - prefix: `${prefix.slice(0, 4)}:${prefix.slice(4, 8)}`, - server4: server4.address, - client4: client4.address, - flags: flagsBase2, - coneNat, - microsoft: { - reserved, - universalLocal, - groupIndividual, - nonce, - }, - udpPort, - }; - } - /** - * Decodes the 6to4 tunneling fields embedded in this address. Returns the - * 6to4 prefix and the embedded IPv4 gateway address. Only meaningful for - * addresses in `2002::/16`. - */ - inspect6to4() { - /* - - Bits 0 to 15 are set to the 6to4 prefix (2002::/16). - - Bits 16 to 48 embed the IPv4 address of the 6to4 gateway that is used. - */ - const prefix = this.getBitsBase16(0, 16); - const gateway = ipv4_1.Address4.fromHex(this.getBitsBase16(16, 48)); - return { - prefix: prefix.slice(0, 4), - gateway: gateway.address, - }; - } - /** - * Return a v6 6to4 address from a v6 v4inv6 address - * @returns {Address6} - */ - to6to4() { - if (!this.is4()) { - return null; - } - const addr6to4 = [ - '2002', - this.getBitsBase16(96, 112), - this.getBitsBase16(112, 128), - '', - '/16', - ].join(':'); - return new Address6(addr6to4); - } - /** - * Embed an IPv4 address into a NAT64 IPv6 address using the encoding - * defined by [RFC 6052](https://datatracker.ietf.org/doc/html/rfc6052). - * The default prefix is the well-known prefix `64:ff9b::/96`. The prefix - * length must be one of 32, 40, 48, 56, 64, or 96; for prefixes shorter - * than /64 the IPv4 octets are split around the reserved bits 64–71. - * @example - * Address6.fromAddress4Nat64('192.0.2.33').correctForm(); // '64:ff9b::c000:221' - * Address6.fromAddress4Nat64('192.0.2.33', '2001:db8::/32').correctForm(); // '2001:db8:c000:221::' - */ - static fromAddress4Nat64(address, prefix = '64:ff9b::/96') { - const v4 = new ipv4_1.Address4(address); - const prefix6 = new Address6(prefix); - const pl = prefix6.subnetMask; - if (pl !== 32 && pl !== 40 && pl !== 48 && pl !== 56 && pl !== 64 && pl !== 96) { - throw new address_error_1.AddressError('NAT64 prefix length must be 32, 40, 48, 56, 64, or 96'); - } - const prefixBits = prefix6.binaryZeroPad(); - const v4Bits = v4.binaryZeroPad(); - let bits; - if (pl === 96) { - bits = prefixBits.slice(0, 96) + v4Bits; - } - else { - const beforeU = 64 - pl; - bits = - prefixBits.slice(0, pl) + - v4Bits.slice(0, beforeU) + - '00000000' + - v4Bits.slice(beforeU) + - '0'.repeat(128 - 72 - (32 - beforeU)); - } - const hex = BigInt(`0b${bits}`).toString(16).padStart(32, '0'); - const groups = []; - for (let i = 0; i < 8; i++) { - groups.push(hex.slice(i * 4, (i + 1) * 4)); - } - return new Address6(groups.join(':')); - } - /** - * Extract the embedded IPv4 address from a NAT64 IPv6 address using the - * encoding defined by [RFC 6052](https://datatracker.ietf.org/doc/html/rfc6052). - * The default prefix is the well-known prefix `64:ff9b::/96`. Returns - * `null` if this address is not contained within the given prefix. - * @example - * new Address6('64:ff9b::c000:221').toAddress4Nat64()!.correctForm(); // '192.0.2.33' - */ - toAddress4Nat64(prefix = '64:ff9b::/96') { - const prefix6 = new Address6(prefix); - const pl = prefix6.subnetMask; - if (pl !== 32 && pl !== 40 && pl !== 48 && pl !== 56 && pl !== 64 && pl !== 96) { - throw new address_error_1.AddressError('NAT64 prefix length must be 32, 40, 48, 56, 64, or 96'); - } - if (!this.isInSubnet(prefix6)) { - return null; - } - const bits = this.binaryZeroPad(); - let v4Bits; - if (pl === 96) { - v4Bits = bits.slice(96, 128); - } - else { - const beforeU = 64 - pl; - v4Bits = bits.slice(pl, pl + beforeU) + bits.slice(72, 72 + (32 - beforeU)); - } - const octets = []; - for (let i = 0; i < 4; i++) { - octets.push(parseInt(v4Bits.slice(i * 8, (i + 1) * 8), 2).toString()); - } - return new ipv4_1.Address4(octets.join('.')); - } - /** - * Return a byte array. - * - * To get a Node.js `Buffer`, wrap the result: `Buffer.from(address.toByteArray())`. - * @returns {Array} - */ - toByteArray() { - const valueWithoutPadding = this.bigInt().toString(16); - const leadingPad = '0'.repeat(valueWithoutPadding.length % 2); - const value = `${leadingPad}${valueWithoutPadding}`; - const bytes = []; - for (let i = 0, length = value.length; i < length; i += 2) { - bytes.push(parseInt(value.substring(i, i + 2), 16)); - } - return bytes; - } - /** - * Return an unsigned byte array. - * - * To get a Node.js `Buffer`, wrap the result: `Buffer.from(address.toUnsignedByteArray())`. - * @returns {Array} - */ - toUnsignedByteArray() { - return this.toByteArray().map(unsignByte); - } - /** - * Convert a byte array to an Address6 object. - * - * To convert from a Node.js `Buffer`, spread it: `Address6.fromByteArray([...buf])`. - * @returns {Address6} - */ - static fromByteArray(bytes) { - return this.fromUnsignedByteArray(bytes.map(unsignByte)); - } - /** - * Convert an unsigned byte array to an Address6 object. - * - * To convert from a Node.js `Buffer`, spread it: `Address6.fromUnsignedByteArray([...buf])`. - * @returns {Address6} - */ - static fromUnsignedByteArray(bytes) { - const BYTE_MAX = BigInt('256'); - let result = BigInt('0'); - let multiplier = BigInt('1'); - for (let i = bytes.length - 1; i >= 0; i--) { - result += multiplier * BigInt(bytes[i].toString(10)); - multiplier *= BYTE_MAX; - } - return Address6.fromBigInt(result); - } - /** - * Returns true if the address is in the canonical form, false otherwise - * @returns {boolean} - */ - isCanonical() { - return this.addressMinusSuffix === this.canonicalForm(); - } - /** - * Returns true if the address is a link local address, false otherwise - * @returns {boolean} - */ - isLinkLocal() { - // Zeroes are required, i.e. we can't check isInSubnet with 'fe80::/10' - if (this.getBitsBase2(0, 64) === - '1111111010000000000000000000000000000000000000000000000000000000') { - return true; - } - return false; - } - /** - * Returns true if the address is a multicast address, false otherwise - * @returns {boolean} - */ - isMulticast() { - const type = this.getType(); - return type === 'Multicast' || type.startsWith('Multicast '); - } - /** - * Returns true if the address was written in v4-in-v6 dotted-quad notation - * (e.g. `::ffff:127.0.0.1`), false otherwise. This is a notation-level flag - * and does not reflect whether the address bits lie in the IPv4-mapped - * (`::ffff:0:0/96`) subnet — for that, see {@link isMapped4}. - * @returns {boolean} - */ - is4() { - return this.v4; - } - /** - * Returns true if the address is an IPv4-mapped IPv6 address in - * `::ffff:0:0/96` ([RFC 4291 §2.5.5.2](https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2)), - * false otherwise. Unlike {@link is4}, this checks the underlying address - * bits rather than the textual notation, so `::ffff:127.0.0.1` and - * `::ffff:7f00:1` both return true. - * @returns {boolean} - */ - isMapped4() { - return this.isInSubnet(IPV4_MAPPED_SUBNET); - } - /** - * Returns true if the address is a Teredo address, false otherwise - * @returns {boolean} - */ - isTeredo() { - return this.isInSubnet(TEREDO_SUBNET); - } - /** - * Returns true if the address is a 6to4 address, false otherwise - * @returns {boolean} - */ - is6to4() { - return this.isInSubnet(SIX_TO_FOUR_SUBNET); - } - /** - * Returns true if the address is a loopback address, false otherwise - * @returns {boolean} - */ - isLoopback() { - return this.getType() === 'Loopback'; - } - /** - * Returns true if the address is a Unique Local Address in `fc00::/7` ([RFC 4193](https://datatracker.ietf.org/doc/html/rfc4193)). ULAs are the IPv6 equivalent of IPv4 [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) private addresses. - * @returns {boolean} - */ - isULA() { - return this.isInSubnet(ULA_SUBNET); - } - /** - * Returns true if the address is the unspecified address `::`. - * @returns {boolean} - */ - isUnspecified() { - return this.getType() === 'Unspecified'; - } - /** - * Returns true if the address is in the documentation prefix `2001:db8::/32` ([RFC 3849](https://datatracker.ietf.org/doc/html/rfc3849)). - * @returns {boolean} - */ - isDocumentation() { - return this.isInSubnet(DOCUMENTATION_SUBNET); - } - // #endregion - // #region HTML - /** - * Returns the address as an HTTP URL with the host bracketed, e.g. - * `http://[2001:db8::1]/`. If `optionalPort` is provided it is appended, - * e.g. `http://[2001:db8::1]:8080/`. - */ - href(optionalPort) { - if (optionalPort === undefined) { - optionalPort = ''; - } - else { - optionalPort = `:${optionalPort}`; - } - return `http://[${this.correctForm()}]${optionalPort}/`; - } - /** - * Returns an HTML `<a>` element whose `href` encodes the address in a URL - * hash fragment (default prefix `/#address=`). Useful for linking between - * pages of an address-inspector UI. - * @param options.className - CSS class for the rendered `<a>` element - * @param options.prefix - hash prefix prepended to the address (default `/#address=`) - * @param options.v4 - when true, render the address in v4-in-v6 form - */ - link(options) { - if (!options) { - options = {}; - } - if (options.className === undefined) { - options.className = ''; - } - if (options.prefix === undefined) { - options.prefix = '/#address='; - } - if (options.v4 === undefined) { - options.v4 = false; - } - let formFunction = this.correctForm; - if (options.v4) { - formFunction = this.to4in6; - } - const form = formFunction.call(this); - const safeHref = helpers.escapeHtml(`${options.prefix}${form}`); - const safeForm = helpers.escapeHtml(form); - if (options.className) { - const safeClass = helpers.escapeHtml(options.className); - return `<a href="${safeHref}" class="${safeClass}">${safeForm}</a>`; - } - return `<a href="${safeHref}">${safeForm}</a>`; - } - /** - * Groups an address - * @returns {String} - */ - group() { - if (this.elidedGroups === 0) { - // The simple case - return helpers.simpleGroup(this.addressMinusSuffix).join(':'); - } - assert(typeof this.elidedGroups === 'number'); - assert(typeof this.elisionBegin === 'number'); - // The elided case - const output = []; - const [left, right] = this.addressMinusSuffix.split('::'); - if (left.length) { - output.push(...helpers.simpleGroup(left)); - } - else { - output.push(''); - } - const classes = ['hover-group']; - for (let i = this.elisionBegin; i < this.elisionBegin + this.elidedGroups; i++) { - classes.push(`group-${i}`); - } - output.push(`<span class="${classes.join(' ')}"></span>`); - if (right.length) { - output.push(...helpers.simpleGroup(right, this.elisionEnd)); - } - else { - output.push(''); - } - if (this.is4()) { - assert(this.address4 instanceof ipv4_1.Address4); - output.pop(); - output.push(this.address4.groupForV6()); - } - return output.join(':'); - } - // #endregion - // #region Regular expressions - /** - * Generate a regular expression string that can be used to find or validate - * all variations of this address - * @param {boolean} substringSearch - * @returns {string} - */ - regularExpressionString(substringSearch = false) { - let output = []; - // TODO: revisit why this is necessary - const address6 = new Address6(this.correctForm()); - if (address6.elidedGroups === 0) { - // The simple case - output.push((0, regular_expressions_1.simpleRegularExpression)(address6.parsedAddress)); - } - else if (address6.elidedGroups === constants6.GROUPS) { - // A completely elided address - output.push((0, regular_expressions_1.possibleElisions)(constants6.GROUPS)); - } - else { - // A partially elided address - const halves = address6.address.split('::'); - if (halves[0].length) { - output.push((0, regular_expressions_1.simpleRegularExpression)(halves[0].split(':'))); - } - assert(typeof address6.elidedGroups === 'number'); - output.push((0, regular_expressions_1.possibleElisions)(address6.elidedGroups, halves[0].length !== 0, halves[1].length !== 0)); - if (halves[1].length) { - output.push((0, regular_expressions_1.simpleRegularExpression)(halves[1].split(':'))); - } - output = [output.join(':')]; - } - if (!substringSearch) { - output = [ - '(?=^|', - regular_expressions_1.ADDRESS_BOUNDARY, - '|[^\\w\\:])(', - ...output, - ')(?=[^\\w\\:]|', - regular_expressions_1.ADDRESS_BOUNDARY, - '|$)', - ]; - } - return output.join(''); - } - /** - * Generate a regular expression that can be used to find or validate all - * variations of this address. - * @param {boolean} substringSearch - * @returns {RegExp} - */ - regularExpression(substringSearch = false) { - return new RegExp(this.regularExpressionString(substringSearch), 'i'); - } -} -exports.Address6 = Address6; -const TYPE_SUBNETS = Object.keys(constants6.TYPES).map((subnet) => [ - new Address6(subnet), - constants6.TYPES[subnet], -]); -const TEREDO_SUBNET = new Address6('2001::/32'); -const SIX_TO_FOUR_SUBNET = new Address6('2002::/16'); -const ULA_SUBNET = new Address6('fc00::/7'); -const DOCUMENTATION_SUBNET = new Address6('2001:db8::/32'); -const IPV4_MAPPED_SUBNET = new Address6('::ffff:0:0/96'); - -},{"./address-error":1,"./common":2,"./ipv4":3,"./v4/constants":5,"./v6/constants":6,"./v6/helpers":7,"./v6/regular-expressions":8}],5:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RE_SUBNET_STRING = exports.RE_ADDRESS = exports.GROUPS = exports.BITS = void 0; -exports.BITS = 32; -exports.GROUPS = 4; -exports.RE_ADDRESS = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g; -exports.RE_SUBNET_STRING = /\/\d{1,2}$/; - -},{}],6:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RE_URL_WITH_PORT = exports.RE_URL = exports.RE_ZONE_STRING = exports.RE_SUBNET_STRING = exports.RE_BAD_ADDRESS = exports.RE_BAD_CHARACTERS = exports.TYPES = exports.SCOPES = exports.GROUPS = exports.BITS = void 0; -exports.BITS = 128; -exports.GROUPS = 8; -/** - * Represents IPv6 address scopes - * @memberof Address6 - * @static - */ -exports.SCOPES = { - 0: 'Reserved', - 1: 'Interface local', - 2: 'Link local', - 4: 'Admin local', - 5: 'Site local', - 8: 'Organization local', - 14: 'Global', - 15: 'Reserved', -}; -/** - * Represents IPv6 address types - * @memberof Address6 - * @static - */ -exports.TYPES = { - 'ff01::1/128': 'Multicast (All nodes on this interface)', - 'ff01::2/128': 'Multicast (All routers on this interface)', - 'ff02::1/128': 'Multicast (All nodes on this link)', - 'ff02::2/128': 'Multicast (All routers on this link)', - 'ff05::2/128': 'Multicast (All routers in this site)', - 'ff02::5/128': 'Multicast (OSPFv3 AllSPF routers)', - 'ff02::6/128': 'Multicast (OSPFv3 AllDR routers)', - 'ff02::9/128': 'Multicast (RIP routers)', - 'ff02::a/128': 'Multicast (EIGRP routers)', - 'ff02::d/128': 'Multicast (PIM routers)', - 'ff02::16/128': 'Multicast (MLDv2 reports)', - 'ff01::fb/128': 'Multicast (mDNSv6)', - 'ff02::fb/128': 'Multicast (mDNSv6)', - 'ff05::fb/128': 'Multicast (mDNSv6)', - 'ff02::1:2/128': 'Multicast (All DHCP servers and relay agents on this link)', - 'ff05::1:2/128': 'Multicast (All DHCP servers and relay agents in this site)', - 'ff02::1:3/128': 'Multicast (All DHCP servers on this link)', - 'ff05::1:3/128': 'Multicast (All DHCP servers in this site)', - '::/128': 'Unspecified', - '::1/128': 'Loopback', - 'ff00::/8': 'Multicast', - 'fe80::/10': 'Link-local unicast', - 'fc00::/7': 'Unique local', - '2002::/16': '6to4', - '2001:db8::/32': 'Documentation', - '64:ff9b::/96': 'NAT64 (well-known)', - '64:ff9b:1::/48': 'NAT64 (local-use)', -}; -/** - * A regular expression that matches bad characters in an IPv6 address - * @memberof Address6 - * @static - */ -exports.RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi; -/** - * A regular expression that matches an incorrect IPv6 address - * @memberof Address6 - * @static - */ -exports.RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\/$)/gi; -/** - * A regular expression that matches an IPv6 subnet - * @memberof Address6 - * @static - */ -exports.RE_SUBNET_STRING = /\/\d{1,3}(?=%|$)/; -/** - * A regular expression that matches an IPv6 zone - * @memberof Address6 - * @static - */ -exports.RE_ZONE_STRING = /%.*$/; -exports.RE_URL = /^\[{0,1}([0-9a-f:]+)\]{0,1}/; -exports.RE_URL_WITH_PORT = /\[([0-9a-f:]+)\]:([0-9]{1,5})/; - -},{}],7:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.escapeHtml = escapeHtml; -exports.spanAllZeroes = spanAllZeroes; -exports.spanAll = spanAll; -exports.spanLeadingZeroes = spanLeadingZeroes; -exports.simpleGroup = simpleGroup; -function escapeHtml(s) { - return s - .replace(/&/g, '&') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} -/** - * @returns {String} the string with all zeroes contained in a <span> - */ -function spanAllZeroes(s) { - return escapeHtml(s).replace(/(0+)/g, '<span class="zero">$1</span>'); -} -/** - * @returns {String} the string with each character contained in a <span> - */ -function spanAll(s, offset = 0) { - const letters = s.split(''); - return letters - .map((n, i) => `<span class="digit value-${escapeHtml(n)} position-${i + offset}">${spanAllZeroes(n)}</span>`) - .join(''); -} -function spanLeadingZeroesSimple(group) { - return escapeHtml(group).replace(/^(0+)/, '<span class="zero">$1</span>'); -} -/** - * @returns {String} the string with leading zeroes contained in a <span> - */ -function spanLeadingZeroes(address) { - const groups = address.split(':'); - return groups.map((g) => spanLeadingZeroesSimple(g)).join(':'); -} -/** - * Groups an address - * @returns {String} a grouped address - */ -function simpleGroup(addressString, offset = 0) { - const groups = addressString.split(':'); - return groups.map((g, i) => { - if (/group-v4/.test(g)) { - return g; - } - return `<span class="hover-group group-${i + offset}">${spanLeadingZeroesSimple(g)}</span>`; - }); -} - -},{}],8:[function(require,module,exports){ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ADDRESS_BOUNDARY = void 0; -exports.groupPossibilities = groupPossibilities; -exports.padGroup = padGroup; -exports.simpleRegularExpression = simpleRegularExpression; -exports.possibleElisions = possibleElisions; -const v6 = __importStar(require("./constants")); -function groupPossibilities(possibilities) { - return `(${possibilities.join('|')})`; -} -function padGroup(group) { - if (group.length < 4) { - return `0{0,${4 - group.length}}${group}`; - } - return group; -} -exports.ADDRESS_BOUNDARY = '[^A-Fa-f0-9:]'; -function simpleRegularExpression(groups) { - const zeroIndexes = []; - groups.forEach((group, i) => { - const groupInteger = parseInt(group, 16); - if (groupInteger === 0) { - zeroIndexes.push(i); - } - }); - // You can technically elide a single 0, this creates the regular expressions - // to match that eventuality - const possibilities = zeroIndexes.map((zeroIndex) => groups - .map((group, i) => { - if (i === zeroIndex) { - const elision = i === 0 || i === v6.GROUPS - 1 ? ':' : ''; - return groupPossibilities([padGroup(group), elision]); - } - return padGroup(group); - }) - .join(':')); - // The simplest case - possibilities.push(groups.map(padGroup).join(':')); - return groupPossibilities(possibilities); -} -function possibleElisions(elidedGroups, moreLeft, moreRight) { - const left = moreLeft ? '' : ':'; - const right = moreRight ? '' : ':'; - const possibilities = []; - // 1. elision of everything (::) - if (!moreLeft && !moreRight) { - possibilities.push('::'); - } - // 2. complete elision of the middle - if (moreLeft && moreRight) { - possibilities.push(''); - } - if ((moreRight && !moreLeft) || (!moreRight && moreLeft)) { - // 3. complete elision of one side - possibilities.push(':'); - } - // 4. elision from the left side - possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`); - // 5. elision from the right side - possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`); - // 6. no elision - possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`); - // 7. elision (including sloppy elision) from the middle - for (let groups = 1; groups < elidedGroups - 1; groups++) { - for (let position = 1; position < elidedGroups - groups; position++) { - possibilities.push(`(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`); - } - } - return groupPossibilities(possibilities); -} - -},{"./constants":6}],"ip-address":[function(require,module,exports){ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.v6 = exports.AddressError = exports.Address6 = exports.Address4 = void 0; -var ipv4_1 = require("./ipv4"); -Object.defineProperty(exports, "Address4", { enumerable: true, get: function () { return ipv4_1.Address4; } }); -var ipv6_1 = require("./ipv6"); -Object.defineProperty(exports, "Address6", { enumerable: true, get: function () { return ipv6_1.Address6; } }); -var address_error_1 = require("./address-error"); -Object.defineProperty(exports, "AddressError", { enumerable: true, get: function () { return address_error_1.AddressError; } }); -const helpers = __importStar(require("./v6/helpers")); -exports.v6 = { helpers }; - -},{"./address-error":1,"./ipv4":3,"./ipv6":4,"./v6/helpers":7}]},{},[]); diff --git a/assets/js/main.js b/assets/js/main.js deleted file mode 100644 index ef57764..0000000 --- a/assets/js/main.js +++ /dev/null @@ -1,1117 +0,0 @@ -var iface = 'wg0'; - -function parse_placeholder(val) { - return val; -} - -function parse_boolean(val) { - if (val === 'true') - return true; - if (val === 'false') - return false; - throw new Error('Cannot parse as a boolean.'); -} - -function parse_boolean_or_string(val) { - if (typeof val == 'boolean') { - return val; - } - return parse_boolean(val); -} - -function try_parse_boolean_or_string(val) { - try { - return parse_boolean_or_string(val); - } catch (err) { - return null; - } -} - -function parse_positive_integer(src) { - var val = parseInt(src); - if (isNaN(val) || val < 1) - throw new Error('Cannot parse as a positive integer.'); - return val; -} - -function parse_keepalive(src) { - return parse_positive_integer(src); -} - -function parse_tunnel_everything(src) { - return parse_boolean_or_string(src); -} - -function parse_endpoint(val) { - val = val.trim(); - if (val.length == 0) - throw new Error('Server endpoint cannot be an empty string.'); - if (!val.match(/^.+:[0-9]+$/)) - throw new Error('Please specify a host and a port in the HOST:PORT format.'); - return val; -} - -function parse_key(val) { - val = val.trim(); - if (val.length == 0) - throw new Error('Key as used by WireGuard cannot be an empty string.'); - if (!val.match(/^[0-9a-zA-Z+/=]+$/)) - throw new Error('Key as used by WireGuard must be Base64-encoded.'); - return val; -} - -var ip_address_lib = require('ip-address'); - -function parse_ip_internal(src, parser) { - src = src.trim(); - if (src.length == 0) - throw new Error('IP address cannot be an empty string.'); - - try { - var result = new parser(src); - } catch (err) { - throw new Error('Sorry, IP address validation failed with the following error:\n' + err.message); - } - return result; -} - -function parse_ip(src, parser) { - var result = parse_ip_internal(src, parser); - - if (result.parsedSubnet) - throw new Error('Please specify address without the netmask.') - - return result.correctForm(); -} - -function parse_ip_cidr(src, parser) { - var result = parse_ip_internal(src, parser); - - if (!result.parsedSubnet) - throw new Error('Please specify address using the CIDR format (including the netmask).'); - - var addr = result.correctForm(); - var netmask = result.subnetMask; - var network_id = result.startAddress().correctForm(); - - return [addr, netmask, network_id]; -} - -function parse_ipv4(src) { - var result = parse_ip(src, ip_address_lib.Address4); - return result; -} - -function parse_ipv4_cidr(src) { - var parser = ip_address_lib.Address4; - var result = parse_ip_cidr(src, ip_address_lib.Address4); - - var addr = result[0]; - var netmask = result[1]; - var network_id = result[2]; - var broadcast_addr = new parser(src).endAddress().correctForm(); - - if (addr == network_id && netmask != 32) - throw new Error('Please use a real host IP address, not a network identifier.'); - if (addr == broadcast_addr && netmask != 32) - throw new Error('Please use a real host IP address, not a broadcast address.'); - - return result; -} - -function parse_ipv6(val) { - return parse_ip(val, ip_address_lib.Address6); -} - -function parse_ipv6_cidr(val) { - return parse_ip_cidr(val, ip_address_lib.Address6); -} - -function parse_ip_list(val, addr_parser) { - var parts = val.split(/, */); - var result = []; - parts.forEach(function(part) { - result.push(addr_parser(part)); - }); - return result; -} - -var Input = function(name) { - this.name = name; - - if (this.is_checkbox()) { - // Keep the checked property and the value consistent. - $(this.input_id()).change(function(evt) { - var elem = evt.target; - elem.value = elem.checked; - }); - } -} - -Input.prototype.input_id = function() { - return '#param_' + this.name; -} - -Input.prototype.error_id = function() { - return this.input_id() + '_error'; -} - -Input.prototype.container_id = function() { - return this.input_id() + '_container'; -} - -Input.prototype.is_checkbox = function() { - return $(this.input_id()).attr('type') == 'checkbox'; -} - -Input.prototype.value = function() { - return $(this.input_id()).val(); -} - -Input.prototype.has_value = function() { - return this.value().length > 0; -} - -Input.prototype.set = function(value) { - if (this.is_checkbox() && try_parse_boolean_or_string(value)) - $(this.input_id()).prop('checked', true); - $(this.input_id()).val(value); -} - -Input.prototype.show = function() { - $(this.container_id()).show(); -} - -Input.prototype.hide = function() { - $(this.container_id()).hide(); -} - -Input.prototype.show_error = function(msg) { - this.show(); - $(this.error_id()).text(msg); - $(this.error_id()).show(); -} - -Input.prototype.hide_error = function() { - $(this.error_id()).hide(); -} - -var Value = function(name, parser) { - this.name = name; - this.input = new Input(name); - this.parser = parser; - this.optional = false; - this.advanced = false; - - this.value = null; - this.error = null; -} - -Value.prototype.parse = function() { - return this.parse_from(this.input.value()); -} - -Value.prototype.parse_from = function(src) { - try { - var value = src; - switch (typeof value) { - case 'boolean': - value = this.parser(value); - break; - default: - if (this.optional && value.length == 0) - break; - value = this.parser(value); - break; - } - this.input.set(src); - this.set_value(value); - return true; - } catch (err) { - this.set_error(err.message); - return false; - } -} - -Value.prototype.set_value = function(value) { - this.value = value; - this.error = null; - this.input.hide_error(); -} - -Value.prototype.set_error = function(msg) { - this.value = null; - this.error = msg; - this.input.show_error(this.error); -} - -Value.prototype.is_set = function() { - return this.input.has_value(); -} - -Value.prototype.show = function() { - this.input.show(); -} - -Value.prototype.hide = function() { - this.input.hide(); -} - -var Endpoint = function(name) { - Value.call(this, name, parse_endpoint); -} - -Endpoint.prototype = Object.create(Value.prototype); -Endpoint.prototype.constructor = Endpoint; - -var Key = function(name) { - Value.call(this, name, parse_key); -} - -Key.prototype = Object.create(Value.prototype); -Key.prototype.constructor = Key; - -var IPAddr = function(name, parser) { - Value.call(this, name, parser); -} - -IPAddr.prototype = Object.create(Value.prototype); -IPAddr.prototype.constructor = IPAddr; - -IPAddr.prototype.set_value = function(value) { - Value.prototype.set_value.call(this, value); - - this.addr = this.value[0]; - this.netmask = this.value[1]; - this.network_id = this.value[2]; -} - -IPAddr.prototype.full_address = function() { - return this.addr + '/' + this.netmask; -} - -IPAddr.prototype.allowed_ips_client = function() { - // As of this writing, _every_ tool accepts something like 192.168.0.1/24 - // here, _except_ for the Android app, which _requires_ the least - // significant bits to be zeroed out. - return this.network_id + '/' + this.netmask; -} - -var IPv4 = function(name) { - IPAddr.call(this, name, parse_ipv4_cidr); -} - -IPv4.prototype = Object.create(IPAddr.prototype); -IPv4.prototype.constructor = IPv4; - -IPv4.prototype.allowed_ips_server = function() { - return this.addr + '/' + 32; -} - -var IPv6 = function(name) { - IPAddr.call(this, name, parse_ipv6_cidr); -} - -IPv6.prototype = Object.create(IPAddr.prototype); -IPv6.prototype.constructor = IPv6; - -IPv6.prototype.allowed_ips_server = function() { - return this.addr + '/' + 128; -} - -var Keepalive = function(name) { - Value.call(this, name, parse_keepalive); - this.optional = true; - this.advanced = true; -} - -Keepalive.prototype = Object.create(Value.prototype); -Keepalive.prototype.constructor = Keepalive; - -var TunnelEverything = function(name) { - Value.call(this, name, parse_tunnel_everything); - this.optional = true; - this.advanced = true; -} - -TunnelEverything.prototype = Object.create(Value.prototype); -TunnelEverything.prototype.constructor = TunnelEverything; - -var IPAddrList = function(name, parser) { - Value.call(this, name, function(val) { - return parse_ip_list(val, parser); - }); -} - -IPAddrList.prototype = Object.create(Value.prototype); -IPAddrList.prototype.constructor = IPAddrList; - -var DNS_IPv4 = function(name) { - IPAddrList.call(this, name, parse_ipv4); - this.optional = true; - this.advanced = true; -} - -DNS_IPv4.prototype = Object.create(IPAddrList.prototype); -DNS_IPv4.prototype.constructor = DNS_IPv4; - -var DNS_IPv6 = function(name) { - IPAddrList.call(this, name, parse_ipv6); - this.optional = true; - this.advanced = true; -} - -DNS_IPv6.prototype = Object.create(IPAddrList.prototype); -DNS_IPv6.prototype.constructor = DNS_IPv6; - -var Data = function() { - this.server_public = new Key('server_public'); - this.server_endpoint = new Endpoint('server_endpoint'); - this.preshared = new Key('preshared'); - this.client_public = new Key('client_public'); - this.client_private = new Key('client_private'); - this.client_ipv4 = new IPv4('client_ipv4'); - this.client_ipv6 = new IPv6('client_ipv6'); - this.keepalive = new Keepalive('keepalive'); - this.tunnel_everything = new TunnelEverything('tunnel_everything'); - this.dns_ipv4 = new DNS_IPv4('dns_ipv4'); - this.dns_ipv6 = new DNS_IPv6('dns_ipv6'); - - this.values = [ - this.server_public, - this.server_endpoint, - this.preshared, - this.client_public, - this.client_private, - this.client_ipv4, - this.client_ipv6, - this.keepalive, - this.tunnel_everything, - this.dns_ipv4, - this.dns_ipv6 - ]; -} - -Data.prototype.set_from_url = function(url) { - var url = new URL(url); - var params = new URLSearchParams(url.search); - var has = false; - - this.values.forEach(function(value) { - if (params.has(value.name)) { - has = true; - value.input.set(params.get(value.name)); - } - }); - - return has; -} - -Data.prototype.set_from_this_url = function() { - return this.set_from_url(window.location.href); -}; - -Data.prototype.get_perma_url = function() { - var url = new URL(window.location.href); - url.search = ''; - var params = new URLSearchParams(); - this.values.forEach(function(value) { - params.append(value.name, value.input.value()); - }); - url.search = params.toString(); - return url.toString(); -} - -Data.prototype.parse = function() { - var success = true; - - this.values.forEach(function(value) { - if (!value.parse()) - success = false; - }); - - if (success) { - if (!$('#advanced_params').prop('checked')) - this.hide_advanced(); - this.hide_error(); - } else { - this.show_error(); - } - return success; -} - -Data.prototype.show_error = function() { - $('#params_error').text('Please correct the input errors above first.'); - $('#params_error').show(); -} - -Data.prototype.hide_error = function() { - $('#params_error').hide(); -} - -Data.prototype.show_advanced = function() { - this.values.forEach(function(value) { - if (!value.advanced) - return; - value.show(); - }); -} - -Data.prototype.hide_advanced = function() { - this.values.forEach(function(value) { - if (!value.advanced) - return; - value.hide(); - }); -} - -function basename(path) { - return path.substring(path.lastIndexOf('/') + 1); -} - -function download(file_name, text) { - // The type must be application/octet-stream; if it's text/plain, the - // Android Chrome appends the .txt suffix to the downloaded file names. - var blob = new Blob([text], {type: 'application/octet-stream'}); - var url = URL.createObjectURL(blob); - - var link = $('<a/>'); - link.prop('href', url); - link.prop('download', file_name); - - // Whoever thought of this [0] is fucking crazy: - // https://stackoverflow.com/a/36483380/514684 - // It literally silently does nothing if this is omitted. - link[0].click(); -} - -function copy_to_clipboard(elem) { - // This is rather neat, thanks: - // https://stackoverflow.com/a/22581382/514684 - - // Apparently, you can only do execCommand('copy') from <input> and - // <textarea> elements. Another stupidity, I'm so surprised. - var is_input = elem.tagName === 'INPUT' || elem.tagName === 'TEXTAREA'; - var orig_select_start, orig_select_end; - - if (is_input) { - var target = elem; - orig_select_start = elem.selectionStart; - orig_select_end = elem.selectionEnd; - } else { - var target = document.createElement('textarea'); - target.textContent = elem.textContent; - document.body.appendChild(target); - } - - var current_focus = document.activeElement; - target.focus(); - target.setSelectionRange(0, target.value.length); - - var succeed; - try { - // Supposedly, this is deprecated. But the "proper" method sucks balls, - // so I'm using this one: - // https://stackoverflow.com/a/30810322/514684 - succeed = document.execCommand('copy'); - } catch (e) { - succeed = false; - } - - if (current_focus && typeof current_focus.focus === "function") { - current_focus.focus(); - } - - if (is_input) { - elem.setSelectionRange(orig_select_start, orig_select_end); - } else { - document.body.removeChild(target); - } - return succeed; -} - -var OverlayButton = function(title, icon, click_handler) { - this.btn = $('<button/>', { - 'class': 'btn btn-default', - type: 'button', - on: { - click: click_handler - } - }); - this.title = title; - this.icon = icon; - this.show_icon(title, icon); -} - -OverlayButton.prototype.show_icon = function(title, icon) { - this.btn.empty(); - this.btn.prop('title', title); - this.btn.append($('<span/>', { - 'class': `glyphicon glyphicon-${icon}` - })); -} - -var EditButton = function(cfg) { - var self = this; - OverlayButton.call(this, 'Edit', 'pencil', function(evt) { - return self.on_click(cfg); - }); -} - -EditButton.prototype = Object.create(OverlayButton.prototype); -EditButton.prototype.constructor = EditButton; - -EditButton.prototype.on_click = function(cfg) { - var editable = cfg.pre.prop('isContentEditable'); - cfg.pre.prop('contentEditable', !editable); - if (editable) { - this.show_icon(this.title, this.icon); - this.btn.blur(); // a.k.a. unfocus - } else { - this.show_icon('Save', 'floppy-disk'); - cfg.pre.focus(); - } -} - -var DownloadButton = function(cfg) { - var self = this; - OverlayButton.call(this, 'Download', 'download-alt', function(evt) { - return self.on_click(cfg); - }); -} - -DownloadButton.prototype = Object.create(OverlayButton.prototype); -DownloadButton.prototype.constructor = DownloadButton; - -DownloadButton.prototype.on_click = function(cfg) { - return download(basename(cfg.name), cfg.pre.text()); -} - -var CopyButton = function(cfg) { - var self = this; - OverlayButton.call(this, 'Copy', 'copy', function(evt) { - return self.on_click(cfg); - }); -} - -CopyButton.prototype = Object.create(OverlayButton.prototype); -CopyButton.prototype.constructor = CopyButton; - -CopyButton.prototype.on_click = function(cfg) { - return copy_to_clipboard(cfg.pre[0]); -} - -var ConfigFile = function(name, text) { - this.name = name; - this.text = text; - this.pre = $('<pre/>').text(text); -} - -ConfigFile.prototype.format = function() { - var buttons = [ - new CopyButton(this), - new DownloadButton(this), - new EditButton(this) - ]; - - var overlay = $('<div class="pre_buttons btn-group btn-group-xs" role="group"/>'); - buttons.forEach(function(btn) { - overlay.append(btn.btn); - }); - - return $('<div class="pre_container"/>') - .append(this.pre) - .append(overlay); -} - -var QRCode = function(pre) { - this.pre = pre; -} - -function draw_qr_code(container, text) { - var canvas = $('<canvas/>'); - var qr = new QRious({ - element: canvas[0], - value: text, - size: 350 - }); - container.append(canvas); -} - -QRCode.prototype.format = function() { - var container = $('<div class="text-center"/>'); - var pre = this.pre; - pre.on('blur', function() { - container.empty(); - draw_qr_code(container, pre.text()); - }); - draw_qr_code(container, pre.text()); - return container; -} - -var catchall_ipv4 = '0.0.0.0/0'; -var catchall_ipv6 = '::/0'; - -function wg_quick_client_file(data) { - var path = `/etc/wireguard/${iface}.conf`; - - var dns = ''; - var allowed_ips = `${data.client_ipv4.allowed_ips_client()}, ${data.client_ipv6.allowed_ips_client()}`; - var keepalive = ''; - - if (data.tunnel_everything.value) { - dns = `DNS = ${(data.dns_ipv4.value.concat(data.dns_ipv6.value)).join(',')}\n`; - allowed_ips = `${catchall_ipv4}, ${catchall_ipv6}`; - } - if (data.keepalive.is_set()) { - keepalive = `PersistentKeepalive = ${data.keepalive.value}\n`; - } - - var contents = -`# On the client, put this to -# ${path} -# and either -# * start the wg-quick@${iface} systemd service, -# * or run \`wg-quick up ${iface}\`. - -[Interface] -PrivateKey = ${data.client_private.value} -Address = ${data.client_ipv4.full_address()}, ${data.client_ipv6.full_address()} -${dns} -[Peer] -Endpoint = ${data.server_endpoint.value} -PublicKey = ${data.server_public.value} -PresharedKey = ${data.preshared.value} -AllowedIPs = ${allowed_ips} -${keepalive}`; - - return new ConfigFile(path, contents); -} - -function wg_quick_server_file(data) { - var path = `/etc/wireguard/${iface}.conf`; - - var contents = -`# On the server, add this to -# ${path} -# and restart the wg-quick@${iface} systemd service. - -# Previous contents goes here... - -[Peer] -PublicKey = ${data.client_public.value} -PresharedKey = ${data.preshared.value} -AllowedIPs = ${data.client_ipv4.allowed_ips_server()}, ${data.client_ipv6.allowed_ips_server()} -`; - - return new ConfigFile(path, contents); -} - -function systemd_client_netdev_file(data) { - var path = `/etc/systemd/network/${iface}.netdev`; - - var fwmark = ''; - var allowed_ips = `${data.client_ipv4.allowed_ips_client()}, ${data.client_ipv6.allowed_ips_client()}`; - var keepalive = ''; - - if (data.tunnel_everything.value) { - fwmark = `FirewallMark = 0x8888\n`; - allowed_ips = `${catchall_ipv4}, ${catchall_ipv6}`; - } - if (data.keepalive.is_set()) { - keepalive = `PersistentKeepalive = ${data.keepalive.value}\n`; - } - - var contents = -`# On the client, you need two files. Put this into -# ${path} -# and after you're done with both files, run -# systemctl daemon-reload -# systemctl restart systemd-networkd - -[NetDev] -Name = ${iface} -Kind = wireguard - -[WireGuard] -PrivateKey = ${data.client_private.value} -${fwmark} -[WireGuardPeer] -Endpoint = ${data.server_endpoint.value} -PublicKey = ${data.server_public.value} -PresharedKey = ${data.preshared.value} -AllowedIPs = ${allowed_ips} -${keepalive}`; - - return new ConfigFile(path, contents); -} - -function systemd_client_network_file(data) { - var path = `/etc/systemd/network/${iface}.network`; - - var dns = ''; - - if (data.tunnel_everything.value) { - data.dns_ipv4.value.forEach(function(val) { - dns += `DNS = ${val}\n`; - }); - data.dns_ipv6.value.forEach(function(val) { - dns += `DNS = ${val}\n`; - }); - dns += -`Domains = ~. -DNSDefaultRoute = true - -[RoutingPolicyRule] -FirewallMark = 0x8888 -InvertRule = true -Table = 1000 -Priority = 10 -`; - } - - var contents = -`# This is the second file. Put this into -# ${path} -# and if you're done with the first file already, run -# systemctl daemon-reload -# systemctl restart systemd-networkd - -[Match] -Name = ${iface} - -[Network] -Address = ${data.client_ipv4.full_address()} -Address = ${data.client_ipv6.full_address()} -${dns}`; - - // I know the above doesn't work, but I don't care, systemd-networkd is a - // fucking nightmare. - return new ConfigFile(path, contents); -} - -function systemd_server_netdev_file(data) { - var path = `/etc/systemd/network/${iface}.netdev`; - - var contents = -`# On the server, add this to -# ${path} -# and run -# systemctl daemon-reload -# systemctl restart systemd-networkd - -# Previous contents goes here... - -[WireGuardPeer] -PublicKey = ${data.client_public.value} -PresharedKey = ${data.preshared.value} -AllowedIPs = ${data.client_ipv4.allowed_ips_server()}, ${data.client_ipv6.allowed_ips_server()} -`; - - return new ConfigFile(path, contents); -} - -function nmcli_client_file(data) { - var path = `/etc/NetworkManager/system-connections/${iface}.nmconnection`; - - var allowed_ips = `${data.client_ipv4.allowed_ips_client()};${data.client_ipv6.allowed_ips_client()};`; - var dns4 = ''; - var dns6 = ''; - var keepalive = ''; - - if (data.tunnel_everything.value) { - allowed_ips = `${catchall_ipv4};${catchall_ipv6};`; - dns4 = -`dns=${data.dns_ipv4.value.join(';')}; -dns-priority=-10 -dns-search=~; -`; - dns6 = -`dns=${data.dns_ipv6.value.join(';')}; -dns-priority=-10 -dns-search=~; -`; - } - if (data.keepalive.is_set()) { - keepalive = `persistent-keepalive=${data.keepalive.value}\n`; - } - - var contents = -`# On the client, put this to -# ${path} -# and run -# chmod 0600 ${path} -# nmcli c reload -# nmcli c up ${iface} - -[connection] -id=${iface} -type=wireguard -interface-name=${iface} - -[wireguard] -private-key=${data.client_private.value} -private-key-flags=0 - -[wireguard-peer.${data.server_public.value}] -endpoint=${data.server_endpoint.value} -preshared-key=${data.preshared.value} -preshared-key-flags=0 -allowed-ips=${allowed_ips} -${keepalive} -[ipv4] -address1=${data.client_ipv4.full_address()} -method=manual -${dns4} -[ipv6] -addr-gen-mode=stable-privacy -address1=${data.client_ipv6.full_address()} -method=manual -${dns6}`; - - return new ConfigFile(path, contents); -} - -function nmcli_server_file(data) { - var path = `/etc/NetworkManager/system-connections/${iface}.nmconnection`; - - var contents = -`# On the server, add this to -# ${path} -# and run -# nmcli c reload -# nmcli c up ${iface} - -# Previous contents goes here... - -[wireguard-peer.${data.client_public.value}] -preshared-key=${data.preshared.value} -preshared-key-flags=0 -allowed-ips=${data.client_ipv4.allowed_ips_server()};${data.client_ipv6.allowed_ips_server()}; -`; - - return new ConfigFile(path, contents); -} - -var shell_info = -`# Make sure your .bash_history or whatever is -# secure before running this. -# Better yet, put into a file and run (possibly, using sudo).`; - -function manual_client_script(data) { - var path = 'client_setup.sh'; - - var allowed_ips = `${data.client_ipv4.allowed_ips_client()},${data.client_ipv6.allowed_ips_client()}`; - var keepalive = ''; - - if (data.tunnel_everything.value) { - allowed_ips = `${catchall_ipv4},${catchall_ipv6}`; - } - if (data.keepalive.is_set()) { - keepalive = ` persistent-keepalive ${data.keepalive.value} \\\n`; - } - - var contents = -`# On the client, run this to set up a connection. -${shell_info} - -ip link add dev ${iface} type wireguard -ip addr add ${data.client_ipv4.full_address()} dev ${iface} -ip addr add ${data.client_ipv6.full_address()} dev ${iface} -wg set ${iface} \\ - private-key <( echo ${data.client_private.value} ) -wg set ${iface} \\ - peer ${data.server_public.value} \\ - preshared-key <( echo ${data.preshared.value} ) \\ -${keepalive} endpoint ${data.server_endpoint.value} \\ - allowed-ips ${allowed_ips} -ip link set ${iface} up -`; - - return new ConfigFile(path, contents); -} - -function manual_server_script(data) { - var path = 'server_setup.sh'; - - var contents = -`# On the server, run this to tweak an existing connection. -${shell_info} - -wg set ${iface} \\ - peer ${data.client_public.value} \\ - preshared-key <( echo ${data.preshared.value} ) \\ - allowed-ips ${data.client_ipv4.allowed_ips_server()},${data.client_ipv6.allowed_ips_server()} -`; - - return new ConfigFile(path, contents); -} - -var Guide = function(name) { - this.name = name; -} - -Guide.prototype.format = function(data) { - var container = $('<div/>'); - container.append($('<h2/>').html(this.name)); - return container; -} - -var GuidePermaURL = function() { - Guide.call(this, 'Permanent URL'); -} - -GuidePermaURL.prototype = Object.create(Guide.prototype); -GuidePermaURL.prototype.constructor = GuidePermaURL; - -GuidePermaURL.prototype.format = function(data) { - var container = Guide.prototype.format.call(this, data); - container - .append($('<p/>').text('Use the following URL to share this configuration. Be careful: it probably contains sensitive data, like your private keys.')) - .append($('<pre/>').append($('<a/>', {href: data.get_perma_url()}).text(data.get_perma_url()))); - return container; -} - -var GuideServerClient = function(name) { - Guide.call(this, name); -} - -GuideServerClient.prototype = Object.create(Guide.prototype); -GuideServerClient.prototype.constructor = GuideServerClient; - -GuideServerClient.prototype.for_server = function(data) { return []; } -GuideServerClient.prototype.for_client = function(data) { return []; } - -GuideServerClient.prototype.join_guides = function(guides) { - var container = $('<div/>'); - guides.forEach(function(guide) { - container.append(guide.format()); - }); - return container; -} - -GuideServerClient.prototype.format = function(data) { - var container = Guide.prototype.format.call(this, data); - container - .append($('<div class="row"/>') - .append($('<div class="col-md-6"/>') - .append(this.join_guides(this.for_server(data)))) - .append($('<div class="col-md-6"/>') - .append(this.join_guides(this.for_client(data))))); - return container; -} - -var GuideWgQuick = function() { - GuideServerClient.call(this, 'wg-quick'); -} - -GuideWgQuick.prototype = Object.create(GuideServerClient.prototype); -GuideWgQuick.prototype.constructor = GuideWgQuick; - -GuideWgQuick.prototype.for_client = function(data) { - var config = wg_quick_client_file(data); - var qr = new QRCode(config.pre); - return [config, qr]; -} - -GuideWgQuick.prototype.for_server = function(data) { - return [wg_quick_server_file(data)]; -} - -var GuideSystemd = function() { - GuideServerClient.call(this, 'systemd-networkd'); -} - -GuideSystemd.prototype = Object.create(GuideServerClient.prototype); -GuideSystemd.prototype.constructor = GuideSystemd; - -GuideSystemd.prototype.for_client = function(data) { - return [systemd_client_netdev_file(data), systemd_client_network_file(data)]; -} - -GuideSystemd.prototype.for_server = function(data) { - return [systemd_server_netdev_file(data)]; -} - -var GuideNetworkManager = function() { - GuideServerClient.call(this, 'NetworkManager'); -} - -GuideNetworkManager.prototype = Object.create(GuideServerClient.prototype); -GuideNetworkManager.prototype.constructor = GuideNetworkManager; - -GuideNetworkManager.prototype.for_client = function(data) { - return [nmcli_client_file(data)]; -} - -GuideNetworkManager.prototype.for_server = function(data) { - return [nmcli_server_file(data)]; -} - -var GuideManual = function() { - GuideServerClient.call(this, '<code>ip</code> & <code>wg</code>'); -} - -GuideManual.prototype = Object.create(GuideServerClient.prototype); -GuideManual.prototype.constructor = GuideManual; - -GuideManual.prototype.for_client = function(data) { - return [manual_client_script(data)]; -} - -GuideManual.prototype.for_server = function(data) { - return [manual_server_script(data)]; -} - -function clear_guides() { - $('#guides').empty(); -} - -function add_guide(guide, data) { - $('#guides').append(guide.format(data)); -} - -function guides_from_data(data) { - if (!data.parse()) { - return; - } - - var guides = [ - new GuidePermaURL(), - new GuideWgQuick(), - new GuideNetworkManager(), - new GuideSystemd(), - new GuideManual() - ]; - - guides.forEach(function(guide) { - add_guide(guide, data); - }); -} - -function form_on_submit() { - clear_guides(); - - var data = new Data(); - guides_from_data(data); - - return false; -} - -function advanced_params_on_click(input) { - var data = new Data(); - if (input.checked) - data.show_advanced(); - else - data.hide_advanced(); -} - -function main() { - var data = new Data(); - if (data.set_from_this_url()) - guides_from_data(data); -} - -$(function() { - main(); -}); |
