├── .nvmrc ├── .gitignore ├── index.js ├── benchmark ├── package.json ├── package-lock.json └── benchmark.js ├── package.json ├── CHANGES.md ├── LICENSE ├── src └── Matcher.js ├── README.md └── test └── MatcherTest.js /.nvmrc: -------------------------------------------------------------------------------- 1 | node 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/Matcher'); 2 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-cidr-matcher-benchmark", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "benchmark.js", 6 | "author": "Marco Pracucci", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "cidr-matcher": "^1.0.5", 10 | "ip-range-check": "0.0.2", 11 | "is-in-subnet": "^1.9.0" 12 | }, 13 | "dependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cidr-matcher", 3 | "version": "2.1.1", 4 | "description": "Fast CIDR matcher. Given an input IPv4 address, it checks if it's inside a set of IP ranges, expressed in CIDR notation.", 5 | "main": "index.js", 6 | "homepage": "https://github.com/pracucci/node-cidr-matcher", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+ssh://git@github.com/pracucci/node-cidr-matcher.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/pracucci/node-cidr-matcher/issues" 13 | }, 14 | "scripts": { 15 | "test": "mocha ${MOCHA_OPTIONS} -R spec --recursive ./test" 16 | }, 17 | "keywords": [ 18 | "ip", 19 | "address", 20 | "cidr", 21 | "network", 22 | "class", 23 | "subnet" 24 | ], 25 | "author": "Marco Pracucci", 26 | "license": "MIT", 27 | "dependencies": { 28 | "ip6addr": "^0.2.2" 29 | }, 30 | "devDependencies": { 31 | "mocha": "^6.1.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ### 2019-07-17 2.1.1 4 | - Upgraded `mocha`, addressing [code injection vulnerability](https://www.npmjs.com/advisories/813) 5 | 6 | ### 2019-02-20 2.1.0 7 | - Optimized IPv4 address against IPv4 CIDRs by a 20x factor 8 | 9 | ### 2019-02-20 2.0.0 10 | - **BREAKING CHANGE**: a network class must always be in CIDR notation (ending with `/number`) otherwise will throw an `Error` 11 | - **BREAKING CHANGE**: removed `Matcher.removeNetworkClass()` 12 | - Added IPv6 support to `Matcher` 13 | - Replaced `ip` module dependency with `ip6addr` 14 | 15 | ### 2017-03-30 1.0.5 16 | - IMPROVEMENT: replaced `chai` with `assert`, reducing devDependencies size (see [issue #4](https://github.com/pracucci/node-cidr-matcher/issues/4) - thanks to [christian-fei](https://github.com/christian-fei)) 17 | 18 | ### 2017-03-30 1.0.4 19 | - FIX: correctly handle `0.0.0.0` IP address 20 | 21 | ### 2016-02-19 1.0.3 22 | - FIX: network and Broadcast addresses should match (issue #1) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Marco Pracucci 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /benchmark/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-cidr-matcher-benchmark", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "cidr-matcher": { 8 | "version": "1.0.5", 9 | "resolved": "https://registry.npmjs.org/cidr-matcher/-/cidr-matcher-1.0.5.tgz", 10 | "integrity": "sha1-GAXkl1G+j8gnmnM1VXTjHJ9JZms=", 11 | "dev": true, 12 | "requires": { 13 | "ip": "^1.0.2" 14 | } 15 | }, 16 | "ip": { 17 | "version": "1.1.5", 18 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 19 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", 20 | "dev": true 21 | }, 22 | "ip-range-check": { 23 | "version": "0.0.2", 24 | "resolved": "https://registry.npmjs.org/ip-range-check/-/ip-range-check-0.0.2.tgz", 25 | "integrity": "sha1-YFyFloeqTxhGORjUYZDYs2maKTw=", 26 | "dev": true, 27 | "requires": { 28 | "ipaddr.js": "^1.0.1" 29 | } 30 | }, 31 | "ipaddr.js": { 32 | "version": "1.9.0", 33 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 34 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", 35 | "dev": true 36 | }, 37 | "is-in-subnet": { 38 | "version": "1.9.0", 39 | "resolved": "https://registry.npmjs.org/is-in-subnet/-/is-in-subnet-1.9.0.tgz", 40 | "integrity": "sha512-zx7GdTPsGrLNIWIx978xGPP6UUjXYH/gG0vFtUARakofU2m+uiMqbSTR/bKCXooHjhW2W1KUDrhuuiaCc27sog==", 41 | "dev": true 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Matcher.js: -------------------------------------------------------------------------------- 1 | var ip6addr = require('ip6addr'); 2 | 3 | 4 | var Matcher = function(classes) { 5 | classes = classes || []; 6 | 7 | // Init 8 | this.ranges = { 9 | "ipv4": [], 10 | "ipv6": [], 11 | }; 12 | 13 | // Import network classes 14 | for (var i = 0; i < classes.length; i++) { 15 | this.addNetworkClass(classes[i]); 16 | } 17 | }; 18 | 19 | Matcher.prototype.addNetworkClass = function(input) { 20 | var cidr = ip6addr.createCIDR(input); 21 | 22 | // Detect if the input network class fits in the IPv4 address space. 23 | // If it doesn't an Error will be thrown and we'll add it to the IPv6 24 | // ranges in the catch block. 25 | try { 26 | var firstNumericAddr = cidr.address().toLong(); 27 | var lastNumericAddr = cidr.broadcast().toLong(); 28 | 29 | this.ranges["ipv4"].push({ 30 | cidr: cidr, 31 | first: firstNumericAddr, 32 | last: lastNumericAddr, 33 | }); 34 | } catch(err) { 35 | // We've been unable to convert first/last address it to Long, 36 | // so it means it spans over the IPv6 address space 37 | this.ranges["ipv6"].push({ 38 | cidr: cidr 39 | }); 40 | } 41 | }; 42 | 43 | Matcher.prototype.contains = function(addr) { 44 | // Parse input address 45 | try { 46 | addr = ip6addr.parse(addr); 47 | } catch(err) { 48 | return false; 49 | } 50 | 51 | var ipv4Ranges = this.ranges["ipv4"]; 52 | var ipv6Ranges = this.ranges["ipv6"]; 53 | 54 | // Check if the input address is within any IPv4 network range. 55 | // We detect if the input address is an IPv4. If yes, we can run an optimized 56 | // version which compares the IPv4 network ranges by numeric comparison. 57 | if (ipv4Ranges.length > 0) { 58 | if (addr.kind() === "ipv4") { 59 | var numericAddr = addr.toLong(); 60 | 61 | for (var i = 0, length = ipv4Ranges.length; i < length; i++) { 62 | if (ipv4Ranges[i].first <= numericAddr && ipv4Ranges[i].last >= numericAddr) { 63 | return true; 64 | } 65 | } 66 | } else { 67 | for (var i = 0, length = ipv4Ranges.length; i < length; i++) { 68 | if (ipv4Ranges[i].cidr.contains(addr)) { 69 | return true; 70 | } 71 | } 72 | } 73 | } 74 | 75 | // Check if the input address is within any IPv6 network range 76 | if (ipv6Ranges.length > 0) { 77 | for (var i = 0, length = ipv6Ranges.length; i < length; i++) { 78 | if (ipv6Ranges[i].cidr.contains(addr)) { 79 | return true; 80 | } 81 | } 82 | } 83 | 84 | return false; 85 | }; 86 | 87 | Matcher.prototype.containsAny = function(addrs) { 88 | for (var i in addrs) { 89 | if (addrs.hasOwnProperty(i) && this.contains(addrs[i])) { 90 | return true; 91 | } 92 | } 93 | 94 | return false; 95 | }; 96 | 97 | module.exports = Matcher; 98 | -------------------------------------------------------------------------------- /benchmark/benchmark.js: -------------------------------------------------------------------------------- 1 | const CIDRMatcherMaster = require("../src/Matcher"); 2 | const CIDRMatcher105 = require("cidr-matcher"); 3 | const { isInSubnet } = require("is-in-subnet"); 4 | const ipRangeCheck = require("ip-range-check"); 5 | 6 | 7 | function generateRandomIPv4() { 8 | return "" + 9 | Math.floor(Math.random() * 255) + "." + 10 | Math.floor(Math.random() * 255) + "." + 11 | Math.floor(Math.random() * 255) + "." + 12 | Math.floor(Math.random() * 255); 13 | } 14 | 15 | function generateRandomIPv6() { 16 | const parts = [... new Array(8)].map(() => { 17 | return Number(Math.floor(Math.random() * 65535)).toString(16); 18 | }); 19 | 20 | return parts.join(":"); 21 | } 22 | 23 | function trackExecutionTimeMs(func) { 24 | const startTime = process.hrtime(); 25 | 26 | func(); 27 | 28 | const elapsedTime = process.hrtime(startTime); 29 | const elapsedTimeMs = (elapsedTime[0] * 1000) + Math.round(elapsedTime[1] / 1000000); 30 | 31 | return elapsedTimeMs; 32 | } 33 | 34 | 35 | // Load Amazon CIDRs 36 | const cidrsV4 = require("./amazon-cidrs.json").prefixes.map((item) => item.ip_prefix); 37 | const cidrsV6 = require("./amazon-cidrs.json").ipv6_prefixes.map((item) => item.ipv6_prefix); 38 | 39 | // Generate input fixtures 40 | const numFixtures = 25000; 41 | const randomV4Set = [... new Array(numFixtures)].map(() => generateRandomIPv4()); 42 | const randomV6Set = [... new Array(numFixtures)].map(() => generateRandomIPv6()); 43 | 44 | 45 | // Print datasets info 46 | console.log("DATASETS"); 47 | console.log(`- ipv4: contains ${cidrsV4.length} CIDRs`); 48 | console.log(`- ipv6: contains ${cidrsV6.length} CIDRs`); 49 | console.log(""); 50 | 51 | // Run benchmark 52 | function runBenchmark(moduleName, moduleVersion, datasetName, func) { 53 | const cidrs = datasetName === "ipv4" ? cidrsV4 : cidrsV6; 54 | const ips = datasetName === "ipv4" ? randomV4Set : randomV6Set; 55 | 56 | const executionTime = trackExecutionTimeMs(() => { 57 | func(cidrs, ips); 58 | }); 59 | 60 | console.log(`${moduleName} v. ${moduleVersion} against ${datasetName} dataset: ${executionTime} ms`); 61 | } 62 | 63 | console.log("BENCHMARK"); 64 | 65 | runBenchmark("node-cidr-matcher", "master", "ipv4", (cidrs, ips) => { 66 | let matcher = new CIDRMatcherMaster(cidrs); 67 | for (let i = 0; i < ips.length; i++) { 68 | matcher.contains(ips[i]); 69 | } 70 | }); 71 | 72 | runBenchmark("node-cidr-matcher", "1.0.5", "ipv4", (cidrs, ips) => { 73 | let matcher = new CIDRMatcher105(cidrs); 74 | for (let i = 0; i < ips.length; i++) { 75 | matcher.contains(ips[i]); 76 | } 77 | }); 78 | 79 | runBenchmark("node-cidr-matcher", "master", "ipv6", (cidrs, ips) => { 80 | let matcher = new CIDRMatcherMaster(cidrs); 81 | for (let i = 0; i < ips.length; i++) { 82 | matcher.contains(ips[i]); 83 | } 84 | }); 85 | 86 | runBenchmark("is-in-subnet", "1.9.0", "ipv4", (cidrs, ips) => { 87 | for (let i = 0; i < ips.length; i++) { 88 | isInSubnet(ips[i], cidrs); 89 | } 90 | }); 91 | 92 | runBenchmark("is-in-subnet", "1.9.0", "ipv6", (cidrs, ips) => { 93 | for (let i = 0; i < ips.length; i++) { 94 | isInSubnet(ips[i], cidrs); 95 | } 96 | }); 97 | 98 | runBenchmark("ip-range-check", "0.0.2", "ipv4", (cidrs, ips) => { 99 | for (let i = 0; i < ips.length; i++) { 100 | ipRangeCheck(ips[i], cidrs); 101 | } 102 | }); 103 | 104 | runBenchmark("ip-range-check", "0.0.2", "ipv6", (cidrs, ips) => { 105 | for (let i = 0; i < ips.length; i++) { 106 | ipRangeCheck(ips[i], cidrs); 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CIDR Matcher 2 | 3 | Fast CIDR matcher. Given an input IPv4 or IPv6 address, it checks if it's inside a set of IP ranges, expressed in CIDR notation. This module is based upon the great [ip6addr](https://github.com/joyent/node-ip6addr) module. 4 | 5 | 6 | ## Installation 7 | 8 | ### npm 9 | ```shell 10 | npm install cidr-matcher 11 | ``` 12 | 13 | ### git 14 | 15 | ```shell 16 | git clone https://github.com/pracucci/node-cidr-matcher.git 17 | ``` 18 | 19 | 20 | ## Usage 21 | 22 | ```js 23 | 24 | var CIDRMatcher = require('cidr-matcher'); 25 | 26 | var matcher = new CIDRMatcher([ '2a05:d07c:2000:0:0:0:0:0/120', '192.168.1.0/24', '192.168.2.3/32', '192.168.3.2/32' ]); 27 | 28 | matcher.contains('192.168.1.1'); // returns true 29 | matcher.contains('192.168.1.2'); // returns true 30 | 31 | matcher.contains('192.168.2.1'); // returns true 32 | matcher.contains('192.168.2.2'); // returns false 33 | 34 | matcher.contains('192.168.3.1'); // returns false 35 | matcher.contains('192.168.3.2'); // returns false 36 | 37 | matcher.containsAny([ '192.168.1.1', '192.168.1.2' ]); // return true 38 | matcher.containsAny([ '192.168.2.1', '192.168.2.2' ]); // return true 39 | matcher.containsAny([ '192.168.3.1', '192.168.3.2' ]); // return false 40 | 41 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:0')); // return true 42 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:ff')); // return true 43 | assert.ok(matcher.contains('2a05:d07c:3000:0:0:0:0:0')); // return false 44 | 45 | // You can also add / remove network classes on-the-fly 46 | matcher.addNetworkClass('192.168.5.0/24'); 47 | matcher.contains('192.168.5.1'); // returns true 48 | 49 | matcher.removeNetworkClass('192.168.5.0/24'); 50 | matcher.contains('192.168.5.1'); // returns false 51 | ``` 52 | 53 | 54 | ## Benchmark 55 | 56 | The following table shows the execution of `benchmark/` across different versions of this module and other modules you can find on npm. Each benchmark is **executed with a random set of 25000 IP addresses**, where each IP is checked against each network range (CIDR) in the test dataset. 57 | 58 | | Module | Version | Dataset | Execution time | 59 | | ---------------- | ------- | --------------------- | -------------- | 60 | | _This one_ | `2.0.0` | AWS IPv4 (1385 CIDRs) | `228 ms` | 61 | | _This one_ | `2.0.0` | AWS IPv6 (474 CIDRs) | `1799 ms` | 62 | | _This one_ | `1.0.5` | AWS IPv4 (1385 CIDRs) | `2895 ms` (_without IPv4 over IPv6 support_) | 63 | | _This one_ | `1.0.5` | AWS IPv6 (474 CIDRs) | _Unsupported_ | 64 | | `is-in-subnet` | `1.9.0` | AWS IPv4 (1385 CIDRs) | `96106 ms` | 65 | | `is-in-subnet` | `1.9.0` | AWS IPv6 (474 CIDRs) | `33482 ms` | 66 | | `ip-range-check` | `0.0.2` | AWS IPv4 (1385 CIDRs) | `390134 ms` | 67 | | `ip-range-check` | `0.0.2` | AWS IPv6 (474 CIDRs) | `73083 ms` | 68 | 69 | _If you're aware of any other module that implements the IP in CIDR and you wanna see it benchmarked, please open an Issue or submit a PR._ 70 | 71 | 72 | ## Contribute 73 | 74 | ### Run tests 75 | 76 | ```shell 77 | npm test 78 | ``` 79 | 80 | 81 | ### License 82 | 83 | This software is licensed under the MIT License. 84 | 85 | Copyright (c) 2015, Marco Pracucci 86 | 87 | Permission is hereby granted, free of charge, to any person obtaining a copy 88 | of this software and associated documentation files (the "Software"), to deal 89 | in the Software without restriction, including without limitation the rights 90 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 91 | copies of the Software, and to permit persons to whom the Software is 92 | furnished to do so, subject to the following conditions: 93 | 94 | The above copyright notice and this permission notice shall be included in 95 | all copies or substantial portions of the Software. 96 | 97 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 98 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 99 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 100 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 101 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 102 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 103 | THE SOFTWARE. 104 | -------------------------------------------------------------------------------- /test/MatcherTest.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | Matcher = require('../src/Matcher.js'); 3 | 4 | describe('Matcher', function() { 5 | 6 | describe('contains()', function() { 7 | 8 | it('should return false with a bad input address', function() { 9 | var matcher = new Matcher([ '192.168.1.2/32', '2a05:d07c:2000::/40' ]); 10 | 11 | assert.ok(!matcher.contains('0.0.0.192.168.1.2')); 12 | assert.ok(!matcher.contains('xxx:d07c:2000::')); 13 | }); 14 | 15 | it('should return true if an IPv4 matches a /32 network range', function() { 16 | var matcher = new Matcher([ '192.168.1.2/32' ]); 17 | 18 | assert.ok(!matcher.contains('192.168.1.1')); 19 | assert.ok(matcher.contains('192.168.1.2')); 20 | assert.ok(!matcher.contains('192.168.1.3')); 21 | }); 22 | 23 | it('should return true if an IPv4 matches a /30 network range', function() { 24 | var matcher = new Matcher([ '192.168.1.0/30' ]); 25 | 26 | assert.ok(!matcher.contains('192.168.0.1')); 27 | 28 | assert.ok(matcher.contains('192.168.1.0')); 29 | assert.ok(matcher.contains('192.168.1.1')); 30 | assert.ok(matcher.contains('192.168.1.2')); 31 | assert.ok(matcher.contains('192.168.1.3')); 32 | assert.ok(!matcher.contains('192.168.1.4')); 33 | }); 34 | 35 | it('should return true if an IPv4 matches a /24 network range', function() { 36 | var matcher = new Matcher([ '192.168.1.0/24' ]); 37 | 38 | assert.ok(!matcher.contains('192.168.0.1')); 39 | assert.ok(!matcher.contains('192.168.0.254')); 40 | 41 | assert.ok(!matcher.contains('192.168.2.1')); 42 | assert.ok(!matcher.contains('192.168.2.254')); 43 | 44 | assert.ok(matcher.contains('192.168.1.0')); 45 | assert.ok(matcher.contains('192.168.1.1')); 46 | assert.ok(matcher.contains('192.168.1.128')); 47 | assert.ok(matcher.contains('192.168.1.254')); 48 | assert.ok(matcher.contains('192.168.1.255')); 49 | }); 50 | 51 | it('should return true if an IPv4 matches a /16 network range', function() { 52 | var matcher = new Matcher([ '192.168.1.0/16' ]); 53 | 54 | assert.ok(matcher.contains('192.168.0.0')); 55 | assert.ok(matcher.contains('192.168.0.1')); 56 | assert.ok(matcher.contains('192.168.0.254')); 57 | 58 | assert.ok(matcher.contains('192.168.128.1')); 59 | assert.ok(matcher.contains('192.168.128.254')); 60 | 61 | assert.ok(matcher.contains('192.168.255.1')); 62 | assert.ok(matcher.contains('192.168.255.254')); 63 | assert.ok(matcher.contains('192.168.255.255')); 64 | 65 | assert.ok(!matcher.contains('192.167.0.1')); 66 | assert.ok(!matcher.contains('192.169.0.1')); 67 | }); 68 | 69 | it('should return true if an IPv4 matches multiple /32 network ranges', function() { 70 | var matcher = new Matcher([ '192.168.1.3/32', '192.168.1.2/32' ]); 71 | 72 | assert.ok(!matcher.contains('192.168.1.1')); 73 | assert.ok(matcher.contains('192.168.1.2')); 74 | assert.ok(matcher.contains('192.168.1.3')); 75 | assert.ok(!matcher.contains('192.168.1.4')); 76 | }); 77 | 78 | it('should return true if an IPv4 matches network ranges', function() { 79 | var matcher = new Matcher([ '192.168.1.0/24', '192.168.2.3/32', '192.168.3.2/32' ]); 80 | 81 | assert.ok(matcher.contains('192.168.1.1')); 82 | assert.ok(matcher.contains('192.168.1.2')); 83 | assert.ok(matcher.contains('192.168.1.3')); 84 | 85 | assert.ok(!matcher.contains('192.168.2.1')); 86 | assert.ok(!matcher.contains('192.168.2.2')); 87 | assert.ok(matcher.contains('192.168.2.3')); 88 | 89 | assert.ok(!matcher.contains('192.168.3.1')); 90 | assert.ok(matcher.contains('192.168.3.2')); 91 | assert.ok(!matcher.contains('192.168.3.3')); 92 | }); 93 | 94 | it('should return true if an IPv4 matches overlapping network ranges', function() { 95 | var matcher = new Matcher([ '192.168.1.0/24', '192.168.1.3/32', '192.168.1.2/32' ]); 96 | 97 | assert.ok(!matcher.contains('192.168.0.1')); 98 | assert.ok(!matcher.contains('192.168.0.254')); 99 | 100 | assert.ok(!matcher.contains('192.168.2.1')); 101 | assert.ok(!matcher.contains('192.168.2.254')); 102 | 103 | assert.ok(matcher.contains('192.168.1.1')); 104 | assert.ok(matcher.contains('192.168.1.2')); 105 | assert.ok(matcher.contains('192.168.1.3')); 106 | assert.ok(matcher.contains('192.168.1.128')); 107 | assert.ok(matcher.contains('192.168.1.254')); 108 | }); 109 | 110 | it('should return true if an IPv4 matches a 0.0.0.0 range', function() { 111 | var matcher = new Matcher([ '0.0.0.0/24' ]); 112 | 113 | assert.ok(!matcher.contains('192.168.2.1')); 114 | assert.ok(!matcher.contains('192.168.2.254')); 115 | 116 | assert.ok(matcher.contains('0.0.0.0')); 117 | assert.ok(matcher.contains('0.0.0.10')); 118 | assert.ok(matcher.contains('0.0.0.254')); 119 | }); 120 | 121 | it('should return true if an IPv6 matches a /128 network range', function() { 122 | var matcher = new Matcher([ '2a05:d07c:2000:0:0:0:0:1/128' ]); 123 | 124 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:0:0')); 125 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:1')); 126 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:0:2')); 127 | }); 128 | 129 | it('should return true if an IPv6 matches a /120 network range', function() { 130 | var matcher = new Matcher([ '2a05:d07c:2000:0:0:0:0:1/120' ]); 131 | 132 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:0')); 133 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:1')); 134 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:ff')); 135 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:0:ffff')); 136 | }); 137 | 138 | it('should return true if an IPv6 matches multiple /128 network ranges', function() { 139 | var matcher = new Matcher([ '2a05:d07c:2000:0:0:0:0:1/128', '2a05:d07c:2000:0:0:0:0:2/128' ]); 140 | 141 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:0:0')); 142 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:1')); 143 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:2')); 144 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:0:3')); 145 | }); 146 | 147 | it('should return true if an IPv6 matches overlapping network ranges', function() { 148 | var matcher = new Matcher([ '2a05:d07c:2000:0:0:0:0:0/120', '2a05:d07c:2000:0:0:0:0:0/110' ]); 149 | 150 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:4:0')); 151 | 152 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:0')); 153 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:ff')); 154 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:1:0')); 155 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:3:ffff')); 156 | }); 157 | 158 | it('should support mixed IPv4 and IPv6 network ranges', function() { 159 | var matcher = new Matcher([ '2a05:d07c:2000:0:0:0:0:0/120', '2a05:d07c:2000:0:0:0:0:0/110', '192.168.1.0/24', '192.168.2.3/32', '192.168.3.2/32' ]); 160 | 161 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:4:0')); 162 | 163 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:0')); 164 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:ff')); 165 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:1:0')); 166 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:3:ffff')); 167 | 168 | assert.ok(matcher.contains('192.168.1.1')); 169 | assert.ok(matcher.contains('192.168.1.2')); 170 | assert.ok(matcher.contains('192.168.1.3')); 171 | 172 | assert.ok(!matcher.contains('192.168.2.1')); 173 | assert.ok(!matcher.contains('192.168.2.2')); 174 | assert.ok(matcher.contains('192.168.2.3')); 175 | 176 | assert.ok(!matcher.contains('192.168.3.1')); 177 | assert.ok(matcher.contains('192.168.3.2')); 178 | assert.ok(!matcher.contains('192.168.3.3')); 179 | }); 180 | 181 | it('should support IPv4 detection in a IPv6 network range', function() { 182 | var matcher = new Matcher([ '0:0:0:0:0:ffff:102:304/128' ]); 183 | 184 | assert.ok(!matcher.contains('1.2.3.3')); 185 | assert.ok(matcher.contains('1.2.3.4')); 186 | assert.ok(!matcher.contains('1.2.3.5')); 187 | }); 188 | 189 | it('should support IPv6 detection in a IPv4 network range', function() { 190 | var matcher = new Matcher([ '1.2.3.4/32' ]); 191 | 192 | assert.ok(!matcher.contains('0:0:0:0:0:ffff:102:303')); 193 | assert.ok(matcher.contains('0:0:0:0:0:ffff:102:304')); 194 | assert.ok(!matcher.contains('0:0:0:0:0:ffff:102:305')); 195 | }); 196 | 197 | it('should support IPv4 detection in a IPv6 network range spanning outside the IPv4 space', function() { 198 | var matcher = new Matcher([ '0:0:0:0:0:0:0:0/64' ]); 199 | 200 | assert.ok(matcher.contains('0.0.0.0')); 201 | assert.ok(matcher.contains('255.255.255.255')); 202 | 203 | assert.ok(matcher.contains('0:0:0:0:0:0:0:0')); 204 | assert.ok(matcher.contains('0:0:0:0:ffff:ffff:ffff:ffff')); 205 | assert.ok(!matcher.contains('0:0:0:1000:ffff:ffff:ffff:ffff')); 206 | }); 207 | }); 208 | 209 | 210 | describe('containsAny()', function() { 211 | 212 | it('should return true if any of input IPv4 addresses matches ranges', function() { 213 | var matcher = new Matcher([ '192.168.1.0/24', '192.168.2.3/32', '192.168.3.2/32' ]); 214 | 215 | assert.ok(!matcher.containsAny([ '192.168.2.1' ])); 216 | assert.ok(!matcher.containsAny([ '192.168.2.1', '192.168.3.3' ])); 217 | 218 | assert.ok(matcher.containsAny([ '192.168.2.1', '192.168.3.3', '192.168.2.3' ])); 219 | }); 220 | 221 | it('should return true if any of input IPv6 addresses matches ranges', function() { 222 | var matcher = new Matcher([ '2a05:d07c:2000::/40' ]); 223 | 224 | assert.ok(!matcher.containsAny([ '2a05:d07c:1000:0:0:0:0:0' ])); 225 | assert.ok(!matcher.containsAny([ '2a05:d07c:1000:0:0:0:0:0', '2a05:d07c:3000:0:0:0:0:0' ])); 226 | 227 | assert.ok(matcher.containsAny([ '2a05:d07c:1000:0:0:0:0:0', '2a05:d07c:2000:0:0:0:0:0', '2a05:d07c:3000:0:0:0:0:0' ])); 228 | }); 229 | }); 230 | 231 | 232 | describe('addNetworkClass()', function() { 233 | 234 | it('should add an IPv4 cidr to the matcher', function() { 235 | var matcher = new Matcher(); 236 | assert.ok(!matcher.contains('192.168.1.3')); 237 | 238 | matcher.addNetworkClass('192.168.1.1/24'); 239 | assert.ok(matcher.contains('192.168.1.3')); 240 | }); 241 | 242 | it('should add an IPv6 cidr to the matcher', function() { 243 | var matcher = new Matcher(); 244 | assert.ok(!matcher.contains('2a05:d07c:2000:0:0:0:0:1')); 245 | 246 | matcher.addNetworkClass('2a05:d07c:2000::/40'); 247 | assert.ok(matcher.contains('2a05:d07c:2000:0:0:0:0:1')); 248 | }); 249 | 250 | it('should throw an error if the input network class is not in the CIDR notation', function() { 251 | var matcher = new Matcher(); 252 | 253 | try { 254 | matcher.addNetworkClass('192.168.1.2'); 255 | fail("should throw an Error"); 256 | } catch(err) { 257 | assert.equal("Invalid argument: / expected", err.message); 258 | } 259 | }); 260 | }); 261 | 262 | }); 263 | --------------------------------------------------------------------------------