ipaddr.js 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. (function (root) {
  2. 'use strict';
  3. // A list of regular expressions that match arbitrary IPv4 addresses,
  4. // for which a number of weird notations exist.
  5. // Note that an address like 0010.0xa5.1.1 is considered legal.
  6. const ipv4Part = '(0?\\d+|0x[a-f0-9]+)';
  7. const ipv4Regexes = {
  8. fourOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'),
  9. threeOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, 'i'),
  10. twoOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}$`, 'i'),
  11. longValue: new RegExp(`^${ipv4Part}$`, 'i')
  12. };
  13. // Regular Expression for checking Octal numbers
  14. const octalRegex = new RegExp(`^0[0-7]+$`, 'i');
  15. const hexRegex = new RegExp(`^0x[a-f0-9]+$`, 'i');
  16. const zoneIndex = '%[0-9a-z]{1,}';
  17. // IPv6-matching regular expressions.
  18. // For IPv6, the task is simpler: it is enough to match the colon-delimited
  19. // hexadecimal IPv6 and a transitional variant with dotted-decimal IPv4 at
  20. // the end.
  21. const ipv6Part = '(?:[0-9a-f]+::?)+';
  22. const ipv6Regexes = {
  23. zoneIndex: new RegExp(zoneIndex, 'i'),
  24. 'native': new RegExp(`^(::)?(${ipv6Part})?([0-9a-f]+)?(::)?(${zoneIndex})?$`, 'i'),
  25. deprecatedTransitional: new RegExp(`^(?:::)(${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?)$`, 'i'),
  26. transitional: new RegExp(`^((?:${ipv6Part})|(?:::)(?:${ipv6Part})?)${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?$`, 'i')
  27. };
  28. // Expand :: in an IPv6 address or address part consisting of `parts` groups.
  29. function expandIPv6 (string, parts) {
  30. // More than one '::' means invalid adddress
  31. if (string.indexOf('::') !== string.lastIndexOf('::')) {
  32. return null;
  33. }
  34. let colonCount = 0;
  35. let lastColon = -1;
  36. let zoneId = (string.match(ipv6Regexes.zoneIndex) || [])[0];
  37. let replacement, replacementCount;
  38. // Remove zone index and save it for later
  39. if (zoneId) {
  40. zoneId = zoneId.substring(1);
  41. string = string.replace(/%.+$/, '');
  42. }
  43. // How many parts do we already have?
  44. while ((lastColon = string.indexOf(':', lastColon + 1)) >= 0) {
  45. colonCount++;
  46. }
  47. // 0::0 is two parts more than ::
  48. if (string.substr(0, 2) === '::') {
  49. colonCount--;
  50. }
  51. if (string.substr(-2, 2) === '::') {
  52. colonCount--;
  53. }
  54. // The following loop would hang if colonCount > parts
  55. if (colonCount > parts) {
  56. return null;
  57. }
  58. // replacement = ':' + '0:' * (parts - colonCount)
  59. replacementCount = parts - colonCount;
  60. replacement = ':';
  61. while (replacementCount--) {
  62. replacement += '0:';
  63. }
  64. // Insert the missing zeroes
  65. string = string.replace('::', replacement);
  66. // Trim any garbage which may be hanging around if :: was at the edge in
  67. // the source strin
  68. if (string[0] === ':') {
  69. string = string.slice(1);
  70. }
  71. if (string[string.length - 1] === ':') {
  72. string = string.slice(0, -1);
  73. }
  74. parts = (function () {
  75. const ref = string.split(':');
  76. const results = [];
  77. for (let i = 0; i < ref.length; i++) {
  78. results.push(parseInt(ref[i], 16));
  79. }
  80. return results;
  81. })();
  82. return {
  83. parts: parts,
  84. zoneId: zoneId
  85. };
  86. }
  87. // A generic CIDR (Classless Inter-Domain Routing) RFC1518 range matcher.
  88. function matchCIDR (first, second, partSize, cidrBits) {
  89. if (first.length !== second.length) {
  90. throw new Error('ipaddr: cannot match CIDR for objects with different lengths');
  91. }
  92. let part = 0;
  93. let shift;
  94. while (cidrBits > 0) {
  95. shift = partSize - cidrBits;
  96. if (shift < 0) {
  97. shift = 0;
  98. }
  99. if (first[part] >> shift !== second[part] >> shift) {
  100. return false;
  101. }
  102. cidrBits -= partSize;
  103. part += 1;
  104. }
  105. return true;
  106. }
  107. function parseIntAuto (string) {
  108. // Hexadedimal base 16 (0x#)
  109. if (hexRegex.test(string)) {
  110. return parseInt(string, 16);
  111. }
  112. // While octal representation is discouraged by ECMAScript 3
  113. // and forbidden by ECMAScript 5, we silently allow it to
  114. // work only if the rest of the string has numbers less than 8.
  115. if (string[0] === '0' && !isNaN(parseInt(string[1], 10))) {
  116. if (octalRegex.test(string)) {
  117. return parseInt(string, 8);
  118. }
  119. throw new Error(`ipaddr: cannot parse ${string} as octal`);
  120. }
  121. // Always include the base 10 radix!
  122. return parseInt(string, 10);
  123. }
  124. function padPart (part, length) {
  125. while (part.length < length) {
  126. part = `0${part}`;
  127. }
  128. return part;
  129. }
  130. const ipaddr = {};
  131. // An IPv4 address (RFC791).
  132. ipaddr.IPv4 = (function () {
  133. // Constructs a new IPv4 address from an array of four octets
  134. // in network order (MSB first)
  135. // Verifies the input.
  136. function IPv4 (octets) {
  137. if (octets.length !== 4) {
  138. throw new Error('ipaddr: ipv4 octet count should be 4');
  139. }
  140. let i, octet;
  141. for (i = 0; i < octets.length; i++) {
  142. octet = octets[i];
  143. if (!((0 <= octet && octet <= 255))) {
  144. throw new Error('ipaddr: ipv4 octet should fit in 8 bits');
  145. }
  146. }
  147. this.octets = octets;
  148. }
  149. // Special IPv4 address ranges.
  150. // See also https://en.wikipedia.org/wiki/Reserved_IP_addresses
  151. IPv4.prototype.SpecialRanges = {
  152. unspecified: [[new IPv4([0, 0, 0, 0]), 8]],
  153. broadcast: [[new IPv4([255, 255, 255, 255]), 32]],
  154. // RFC3171
  155. multicast: [[new IPv4([224, 0, 0, 0]), 4]],
  156. // RFC3927
  157. linkLocal: [[new IPv4([169, 254, 0, 0]), 16]],
  158. // RFC5735
  159. loopback: [[new IPv4([127, 0, 0, 0]), 8]],
  160. // RFC6598
  161. carrierGradeNat: [[new IPv4([100, 64, 0, 0]), 10]],
  162. // RFC1918
  163. 'private': [
  164. [new IPv4([10, 0, 0, 0]), 8],
  165. [new IPv4([172, 16, 0, 0]), 12],
  166. [new IPv4([192, 168, 0, 0]), 16]
  167. ],
  168. // Reserved and testing-only ranges; RFCs 5735, 5737, 2544, 1700
  169. reserved: [
  170. [new IPv4([192, 0, 0, 0]), 24],
  171. [new IPv4([192, 0, 2, 0]), 24],
  172. [new IPv4([192, 88, 99, 0]), 24],
  173. [new IPv4([198, 18, 0, 0]), 15],
  174. [new IPv4([198, 51, 100, 0]), 24],
  175. [new IPv4([203, 0, 113, 0]), 24],
  176. [new IPv4([240, 0, 0, 0]), 4]
  177. ]
  178. };
  179. // The 'kind' method exists on both IPv4 and IPv6 classes.
  180. IPv4.prototype.kind = function () {
  181. return 'ipv4';
  182. };
  183. // Checks if this address matches other one within given CIDR range.
  184. IPv4.prototype.match = function (other, cidrRange) {
  185. let ref;
  186. if (cidrRange === undefined) {
  187. ref = other;
  188. other = ref[0];
  189. cidrRange = ref[1];
  190. }
  191. if (other.kind() !== 'ipv4') {
  192. throw new Error('ipaddr: cannot match ipv4 address with non-ipv4 one');
  193. }
  194. return matchCIDR(this.octets, other.octets, 8, cidrRange);
  195. };
  196. // returns a number of leading ones in IPv4 address, making sure that
  197. // the rest is a solid sequence of 0's (valid netmask)
  198. // returns either the CIDR length or null if mask is not valid
  199. IPv4.prototype.prefixLengthFromSubnetMask = function () {
  200. let cidr = 0;
  201. // non-zero encountered stop scanning for zeroes
  202. let stop = false;
  203. // number of zeroes in octet
  204. const zerotable = {
  205. 0: 8,
  206. 128: 7,
  207. 192: 6,
  208. 224: 5,
  209. 240: 4,
  210. 248: 3,
  211. 252: 2,
  212. 254: 1,
  213. 255: 0
  214. };
  215. let i, octet, zeros;
  216. for (i = 3; i >= 0; i -= 1) {
  217. octet = this.octets[i];
  218. if (octet in zerotable) {
  219. zeros = zerotable[octet];
  220. if (stop && zeros !== 0) {
  221. return null;
  222. }
  223. if (zeros !== 8) {
  224. stop = true;
  225. }
  226. cidr += zeros;
  227. } else {
  228. return null;
  229. }
  230. }
  231. return 32 - cidr;
  232. };
  233. // Checks if the address corresponds to one of the special ranges.
  234. IPv4.prototype.range = function () {
  235. return ipaddr.subnetMatch(this, this.SpecialRanges);
  236. };
  237. // Returns an array of byte-sized values in network order (MSB first)
  238. IPv4.prototype.toByteArray = function () {
  239. return this.octets.slice(0);
  240. };
  241. // Converts this IPv4 address to an IPv4-mapped IPv6 address.
  242. IPv4.prototype.toIPv4MappedAddress = function () {
  243. return ipaddr.IPv6.parse(`::ffff:${this.toString()}`);
  244. };
  245. // Symmetrical method strictly for aligning with the IPv6 methods.
  246. IPv4.prototype.toNormalizedString = function () {
  247. return this.toString();
  248. };
  249. // Returns the address in convenient, decimal-dotted format.
  250. IPv4.prototype.toString = function () {
  251. return this.octets.join('.');
  252. };
  253. return IPv4;
  254. })();
  255. // A utility function to return broadcast address given the IPv4 interface and prefix length in CIDR notation
  256. ipaddr.IPv4.broadcastAddressFromCIDR = function (string) {
  257. try {
  258. const cidr = this.parseCIDR(string);
  259. const ipInterfaceOctets = cidr[0].toByteArray();
  260. const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
  261. const octets = [];
  262. let i = 0;
  263. while (i < 4) {
  264. // Broadcast address is bitwise OR between ip interface and inverted mask
  265. octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255);
  266. i++;
  267. }
  268. return new this(octets);
  269. } catch (e) {
  270. throw new Error('ipaddr: the address does not have IPv4 CIDR format');
  271. }
  272. };
  273. // Checks if a given string is formatted like IPv4 address.
  274. ipaddr.IPv4.isIPv4 = function (string) {
  275. return this.parser(string) !== null;
  276. };
  277. // Checks if a given string is a valid IPv4 address.
  278. ipaddr.IPv4.isValid = function (string) {
  279. try {
  280. new this(this.parser(string));
  281. return true;
  282. } catch (e) {
  283. return false;
  284. }
  285. };
  286. // Checks if a given string is a full four-part IPv4 Address.
  287. ipaddr.IPv4.isValidFourPartDecimal = function (string) {
  288. if (ipaddr.IPv4.isValid(string) && string.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/)) {
  289. return true;
  290. } else {
  291. return false;
  292. }
  293. };
  294. // A utility function to return network address given the IPv4 interface and prefix length in CIDR notation
  295. ipaddr.IPv4.networkAddressFromCIDR = function (string) {
  296. let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets;
  297. try {
  298. cidr = this.parseCIDR(string);
  299. ipInterfaceOctets = cidr[0].toByteArray();
  300. subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
  301. octets = [];
  302. i = 0;
  303. while (i < 4) {
  304. // Network address is bitwise AND between ip interface and mask
  305. octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10));
  306. i++;
  307. }
  308. return new this(octets);
  309. } catch (e) {
  310. throw new Error('ipaddr: the address does not have IPv4 CIDR format');
  311. }
  312. };
  313. // Tries to parse and validate a string with IPv4 address.
  314. // Throws an error if it fails.
  315. ipaddr.IPv4.parse = function (string) {
  316. const parts = this.parser(string);
  317. if (parts === null) {
  318. throw new Error('ipaddr: string is not formatted like an IPv4 Address');
  319. }
  320. return new this(parts);
  321. };
  322. // Parses the string as an IPv4 Address with CIDR Notation.
  323. ipaddr.IPv4.parseCIDR = function (string) {
  324. let match;
  325. if ((match = string.match(/^(.+)\/(\d+)$/))) {
  326. const maskLength = parseInt(match[2]);
  327. if (maskLength >= 0 && maskLength <= 32) {
  328. const parsed = [this.parse(match[1]), maskLength];
  329. Object.defineProperty(parsed, 'toString', {
  330. value: function () {
  331. return this.join('/');
  332. }
  333. });
  334. return parsed;
  335. }
  336. }
  337. throw new Error('ipaddr: string is not formatted like an IPv4 CIDR range');
  338. };
  339. // Classful variants (like a.b, where a is an octet, and b is a 24-bit
  340. // value representing last three octets; this corresponds to a class C
  341. // address) are omitted due to classless nature of modern Internet.
  342. ipaddr.IPv4.parser = function (string) {
  343. let match, part, value;
  344. // parseInt recognizes all that octal & hexadecimal weirdness for us
  345. if ((match = string.match(ipv4Regexes.fourOctet))) {
  346. return (function () {
  347. const ref = match.slice(1, 6);
  348. const results = [];
  349. for (let i = 0; i < ref.length; i++) {
  350. part = ref[i];
  351. results.push(parseIntAuto(part));
  352. }
  353. return results;
  354. })();
  355. } else if ((match = string.match(ipv4Regexes.longValue))) {
  356. value = parseIntAuto(match[1]);
  357. if (value > 0xffffffff || value < 0) {
  358. throw new Error('ipaddr: address outside defined range');
  359. }
  360. return ((function () {
  361. const results = [];
  362. let shift;
  363. for (shift = 0; shift <= 24; shift += 8) {
  364. results.push((value >> shift) & 0xff);
  365. }
  366. return results;
  367. })()).reverse();
  368. } else if ((match = string.match(ipv4Regexes.twoOctet))) {
  369. return (function () {
  370. const ref = match.slice(1, 4);
  371. const results = [];
  372. value = parseIntAuto(ref[1]);
  373. if (value > 0xffffff || value < 0) {
  374. throw new Error('ipaddr: address outside defined range');
  375. }
  376. results.push(parseIntAuto(ref[0]));
  377. results.push((value >> 16) & 0xff);
  378. results.push((value >> 8) & 0xff);
  379. results.push( value & 0xff);
  380. return results;
  381. })();
  382. } else if ((match = string.match(ipv4Regexes.threeOctet))) {
  383. return (function () {
  384. const ref = match.slice(1, 5);
  385. const results = [];
  386. value = parseIntAuto(ref[2]);
  387. if (value > 0xffff || value < 0) {
  388. throw new Error('ipaddr: address outside defined range');
  389. }
  390. results.push(parseIntAuto(ref[0]));
  391. results.push(parseIntAuto(ref[1]));
  392. results.push((value >> 8) & 0xff);
  393. results.push( value & 0xff);
  394. return results;
  395. })();
  396. } else {
  397. return null;
  398. }
  399. };
  400. // A utility function to return subnet mask in IPv4 format given the prefix length
  401. ipaddr.IPv4.subnetMaskFromPrefixLength = function (prefix) {
  402. prefix = parseInt(prefix);
  403. if (prefix < 0 || prefix > 32) {
  404. throw new Error('ipaddr: invalid IPv4 prefix length');
  405. }
  406. const octets = [0, 0, 0, 0];
  407. let j = 0;
  408. const filledOctetCount = Math.floor(prefix / 8);
  409. while (j < filledOctetCount) {
  410. octets[j] = 255;
  411. j++;
  412. }
  413. if (filledOctetCount < 4) {
  414. octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8);
  415. }
  416. return new this(octets);
  417. };
  418. // An IPv6 address (RFC2460)
  419. ipaddr.IPv6 = (function () {
  420. // Constructs an IPv6 address from an array of eight 16 - bit parts
  421. // or sixteen 8 - bit parts in network order(MSB first).
  422. // Throws an error if the input is invalid.
  423. function IPv6 (parts, zoneId) {
  424. let i, part;
  425. if (parts.length === 16) {
  426. this.parts = [];
  427. for (i = 0; i <= 14; i += 2) {
  428. this.parts.push((parts[i] << 8) | parts[i + 1]);
  429. }
  430. } else if (parts.length === 8) {
  431. this.parts = parts;
  432. } else {
  433. throw new Error('ipaddr: ipv6 part count should be 8 or 16');
  434. }
  435. for (i = 0; i < this.parts.length; i++) {
  436. part = this.parts[i];
  437. if (!((0 <= part && part <= 0xffff))) {
  438. throw new Error('ipaddr: ipv6 part should fit in 16 bits');
  439. }
  440. }
  441. if (zoneId) {
  442. this.zoneId = zoneId;
  443. }
  444. }
  445. // Special IPv6 ranges
  446. IPv6.prototype.SpecialRanges = {
  447. // RFC4291, here and after
  448. unspecified: [new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128],
  449. linkLocal: [new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10],
  450. multicast: [new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8],
  451. loopback: [new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128],
  452. uniqueLocal: [new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7],
  453. ipv4Mapped: [new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96],
  454. // RFC6145
  455. rfc6145: [new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96],
  456. // RFC6052
  457. rfc6052: [new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96],
  458. // RFC3056
  459. '6to4': [new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16],
  460. // RFC6052, RFC6146
  461. teredo: [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32],
  462. // RFC4291
  463. reserved: [[new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32]],
  464. benchmarking: [new IPv6([0x2001, 0x2, 0, 0, 0, 0, 0, 0]), 48],
  465. amt: [new IPv6([0x2001, 0x3, 0, 0, 0, 0, 0, 0]), 32],
  466. as112v6: [new IPv6([0x2001, 0x4, 0x112, 0, 0, 0, 0, 0]), 48],
  467. deprecated: [new IPv6([0x2001, 0x10, 0, 0, 0, 0, 0, 0]), 28],
  468. orchid2: [new IPv6([0x2001, 0x20, 0, 0, 0, 0, 0, 0]), 28]
  469. };
  470. // Checks if this address is an IPv4-mapped IPv6 address.
  471. IPv6.prototype.isIPv4MappedAddress = function () {
  472. return this.range() === 'ipv4Mapped';
  473. };
  474. // The 'kind' method exists on both IPv4 and IPv6 classes.
  475. IPv6.prototype.kind = function () {
  476. return 'ipv6';
  477. };
  478. // Checks if this address matches other one within given CIDR range.
  479. IPv6.prototype.match = function (other, cidrRange) {
  480. let ref;
  481. if (cidrRange === undefined) {
  482. ref = other;
  483. other = ref[0];
  484. cidrRange = ref[1];
  485. }
  486. if (other.kind() !== 'ipv6') {
  487. throw new Error('ipaddr: cannot match ipv6 address with non-ipv6 one');
  488. }
  489. return matchCIDR(this.parts, other.parts, 16, cidrRange);
  490. };
  491. // returns a number of leading ones in IPv6 address, making sure that
  492. // the rest is a solid sequence of 0's (valid netmask)
  493. // returns either the CIDR length or null if mask is not valid
  494. IPv6.prototype.prefixLengthFromSubnetMask = function () {
  495. let cidr = 0;
  496. // non-zero encountered stop scanning for zeroes
  497. let stop = false;
  498. // number of zeroes in octet
  499. const zerotable = {
  500. 0: 16,
  501. 32768: 15,
  502. 49152: 14,
  503. 57344: 13,
  504. 61440: 12,
  505. 63488: 11,
  506. 64512: 10,
  507. 65024: 9,
  508. 65280: 8,
  509. 65408: 7,
  510. 65472: 6,
  511. 65504: 5,
  512. 65520: 4,
  513. 65528: 3,
  514. 65532: 2,
  515. 65534: 1,
  516. 65535: 0
  517. };
  518. let part, zeros;
  519. for (let i = 7; i >= 0; i -= 1) {
  520. part = this.parts[i];
  521. if (part in zerotable) {
  522. zeros = zerotable[part];
  523. if (stop && zeros !== 0) {
  524. return null;
  525. }
  526. if (zeros !== 16) {
  527. stop = true;
  528. }
  529. cidr += zeros;
  530. } else {
  531. return null;
  532. }
  533. }
  534. return 128 - cidr;
  535. };
  536. // Checks if the address corresponds to one of the special ranges.
  537. IPv6.prototype.range = function () {
  538. return ipaddr.subnetMatch(this, this.SpecialRanges);
  539. };
  540. // Returns an array of byte-sized values in network order (MSB first)
  541. IPv6.prototype.toByteArray = function () {
  542. let part;
  543. const bytes = [];
  544. const ref = this.parts;
  545. for (let i = 0; i < ref.length; i++) {
  546. part = ref[i];
  547. bytes.push(part >> 8);
  548. bytes.push(part & 0xff);
  549. }
  550. return bytes;
  551. };
  552. // Returns the address in expanded format with all zeroes included, like
  553. // 2001:0db8:0008:0066:0000:0000:0000:0001
  554. IPv6.prototype.toFixedLengthString = function () {
  555. const addr = ((function () {
  556. const results = [];
  557. for (let i = 0; i < this.parts.length; i++) {
  558. results.push(padPart(this.parts[i].toString(16), 4));
  559. }
  560. return results;
  561. }).call(this)).join(':');
  562. let suffix = '';
  563. if (this.zoneId) {
  564. suffix = `%${this.zoneId}`;
  565. }
  566. return addr + suffix;
  567. };
  568. // Converts this address to IPv4 address if it is an IPv4-mapped IPv6 address.
  569. // Throws an error otherwise.
  570. IPv6.prototype.toIPv4Address = function () {
  571. if (!this.isIPv4MappedAddress()) {
  572. throw new Error('ipaddr: trying to convert a generic ipv6 address to ipv4');
  573. }
  574. const ref = this.parts.slice(-2);
  575. const high = ref[0];
  576. const low = ref[1];
  577. return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]);
  578. };
  579. // Returns the address in expanded format with all zeroes included, like
  580. // 2001:db8:8:66:0:0:0:1
  581. //
  582. // Deprecated: use toFixedLengthString() instead.
  583. IPv6.prototype.toNormalizedString = function () {
  584. const addr = ((function () {
  585. const results = [];
  586. for (let i = 0; i < this.parts.length; i++) {
  587. results.push(this.parts[i].toString(16));
  588. }
  589. return results;
  590. }).call(this)).join(':');
  591. let suffix = '';
  592. if (this.zoneId) {
  593. suffix = `%${this.zoneId}`;
  594. }
  595. return addr + suffix;
  596. };
  597. // Returns the address in compact, human-readable format like
  598. // 2001:db8:8:66::1
  599. // in line with RFC 5952 (see https://tools.ietf.org/html/rfc5952#section-4)
  600. IPv6.prototype.toRFC5952String = function () {
  601. const regex = /((^|:)(0(:|$)){2,})/g;
  602. const string = this.toNormalizedString();
  603. let bestMatchIndex = 0;
  604. let bestMatchLength = -1;
  605. let match;
  606. while ((match = regex.exec(string))) {
  607. if (match[0].length > bestMatchLength) {
  608. bestMatchIndex = match.index;
  609. bestMatchLength = match[0].length;
  610. }
  611. }
  612. if (bestMatchLength < 0) {
  613. return string;
  614. }
  615. return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}`;
  616. };
  617. // Returns the address in compact, human-readable format like
  618. // 2001:db8:8:66::1
  619. // Calls toRFC5952String under the hood.
  620. IPv6.prototype.toString = function () {
  621. return this.toRFC5952String();
  622. };
  623. return IPv6;
  624. })();
  625. // A utility function to return broadcast address given the IPv6 interface and prefix length in CIDR notation
  626. ipaddr.IPv6.broadcastAddressFromCIDR = function (string) {
  627. try {
  628. const cidr = this.parseCIDR(string);
  629. const ipInterfaceOctets = cidr[0].toByteArray();
  630. const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
  631. const octets = [];
  632. let i = 0;
  633. while (i < 16) {
  634. // Broadcast address is bitwise OR between ip interface and inverted mask
  635. octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255);
  636. i++;
  637. }
  638. return new this(octets);
  639. } catch (e) {
  640. throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`);
  641. }
  642. };
  643. // Checks if a given string is formatted like IPv6 address.
  644. ipaddr.IPv6.isIPv6 = function (string) {
  645. return this.parser(string) !== null;
  646. };
  647. // Checks to see if string is a valid IPv6 Address
  648. ipaddr.IPv6.isValid = function (string) {
  649. // Since IPv6.isValid is always called first, this shortcut
  650. // provides a substantial performance gain.
  651. if (typeof string === 'string' && string.indexOf(':') === -1) {
  652. return false;
  653. }
  654. try {
  655. const addr = this.parser(string);
  656. new this(addr.parts, addr.zoneId);
  657. return true;
  658. } catch (e) {
  659. return false;
  660. }
  661. };
  662. // A utility function to return network address given the IPv6 interface and prefix length in CIDR notation
  663. ipaddr.IPv6.networkAddressFromCIDR = function (string) {
  664. let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets;
  665. try {
  666. cidr = this.parseCIDR(string);
  667. ipInterfaceOctets = cidr[0].toByteArray();
  668. subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
  669. octets = [];
  670. i = 0;
  671. while (i < 16) {
  672. // Network address is bitwise AND between ip interface and mask
  673. octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10));
  674. i++;
  675. }
  676. return new this(octets);
  677. } catch (e) {
  678. throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`);
  679. }
  680. };
  681. // Tries to parse and validate a string with IPv6 address.
  682. // Throws an error if it fails.
  683. ipaddr.IPv6.parse = function (string) {
  684. const addr = this.parser(string);
  685. if (addr.parts === null) {
  686. throw new Error('ipaddr: string is not formatted like an IPv6 Address');
  687. }
  688. return new this(addr.parts, addr.zoneId);
  689. };
  690. ipaddr.IPv6.parseCIDR = function (string) {
  691. let maskLength, match, parsed;
  692. if ((match = string.match(/^(.+)\/(\d+)$/))) {
  693. maskLength = parseInt(match[2]);
  694. if (maskLength >= 0 && maskLength <= 128) {
  695. parsed = [this.parse(match[1]), maskLength];
  696. Object.defineProperty(parsed, 'toString', {
  697. value: function () {
  698. return this.join('/');
  699. }
  700. });
  701. return parsed;
  702. }
  703. }
  704. throw new Error('ipaddr: string is not formatted like an IPv6 CIDR range');
  705. };
  706. // Parse an IPv6 address.
  707. ipaddr.IPv6.parser = function (string) {
  708. let addr, i, match, octet, octets, zoneId;
  709. if ((match = string.match(ipv6Regexes.deprecatedTransitional))) {
  710. return this.parser(`::ffff:${match[1]}`);
  711. }
  712. if (ipv6Regexes.native.test(string)) {
  713. return expandIPv6(string, 8);
  714. }
  715. if ((match = string.match(ipv6Regexes.transitional))) {
  716. zoneId = match[6] || '';
  717. addr = expandIPv6(match[1].slice(0, -1) + zoneId, 6);
  718. if (addr.parts) {
  719. octets = [
  720. parseInt(match[2]),
  721. parseInt(match[3]),
  722. parseInt(match[4]),
  723. parseInt(match[5])
  724. ];
  725. for (i = 0; i < octets.length; i++) {
  726. octet = octets[i];
  727. if (!((0 <= octet && octet <= 255))) {
  728. return null;
  729. }
  730. }
  731. addr.parts.push(octets[0] << 8 | octets[1]);
  732. addr.parts.push(octets[2] << 8 | octets[3]);
  733. return {
  734. parts: addr.parts,
  735. zoneId: addr.zoneId
  736. };
  737. }
  738. }
  739. return null;
  740. };
  741. // A utility function to return subnet mask in IPv6 format given the prefix length
  742. ipaddr.IPv6.subnetMaskFromPrefixLength = function (prefix) {
  743. prefix = parseInt(prefix);
  744. if (prefix < 0 || prefix > 128) {
  745. throw new Error('ipaddr: invalid IPv6 prefix length');
  746. }
  747. const octets = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  748. let j = 0;
  749. const filledOctetCount = Math.floor(prefix / 8);
  750. while (j < filledOctetCount) {
  751. octets[j] = 255;
  752. j++;
  753. }
  754. if (filledOctetCount < 16) {
  755. octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8);
  756. }
  757. return new this(octets);
  758. };
  759. // Try to parse an array in network order (MSB first) for IPv4 and IPv6
  760. ipaddr.fromByteArray = function (bytes) {
  761. const length = bytes.length;
  762. if (length === 4) {
  763. return new ipaddr.IPv4(bytes);
  764. } else if (length === 16) {
  765. return new ipaddr.IPv6(bytes);
  766. } else {
  767. throw new Error('ipaddr: the binary input is neither an IPv6 nor IPv4 address');
  768. }
  769. };
  770. // Checks if the address is valid IP address
  771. ipaddr.isValid = function (string) {
  772. return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string);
  773. };
  774. // Attempts to parse an IP Address, first through IPv6 then IPv4.
  775. // Throws an error if it could not be parsed.
  776. ipaddr.parse = function (string) {
  777. if (ipaddr.IPv6.isValid(string)) {
  778. return ipaddr.IPv6.parse(string);
  779. } else if (ipaddr.IPv4.isValid(string)) {
  780. return ipaddr.IPv4.parse(string);
  781. } else {
  782. throw new Error('ipaddr: the address has neither IPv6 nor IPv4 format');
  783. }
  784. };
  785. // Attempt to parse CIDR notation, first through IPv6 then IPv4.
  786. // Throws an error if it could not be parsed.
  787. ipaddr.parseCIDR = function (string) {
  788. try {
  789. return ipaddr.IPv6.parseCIDR(string);
  790. } catch (e) {
  791. try {
  792. return ipaddr.IPv4.parseCIDR(string);
  793. } catch (e2) {
  794. throw new Error('ipaddr: the address has neither IPv6 nor IPv4 CIDR format');
  795. }
  796. }
  797. };
  798. // Parse an address and return plain IPv4 address if it is an IPv4-mapped address
  799. ipaddr.process = function (string) {
  800. const addr = this.parse(string);
  801. if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) {
  802. return addr.toIPv4Address();
  803. } else {
  804. return addr;
  805. }
  806. };
  807. // An utility function to ease named range matching. See examples below.
  808. // rangeList can contain both IPv4 and IPv6 subnet entries and will not throw errors
  809. // on matching IPv4 addresses to IPv6 ranges or vice versa.
  810. ipaddr.subnetMatch = function (address, rangeList, defaultName) {
  811. let i, rangeName, rangeSubnets, subnet;
  812. if (defaultName === undefined || defaultName === null) {
  813. defaultName = 'unicast';
  814. }
  815. for (rangeName in rangeList) {
  816. if (Object.prototype.hasOwnProperty.call(rangeList, rangeName)) {
  817. rangeSubnets = rangeList[rangeName];
  818. // ECMA5 Array.isArray isn't available everywhere
  819. if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) {
  820. rangeSubnets = [rangeSubnets];
  821. }
  822. for (i = 0; i < rangeSubnets.length; i++) {
  823. subnet = rangeSubnets[i];
  824. if (address.kind() === subnet[0].kind() && address.match.apply(address, subnet)) {
  825. return rangeName;
  826. }
  827. }
  828. }
  829. }
  830. return defaultName;
  831. };
  832. // Export for both the CommonJS and browser-like environment
  833. if (typeof module !== 'undefined' && module.exports) {
  834. module.exports = ipaddr;
  835. } else {
  836. root.ipaddr = ipaddr;
  837. }
  838. }(this));