ipaddr.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. (function() {
  2. var expandIPv6, ipaddr, ipv4Part, ipv4Regexes, ipv6Part, ipv6Regexes, matchCIDR, root;
  3. ipaddr = {};
  4. root = this;
  5. if ((typeof module !== "undefined" && module !== null) && module.exports) {
  6. module.exports = ipaddr;
  7. } else {
  8. root['ipaddr'] = ipaddr;
  9. }
  10. matchCIDR = function(first, second, partSize, cidrBits) {
  11. var part, shift;
  12. if (first.length !== second.length) {
  13. throw new Error("ipaddr: cannot match CIDR for objects with different lengths");
  14. }
  15. part = 0;
  16. while (cidrBits > 0) {
  17. shift = partSize - cidrBits;
  18. if (shift < 0) {
  19. shift = 0;
  20. }
  21. if (first[part] >> shift !== second[part] >> shift) {
  22. return false;
  23. }
  24. cidrBits -= partSize;
  25. part += 1;
  26. }
  27. return true;
  28. };
  29. ipaddr.subnetMatch = function(address, rangeList, defaultName) {
  30. var rangeName, rangeSubnets, subnet, _i, _len;
  31. if (defaultName == null) {
  32. defaultName = 'unicast';
  33. }
  34. for (rangeName in rangeList) {
  35. rangeSubnets = rangeList[rangeName];
  36. if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) {
  37. rangeSubnets = [rangeSubnets];
  38. }
  39. for (_i = 0, _len = rangeSubnets.length; _i < _len; _i++) {
  40. subnet = rangeSubnets[_i];
  41. if (address.match.apply(address, subnet)) {
  42. return rangeName;
  43. }
  44. }
  45. }
  46. return defaultName;
  47. };
  48. ipaddr.IPv4 = (function() {
  49. function IPv4(octets) {
  50. var octet, _i, _len;
  51. if (octets.length !== 4) {
  52. throw new Error("ipaddr: ipv4 octet count should be 4");
  53. }
  54. for (_i = 0, _len = octets.length; _i < _len; _i++) {
  55. octet = octets[_i];
  56. if (!((0 <= octet && octet <= 255))) {
  57. throw new Error("ipaddr: ipv4 octet is a byte");
  58. }
  59. }
  60. this.octets = octets;
  61. }
  62. IPv4.prototype.kind = function() {
  63. return 'ipv4';
  64. };
  65. IPv4.prototype.toString = function() {
  66. return this.octets.join(".");
  67. };
  68. IPv4.prototype.toByteArray = function() {
  69. return this.octets.slice(0);
  70. };
  71. IPv4.prototype.match = function(other, cidrRange) {
  72. var _ref;
  73. if (cidrRange === void 0) {
  74. _ref = other, other = _ref[0], cidrRange = _ref[1];
  75. }
  76. if (other.kind() !== 'ipv4') {
  77. throw new Error("ipaddr: cannot match ipv4 address with non-ipv4 one");
  78. }
  79. return matchCIDR(this.octets, other.octets, 8, cidrRange);
  80. };
  81. IPv4.prototype.SpecialRanges = {
  82. unspecified: [[new IPv4([0, 0, 0, 0]), 8]],
  83. broadcast: [[new IPv4([255, 255, 255, 255]), 32]],
  84. multicast: [[new IPv4([224, 0, 0, 0]), 4]],
  85. linkLocal: [[new IPv4([169, 254, 0, 0]), 16]],
  86. loopback: [[new IPv4([127, 0, 0, 0]), 8]],
  87. "private": [[new IPv4([10, 0, 0, 0]), 8], [new IPv4([172, 16, 0, 0]), 12], [new IPv4([192, 168, 0, 0]), 16]],
  88. reserved: [[new IPv4([192, 0, 0, 0]), 24], [new IPv4([192, 0, 2, 0]), 24], [new IPv4([192, 88, 99, 0]), 24], [new IPv4([198, 51, 100, 0]), 24], [new IPv4([203, 0, 113, 0]), 24], [new IPv4([240, 0, 0, 0]), 4]]
  89. };
  90. IPv4.prototype.range = function() {
  91. return ipaddr.subnetMatch(this, this.SpecialRanges);
  92. };
  93. IPv4.prototype.toIPv4MappedAddress = function() {
  94. return ipaddr.IPv6.parse("::ffff:" + (this.toString()));
  95. };
  96. return IPv4;
  97. })();
  98. ipv4Part = "(0?\\d+|0x[a-f0-9]+)";
  99. ipv4Regexes = {
  100. fourOctet: new RegExp("^" + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "$", 'i'),
  101. longValue: new RegExp("^" + ipv4Part + "$", 'i')
  102. };
  103. ipaddr.IPv4.parser = function(string) {
  104. var match, parseIntAuto, part, shift, value;
  105. parseIntAuto = function(string) {
  106. if (string[0] === "0" && string[1] !== "x") {
  107. return parseInt(string, 8);
  108. } else {
  109. return parseInt(string);
  110. }
  111. };
  112. if (match = string.match(ipv4Regexes.fourOctet)) {
  113. return (function() {
  114. var _i, _len, _ref, _results;
  115. _ref = match.slice(1, 6);
  116. _results = [];
  117. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  118. part = _ref[_i];
  119. _results.push(parseIntAuto(part));
  120. }
  121. return _results;
  122. })();
  123. } else if (match = string.match(ipv4Regexes.longValue)) {
  124. value = parseIntAuto(match[1]);
  125. if (value > 0xffffffff || value < 0) {
  126. throw new Error("ipaddr: address outside defined range");
  127. }
  128. return ((function() {
  129. var _i, _results;
  130. _results = [];
  131. for (shift = _i = 0; _i <= 24; shift = _i += 8) {
  132. _results.push((value >> shift) & 0xff);
  133. }
  134. return _results;
  135. })()).reverse();
  136. } else {
  137. return null;
  138. }
  139. };
  140. ipaddr.IPv6 = (function() {
  141. function IPv6(parts) {
  142. var part, _i, _len;
  143. if (parts.length !== 8) {
  144. throw new Error("ipaddr: ipv6 part count should be 8");
  145. }
  146. for (_i = 0, _len = parts.length; _i < _len; _i++) {
  147. part = parts[_i];
  148. if (!((0 <= part && part <= 0xffff))) {
  149. throw new Error("ipaddr: ipv6 part should fit to two octets");
  150. }
  151. }
  152. this.parts = parts;
  153. }
  154. IPv6.prototype.kind = function() {
  155. return 'ipv6';
  156. };
  157. IPv6.prototype.toString = function() {
  158. var compactStringParts, part, pushPart, state, stringParts, _i, _len;
  159. stringParts = (function() {
  160. var _i, _len, _ref, _results;
  161. _ref = this.parts;
  162. _results = [];
  163. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  164. part = _ref[_i];
  165. _results.push(part.toString(16));
  166. }
  167. return _results;
  168. }).call(this);
  169. compactStringParts = [];
  170. pushPart = function(part) {
  171. return compactStringParts.push(part);
  172. };
  173. state = 0;
  174. for (_i = 0, _len = stringParts.length; _i < _len; _i++) {
  175. part = stringParts[_i];
  176. switch (state) {
  177. case 0:
  178. if (part === '0') {
  179. pushPart('');
  180. } else {
  181. pushPart(part);
  182. }
  183. state = 1;
  184. break;
  185. case 1:
  186. if (part === '0') {
  187. state = 2;
  188. } else {
  189. pushPart(part);
  190. }
  191. break;
  192. case 2:
  193. if (part !== '0') {
  194. pushPart('');
  195. pushPart(part);
  196. state = 3;
  197. }
  198. break;
  199. case 3:
  200. pushPart(part);
  201. }
  202. }
  203. if (state === 2) {
  204. pushPart('');
  205. pushPart('');
  206. }
  207. return compactStringParts.join(":");
  208. };
  209. IPv6.prototype.toByteArray = function() {
  210. var bytes, part, _i, _len, _ref;
  211. bytes = [];
  212. _ref = this.parts;
  213. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  214. part = _ref[_i];
  215. bytes.push(part >> 8);
  216. bytes.push(part & 0xff);
  217. }
  218. return bytes;
  219. };
  220. IPv6.prototype.toNormalizedString = function() {
  221. var part;
  222. return ((function() {
  223. var _i, _len, _ref, _results;
  224. _ref = this.parts;
  225. _results = [];
  226. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  227. part = _ref[_i];
  228. _results.push(part.toString(16));
  229. }
  230. return _results;
  231. }).call(this)).join(":");
  232. };
  233. IPv6.prototype.match = function(other, cidrRange) {
  234. var _ref;
  235. if (cidrRange === void 0) {
  236. _ref = other, other = _ref[0], cidrRange = _ref[1];
  237. }
  238. if (other.kind() !== 'ipv6') {
  239. throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one");
  240. }
  241. return matchCIDR(this.parts, other.parts, 16, cidrRange);
  242. };
  243. IPv6.prototype.SpecialRanges = {
  244. unspecified: [new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128],
  245. linkLocal: [new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10],
  246. multicast: [new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8],
  247. loopback: [new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128],
  248. uniqueLocal: [new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7],
  249. ipv4Mapped: [new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96],
  250. rfc6145: [new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96],
  251. rfc6052: [new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96],
  252. '6to4': [new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16],
  253. teredo: [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32],
  254. reserved: [[new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32]]
  255. };
  256. IPv6.prototype.range = function() {
  257. return ipaddr.subnetMatch(this, this.SpecialRanges);
  258. };
  259. IPv6.prototype.isIPv4MappedAddress = function() {
  260. return this.range() === 'ipv4Mapped';
  261. };
  262. IPv6.prototype.toIPv4Address = function() {
  263. var high, low, _ref;
  264. if (!this.isIPv4MappedAddress()) {
  265. throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4");
  266. }
  267. _ref = this.parts.slice(-2), high = _ref[0], low = _ref[1];
  268. return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]);
  269. };
  270. return IPv6;
  271. })();
  272. ipv6Part = "(?:[0-9a-f]+::?)+";
  273. ipv6Regexes = {
  274. "native": new RegExp("^(::)?(" + ipv6Part + ")?([0-9a-f]+)?(::)?$", 'i'),
  275. transitional: new RegExp(("^((?:" + ipv6Part + ")|(?:::)(?:" + ipv6Part + ")?)") + ("" + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "$"), 'i')
  276. };
  277. expandIPv6 = function(string, parts) {
  278. var colonCount, lastColon, part, replacement, replacementCount;
  279. if (string.indexOf('::') !== string.lastIndexOf('::')) {
  280. return null;
  281. }
  282. colonCount = 0;
  283. lastColon = -1;
  284. while ((lastColon = string.indexOf(':', lastColon + 1)) >= 0) {
  285. colonCount++;
  286. }
  287. if (string.substr(0, 2) === '::') {
  288. colonCount--;
  289. }
  290. if (string.substr(-2, 2) === '::') {
  291. colonCount--;
  292. }
  293. if (colonCount > parts) {
  294. return null;
  295. }
  296. replacementCount = parts - colonCount;
  297. replacement = ':';
  298. while (replacementCount--) {
  299. replacement += '0:';
  300. }
  301. string = string.replace('::', replacement);
  302. if (string[0] === ':') {
  303. string = string.slice(1);
  304. }
  305. if (string[string.length - 1] === ':') {
  306. string = string.slice(0, -1);
  307. }
  308. return (function() {
  309. var _i, _len, _ref, _results;
  310. _ref = string.split(":");
  311. _results = [];
  312. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  313. part = _ref[_i];
  314. _results.push(parseInt(part, 16));
  315. }
  316. return _results;
  317. })();
  318. };
  319. ipaddr.IPv6.parser = function(string) {
  320. var match, parts;
  321. if (string.match(ipv6Regexes['native'])) {
  322. return expandIPv6(string, 8);
  323. } else if (match = string.match(ipv6Regexes['transitional'])) {
  324. parts = expandIPv6(match[1].slice(0, -1), 6);
  325. if (parts) {
  326. parts.push(parseInt(match[2]) << 8 | parseInt(match[3]));
  327. parts.push(parseInt(match[4]) << 8 | parseInt(match[5]));
  328. return parts;
  329. }
  330. }
  331. return null;
  332. };
  333. ipaddr.IPv4.isIPv4 = ipaddr.IPv6.isIPv6 = function(string) {
  334. return this.parser(string) !== null;
  335. };
  336. ipaddr.IPv4.isValid = function(string) {
  337. var e;
  338. try {
  339. new this(this.parser(string));
  340. return true;
  341. } catch (_error) {
  342. e = _error;
  343. return false;
  344. }
  345. };
  346. ipaddr.IPv6.isValid = function(string) {
  347. var e;
  348. if (typeof string === "string" && string.indexOf(":") === -1) {
  349. return false;
  350. }
  351. try {
  352. new this(this.parser(string));
  353. return true;
  354. } catch (_error) {
  355. e = _error;
  356. return false;
  357. }
  358. };
  359. ipaddr.IPv4.parse = ipaddr.IPv6.parse = function(string) {
  360. var parts;
  361. parts = this.parser(string);
  362. if (parts === null) {
  363. throw new Error("ipaddr: string is not formatted like ip address");
  364. }
  365. return new this(parts);
  366. };
  367. ipaddr.IPv4.parseCIDR = function(string) {
  368. var maskLength, match;
  369. if (match = string.match(/^(.+)\/(\d+)$/)) {
  370. maskLength = parseInt(match[2]);
  371. if (maskLength >= 0 && maskLength <= 32) {
  372. return [this.parse(match[1]), maskLength];
  373. }
  374. }
  375. throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range");
  376. };
  377. ipaddr.IPv6.parseCIDR = function(string) {
  378. var maskLength, match;
  379. if (match = string.match(/^(.+)\/(\d+)$/)) {
  380. maskLength = parseInt(match[2]);
  381. if (maskLength >= 0 && maskLength <= 128) {
  382. return [this.parse(match[1]), maskLength];
  383. }
  384. }
  385. throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range");
  386. };
  387. ipaddr.isValid = function(string) {
  388. return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string);
  389. };
  390. ipaddr.parse = function(string) {
  391. if (ipaddr.IPv6.isValid(string)) {
  392. return ipaddr.IPv6.parse(string);
  393. } else if (ipaddr.IPv4.isValid(string)) {
  394. return ipaddr.IPv4.parse(string);
  395. } else {
  396. throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format");
  397. }
  398. };
  399. ipaddr.parseCIDR = function(string) {
  400. var e;
  401. try {
  402. return ipaddr.IPv6.parseCIDR(string);
  403. } catch (_error) {
  404. e = _error;
  405. try {
  406. return ipaddr.IPv4.parseCIDR(string);
  407. } catch (_error) {
  408. e = _error;
  409. throw new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format");
  410. }
  411. }
  412. };
  413. ipaddr.process = function(string) {
  414. var addr;
  415. addr = this.parse(string);
  416. if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) {
  417. return addr.toIPv4Address();
  418. } else {
  419. return addr;
  420. }
  421. };
  422. }).call(this);