123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- // Copyright 2011 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview This file contains classes to handle IPv4 and IPv6 addresses.
- * This implementation is mostly based on Google's project:
- * http://code.google.com/p/ipaddr-py/.
- *
- */
- goog.provide('goog.net.IpAddress');
- goog.provide('goog.net.Ipv4Address');
- goog.provide('goog.net.Ipv6Address');
- goog.require('goog.array');
- goog.require('goog.math.Integer');
- goog.require('goog.object');
- goog.require('goog.string');
- /**
- * Abstract class defining an IP Address.
- *
- * Please use goog.net.IpAddress static methods or
- * goog.net.Ipv4Address/Ipv6Address classes.
- *
- * @param {!goog.math.Integer} address The Ip Address.
- * @param {number} version The version number (4, 6).
- * @constructor
- */
- goog.net.IpAddress = function(address, version) {
- /**
- * The IP Address.
- * @type {!goog.math.Integer}
- * @private
- */
- this.ip_ = address;
- /**
- * The IP Address version.
- * @type {number}
- * @private
- */
- this.version_ = version;
- };
- /**
- * @return {number} The IP Address version.
- */
- goog.net.IpAddress.prototype.getVersion = function() {
- return this.version_;
- };
- /**
- * @param {!goog.net.IpAddress} other The other IP Address.
- * @return {boolean} true if the IP Addresses are equal.
- */
- goog.net.IpAddress.prototype.equals = function(other) {
- return (
- this.version_ == other.getVersion() &&
- this.ip_.equals(other.toInteger()));
- };
- /**
- * @return {!goog.math.Integer} The IP Address, as an Integer.
- */
- goog.net.IpAddress.prototype.toInteger = function() {
- return /** @type {!goog.math.Integer} */ (goog.object.clone(this.ip_));
- };
- /**
- * @return {string} The IP Address, as an URI string following RFC 3986.
- */
- goog.net.IpAddress.prototype.toUriString = goog.abstractMethod;
- /**
- * @return {string} The IP Address, as a string.
- * @override
- */
- goog.net.IpAddress.prototype.toString = goog.abstractMethod;
- /**
- * @return {boolean} Whether or not the address is site-local.
- */
- goog.net.IpAddress.prototype.isSiteLocal = goog.abstractMethod;
- /**
- * @return {boolean} Whether or not the address is link-local.
- */
- goog.net.IpAddress.prototype.isLinkLocal = goog.abstractMethod;
- /**
- * Parses an IP Address in a string.
- * If the string is malformed, the function will simply return null
- * instead of raising an exception.
- *
- * @param {string} address The IP Address.
- * @see {goog.net.Ipv4Address}
- * @see {goog.net.Ipv6Address}
- * @return {goog.net.IpAddress} The IP Address or null.
- */
- goog.net.IpAddress.fromString = function(address) {
- try {
- if (address.indexOf(':') != -1) {
- return new goog.net.Ipv6Address(address);
- }
- return new goog.net.Ipv4Address(address);
- } catch (e) {
- // Both constructors raise exception if the address is malformed (ie.
- // invalid). The user of this function should not care about catching
- // the exception, espcially if it's used to validate an user input.
- return null;
- }
- };
- /**
- * Tries to parse a string represented as a host portion of an URI.
- * See RFC 3986 for more details on IPv6 addresses inside URI.
- * If the string is malformed, the function will simply return null
- * instead of raising an exception.
- *
- * @param {string} address A RFC 3986 encoded IP address.
- * @see {goog.net.Ipv4Address}
- * @see {goog.net.Ipv6Address}
- * @return {goog.net.IpAddress} The IP Address.
- */
- goog.net.IpAddress.fromUriString = function(address) {
- try {
- if (goog.string.startsWith(address, '[') &&
- goog.string.endsWith(address, ']')) {
- return new goog.net.Ipv6Address(address.substring(1, address.length - 1));
- }
- return new goog.net.Ipv4Address(address);
- } catch (e) {
- // Both constructors raise exception if the address is malformed (ie.
- // invalid). The user of this function should not care about catching
- // the exception, espcially if it's used to validate an user input.
- return null;
- }
- };
- /**
- * Takes a string or a number and returns a IPv4 Address.
- *
- * This constructor accepts strings and instance of goog.math.Integer.
- * If you pass a goog.math.Integer, make sure that its sign is set to positive.
- * @param {(string|!goog.math.Integer)} address The address to store.
- * @extends {goog.net.IpAddress}
- * @constructor
- * @final
- */
- goog.net.Ipv4Address = function(address) {
- /**
- * The cached string representation of the IP Address.
- * @type {?string}
- * @private
- */
- this.ipStr_ = null;
- var ip = goog.math.Integer.ZERO;
- if (address instanceof goog.math.Integer) {
- if (address.getSign() != 0 || address.lessThan(goog.math.Integer.ZERO) ||
- address.greaterThan(goog.net.Ipv4Address.MAX_ADDRESS_)) {
- throw Error('The address does not look like an IPv4.');
- } else {
- ip = goog.object.clone(address);
- }
- } else {
- if (!goog.net.Ipv4Address.REGEX_.test(address)) {
- throw Error(address + ' does not look like an IPv4 address.');
- }
- var octets = address.split('.');
- if (octets.length != 4) {
- throw Error(address + ' does not look like an IPv4 address.');
- }
- for (var i = 0; i < octets.length; i++) {
- var parsedOctet = goog.string.toNumber(octets[i]);
- if (isNaN(parsedOctet) || parsedOctet < 0 || parsedOctet > 255 ||
- (octets[i].length != 1 && goog.string.startsWith(octets[i], '0'))) {
- throw Error('In ' + address + ', octet ' + i + ' is not valid');
- }
- var intOctet = goog.math.Integer.fromNumber(parsedOctet);
- ip = ip.shiftLeft(8).or(intOctet);
- }
- }
- goog.net.Ipv4Address.base(
- this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 4);
- };
- goog.inherits(goog.net.Ipv4Address, goog.net.IpAddress);
- /**
- * Regular expression matching all the allowed chars for IPv4.
- * @type {RegExp}
- * @private
- * @const
- */
- goog.net.Ipv4Address.REGEX_ = /^[0-9.]*$/;
- /**
- * The Maximum length for a netmask (aka, the number of bits for IPv4).
- * @type {number}
- * @const
- */
- goog.net.Ipv4Address.MAX_NETMASK_LENGTH = 32;
- /**
- * The Maximum address possible for IPv4.
- * @type {goog.math.Integer}
- * @private
- * @const
- */
- goog.net.Ipv4Address.MAX_ADDRESS_ =
- goog.math.Integer.ONE.shiftLeft(goog.net.Ipv4Address.MAX_NETMASK_LENGTH)
- .subtract(goog.math.Integer.ONE);
- /**
- * @override
- */
- goog.net.Ipv4Address.prototype.toString = function() {
- if (this.ipStr_) {
- return this.ipStr_;
- }
- var ip = this.ip_.getBitsUnsigned(0);
- var octets = [];
- for (var i = 3; i >= 0; i--) {
- octets[i] = String((ip & 0xff));
- ip = ip >>> 8;
- }
- this.ipStr_ = octets.join('.');
- return this.ipStr_;
- };
- /**
- * @override
- */
- goog.net.Ipv4Address.prototype.toUriString = function() {
- return this.toString();
- };
- /**
- * @override
- */
- goog.net.Ipv4Address.prototype.isSiteLocal = function() {
- // Check for prefix 10/8, 172.16/12, or 192.168/16.
- var ipInt = this.ip_.toInt();
- return (((ipInt >>> 24) & 0xff) == 10) ||
- ((((ipInt >>> 24) & 0xff) == 172) && (((ipInt >>> 16) & 0xf0) == 16)) ||
- ((((ipInt >>> 24) & 0xff) == 192) && (((ipInt >>> 16) & 0xff) == 168));
- };
- /**
- * @override
- */
- goog.net.Ipv4Address.prototype.isLinkLocal = function() {
- // Check for prefix 169.254/16.
- var ipInt = this.ip_.toInt();
- return (((ipInt >>> 24) & 0xff) == 169) && (((ipInt >>> 16) & 0xff) == 254);
- };
- /**
- * Takes a string or a number and returns an IPv6 Address.
- *
- * This constructor accepts strings and instance of goog.math.Integer.
- * If you pass a goog.math.Integer, make sure that its sign is set to positive.
- * @param {(string|!goog.math.Integer)} address The address to store.
- * @constructor
- * @extends {goog.net.IpAddress}
- * @final
- */
- goog.net.Ipv6Address = function(address) {
- /**
- * The cached string representation of the IP Address.
- * @type {?string}
- * @private
- */
- this.ipStr_ = null;
- var ip = goog.math.Integer.ZERO;
- if (address instanceof goog.math.Integer) {
- if (address.getSign() != 0 || address.lessThan(goog.math.Integer.ZERO) ||
- address.greaterThan(goog.net.Ipv6Address.MAX_ADDRESS_)) {
- throw Error('The address does not look like a valid IPv6.');
- } else {
- ip = goog.object.clone(address);
- }
- } else {
- if (!goog.net.Ipv6Address.REGEX_.test(address)) {
- throw Error(address + ' is not a valid IPv6 address.');
- }
- var splitColon = address.split(':');
- if (splitColon[splitColon.length - 1].indexOf('.') != -1) {
- var newHextets = goog.net.Ipv6Address.dottedQuadtoHextets_(
- splitColon[splitColon.length - 1]);
- goog.array.removeAt(splitColon, splitColon.length - 1);
- goog.array.extend(splitColon, newHextets);
- address = splitColon.join(':');
- }
- var splitDoubleColon = address.split('::');
- if (splitDoubleColon.length > 2 ||
- (splitDoubleColon.length == 1 && splitColon.length != 8)) {
- throw Error(address + ' is not a valid IPv6 address.');
- }
- var ipArr;
- if (splitDoubleColon.length > 1) {
- ipArr = goog.net.Ipv6Address.explode_(splitDoubleColon);
- } else {
- ipArr = splitColon;
- }
- if (ipArr.length != 8) {
- throw Error(address + ' is not a valid IPv6 address');
- }
- for (var i = 0; i < ipArr.length; i++) {
- var parsedHextet = goog.math.Integer.fromString(ipArr[i], 16);
- if (parsedHextet.lessThan(goog.math.Integer.ZERO) ||
- parsedHextet.greaterThan(goog.net.Ipv6Address.MAX_HEXTET_VALUE_)) {
- throw Error(ipArr[i] + ' in ' + address + ' is not a valid hextet.');
- }
- ip = ip.shiftLeft(16).or(parsedHextet);
- }
- }
- goog.net.Ipv6Address.base(
- this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 6);
- };
- goog.inherits(goog.net.Ipv6Address, goog.net.IpAddress);
- /**
- * Regular expression matching all allowed chars for an IPv6.
- * @type {RegExp}
- * @private
- * @const
- */
- goog.net.Ipv6Address.REGEX_ = /^([a-fA-F0-9]*:){2}[a-fA-F0-9:.]*$/;
- /**
- * The Maximum length for a netmask (aka, the number of bits for IPv6).
- * @type {number}
- * @const
- */
- goog.net.Ipv6Address.MAX_NETMASK_LENGTH = 128;
- /**
- * The maximum value of a hextet.
- * @type {goog.math.Integer}
- * @private
- * @const
- */
- goog.net.Ipv6Address.MAX_HEXTET_VALUE_ = goog.math.Integer.fromInt(65535);
- /**
- * The Maximum address possible for IPv6.
- * @type {goog.math.Integer}
- * @private
- * @const
- */
- goog.net.Ipv6Address.MAX_ADDRESS_ =
- goog.math.Integer.ONE.shiftLeft(goog.net.Ipv6Address.MAX_NETMASK_LENGTH)
- .subtract(goog.math.Integer.ONE);
- /**
- * @override
- */
- goog.net.Ipv6Address.prototype.toString = function() {
- if (this.ipStr_) {
- return this.ipStr_;
- }
- var outputArr = [];
- for (var i = 3; i >= 0; i--) {
- var bits = this.ip_.getBitsUnsigned(i);
- var firstHextet = bits >>> 16;
- var secondHextet = bits & 0xffff;
- outputArr.push(firstHextet.toString(16));
- outputArr.push(secondHextet.toString(16));
- }
- outputArr = goog.net.Ipv6Address.compress_(outputArr);
- this.ipStr_ = outputArr.join(':');
- return this.ipStr_;
- };
- /**
- * @override
- */
- goog.net.Ipv6Address.prototype.toUriString = function() {
- return '[' + this.toString() + ']';
- };
- /**
- * @override
- */
- goog.net.Ipv6Address.prototype.isSiteLocal = function() {
- // Check for prefix fd00::/8.
- var firstDWord = this.ip_.getBitsUnsigned(3);
- var firstHextet = firstDWord >>> 16;
- return (firstHextet & 0xff00) == 0xfd00;
- };
- /**
- * @override
- */
- goog.net.Ipv6Address.prototype.isLinkLocal = function() {
- // Check for prefix fe80::/10.
- var firstDWord = this.ip_.getBitsUnsigned(3);
- var firstHextet = firstDWord >>> 16;
- return (firstHextet & 0xffc0) == 0xfe80;
- };
- /**
- * This method is in charge of expanding/exploding an IPv6 string from its
- * compressed form.
- * @private
- * @param {!Array<string>} address An IPv6 address split around '::'.
- * @return {!Array<string>} The expanded version of the IPv6.
- */
- goog.net.Ipv6Address.explode_ = function(address) {
- var basePart = address[0].split(':');
- var secondPart = address[1].split(':');
- if (basePart.length == 1 && basePart[0] == '') {
- basePart = [];
- }
- if (secondPart.length == 1 && secondPart[0] == '') {
- secondPart = [];
- }
- // Now we fill the gap with 0.
- var gap = 8 - (basePart.length + secondPart.length);
- if (gap < 1) {
- return [];
- }
- return goog.array.join(basePart, goog.array.repeat('0', gap), secondPart);
- };
- /**
- * This method is in charge of compressing an expanded IPv6 array of hextets.
- * @private
- * @param {!Array<string>} hextets The array of hextet.
- * @return {!Array<string>} The compressed version of this array.
- */
- goog.net.Ipv6Address.compress_ = function(hextets) {
- var bestStart = -1;
- var start = -1;
- var bestSize = 0;
- var size = 0;
- for (var i = 0; i < hextets.length; i++) {
- if (hextets[i] == '0') {
- size++;
- if (start == -1) {
- start = i;
- }
- if (size > bestSize) {
- bestSize = size;
- bestStart = start;
- }
- } else {
- start = -1;
- size = 0;
- }
- }
- if (bestSize > 0) {
- if ((bestStart + bestSize) == hextets.length) {
- hextets.push('');
- }
- hextets.splice(bestStart, bestSize, '');
- if (bestStart == 0) {
- hextets = [''].concat(hextets);
- }
- }
- return hextets;
- };
- /**
- * This method will convert an IPv4 to a list of 2 hextets.
- *
- * For instance, 1.2.3.4 will be converted to ['0102', '0304'].
- * @private
- * @param {string} quads An IPv4 as a string.
- * @return {!Array<string>} A list of 2 hextets.
- */
- goog.net.Ipv6Address.dottedQuadtoHextets_ = function(quads) {
- var ip4 = new goog.net.Ipv4Address(quads).toInteger();
- var bits = ip4.getBitsUnsigned(0);
- var hextets = [];
- hextets.push(((bits >>> 16) & 0xffff).toString(16));
- hextets.push((bits & 0xffff).toString(16));
- return hextets;
- };
- /**
- * @return {boolean} true if the IPv6 contains a mapped IPv4.
- */
- goog.net.Ipv6Address.prototype.isMappedIpv4Address = function() {
- return (
- this.ip_.getBitsUnsigned(3) == 0 && this.ip_.getBitsUnsigned(2) == 0 &&
- this.ip_.getBitsUnsigned(1) == 0xffff);
- };
- /**
- * Will return the mapped IPv4 address in this IPv6 address.
- * @return {goog.net.Ipv4Address} an IPv4 or null.
- */
- goog.net.Ipv6Address.prototype.getMappedIpv4Address = function() {
- if (!this.isMappedIpv4Address()) {
- return null;
- }
- var newIpv4 = new goog.math.Integer([this.ip_.getBitsUnsigned(0)], 0);
- return new goog.net.Ipv4Address(newIpv4);
- };
|