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