diff --git a/lib/ip.js b/lib/ip.js index 26d0be3..63e6579 100644 --- a/lib/ip.js +++ b/lib/ip.js @@ -34,6 +34,7 @@ const binet = exports; */ const ZERO_IP = Buffer.from('00000000000000000000000000000000', 'hex'); +const ZERO_IPV4 = Buffer.from('00000000000000000000ffff00000000', 'hex'); const LOCAL_IP = Buffer.from('00000000000000000000000000000001', 'hex'); const RFC6052 = Buffer.from('0064ff9b0000000000000000', 'hex'); const RFC4862 = Buffer.from('fe80000000000000', 'hex'); @@ -1602,5 +1603,7 @@ binet.ip = binet; binet.types = types; binet.networks = networks; binet.ZERO_IP = ZERO_IP; +binet.ZERO_IPV6 = ZERO_IP; +binet.ZERO_IPV4 = ZERO_IPV4; binet.onion = onion; binet.inet = inet; diff --git a/test/binet-test.js b/test/binet-test.js index 6ef8415..f73ab44 100644 --- a/test/binet-test.js +++ b/test/binet-test.js @@ -1,24 +1,10 @@ -/** - * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License). - * Copyright (c) 2019, Mark Tyneway (MIT License). - * Copyright (c) 2019, Sean Kilgarriff (MIT License). - * - * Parts of this software are based on bitcoin/bitcoin: - * Copyright (c) 2009-2019, The Bitcoin Core Developers (MIT License). - * Copyright (c) 2009-2019, The Bitcoin Developers (MIT License). - * https://github.com/bitcoin/bitcoin - * - * Resources: - * https://github.com/bitcoin/bitcoin/blob/master/src/test/netbase_tests.cpp - */ - -/* eslint-env mocha */ -/* eslint prefer-arrow-callback: "off" */ - 'use strict'; const assert = require('assert'); -const binet = require('../lib/binet'); +const binet = require('../lib/binet'); +const vectors = require('./data/vectors'); + +const allVectors = vectors.all; const { NONE, @@ -64,26 +50,19 @@ describe('binet', function() { } }); - it('should convert back and forth', () => { - const ip4 = '192.168.1.1'; - const ip6 = '2001:db8:85a3::8a2e:370:7334'; - - const raw4 = binet.decode(ip4); - const raw6 = binet.decode(ip6); - - assert.strictEqual(binet.encode(raw4), ip4); - assert.strictEqual(binet.encode(raw6), ip6); + it('should getNetwork', () => { + assert.equal(binet.getNetwork(binet.decode('127.0.0.1')), NONE); + assert.equal(binet.getNetwork(binet.decode('::1')), NONE); + assert.equal(binet.getNetwork(binet.decode('8.8.8.8')), INET4); + assert.equal(binet.getNetwork(binet.decode('8888::8888')), INET6); + assert.equal(binet.getNetwork(binet.decode('2001::')), TEREDO); + assert.equal(binet.getNetwork(binet.decode('FD87:D87E:EB43:edb1:8e4:3588:e546:35ca')), ONION); }); it('should return the correct property', () => { assert(binet.isIPv4(binet.decode('127.0.0.1'))); assert(binet.isIPv4(binet.decode('::FFFF:192.168.1.1'))); assert(binet.isIPv6(binet.decode('::1'))); - assert(binet.isRFC1918(binet.decode('10.0.0.1'))); - assert(binet.isRFC1918(binet.decode('192.168.1.1'))); - assert(binet.isRFC1918(binet.decode('172.31.255.255'))); - assert(binet.isRFC3849(binet.decode('2001:0DB8::'))); - assert(binet.isRFC3927(binet.decode('169.254.1.1'))); assert(binet.isRFC3964(binet.decode('2002::1'))); assert(binet.isRFC4193(binet.decode('FC00::'))); assert(binet.isRFC4843(binet.decode('2001:10::'))); @@ -93,13 +72,6 @@ describe('binet', function() { binet.isOnion(binet.decode('FD87:D87E:EB43:edb1:8e4:3588:e546:35ca')) ); - // isRFC2544 should return true for: - // - IPv4 inter-network communications (198.18.0.0/15) - assert(binet.isRFC2544(binet.decode('198.18.0.0'))); - assert(binet.isRFC2544(binet.decode('198.19.255.255'))); - assert(!binet.isRFC2544(binet.decode('198.17.255.255'))); - assert(!binet.isRFC2544(binet.decode('198.20.5.255'))); - // isLocal should return true for: // - IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8) // - IPv6 loopback (::1/128) @@ -128,12 +100,223 @@ describe('binet', function() { assert(binet.isValid(binet.decode('127.0.0.1'))); }); - it('should getNetwork', () => { - assert.equal(binet.getNetwork(binet.decode('127.0.0.1')), NONE); - assert.equal(binet.getNetwork(binet.decode('::1')), NONE); - assert.equal(binet.getNetwork(binet.decode('8.8.8.8')), INET4); - assert.equal(binet.getNetwork(binet.decode('8888::8888')), INET6); - assert.equal(binet.getNetwork(binet.decode('2001::')), TEREDO); - assert.equal(binet.getNetwork(binet.decode('FD87:D87E:EB43:edb1:8e4:3588:e546:35ca')), ONION); + it('should convert back and forth', () => { + for (const v of allVectors) { + const norm = binet.normalize(v); + const raw = binet.decode(v); + const encoded = binet.encode(raw); + + assert.strictEqual(encoded, norm); + } + }); + + describe('isNull', function() { + const notNull = subtract(allVectors, vectors.NULL); + + it('should determine null IPs', () => { + for (const v of vectors.NULL) { + const decoded = binet.decode(v); + + assert.strictEqual(binet.isNull(decoded), true, + `${v} is null.`); + } + }); + + it('should determine not-null IPs', () => { + for (const v of notNull) { + const decoded = binet.decode(v); + + assert.strictEqual(binet.isNull(decoded), false, + `${v} is not null.`); + } + }); + }); + + describe('isBroadcast', function() { + const notBroadcast = subtract(allVectors, vectors.BROADCAST); + + it('should determine broadcast IPs', () => { + for (const v of vectors.BROADCAST) { + const decoded = binet.decode(v); + + assert.strictEqual(binet.isBroadcast(decoded), true, + `${v} is broadcast.`); + } + }); + + it('should determine non-broadcast IPs', () => { + for (const v of notBroadcast) { + const decoded = binet.decode(v); + + assert.strictEqual(binet.isBroadcast(decoded), false, + `${v} is not a broadcast.`); + } + }); + }); + + describe('isRFC1918', function() { + const notRFC1918 = subtract(allVectors, vectors.RFC1918); + + it('should determine RFC1918 IPs', () => { + for (const v of vectors.RFC1918) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC1918(decoded), true, + `${v} is RFC1918.`); + } + }); + + it('should determine non-RFC1918 IPs', () => { + for (const v of notRFC1918) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC1918(decoded), false, + `${v} is not RFC1918.`); + } + }); + }); + + describe('isRFC2544', function() { + const notRFC2544 = subtract(allVectors, vectors.RFC2544); + + it('should determine RFC2544 IPs', () => { + for (const v of vectors.RFC2544) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC2544(decoded), true, + `${v} is RFC2544.`); + } + }); + + it('should determine non-RFC2544 IPs', () => { + for (const v of notRFC2544) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC2544(decoded), false, + `${v} is not RFC2544.`); + } + }); + }); + + describe('isRFC3927', function() { + const notRFC3927 = subtract(allVectors, vectors.RFC3927); + + it('should determine RFC3927 IPs', () => { + for (const v of vectors.RFC3927) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC3927(decoded), true, + `${v} is RFC3927.`); + } + }); + + it('should determine non-RFC3927 IPs', () => { + for (const v of notRFC3927) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC3927(decoded), false, + `${v} is not RFC3927.`); + } + }); + }); + + describe('isRFC6598', function() { + const notRFC6598 = subtract(allVectors, vectors.RFC6598); + + it('should determine RFC6598 IPs', () => { + for (const v of vectors.RFC6598) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC6598(decoded), true, + `${v} is RFC6598.`); + } + }); + + it('should determine non-RFC6598 IPs', () => { + for (const v of notRFC6598) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC6598(decoded), false, + `${v} is not RFC6598.`); + } + }); + }); + + describe('isRFC5737', function() { + const notRFC5737 = subtract(allVectors, vectors.RFC5737); + + it('should determine RFC5737 IPs', () => { + for (const v of vectors.RFC5737) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC5737(decoded), true, + `${v} is RFC5737.`); + } + }); + + it('should determine non-RFC5737 IPs', () => { + for (const v of notRFC5737) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC5737(decoded), false, + `${v} is not RFC5737.`); + } + }); + }); + + describe('isValid', function() { + const invalidVectors = [ + ...vectors.SHIFTED, + ...vectors.NULL, + ...vectors.BROADCAST, + ...vectors.RFC3849 + ]; + + const validVectors = subtract(allVectors, invalidVectors); + + it('should validate valid IPs', () => { + for (const v of validVectors) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isValid(decoded), true, + `${v} is valid.`); + } + }); + + it('should validate invalid IPs', () => { + for (const v of invalidVectors) { + const decoded = binet.decode(v); + + assert.strictEqual(binet.isValid(decoded), false, + `${v} is invalid.`); + } + }); + }); + + describe('isRFC3849', function() { + const notRFC3849 = subtract(allVectors, vectors.RFC3849); + + it('should determine RFC3849 IPs', () => { + for (const v of vectors.RFC3849) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC3849(decoded), true, + `${v} is valid RFC3849.`); + } + }); + + it('should determine non-RFC3849 IPs', () => { + for (const v of notRFC3849) { + const decoded = binet.decode(v); + assert.strictEqual(binet.isRFC3849(decoded), false, + `${v} is not RFC3849.`); + } + }); }); }); + +function subtract(va, vb) { + const sa = new Set(va); + + for (const vector of vb) + sa.delete(vector); + + return Array.from(sa); +} + +function add(va, vb) { + const sa = new Set(va); + + for (const vector of vb) + sa.add(vector); + + return Array.from(sa); +} diff --git a/test/data/vectors.js b/test/data/vectors.js new file mode 100644 index 0000000..96be3ad --- /dev/null +++ b/test/data/vectors.js @@ -0,0 +1,120 @@ +'use strict'; + +const vectors = exports; + +// Invalid shifted. +vectors.SHIFTED = [ + '::ff:ff00:0:0:0', + '::ff:ff00:0:0:1', + '::ff:ffff:ffff:ffff:ffff' +]; + +// Null +vectors.NULL = [ + '::', + '0.0.0.0' +]; + +// Broadcast +vectors.BROADCAST = [ + '255.255.255.255' +]; + +// RFC 1918 - Private Internets +// - 10/8 +// - 172.16/12 +// - 192.168/16 +vectors.RFC1918 = [ + '192.168.0.0', + '192.168.1.1', + '192.168.255.255', + '10.0.0.0', + '10.0.0.1', + '10.255.255.255', + '172.16.0.0', + '172.16.255.255', + '172.31.255.255' +]; + +// RFC 2544 - IPv4 inter-network communications +// - 198.18.0.0/15 +vectors.RFC2544 = [ + '198.18.0.0', + '198.18.255.255', + '198.19.0.0', + '198.19.255.255' +]; + +// RFC 3927 - Dynamic Configuration of IPv4 Link-Local Addresses +// - 169.254/16 +vectors.RFC3927 = [ + '169.254.0.0', + '169.254.1.1', + '169.254.255.255' +]; + +// RFC 6598 - IANA-Reserved IPv4 Prefix for Shared Address Space +// - 100.64.0.0/10 +vectors.RFC6598 = [ + '100.64.0.0', + '100.64.255.255', + '100.100.100.100', + '100.100.200.200', + '100.127.255.255' +]; + +// RFC 5737 - IPv4 Address Blocks Reserved for Documentation +// - 192.0.2.0/24 (TEST-NET-1) +// - 198.51.100.0/24 (TEST-NET-2) +// - 203.0.113.0/24 (TEST-NET-3) +vectors.RFC5737 = [ + '192.0.2.0', + '192.0.2.1', + '192.0.2.255', + '198.51.100.0', + '198.51.100.1', + '198.51.100.255', + '203.0.113.0', + '203.0.113.1', + '203.0.113.255' +]; + +// RFC3849 - IPv6 Reserved prefix +// 2001:DB8::/32 +vectors.RFC3849 = [ + '2001:0db8::', + '2001:db8::', + '2001:db8::1:1', + '2001:db8:85a3::8a2e:370:7334', + '2001:0db8:ffff:ffff:ffff:ffff:ffff:ffff' +]; + +// IPV4 +vectors.IPV4 = [ + '0.0.0.0', + '255.255.255.255', + ...vectors.RFC1918, + ...vectors.RFC2544, + ...vectors.RFC3927, + ...vectors.RFC6598, + ...vectors.RFC5737 +]; + +vectors.IPV6 = [ + '::', + ...vectors.RFC3849 +]; + +vectors.all = [ + ...vectors.SHIFTED, + ...vectors.NULL, + ...vectors.BROADCAST, + ...vectors.RFC1918, + ...vectors.RFC2544, + ...vectors.RFC3927, + ...vectors.RFC6598, + ...vectors.RFC5737, + ...vectors.RFC3849, + ...vectors.IPV4, + ...vectors.IPV6 +];