├── .github ├── dependabot.yml └── workflows │ ├── SAST.yml │ └── Unit-Test.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CREDITS.md ├── LICENSE.md ├── README.md ├── example └── ipcalc.coffee ├── lib └── netmask.coffee ├── package.json ├── test ├── badnets.coffee └── netmasks.coffee └── tests └── netmask.js /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | 10 | # Maintain dependencies for npm 11 | - package-ecosystem: "npm" 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" 15 | -------------------------------------------------------------------------------- /.github/workflows/SAST.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | # The branches below must be a subset of the branches above 9 | branches: [main] 10 | schedule: 11 | - cron: "44 18 * * 1" 12 | 13 | jobs: 14 | analyze: 15 | name: Analyze 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | language: ["javascript-typescript"] 22 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 23 | # Learn more: 24 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v4 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v2 33 | with: 34 | languages: ${{ matrix.language }} 35 | # If you wish to specify custom queries, you can do so here or in a config file. 36 | # By default, queries listed here will override any specified in a config file. 37 | # Prefix the list here with "+" to use these queries and those in the config file. 38 | queries: +security-extended,security-and-quality 39 | 40 | - name: Install Dependencies 41 | run: npm install 42 | 43 | - name: Build Project 44 | run: npm run-script prepublish 45 | 46 | - name: Perform CodeQL Analysis 47 | uses: github/codeql-action/analyze@v2 48 | -------------------------------------------------------------------------------- /.github/workflows/Unit-Test.yml: -------------------------------------------------------------------------------- 1 | name: Unit Test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | Test: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Download Source Code 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up the Node.JS Runtime 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 18 22 | 23 | - name: Install Dependencies 24 | run: npm install 25 | 26 | - name: Build Project 27 | run: npm run-script prepublish 28 | 29 | - name: Run the Unit Tests 30 | run: npm run-script test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | lib/netmask.js 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git* 2 | tests/ 3 | test/ 4 | example/ 5 | lib/*.coffee 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v2.0.2 (Apr 2, 2021) 2 | 3 | ### Bugfixes 4 | 5 | * Fix `08` parsed as decimal while `018` rejected. ([commit](https://github.com/rs/node-netmask/commit/50a0053bd869b72313cc96ab73108bb83079c3f5)) 6 | 7 | ## v2.0.1 (Mar 29, 2021) 8 | 9 | ### IMPORTANT: Security Fix 10 | 11 | > This version contains an important security fix. If you are using netmask `<=2.0.0`, please upgrade to `2.0.1` or above. 12 | 13 | * Rewrite byte parsing without using JS `parseInt()`([commit](https://github.com/rs/node-netmask/commit/3f19a056c4eb808ea4a29f234274c67bc5a848f4)) 14 | * This is [CVE-2021-29418](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29418). 15 | 16 | ### Bugfixes 17 | 18 | * Add checks on spaces before and after bytes 19 | * This will now throw an exception when spaces are present like ' 1.2.3.4' or '1. 2.3.4' or '1.2.3.4 '. 20 | 21 | ### Internal Changes 22 | 23 | * Avoid some useless memory allocations 24 | * New Mocha testing suite, thanks @kaoudis [#36](https://github.com/rs/node-netmask/pull/36) 25 | 26 | ## v2.0.0 (Mar 19, 2021) 27 | 28 | ### Breaking Change 29 | 30 | Previous API was treating IPs with less than four bytes as IP with a 31 | netmask of the size of the provided bytes (1=8, 2=16, 3=24) and was 32 | interpreting the IP as if it was completed with 0s on the right side. 33 | 34 | *Proper IP parsing for these is to consider missing bytes as being 0s on 35 | the left side.* 36 | 37 | Mask size is no longer infered by the number of bytes provided. 38 | 39 | This means that the input `216.240` will no longer be interpreted as `216.240.0.0/16`, but as `0.0.216.240/32`, 40 | as per convention. 41 | 42 | See [the change](https://github.com/rs/node-netmask/commit/9f9fc38c6db1a682d23289b5c9dc2009d957a00b). 43 | 44 | ### Bugfixes 45 | 46 | * Fix improper parsing of hex bytes 47 | 48 | ## v1.1.0 (Mar 18, 2021) 49 | 50 | ### IMPORTANT: Security Fix 51 | 52 | > This version contains an important security fix. If you are using netmask `<=1.0.6`, please upgrade to `1.1.0` or above. 53 | 54 | * Fix improper parsing of octal bytes ([commit](https://github.com/rs/node-netmask/commit/4678fd840ad0b4730dbad2d415712c0782e886cc)) 55 | * This is [CVE-2021-28918](https://sick.codes/sick-2021-011). 56 | * See also the [npm advisory](https://www.npmjs.com/advisories/1658) 57 | 58 | ### Other Changes 59 | 60 | * Performance: Avoid large allocations when provided large netmasks (like `/8`) 61 | * Thanks @dschenkelman [#34](https://github.com/rs/node-netmask/pull/34) 62 | 63 | ## v1.0.6 (May 30, 2016) 64 | 65 | * Changes before this release are not documented here. Please see [the commit list](https://github.com/rs/node-netmask/commits/master) 66 | or the [compare view](https://github.com/rs/node-netmask/compare/1.0.5...rs:1.0.6). 67 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | Credits 2 | ======= 3 | 4 | These credits refer to the contributors to this repository: 5 | 6 | [@rs](https://github.com/rs) - maintainer 7 | 8 | [@ryanrolds](https://github.com/ryanrolds) - Now with 0.4.x support. #2 9 | 10 | [@palmerabollo](https://github.com/palmerabollo) - Expressions "1.2.3.4/" are not valid #6 11 | 12 | [@steve-jansen](https://github.com/steve-jansen) - fixes typo in readme.md #7 13 | 14 | [@jksdua](https://github.com/jksdua) - Added forEach helper to allow looping through usable IP addresses #9 15 | 16 | [@gmiroshnykov](https://github.com/gmiroshnykov) - Tiny typo fix #10 17 | 18 | [@TooTallNate](https://github.com/TooTallNate) - package: move "coffee-script" to devDependencies #11, README: fix small typo #12 19 | 20 | [@yorkie](https://github.com/yorkie) - more rigid check for Netmask.constructor #13 21 | 22 | [@runk](https://github.com/runk) - fix contains method for netmasks #18 23 | 24 | [@yvesago](https://github.com/yvesago) - a patch with mocha test to fix /31 and /32 block.contains #20 25 | 26 | [@meteormatt](https://github.com/meteormatt) - The comment in README.md is wrong #22 27 | 28 | [@dschenkelman](https://github.com/dschenkelman) - Avoid large memory allocations when doing forEach in case netmask is large (e.g. /8) #34 29 | 30 | [@sickcodes](https://github.com/sickcodes), [@kaoudis](https://github.com/kaoudis), [@Koroeskohr](https://github.com/Koroeskohr), [@nicksahler](https://github.com/nicksahler) - adds CREDITS, plus mocha tests for transpiled node #36 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011 Olivier Poitrey rs@rhapsodyk.net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Netmask 2 | ======= 3 | 4 | The Netmask class parses and understands IPv4 CIDR blocks so they can be explored and compared. This module is highly inspired by Perl [Net::Netmask](http://search.cpan.org/dist/Net-Netmask/) module. 5 | 6 | Synopsis 7 | -------- 8 | 9 | ```js 10 | var Netmask = require('netmask').Netmask 11 | 12 | var block = new Netmask('10.0.0.0/12'); 13 | block.base; // 10.0.0.0 14 | block.mask; // 255.240.0.0 15 | block.bitmask; // 12 16 | block.hostmask; // 0.15.255.255 17 | block.broadcast; // 10.15.255.255 18 | block.size; // 1048576 19 | block.first; // 10.0.0.1 20 | block.last; // 10.15.255.254 21 | 22 | block.contains('10.0.8.10'); // true 23 | block.contains('10.8.0.10'); // true 24 | block.contains('192.168.1.20'); // false 25 | 26 | block.forEach(function(ip, long, index)); 27 | 28 | block.next() // Netmask('10.16.0.0/12') 29 | ``` 30 | 31 | Constructing 32 | ------------ 33 | 34 | Netmask objects are created with an IP address and optionally a mask. There are many forms that are recognized: 35 | 36 | ``` 37 | '216.240.32.0/24' // The preferred form. 38 | '216.240.32.0/255.255.255.0' 39 | '216.240.32.0', '255.255.255.0' 40 | '216.240.32.0', 0xffffff00 41 | '216.240.32.4' // A /32 block. 42 | '0330.0360.040.04' // Octal form 43 | '0xd8.0xf0.0x20.0x4' // Hex form 44 | ``` 45 | 46 | API 47 | --- 48 | 49 | - `.base`: The base address of the network block as a string (eg: 216.240.32.0). Base does not give an indication of the size of the network block. 50 | - `.mask`: The netmask as a string (eg: 255.255.255.0). 51 | - `.hostmask`: The host mask which is the opposite of the netmask (eg: 0.0.0.255). 52 | - `.bitmask`: The netmask as a number of bits in the network portion of the address for this block (eg: 24). 53 | - `.size`: The number of IP addresses in a block (eg: 256). 54 | - `.broadcast`: The blocks broadcast address (eg: 192.168.1.0/24 => 192.168.1.255) 55 | - `.first`, `.last`: First and last useable address 56 | - `.contains(ip or block)`: Returns a true if the IP number `ip` is part of the network. That is, a true value is returned if `ip` is between `base` and `broadcast`. If a Netmask object or a block is given, it returns true only of the given block fits inside the network. 57 | - `.forEach(fn)`: Similar to the Array prototype method. It loops through all the useable addresses, ie between `first` and `last`. 58 | - `.next(count)`: Without a `count`, return the next block of the same size after the current one. With a count, return the Nth block after the current one. A count of -1 returns the previous block. Undef will be returned if out of legal address space. 59 | - `.toString()`: The netmask in base/bitmask format (e.g., '216.240.32.0/24') 60 | 61 | Installation 62 | ------------ 63 | 64 | $ npm install netmask 65 | 66 | Run all tests (vows plus mocha) 67 | ------------------------------- 68 | 69 | $ npm test 70 | 71 | License 72 | ------- 73 | 74 | (The MIT License) 75 | 76 | Copyright (c) 2011 Olivier Poitrey 77 | 78 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 79 | 80 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 81 | 82 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /example/ipcalc.coffee: -------------------------------------------------------------------------------- 1 | Netmask = require('netmask').Netmask 2 | 3 | ip = process.argv[2] 4 | netmask = new Netmask(ip) 5 | 6 | out = console.log 7 | out "Address: #{ip.split('/', 1)[0]}" 8 | out "Netmask: #{netmask.mask} = #{netmask.bitmask}" 9 | out "Wildcard: #{netmask.hostmask}" 10 | out "=>" 11 | out "Network: #{netmask.base}/#{netmask.bitmask}" 12 | out "HostMin: #{netmask.first}" 13 | out "HostMax: #{netmask.last}" 14 | out "Broadcast: #{netmask.broadcast}" 15 | out "Hosts/Net: #{netmask.size}" 16 | 17 | out netmask.next() -------------------------------------------------------------------------------- /lib/netmask.coffee: -------------------------------------------------------------------------------- 1 | long2ip = (long) -> 2 | a = (long & (0xff << 24)) >>> 24; 3 | b = (long & (0xff << 16)) >>> 16; 4 | c = (long & (0xff << 8)) >>> 8; 5 | d = long & 0xff; 6 | return [a, b, c, d].join('.') 7 | 8 | ip2long = (ip) -> 9 | b = [] 10 | for i in [0..3] 11 | if ip.length == 0 12 | break 13 | if i > 0 14 | if ip[0] != '.' 15 | throw new Error('Invalid IP') 16 | ip = ip.substring(1) 17 | [n, c] = atob(ip) 18 | ip = ip.substring(c) 19 | b.push(n) 20 | if ip.length != 0 21 | throw new Error('Invalid IP') 22 | switch b.length 23 | when 1 24 | # Long input notation 25 | if b[0] > 0xFFFFFFFF 26 | throw new Error('Invalid IP') 27 | return b[0] >>> 0 28 | when 2 29 | # Class A notation 30 | if b[0] > 0xFF or b[1] > 0xFFFFFF 31 | throw new Error('Invalid IP') 32 | return (b[0] << 24 | b[1]) >>> 0 33 | when 3 34 | # Class B notation 35 | if b[0] > 0xFF or b[1] > 0xFF or b[2] > 0xFFFF 36 | throw new Error('Invalid IP') 37 | return (b[0] << 24 | b[1] << 16 | b[2]) >>> 0 38 | when 4 39 | # Dotted quad notation 40 | if b[0] > 0xFF or b[1] > 0xFF or b[2] > 0xFF or b[3] > 0xFF 41 | throw new Error('Invalid IP') 42 | return (b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]) >>> 0 43 | else 44 | throw new Error('Invalid IP') 45 | 46 | chr = (b) -> 47 | return b.charCodeAt(0) 48 | 49 | chr0 = chr('0') 50 | chra = chr('a') 51 | chrA = chr('A') 52 | 53 | atob = (s) -> 54 | n = 0 55 | base = 10 56 | dmax = '9' 57 | i = 0 58 | if s.length > 1 and s[i] == '0' 59 | if s[i+1] == 'x' or s[i+1] == 'X' 60 | i += 2 61 | base = 16 62 | else if '0' <= s[i+1] and s[i+1] <= '9' 63 | i++ 64 | base = 8 65 | dmax = '7' 66 | start = i 67 | while i < s.length 68 | if '0' <= s[i] and s[i] <= dmax 69 | n = (n*base + (chr(s[i])-chr0)) >>> 0 70 | else if base == 16 71 | if 'a' <= s[i] and s[i] <= 'f' 72 | n = (n*base + (10+chr(s[i])-chra)) >>> 0 73 | else if 'A' <= s[i] and s[i] <= 'F' 74 | n = (n*base + (10+chr(s[i])-chrA)) >>> 0 75 | else 76 | break 77 | else 78 | break 79 | if n > 0xFFFFFFFF 80 | throw new Error('too large') 81 | i++ 82 | if i == start 83 | throw new Error('empty octet') 84 | return [n, i] 85 | 86 | class Netmask 87 | constructor: (net, mask) -> 88 | throw new Error("Missing `net' parameter") unless typeof net is 'string' 89 | unless mask 90 | # try to find the mask in the net (i.e.: 1.2.3.4/24 or 1.2.3.4/255.255.255.0) 91 | [net, mask] = net.split('/', 2) 92 | unless mask 93 | mask = 32 94 | if typeof mask is 'string' and mask.indexOf('.') > -1 95 | # Compute bitmask, the netmask as a number of bits in the network portion of the address for this block (eg.: 24) 96 | try 97 | @maskLong = ip2long(mask) 98 | catch error 99 | throw new Error("Invalid mask: #{mask}") 100 | for i in [32..0] 101 | if @maskLong == (0xffffffff << (32 - i)) >>> 0 102 | @bitmask = i 103 | break 104 | else if mask or mask == 0 105 | # The mask was passed as bitmask, compute the mask as long from it 106 | @bitmask = parseInt(mask, 10) 107 | @maskLong = 0 108 | if @bitmask > 0 109 | @maskLong = (0xffffffff << (32 - @bitmask)) >>> 0 110 | else 111 | throw new Error("Invalid mask: empty") 112 | 113 | try 114 | @netLong = (ip2long(net) & @maskLong) >>> 0 115 | catch error 116 | throw new Error("Invalid net address: #{net}") 117 | 118 | throw new Error("Invalid mask for ip4: #{mask}") unless @bitmask <= 32 119 | 120 | # The number of IP address in the block (eg.: 254) 121 | @size = Math.pow(2, 32 - @bitmask) 122 | # The address of the network block as a string (eg.: 216.240.32.0) 123 | @base = long2ip(@netLong) 124 | # The netmask as a string (eg.: 255.255.255.0) 125 | @mask = long2ip(@maskLong) 126 | # The host mask, the opposite of the netmask (eg.: 0.0.0.255) 127 | @hostmask = long2ip(~@maskLong) 128 | # The first usable address of the block 129 | @first = if @bitmask <= 30 then long2ip(@netLong + 1) else @base 130 | # The last usable address of the block 131 | @last = if @bitmask <= 30 then long2ip(@netLong + @size - 2) else long2ip(@netLong + @size - 1) 132 | # The block's broadcast address: the last address of the block (eg.: 192.168.1.255) 133 | @broadcast = if @bitmask <= 30 then long2ip(@netLong + @size - 1) 134 | 135 | # Returns true if the given ip or netmask is contained in the block 136 | contains: (ip) -> 137 | if typeof ip is 'string' and (ip.indexOf('/') > 0 or ip.split('.').length isnt 4) 138 | ip = new Netmask(ip) 139 | 140 | if ip instanceof Netmask 141 | return @contains(ip.base) and @contains((ip.broadcast || ip.last)) 142 | else 143 | return (ip2long(ip) & @maskLong) >>> 0 == ((@netLong & @maskLong)) >>> 0 144 | 145 | # Returns the Netmask object for the block which follow this one 146 | next: (count=1) -> 147 | return new Netmask(long2ip(@netLong + (@size * count)), @mask) 148 | 149 | forEach: (fn) -> 150 | # this implementation is not idiomatic but avoids large memory allocations (2 arrays, one for range and one for the results) in cases when then netmask is large 151 | long = ip2long(@first) 152 | lastLong = ip2long(@last) 153 | index = 0 154 | while long <= lastLong 155 | fn long2ip(long), long, index 156 | index++ 157 | long++ 158 | return 159 | 160 | # Returns the complete netmask formatted as `base/bitmask` 161 | toString: -> 162 | return @base + "/" + @bitmask 163 | 164 | 165 | exports.ip2long = ip2long 166 | exports.long2ip = long2ip 167 | exports.Netmask = Netmask 168 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Olivier Poitrey ", 3 | "name": "netmask", 4 | "description": "Parse and lookup IP network blocks", 5 | "version": "2.0.2", 6 | "homepage": "https://github.com/rs/node-netmask", 7 | "bugs": "https://github.com/rs/node-netmask/issues", 8 | "license": "MIT", 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/rs/node-netmask.git" 12 | }, 13 | "keywords": [ 14 | "net", 15 | "mask", 16 | "ip", 17 | "network", 18 | "cidr", 19 | "netmask", 20 | "subnet", 21 | "ipcalc" 22 | ], 23 | "main": "./lib/netmask", 24 | "scripts": { 25 | "prepublish": "coffee -c lib/*.coffee", 26 | "test": "coffee -c lib/*.coffee && vows --spec test/* && mocha tests/*" 27 | }, 28 | "engines": { 29 | "node": ">= 0.4.0" 30 | }, 31 | "devDependencies": { 32 | "coffee-script": ">=1.2.0", 33 | "mocha": "^10.2.0", 34 | "vows": "*" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/badnets.coffee: -------------------------------------------------------------------------------- 1 | vows = require 'vows' 2 | assert = require 'assert' 3 | Netmask = require('../lib/netmask').Netmask 4 | 5 | shouldFailWithError = (msg) -> 6 | context = 7 | topic: -> 8 | try 9 | return new Netmask(@context.name) 10 | catch e 11 | return e 12 | 'should fail': (e) -> 13 | assert.ok isError(e), "is an Error object #{e}" 14 | "with error `#{msg}'": (e) -> 15 | assert.ok e.message?.toLowerCase().indexOf(msg.toLowerCase()) > -1, "'#{e.message}' =~ #{msg}" 16 | 17 | return context 18 | 19 | isError = (e) -> 20 | return typeof e == 'object' and Object.prototype.toString.call(e) == '[object Error]' 21 | 22 | vows.describe('IPs with bytes greater than 255') 23 | .addBatch 24 | '209.256.68.22/255.255.224.0': shouldFailWithError 'Invalid net' 25 | '209.180.68.22/256.255.224.0': shouldFailWithError 'Invalid mask' 26 | '209.500.70.33/19': shouldFailWithError 'Invalid net' 27 | '140.999.82': shouldFailWithError 'Invalid net' 28 | '899.174': shouldFailWithError 'Invalid net' 29 | '209.157.65536/19': shouldFailWithError 'Invalid net' 30 | '209.300.64.0.10': shouldFailWithError 'Invalid net' 31 | 'garbage': shouldFailWithError 'Invalid net' 32 | .export(module) 33 | 34 | 35 | vows.describe('Invalid IP format') 36 | .addBatch 37 | ' 1.2.3.4': shouldFailWithError 'Invalid net' 38 | ' 1.2.3.4': shouldFailWithError 'Invalid net' 39 | '1. 2.3.4': shouldFailWithError 'Invalid net' 40 | '1.2. 3.4': shouldFailWithError 'Invalid net' 41 | '1.2.3. 4': shouldFailWithError 'Invalid net' 42 | '1.2.3.4 ': shouldFailWithError 'Invalid net' 43 | '1 .2.3.4': shouldFailWithError 'Invalid net' 44 | '018.0.0.0': shouldFailWithError 'Invalid net' 45 | '08.0.0.0': shouldFailWithError 'Invalid net' 46 | '0xfg.0.0.0': shouldFailWithError 'Invalid net' 47 | .export(module) 48 | 49 | vows.describe('Ranges that are a power-of-two big, but are not legal blocks') 50 | .addBatch 51 | '218.0.0.0/221.255.255.255': shouldFailWithError 'Invalid mask' 52 | '218.0.0.4/218.0.0.11': shouldFailWithError 'Invalid mask' 53 | .export(module) 54 | -------------------------------------------------------------------------------- /test/netmasks.coffee: -------------------------------------------------------------------------------- 1 | vows = require 'vows' 2 | assert = require 'assert' 3 | util = require 'util' 4 | Netmask = require('../lib/netmask').Netmask 5 | 6 | fixtures = 7 | [ 8 | # addr mask base newmask bitmask 9 | ['209.157.68.22/255.255.224.0', null, '209.157.64.0', '255.255.224.0', 19] 10 | ['209.157.68.22', '255.255.224.0', '209.157.64.0', '255.255.224.0', 19] 11 | ['209.157.70.33/19', null, '209.157.64.0', '255.255.224.0', 19] 12 | ['209.157.70.33', null, '209.157.70.33', '255.255.255.255', 32] 13 | ['140.174.82', null, '140.174.0.82', '255.255.255.255', 32] 14 | ['140.174', null, '140.0.0.174', '255.255.255.255', 32] 15 | ['10', null, '0.0.0.10', '255.255.255.255', 32] 16 | ['10/8', null, '0.0.0.0', '255.0.0.0', 8] 17 | ['209.157.64/19', null, '209.157.0.0', '255.255.224.0', 19] 18 | ['216.140.48.16/32', null, '216.140.48.16', '255.255.255.255', 32] 19 | ['209.157/17', null, '209.0.0.0', '255.255.128.0', 17] 20 | ['0.0.0.0/0', null, '0.0.0.0', '0.0.0.0', 0] 21 | ['0xffffffff', null, '255.255.255.255', '255.255.255.255', 32] 22 | ['1.1', null, '1.0.0.1', '255.255.255.255', 32] 23 | ['1.0xffffff', null, '1.255.255.255', '255.255.255.255', 32] 24 | ['1.2.3', null, '1.2.0.3', '255.255.255.255', 32] 25 | ['1.2.0xffff', null, '1.2.255.255', '255.255.255.255', 32] 26 | ] 27 | 28 | contexts = [] 29 | 30 | fixtures.forEach (fixture) -> 31 | [addr, mask, base, newmask, bitmask] = fixture 32 | context = topic: -> new Netmask(addr, mask) 33 | context["base is `#{base}'"] = (block) -> assert.equal block.base, base 34 | context["mask is `#{newmask}'"] = (block) -> assert.equal block.mask, newmask 35 | context["bitmask is `#{bitmask}'"] = (block) -> assert.equal block.bitmask, bitmask 36 | context["toString is `#{base}/`#{bitmask}'"] = (block) -> assert.equal block.toString(), block.base + "/" + block.bitmask 37 | contexts["for #{addr}" + (if mask then " with #{mask}" else '')] = context 38 | 39 | vows.describe('Netmaks parsing').addBatch(contexts).export(module) 40 | 41 | vows.describe('Netmask contains IP') 42 | .addBatch 43 | 'block 192.168.1.0/24': 44 | topic: -> new Netmask('192.168.1.0/24') 45 | 'contains IP 192.168.1.0': (block) -> assert.ok block.contains('192.168.1.0') 46 | 'contains IP 192.168.1.255': (block) -> assert.ok block.contains('192.168.1.255') 47 | 'contains IP 192.168.1.63': (block) -> assert.ok block.contains('192.168.1.63') 48 | 'does not contain IP 192.168.0.255': (block) -> assert.ok not block.contains('192.168.0.255') 49 | 'does not contain IP 192.168.2.0': (block) -> assert.ok not block.contains('192.168.2.0') 50 | 'does not contain IP 10.168.2.0': (block) -> assert.ok not block.contains('10.168.2.0') 51 | 'does not contain IP 209.168.2.0': (block) -> assert.ok not block.contains('209.168.2.0') 52 | 'contains block 192.168.1.0/24': (block) -> assert.ok block.contains('192.168.1.0/24') 53 | 'contains block 192.168.1 (0.192.168.10)': (block) -> assert.ok not block.contains('192.168.1') 54 | 'does not contains block 192.168.1.128/25': (block) -> assert.ok block.contains('192.168.1.128/25') 55 | 'does not contain block 192.168.1.0/23': (block) -> assert.ok not block.contains('192.168.1.0/23') 56 | 'does not contain block 192.168.2.0/24': (block) -> assert.ok not block.contains('192.168.2.0/24') 57 | 'toString equals 192.168.1.0/24': (block) -> assert.equal block.toString(), '192.168.1.0/24' 58 | 'block 192.168.0.0/24': 59 | topic: -> new Netmask('192.168.0.0/24') 60 | 'does not contain block 192.168 (0.0.192.168)': (block) -> assert.ok not block.contains('192.168') 61 | 'does not contain block 192.168.0.0/16': (block) -> assert.ok not block.contains('192.168.0.0/16') 62 | 'block 31.0.0.0/8': 63 | topic: -> new Netmask('31.0.0.0/8') 64 | 'contains IP 31.5.5.5': (block) -> assert.ok block.contains('31.5.5.5') 65 | 'does not contain IP 031.5.5.5 (25.5.5.5)': (block) -> assert.ok not block.contains('031.5.5.5') 66 | 'does not contain IP 0x31.5.5.5 (49.5.5.5)': (block) -> assert.ok not block.contains('0x31.5.5.5') 67 | 'does not contain IP 0X31.5.5.5 (49.5.5.5)': (block) -> assert.ok not block.contains('0X31.5.5.5') 68 | 'block 127.0.0.0/8': 69 | topic: -> new Netmask('127.0.0.0/8') 70 | 'contains IP 127.0.0.2': (block) -> assert.ok block.contains('127.0.0.2') 71 | 'contains IP 0177.0.0.2 (127.0.0.2)': (block) -> assert.ok block.contains('0177.0.0.2') 72 | 'contains IP 0x7f.0.0.2 (127.0.0.2)': (block) -> assert.ok block.contains('0x7f.0.0.2') 73 | 'does not contains IP 127 (0.0.0.127)': (block) -> assert.ok not block.contains('127') 74 | 'does not contains IP 0177 (0.0.0.127)': (block) -> assert.ok not block.contains('0177') 75 | 'block 0.0.0.0/24': 76 | topic: -> new Netmask('0.0.0.0/0') 77 | 'contains IP 0.0.0.0': (block) -> assert.ok block.contains('0.0.0.0') 78 | 'contains IP 0': (block) -> assert.ok block.contains('0') 79 | 'contains IP 10 (0.0.0.10)': (block) -> assert.ok block.contains('10') 80 | 'contains IP 010 (0.0.0.8)': (block) -> assert.ok block.contains('010') 81 | 'contains IP 0x10 (0.0.0.16)': (block) -> assert.ok block.contains('0x10') 82 | 83 | .export(module) 84 | 85 | vows.describe('Netmask forEach') 86 | .addBatch 87 | 'block 192.168.1.0/24': 88 | topic: -> new Netmask('192.168.1.0/24') 89 | 'should loop through all ip addresses': (block) -> 90 | called = 0 91 | block.forEach (ip, long, index) -> 92 | called = index 93 | assert.equal (called + 1), 254 94 | 'block 192.168.1.0/23': 95 | topic: -> new Netmask('192.168.1.0/23') 96 | 'should loop through all ip addresses': (block) -> 97 | called = 0 98 | block.forEach (ip, long, index) -> 99 | called = index 100 | assert.equal (called + 1), 510 101 | .export(module) -------------------------------------------------------------------------------- /tests/netmask.js: -------------------------------------------------------------------------------- 1 | /* It is important to test our Javascript output as well as our coffeescript, 2 | * since code that is transpiled may be slightly different in effect from the 3 | * original. 4 | * 5 | * Run these tests (against lib/netmask.js, not lib/netmask.coffee directly) 6 | * using mocha, after re-generating lib/netmask.js including your changes: 7 | * 8 | * mocha tests/netmask.js 9 | */ 10 | 11 | const assert = require('assert'); 12 | const Netmask = require('../').Netmask; 13 | 14 | describe('Netmask', () => { 15 | describe('can build a block', () => { 16 | let block = new Netmask('10.1.2.0/24'); 17 | 18 | it('should contain a sub-block', () => { 19 | let block1 = new Netmask('10.1.2.10/29'); 20 | assert(block.contains(block1)); 21 | }); 22 | 23 | it('should contain another sub-block', () => { 24 | let block2 = new Netmask('10.1.2.10/31'); 25 | assert(block.contains(block2)); 26 | }); 27 | 28 | it('should contain a third sub-block', () => { 29 | let block3 = new Netmask('10.1.2.20/32'); 30 | assert(block.contains(block3)); 31 | }); 32 | }); 33 | 34 | describe('can describe a block', () => { 35 | let block = new Netmask('10.1.2.0/24'); 36 | 37 | it('should have a specific size', () => { 38 | assert.equal(block.size, 256); 39 | }); 40 | 41 | it('should have a specific base', () => { 42 | assert.equal(block.base, '10.1.2.0'); 43 | }); 44 | 45 | it('should have a specific mask', () => { 46 | assert.equal(block.mask, '255.255.255.0'); 47 | }); 48 | 49 | it('should have a specific host mask', () => { 50 | assert.equal(block.hostmask, '0.0.0.255'); 51 | }); 52 | 53 | it('should have a specific first ip', () => { 54 | assert.equal(block.first, '10.1.2.1'); 55 | }); 56 | 57 | it('should have a specific last ip', () => { 58 | assert.equal(block.last, '10.1.2.254'); 59 | }); 60 | 61 | it('should have a specific broadcast', () => { 62 | assert.equal(block.broadcast, '10.1.2.255'); 63 | }); 64 | }); 65 | 66 | describe('when presented with an octet which is not a number', () => { 67 | let block = new Netmask('192.168.0.0/29') 68 | 69 | it('should throw', () => { 70 | assert.throws(() => block.contains('192.168.~.4'), Error); 71 | }); 72 | }); 73 | 74 | describe('can handle hexadecimal, octal, & decimal octets in input IP', () => { 75 | let block1 = new Netmask('31.0.0.0/19'); 76 | let block2 = new Netmask('127.0.0.0/8'); 77 | let block3 = new Netmask('255.0.0.1/12'); 78 | let block4 = new Netmask('10.0.0.1/8'); 79 | let block5 = new Netmask('1.0.0.1/4'); 80 | 81 | describe('octal', () => { 82 | it('block 31.0.0.0/19 does not contain 031.0.5.5', () => { 83 | assert(!block1.contains('031.0.5.5')); 84 | }); 85 | it('block 127.0.0.0/8 contains 0177.0.0.2 (127.0.0.2)', () => { 86 | assert(block2.contains('0177.0.0.2')); 87 | }); 88 | it('block 255.0.0.1/12 does not contain 0255.0.0.2 (173.0.0.2)', () => { 89 | assert(!block3.contains('0255.0.0.2')); 90 | }); 91 | it('block 10.0.0.1/8 contains 012.0.0.255 (10.0.0.255)', () => { 92 | assert(block4.contains('012.0.0.255')); 93 | }); 94 | it('block 1.0.0.1/4 contains 01.02.03.04', () => { 95 | assert(block5.contains('01.02.03.04')); 96 | }); 97 | }); 98 | 99 | describe('hexadecimal', () => { 100 | it('block 31.0.0.0/19 does not contain 0x31.0.5.5', () => { 101 | assert(!block1.contains('0x31.0.5.5')); 102 | }); 103 | it('block 127.0.0.0/8 contains 0x7f.0.0.0x2 (127.0.0.2)', () => { 104 | assert(block2.contains('0x7f.0.0.0x2')); 105 | }); 106 | it('block 255.0.0.1/12 contains 0xff.0.0.2', () => { 107 | assert(block3.contains('0xff.0.0.2')); 108 | }); 109 | it('block 10.0.0.1/8 does not contain 0x10.0.0.255', () => { 110 | assert(!block4.contains('0x10.0.0.255')); 111 | }); 112 | it('block 1.0.0.1/4 contains 0x1.0x2.0x3.0x4', () => { 113 | assert(block5.contains('0x1.0x2.0x3.0x4')); 114 | }); 115 | }); 116 | 117 | describe('decimal', () => { 118 | it('block 31.0.0.0/19 contains 31.0.5.5', () => { 119 | assert(block1.contains('31.0.5.5')); 120 | }); 121 | it('block 127.0.0.0/8 does not contain 128.0.0.2', () =>{ 122 | assert(!block2.contains('128.0.0.2')); 123 | }); 124 | it('block 255.0.0.1/12 contains 255.0.0.2', () => { 125 | assert(block3.contains('255.0.0.2')); 126 | }); 127 | it('block 10.0.0.1/8 contains 10.0.0.255', () => { 128 | assert(block4.contains('10.0.0.255')); 129 | }); 130 | it('block 1.0.0.1/4 contains 1.2.3.4', () => { 131 | assert(block5.contains('1.2.3.4')); 132 | }); 133 | }); 134 | }); 135 | }); --------------------------------------------------------------------------------