ipaddr.js 37 KB

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