From 1b089e923b8a3320366bb076a6d669d09179c758 Mon Sep 17 00:00:00 2001 From: Cayman Date: Tue, 16 Jan 2024 12:39:20 -0500 Subject: [PATCH 1/4] feat: add isPrivateIp to @libp2p/utils --- packages/utils/package.json | 6 + packages/utils/src/private-ip.ts | 61 ++++++++ packages/utils/test/private-ip.spec.ts | 203 +++++++++++++++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 packages/utils/src/private-ip.ts create mode 100644 packages/utils/test/private-ip.spec.ts diff --git a/packages/utils/package.json b/packages/utils/package.json index 35e9a517e4..34ae9e103d 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -80,6 +80,10 @@ "types": "./dist/src/peer-queue.d.ts", "import": "./dist/src/peer-queue.js" }, + "./private-ip": { + "types": "./dist/src/private-ip.d.ts", + "import": "./dist/src/private-ip.js" + }, "./queue": { "types": "./dist/src/queue/index.d.ts", "import": "./dist/src/queue/index.js" @@ -132,6 +136,7 @@ "is-loopback-addr": "^2.0.1", "it-pushable": "^3.2.3", "it-stream-types": "^2.0.1", + "netmask": "^2.0.2", "p-defer": "^4.0.0", "private-ip": "^3.0.1", "race-event": "^1.1.0", @@ -140,6 +145,7 @@ }, "devDependencies": { "@libp2p/peer-id-factory": "^4.0.5", + "@types/netmask": "^2.0.5", "aegir": "^42.0.0", "delay": "^6.0.0", "it-all": "^3.0.3", diff --git a/packages/utils/src/private-ip.ts b/packages/utils/src/private-ip.ts new file mode 100644 index 0000000000..0957691c2d --- /dev/null +++ b/packages/utils/src/private-ip.ts @@ -0,0 +1,61 @@ +import { isIPv4, isIPv6 } from '@chainsafe/is-ip' +import { Netmask } from 'netmask' + +const PRIVATE_IP_RANGES = [ + '0.0.0.0/8', + '10.0.0.0/8', + '100.64.0.0/10', + '127.0.0.0/8', + '169.254.0.0/16', + '172.16.0.0/12', + '192.0.0.0/24', + '192.0.0.0/29', + '192.0.0.8/32', + '192.0.0.9/32', + '192.0.0.10/32', + '192.0.0.170/32', + '192.0.0.171/32', + '192.0.2.0/24', + '192.31.196.0/24', + '192.52.193.0/24', + '192.88.99.0/24', + '192.168.0.0/16', + '192.175.48.0/24', + '198.18.0.0/15', + '198.51.100.0/24', + '203.0.113.0/24', + '240.0.0.0/4', + '255.255.255.255/32' +] + +const NETMASK_RANGES = PRIVATE_IP_RANGES.map(ipRange => new Netmask(ipRange)) + +function ipv4Check (ipAddr: string): boolean { + for (const r of NETMASK_RANGES) { + if (r.contains(ipAddr) === true) return true + } + + return false +} + +function ipv6Check (ipAddr: string): boolean { + return /^::$/.test(ipAddr) || + /^::1$/.test(ipAddr) || + /^::f{4}:([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ipAddr) || + /^::f{4}:0.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ipAddr) || + /^64:ff9b::([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ipAddr) || + /^100::([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4})$/.test(ipAddr) || + /^2001::([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4})$/.test(ipAddr) || + /^2001:2[0-9a-fA-F]:([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4})$/.test(ipAddr) || + /^2001:db8:([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4})$/.test(ipAddr) || + /^2002:([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4}):?([0-9a-fA-F]{0,4})$/.test(ipAddr) || + /^f[c-d]([0-9a-fA-F]{2,2}):/i.test(ipAddr) || + /^fe[8-9a-bA-B][0-9a-fA-F]:/i.test(ipAddr) || + /^ff([0-9a-fA-F]{2,2}):/i.test(ipAddr) +} + +export function isPrivateIp (ip: string): boolean | undefined { + if (isIPv4(ip)) return ipv4Check(ip) + else if (isIPv6(ip)) return ipv6Check(ip) + else return undefined +} diff --git a/packages/utils/test/private-ip.spec.ts b/packages/utils/test/private-ip.spec.ts new file mode 100644 index 0000000000..66acade7fa --- /dev/null +++ b/packages/utils/test/private-ip.spec.ts @@ -0,0 +1,203 @@ +/* eslint-env mocha */ + +import { expect } from 'aegir/chai' +import { isPrivateIp } from '../src/private-ip.js' + +describe('private-ip', function () { + const pubIps = [ + '44.37.112.180', + '46.192.247.73', + '71.12.102.112', + '101.0.26.90', + '111.211.73.40', + '156.238.194.84', + '164.101.185.82', + '223.231.138.242', + '226.84.185.150', + '227.202.96.196', + '::1fff:0.0.0.0', + '::1fff:10.0.0.0', + '::1fff:0:0.0.0.0', + '::1fff:0:10.0.0.0', + '2001:2:ffff:ffff:ffff:ffff:ffff:ffff', + '64:ff9a::0.0.0.0', + '64:ff9a::255.255.255.255', + '99::', + '99::ffff:ffff:ffff:ffff', + '101::', + '101::ffff:ffff:ffff:ffff', + '2000::', + '2000::ffff:ffff:ffff:ffff:ffff:ffff', + '2001:10::', + '2001:1f:ffff:ffff:ffff:ffff:ffff:ffff', + '2001:db7::', + '2001:db7:ffff:ffff:ffff:ffff:ffff:ffff', + '2001:db9::', + 'fb00::', + 'fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', + 'fec0::' + ] + + const privIps = [ + '0.0.0.0', + '0000.0000.0000.0000', + '0000.0000', + '0.0.0.1', + '0.0.0.7', + '0.0.0.255', + '0.0.255.255', + '0.1.255.255', + '0.15.255.255', + '0.63.255.255', + '0.255.255.254', + '0.255.255.255', + '10.0.0.0', + '10.0.0.1', + '10.0.0.01', + '10.0.0.001', + '10.255.255.254', + '10.255.255.255', + '100.64.0.0', + '100.64.0.1', + '100.127.255.254', + '100.127.255.255', + '127.0.0.0', + '127.0.0.1', + '127.0.0.01', + '127.1', + '127.0.1', + '127.000.000.1', + '127.255.255.254', + '127.255.255.255', + '169.254.0.0', + '169.254.0.1', + '169.254.255.254', + '169.254.255.255', + '172.16.0.0', + '172.16.0.1', + '172.16.0.001', + '172.31.255.254', + '172.31.255.255', + '192.0.0.0', + '192.0.0.1', + '192.0.0.6', + '192.0.0.7', + '192.0.0.8', + '192.0.0.9', + '192.0.0.10', + '192.0.0.11', + '192.0.0.170', + '192.0.0.171', + '192.0.0.254', + '192.0.0.255', + '192.0.2.0', + '192.0.2.1', + '192.0.2.254', + '192.0.2.255', + '192.31.196.0', + '192.31.196.1', + '192.31.196.254', + '192.31.196.255', + '192.52.193.0', + '192.52.193.1', + '192.52.193.254', + '192.52.193.255', + '192.88.99.0', + '192.88.99.1', + '192.88.99.254', + '192.88.99.255', + '192.168.0.0', + '192.168.0.1', + '192.168.255.254', + '192.168.255.255', + '192.175.48.0', + '192.175.48.1', + '192.175.48.254', + '192.175.48.255', + '198.18.0.0', + '198.18.0.1', + '198.19.255.254', + '198.19.255.255', + '198.51.100.0', + '198.51.100.1', + '198.51.100.254', + '198.51.100.255', + '203.0.113.0', + '203.0.113.1', + '203.0.113.254', + '203.0.113.255', + '240.0.0.0', + '240.0.0.1', + '255.0.0.0', + '255.192.0.0', + '255.240.0.0', + '255.254.0.0', + '255.255.0.0', + '255.255.255.0', + '255.255.255.248', + '255.255.255.254', + '255.255.255.255', + '::', + '::1', + '::ffff:0.0.0.0', + '::ffff:255.255.255.255', + '64:ff9b::0.0.0.0', + '64:ff9b::16.10.11.1', + '64:ff9b::255.255.255.255', + '100::', + '100::0:0:0:0', + '100::1:eabc:0:2', + '100::ffff:ffff:ffff:ffff', + '2001::', + '2001::a:b:c', + '2001::ffff:ffff:ffff:ffff:ffff:ffff', + '2001:20::', + '2001:20::a:b:c', + '2001:2f::a:b:c', + '2001:2f:ffff:ffff:ffff:ffff:ffff:ffff', + '2001:db8::', + '2001:db8::1', + '2001:db8:abc::1', + '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff', + '2002::', + '2002::1', + '2002::abc:1', + '2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff', + 'fe80::', + 'fe80::1', + 'fe80::abc:1', + 'febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff', + 'fc00::', + 'fc00::1', + 'fc00::abc:1', + 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', + 'ff00::', + 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', + '2130706433', + '0x7f000001' + ] + + const invalidIps = [ + '100::ffff::', + '::ffff:0.0.255.255.255', + '::ffff:0.255.255.255.255' + ] + + it('identifies public ips', function () { + for (const ip of pubIps) { + expect(isPrivateIp(ip)).to.be.false(`isPrivateIp('${ip}') should be false`) + } + }) + + it('identifies private ips', function () { + for (const ip of privIps) { + expect(isPrivateIp(ip)).to.be.true(`isPrivateIp('${ip}') should be true`) + } + }) + + it('identifies invalid ips', function () { + for (const ip of invalidIps) { + expect(isPrivateIp(ip)).to.be.undefined(`isPrivateIp('${ip}') should be undefined`) + } + }) +}) From 11225fe7368b73cdfcd2bbbbc61d3cc1624a793d Mon Sep 17 00:00:00 2001 From: Cayman Date: Tue, 16 Jan 2024 12:39:42 -0500 Subject: [PATCH 2/4] feat: use @libp2p/utils/private-ip instead of private-ip --- packages/kad-dht/package.json | 1 - packages/kad-dht/src/utils.ts | 2 +- packages/libp2p/package.json | 1 - packages/libp2p/src/config/connection-gater.browser.ts | 4 ++-- packages/protocol-autonat/package.json | 2 +- packages/protocol-autonat/src/autonat.ts | 2 +- packages/protocol-dcutr/package.json | 2 +- packages/protocol-dcutr/src/utils.ts | 4 ++-- packages/upnp-nat/package.json | 1 - packages/upnp-nat/src/upnp-nat.ts | 2 +- packages/utils/package.json | 1 - packages/utils/src/multiaddr/is-private.ts | 4 ++-- 12 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/kad-dht/package.json b/packages/kad-dht/package.json index 2d084e2237..45c563de29 100644 --- a/packages/kad-dht/package.json +++ b/packages/kad-dht/package.json @@ -81,7 +81,6 @@ "p-defer": "^4.0.0", "p-event": "^6.0.0", "p-queue": "^8.0.0", - "private-ip": "^3.0.1", "progress-events": "^1.0.0", "protons-runtime": "^5.0.0", "race-signal": "^1.0.2", diff --git a/packages/kad-dht/src/utils.ts b/packages/kad-dht/src/utils.ts index 6bd44b2c5e..b50e0433d1 100644 --- a/packages/kad-dht/src/utils.ts +++ b/packages/kad-dht/src/utils.ts @@ -1,7 +1,7 @@ import { peerIdFromBytes } from '@libp2p/peer-id' +import { isPrivateIp } from '@libp2p/utils/private-ip' import { Key } from 'interface-datastore/key' import { sha256 } from 'multiformats/hashes/sha2' -import isPrivateIp from 'private-ip' import { concat as uint8ArrayConcat } from 'uint8arrays/concat' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' diff --git a/packages/libp2p/package.json b/packages/libp2p/package.json index e7465b4a1a..e75eefbc03 100644 --- a/packages/libp2p/package.json +++ b/packages/libp2p/package.json @@ -102,7 +102,6 @@ "it-parallel": "^3.0.6", "merge-options": "^3.0.4", "multiformats": "^13.0.0", - "private-ip": "^3.0.1", "uint8arrays": "^5.0.0" }, "devDependencies": { diff --git a/packages/libp2p/src/config/connection-gater.browser.ts b/packages/libp2p/src/config/connection-gater.browser.ts index 5593c8dbe9..1196b2b2bc 100644 --- a/packages/libp2p/src/config/connection-gater.browser.ts +++ b/packages/libp2p/src/config/connection-gater.browser.ts @@ -1,4 +1,4 @@ -import isPrivate from 'private-ip' +import { isPrivateIp } from '@libp2p/utils/private-ip' import type { ConnectionGater } from '@libp2p/interface' import type { Multiaddr } from '@multiformats/multiaddr' @@ -14,7 +14,7 @@ export function connectionGater (gater: ConnectionGater = {}): ConnectionGater { const tuples = multiaddr.stringTuples() if (tuples[0][0] === 4 || tuples[0][0] === 41) { - return Boolean(isPrivate(`${tuples[0][1]}`)) + return Boolean(isPrivateIp(`${tuples[0][1]}`)) } return false diff --git a/packages/protocol-autonat/package.json b/packages/protocol-autonat/package.json index dbc54c239a..7ee9990886 100644 --- a/packages/protocol-autonat/package.json +++ b/packages/protocol-autonat/package.json @@ -55,13 +55,13 @@ "@libp2p/interface-internal": "^1.0.7", "@libp2p/peer-id": "^4.0.5", "@libp2p/peer-id-factory": "^4.0.5", + "@libp2p/utils": "^5.2.2", "@multiformats/multiaddr": "^12.1.10", "it-first": "^3.0.3", "it-length-prefixed": "^9.0.3", "it-map": "^3.0.4", "it-parallel": "^3.0.6", "it-pipe": "^3.0.1", - "private-ip": "^3.0.1", "protons-runtime": "^5.0.0", "uint8arraylist": "^2.4.7" }, diff --git a/packages/protocol-autonat/src/autonat.ts b/packages/protocol-autonat/src/autonat.ts index 12d4b8a112..b55155fa6f 100644 --- a/packages/protocol-autonat/src/autonat.ts +++ b/packages/protocol-autonat/src/autonat.ts @@ -1,13 +1,13 @@ import { CodeError, ERR_TIMEOUT, setMaxListeners } from '@libp2p/interface' import { peerIdFromBytes } from '@libp2p/peer-id' import { createEd25519PeerId } from '@libp2p/peer-id-factory' +import { isPrivateIp } from '@libp2p/utils/private-ip' import { multiaddr, protocols } from '@multiformats/multiaddr' import first from 'it-first' import * as lp from 'it-length-prefixed' import map from 'it-map' import parallel from 'it-parallel' import { pipe } from 'it-pipe' -import isPrivateIp from 'private-ip' import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, diff --git a/packages/protocol-dcutr/package.json b/packages/protocol-dcutr/package.json index 66a002e1dc..e693d24382 100644 --- a/packages/protocol-dcutr/package.json +++ b/packages/protocol-dcutr/package.json @@ -53,11 +53,11 @@ "dependencies": { "@libp2p/interface": "^1.1.2", "@libp2p/interface-internal": "^1.0.7", + "@libp2p/utils": "^5.2.2", "@multiformats/multiaddr": "^12.1.10", "@multiformats/multiaddr-matcher": "^1.1.0", "delay": "^6.0.0", "it-protobuf-stream": "^1.1.1", - "private-ip": "^3.0.1", "protons-runtime": "^5.0.0", "uint8arraylist": "^2.4.7" }, diff --git a/packages/protocol-dcutr/src/utils.ts b/packages/protocol-dcutr/src/utils.ts index 900d0b3573..4cf30fbc83 100644 --- a/packages/protocol-dcutr/src/utils.ts +++ b/packages/protocol-dcutr/src/utils.ts @@ -1,6 +1,6 @@ +import { isPrivateIp } from '@libp2p/utils/private-ip' import { type Multiaddr } from '@multiformats/multiaddr' import { Circuit, IP, DNS } from '@multiformats/multiaddr-matcher' -import isPrivate from 'private-ip' import type { TransportManager } from '@libp2p/interface-internal' /** @@ -29,5 +29,5 @@ export function isPublicAndDialable (ma: Multiaddr, transportManager: TransportM return false } - return isPrivate(ma.toOptions().host) === false + return isPrivateIp(ma.toOptions().host) === false } diff --git a/packages/upnp-nat/package.json b/packages/upnp-nat/package.json index 5743b5f7e8..79229f4c81 100644 --- a/packages/upnp-nat/package.json +++ b/packages/upnp-nat/package.json @@ -54,7 +54,6 @@ "@libp2p/interface-internal": "^1.0.7", "@libp2p/utils": "^5.2.2", "@multiformats/multiaddr": "^12.1.10", - "private-ip": "^3.0.1", "wherearewe": "^2.0.1" }, "devDependencies": { diff --git a/packages/upnp-nat/src/upnp-nat.ts b/packages/upnp-nat/src/upnp-nat.ts index 9c40fbb531..03bcffce3d 100644 --- a/packages/upnp-nat/src/upnp-nat.ts +++ b/packages/upnp-nat/src/upnp-nat.ts @@ -1,8 +1,8 @@ import { upnpNat, type NatAPI } from '@achingbrain/nat-port-mapper' import { CodeError, ERR_INVALID_PARAMETERS } from '@libp2p/interface' import { isLoopback } from '@libp2p/utils/multiaddr/is-loopback' +import { isPrivateIp } from '@libp2p/utils/private-ip' import { fromNodeAddress } from '@multiformats/multiaddr' -import isPrivateIp from 'private-ip' import { isBrowser } from 'wherearewe' import type { UPnPNATComponents, UPnPNATInit } from './index.js' import type { Logger, Startable } from '@libp2p/interface' diff --git a/packages/utils/package.json b/packages/utils/package.json index 34ae9e103d..ef1f20e9fe 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -138,7 +138,6 @@ "it-stream-types": "^2.0.1", "netmask": "^2.0.2", "p-defer": "^4.0.0", - "private-ip": "^3.0.1", "race-event": "^1.1.0", "race-signal": "^1.0.2", "uint8arraylist": "^2.4.7" diff --git a/packages/utils/src/multiaddr/is-private.ts b/packages/utils/src/multiaddr/is-private.ts index ab563e032c..a2667082ec 100644 --- a/packages/utils/src/multiaddr/is-private.ts +++ b/packages/utils/src/multiaddr/is-private.ts @@ -1,4 +1,4 @@ -import isIpPrivate from 'private-ip' +import { isPrivateIp } from '../private-ip.js' import type { Multiaddr } from '@multiformats/multiaddr' /** @@ -8,7 +8,7 @@ export function isPrivate (ma: Multiaddr): boolean { try { const { address } = ma.nodeAddress() - return Boolean(isIpPrivate(address)) + return Boolean(isPrivateIp(address)) } catch { return true } From c8fb22d258413abfb7c1bd01e84d85cebfb67d11 Mon Sep 17 00:00:00 2001 From: Cayman Date: Tue, 16 Jan 2024 13:02:05 -0500 Subject: [PATCH 3/4] chore: fix lint error --- packages/utils/src/private-ip.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/private-ip.ts b/packages/utils/src/private-ip.ts index 0957691c2d..66f6aa0eaf 100644 --- a/packages/utils/src/private-ip.ts +++ b/packages/utils/src/private-ip.ts @@ -32,7 +32,7 @@ const NETMASK_RANGES = PRIVATE_IP_RANGES.map(ipRange => new Netmask(ipRange)) function ipv4Check (ipAddr: string): boolean { for (const r of NETMASK_RANGES) { - if (r.contains(ipAddr) === true) return true + if (r.contains(ipAddr)) return true } return false From 9ae94621033e7fee49c0ef938b903d957a080569 Mon Sep 17 00:00:00 2001 From: Cayman Date: Thu, 18 Jan 2024 11:45:01 -0500 Subject: [PATCH 4/4] chore: remove invalid ip address testcases --- packages/utils/test/private-ip.spec.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/utils/test/private-ip.spec.ts b/packages/utils/test/private-ip.spec.ts index 66acade7fa..dd3b63d570 100644 --- a/packages/utils/test/private-ip.spec.ts +++ b/packages/utils/test/private-ip.spec.ts @@ -40,8 +40,6 @@ describe('private-ip', function () { const privIps = [ '0.0.0.0', - '0000.0000.0000.0000', - '0000.0000', '0.0.0.1', '0.0.0.7', '0.0.0.255', @@ -53,8 +51,6 @@ describe('private-ip', function () { '0.255.255.255', '10.0.0.0', '10.0.0.1', - '10.0.0.01', - '10.0.0.001', '10.255.255.254', '10.255.255.255', '100.64.0.0', @@ -63,10 +59,6 @@ describe('private-ip', function () { '100.127.255.255', '127.0.0.0', '127.0.0.1', - '127.0.0.01', - '127.1', - '127.0.1', - '127.000.000.1', '127.255.255.254', '127.255.255.255', '169.254.0.0', @@ -75,7 +67,6 @@ describe('private-ip', function () { '169.254.255.255', '172.16.0.0', '172.16.0.1', - '172.16.0.001', '172.31.255.254', '172.31.255.255', '192.0.0.0', @@ -172,9 +163,7 @@ describe('private-ip', function () { 'fc00::abc:1', 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'ff00::', - 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', - '2130706433', - '0x7f000001' + 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' ] const invalidIps = [