-
Notifications
You must be signed in to change notification settings - Fork 443
/
private-ip.ts
97 lines (82 loc) · 3.16 KB
/
private-ip.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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)) return true
}
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) ||
/^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 (isIpv4MappedIpv6(ip)) return ipv4MappedIpv6Check(ip)
else if (isIpv4EmbeddedIpv6(ip)) return ipv4EmbeddedIpv6Check(ip)
else if (isIPv6(ip)) return ipv6Check(ip)
else return undefined
}