Skip to content

Commit 3294c36

Browse files
committed
Merge branch 'feat/ip-range-to-cidr' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml # src/tools/index.ts
2 parents 7fab4e4 + 2fe369a commit 3294c36

File tree

5 files changed

+154
-0
lines changed

5 files changed

+154
-0
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@
206206
"is-cidr": "^5.0.3",
207207
"is-ip": "^5.0.1",
208208
"ip-matching": "^2.1.2",
209+
"ip-address": "^9.0.5",
210+
"ip-bigint": "^8.0.2",
211+
"ip-cidr": "^4.0.0",
212+
"is-cidr": "^5.0.3",
213+
"is-ip": "^5.0.1",
209214
"json5": "^2.2.3",
210215
"jsonpath": "^1.1.1",
211216
"jsonar-mod": "^1.9.0",

src/tools/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ import { tool as barcodeGenerator } from './barcode-generator';
207207
import { tool as htmlToMarkdown } from './html-to-markdown';
208208
import { tool as pdfUnlock } from './pdf-unlock';
209209
import { tool as ipCidrToRange } from './ip-cidr-to-range';
210+
import { tool as ipRangeToCidr } from './ip-range-to-cidr';
210211

211212
export const toolsByCategory: ToolCategory[] = [
212213
{
@@ -422,6 +423,8 @@ export const toolsByCategory: ToolCategory[] = [
422423
websocketTester,
423424
ipv4RangeExpander,
424425
ipCidrToRange,
426+
ipv4RangeExpander,
427+
ipRangeToCidr,
425428
macAddressLookup,
426429
macAddressGenerator,
427430
ipv6UlaGenerator,

src/tools/ip-range-to-cidr/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Binary } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'IPv4/6 Range to CIDR(s) Calculator',
6+
path: '/ip-range-to-cidr',
7+
description: 'Calculate CIDR(s) from an IP Range (IPv4/6)',
8+
keywords: ['ip', 'range', 'to', 'cidr'],
9+
component: () => import('./ip-range-to-cidr.vue'),
10+
icon: Binary,
11+
createdAt: new Date('2024-01-10'),
12+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
declare module 'ip-bigint' {
2+
type IPInfo = {
3+
number: bigint;
4+
version: number;
5+
ipv4mapped?: boolean;
6+
scopeid?:string;
7+
};
8+
type StringifyOptions = {
9+
compress?:boolean;
10+
hexify?:boolean;
11+
};
12+
13+
export function normalizeIp(ip: string, options: StringifyOptions = {compress = true, hexify = false} = {})
14+
export function stringifyIp(ip: IPInfo, options: StringifyOptions = {compress = true, hexify = false}): string;
15+
export function ipVersion(ip: string): number;
16+
export function parseIp(ip): IPInfo;
17+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<script setup lang="ts">
2+
import { merge, parse } from 'cidr-tools';
3+
import { isIP, isIPv6 } from 'is-ip';
4+
import { Exchange } from '@vicons/tabler';
5+
import { stringifyIp } from 'ip-bigint';
6+
import { useValidation } from '@/composable/validation';
7+
8+
const rawStartAddress = useStorage('ip-range-to-cidr:startAddress', '192.168.1.1');
9+
const rawEndAddress = useStorage('ip-range-to-cidr:endAddress', '192.168.6.255');
10+
11+
const isReversed = ref<boolean>(false);
12+
const isNotSameVersion = ref<boolean>(false);
13+
14+
const result = computed(() => {
15+
try {
16+
isNotSameVersion.value = isIPv6(rawEndAddress.value) !== isIPv6(rawStartAddress.value);
17+
isReversed.value = false;
18+
if (isNotSameVersion.value) {
19+
return [];
20+
}
21+
22+
const startIp = parse(rawStartAddress.value).start;
23+
const endIp = parse(rawEndAddress.value).end;
24+
isReversed.value = startIp > endIp;
25+
if (isReversed.value) {
26+
return [];
27+
}
28+
29+
const version = isIPv6(rawStartAddress.value) ? 6 : 4;
30+
const ipRangeLength = endIp - startIp + 1n;
31+
const allIps = new BigInt64Array(Number(ipRangeLength));
32+
let iterIp = startIp;
33+
for (let i = 0; i < ipRangeLength; i++) {
34+
allIps[i] = iterIp++;
35+
}
36+
37+
return merge(Array.from(allIps, ip => stringifyIp({ number: ip, version })));
38+
}
39+
catch (e) {
40+
return [];
41+
}
42+
});
43+
44+
const startIpValidation = useValidation({
45+
source: rawStartAddress,
46+
rules: [{ message: 'Invalid ipv4/6 address', validator: ip => isIP(ip) }],
47+
});
48+
const endIpValidation = useValidation({
49+
source: rawEndAddress,
50+
rules: [{ message: 'Invalid ipv4/6 address', validator: ip => isIP(ip) }],
51+
});
52+
53+
const showResult = computed(() => endIpValidation.isValid && startIpValidation.isValid && result.value.length > 0);
54+
55+
function onSwitchStartEndClicked() {
56+
const tmpStart = rawStartAddress.value;
57+
rawStartAddress.value = rawEndAddress.value;
58+
rawEndAddress.value = tmpStart;
59+
}
60+
</script>
61+
62+
<template>
63+
<div>
64+
<n-grid cols="4" x-gap="12" mb-6 w-full>
65+
<n-gi span="2">
66+
<c-input-text
67+
v-model:value="rawStartAddress"
68+
label="Start address"
69+
placeholder="Start IPv4/6 address..."
70+
:validation="startIpValidation"
71+
clearable
72+
/>
73+
</n-gi>
74+
<n-gi span="2">
75+
<c-input-text
76+
v-model:value="rawEndAddress"
77+
label="End address"
78+
placeholder="End IPv4/6 address..."
79+
:validation="endIpValidation"
80+
clearable
81+
/>
82+
</n-gi>
83+
</n-grid>
84+
85+
<c-card v-if="showResult" title="CIDR" data-test-id="result">
86+
<ul style="list-item-type: none">
87+
<li v-for="cidr in result" :key="cidr">
88+
{{ cidr }}
89+
</li>
90+
</ul>
91+
</c-card>
92+
<n-alert
93+
v-else-if="startIpValidation.isValid && endIpValidation.isValid && isReversed"
94+
title="Invalid combination of start and end IPv4/6 address"
95+
type="error"
96+
>
97+
<div my-3 op-70>
98+
The end IPv4/6 address is lower than the start IPv4/6 address. This is not valid and no result could be calculated.
99+
In the most cases the solution to solve this problem is to change start and end address.
100+
</div>
101+
102+
<c-button @click="onSwitchStartEndClicked">
103+
<n-icon mr-2 :component="Exchange" depth="3" size="22" />
104+
Switch start and end IPv4/6 address
105+
</c-button>
106+
</n-alert>
107+
<n-alert
108+
v-else-if="isNotSameVersion"
109+
title="Invalid combination of IP version 4/6"
110+
type="error"
111+
>
112+
<div my-3 op-70>
113+
Start IP and End IP must be of same version: IPv4 or IPv6
114+
</div>
115+
</n-alert>
116+
</div>
117+
</template>

0 commit comments

Comments
 (0)