ipaddress.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  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. */
  20. goog.provide('goog.net.IpAddress');
  21. goog.provide('goog.net.Ipv4Address');
  22. goog.provide('goog.net.Ipv6Address');
  23. goog.require('goog.array');
  24. goog.require('goog.math.Integer');
  25. goog.require('goog.object');
  26. goog.require('goog.string');
  27. /**
  28. * Abstract class defining an IP Address.
  29. *
  30. * Please use goog.net.IpAddress static methods or
  31. * goog.net.Ipv4Address/Ipv6Address classes.
  32. *
  33. * @param {!goog.math.Integer} address The Ip Address.
  34. * @param {number} version The version number (4, 6).
  35. * @constructor
  36. */
  37. goog.net.IpAddress = function(address, version) {
  38. /**
  39. * The IP Address.
  40. * @type {!goog.math.Integer}
  41. * @private
  42. */
  43. this.ip_ = address;
  44. /**
  45. * The IP Address version.
  46. * @type {number}
  47. * @private
  48. */
  49. this.version_ = version;
  50. };
  51. /**
  52. * @return {number} The IP Address version.
  53. */
  54. goog.net.IpAddress.prototype.getVersion = function() {
  55. return this.version_;
  56. };
  57. /**
  58. * @param {!goog.net.IpAddress} other The other IP Address.
  59. * @return {boolean} true if the IP Addresses are equal.
  60. */
  61. goog.net.IpAddress.prototype.equals = function(other) {
  62. return (
  63. this.version_ == other.getVersion() &&
  64. this.ip_.equals(other.toInteger()));
  65. };
  66. /**
  67. * @return {!goog.math.Integer} The IP Address, as an Integer.
  68. */
  69. goog.net.IpAddress.prototype.toInteger = function() {
  70. return /** @type {!goog.math.Integer} */ (goog.object.clone(this.ip_));
  71. };
  72. /**
  73. * @return {string} The IP Address, as an URI string following RFC 3986.
  74. */
  75. goog.net.IpAddress.prototype.toUriString = goog.abstractMethod;
  76. /**
  77. * @return {string} The IP Address, as a string.
  78. * @override
  79. */
  80. goog.net.IpAddress.prototype.toString = goog.abstractMethod;
  81. /**
  82. * @return {boolean} Whether or not the address is site-local.
  83. */
  84. goog.net.IpAddress.prototype.isSiteLocal = goog.abstractMethod;
  85. /**
  86. * @return {boolean} Whether or not the address is link-local.
  87. */
  88. goog.net.IpAddress.prototype.isLinkLocal = goog.abstractMethod;
  89. /**
  90. * Parses an IP Address in a string.
  91. * If the string is malformed, the function will simply return null
  92. * instead of raising an exception.
  93. *
  94. * @param {string} address The IP Address.
  95. * @see {goog.net.Ipv4Address}
  96. * @see {goog.net.Ipv6Address}
  97. * @return {goog.net.IpAddress} The IP Address or null.
  98. */
  99. goog.net.IpAddress.fromString = function(address) {
  100. try {
  101. if (address.indexOf(':') != -1) {
  102. return new goog.net.Ipv6Address(address);
  103. }
  104. return new goog.net.Ipv4Address(address);
  105. } catch (e) {
  106. // Both constructors raise exception if the address is malformed (ie.
  107. // invalid). The user of this function should not care about catching
  108. // the exception, espcially if it's used to validate an user input.
  109. return null;
  110. }
  111. };
  112. /**
  113. * Tries to parse a string represented as a host portion of an URI.
  114. * See RFC 3986 for more details on IPv6 addresses inside URI.
  115. * If the string is malformed, the function will simply return null
  116. * instead of raising an exception.
  117. *
  118. * @param {string} address A RFC 3986 encoded IP address.
  119. * @see {goog.net.Ipv4Address}
  120. * @see {goog.net.Ipv6Address}
  121. * @return {goog.net.IpAddress} The IP Address.
  122. */
  123. goog.net.IpAddress.fromUriString = function(address) {
  124. try {
  125. if (goog.string.startsWith(address, '[') &&
  126. goog.string.endsWith(address, ']')) {
  127. return new goog.net.Ipv6Address(address.substring(1, address.length - 1));
  128. }
  129. return new goog.net.Ipv4Address(address);
  130. } catch (e) {
  131. // Both constructors raise exception if the address is malformed (ie.
  132. // invalid). The user of this function should not care about catching
  133. // the exception, espcially if it's used to validate an user input.
  134. return null;
  135. }
  136. };
  137. /**
  138. * Takes a string or a number and returns a IPv4 Address.
  139. *
  140. * This constructor accepts strings and instance of goog.math.Integer.
  141. * If you pass a goog.math.Integer, make sure that its sign is set to positive.
  142. * @param {(string|!goog.math.Integer)} address The address to store.
  143. * @extends {goog.net.IpAddress}
  144. * @constructor
  145. * @final
  146. */
  147. goog.net.Ipv4Address = function(address) {
  148. /**
  149. * The cached string representation of the IP Address.
  150. * @type {?string}
  151. * @private
  152. */
  153. this.ipStr_ = null;
  154. var ip = goog.math.Integer.ZERO;
  155. if (address instanceof goog.math.Integer) {
  156. if (address.getSign() != 0 || address.lessThan(goog.math.Integer.ZERO) ||
  157. address.greaterThan(goog.net.Ipv4Address.MAX_ADDRESS_)) {
  158. throw Error('The address does not look like an IPv4.');
  159. } else {
  160. ip = goog.object.clone(address);
  161. }
  162. } else {
  163. if (!goog.net.Ipv4Address.REGEX_.test(address)) {
  164. throw Error(address + ' does not look like an IPv4 address.');
  165. }
  166. var octets = address.split('.');
  167. if (octets.length != 4) {
  168. throw Error(address + ' does not look like an IPv4 address.');
  169. }
  170. for (var i = 0; i < octets.length; i++) {
  171. var parsedOctet = goog.string.toNumber(octets[i]);
  172. if (isNaN(parsedOctet) || parsedOctet < 0 || parsedOctet > 255 ||
  173. (octets[i].length != 1 && goog.string.startsWith(octets[i], '0'))) {
  174. throw Error('In ' + address + ', octet ' + i + ' is not valid');
  175. }
  176. var intOctet = goog.math.Integer.fromNumber(parsedOctet);
  177. ip = ip.shiftLeft(8).or(intOctet);
  178. }
  179. }
  180. goog.net.Ipv4Address.base(
  181. this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 4);
  182. };
  183. goog.inherits(goog.net.Ipv4Address, goog.net.IpAddress);
  184. /**
  185. * Regular expression matching all the allowed chars for IPv4.
  186. * @type {RegExp}
  187. * @private
  188. * @const
  189. */
  190. goog.net.Ipv4Address.REGEX_ = /^[0-9.]*$/;
  191. /**
  192. * The Maximum length for a netmask (aka, the number of bits for IPv4).
  193. * @type {number}
  194. * @const
  195. */
  196. goog.net.Ipv4Address.MAX_NETMASK_LENGTH = 32;
  197. /**
  198. * The Maximum address possible for IPv4.
  199. * @type {goog.math.Integer}
  200. * @private
  201. * @const
  202. */
  203. goog.net.Ipv4Address.MAX_ADDRESS_ =
  204. goog.math.Integer.ONE.shiftLeft(goog.net.Ipv4Address.MAX_NETMASK_LENGTH)
  205. .subtract(goog.math.Integer.ONE);
  206. /**
  207. * @override
  208. */
  209. goog.net.Ipv4Address.prototype.toString = function() {
  210. if (this.ipStr_) {
  211. return this.ipStr_;
  212. }
  213. var ip = this.ip_.getBitsUnsigned(0);
  214. var octets = [];
  215. for (var i = 3; i >= 0; i--) {
  216. octets[i] = String((ip & 0xff));
  217. ip = ip >>> 8;
  218. }
  219. this.ipStr_ = octets.join('.');
  220. return this.ipStr_;
  221. };
  222. /**
  223. * @override
  224. */
  225. goog.net.Ipv4Address.prototype.toUriString = function() {
  226. return this.toString();
  227. };
  228. /**
  229. * @override
  230. */
  231. goog.net.Ipv4Address.prototype.isSiteLocal = function() {
  232. // Check for prefix 10/8, 172.16/12, or 192.168/16.
  233. var ipInt = this.ip_.toInt();
  234. return (((ipInt >>> 24) & 0xff) == 10) ||
  235. ((((ipInt >>> 24) & 0xff) == 172) && (((ipInt >>> 16) & 0xf0) == 16)) ||
  236. ((((ipInt >>> 24) & 0xff) == 192) && (((ipInt >>> 16) & 0xff) == 168));
  237. };
  238. /**
  239. * @override
  240. */
  241. goog.net.Ipv4Address.prototype.isLinkLocal = function() {
  242. // Check for prefix 169.254/16.
  243. var ipInt = this.ip_.toInt();
  244. return (((ipInt >>> 24) & 0xff) == 169) && (((ipInt >>> 16) & 0xff) == 254);
  245. };
  246. /**
  247. * Takes a string or a number and returns an IPv6 Address.
  248. *
  249. * This constructor accepts strings and instance of goog.math.Integer.
  250. * If you pass a goog.math.Integer, make sure that its sign is set to positive.
  251. * @param {(string|!goog.math.Integer)} address The address to store.
  252. * @constructor
  253. * @extends {goog.net.IpAddress}
  254. * @final
  255. */
  256. goog.net.Ipv6Address = function(address) {
  257. /**
  258. * The cached string representation of the IP Address.
  259. * @type {?string}
  260. * @private
  261. */
  262. this.ipStr_ = null;
  263. var ip = goog.math.Integer.ZERO;
  264. if (address instanceof goog.math.Integer) {
  265. if (address.getSign() != 0 || address.lessThan(goog.math.Integer.ZERO) ||
  266. address.greaterThan(goog.net.Ipv6Address.MAX_ADDRESS_)) {
  267. throw Error('The address does not look like a valid IPv6.');
  268. } else {
  269. ip = goog.object.clone(address);
  270. }
  271. } else {
  272. if (!goog.net.Ipv6Address.REGEX_.test(address)) {
  273. throw Error(address + ' is not a valid IPv6 address.');
  274. }
  275. var splitColon = address.split(':');
  276. if (splitColon[splitColon.length - 1].indexOf('.') != -1) {
  277. var newHextets = goog.net.Ipv6Address.dottedQuadtoHextets_(
  278. splitColon[splitColon.length - 1]);
  279. goog.array.removeAt(splitColon, splitColon.length - 1);
  280. goog.array.extend(splitColon, newHextets);
  281. address = splitColon.join(':');
  282. }
  283. var splitDoubleColon = address.split('::');
  284. if (splitDoubleColon.length > 2 ||
  285. (splitDoubleColon.length == 1 && splitColon.length != 8)) {
  286. throw Error(address + ' is not a valid IPv6 address.');
  287. }
  288. var ipArr;
  289. if (splitDoubleColon.length > 1) {
  290. ipArr = goog.net.Ipv6Address.explode_(splitDoubleColon);
  291. } else {
  292. ipArr = splitColon;
  293. }
  294. if (ipArr.length != 8) {
  295. throw Error(address + ' is not a valid IPv6 address');
  296. }
  297. for (var i = 0; i < ipArr.length; i++) {
  298. var parsedHextet = goog.math.Integer.fromString(ipArr[i], 16);
  299. if (parsedHextet.lessThan(goog.math.Integer.ZERO) ||
  300. parsedHextet.greaterThan(goog.net.Ipv6Address.MAX_HEXTET_VALUE_)) {
  301. throw Error(ipArr[i] + ' in ' + address + ' is not a valid hextet.');
  302. }
  303. ip = ip.shiftLeft(16).or(parsedHextet);
  304. }
  305. }
  306. goog.net.Ipv6Address.base(
  307. this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 6);
  308. };
  309. goog.inherits(goog.net.Ipv6Address, goog.net.IpAddress);
  310. /**
  311. * Regular expression matching all allowed chars for an IPv6.
  312. * @type {RegExp}
  313. * @private
  314. * @const
  315. */
  316. goog.net.Ipv6Address.REGEX_ = /^([a-fA-F0-9]*:){2}[a-fA-F0-9:.]*$/;
  317. /**
  318. * The Maximum length for a netmask (aka, the number of bits for IPv6).
  319. * @type {number}
  320. * @const
  321. */
  322. goog.net.Ipv6Address.MAX_NETMASK_LENGTH = 128;
  323. /**
  324. * The maximum value of a hextet.
  325. * @type {goog.math.Integer}
  326. * @private
  327. * @const
  328. */
  329. goog.net.Ipv6Address.MAX_HEXTET_VALUE_ = goog.math.Integer.fromInt(65535);
  330. /**
  331. * The Maximum address possible for IPv6.
  332. * @type {goog.math.Integer}
  333. * @private
  334. * @const
  335. */
  336. goog.net.Ipv6Address.MAX_ADDRESS_ =
  337. goog.math.Integer.ONE.shiftLeft(goog.net.Ipv6Address.MAX_NETMASK_LENGTH)
  338. .subtract(goog.math.Integer.ONE);
  339. /**
  340. * @override
  341. */
  342. goog.net.Ipv6Address.prototype.toString = function() {
  343. if (this.ipStr_) {
  344. return this.ipStr_;
  345. }
  346. var outputArr = [];
  347. for (var i = 3; i >= 0; i--) {
  348. var bits = this.ip_.getBitsUnsigned(i);
  349. var firstHextet = bits >>> 16;
  350. var secondHextet = bits & 0xffff;
  351. outputArr.push(firstHextet.toString(16));
  352. outputArr.push(secondHextet.toString(16));
  353. }
  354. outputArr = goog.net.Ipv6Address.compress_(outputArr);
  355. this.ipStr_ = outputArr.join(':');
  356. return this.ipStr_;
  357. };
  358. /**
  359. * @override
  360. */
  361. goog.net.Ipv6Address.prototype.toUriString = function() {
  362. return '[' + this.toString() + ']';
  363. };
  364. /**
  365. * @override
  366. */
  367. goog.net.Ipv6Address.prototype.isSiteLocal = function() {
  368. // Check for prefix fd00::/8.
  369. var firstDWord = this.ip_.getBitsUnsigned(3);
  370. var firstHextet = firstDWord >>> 16;
  371. return (firstHextet & 0xff00) == 0xfd00;
  372. };
  373. /**
  374. * @override
  375. */
  376. goog.net.Ipv6Address.prototype.isLinkLocal = function() {
  377. // Check for prefix fe80::/10.
  378. var firstDWord = this.ip_.getBitsUnsigned(3);
  379. var firstHextet = firstDWord >>> 16;
  380. return (firstHextet & 0xffc0) == 0xfe80;
  381. };
  382. /**
  383. * This method is in charge of expanding/exploding an IPv6 string from its
  384. * compressed form.
  385. * @private
  386. * @param {!Array<string>} address An IPv6 address split around '::'.
  387. * @return {!Array<string>} The expanded version of the IPv6.
  388. */
  389. goog.net.Ipv6Address.explode_ = function(address) {
  390. var basePart = address[0].split(':');
  391. var secondPart = address[1].split(':');
  392. if (basePart.length == 1 && basePart[0] == '') {
  393. basePart = [];
  394. }
  395. if (secondPart.length == 1 && secondPart[0] == '') {
  396. secondPart = [];
  397. }
  398. // Now we fill the gap with 0.
  399. var gap = 8 - (basePart.length + secondPart.length);
  400. if (gap < 1) {
  401. return [];
  402. }
  403. return goog.array.join(basePart, goog.array.repeat('0', gap), secondPart);
  404. };
  405. /**
  406. * This method is in charge of compressing an expanded IPv6 array of hextets.
  407. * @private
  408. * @param {!Array<string>} hextets The array of hextet.
  409. * @return {!Array<string>} The compressed version of this array.
  410. */
  411. goog.net.Ipv6Address.compress_ = function(hextets) {
  412. var bestStart = -1;
  413. var start = -1;
  414. var bestSize = 0;
  415. var size = 0;
  416. for (var i = 0; i < hextets.length; i++) {
  417. if (hextets[i] == '0') {
  418. size++;
  419. if (start == -1) {
  420. start = i;
  421. }
  422. if (size > bestSize) {
  423. bestSize = size;
  424. bestStart = start;
  425. }
  426. } else {
  427. start = -1;
  428. size = 0;
  429. }
  430. }
  431. if (bestSize > 0) {
  432. if ((bestStart + bestSize) == hextets.length) {
  433. hextets.push('');
  434. }
  435. hextets.splice(bestStart, bestSize, '');
  436. if (bestStart == 0) {
  437. hextets = [''].concat(hextets);
  438. }
  439. }
  440. return hextets;
  441. };
  442. /**
  443. * This method will convert an IPv4 to a list of 2 hextets.
  444. *
  445. * For instance, 1.2.3.4 will be converted to ['0102', '0304'].
  446. * @private
  447. * @param {string} quads An IPv4 as a string.
  448. * @return {!Array<string>} A list of 2 hextets.
  449. */
  450. goog.net.Ipv6Address.dottedQuadtoHextets_ = function(quads) {
  451. var ip4 = new goog.net.Ipv4Address(quads).toInteger();
  452. var bits = ip4.getBitsUnsigned(0);
  453. var hextets = [];
  454. hextets.push(((bits >>> 16) & 0xffff).toString(16));
  455. hextets.push((bits & 0xffff).toString(16));
  456. return hextets;
  457. };
  458. /**
  459. * @return {boolean} true if the IPv6 contains a mapped IPv4.
  460. */
  461. goog.net.Ipv6Address.prototype.isMappedIpv4Address = function() {
  462. return (
  463. this.ip_.getBitsUnsigned(3) == 0 && this.ip_.getBitsUnsigned(2) == 0 &&
  464. this.ip_.getBitsUnsigned(1) == 0xffff);
  465. };
  466. /**
  467. * Will return the mapped IPv4 address in this IPv6 address.
  468. * @return {goog.net.Ipv4Address} an IPv4 or null.
  469. */
  470. goog.net.Ipv6Address.prototype.getMappedIpv4Address = function() {
  471. if (!this.isMappedIpv4Address()) {
  472. return null;
  473. }
  474. var newIpv4 = new goog.math.Integer([this.ip_.getBitsUnsigned(0)], 0);
  475. return new goog.net.Ipv4Address(newIpv4);
  476. };