Path: blob/trunk/third_party/closure/goog/net/ipaddress.js
2868 views
// Copyright 2011 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview This file contains classes to handle IPv4 and IPv6 addresses.16* This implementation is mostly based on Google's project:17* http://code.google.com/p/ipaddr-py/.18*19*/2021goog.provide('goog.net.IpAddress');22goog.provide('goog.net.Ipv4Address');23goog.provide('goog.net.Ipv6Address');2425goog.require('goog.array');26goog.require('goog.math.Integer');27goog.require('goog.object');28goog.require('goog.string');29303132/**33* Abstract class defining an IP Address.34*35* Please use goog.net.IpAddress static methods or36* goog.net.Ipv4Address/Ipv6Address classes.37*38* @param {!goog.math.Integer} address The Ip Address.39* @param {number} version The version number (4, 6).40* @constructor41*/42goog.net.IpAddress = function(address, version) {43/**44* The IP Address.45* @type {!goog.math.Integer}46* @private47*/48this.ip_ = address;4950/**51* The IP Address version.52* @type {number}53* @private54*/55this.version_ = version;5657};585960/**61* @return {number} The IP Address version.62*/63goog.net.IpAddress.prototype.getVersion = function() {64return this.version_;65};666768/**69* @param {!goog.net.IpAddress} other The other IP Address.70* @return {boolean} true if the IP Addresses are equal.71*/72goog.net.IpAddress.prototype.equals = function(other) {73return (74this.version_ == other.getVersion() &&75this.ip_.equals(other.toInteger()));76};777879/**80* @return {!goog.math.Integer} The IP Address, as an Integer.81*/82goog.net.IpAddress.prototype.toInteger = function() {83return /** @type {!goog.math.Integer} */ (goog.object.clone(this.ip_));84};858687/**88* @return {string} The IP Address, as an URI string following RFC 3986.89*/90goog.net.IpAddress.prototype.toUriString = goog.abstractMethod;919293/**94* @return {string} The IP Address, as a string.95* @override96*/97goog.net.IpAddress.prototype.toString = goog.abstractMethod;9899100/**101* @return {boolean} Whether or not the address is site-local.102*/103goog.net.IpAddress.prototype.isSiteLocal = goog.abstractMethod;104105106/**107* @return {boolean} Whether or not the address is link-local.108*/109goog.net.IpAddress.prototype.isLinkLocal = goog.abstractMethod;110111112/**113* Parses an IP Address in a string.114* If the string is malformed, the function will simply return null115* instead of raising an exception.116*117* @param {string} address The IP Address.118* @see {goog.net.Ipv4Address}119* @see {goog.net.Ipv6Address}120* @return {goog.net.IpAddress} The IP Address or null.121*/122goog.net.IpAddress.fromString = function(address) {123try {124if (address.indexOf(':') != -1) {125return new goog.net.Ipv6Address(address);126}127128return new goog.net.Ipv4Address(address);129} catch (e) {130// Both constructors raise exception if the address is malformed (ie.131// invalid). The user of this function should not care about catching132// the exception, espcially if it's used to validate an user input.133return null;134}135};136137138/**139* Tries to parse a string represented as a host portion of an URI.140* See RFC 3986 for more details on IPv6 addresses inside URI.141* If the string is malformed, the function will simply return null142* instead of raising an exception.143*144* @param {string} address A RFC 3986 encoded IP address.145* @see {goog.net.Ipv4Address}146* @see {goog.net.Ipv6Address}147* @return {goog.net.IpAddress} The IP Address.148*/149goog.net.IpAddress.fromUriString = function(address) {150try {151if (goog.string.startsWith(address, '[') &&152goog.string.endsWith(address, ']')) {153return new goog.net.Ipv6Address(address.substring(1, address.length - 1));154}155156return new goog.net.Ipv4Address(address);157} catch (e) {158// Both constructors raise exception if the address is malformed (ie.159// invalid). The user of this function should not care about catching160// the exception, espcially if it's used to validate an user input.161return null;162}163};164165166167/**168* Takes a string or a number and returns a IPv4 Address.169*170* This constructor accepts strings and instance of goog.math.Integer.171* If you pass a goog.math.Integer, make sure that its sign is set to positive.172* @param {(string|!goog.math.Integer)} address The address to store.173* @extends {goog.net.IpAddress}174* @constructor175* @final176*/177goog.net.Ipv4Address = function(address) {178/**179* The cached string representation of the IP Address.180* @type {?string}181* @private182*/183this.ipStr_ = null;184185var ip = goog.math.Integer.ZERO;186if (address instanceof goog.math.Integer) {187if (address.getSign() != 0 || address.lessThan(goog.math.Integer.ZERO) ||188address.greaterThan(goog.net.Ipv4Address.MAX_ADDRESS_)) {189throw Error('The address does not look like an IPv4.');190} else {191ip = goog.object.clone(address);192}193} else {194if (!goog.net.Ipv4Address.REGEX_.test(address)) {195throw Error(address + ' does not look like an IPv4 address.');196}197198var octets = address.split('.');199if (octets.length != 4) {200throw Error(address + ' does not look like an IPv4 address.');201}202203for (var i = 0; i < octets.length; i++) {204var parsedOctet = goog.string.toNumber(octets[i]);205if (isNaN(parsedOctet) || parsedOctet < 0 || parsedOctet > 255 ||206(octets[i].length != 1 && goog.string.startsWith(octets[i], '0'))) {207throw Error('In ' + address + ', octet ' + i + ' is not valid');208}209var intOctet = goog.math.Integer.fromNumber(parsedOctet);210ip = ip.shiftLeft(8).or(intOctet);211}212}213goog.net.Ipv4Address.base(214this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 4);215};216goog.inherits(goog.net.Ipv4Address, goog.net.IpAddress);217218219/**220* Regular expression matching all the allowed chars for IPv4.221* @type {RegExp}222* @private223* @const224*/225goog.net.Ipv4Address.REGEX_ = /^[0-9.]*$/;226227228/**229* The Maximum length for a netmask (aka, the number of bits for IPv4).230* @type {number}231* @const232*/233goog.net.Ipv4Address.MAX_NETMASK_LENGTH = 32;234235236/**237* The Maximum address possible for IPv4.238* @type {goog.math.Integer}239* @private240* @const241*/242goog.net.Ipv4Address.MAX_ADDRESS_ =243goog.math.Integer.ONE.shiftLeft(goog.net.Ipv4Address.MAX_NETMASK_LENGTH)244.subtract(goog.math.Integer.ONE);245246247/**248* @override249*/250goog.net.Ipv4Address.prototype.toString = function() {251if (this.ipStr_) {252return this.ipStr_;253}254255var ip = this.ip_.getBitsUnsigned(0);256var octets = [];257for (var i = 3; i >= 0; i--) {258octets[i] = String((ip & 0xff));259ip = ip >>> 8;260}261262this.ipStr_ = octets.join('.');263264return this.ipStr_;265};266267268/**269* @override270*/271goog.net.Ipv4Address.prototype.toUriString = function() {272return this.toString();273};274275276/**277* @override278*/279goog.net.Ipv4Address.prototype.isSiteLocal = function() {280// Check for prefix 10/8, 172.16/12, or 192.168/16.281var ipInt = this.ip_.toInt();282return (((ipInt >>> 24) & 0xff) == 10) ||283((((ipInt >>> 24) & 0xff) == 172) && (((ipInt >>> 16) & 0xf0) == 16)) ||284((((ipInt >>> 24) & 0xff) == 192) && (((ipInt >>> 16) & 0xff) == 168));285};286287288/**289* @override290*/291goog.net.Ipv4Address.prototype.isLinkLocal = function() {292// Check for prefix 169.254/16.293var ipInt = this.ip_.toInt();294return (((ipInt >>> 24) & 0xff) == 169) && (((ipInt >>> 16) & 0xff) == 254);295};296297298/**299* Takes a string or a number and returns an IPv6 Address.300*301* This constructor accepts strings and instance of goog.math.Integer.302* If you pass a goog.math.Integer, make sure that its sign is set to positive.303* @param {(string|!goog.math.Integer)} address The address to store.304* @constructor305* @extends {goog.net.IpAddress}306* @final307*/308goog.net.Ipv6Address = function(address) {309/**310* The cached string representation of the IP Address.311* @type {?string}312* @private313*/314this.ipStr_ = null;315316var ip = goog.math.Integer.ZERO;317if (address instanceof goog.math.Integer) {318if (address.getSign() != 0 || address.lessThan(goog.math.Integer.ZERO) ||319address.greaterThan(goog.net.Ipv6Address.MAX_ADDRESS_)) {320throw Error('The address does not look like a valid IPv6.');321} else {322ip = goog.object.clone(address);323}324} else {325if (!goog.net.Ipv6Address.REGEX_.test(address)) {326throw Error(address + ' is not a valid IPv6 address.');327}328329var splitColon = address.split(':');330if (splitColon[splitColon.length - 1].indexOf('.') != -1) {331var newHextets = goog.net.Ipv6Address.dottedQuadtoHextets_(332splitColon[splitColon.length - 1]);333goog.array.removeAt(splitColon, splitColon.length - 1);334goog.array.extend(splitColon, newHextets);335address = splitColon.join(':');336}337338var splitDoubleColon = address.split('::');339if (splitDoubleColon.length > 2 ||340(splitDoubleColon.length == 1 && splitColon.length != 8)) {341throw Error(address + ' is not a valid IPv6 address.');342}343344var ipArr;345if (splitDoubleColon.length > 1) {346ipArr = goog.net.Ipv6Address.explode_(splitDoubleColon);347} else {348ipArr = splitColon;349}350351if (ipArr.length != 8) {352throw Error(address + ' is not a valid IPv6 address');353}354355for (var i = 0; i < ipArr.length; i++) {356var parsedHextet = goog.math.Integer.fromString(ipArr[i], 16);357if (parsedHextet.lessThan(goog.math.Integer.ZERO) ||358parsedHextet.greaterThan(goog.net.Ipv6Address.MAX_HEXTET_VALUE_)) {359throw Error(ipArr[i] + ' in ' + address + ' is not a valid hextet.');360}361ip = ip.shiftLeft(16).or(parsedHextet);362}363}364goog.net.Ipv6Address.base(365this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 6);366};367goog.inherits(goog.net.Ipv6Address, goog.net.IpAddress);368369370/**371* Regular expression matching all allowed chars for an IPv6.372* @type {RegExp}373* @private374* @const375*/376goog.net.Ipv6Address.REGEX_ = /^([a-fA-F0-9]*:){2}[a-fA-F0-9:.]*$/;377378379/**380* The Maximum length for a netmask (aka, the number of bits for IPv6).381* @type {number}382* @const383*/384goog.net.Ipv6Address.MAX_NETMASK_LENGTH = 128;385386387/**388* The maximum value of a hextet.389* @type {goog.math.Integer}390* @private391* @const392*/393goog.net.Ipv6Address.MAX_HEXTET_VALUE_ = goog.math.Integer.fromInt(65535);394395396/**397* The Maximum address possible for IPv6.398* @type {goog.math.Integer}399* @private400* @const401*/402goog.net.Ipv6Address.MAX_ADDRESS_ =403goog.math.Integer.ONE.shiftLeft(goog.net.Ipv6Address.MAX_NETMASK_LENGTH)404.subtract(goog.math.Integer.ONE);405406407/**408* @override409*/410goog.net.Ipv6Address.prototype.toString = function() {411if (this.ipStr_) {412return this.ipStr_;413}414415var outputArr = [];416for (var i = 3; i >= 0; i--) {417var bits = this.ip_.getBitsUnsigned(i);418var firstHextet = bits >>> 16;419var secondHextet = bits & 0xffff;420outputArr.push(firstHextet.toString(16));421outputArr.push(secondHextet.toString(16));422}423424outputArr = goog.net.Ipv6Address.compress_(outputArr);425this.ipStr_ = outputArr.join(':');426return this.ipStr_;427};428429430/**431* @override432*/433goog.net.Ipv6Address.prototype.toUriString = function() {434return '[' + this.toString() + ']';435};436437438/**439* @override440*/441goog.net.Ipv6Address.prototype.isSiteLocal = function() {442// Check for prefix fd00::/8.443var firstDWord = this.ip_.getBitsUnsigned(3);444var firstHextet = firstDWord >>> 16;445return (firstHextet & 0xff00) == 0xfd00;446};447448449/**450* @override451*/452goog.net.Ipv6Address.prototype.isLinkLocal = function() {453// Check for prefix fe80::/10.454var firstDWord = this.ip_.getBitsUnsigned(3);455var firstHextet = firstDWord >>> 16;456return (firstHextet & 0xffc0) == 0xfe80;457};458459460/**461* This method is in charge of expanding/exploding an IPv6 string from its462* compressed form.463* @private464* @param {!Array<string>} address An IPv6 address split around '::'.465* @return {!Array<string>} The expanded version of the IPv6.466*/467goog.net.Ipv6Address.explode_ = function(address) {468var basePart = address[0].split(':');469var secondPart = address[1].split(':');470471if (basePart.length == 1 && basePart[0] == '') {472basePart = [];473}474if (secondPart.length == 1 && secondPart[0] == '') {475secondPart = [];476}477478// Now we fill the gap with 0.479var gap = 8 - (basePart.length + secondPart.length);480481if (gap < 1) {482return [];483}484485return goog.array.join(basePart, goog.array.repeat('0', gap), secondPart);486};487488489/**490* This method is in charge of compressing an expanded IPv6 array of hextets.491* @private492* @param {!Array<string>} hextets The array of hextet.493* @return {!Array<string>} The compressed version of this array.494*/495goog.net.Ipv6Address.compress_ = function(hextets) {496var bestStart = -1;497var start = -1;498var bestSize = 0;499var size = 0;500for (var i = 0; i < hextets.length; i++) {501if (hextets[i] == '0') {502size++;503if (start == -1) {504start = i;505}506if (size > bestSize) {507bestSize = size;508bestStart = start;509}510} else {511start = -1;512size = 0;513}514}515516if (bestSize > 0) {517if ((bestStart + bestSize) == hextets.length) {518hextets.push('');519}520hextets.splice(bestStart, bestSize, '');521522if (bestStart == 0) {523hextets = [''].concat(hextets);524}525}526return hextets;527};528529530/**531* This method will convert an IPv4 to a list of 2 hextets.532*533* For instance, 1.2.3.4 will be converted to ['0102', '0304'].534* @private535* @param {string} quads An IPv4 as a string.536* @return {!Array<string>} A list of 2 hextets.537*/538goog.net.Ipv6Address.dottedQuadtoHextets_ = function(quads) {539var ip4 = new goog.net.Ipv4Address(quads).toInteger();540var bits = ip4.getBitsUnsigned(0);541var hextets = [];542543hextets.push(((bits >>> 16) & 0xffff).toString(16));544hextets.push((bits & 0xffff).toString(16));545546return hextets;547};548549550/**551* @return {boolean} true if the IPv6 contains a mapped IPv4.552*/553goog.net.Ipv6Address.prototype.isMappedIpv4Address = function() {554return (555this.ip_.getBitsUnsigned(3) == 0 && this.ip_.getBitsUnsigned(2) == 0 &&556this.ip_.getBitsUnsigned(1) == 0xffff);557};558559560/**561* Will return the mapped IPv4 address in this IPv6 address.562* @return {goog.net.Ipv4Address} an IPv4 or null.563*/564goog.net.Ipv6Address.prototype.getMappedIpv4Address = function() {565if (!this.isMappedIpv4Address()) {566return null;567}568569var newIpv4 = new goog.math.Integer([this.ip_.getBitsUnsigned(0)], 0);570return new goog.net.Ipv4Address(newIpv4);571};572573574