mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-23 00:06:15 -04:00
feat(CIDR in CIDR): IP Range (single IP, CIDR, range, mask) in IP Range (CIDR, range, mask)
This commit is contained in:
parent
f9cc935f14
commit
bb6995320c
7 changed files with 114 additions and 52 deletions
12
components.d.ts
vendored
12
components.d.ts
vendored
|
@ -37,6 +37,7 @@ declare module '@vue/runtime-core' {
|
||||||
'CFileUpload.demo': typeof import('./src/ui/c-file-upload/c-file-upload.demo.vue')['default']
|
'CFileUpload.demo': typeof import('./src/ui/c-file-upload/c-file-upload.demo.vue')['default']
|
||||||
ChmodCalculator: typeof import('./src/tools/chmod-calculator/chmod-calculator.vue')['default']
|
ChmodCalculator: typeof import('./src/tools/chmod-calculator/chmod-calculator.vue')['default']
|
||||||
Chronometer: typeof import('./src/tools/chronometer/chronometer.vue')['default']
|
Chronometer: typeof import('./src/tools/chronometer/chronometer.vue')['default']
|
||||||
|
CidrInCidr: typeof import('./src/tools/cidr-in-cidr/cidr-in-cidr.vue')['default']
|
||||||
CInputText: typeof import('./src/ui/c-input-text/c-input-text.vue')['default']
|
CInputText: typeof import('./src/ui/c-input-text/c-input-text.vue')['default']
|
||||||
'CInputText.demo': typeof import('./src/ui/c-input-text/c-input-text.demo.vue')['default']
|
'CInputText.demo': typeof import('./src/ui/c-input-text/c-input-text.demo.vue')['default']
|
||||||
CKeyValueList: typeof import('./src/ui/c-key-value-list/c-key-value-list.vue')['default']
|
CKeyValueList: typeof import('./src/ui/c-key-value-list/c-key-value-list.vue')['default']
|
||||||
|
@ -102,6 +103,7 @@ declare module '@vue/runtime-core' {
|
||||||
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
|
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
|
||||||
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
|
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
|
||||||
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
|
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
|
||||||
|
IpInRange: typeof import('./src/tools/cidr-in-cidr/cidr-in-cidr.vue')['default']
|
||||||
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
|
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
|
||||||
Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
|
Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
|
||||||
Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
|
Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
|
||||||
|
@ -127,24 +129,17 @@ declare module '@vue/runtime-core' {
|
||||||
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
|
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
|
||||||
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
|
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
|
||||||
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
|
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
|
||||||
NCode: typeof import('naive-ui')['NCode']
|
|
||||||
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
NDivider: typeof import('naive-ui')['NDivider']
|
NDivider: typeof import('naive-ui')['NDivider']
|
||||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
|
||||||
NGi: typeof import('naive-ui')['NGi']
|
|
||||||
NGrid: typeof import('naive-ui')['NGrid']
|
|
||||||
NH1: typeof import('naive-ui')['NH1']
|
NH1: typeof import('naive-ui')['NH1']
|
||||||
NH3: typeof import('naive-ui')['NH3']
|
NH3: typeof import('naive-ui')['NH3']
|
||||||
NIcon: typeof import('naive-ui')['NIcon']
|
NIcon: typeof import('naive-ui')['NIcon']
|
||||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
|
||||||
NLabel: typeof import('naive-ui')['NLabel']
|
|
||||||
NLayout: typeof import('naive-ui')['NLayout']
|
NLayout: typeof import('naive-ui')['NLayout']
|
||||||
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NTable: typeof import('naive-ui')['NTable']
|
||||||
NSpin: typeof import('naive-ui')['NSpin']
|
|
||||||
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
|
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
|
||||||
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
|
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
|
||||||
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
|
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
|
||||||
|
@ -159,6 +154,7 @@ declare module '@vue/runtime-core' {
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
|
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
|
||||||
|
SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
|
||||||
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
|
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
|
||||||
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
|
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
|
||||||
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
|
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
|
||||||
|
|
48
src/tools/cidr-in-cidr/cidr-in-cidr.service.test.ts
Normal file
48
src/tools/cidr-in-cidr/cidr-in-cidr.service.test.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { cidrInCidr } from './cidr-in-cidr.service';
|
||||||
|
|
||||||
|
describe('cidr-in-cidr', () => {
|
||||||
|
describe('cidrInCidr', () => {
|
||||||
|
it('should return correct inclusion', () => {
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.0.0/16', ipOrRangeToTest: '192.168.0.1' }).isIncluded).toBe(true); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.1.0/24', ipOrRangeToTest: '192.168.0.10' }).isIncluded).toBe(false); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.1.*', ipOrRangeToTest: '192.168.0.10' }).isIncluded).toBe(false); // NOSONAR
|
||||||
|
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.0.0/24', ipOrRangeToTest: '192.168.0.0/28' }).isIncluded).toBe(true); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.0/24', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(false); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.*', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(false); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.0.*', ipOrRangeToTest: '192.168.0.0/28' }).isIncluded).toBe(true); // NOSONAR
|
||||||
|
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.12' }).isIncluded).toBe(true); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.20' }).isIncluded).toBe(false); // NOSONAR
|
||||||
|
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.9-192.168.20.12' }).isIncluded).toBe(true); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.15', ipOrRangeToTest: '192.168.20.9-192.168.20.20' }).isIncluded).toBe(false); // NOSONAR
|
||||||
|
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.0-192.168.20.255', ipOrRangeToTest: '192.168.20.0/28' }).isIncluded).toBe(true); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.0-192.168.20.255', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(false); // NOSONAR
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.0.0-192.168.1.255', ipOrRangeToTest: '192.168.1.0/28' }).isIncluded).toBe(true); // NOSONAR
|
||||||
|
});
|
||||||
|
it('should return correct subnet', () => {
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.0.0/16', ipOrRangeToTest: '192.168.0.1' }).baseSubnets).to.deep.eq([ // NOSONAR
|
||||||
|
{
|
||||||
|
cidr: '192.168.0.0/16', // NOSONAR
|
||||||
|
end: '192.168.255.255', // NOSONAR
|
||||||
|
start: '192.168.0.0', // NOSONAR
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(cidrInCidr({ baseRange: '192.168.20.1-192.168.20.3', ipOrRangeToTest: '192.168.1.0/28' }).baseSubnets).to.deep.eq([ // NOSONAR
|
||||||
|
{
|
||||||
|
cidr: '192.168.20.1/32', // NOSONAR
|
||||||
|
end: '192.168.20.1', // NOSONAR
|
||||||
|
start: '192.168.20.1', // NOSONAR
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cidr: '192.168.20.2/31', // NOSONAR
|
||||||
|
end: '192.168.20.3', // NOSONAR
|
||||||
|
start: '192.168.20.2', // NOSONAR
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
28
src/tools/cidr-in-cidr/cidr-in-cidr.service.ts
Normal file
28
src/tools/cidr-in-cidr/cidr-in-cidr.service.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { getMatch } from 'ip-matching';
|
||||||
|
|
||||||
|
export function cidrInCidr(
|
||||||
|
{ baseRange, ipOrRangeToTest }:
|
||||||
|
{
|
||||||
|
baseRange: string
|
||||||
|
ipOrRangeToTest: string
|
||||||
|
}) {
|
||||||
|
const baseMatchMasks = getMatch(baseRange)?.convertToMasks() || [];
|
||||||
|
const ipOrRangeToTestMatchMasks = getMatch(ipOrRangeToTest)?.convertToMasks() || [];
|
||||||
|
|
||||||
|
const baseSubnets = baseMatchMasks.map((mask) => {
|
||||||
|
const subnet = mask.convertToSubnet();
|
||||||
|
if (!subnet) {
|
||||||
|
return { cidr: '', start: '', end: '' };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
cidr: subnet.toString(),
|
||||||
|
start: subnet.getFirst().toString(),
|
||||||
|
end: subnet.getLast().toString(),
|
||||||
|
};
|
||||||
|
}).filter(subnet => subnet.cidr !== '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
baseSubnets,
|
||||||
|
isIncluded: baseMatchMasks.some(baseMask => ipOrRangeToTestMatchMasks.every(ipOrRangeMask => ipOrRangeMask.isSubsetOf(baseMask))),
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,33 +2,22 @@
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
import { Check as CheckIcon, LetterX as CrossIcon } from '@vicons/tabler';
|
import { Check as CheckIcon, LetterX as CrossIcon } from '@vicons/tabler';
|
||||||
import { getMatch } from 'ip-matching';
|
import { getMatch } from 'ip-matching';
|
||||||
|
import { cidrInCidr } from './cidr-in-cidr.service';
|
||||||
import { withDefaultOnError } from '@/utils/defaults';
|
import { withDefaultOnError } from '@/utils/defaults';
|
||||||
import { isNotThrowing } from '@/utils/boolean';
|
import { isNotThrowing } from '@/utils/boolean';
|
||||||
import SpanCopyable from '@/components/SpanCopyable.vue';
|
import SpanCopyable from '@/components/SpanCopyable.vue';
|
||||||
|
|
||||||
const range = useStorage('ip-in-range:range', '192.168.0.1/24'); // NOSONAR
|
const baseRange = useStorage('cidr-in-cidr:range', '192.168.0.1/24'); // NOSONAR
|
||||||
const ip = useStorage('ip-in-range:ip', '192.168.0.1'); // NOSONAR
|
const ipOrRangeToTest = useStorage('cidr-in-cidr:ip', '192.168.0.1'); // NOSONAR
|
||||||
|
|
||||||
const match = computed(() => withDefaultOnError(() => getMatch(range.value), undefined));
|
const matchResult = computed(() => withDefaultOnError(
|
||||||
const subnets = computed(() => {
|
() => cidrInCidr({ baseRange: baseRange.value, ipOrRangeToTest: ipOrRangeToTest.value }),
|
||||||
return (match.value?.convertToMasks() || []).map((mask) => {
|
{ baseSubnets: [], isIncluded: false }));
|
||||||
const subnet = mask.convertToSubnet();
|
|
||||||
if (!subnet) {
|
|
||||||
return { cidr: '', start: '', end: '' };
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
cidr: subnet.toString(),
|
|
||||||
start: subnet.getFirst().toString(),
|
|
||||||
end: subnet.getLast().toString(),
|
|
||||||
};
|
|
||||||
}).filter(subnet => subnet.cidr !== '');
|
|
||||||
});
|
|
||||||
const ipIsInMatch = computed(() => match.value?.matches(ip.value));
|
|
||||||
|
|
||||||
const ipValidationRules = [
|
const rangeValidationRules = [
|
||||||
{
|
{
|
||||||
message: 'We cannot parse this CIDR/IP Range/Mask/Wildcard, check the format',
|
message: 'We cannot parse this CIDR/IP Range/Mask/Wildcard, check the format',
|
||||||
validator: (value: string) => isNotThrowing(() => getMatch(value)) && getMatch(range.value),
|
validator: (value: string) => isNotThrowing(() => getMatch(value)) && getMatch(value),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
@ -36,35 +25,35 @@ const ipValidationRules = [
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<c-input-text
|
<c-input-text
|
||||||
v-model:value="range"
|
v-model:value="baseRange"
|
||||||
label="An IPv4/6 CIDR/Range/Mask/Wildcard"
|
label="An IPv4/6 CIDR/Range/Mask/Wildcard (base network)"
|
||||||
placeholder="The ipv4/6 CIDR..."
|
placeholder="The ipv4/6 CIDR..."
|
||||||
:validation-rules="ipValidationRules"
|
:validation-rules="rangeValidationRules"
|
||||||
mb-4
|
mb-4
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<c-input-text
|
<c-input-text
|
||||||
v-model:value="ip"
|
v-model:value="ipOrRangeToTest"
|
||||||
label="An IPv4/6 address"
|
label="An IPv4/6 CIDR/Range/Mask/Wildcard (to test for inclusion)"
|
||||||
placeholder="The ipv4/6 address..."
|
placeholder="The An IPv4/6 CIDR/Range/Mask/Wildcard..."
|
||||||
:validation-rules="ipValidationRules"
|
:validation-rules="rangeValidationRules"
|
||||||
mb-4
|
mb-4
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<n-divider />
|
<n-divider />
|
||||||
|
|
||||||
<div flex justify-center>
|
<div flex justify-center>
|
||||||
<span v-if="ipIsInMatch">
|
<span v-if="matchResult.isIncluded">
|
||||||
<n-icon color="green">
|
<n-icon color="green">
|
||||||
<CheckIcon />
|
<CheckIcon />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
Match
|
Included
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<n-icon color="red">
|
<n-icon color="red">
|
||||||
<CrossIcon />
|
<CrossIcon />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
Not Match
|
Not included
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -73,7 +62,7 @@ const ipValidationRules = [
|
||||||
<c-card title="Subnets">
|
<c-card title="Subnets">
|
||||||
<n-table>
|
<n-table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="{ cidr, start, end } in subnets" :key="cidr">
|
<tr v-for="{ cidr, start, end } in matchResult.baseSubnets" :key="cidr">
|
||||||
<td font-bold>
|
<td font-bold>
|
||||||
<SpanCopyable :value="cidr" />
|
<SpanCopyable :value="cidr" />
|
||||||
</td>
|
</td>
|
13
src/tools/cidr-in-cidr/index.ts
Normal file
13
src/tools/cidr-in-cidr/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { UnfoldMoreOutlined } from '@vicons/material';
|
||||||
|
import { defineTool } from '../tool';
|
||||||
|
|
||||||
|
export const tool = defineTool({
|
||||||
|
name: 'IPv4-6/IPRange/CIDR in IPRange/CIDR/IPMask',
|
||||||
|
path: '/cidr-in-cidr',
|
||||||
|
description: 'Given a CIDR/IP Range/Wildcard IP/IP Mask, tell if a given IPv4-6/Range/CIDR/Wildcard IP/IP Mask is in subnet range',
|
||||||
|
keywords: ['ip', 'cidr', 'range', 'mask', 'wildcard', 'ipv4', 'ipv6', 'subnet', 'include', 'inclusion'],
|
||||||
|
component: () => import('./cidr-in-cidr.vue'),
|
||||||
|
redirectFrom: ['/ip-in-range'],
|
||||||
|
icon: UnfoldMoreOutlined,
|
||||||
|
createdAt: new Date('2025-01-12'),
|
||||||
|
});
|
|
@ -80,7 +80,7 @@ import { tool as urlParser } from './url-parser';
|
||||||
import { tool as uuidGenerator } from './uuid-generator';
|
import { tool as uuidGenerator } from './uuid-generator';
|
||||||
import { tool as macAddressLookup } from './mac-address-lookup';
|
import { tool as macAddressLookup } from './mac-address-lookup';
|
||||||
import { tool as xmlFormatter } from './xml-formatter';
|
import { tool as xmlFormatter } from './xml-formatter';
|
||||||
import { tool as ipInRange } from './ip-in-range';
|
import { tool as cidrInCidr } from './cidr-in-cidr';
|
||||||
import { tool as yamlViewer } from './yaml-viewer';
|
import { tool as yamlViewer } from './yaml-viewer';
|
||||||
|
|
||||||
export const toolsByCategory: ToolCategory[] = [
|
export const toolsByCategory: ToolCategory[] = [
|
||||||
|
@ -156,7 +156,7 @@ export const toolsByCategory: ToolCategory[] = [
|
||||||
components: [
|
components: [
|
||||||
ipv4SubnetCalculator,
|
ipv4SubnetCalculator,
|
||||||
ipv4AddressConverter,
|
ipv4AddressConverter,
|
||||||
ipInRange,
|
cidrInCidr,
|
||||||
ipv4RangeExpander,
|
ipv4RangeExpander,
|
||||||
macAddressLookup,
|
macAddressLookup,
|
||||||
macAddressGenerator,
|
macAddressGenerator,
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { UnfoldMoreOutlined } from '@vicons/material';
|
|
||||||
import { defineTool } from '../tool';
|
|
||||||
|
|
||||||
export const tool = defineTool({
|
|
||||||
name: 'IP in Range/CIDR/Mask',
|
|
||||||
path: '/ip-in-range',
|
|
||||||
description: 'Given a CIDR/IP Range/Wildcard IP/IP Mask, tell if a given IP is in subnet range',
|
|
||||||
keywords: ['ip', 'cidr', 'range'],
|
|
||||||
component: () => import('./ip-in-range.vue'),
|
|
||||||
icon: UnfoldMoreOutlined,
|
|
||||||
createdAt: new Date('2024-03-09'),
|
|
||||||
});
|
|
Loading…
Add table
Add a link
Reference in a new issue