Skip to content

Commit

Permalink
fix: add ipv6Check regex for private address (#2624)
Browse files Browse the repository at this point in the history
Improves the handling of IPv6 addresses when checking for private addresses.

Fixes #2623 

---------

Co-authored-by: Alex Potsides <alex@achingbrain.net>
  • Loading branch information
marcus-pousette and achingbrain committed Jul 26, 2024
1 parent 34cf1f7 commit a82ff82
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 5 deletions.
40 changes: 38 additions & 2 deletions packages/utils/src/private-ip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,45 @@ function ipv4Check (ipAddr: string): boolean {
return false
}

function isIpv4MappedIpv6 (ipAddr: string): boolean {
return /^::ffff:([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,4})$/.test(ipAddr)
}

/**
* @see https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
*/
function ipv4MappedIpv6Check (ipAddr: string): boolean {
const parts = ipAddr.split(':')

if (parts.length < 2) {
return false
}

const octet34 = parts[parts.length - 1].padStart(4, '0')
const octet12 = parts[parts.length - 2].padStart(4, '0')

const ip4 = `${parseInt(octet12.substring(0, 2), 16)}.${parseInt(octet12.substring(2), 16)}.${parseInt(octet34.substring(0, 2), 16)}.${parseInt(octet34.substring(2), 16)}`

return ipv4Check(ip4)
}

/**
* @see https://datatracker.ietf.org/doc/html/rfc4291#section-2.2 example 3
*/
function isIpv4EmbeddedIpv6 (ipAddr: string): boolean {
return /^::ffff:([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(ipAddr)
}

function ipv4EmbeddedIpv6Check (ipAddr: string): boolean {
const parts = ipAddr.split(':')
const ip4 = parts[parts.length - 1]

return ipv4Check(ip4)
}

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) ||
Expand All @@ -56,6 +90,8 @@ function ipv6Check (ipAddr: string): boolean {

export function isPrivateIp (ip: string): boolean | undefined {
if (isIPv4(ip)) return ipv4Check(ip)
else if (isIpv4MappedIpv6(ip)) return ipv4MappedIpv6Check(ip)
else if (isIpv4EmbeddedIpv6(ip)) return ipv4EmbeddedIpv6Check(ip)
else if (isIPv6(ip)) return ipv6Check(ip)
else return undefined
}
34 changes: 31 additions & 3 deletions packages/utils/test/multiaddr/is-private.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,44 @@ describe('multiaddr isPrivate', () => {
it('identifies private ip6 multiaddrs', () => {
[
multiaddr('/ip6/fd52:8342:fc46:6c91:3ac9:86ff:fe31:7095/tcp/1000'),
multiaddr('/ip6/fd52:8342:fc46:6c91:3ac9:86ff:fe31:1/tcp/1000')
multiaddr('/ip6/fd52:8342:fc46:6c91:3ac9:86ff:fe31:1/tcp/1000'),
multiaddr('/ip6/::ffff:0a00:0001/tcp/1000'), // 10.0.0.1
multiaddr('/ip6/::ffff:ac10:0001/tcp/1000'), // 172.16.0.1
multiaddr('/ip6/::ffff:c0a8:0001/tcp/1000'), // 192.168.0.1
multiaddr('/ip6/::ffff:7f00:0001/tcp/1000'), // 127.0.0.1
multiaddr('/ip6/::ffff:10.0.0.1/tcp/1000'),
multiaddr('/ip6/::ffff:172.16.0.1/tcp/1000'),
multiaddr('/ip6/::ffff:192.168.0.1/tcp/1000'),
multiaddr('/ip6/::ffff:127.0.0.1/tcp/1000')
].forEach(ma => {
expect(isPrivate(ma)).to.eql(true)
try {
expect(isPrivate(ma)).to.eql(true)
} catch (error) {
throw new Error(`Failed for ${ma.toString()}`)
}
})
})

it('identifies public ip6 multiaddrs', () => {
[
multiaddr('/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/1000'),
multiaddr('/ip6/2000:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/1000')
multiaddr('/ip6/2000:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/1000'),
multiaddr('/ip6/::ffff:6500:1a5a/tcp/1000'), // 101.0.26.90
multiaddr('/ip6/::ffff:2801:1409/tcp/1000'), // 40.1.20.9
multiaddr('/ip6/::ffff:5ca8:0001/tcp/1000'), // 92.168.0.1 (not a private range)
multiaddr('/ip6/::ffff:0200:0010/tcp/1000'), // 2.16.0.1 (not a private range)
multiaddr('/ip6/::ffff:ac09:0001/tcp/1000'), // 172.15.0.1 (not a private range)
multiaddr('/ip6/::ffff:ac20:0001/tcp/1000'), // 172.32.0.1 (not a private range)
multiaddr('/ip6/::ffff:c0a7:0001/tcp/1000'), // 192.167.0.1 (not a private range)
multiaddr('/ip6/::ffff:c0a9:0001/tcp/1000'), // 192.169.0.1 (not a private range)
multiaddr('/ip6/::ffff:101.0.26.90/tcp/1000'),
multiaddr('/ip6/::ffff:40.1.20.9/tcp/1000'),
multiaddr('/ip6/::ffff:92.168.0.1/tcp/1000'), // not a private range
multiaddr('/ip6/::ffff:2.16.0.1/tcp/1000'), // not a private range
multiaddr('/ip6/::ffff:172.15.0.1/tcp/1000'), // not a private range
multiaddr('/ip6/::ffff:172.32.0.1/tcp/1000'), // not a private range
multiaddr('/ip6/::ffff:192.167.0.1/tcp/1000'), // not a private range
multiaddr('/ip6/::ffff:192.169.0.1/tcp/1000') // not a private range
].forEach(ma => {
expect(isPrivate(ma)).to.eql(false)
})
Expand Down

0 comments on commit a82ff82

Please sign in to comment.