Skip to content

Commit 94f0b9e

Browse files
committed
feat(utils): IPv4/6 Utils
Network Type, Subnets and counts, 6to4 prefix, ARPA, IPv4 Mapped Parse IP/Mask/Range as CIDR
1 parent 070ec0b commit 94f0b9e

File tree

5 files changed

+437
-0
lines changed

5 files changed

+437
-0
lines changed

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@vueuse/router": "^10.0.0",
5050
"bcryptjs": "^2.4.3",
5151
"change-case": "^4.1.2",
52+
"cidr-tools": "^7.0.4",
5253
"colord": "^2.9.3",
5354
"composerize-ts": "^0.6.2",
5455
"country-code-lookup": "^0.1.0",
@@ -64,6 +65,12 @@
6465
"highlight.js": "^11.7.0",
6566
"iarna-toml-esm": "^3.0.5",
6667
"ibantools": "^4.3.3",
68+
"ip-address": "^9.0.5",
69+
"ip-bigint": "^8.0.2",
70+
"ip-cidr": "^4.0.0",
71+
"ip-matching": "^2.1.2",
72+
"is-cidr": "^5.0.3",
73+
"is-ip": "^5.0.1",
6774
"json5": "^2.2.3",
6875
"jwt-decode": "^3.1.2",
6976
"libphonenumber-js": "^1.10.28",

src/utils/ip.test.ts

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { getIPNetworkType, getNetworksCount, getSubnets, parseAsCIDR, to6to4Prefix, toARPA, toIPv4MappedAddress, toIPv4MappedAddressDecimal } from './ip';
3+
4+
describe('ipv4/6 util', () => {
5+
describe('parseAsCIDR', () => {
6+
it('returns cidr', () => {
7+
expect(parseAsCIDR('1.1.1.1/6')).to.eql('1.1.1.1/6');
8+
expect(parseAsCIDR('172.16.2.2/16')).to.eql('172.16.2.2/16');
9+
expect(parseAsCIDR('1a:b:c::d:e:f/ffff:ffff:f4ff:ffff:ffff:ff5f:ffff:ff00')).to.eql();
10+
expect(parseAsCIDR('10.1.2.3/255.255.255.252')).to.eql('10.1.2.0/30');
11+
expect(parseAsCIDR('10.*.0.*')).to.eql();
12+
expect(parseAsCIDR('10.1.0.*')).to.eql('10.1.0.0/24');
13+
expect(parseAsCIDR('10.2.*.*')).to.eql('10.2.0.0/16');
14+
expect(parseAsCIDR('a:b:0:8000::/ffff:ffff:ffff:8000::')).to.eql('a:b:0:8000::/49');
15+
expect(parseAsCIDR('::/::')).to.eql('::/0');
16+
expect(parseAsCIDR('10.20.30.64-10.20.30.127')).to.eql('10.20.30.64/26');
17+
expect(parseAsCIDR('10.0.128.0/255.0.128.0')).to.eql();
18+
expect(parseAsCIDR('a::bc:1234/128')).to.eql('a::bc:1234/128');
19+
expect(parseAsCIDR('a::bc:ff00-a::bc:ff0f')).to.eql('a::bc:ff00/124');
20+
expect(parseAsCIDR('10.0.1.1/255.255.1.0')).to.eql();
21+
expect(parseAsCIDR('10.0.0.0/255.255.0.0')).to.eql('10.0.0.0/16');
22+
});
23+
});
24+
describe('getSubnets', () => {
25+
it('returns subnets', () => {
26+
expect(getSubnets('1.1.1.1/1')).to.eql([
27+
'0.0.0.0/1',
28+
'128.0.0.0/1',
29+
]);
30+
expect(getSubnets('1.1.1.1/6')).to.eql([
31+
'0.0.0.0/6',
32+
'4.0.0.0/6',
33+
'8.0.0.0/6',
34+
'12.0.0.0/6',
35+
'16.0.0.0/6',
36+
'20.0.0.0/6',
37+
'24.0.0.0/6',
38+
'28.0.0.0/6',
39+
'32.0.0.0/6',
40+
'36.0.0.0/6',
41+
'40.0.0.0/6',
42+
'44.0.0.0/6',
43+
'48.0.0.0/6',
44+
'52.0.0.0/6',
45+
'56.0.0.0/6',
46+
'60.0.0.0/6',
47+
'64.0.0.0/6',
48+
'68.0.0.0/6',
49+
'72.0.0.0/6',
50+
'76.0.0.0/6',
51+
'80.0.0.0/6',
52+
'84.0.0.0/6',
53+
'88.0.0.0/6',
54+
'92.0.0.0/6',
55+
'96.0.0.0/6',
56+
'100.0.0.0/6',
57+
'104.0.0.0/6',
58+
'108.0.0.0/6',
59+
'112.0.0.0/6',
60+
'116.0.0.0/6',
61+
'120.0.0.0/6',
62+
'124.0.0.0/6',
63+
'128.0.0.0/6',
64+
'132.0.0.0/6',
65+
'136.0.0.0/6',
66+
'140.0.0.0/6',
67+
'144.0.0.0/6',
68+
'148.0.0.0/6',
69+
'152.0.0.0/6',
70+
'156.0.0.0/6',
71+
'160.0.0.0/6',
72+
'164.0.0.0/6',
73+
'168.0.0.0/6',
74+
'172.0.0.0/6',
75+
'176.0.0.0/6',
76+
'180.0.0.0/6',
77+
'184.0.0.0/6',
78+
'188.0.0.0/6',
79+
'192.0.0.0/6',
80+
'196.0.0.0/6',
81+
'200.0.0.0/6',
82+
'204.0.0.0/6',
83+
'208.0.0.0/6',
84+
'212.0.0.0/6',
85+
'216.0.0.0/6',
86+
'220.0.0.0/6',
87+
'224.0.0.0/6',
88+
'228.0.0.0/6',
89+
'232.0.0.0/6',
90+
'236.0.0.0/6',
91+
'240.0.0.0/6',
92+
'244.0.0.0/6',
93+
'248.0.0.0/6',
94+
'252.0.0.0/6',
95+
]);
96+
expect(getSubnets('1.1.1.1/8')).to.eql([]);
97+
expect(getSubnets('1.1.1.1/11')).to.eql([
98+
'1.0.0.0/11',
99+
'1.32.0.0/11',
100+
'1.64.0.0/11',
101+
'1.96.0.0/11',
102+
'1.128.0.0/11',
103+
'1.160.0.0/11',
104+
'1.192.0.0/11',
105+
'1.224.0.0/11',
106+
]);
107+
expect(getSubnets('172.16.2.2/16')).to.eql([]);
108+
expect(getSubnets('172.16.2.2/26')).to.eql([
109+
'172.16.2.0/26',
110+
'172.16.2.64/26',
111+
'172.16.2.128/26',
112+
'172.16.2.192/26',
113+
]);
114+
expect(getSubnets('172.16.2.2/31').length).to.eql(128);
115+
expect(getSubnets('255.255.255.0/32')).to.eql([]);
116+
expect(getSubnets('2001:db8:0:85a3::ac1f:8001/62')).to.eql([]);
117+
expect(getSubnets('2001:db8:0:85a3:0:0:ac1f:8001/62')).to.eql([]);
118+
expect(getSubnets('2001:db8:0:85a3::ac1f:8001/112')).to.eql([]);
119+
expect(getSubnets('2001:db8:0:85a3:0:0:ac1f:8001/112')).to.eql([]);
120+
});
121+
});
122+
describe('getNetworksCount', () => {
123+
it('returns networks count', () => {
124+
expect(getNetworksCount('1.1.1.1/1')).to.eql(2);
125+
expect(getNetworksCount('1.1.1.1/2')).to.eql(4);
126+
expect(getNetworksCount('1.1.1.1/3')).to.eql(8);
127+
expect(getNetworksCount('1.1.1.1/4')).to.eql(16);
128+
expect(getNetworksCount('1.1.1.1/5')).to.eql(32);
129+
expect(getNetworksCount('1.1.1.1/6')).to.eql(64);
130+
expect(getNetworksCount('1.1.1.1/7')).to.eql(128);
131+
expect(getNetworksCount('1.1.1.1/8')).to.eql(0);
132+
expect(getNetworksCount('1.1.1.1/9')).to.eql(2);
133+
expect(getNetworksCount('1.1.1.1/10')).to.eql(4);
134+
expect(getNetworksCount('1.1.1.1/11')).to.eql(8);
135+
expect(getNetworksCount('1.1.1.1/12')).to.eql(16);
136+
expect(getNetworksCount('1.1.1.1/13')).to.eql(32);
137+
expect(getNetworksCount('1.1.1.1/14')).to.eql(64);
138+
expect(getNetworksCount('1.1.1.1/15')).to.eql(128);
139+
expect(getNetworksCount('1.1.1.1/16')).to.eql(0);
140+
expect(getNetworksCount('1.1.1.1/17')).to.eql(2);
141+
expect(getNetworksCount('1.1.1.1/18')).to.eql(4);
142+
expect(getNetworksCount('1.1.1.1/19')).to.eql(8);
143+
expect(getNetworksCount('1.1.1.1/20')).to.eql(16);
144+
expect(getNetworksCount('1.1.1.1/21')).to.eql(32);
145+
expect(getNetworksCount('1.1.1.1/22')).to.eql(64);
146+
expect(getNetworksCount('1.1.1.1/23')).to.eql(128);
147+
expect(getNetworksCount('1.1.1.1/24')).to.eql(0);
148+
expect(getNetworksCount('1.1.1.1/25')).to.eql(2);
149+
expect(getNetworksCount('1.1.1.1/26')).to.eql(4);
150+
expect(getNetworksCount('1.1.1.1/27')).to.eql(8);
151+
expect(getNetworksCount('1.1.1.1/28')).to.eql(16);
152+
expect(getNetworksCount('1.1.1.1/29')).to.eql(32);
153+
expect(getNetworksCount('1.1.1.1/30')).to.eql(64);
154+
expect(getNetworksCount('1.1.1.1/31')).to.eql(128);
155+
expect(getNetworksCount('1.1.1.1/32')).to.eql(0);
156+
expect(getNetworksCount('2001:db8:0:85a3::ac1f:8001/32')).to.eql(4294967296n);
157+
expect(getNetworksCount('2001:db8:0:85a3::ac1f:8001/42')).to.eql(4194304n);
158+
expect(getNetworksCount('2001:db8:0:85a3:0:0:ac1f:8001/62')).to.eql(4n);
159+
expect(getNetworksCount('2001:db8:0:85a3::ac1f:8001/112')).to.eql(-1);
160+
expect(getNetworksCount('2001:db8:0:85a3:0:0:ac1f:8001/122')).to.eql(-1);
161+
});
162+
});
163+
describe('getIPNetworkType', () => {
164+
it('returns network type', () => {
165+
expect(getIPNetworkType('1.1.1.1')).to.eql('Public');
166+
expect(getIPNetworkType('10.10.1.1')).to.eql('Private Use');
167+
expect(getIPNetworkType('172.16.0.1')).to.eql('Private Use');
168+
expect(getIPNetworkType('192.168.1.1')).to.eql('Private Use');
169+
expect(getIPNetworkType('255.255.255.0')).to.eql('Reserved');
170+
expect(getIPNetworkType('224.0.0.1')).to.eql('Multicast');
171+
expect(getIPNetworkType('198.51.100.1')).to.eql('Documentation (TEST-NET-2)');
172+
expect(getIPNetworkType('198.18.0.1')).to.eql('Benchmarking');
173+
expect(getIPNetworkType('169.254.0.1')).to.eql('Link Local');
174+
expect(getIPNetworkType('127.0.0.1')).to.eql('Loopback');
175+
expect(getIPNetworkType('2001:db8:8:4::2')).to.eql('Documentation');
176+
expect(getIPNetworkType('FF02::2')).to.eql('Multicast address');
177+
expect(getIPNetworkType('2345:0425:2CA1:0000:0000:0567:5673:23b5')).to.eql('Public');
178+
expect(getIPNetworkType('fdf8:f53b:82e4::53')).to.eql('Unique-Local');
179+
expect(getIPNetworkType('::ffff:192.0.2.47')).to.eql('IPv4-mapped Address');
180+
expect(getIPNetworkType('::ffff:ac12:0a09')).to.eql('IPv4-mapped Address');
181+
expect(getIPNetworkType('::1')).to.eql('Loopback Address');
182+
expect(getIPNetworkType('fe80::200:5aee:feaa:20a2')).to.eql('Link-Local Unicast');
183+
expect(getIPNetworkType('2001:0002::6c::430')).to.eql('Benchmarking');
184+
expect(getIPNetworkType('2001:0000:4136:e378:8000:63bf:3fff:fdd2')).to.eql('TEREDO');
185+
expect(getIPNetworkType('2002:cb0a:3cdd:1::1')).to.eql('6to4');
186+
expect(getIPNetworkType('ff01:0:0:0:0:0:0:2')).to.eql('Multicast address');
187+
});
188+
});
189+
190+
describe('toARPA', () => {
191+
it('returns ARPA address', () => {
192+
expect(toARPA('1.1.1.1')).to.eql('1.1.1.1.in-addr.arpa');
193+
expect(toARPA('10.10.1.1')).to.eql('1.1.10.10.in-addr.arpa');
194+
expect(toARPA('192.168.1.1')).to.eql('1.1.168.192.in-addr.arpa');
195+
expect(toARPA('255.255.255.0')).to.eql('0.255.255.255.in-addr.arpa');
196+
expect(toARPA('FF02::2')).to.eql('2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.f.f.ip6.arpa.');
197+
expect(toARPA('2345:0425:2CA1:0000:0000:0567:5673:23b5')).to.eql('5.b.3.2.3.7.6.5.7.6.5.0.0.0.0.0.0.0.0.0.1.a.c.2.5.2.4.0.5.4.3.2.ip6.arpa.');
198+
expect(toARPA('fdf8:f53b:82e4::53')).to.eql('3.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.e.2.8.b.3.5.f.8.f.d.f.ip6.arpa.');
199+
expect(toARPA('::ffff:192.0.2.47')).to.eql('f.2.2.0.0.0.0.c.f.f.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.');
200+
expect(toARPA('::1')).to.eql('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.');
201+
});
202+
});
203+
describe('toIPv4MappedAddress', () => {
204+
it('returns IPv4MappedAddress', () => {
205+
expect(toIPv4MappedAddress('1.1.1.1')).to.eql('::ffff:0101:0101');
206+
expect(toIPv4MappedAddress('10.10.1.1')).to.eql('::ffff:0a0a:0101');
207+
expect(toIPv4MappedAddress('172.18.10.9')).to.eql('::ffff:ac12:0a09');
208+
expect(toIPv4MappedAddress('192.168.1.1')).to.eql('::ffff:c0a8:0101');
209+
expect(toIPv4MappedAddress('255.255.255.0')).to.eql('::ffff:ffff:ff00');
210+
});
211+
});
212+
describe('toIPv4MappedAddressDecimal', () => {
213+
it('returns networks count', () => {
214+
expect(toIPv4MappedAddressDecimal('1.1.1.1')).to.eql('::ffff:1.1.1.1');
215+
expect(toIPv4MappedAddressDecimal('10.10.1.1')).to.eql('::ffff:10.10.1.1');
216+
expect(toIPv4MappedAddressDecimal('192.168.1.1')).to.eql('::ffff:192.168.1.1');
217+
expect(toIPv4MappedAddressDecimal('172.18.10.9')).to.eql('::ffff:172.18.10.9');
218+
expect(toIPv4MappedAddressDecimal('255.255.255.0')).to.eql('::ffff:255.255.255.0');
219+
expect(toIPv4MappedAddressDecimal('2001:db8:0:85a3::ac1f:8001')).to.eql('');
220+
});
221+
});
222+
describe('to6to4Prefix', () => {
223+
it('returns networks count', () => {
224+
expect(to6to4Prefix('1.1.1.1')).to.eql('2002:01:0:1:01:01::/48');
225+
expect(to6to4Prefix('10.10.1.1')).to.eql('2002:0a:0:a:01:01::/48');
226+
expect(to6to4Prefix('172.18.10.9')).to.eql('2002:ac:1:2:0a:09::/48');
227+
expect(to6to4Prefix('192.168.1.1')).to.eql('2002:c0:a:8:01:01::/48');
228+
expect(to6to4Prefix('255.255.255.0')).to.eql('2002:ff:f:f:ff:00::/48');
229+
expect(to6to4Prefix('2001:db8:0:85a3::ac1f:8001')).to.eql('');
230+
});
231+
});
232+
});

0 commit comments

Comments
 (0)