diff --git a/test/data/read-write-vectors.js b/test/data/other-vectors.js similarity index 50% rename from test/data/read-write-vectors.js rename to test/data/other-vectors.js index 882c41e..6abe049 100644 --- a/test/data/read-write-vectors.js +++ b/test/data/other-vectors.js @@ -1,10 +1,92 @@ 'use strict'; +const binet = require('../..'); + +const vectors = exports; + /* - * Some extra vectors for the options and etc. + * Map/Unmap */ -const vectors = exports; +vectors.MAP_VECTORS = [ + [binet.ZERO_IPV6, binet.ZERO_IPV6], + [Buffer.alloc(16, 1), Buffer.alloc(16, 1)], + [binet.ZERO_IPV4, binet.ZERO_IPV4], + [Buffer.alloc(4, 0), binet.ZERO_IPV4], + [ + Buffer.from('00000000000000000000fffffefefefe', 'hex'), + Buffer.from('00000000000000000000fffffefefefe', 'hex') + ], [ + Buffer.from('fefefefe', 'hex'), + Buffer.from('00000000000000000000fffffefefefe', 'hex') + ], [ + Buffer.from('ffffffff', 'hex'), + Buffer.from('00000000000000000000ffffffffffff', 'hex') + ], [ + // NOTE: Maybe it should fail if RAW is not mapped and is unmappable? + Buffer.from('ffffffffffffffffffffffffffffffff', 'hex'), + Buffer.from('ffffffffffffffffffffffffffffffff', 'hex') + ] +]; + +// these are mapped ips +vectors.UNMAP_VECTOR = [ + [Buffer.alloc(4, 0), Buffer.alloc(4, 0)], + [Buffer.alloc(4, 0xff), Buffer.alloc(4, 0xff)], + [binet.ZERO_IPV4, Buffer.alloc(4, 0)], + [ + Buffer.from('00000000000000000000ffffffffffff', 'hex'), + Buffer.from('ffffffff', 'hex') + ] +]; + +// isMappedString tests +vectors.IS_MAPPED_STRING = [ + ['127.0.0.1', false], + ['0.0.0.0', false], + ['255.255.255.255', false], + ['hello', false], + ['::', false], + ['::1', false], + ['::ffff:ffff', false], + ['::f:ffff:ffff', false], + ['::ff:ffff:ffff', false], + ['::fff:ffff:ffff', false], + ['::1:ffff:ffff:ffff', false], + ['1::ffff:ffff:ffff', false], + ['::ffff:ffff:ffff', true], + ['::ffff:0000:0000', true] +]; + +// These are not mapped. +vectors.UNMAPPED_VECTOR = [ + binet.ZERO_IPV6, + Buffer.from('000000000000000000000000ffffffff', 'hex'), + Buffer.from('00000000000000000000000fffffffff', 'hex'), + Buffer.from('0000000000000000000000ffffffffff', 'hex'), + Buffer.from('000000000000000000000fffffffffff', 'hex'), + Buffer.from('00000000000000000001ffffffffffff', 'hex'), + Buffer.from('10000000000000000000ffffffffffff', 'hex'), + Buffer.from('f0000000000000000000ffff00000000', 'hex'), + Buffer.from('ffffffffffffffffffffffff00000000', 'hex'), + Buffer.from('ffffffffffffffffffffffffffffffff', 'hex') +]; + +/* + * Small onion address set + */ +vectors.LEGACY_ONIONS = [ + '0000000000000000.onion', + 'aaaaaaaaaaaaaaaa.onion', + 'ffffffffffffffff.onion', + 'zzzzzzzzzzzzzzzz.onion', + 'abcdefghijklmnop.onion', + 'qrstuvwxyz234567.onion' +]; + +/* + * READ/WRITE Vectors + */ // NOTE: str needs to be normalized. vectors.READ = [ @@ -221,3 +303,45 @@ vectors.WRITE = [ ...vectors.WRITE_6, ...vectors.WRITE_ONION ]; + +/* + * toHost/fromHost tests + */ + +// host, port, key, returned val +vectors.TOHOST = [ + ['handshake', 0, null, 'handshake:0'], + ['handshake', 0xffff, null, 'handshake:65535'], + ['::1', 1000, null, '[::1]:1000'], + ['127.0.0.1', 1000, null, '127.0.0.1:1000'], + ['0.0.0.0', 1000, null, '0.0.0.0:1000'], + + // do we need more strict rules in toHost? + ['---', 1000, null, '---:1000'], + ['handshake', 1000, Buffer.alloc(33, 0), 'handshake:1000'], + ['handshake', 1000, Buffer.alloc(33, 0x11), + 'ceirceirceirceirceirceirceirceirceirceirceirceirceirc@handshake:1000'] +]; + +// host, port, key, thrown err message - null = assertion error. +vectors.TOHOST_ERR = [ + // bad hostnames + ['', 1000, null, 'Invalid host (zero length).'], + ['a'.repeat(255 + 1 + 5 + 1), 1000, null, 'Invalid host (too large).'], + ['[::]', 1000, null, 'Bad host.'], + ['hsd@handshake-org', 1000, null, 'Bad host.'], + ['handshake\n', 1000, null, 'Bad host.'], + ['handshake\u0019', 1000, null, 'Bad host.'], + ['handshake\u007f', 1000, null, 'Bad host.'], + ['not-an-ipv6:addr', 1000, null, 'Unexpected colon.'], + ['not-an-ipv6:1000', 1000, null, 'Unexpected colon.'], + + // bad ports + ['handshake', null, null, null], + ['handshake', -1, null, null], + ['handshake', 0xffff + 1, null, null], + + // bad keys + ['handshake', 100, 'not-a-key', null], + ['handshake', 100, Buffer.alloc(0), null] +]; diff --git a/test/ip-test.js b/test/ip-test.js index 4c0cc6d..df22bcf 100644 --- a/test/ip-test.js +++ b/test/ip-test.js @@ -5,7 +5,7 @@ const bufio = require('bufio'); const binet = require('../lib/binet'); const ipVectors = require('./data/ip-vectors'); -const readWriteVectors = require('./data/read-write-vectors'); +const otherVectors = require('./data/other-vectors'); const allVectors = ipVectors.ALL.reduce((p, c) => add(p, c)); @@ -56,7 +56,7 @@ describe('binet', function() { }); it('should read IP from a buffer', () => { - for (const {off, size, buf, str, err} of readWriteVectors.READ) { + for (const {off, size, buf, str, err} of otherVectors.READ) { let rerr = null; let rres = null; @@ -79,7 +79,7 @@ describe('binet', function() { }); it('should write IP string to a buffer', () => { - for (const {off, size, foff, buf, str, err} of readWriteVectors.WRITE) { + for (const {off, size, foff, buf, str, err} of otherVectors.WRITE) { let rerr = null; let roff = null; const dest = Buffer.alloc(buf.length); @@ -369,6 +369,190 @@ describe('binet', function() { }); } }); + + describe('Mappings', function() { + it('should map 4 byte ip to IPv6', () => { + for (const vector of otherVectors.MAP_VECTORS) { + const mapped = binet.map(vector[0]); + + assert.bufferEqual(mapped, vector[1]); + } + }); + + it('should fail mapping non-4 ip to IPv6', () => { + for (let i = 0; i < 20; i++) { + if (i === 4 || i === 16) + continue; + + assert.throws(() => { + binet.map(Buffer.alloc(i)); + }, { + message: 'Not an IPv4 address.' + }); + } + }); + + it('should unmap IPv6 mapped to IPv4', () => { + for (const vector of otherVectors.UNMAP_VECTOR) { + const unmapped = binet.unmap(vector[0]); + + assert.bufferEqual(unmapped, vector[1]); + } + }); + + it('should fail unmapping non-mapped IPs', () => { + // incorrect sizes + for (let i = 0; i < 20; i++) { + if (i === 4 || i === 16) + continue; + + assert.throws(() => { + binet.unmap(Buffer.alloc(i)); + }, { + message: 'Not an IPv6 address.' + }); + } + }); + + it('should fail unmapping non-mapped IPv6s', () => { + for (const vector of otherVectors.UNMAPPED_VECTOR) { + assert.throws(() => { + binet.unmap(vector); + }, { + message: 'Not an IPv4 mapped address.' + }); + } + }); + + it('should check mapped string', () => { + for (const vector of otherVectors.IS_MAPPED_STRING) { + assert.strictEqual(binet.isMappedString(vector[0]), vector[1]); + } + }); + }); + + describe('String type', function() { + const {types} = binet; + + const none = [ + '', + '1'.repeat(255 + 1), + '1'.repeat(100) + ]; + + const all = add( + ipVectors.IPV4, + ipVectors.IPV6, + ipVectors.ONION, + otherVectors.LEGACY_ONIONS, + none + ); + + it('should return none', () => { + assert.strictEqual(binet.getTypeString(''), types.NONE); + assert.strictEqual(binet.getTypeString('1'.repeat(255 + 1)), types.NONE); + assert.strictEqual(binet.getTypeString('1'.repeat(100)), types.NONE); + }); + + it('should return IPv4', () => { + for (const vector of ipVectors.IPV4) + assert.strictEqual(binet.getTypeString(vector), types.INET4); + }); + + it('should return IPv6', () => { + for (const vector of ipVectors.IPV6) + assert.strictEqual(binet.getTypeString(vector), types.INET6); + }); + + it('should return ONION', () => { + for (const vector of otherVectors.LEGACY_ONIONS) + assert.strictEqual(binet.getTypeString(vector), types.ONION); + }); + + it('should check isIPv4String', () => { + const not = sub(all, ipVectors.IPV4); + + for (const vector of ipVectors.IPV4) + assert.strictEqual(binet.isIPv4String(vector), true); + + for (const vector of not) + assert.strictEqual(binet.isIPv4String(vector), false); + }); + + it('should check isIPv6String', () => { + const not = sub(all, ipVectors.IPV6); + + for (const vector of ipVectors.IPV6) + assert.strictEqual(binet.isIPv6String(vector), true); + + for (const vector of not) + assert.strictEqual(binet.isIPv6String(vector), false); + }); + + it('should check isOnionString', () => { + const onions = add(otherVectors.LEGACY_ONIONS, ipVectors.ONION); + const not = sub(all, onions); + + for (const vector of onions) + assert.strictEqual(binet.isOnionString(vector), true); + + for (const vector of not) + assert.strictEqual(binet.isOnionString(vector), false); + }); + + it('should check isUnknownString', () => { + const not = sub(all, none); + + for (const vector of none) + assert.strictEqual(binet.isUnknownString(vector), true); + + for (const vector of not) + assert.strictEqual(binet.isUnknownString(vector), false); + }); + + it('should check isIPString', () => { + const notIPString = add(ipVectors.NONE, otherVectors.LEGACY_ONIONS); + + // check inet4 + for (const vector of ipVectors.IPV4) + assert.strictEqual(binet.isIPString(vector), types.INET4); + + for (const vector of ipVectors.IPV6) + assert.strictEqual(binet.isIPString(vector), types.INET6); + + for (const vector of notIPString) + assert.strictEqual(binet.isIPString(vector), types.NONE); + }); + }); + + describe('Host string', function() { + it('should convert toHost string', () => { + for (const vector of otherVectors.TOHOST) { + const host = binet.toHost(vector[0], vector[1], vector[2]); + + assert.strictEqual(host, vector[3]); + } + }); + + it('should fail toHost', () => { + for (const vector of otherVectors.TOHOST_ERR) { + let err; + try { + binet.toHost(vector[0], vector[1], vector[2]); + } catch (e) { + err = e; + } + + assert(err, `Error not found: ${err}`); + + if (!vector[3]) + assert.strictEqual(err instanceof assert.AssertionError, true, err); + + if (vector[3]) + assert.strictEqual(err.message, vector[3]); + } + }); + }); }); function sub(va, ...args) {