Merge f4ec4da68b into 07eea0f484
				
					
				
			This commit is contained in:
		
						commit
						18e922bd40
					
				| @ -286,6 +286,9 @@ | |||||||
|     "watchTriggerable": true, |     "watchTriggerable": true, | ||||||
|     "watchWithFilter": true, |     "watchWithFilter": true, | ||||||
|     "whenever": true, |     "whenever": true, | ||||||
|     "toValue": true |     "toValue": true, | ||||||
|  |     "injectLocal": true, | ||||||
|  |     "provideLocal": true, | ||||||
|  |     "useClipboardItems": true | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										9
									
								
								auto-imports.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								auto-imports.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -36,6 +36,7 @@ declare global { | |||||||
|   const h: typeof import('vue')['h'] |   const h: typeof import('vue')['h'] | ||||||
|   const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] |   const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] | ||||||
|   const inject: typeof import('vue')['inject'] |   const inject: typeof import('vue')['inject'] | ||||||
|  |   const injectLocal: typeof import('@vueuse/core')['injectLocal'] | ||||||
|   const isDefined: typeof import('@vueuse/core')['isDefined'] |   const isDefined: typeof import('@vueuse/core')['isDefined'] | ||||||
|   const isProxy: typeof import('vue')['isProxy'] |   const isProxy: typeof import('vue')['isProxy'] | ||||||
|   const isReactive: typeof import('vue')['isReactive'] |   const isReactive: typeof import('vue')['isReactive'] | ||||||
| @ -65,6 +66,7 @@ declare global { | |||||||
|   const onUpdated: typeof import('vue')['onUpdated'] |   const onUpdated: typeof import('vue')['onUpdated'] | ||||||
|   const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] |   const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] | ||||||
|   const provide: typeof import('vue')['provide'] |   const provide: typeof import('vue')['provide'] | ||||||
|  |   const provideLocal: typeof import('@vueuse/core')['provideLocal'] | ||||||
|   const reactify: typeof import('@vueuse/core')['reactify'] |   const reactify: typeof import('@vueuse/core')['reactify'] | ||||||
|   const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] |   const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] | ||||||
|   const reactive: typeof import('vue')['reactive'] |   const reactive: typeof import('vue')['reactive'] | ||||||
| @ -128,6 +130,7 @@ declare global { | |||||||
|   const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] |   const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] | ||||||
|   const useCached: typeof import('@vueuse/core')['useCached'] |   const useCached: typeof import('@vueuse/core')['useCached'] | ||||||
|   const useClipboard: typeof import('@vueuse/core')['useClipboard'] |   const useClipboard: typeof import('@vueuse/core')['useClipboard'] | ||||||
|  |   const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems'] | ||||||
|   const useCloned: typeof import('@vueuse/core')['useCloned'] |   const useCloned: typeof import('@vueuse/core')['useCloned'] | ||||||
|   const useColorMode: typeof import('@vueuse/core')['useColorMode'] |   const useColorMode: typeof import('@vueuse/core')['useColorMode'] | ||||||
|   const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] |   const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] | ||||||
| @ -326,6 +329,7 @@ declare module 'vue' { | |||||||
|     readonly h: UnwrapRef<typeof import('vue')['h']> |     readonly h: UnwrapRef<typeof import('vue')['h']> | ||||||
|     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']> |     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']> | ||||||
|     readonly inject: UnwrapRef<typeof import('vue')['inject']> |     readonly inject: UnwrapRef<typeof import('vue')['inject']> | ||||||
|  |     readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']> | ||||||
|     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']> |     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']> | ||||||
|     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']> |     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']> | ||||||
|     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']> |     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']> | ||||||
| @ -355,6 +359,7 @@ declare module 'vue' { | |||||||
|     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']> |     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']> | ||||||
|     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']> |     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']> | ||||||
|     readonly provide: UnwrapRef<typeof import('vue')['provide']> |     readonly provide: UnwrapRef<typeof import('vue')['provide']> | ||||||
|  |     readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']> | ||||||
|     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']> |     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']> | ||||||
|     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']> |     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']> | ||||||
|     readonly reactive: UnwrapRef<typeof import('vue')['reactive']> |     readonly reactive: UnwrapRef<typeof import('vue')['reactive']> | ||||||
| @ -418,6 +423,7 @@ declare module 'vue' { | |||||||
|     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']> |     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']> | ||||||
|     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']> |     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']> | ||||||
|     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']> |     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']> | ||||||
|  |     readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']> | ||||||
|     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']> |     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']> | ||||||
|     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']> |     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']> | ||||||
|     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']> |     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']> | ||||||
| @ -610,6 +616,7 @@ declare module '@vue/runtime-core' { | |||||||
|     readonly h: UnwrapRef<typeof import('vue')['h']> |     readonly h: UnwrapRef<typeof import('vue')['h']> | ||||||
|     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']> |     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']> | ||||||
|     readonly inject: UnwrapRef<typeof import('vue')['inject']> |     readonly inject: UnwrapRef<typeof import('vue')['inject']> | ||||||
|  |     readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']> | ||||||
|     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']> |     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']> | ||||||
|     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']> |     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']> | ||||||
|     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']> |     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']> | ||||||
| @ -639,6 +646,7 @@ declare module '@vue/runtime-core' { | |||||||
|     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']> |     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']> | ||||||
|     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']> |     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']> | ||||||
|     readonly provide: UnwrapRef<typeof import('vue')['provide']> |     readonly provide: UnwrapRef<typeof import('vue')['provide']> | ||||||
|  |     readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']> | ||||||
|     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']> |     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']> | ||||||
|     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']> |     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']> | ||||||
|     readonly reactive: UnwrapRef<typeof import('vue')['reactive']> |     readonly reactive: UnwrapRef<typeof import('vue')['reactive']> | ||||||
| @ -702,6 +710,7 @@ declare module '@vue/runtime-core' { | |||||||
|     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']> |     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']> | ||||||
|     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']> |     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']> | ||||||
|     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']> |     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']> | ||||||
|  |     readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']> | ||||||
|     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']> |     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']> | ||||||
|     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']> |     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']> | ||||||
|     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']> |     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']> | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -103,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'] | ||||||
|  |     IpCidrToRange: typeof import('./src/tools/ip-cidr-to-range/ip-cidr-to-range.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'] | ||||||
|  | |||||||
| @ -54,6 +54,7 @@ | |||||||
|     "@vueuse/router": "^10.0.0", |     "@vueuse/router": "^10.0.0", | ||||||
|     "bcryptjs": "^2.4.3", |     "bcryptjs": "^2.4.3", | ||||||
|     "change-case": "^4.1.2", |     "change-case": "^4.1.2", | ||||||
|  |     "cidr-tools": "^7.0.4", | ||||||
|     "colord": "^2.9.3", |     "colord": "^2.9.3", | ||||||
|     "composerize-ts": "^0.6.2", |     "composerize-ts": "^0.6.2", | ||||||
|     "country-code-lookup": "^0.1.0", |     "country-code-lookup": "^0.1.0", | ||||||
| @ -71,6 +72,12 @@ | |||||||
|     "iarna-toml-esm": "^3.0.5", |     "iarna-toml-esm": "^3.0.5", | ||||||
|     "ibantools": "^4.3.3", |     "ibantools": "^4.3.3", | ||||||
|     "js-base64": "^3.7.6", |     "js-base64": "^3.7.6", | ||||||
|  |     "ip-address": "^9.0.5", | ||||||
|  |     "ip-bigint": "^8.0.2", | ||||||
|  |     "ip-cidr": "^4.0.0", | ||||||
|  |     "ip-matching": "^2.1.2", | ||||||
|  |     "is-cidr": "^5.0.3", | ||||||
|  |     "is-ip": "^5.0.1", | ||||||
|     "json5": "^2.2.3", |     "json5": "^2.2.3", | ||||||
|     "jwt-decode": "^3.1.2", |     "jwt-decode": "^3.1.2", | ||||||
|     "libphonenumber-js": "^1.10.28", |     "libphonenumber-js": "^1.10.28", | ||||||
|  | |||||||
							
								
								
									
										13739
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13739
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -128,7 +128,7 @@ function activateOption(option: PaletteOption) { | |||||||
|       <c-input-text ref="inputRef" v-model:value="searchPrompt" raw-text placeholder="Type to search a tool or a command..." autofocus clearable /> |       <c-input-text ref="inputRef" v-model:value="searchPrompt" raw-text placeholder="Type to search a tool or a command..." autofocus clearable /> | ||||||
| 
 | 
 | ||||||
|       <div v-for="(options, category) in filteredSearchResult" :key="category"> |       <div v-for="(options, category) in filteredSearchResult" :key="category"> | ||||||
|         <div ml-3 mt-3 text-sm font-bold text-primary op-60> |         <div ml-3 mt-3 text-sm text-primary font-bold op-60> | ||||||
|           {{ category }} |           {{ category }} | ||||||
|         </div> |         </div> | ||||||
|         <command-palette-option v-for="option in options" :key="option.name" :option="option" :selected="selectedOptionIndex === getOptionIndex(option)" @activated="activateOption" /> |         <command-palette-option v-for="option in options" :key="option.name" :option="option" :selected="selectedOptionIndex === getOptionIndex(option)" @activated="activateOption" /> | ||||||
|  | |||||||
| @ -87,6 +87,7 @@ 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 yamlViewer } from './yaml-viewer'; | import { tool as yamlViewer } from './yaml-viewer'; | ||||||
|  | import { tool as ipCidrToRange } from './ip-cidr-to-range'; | ||||||
| 
 | 
 | ||||||
| export const toolsByCategory: ToolCategory[] = [ | export const toolsByCategory: ToolCategory[] = [ | ||||||
|   { |   { | ||||||
| @ -164,7 +165,15 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'Network', |     name: 'Network', | ||||||
|     components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator], |     components: [ | ||||||
|  |       ipv4SubnetCalculator, | ||||||
|  |       ipv4AddressConverter, | ||||||
|  |       ipv4RangeExpander, | ||||||
|  |       ipCidrToRange, | ||||||
|  |       macAddressLookup, | ||||||
|  |       macAddressGenerator, | ||||||
|  |       ipv6UlaGenerator, | ||||||
|  |     ], | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'Math', |     name: 'Math', | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								src/tools/ip-cidr-to-range/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/ip-cidr-to-range/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import { Binary } from '@vicons/tabler'; | ||||||
|  | import { defineTool } from '../tool'; | ||||||
|  | 
 | ||||||
|  | export const tool = defineTool({ | ||||||
|  |   name: 'Ipv4/6 CIDR to IP Range Calculator', | ||||||
|  |   path: '/ip-cidr-to-range', | ||||||
|  |   description: 'Calculate IP Range from a CIDR (IPv4/6)', | ||||||
|  |   keywords: ['ipv4', 'ipv6', 'cidr'], | ||||||
|  |   component: () => import('./ip-cidr-to-range.vue'), | ||||||
|  |   icon: Binary, | ||||||
|  |   createdAt: new Date('2024-01-10'), | ||||||
|  | }); | ||||||
							
								
								
									
										85
									
								
								src/tools/ip-cidr-to-range/ip-cidr-to-range.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/tools/ip-cidr-to-range/ip-cidr-to-range.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import isCidr from 'is-cidr'; | ||||||
|  | import { expand } from 'cidr-tools'; | ||||||
|  | import { getIPNetworkType, parseAsCIDR } from '@/utils/ip'; | ||||||
|  | import { useValidation } from '@/composable/validation'; | ||||||
|  | 
 | ||||||
|  | const rawCIDR = useStorage('ip-cidr-to-range:cidr', '192.168.1.0/24'); // NOSONAR | ||||||
|  | 
 | ||||||
|  | const result = computed(() => { | ||||||
|  |   const parsedCIDR = parseAsCIDR(rawCIDR.value) || rawCIDR.value; | ||||||
|  |   const ips = expand(parsedCIDR); | ||||||
|  |   if (!ips || !ips.length) { | ||||||
|  |     return undefined; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     startIpAddress: ips.slice(0, 1)[0], | ||||||
|  |     endIpAddress: ips.slice(-1)[0], | ||||||
|  |     parsedCIDR, | ||||||
|  |     networkType: getIPNetworkType(ips.slice(0, 1)[0]) || 'Public', | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const cidrValidation = useValidation({ | ||||||
|  |   source: rawCIDR, | ||||||
|  |   rules: [{ message: 'Invalid ipv4/6 CIDR', validator: cidr => isCidr(parseAsCIDR(cidr) || cidr) }], | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const showResult = computed(() => cidrValidation.isValid && result.value !== undefined); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <c-input-text | ||||||
|  |       v-model:value="rawCIDR" | ||||||
|  |       label="IPv4/6 CIDR (ie, 1.0.0.0/23 or 1.1.1.1/255.255.252.0 or 1.1.1.1-2.2.2.2 or 10.0.0.*)" | ||||||
|  |       placeholder="IPv4/6 CIDR (ie, 1.0.0.0/23  or 1.1.1.1/255.255.252.0 or 1.1.1.1-2.2.2.2 or 10.0.0.*)" | ||||||
|  |       :validation="cidrValidation" | ||||||
|  |       clearable | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <c-card v-if="showResult" title="Resulting CIDR" mt-4> | ||||||
|  |       <input-copyable | ||||||
|  |         label="CIDR" | ||||||
|  |         label-position="left" | ||||||
|  |         label-width="150px" | ||||||
|  |         label-align="right" | ||||||
|  | 
 | ||||||
|  |         :value="result?.parsedCIDR" | ||||||
|  |         disabled mb-2 | ||||||
|  |       /> | ||||||
|  |     </c-card> | ||||||
|  | 
 | ||||||
|  |     <c-card v-if="showResult" title="IPv4/6 range" mt-4> | ||||||
|  |       <input-copyable | ||||||
|  |         label="Start IP Address" | ||||||
|  |         label-position="left" | ||||||
|  |         label-width="150px" | ||||||
|  |         label-align="right" | ||||||
|  | 
 | ||||||
|  |         :value="result?.startIpAddress" | ||||||
|  |         disabled mb-2 | ||||||
|  |       /> | ||||||
|  |       <input-copyable | ||||||
|  |         label="End IP Address" | ||||||
|  |         label-position="left" | ||||||
|  |         label-width="150px" | ||||||
|  |         label-align="right" | ||||||
|  | 
 | ||||||
|  |         :value="result?.endIpAddress" | ||||||
|  |         disabled mb-2 | ||||||
|  |       /> | ||||||
|  | 
 | ||||||
|  |       <input-copyable | ||||||
|  |         label="Network type" | ||||||
|  |         label-position="left" | ||||||
|  |         label-width="150px" | ||||||
|  |         label-align="right" | ||||||
|  | 
 | ||||||
|  |         :value="result?.networkType" | ||||||
|  |         disabled mb-2 | ||||||
|  |       /> | ||||||
|  |     </c-card> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
| @ -151,7 +151,7 @@ function onSearchInput() { | |||||||
|       > |       > | ||||||
|         <div flex-1 truncate> |         <div flex-1 truncate> | ||||||
|           <slot name="displayed-value"> |           <slot name="displayed-value"> | ||||||
|             <input v-if="searchable && isOpen" ref="searchInputRef" v-model="searchQuery" type="text" placeholder="Search..." class="search-input" w-full lh-normal color-current @input="onSearchInput"> |             <input v-if="searchable && isOpen" ref="searchInputRef" v-model="searchQuery" type="text" placeholder="Search..." class="search-input" w-full color-current lh-normal @input="onSearchInput"> | ||||||
|             <span v-else-if="selectedOption" lh-normal> |             <span v-else-if="selectedOption" lh-normal> | ||||||
|               {{ selectedOption.label }} |               {{ selectedOption.label }} | ||||||
|             </span> |             </span> | ||||||
|  | |||||||
| @ -39,7 +39,7 @@ const headers = computed(() => { | |||||||
| <template> | <template> | ||||||
|   <div class="relative overflow-x-auto rounded"> |   <div class="relative overflow-x-auto rounded"> | ||||||
|     <table class="w-full border-collapse text-left text-sm text-gray-500 dark:text-gray-400" role="table" :aria-label="description"> |     <table class="w-full border-collapse text-left text-sm text-gray-500 dark:text-gray-400" role="table" :aria-label="description"> | ||||||
|       <thead v-if="!hideHeaders" class="bg-#ffffff uppercase text-gray-700 dark:bg-#333333 dark:text-gray-400" border-b="1px solid dark:transparent #efeff5"> |       <thead v-if="!hideHeaders" class="bg-#ffffff text-gray-700 uppercase dark:bg-#333333 dark:text-gray-400" border-b="1px solid dark:transparent #efeff5"> | ||||||
|         <tr> |         <tr> | ||||||
|           <th v-for="header in headers" :key="header.key" scope="col" class="px-6 py-3 text-xs"> |           <th v-for="header in headers" :key="header.key" scope="col" class="px-6 py-3 text-xs"> | ||||||
|             {{ header.label }} |             {{ header.label }} | ||||||
|  | |||||||
							
								
								
									
										232
									
								
								src/utils/ip.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								src/utils/ip.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,232 @@ | |||||||
|  | import { describe, expect, it } from 'vitest'; | ||||||
|  | import { getIPNetworkType, getNetworksCount, getSubnets, parseAsCIDR, to6to4Prefix, toARPA, toIPv4MappedAddress, toIPv4MappedAddressDecimal } from './ip'; | ||||||
|  | 
 | ||||||
|  | describe('ipv4/6 util', () => { | ||||||
|  |   describe('parseAsCIDR', () => { | ||||||
|  |     it('returns cidr', () => { | ||||||
|  |       expect(parseAsCIDR('1.1.1.1/6')).to.eql('1.1.1.1/6'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('172.16.2.2/16')).to.eql('172.16.2.2/16'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('1a:b:c::d:e:f/ffff:ffff:f4ff:ffff:ffff:ff5f:ffff:ff00')).to.eql(undefined); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.1.2.3/255.255.255.252')).to.eql('10.1.2.0/30'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.*.0.*')).to.eql(undefined); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.1.0.*')).to.eql('10.1.0.0/24'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.2.*.*')).to.eql('10.2.0.0/16'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('a:b:0:8000::/ffff:ffff:ffff:8000::')).to.eql('a:b:0:8000::/49'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('::/::')).to.eql('::/0'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.20.30.64-10.20.30.127')).to.eql('10.20.30.64/26'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.0.128.0/255.0.128.0')).to.eql(undefined); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('a::bc:1234/128')).to.eql('a::bc:1234/128'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('a::bc:ff00-a::bc:ff0f')).to.eql('a::bc:ff00/124'); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.0.1.1/255.255.1.0')).to.eql(undefined); // NOSONAR
 | ||||||
|  |       expect(parseAsCIDR('10.0.0.0/255.255.0.0')).to.eql('10.0.0.0/16'); // NOSONAR
 | ||||||
|  |     }); | ||||||
|  |   }); // NOSONAR
 | ||||||
|  |   describe('getSubnets', () => { | ||||||
|  |     it('returns subnets', () => { | ||||||
|  |       expect(getSubnets('1.1.1.1/1')).to.eql([ // NOSONAR
 | ||||||
|  |         '0.0.0.0/1', // NOSONAR
 | ||||||
|  |         '128.0.0.0/1', // NOSONAR
 | ||||||
|  |       ]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('1.1.1.1/6')).to.eql([ // NOSONAR
 | ||||||
|  |         '0.0.0.0/6', // NOSONAR
 | ||||||
|  |         '4.0.0.0/6', // NOSONAR
 | ||||||
|  |         '8.0.0.0/6', // NOSONAR
 | ||||||
|  |         '12.0.0.0/6', // NOSONAR
 | ||||||
|  |         '16.0.0.0/6', // NOSONAR
 | ||||||
|  |         '20.0.0.0/6', // NOSONAR
 | ||||||
|  |         '24.0.0.0/6', // NOSONAR
 | ||||||
|  |         '28.0.0.0/6', // NOSONAR
 | ||||||
|  |         '32.0.0.0/6', // NOSONAR
 | ||||||
|  |         '36.0.0.0/6', // NOSONAR
 | ||||||
|  |         '40.0.0.0/6', // NOSONAR
 | ||||||
|  |         '44.0.0.0/6', // NOSONAR
 | ||||||
|  |         '48.0.0.0/6', // NOSONAR
 | ||||||
|  |         '52.0.0.0/6', // NOSONAR
 | ||||||
|  |         '56.0.0.0/6', // NOSONAR
 | ||||||
|  |         '60.0.0.0/6', // NOSONAR
 | ||||||
|  |         '64.0.0.0/6', // NOSONAR
 | ||||||
|  |         '68.0.0.0/6', // NOSONAR
 | ||||||
|  |         '72.0.0.0/6', // NOSONAR
 | ||||||
|  |         '76.0.0.0/6', // NOSONAR
 | ||||||
|  |         '80.0.0.0/6', // NOSONAR
 | ||||||
|  |         '84.0.0.0/6', // NOSONAR
 | ||||||
|  |         '88.0.0.0/6', // NOSONAR
 | ||||||
|  |         '92.0.0.0/6', // NOSONAR
 | ||||||
|  |         '96.0.0.0/6', // NOSONAR
 | ||||||
|  |         '100.0.0.0/6', // NOSONAR
 | ||||||
|  |         '104.0.0.0/6', // NOSONAR
 | ||||||
|  |         '108.0.0.0/6', // NOSONAR
 | ||||||
|  |         '112.0.0.0/6', // NOSONAR
 | ||||||
|  |         '116.0.0.0/6', // NOSONAR
 | ||||||
|  |         '120.0.0.0/6', // NOSONAR
 | ||||||
|  |         '124.0.0.0/6', // NOSONAR
 | ||||||
|  |         '128.0.0.0/6', // NOSONAR
 | ||||||
|  |         '132.0.0.0/6', // NOSONAR
 | ||||||
|  |         '136.0.0.0/6', // NOSONAR
 | ||||||
|  |         '140.0.0.0/6', // NOSONAR
 | ||||||
|  |         '144.0.0.0/6', // NOSONAR
 | ||||||
|  |         '148.0.0.0/6', // NOSONAR
 | ||||||
|  |         '152.0.0.0/6', // NOSONAR
 | ||||||
|  |         '156.0.0.0/6', // NOSONAR
 | ||||||
|  |         '160.0.0.0/6', // NOSONAR
 | ||||||
|  |         '164.0.0.0/6', // NOSONAR
 | ||||||
|  |         '168.0.0.0/6', // NOSONAR
 | ||||||
|  |         '172.0.0.0/6', // NOSONAR
 | ||||||
|  |         '176.0.0.0/6', // NOSONAR
 | ||||||
|  |         '180.0.0.0/6', // NOSONAR
 | ||||||
|  |         '184.0.0.0/6', // NOSONAR
 | ||||||
|  |         '188.0.0.0/6', // NOSONAR
 | ||||||
|  |         '192.0.0.0/6', // NOSONAR
 | ||||||
|  |         '196.0.0.0/6', // NOSONAR
 | ||||||
|  |         '200.0.0.0/6', // NOSONAR
 | ||||||
|  |         '204.0.0.0/6', // NOSONAR
 | ||||||
|  |         '208.0.0.0/6', // NOSONAR
 | ||||||
|  |         '212.0.0.0/6', // NOSONAR
 | ||||||
|  |         '216.0.0.0/6', // NOSONAR
 | ||||||
|  |         '220.0.0.0/6', // NOSONAR
 | ||||||
|  |         '224.0.0.0/6', // NOSONAR
 | ||||||
|  |         '228.0.0.0/6', // NOSONAR
 | ||||||
|  |         '232.0.0.0/6', // NOSONAR
 | ||||||
|  |         '236.0.0.0/6', // NOSONAR
 | ||||||
|  |         '240.0.0.0/6', // NOSONAR
 | ||||||
|  |         '244.0.0.0/6', // NOSONAR
 | ||||||
|  |         '248.0.0.0/6', // NOSONAR
 | ||||||
|  |         '252.0.0.0/6', // NOSONAR
 | ||||||
|  |       ]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('1.1.1.1/8')).to.eql([]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('1.1.1.1/11')).to.eql([ // NOSONAR
 | ||||||
|  |         '1.0.0.0/11', // NOSONAR
 | ||||||
|  |         '1.32.0.0/11', // NOSONAR
 | ||||||
|  |         '1.64.0.0/11', // NOSONAR
 | ||||||
|  |         '1.96.0.0/11', // NOSONAR
 | ||||||
|  |         '1.128.0.0/11', // NOSONAR
 | ||||||
|  |         '1.160.0.0/11', // NOSONAR
 | ||||||
|  |         '1.192.0.0/11', // NOSONAR
 | ||||||
|  |         '1.224.0.0/11', // NOSONAR
 | ||||||
|  |       ]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('172.16.2.2/16')).to.eql([]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('172.16.2.2/26')).to.eql([ // NOSONAR
 | ||||||
|  |         '172.16.2.0/26', // NOSONAR
 | ||||||
|  |         '172.16.2.64/26', // NOSONAR
 | ||||||
|  |         '172.16.2.128/26', // NOSONAR
 | ||||||
|  |         '172.16.2.192/26', // NOSONAR
 | ||||||
|  |       ]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('172.16.2.2/31').length).to.eql(128); // NOSONAR
 | ||||||
|  |       expect(getSubnets('255.255.255.0/32')).to.eql([]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('2001:db8:0:85a3::ac1f:8001/62')).to.eql([]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('2001:db8:0:85a3:0:0:ac1f:8001/62')).to.eql([]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('2001:db8:0:85a3::ac1f:8001/112')).to.eql([]); // NOSONAR
 | ||||||
|  |       expect(getSubnets('2001:db8:0:85a3:0:0:ac1f:8001/112')).to.eql([]); // NOSONAR
 | ||||||
|  |     }); // NOSONAR
 | ||||||
|  |   }); // NOSONAR
 | ||||||
|  |   describe('getNetworksCount', () => { | ||||||
|  |     it('returns networks count', () => { | ||||||
|  |       expect(getNetworksCount('1.1.1.1/1')).to.eql(2); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/2')).to.eql(4); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/3')).to.eql(8); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/4')).to.eql(16); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/5')).to.eql(32); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/6')).to.eql(64); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/7')).to.eql(128); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/8')).to.eql(0); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/9')).to.eql(2); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/10')).to.eql(4); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/11')).to.eql(8); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/12')).to.eql(16); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/13')).to.eql(32); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/14')).to.eql(64); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/15')).to.eql(128); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/16')).to.eql(0); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/17')).to.eql(2); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/18')).to.eql(4); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/19')).to.eql(8); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/20')).to.eql(16); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/21')).to.eql(32); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/22')).to.eql(64); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/23')).to.eql(128); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/24')).to.eql(0); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/25')).to.eql(2); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/26')).to.eql(4); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/27')).to.eql(8); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/28')).to.eql(16); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/29')).to.eql(32); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/30')).to.eql(64); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/31')).to.eql(128); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('1.1.1.1/32')).to.eql(0); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('2001:db8:0:85a3::ac1f:8001/32')).to.eql(4294967296n); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('2001:db8:0:85a3::ac1f:8001/42')).to.eql(4194304n); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('2001:db8:0:85a3:0:0:ac1f:8001/62')).to.eql(4n); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('2001:db8:0:85a3::ac1f:8001/112')).to.eql(-1); // NOSONAR
 | ||||||
|  |       expect(getNetworksCount('2001:db8:0:85a3:0:0:ac1f:8001/122')).to.eql(-1); // NOSONAR
 | ||||||
|  |     }); // NOSONAR
 | ||||||
|  |   }); // NOSONAR
 | ||||||
|  |   describe('getIPNetworkType', () => { | ||||||
|  |     it('returns network type', () => { | ||||||
|  |       expect(getIPNetworkType('1.1.1.1')).to.eql('Public'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('10.10.1.1')).to.eql('Private Use'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('172.16.0.1')).to.eql('Private Use'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('192.168.1.1')).to.eql('Private Use'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('255.255.255.0')).to.eql('Reserved'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('224.0.0.1')).to.eql('Multicast'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('198.51.100.1')).to.eql('Documentation (TEST-NET-2)'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('198.18.0.1')).to.eql('Benchmarking'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('169.254.0.1')).to.eql('Link Local'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('127.0.0.1')).to.eql('Loopback'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('2001:db8:8:4::2')).to.eql('Documentation'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('FF02::2')).to.eql('Multicast address'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('2345:0425:2CA1:0000:0000:0567:5673:23b5')).to.eql('Public'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('fdf8:f53b:82e4::53')).to.eql('Unique-Local'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('::ffff:192.0.2.47')).to.eql('IPv4-mapped Address'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('::ffff:ac12:0a09')).to.eql('IPv4-mapped Address'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('::1')).to.eql('Loopback Address'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('fe80::200:5aee:feaa:20a2')).to.eql('Link-Local Unicast'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('2001:0002::6c::430')).to.eql('Benchmarking'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('2001:0000:4136:e378:8000:63bf:3fff:fdd2')).to.eql('TEREDO'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('2002:cb0a:3cdd:1::1')).to.eql('6to4'); // NOSONAR
 | ||||||
|  |       expect(getIPNetworkType('ff01:0:0:0:0:0:0:2')).to.eql('Multicast address'); // NOSONAR
 | ||||||
|  |     }); // NOSONAR
 | ||||||
|  |   }); // NOSONAR
 | ||||||
|  | 
 | ||||||
|  |   describe('toARPA', () => { | ||||||
|  |     it('returns ARPA address', () => { | ||||||
|  |       expect(toARPA('1.1.1.1')).to.eql('1.1.1.1.in-addr.arpa'); // NOSONAR
 | ||||||
|  |       expect(toARPA('10.10.1.1')).to.eql('1.1.10.10.in-addr.arpa'); // NOSONAR
 | ||||||
|  |       expect(toARPA('192.168.1.1')).to.eql('1.1.168.192.in-addr.arpa'); // NOSONAR
 | ||||||
|  |       expect(toARPA('255.255.255.0')).to.eql('0.255.255.255.in-addr.arpa'); // NOSONAR
 | ||||||
|  |       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.'); // NOSONAR
 | ||||||
|  |       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.'); // NOSONAR
 | ||||||
|  |       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.'); // NOSONAR
 | ||||||
|  |       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.'); // NOSONAR
 | ||||||
|  |       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.'); // NOSONAR
 | ||||||
|  |     }); // NOSONAR
 | ||||||
|  |   }); // NOSONAR
 | ||||||
|  |   describe('toIPv4MappedAddress', () => { | ||||||
|  |     it('returns IPv4MappedAddress', () => { | ||||||
|  |       expect(toIPv4MappedAddress('1.1.1.1')).to.eql('::ffff:0101:0101'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddress('10.10.1.1')).to.eql('::ffff:0a0a:0101'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddress('172.18.10.9')).to.eql('::ffff:ac12:0a09'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddress('192.168.1.1')).to.eql('::ffff:c0a8:0101'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddress('255.255.255.0')).to.eql('::ffff:ffff:ff00'); // NOSONAR
 | ||||||
|  |     }); // NOSONAR
 | ||||||
|  |   }); // NOSONAR
 | ||||||
|  |   describe('toIPv4MappedAddressDecimal', () => { | ||||||
|  |     it('returns networks count', () => { | ||||||
|  |       expect(toIPv4MappedAddressDecimal('1.1.1.1')).to.eql('::ffff:1.1.1.1'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddressDecimal('10.10.1.1')).to.eql('::ffff:10.10.1.1'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddressDecimal('192.168.1.1')).to.eql('::ffff:192.168.1.1'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddressDecimal('172.18.10.9')).to.eql('::ffff:172.18.10.9'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddressDecimal('255.255.255.0')).to.eql('::ffff:255.255.255.0'); // NOSONAR
 | ||||||
|  |       expect(toIPv4MappedAddressDecimal('2001:db8:0:85a3::ac1f:8001')).to.eql(''); // NOSONAR
 | ||||||
|  |     }); // NOSONAR
 | ||||||
|  |   }); // NOSONAR
 | ||||||
|  |   describe('to6to4Prefix', () => { | ||||||
|  |     it('returns networks count', () => { | ||||||
|  |       expect(to6to4Prefix('1.1.1.1')).to.eql('2002:01:0:1:01:01::/48'); // NOSONAR
 | ||||||
|  |       expect(to6to4Prefix('10.10.1.1')).to.eql('2002:0a:0:a:01:01::/48'); // NOSONAR
 | ||||||
|  |       expect(to6to4Prefix('172.18.10.9')).to.eql('2002:ac:1:2:0a:09::/48'); // NOSONAR
 | ||||||
|  |       expect(to6to4Prefix('192.168.1.1')).to.eql('2002:c0:a:8:01:01::/48'); // NOSONAR
 | ||||||
|  |       expect(to6to4Prefix('255.255.255.0')).to.eql('2002:ff:f:f:ff:00::/48'); // NOSONAR
 | ||||||
|  |       expect(to6to4Prefix('2001:db8:0:85a3::ac1f:8001')).to.eql(''); // NOSONAR
 | ||||||
|  |     }); // NOSONAR
 | ||||||
|  |   }); // NOSONAR
 | ||||||
|  | }); // NOSONAR
 | ||||||
							
								
								
									
										147
									
								
								src/utils/ip.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/utils/ip.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,147 @@ | |||||||
|  | import { isIPv4 } from 'is-ip'; | ||||||
|  | import { Address4, Address6 } from 'ip-address'; | ||||||
|  | import { contains as containsCidr } from 'cidr-tools'; | ||||||
|  | import type { IPMatch } from 'ip-matching'; | ||||||
|  | import { IPMask, IPSubnetwork, getMatch } from 'ip-matching'; | ||||||
|  | import isCidr from 'is-cidr'; | ||||||
|  | import ipv4registry from './ipv4registry.json'; | ||||||
|  | import ipv6registry from './ipv6registry.json'; | ||||||
|  | 
 | ||||||
|  | const IPv4MAX = (BigInt(2) ** BigInt(32)) - BigInt(1); | ||||||
|  | 
 | ||||||
|  | // IP range specific information, see IANA allocations.
 | ||||||
|  | // http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
 | ||||||
|  | const _ipv4Registry = new Map(ipv4registry.map(v => [v[0] as string, v[1]])); | ||||||
|  | 
 | ||||||
|  | // https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
 | ||||||
|  | const _ipv6Registry = new Map(ipv6registry.map(v => [v[0] as string, v[1]])); | ||||||
|  | 
 | ||||||
|  | export function parseAsCIDR(form: string) { | ||||||
|  |   if (isCidr(form)) { | ||||||
|  |     return form; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const ipMatch: IPMatch = getMatch(form); | ||||||
|  |   if (ipMatch instanceof IPSubnetwork) { | ||||||
|  |     return (ipMatch as IPSubnetwork).toString(); | ||||||
|  |   } | ||||||
|  |   if (ipMatch instanceof IPMask) { | ||||||
|  |     return (ipMatch as IPMask).convertToSubnet()?.toString(); | ||||||
|  |   } | ||||||
|  |   return (ipMatch.convertToMasks() || [])[0]?.convertToSubnet()?.toString(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getSubnets(cidr: string) { | ||||||
|  |   const [address, prefix] = cidr.split('/'); | ||||||
|  |   if (isIPv4(address)) { | ||||||
|  |     const prefix4Int = Number(prefix || '32'); | ||||||
|  |     const getMask = (prefix: number) => (IPv4MAX >> (BigInt(32) - BigInt(prefix))) << (BigInt(32) - BigInt(prefix)); | ||||||
|  |     const bigInt = BigInt((new Address4(address)).bigInteger()); | ||||||
|  | 
 | ||||||
|  |     const subnets = []; | ||||||
|  |     let startNetwork; | ||||||
|  |     if (prefix4Int < 8) { | ||||||
|  |       startNetwork = 0; | ||||||
|  |     } | ||||||
|  |     if (prefix4Int % 8 === 0) { | ||||||
|  |       return []; | ||||||
|  |     } | ||||||
|  |     startNetwork = bigInt & getMask(prefix4Int); | ||||||
|  |     const increment = BigInt(2) ** BigInt(32 - prefix4Int); | ||||||
|  |     const netCount = getNetworksCount(cidr); | ||||||
|  |     for (let netIndex = 0; netIndex < netCount; netIndex += 1) { | ||||||
|  |       const netAddr = Address4.fromBigInteger(startNetwork.toString()).correctForm(); | ||||||
|  |       subnets.push(`${netAddr}/${prefix4Int}`); | ||||||
|  |       startNetwork += increment; | ||||||
|  |     } | ||||||
|  |     return subnets; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return []; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getNetworksCount(cidr: string) { | ||||||
|  |   const [address, prefix] = cidr.split('/'); | ||||||
|  |   if (isIPv4(address)) { | ||||||
|  |     const prefix4Int = Number(prefix || '32'); | ||||||
|  | 
 | ||||||
|  |     if (prefix4Int % 8 === 0) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |     else if (prefix4Int < 8) { | ||||||
|  |       return 2 ** prefix4Int; | ||||||
|  |     } | ||||||
|  |     else if (prefix4Int < 16) { | ||||||
|  |       return 2 ** (prefix4Int - 8); | ||||||
|  |     } | ||||||
|  |     else if (prefix4Int < 24) { | ||||||
|  |       return 2 ** (prefix4Int - 16); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       return 2 ** (prefix4Int - 24); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const prefix6Int = Number(prefix || '128'); | ||||||
|  |   return prefix6Int <= 64 ? (BigInt(2) ** BigInt(64n - BigInt(prefix6Int))) : -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getIPNetworkType(address: string) { | ||||||
|  |   const results = []; | ||||||
|  |   for (const [addr, info] of (isIPv4(address) ? _ipv4Registry : _ipv6Registry).entries()) { | ||||||
|  |     const found = containsCidr([`${addr}/${Number(info[0])}`], address); | ||||||
|  |     if (found) { | ||||||
|  |       results.unshift(info[1]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return results.length === 0 ? 'Public' : results[0]?.toString(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function toARPA(address: string) { | ||||||
|  |   if (isIPv4(address)) { | ||||||
|  |     const bigInt = BigInt((new Address4(address)).bigInteger()); | ||||||
|  |     const reverseIP = ( | ||||||
|  |       [(bigInt & BigInt(255)), (bigInt >> BigInt(8) & BigInt(255)), | ||||||
|  |         (bigInt >> BigInt(16) & BigInt(255)), | ||||||
|  |         (bigInt >> BigInt(24) & BigInt(255)), | ||||||
|  |       ].join('.') | ||||||
|  |     ); | ||||||
|  |     return `${reverseIP}.in-addr.arpa`; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return (new Address6(address)).reverseForm(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function toIPv4MappedAddress(address: string) { | ||||||
|  |   if (!isIPv4(address)) { | ||||||
|  |     return ''; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const hexIP = (new Address4(address)).toHex().replace(/:/g, ''); | ||||||
|  |   return `::ffff:${hexIP.substring(0, 4)}:${hexIP.substring(4)}`; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function toIPv4MappedAddressDecimal(address: string) { | ||||||
|  |   if (!isIPv4(address)) { | ||||||
|  |     return ''; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return `::ffff:${address}`; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function to6to4Prefix(address: string) { | ||||||
|  |   if (!isIPv4(address)) { | ||||||
|  |     return ''; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const hexIP = (new Address4(address)).toHex(); | ||||||
|  |   return `2002:${hexIP.substring(0, 4)}:${hexIP.substring(4)}::/48`; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function toMicrosoftTranscription(address: string) { | ||||||
|  |   if (!isIPv4(address)) { | ||||||
|  |     return ''; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return (new Address6(address)).microsoftTranscription(); | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/utils/ipv4registry.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/utils/ipv4registry.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | [ | ||||||
|  |     ["0.0.0.0", [8, "This host on this network"]], | ||||||
|  |     ["10.0.0.0", [8, "Private Use"]], | ||||||
|  |     ["100.64.0.0", [10, "Shared Address Space"]], | ||||||
|  |     ["127.0.0.0", [8, "Loopback"]], | ||||||
|  |     ["169.254.0.0", [16, "Link Local"]], | ||||||
|  |     ["172.16.0.0", [12, "Private Use"]], | ||||||
|  |     ["192.0.0.0", [24, "IETF Protocol Assignments"]], | ||||||
|  |     ["192.0.0.0", [29, "IPv4 Service Continuity Prefix"]], | ||||||
|  |     ["192.0.0.8", [32, "IPv4 dummy address"]], | ||||||
|  |     ["192.0.0.9", [32, "Port Control Protocol Anycast"]], | ||||||
|  |     ["192.0.0.10", [32, "Traversal Using Relays around NAT Anycast"]], | ||||||
|  |     ["192.0.0.170", [32, "NAT64/DNS64 Discovery"]], | ||||||
|  |     ["192.0.0.171", [32, "NAT64/DNS64 Discovery"]], | ||||||
|  |     ["192.0.2.0", [24, "Documentation (TEST-NET-1)"]], | ||||||
|  |     ["192.31.196.0", [24, "AS112-v4"]], | ||||||
|  |     ["192.52.193.0", [24, "AMT"]], | ||||||
|  |     ["192.88.99.0", [24, "Deprecated (6to4 Relay Anycast)"]], | ||||||
|  |     ["192.168.0.0", [16, "Private Use"]], | ||||||
|  |     ["192.175.48.0", [24, "Direct Delegation AS112 Service"]], | ||||||
|  |     ["198.18.0.0", [15, "Benchmarking"]], | ||||||
|  |     ["198.51.100.0", [24, "Documentation (TEST-NET-2)"]], | ||||||
|  |     ["203.0.113.0", [24, "Documentation (TEST-NET-3)"]], | ||||||
|  |     ["224.0.0.0", [24, "Multicast"]], | ||||||
|  |     ["240.0.0.0", [4, "Reserved"]], | ||||||
|  |     ["255.255.255.255", [32, "Limited Broadcast"]] | ||||||
|  | ] | ||||||
							
								
								
									
										24
									
								
								src/utils/ipv6registry.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/utils/ipv6registry.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | [ | ||||||
|  |     ["::1", [128, "Loopback Address"]], | ||||||
|  |     ["::", [128, "Unspecified Address"]], | ||||||
|  |     ["::ffff::", [96, "IPv4-mapped Address"]], | ||||||
|  |     ["64:ff9b::", [96, "IPv4-IPv6 Translat."]], | ||||||
|  |     ["64:ff9b:1::", [48, "IPv4-IPv6 Translat."]], | ||||||
|  |     ["100::", [64, "Discard-Only Address Block"]], | ||||||
|  |     ["2001::", [23, "IETF Protocol Assignments"]], | ||||||
|  |     ["2001::", [32, "TEREDO"]], | ||||||
|  |     ["2001:1::1", [128, "Port Control Protocol Anycast"]], | ||||||
|  |     ["2001:1::2", [128, "Traversal Using Relays around NAT Anycast"]], | ||||||
|  |     ["2001:2::", [48, "Benchmarking"]], | ||||||
|  |     ["2001:3::", [32, "AMT"]], | ||||||
|  |     ["2001:4:112::", [48, "AS112-v6"]], | ||||||
|  |     ["2001:5::", [32, "EID Space for LISP (Managed by RIPE NCC)"]], | ||||||
|  |     ["2001:10::", [28, "Deprecated (previously ORCHID)"]], | ||||||
|  |     ["2001:20::", [28, "ORCHIDv2"]], | ||||||
|  |     ["2001:db8::", [32, "Documentation"]], | ||||||
|  |     ["2002::", [16, "6to4"]], | ||||||
|  |     ["2620:4f:8000::", [48, "Direct Delegation AS112 Service"]], | ||||||
|  |     ["fc00::", [7, "Unique-Local"]], | ||||||
|  |     ["fe80::", [10, "Link-Local Unicast"]], | ||||||
|  |     ["ff00::", [8, "Multicast address"]] | ||||||
|  |   ] | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user