From 93f7834922666b732507ca9c9f9dc7ee85e44a70 Mon Sep 17 00:00:00 2001 From: rwv <7891383+rwv@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:11:45 +0800 Subject: [PATCH] feat(tools): add network related tools (#8) * feat(tools): add dns related tools * feat(tools): add ip related tools * fix: remove ipv6 to mac address converter link * fix: remove unused link and views * fix: remove unused import * fix: fix ip info link * test: add network related tests * refactor: use common tools list component for network --- package.json | 11 +- pnpm-lock.yaml | 136 ++++++++++++++- .../layouts/tools-list/ToolsList.vue | 23 +++ src/components/layouts/tools-list/index.ts | 2 + src/components/layouts/tools-list/types.ts | 8 + .../components/doh/DOHServerSelect.vue | 27 +++ .../cidr-merge-exclude/CIDRMergeExclude.vue | 26 +++ .../CIDRMergeExclude.vue.test.ts | 76 +++++++++ .../tools/cidr-merge-exclude/CIDRsInput.vue | 59 +++++++ .../CIDRsMergeExcludeResult.vue | 31 ++++ .../components/tools/cidr-parse/CIDRInput.vue | 31 ++++ .../components/tools/cidr-parse/CIDRParse.vue | 17 ++ .../tools/cidr-parse/CIDRParse.vue.test.ts | 39 +++++ .../tools/cidr-parse/CIDRParseResult.vue | 150 +++++++++++++++++ .../components/tools/dns-lookup/DNSLookup.vue | 19 +++ .../tools/dns-lookup/query/DNSQuery.vue | 60 +++++++ .../tools/dns-lookup/query/DomainInput.vue | 33 ++++ .../dns-lookup/query/RecordTypeInput.vue | 29 ++++ .../tools/dns-lookup/result/DNSRawResult.vue | 15 ++ .../tools/dns-lookup/result/DNSResult.vue | 25 +++ .../tools/dns-lookup/result/DNSResultData.vue | 39 +++++ .../dns-lookup/result/DNSResultMetadata.vue | 79 +++++++++ .../dns-lookup/result/DNSResultTable.vue | 52 ++++++ .../tools/ip-cidr-normalize/IPCIDRInput.vue | 32 ++++ .../ip-cidr-normalize/IPCIDRNormalize.test.ts | 46 +++++ .../ip-cidr-normalize/IPCIDRNormalize.vue | 18 ++ .../IPCIDRNormalizedResult.vue | 16 ++ .../components/tools/ip-info/IPInfo.vue | 157 ++++++++++++++++++ .../ip-info/IPInfoDataLoadingUnknown.vue | 12 ++ .../tools/ip-info/IPInfoSearchInput.vue | 41 +++++ .../components/tools/ip-info/IPVersionTag.vue | 35 ++++ .../ip-range-to-cidr/IPRangeCIDRResult.vue | 28 ++++ .../tools/ip-range-to-cidr/IPRangeInput.vue | 53 ++++++ .../tools/ip-range-to-cidr/IPRangeToCIDR.vue | 20 +++ .../IPRangeToCIDR.vue.test.ts | 46 +++++ .../IPv6LinkLocalResult.vue | 25 +++ .../tools/mac-to-ipv6-link-local/MACInput.vue | 39 +++++ .../MACToIPv6LinkLocal.vue | 18 ++ .../MACToIPv6LinkLocal.vue.test.ts | 31 ++++ .../components/tools/my-ip/IPDisplay.vue | 34 ++++ .../network/components/tools/my-ip/MyIP.vue | 77 +++++++++ .../tools/my-ip/MyIPLoadingNull.vue | 29 ++++ .../tools/punycode/PunycodeTool.vue | 89 ++++++++++ .../tools/punycode/PunycodeTool.vue.test.ts | 34 ++++ .../reverse-ip-lookup/ReverseIPLookup.vue | 14 ++ .../reverse-ip-lookup/query/DNSQuery.vue | 48 ++++++ .../tools/reverse-ip-lookup/query/IPInput.vue | 37 +++++ src/tools/network/routes.ts | 96 +++++++++++ src/tools/network/utils/dns/common/rcode.ts | 88 ++++++++++ .../network/utils/dns/common/record-type.ts | 50 ++++++ .../utils/dns/common/reverse-ip-domain.ts | 47 ++++++ .../network/utils/dns/doh/dns-json-types.ts | 25 +++ src/tools/network/utils/dns/doh/index.ts | 3 + src/tools/network/utils/dns/doh/query.ts | 49 ++++++ src/tools/network/utils/dns/doh/servers.ts | 14 ++ .../network/utils/ip/get-domain-ips/index.ts | 30 ++++ .../utils/ip/get-ip-info/cloudflare-doh.ts | 37 +++++ .../utils/ip/get-ip-info/geojs.io-ptr.ts | 24 +++ .../network/utils/ip/get-ip-info/geojs.io.ts | 35 ++++ .../utils/ip/get-ip-info/google-doh.ts | 37 +++++ .../network/utils/ip/get-ip-info/index.ts | 23 +++ .../network/utils/ip/get-ip-info/ip.sb.ts | 29 ++++ .../utils/ip/get-ip-info/reverse-ip-domain.ts | 47 ++++++ .../network/utils/ip/get-ip-info/types.ts | 16 ++ .../network/utils/ip/get-my-ip/cloudflare.ts | 44 +++++ .../network/utils/ip/get-my-ip/geojs.io.ts | 35 ++++ src/tools/network/utils/ip/get-my-ip/index.ts | 35 ++++ src/tools/network/utils/ip/get-my-ip/ip.sb.ts | 35 ++++ .../network/utils/ip/get-my-ip/ipify.org.ts | 35 ++++ src/tools/network/utils/mac/index.ts | 2 + .../network/utils/mac/is-valid-mac-address.ts | 4 + .../mac-address-to-ipv6-link-local-address.ts | 30 ++++ .../network/views/CIDRMergeExcludeView.vue | 36 ++++ src/tools/network/views/CIDRParseView.vue | 36 ++++ src/tools/network/views/DNSLookupView.vue | 17 ++ src/tools/network/views/DOHServersView.vue | 119 +++++++++++++ src/tools/network/views/DOTServersView.vue | 68 ++++++++ .../network/views/IPCIDRNormalizeView.vue | 35 ++++ src/tools/network/views/IPInfoLookupView.vue | 37 +++++ src/tools/network/views/IPInfoView.vue | 77 +++++++++ src/tools/network/views/IPRangeToCIDRView.vue | 36 ++++ .../network/views/MACToIPv6LinkLocalView.vue | 31 ++++ src/tools/network/views/MyIPView.vue | 36 ++++ src/tools/network/views/NetworkHomeView.vue | 95 +++++++++++ src/tools/network/views/PunycodeToolView.vue | 17 ++ .../network/views/ReverseIPLookupView.vue | 17 ++ src/tools/network/views/UDPServersView.vue | 77 +++++++++ src/tools/routes.ts | 4 +- tsconfig.app.json | 22 ++- tsconfig.json | 2 +- tsconfig.node.json | 7 +- tsconfig.vitest.json | 12 +- 92 files changed, 3599 insertions(+), 18 deletions(-) create mode 100644 src/components/layouts/tools-list/ToolsList.vue create mode 100644 src/components/layouts/tools-list/index.ts create mode 100644 src/components/layouts/tools-list/types.ts create mode 100644 src/tools/network/components/doh/DOHServerSelect.vue create mode 100644 src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue create mode 100644 src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue.test.ts create mode 100644 src/tools/network/components/tools/cidr-merge-exclude/CIDRsInput.vue create mode 100644 src/tools/network/components/tools/cidr-merge-exclude/CIDRsMergeExcludeResult.vue create mode 100644 src/tools/network/components/tools/cidr-parse/CIDRInput.vue create mode 100644 src/tools/network/components/tools/cidr-parse/CIDRParse.vue create mode 100644 src/tools/network/components/tools/cidr-parse/CIDRParse.vue.test.ts create mode 100644 src/tools/network/components/tools/cidr-parse/CIDRParseResult.vue create mode 100644 src/tools/network/components/tools/dns-lookup/DNSLookup.vue create mode 100644 src/tools/network/components/tools/dns-lookup/query/DNSQuery.vue create mode 100644 src/tools/network/components/tools/dns-lookup/query/DomainInput.vue create mode 100644 src/tools/network/components/tools/dns-lookup/query/RecordTypeInput.vue create mode 100644 src/tools/network/components/tools/dns-lookup/result/DNSRawResult.vue create mode 100644 src/tools/network/components/tools/dns-lookup/result/DNSResult.vue create mode 100644 src/tools/network/components/tools/dns-lookup/result/DNSResultData.vue create mode 100644 src/tools/network/components/tools/dns-lookup/result/DNSResultMetadata.vue create mode 100644 src/tools/network/components/tools/dns-lookup/result/DNSResultTable.vue create mode 100644 src/tools/network/components/tools/ip-cidr-normalize/IPCIDRInput.vue create mode 100644 src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.test.ts create mode 100644 src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.vue create mode 100644 src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalizedResult.vue create mode 100644 src/tools/network/components/tools/ip-info/IPInfo.vue create mode 100644 src/tools/network/components/tools/ip-info/IPInfoDataLoadingUnknown.vue create mode 100644 src/tools/network/components/tools/ip-info/IPInfoSearchInput.vue create mode 100644 src/tools/network/components/tools/ip-info/IPVersionTag.vue create mode 100644 src/tools/network/components/tools/ip-range-to-cidr/IPRangeCIDRResult.vue create mode 100644 src/tools/network/components/tools/ip-range-to-cidr/IPRangeInput.vue create mode 100644 src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue create mode 100644 src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue.test.ts create mode 100644 src/tools/network/components/tools/mac-to-ipv6-link-local/IPv6LinkLocalResult.vue create mode 100644 src/tools/network/components/tools/mac-to-ipv6-link-local/MACInput.vue create mode 100644 src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue create mode 100644 src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue.test.ts create mode 100644 src/tools/network/components/tools/my-ip/IPDisplay.vue create mode 100644 src/tools/network/components/tools/my-ip/MyIP.vue create mode 100644 src/tools/network/components/tools/my-ip/MyIPLoadingNull.vue create mode 100644 src/tools/network/components/tools/punycode/PunycodeTool.vue create mode 100644 src/tools/network/components/tools/punycode/PunycodeTool.vue.test.ts create mode 100644 src/tools/network/components/tools/reverse-ip-lookup/ReverseIPLookup.vue create mode 100644 src/tools/network/components/tools/reverse-ip-lookup/query/DNSQuery.vue create mode 100644 src/tools/network/components/tools/reverse-ip-lookup/query/IPInput.vue create mode 100644 src/tools/network/routes.ts create mode 100644 src/tools/network/utils/dns/common/rcode.ts create mode 100644 src/tools/network/utils/dns/common/record-type.ts create mode 100644 src/tools/network/utils/dns/common/reverse-ip-domain.ts create mode 100644 src/tools/network/utils/dns/doh/dns-json-types.ts create mode 100644 src/tools/network/utils/dns/doh/index.ts create mode 100644 src/tools/network/utils/dns/doh/query.ts create mode 100644 src/tools/network/utils/dns/doh/servers.ts create mode 100644 src/tools/network/utils/ip/get-domain-ips/index.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/cloudflare-doh.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/geojs.io-ptr.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/geojs.io.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/google-doh.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/index.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/ip.sb.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/reverse-ip-domain.ts create mode 100644 src/tools/network/utils/ip/get-ip-info/types.ts create mode 100644 src/tools/network/utils/ip/get-my-ip/cloudflare.ts create mode 100644 src/tools/network/utils/ip/get-my-ip/geojs.io.ts create mode 100644 src/tools/network/utils/ip/get-my-ip/index.ts create mode 100644 src/tools/network/utils/ip/get-my-ip/ip.sb.ts create mode 100644 src/tools/network/utils/ip/get-my-ip/ipify.org.ts create mode 100644 src/tools/network/utils/mac/index.ts create mode 100644 src/tools/network/utils/mac/is-valid-mac-address.ts create mode 100644 src/tools/network/utils/mac/mac-address-to-ipv6-link-local-address.ts create mode 100644 src/tools/network/views/CIDRMergeExcludeView.vue create mode 100644 src/tools/network/views/CIDRParseView.vue create mode 100644 src/tools/network/views/DNSLookupView.vue create mode 100644 src/tools/network/views/DOHServersView.vue create mode 100644 src/tools/network/views/DOTServersView.vue create mode 100644 src/tools/network/views/IPCIDRNormalizeView.vue create mode 100644 src/tools/network/views/IPInfoLookupView.vue create mode 100644 src/tools/network/views/IPInfoView.vue create mode 100644 src/tools/network/views/IPRangeToCIDRView.vue create mode 100644 src/tools/network/views/MACToIPv6LinkLocalView.vue create mode 100644 src/tools/network/views/MyIPView.vue create mode 100644 src/tools/network/views/NetworkHomeView.vue create mode 100644 src/tools/network/views/PunycodeToolView.vue create mode 100644 src/tools/network/views/ReverseIPLookupView.vue create mode 100644 src/tools/network/views/UDPServersView.vue diff --git a/package.json b/package.json index b484c7b..2d48339 100644 --- a/package.json +++ b/package.json @@ -36,14 +36,21 @@ "@vueuse/core": "^11.1.0", "@vueuse/head": "^2.0.0", "@zip.js/zip.js": "^2.7.52", + "cidr-calc": "^1.0.4", + "cidr-tools": "^7.0.7", "highlight.js": "^11.10.0", + "ip-bigint": "^8.0.0", + "is-cidr": "^5.1.0", + "is-ip": "^5.0.1", "naive-ui": "^2.40.1", "pinia": "^2.1.7", + "punycode": "^2.3.1", "svgo": "^3.3.2", "uuid": "^10.0.0", "validator": "^13.12.0", "vue": "^3.4.29", - "vue-router": "^4.3.3" + "vue-router": "^4.3.3", + "webrtc-ips": "^0.2.0" }, "devDependencies": { "@commitlint/cli": "^19.5.0", @@ -71,7 +78,7 @@ "npm-run-all2": "^6.2.0", "prettier": "^3.2.5", "start-server-and-test": "^2.0.4", - "typescript": "~5.4.0", + "typescript": "~5.4.5", "vite": "^5.3.1", "vitest": "^1.6.0", "vue-tsc": "^2.0.21" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5242f0b..d87fd68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,15 +50,33 @@ importers: '@zip.js/zip.js': specifier: ^2.7.52 version: 2.7.52 + cidr-calc: + specifier: ^1.0.4 + version: 1.0.4 + cidr-tools: + specifier: ^7.0.7 + version: 7.0.7 highlight.js: specifier: ^11.10.0 version: 11.10.0 + ip-bigint: + specifier: ^8.0.0 + version: 8.0.0 + is-cidr: + specifier: ^5.1.0 + version: 5.1.0 + is-ip: + specifier: ^5.0.1 + version: 5.0.1 naive-ui: specifier: ^2.40.1 version: 2.40.1(vue@3.5.11(typescript@5.4.5)) pinia: specifier: ^2.1.7 version: 2.2.4(typescript@5.4.5)(vue@3.5.11(typescript@5.4.5)) + punycode: + specifier: ^2.3.1 + version: 2.3.1 svgo: specifier: ^3.3.2 version: 3.3.2 @@ -74,6 +92,9 @@ importers: vue-router: specifier: ^4.3.3 version: 4.4.5(vue@3.5.11(typescript@5.4.5)) + webrtc-ips: + specifier: ^0.2.0 + version: 0.2.0 devDependencies: '@commitlint/cli': specifier: ^19.5.0 @@ -151,7 +172,7 @@ importers: specifier: ^2.0.4 version: 2.0.8 typescript: - specifier: ~5.4.0 + specifier: ~5.4.5 version: 5.4.5 vite: specifier: ^5.3.1 @@ -1217,6 +1238,17 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + cidr-calc@1.0.4: + resolution: {integrity: sha512-SZcjmxxNkaCVcsFAuT+Xo9YLG9vbkrdFrspC9IVG+3kfSC84hfMzVEHttNrr0C9u7gTNf7OUYwb1WnM5pZtLZg==} + + cidr-regex@4.1.1: + resolution: {integrity: sha512-ekKcVp+iRB9zlKFXyx7io7nINgb0oRjgRdXNEodp1OuxRui8FXr/CA40Tz1voWUp9DPPrMyQKy01vJhDo4N1lw==} + engines: {node: '>=14'} + + cidr-tools@7.0.7: + resolution: {integrity: sha512-JeLGxKmaxk59IDRptqYfKa6Pw0bq8EzX7NoBu5XRKLPP9YmUu9mYcqiNliX4h3exjOqMdtzuY6F/rFj43V4Yww==} + engines: {node: '>=18'} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -1245,6 +1277,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clone-regexp@3.0.0: + resolution: {integrity: sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==} + engines: {node: '>=12'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -1313,6 +1349,10 @@ packages: engines: {node: '>=16'} hasBin: true + convert-hrtime@5.0.0: + resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} + engines: {node: '>=12'} + core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -1748,6 +1788,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-timeout@0.1.1: + resolution: {integrity: sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==} + engines: {node: '>=14.16'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1938,6 +1982,18 @@ packages: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ip-bigint@8.0.0: + resolution: {integrity: sha512-QM9TKZiXcQ+pXDCN06AZbEsgvfJYUjXxHyWwrzvYs5iyK7MWjYtFdJuz1LPgnm9d8ezd5kAGaCTHAxBfYCnQ9A==} + engines: {node: '>=18'} + + ip-bigint@8.2.0: + resolution: {integrity: sha512-46EAEKzGNxojH5JaGEeCix49tL4h1W8ia5mhogZ68HroVAfyLj1E+SFFid4GuyK0mdIKjwcAITLqwg1wlkx2iQ==} + engines: {node: '>=18'} + + ip-regex@5.0.0: + resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -1945,6 +2001,10 @@ packages: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true + is-cidr@5.1.0: + resolution: {integrity: sha512-OkVS+Ht2ssF27d48gZdB+ho1yND1VbkJRKKS6Pc1/Cw7uqkd9IOJg8/bTwBDQL6tfBhSdguPRnlGiE8pU/X5NQ==} + engines: {node: '>=14'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1969,6 +2029,10 @@ packages: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} + is-ip@5.0.1: + resolution: {integrity: sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==} + engines: {node: '>=14.16'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1984,6 +2048,10 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regexp@3.1.0: + resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} + engines: {node: '>=12'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -2481,6 +2549,10 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} + promise-controller@0.3.0: + resolution: {integrity: sha512-w1RTaOwD5bNtYjIj5L8+ruDtMgnKC0pLtOogA3kcS4LRtiwLOpAdokjYbkjJMa6AGV0oasHa8RRNa8qEpG1qyw==} + engines: {node: '>=6'} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -2714,6 +2786,10 @@ packages: strip-literal@2.1.0: resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + super-regex@0.2.0: + resolution: {integrity: sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==} + engines: {node: '>=14.16'} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -2751,6 +2827,10 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + time-span@5.1.0: + resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} + engines: {node: '>=12'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -3008,6 +3088,9 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + webrtc-ips@0.2.0: + resolution: {integrity: sha512-5TaJMFMIUMycsDBcg5QzRe1RlD2akDGfYs5+9+0+1DgTZ858KfyXjA8oIfR5xq3Y3X/ILGzRZ/mkN3/yuC+kcA==} + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -4112,6 +4195,16 @@ snapshots: ci-info@3.9.0: {} + cidr-calc@1.0.4: {} + + cidr-regex@4.1.1: + dependencies: + ip-regex: 5.0.0 + + cidr-tools@7.0.7: + dependencies: + ip-bigint: 8.2.0 + clean-stack@2.2.0: {} cli-cursor@3.1.0: @@ -4144,6 +4237,10 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clone-regexp@3.0.0: + dependencies: + is-regexp: 3.1.0 + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -4203,6 +4300,8 @@ snapshots: meow: 12.1.1 split2: 4.2.0 + convert-hrtime@5.0.0: {} + core-util-is@1.0.2: {} cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.11)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): @@ -4766,6 +4865,8 @@ snapshots: function-bind@1.1.2: {} + function-timeout@0.1.1: {} + get-caller-file@2.0.5: {} get-east-asian-width@1.2.0: {} @@ -4943,12 +5044,22 @@ snapshots: ini@4.1.1: {} + ip-bigint@8.0.0: {} + + ip-bigint@8.2.0: {} + + ip-regex@5.0.0: {} + is-arrayish@0.2.1: {} is-ci@3.0.1: dependencies: ci-info: 3.9.0 + is-cidr@5.1.0: + dependencies: + cidr-regex: 4.1.1 + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -4968,6 +5079,11 @@ snapshots: global-dirs: 3.0.1 is-path-inside: 3.0.3 + is-ip@5.0.1: + dependencies: + ip-regex: 5.0.0 + super-regex: 0.2.0 + is-number@7.0.0: {} is-obj@2.0.0: {} @@ -4976,6 +5092,8 @@ snapshots: is-potential-custom-element-name@1.0.1: {} + is-regexp@3.1.0: {} + is-stream@2.0.1: {} is-stream@3.0.0: {} @@ -5473,6 +5591,8 @@ snapshots: process@0.11.10: {} + promise-controller@0.3.0: {} + proto-list@1.2.4: {} proxy-from-env@1.0.0: {} @@ -5716,6 +5836,12 @@ snapshots: dependencies: js-tokens: 9.0.0 + super-regex@0.2.0: + dependencies: + clone-regexp: 3.0.0 + function-timeout: 0.1.1 + time-span: 5.1.0 + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -5753,6 +5879,10 @@ snapshots: through@2.3.8: {} + time-span@5.1.0: + dependencies: + convert-hrtime: 5.0.0 + tinybench@2.9.0: {} tinyexec@0.3.0: {} @@ -5992,6 +6122,10 @@ snapshots: webidl-conversions@7.0.0: {} + webrtc-ips@0.2.0: + dependencies: + promise-controller: 0.3.0 + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 diff --git a/src/components/layouts/tools-list/ToolsList.vue b/src/components/layouts/tools-list/ToolsList.vue new file mode 100644 index 0000000..7843c43 --- /dev/null +++ b/src/components/layouts/tools-list/ToolsList.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/layouts/tools-list/index.ts b/src/components/layouts/tools-list/index.ts new file mode 100644 index 0000000..ba5e891 --- /dev/null +++ b/src/components/layouts/tools-list/index.ts @@ -0,0 +1,2 @@ +export type { ToolItem } from './types' +export { default as ToolsList } from './ToolsList.vue' diff --git a/src/components/layouts/tools-list/types.ts b/src/components/layouts/tools-list/types.ts new file mode 100644 index 0000000..cc917ce --- /dev/null +++ b/src/components/layouts/tools-list/types.ts @@ -0,0 +1,8 @@ +import type { Component } from 'vue' +import type { RouteLocationRaw } from 'vue-router' + +export interface ToolItem { + icon: Component + title: string + to: RouteLocationRaw +} diff --git a/src/tools/network/components/doh/DOHServerSelect.vue b/src/tools/network/components/doh/DOHServerSelect.vue new file mode 100644 index 0000000..c8c5a8e --- /dev/null +++ b/src/tools/network/components/doh/DOHServerSelect.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue b/src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue new file mode 100644 index 0000000..f32902e --- /dev/null +++ b/src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue.test.ts b/src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue.test.ts new file mode 100644 index 0000000..9d3e0ad --- /dev/null +++ b/src/tools/network/components/tools/cidr-merge-exclude/CIDRMergeExclude.vue.test.ts @@ -0,0 +1,76 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import CIDRMergeExclude from './CIDRMergeExclude.vue' + +describe('CIDR Merge and Exclude', () => { + it('renders properly and performs IPv4CIDR operations correctly', async () => { + const wrapper = mount(CIDRMergeExclude) + + // Find create button + const buttons = wrapper.findAll('button') + expect(buttons.length).toBe(2) + + // click first button + await buttons[0].trigger('click') + + // find input + const inputs = wrapper.findAll('input') + expect(inputs.length).toBe(1) + + // set value + await inputs[0].setValue('192.168.0.0/22') + await inputs[0].trigger('change') + + // click second button + await buttons[1].trigger('click') + + // find inputs + const inputs2 = wrapper.findAll('input') + expect(inputs2.length).toBe(2) + + // set value + await inputs2[0].setValue('192.168.0.0/22') + await inputs2[0].trigger('change') + await inputs2[1].setValue('192.168.1.0/24') + await inputs2[1].trigger('change') + + expect(wrapper.text()).toContain('192.168.0.0/24') + expect(wrapper.text()).toContain('192.168.2.0/23') + }) + + it('renders properly and performs IPv6CIDR operations correctly', async () => { + const wrapper = mount(CIDRMergeExclude) + + // Find create button + const buttons = wrapper.findAll('button') + expect(buttons.length).toBe(2) + + // click first button + await buttons[0].trigger('click') + + // find input + const inputs = wrapper.findAll('input') + expect(inputs.length).toBe(1) + + // set value + await inputs[0].setValue('2001:db8::/32') + await inputs[0].trigger('change') + + // click second button + await buttons[1].trigger('click') + + // find inputs + const inputs2 = wrapper.findAll('input') + expect(inputs2.length).toBe(2) + + // set values + await inputs2[0].setValue('2001:db8::/32') + await inputs2[0].trigger('change') + await inputs2[1].setValue('2001:db8:1::/34') + await inputs2[1].trigger('change') + + // Check the results + expect(wrapper.text()).toContain('2001:db8:8000::/33') + expect(wrapper.text()).toContain('2001:db8:4000::/34') + }) +}) diff --git a/src/tools/network/components/tools/cidr-merge-exclude/CIDRsInput.vue b/src/tools/network/components/tools/cidr-merge-exclude/CIDRsInput.vue new file mode 100644 index 0000000..6a006c8 --- /dev/null +++ b/src/tools/network/components/tools/cidr-merge-exclude/CIDRsInput.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/tools/network/components/tools/cidr-merge-exclude/CIDRsMergeExcludeResult.vue b/src/tools/network/components/tools/cidr-merge-exclude/CIDRsMergeExcludeResult.vue new file mode 100644 index 0000000..0dfbc54 --- /dev/null +++ b/src/tools/network/components/tools/cidr-merge-exclude/CIDRsMergeExcludeResult.vue @@ -0,0 +1,31 @@ + + + diff --git a/src/tools/network/components/tools/cidr-parse/CIDRInput.vue b/src/tools/network/components/tools/cidr-parse/CIDRInput.vue new file mode 100644 index 0000000..c151d67 --- /dev/null +++ b/src/tools/network/components/tools/cidr-parse/CIDRInput.vue @@ -0,0 +1,31 @@ + + + diff --git a/src/tools/network/components/tools/cidr-parse/CIDRParse.vue b/src/tools/network/components/tools/cidr-parse/CIDRParse.vue new file mode 100644 index 0000000..542ede0 --- /dev/null +++ b/src/tools/network/components/tools/cidr-parse/CIDRParse.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/tools/network/components/tools/cidr-parse/CIDRParse.vue.test.ts b/src/tools/network/components/tools/cidr-parse/CIDRParse.vue.test.ts new file mode 100644 index 0000000..fa1778f --- /dev/null +++ b/src/tools/network/components/tools/cidr-parse/CIDRParse.vue.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import CIDRParse from './CIDRParse.vue' + +describe('CIDR Parse', () => { + it('renders properly and parses CIDR correctly', async () => { + const wrapper = mount(CIDRParse) + + // Find the input component + const input = wrapper.find('input') + expect(input.exists()).toBe(true) + + // Simulate user input for IPv4 CIDR + await input.setValue('192.168.0.0/24') + await input.trigger('change') + + // Check if the result is displayed correctly + expect(wrapper.text()).toContain('IPv4') + expect(wrapper.text()).toContain('192.168.0.0') + expect(wrapper.text()).toContain('192.168.0.255') + expect(wrapper.text()).toContain('256') + expect(wrapper.text()).toContain('255.255.255.0') + + // Test with an IPv6 CIDR + await input.setValue('2001:db8::/32') + await input.trigger('change') + + expect(wrapper.text()).toContain('IPv6') + expect(wrapper.text()).toContain('2001:db8::') + expect(wrapper.text()).toContain('2001:db8:ffff:ffff:ffff:ffff:ffff:ffff') + + // Test with an invalid CIDR + await input.setValue('invalid-cidr') + await input.trigger('change') + + expect(wrapper.text()).not.toContain('Start IP') + expect(wrapper.text()).not.toContain('End IP') + }) +}) diff --git a/src/tools/network/components/tools/cidr-parse/CIDRParseResult.vue b/src/tools/network/components/tools/cidr-parse/CIDRParseResult.vue new file mode 100644 index 0000000..9449d8a --- /dev/null +++ b/src/tools/network/components/tools/cidr-parse/CIDRParseResult.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/src/tools/network/components/tools/dns-lookup/DNSLookup.vue b/src/tools/network/components/tools/dns-lookup/DNSLookup.vue new file mode 100644 index 0000000..dd793b9 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/DNSLookup.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/tools/network/components/tools/dns-lookup/query/DNSQuery.vue b/src/tools/network/components/tools/dns-lookup/query/DNSQuery.vue new file mode 100644 index 0000000..07fd1f7 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/query/DNSQuery.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/tools/network/components/tools/dns-lookup/query/DomainInput.vue b/src/tools/network/components/tools/dns-lookup/query/DomainInput.vue new file mode 100644 index 0000000..ace0ed0 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/query/DomainInput.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/tools/network/components/tools/dns-lookup/query/RecordTypeInput.vue b/src/tools/network/components/tools/dns-lookup/query/RecordTypeInput.vue new file mode 100644 index 0000000..9738ca3 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/query/RecordTypeInput.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/tools/network/components/tools/dns-lookup/result/DNSRawResult.vue b/src/tools/network/components/tools/dns-lookup/result/DNSRawResult.vue new file mode 100644 index 0000000..dcb4f72 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/result/DNSRawResult.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/tools/network/components/tools/dns-lookup/result/DNSResult.vue b/src/tools/network/components/tools/dns-lookup/result/DNSResult.vue new file mode 100644 index 0000000..825b490 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/result/DNSResult.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/tools/network/components/tools/dns-lookup/result/DNSResultData.vue b/src/tools/network/components/tools/dns-lookup/result/DNSResultData.vue new file mode 100644 index 0000000..c7e61b1 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/result/DNSResultData.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/tools/network/components/tools/dns-lookup/result/DNSResultMetadata.vue b/src/tools/network/components/tools/dns-lookup/result/DNSResultMetadata.vue new file mode 100644 index 0000000..c096107 --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/result/DNSResultMetadata.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/tools/network/components/tools/dns-lookup/result/DNSResultTable.vue b/src/tools/network/components/tools/dns-lookup/result/DNSResultTable.vue new file mode 100644 index 0000000..fb9ad9f --- /dev/null +++ b/src/tools/network/components/tools/dns-lookup/result/DNSResultTable.vue @@ -0,0 +1,52 @@ + + + diff --git a/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRInput.vue b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRInput.vue new file mode 100644 index 0000000..e909340 --- /dev/null +++ b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRInput.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.test.ts b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.test.ts new file mode 100644 index 0000000..e6847b1 --- /dev/null +++ b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import IPCIDRNormalize from './IPCIDRNormalize.vue' + +describe('IP/CIDR Normalize', () => { + it('renders properly and normalizes IP/CIDR correctly', async () => { + const wrapper = mount(IPCIDRNormalize) + + // Find the input component + const input = wrapper.find('input') + expect(input.exists()).toBe(true) + + // Test with IPv4 address + await input.setValue('192.168.0.1') + await input.trigger('change') + + expect(wrapper.text()).toContain('192.168.0.1') + + // Test with IPv4 CIDR + await input.setValue('192.168.0.0/24') + await input.trigger('change') + + expect(wrapper.text()).toContain('192.168.0.0') + expect(wrapper.text()).toContain('192.168.0.0/24') + + // Test with IPv6 address + await input.setValue('2001:db8::1') + await input.trigger('change') + + expect(wrapper.text()).toContain('2001:db8::1') + + // Test with IPv6 CIDR + await input.setValue('2001:db8::/32') + await input.trigger('change') + + expect(wrapper.text()).toContain('2001:db8::') + expect(wrapper.text()).toContain('2001:db8::/32') + + // Test with invalid input + await input.setValue('invalid-input') + await input.trigger('change') + + expect(wrapper.text()).not.toContain('Normalized IP') + expect(wrapper.text()).not.toContain('Normalized CIDR') + }) +}) diff --git a/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.vue b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.vue new file mode 100644 index 0000000..2eb79d2 --- /dev/null +++ b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalize.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalizedResult.vue b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalizedResult.vue new file mode 100644 index 0000000..f7f2ce6 --- /dev/null +++ b/src/tools/network/components/tools/ip-cidr-normalize/IPCIDRNormalizedResult.vue @@ -0,0 +1,16 @@ + + + diff --git a/src/tools/network/components/tools/ip-info/IPInfo.vue b/src/tools/network/components/tools/ip-info/IPInfo.vue new file mode 100644 index 0000000..7215df2 --- /dev/null +++ b/src/tools/network/components/tools/ip-info/IPInfo.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/src/tools/network/components/tools/ip-info/IPInfoDataLoadingUnknown.vue b/src/tools/network/components/tools/ip-info/IPInfoDataLoadingUnknown.vue new file mode 100644 index 0000000..8754aa3 --- /dev/null +++ b/src/tools/network/components/tools/ip-info/IPInfoDataLoadingUnknown.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/tools/network/components/tools/ip-info/IPInfoSearchInput.vue b/src/tools/network/components/tools/ip-info/IPInfoSearchInput.vue new file mode 100644 index 0000000..add1987 --- /dev/null +++ b/src/tools/network/components/tools/ip-info/IPInfoSearchInput.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/tools/network/components/tools/ip-info/IPVersionTag.vue b/src/tools/network/components/tools/ip-info/IPVersionTag.vue new file mode 100644 index 0000000..c593728 --- /dev/null +++ b/src/tools/network/components/tools/ip-info/IPVersionTag.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/tools/network/components/tools/ip-range-to-cidr/IPRangeCIDRResult.vue b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeCIDRResult.vue new file mode 100644 index 0000000..f1dc40b --- /dev/null +++ b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeCIDRResult.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/tools/network/components/tools/ip-range-to-cidr/IPRangeInput.vue b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeInput.vue new file mode 100644 index 0000000..26d4300 --- /dev/null +++ b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeInput.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue new file mode 100644 index 0000000..d760e21 --- /dev/null +++ b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue.test.ts b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue.test.ts new file mode 100644 index 0000000..8618a70 --- /dev/null +++ b/src/tools/network/components/tools/ip-range-to-cidr/IPRangeToCIDR.vue.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import IPRangeToCIDR from './IPRangeToCIDR.vue' + +describe('IP Range to CIDR', () => { + it('renders properly and converts IP range to CIDR correctly', async () => { + const wrapper = mount(IPRangeToCIDR) + + // Find the input components + const inputs = wrapper.findAll('input') + expect(inputs.length).toBe(2) + + // Test with IPv4 range + await inputs[0].setValue('192.168.0.1') + await inputs[1].setValue('192.168.0.10') + await inputs[0].trigger('change') + await inputs[1].trigger('change') + + // Check if the result is displayed correctly + expect(wrapper.text()).toContain('192.168.0.1/32') + expect(wrapper.text()).toContain('192.168.0.2/31') + expect(wrapper.text()).toContain('192.168.0.4/30') + expect(wrapper.text()).toContain('192.168.0.8/31') + expect(wrapper.text()).toContain('192.168.0.10/32') + + // Test with IPv6 range + await inputs[0].setValue('2001:db8::1') + await inputs[1].setValue('2001:db8::10') + await inputs[0].trigger('change') + await inputs[1].trigger('change') + + expect(wrapper.text()).toContain('2001:db8::1/128') + expect(wrapper.text()).toContain('2001:db8::2/127') + expect(wrapper.text()).toContain('2001:db8::4/126') + expect(wrapper.text()).toContain('2001:db8::8/125') + expect(wrapper.text()).toContain('2001:db8::10/128') + + // Test with invalid input + await inputs[0].setValue('invalid-ip') + await inputs[1].setValue('192.168.0.10') + await inputs[0].trigger('change') + await inputs[1].trigger('change') + + expect(wrapper.text()).not.toContain('CIDR Result') + }) +}) diff --git a/src/tools/network/components/tools/mac-to-ipv6-link-local/IPv6LinkLocalResult.vue b/src/tools/network/components/tools/mac-to-ipv6-link-local/IPv6LinkLocalResult.vue new file mode 100644 index 0000000..748d1d0 --- /dev/null +++ b/src/tools/network/components/tools/mac-to-ipv6-link-local/IPv6LinkLocalResult.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/tools/network/components/tools/mac-to-ipv6-link-local/MACInput.vue b/src/tools/network/components/tools/mac-to-ipv6-link-local/MACInput.vue new file mode 100644 index 0000000..f20ff44 --- /dev/null +++ b/src/tools/network/components/tools/mac-to-ipv6-link-local/MACInput.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue b/src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue new file mode 100644 index 0000000..e44dcd9 --- /dev/null +++ b/src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue.test.ts b/src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue.test.ts new file mode 100644 index 0000000..f19e94e --- /dev/null +++ b/src/tools/network/components/tools/mac-to-ipv6-link-local/MACToIPv6LinkLocal.vue.test.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import MACToIPv6LinkLocal from './MACToIPv6LinkLocal.vue' + +describe('MAC to IPv6 Link-Local', () => { + it('renders properly and converts MAC to IPv6 Link-Local correctly', async () => { + const wrapper = mount(MACToIPv6LinkLocal) + + // Find the input component + const input = wrapper.find('input') + expect(input.exists()).toBe(true) + + // Test with a valid MAC address + await input.setValue('00:11:22:33:44:55') + await input.trigger('change') + + expect(wrapper.text()).toContain('fe80::211:22ff:fe33:4455') + + // Test with another valid MAC address + await input.setValue('aa:bb:cc:dd:ee:ff') + await input.trigger('change') + + expect(wrapper.text()).toContain('fe80::a8bb:ccff:fedd:eeff') + + // Test with an invalid MAC address + await input.setValue('invalid-mac') + await input.trigger('change') + + expect(wrapper.text()).not.toContain('fe80::') + }) +}) diff --git a/src/tools/network/components/tools/my-ip/IPDisplay.vue b/src/tools/network/components/tools/my-ip/IPDisplay.vue new file mode 100644 index 0000000..42ef25e --- /dev/null +++ b/src/tools/network/components/tools/my-ip/IPDisplay.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/src/tools/network/components/tools/my-ip/MyIP.vue b/src/tools/network/components/tools/my-ip/MyIP.vue new file mode 100644 index 0000000..02bf4d5 --- /dev/null +++ b/src/tools/network/components/tools/my-ip/MyIP.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/tools/network/components/tools/my-ip/MyIPLoadingNull.vue b/src/tools/network/components/tools/my-ip/MyIPLoadingNull.vue new file mode 100644 index 0000000..0fc1267 --- /dev/null +++ b/src/tools/network/components/tools/my-ip/MyIPLoadingNull.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/src/tools/network/components/tools/punycode/PunycodeTool.vue b/src/tools/network/components/tools/punycode/PunycodeTool.vue new file mode 100644 index 0000000..f864c27 --- /dev/null +++ b/src/tools/network/components/tools/punycode/PunycodeTool.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/src/tools/network/components/tools/punycode/PunycodeTool.vue.test.ts b/src/tools/network/components/tools/punycode/PunycodeTool.vue.test.ts new file mode 100644 index 0000000..8623d7b --- /dev/null +++ b/src/tools/network/components/tools/punycode/PunycodeTool.vue.test.ts @@ -0,0 +1,34 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import PunycodeTool from './PunycodeTool.vue' + +describe('Punycode Tool', () => { + it('renders properly and converts domain names correctly', async () => { + const wrapper = mount(PunycodeTool) + + // Find the input components + const inputs = wrapper.findAll('input') + expect(inputs.length).toBe(2) + + const asciiInput = inputs[0] + const unicodeInput = inputs[1] + + // Test ASCII to Punycode conversion + await asciiInput.setValue('example.com') + await asciiInput.trigger('change') + + expect(unicodeInput.element.value).toBe('example.com') + + // Test IDN to Punycode conversion + await unicodeInput.setValue('例子.测试') + await unicodeInput.trigger('change') + + expect(asciiInput.element.value).toBe('xn--fsqu00a.xn--0zwm56d') + + // Test Punycode to IDN conversion + await asciiInput.setValue('xn--fsqu00a.xn--0zwm56d') + await asciiInput.trigger('change') + + expect(unicodeInput.element.value).toBe('例子.测试') + }) +}) diff --git a/src/tools/network/components/tools/reverse-ip-lookup/ReverseIPLookup.vue b/src/tools/network/components/tools/reverse-ip-lookup/ReverseIPLookup.vue new file mode 100644 index 0000000..3d64655 --- /dev/null +++ b/src/tools/network/components/tools/reverse-ip-lookup/ReverseIPLookup.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/tools/network/components/tools/reverse-ip-lookup/query/DNSQuery.vue b/src/tools/network/components/tools/reverse-ip-lookup/query/DNSQuery.vue new file mode 100644 index 0000000..194cbd0 --- /dev/null +++ b/src/tools/network/components/tools/reverse-ip-lookup/query/DNSQuery.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/tools/network/components/tools/reverse-ip-lookup/query/IPInput.vue b/src/tools/network/components/tools/reverse-ip-lookup/query/IPInput.vue new file mode 100644 index 0000000..50033b7 --- /dev/null +++ b/src/tools/network/components/tools/reverse-ip-lookup/query/IPInput.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/tools/network/routes.ts b/src/tools/network/routes.ts new file mode 100644 index 0000000..f7dea3e --- /dev/null +++ b/src/tools/network/routes.ts @@ -0,0 +1,96 @@ +import type { RouteRecordRaw } from 'vue-router' + +const prefix = '/tools/network' + +export const routes: RouteRecordRaw[] = [ + { + path: prefix, + name: 'tools-network', + component: () => import('./views/NetworkHomeView.vue') + }, + { + path: prefix + '/dns-lookup', + name: 'tools-network-dns-lookup', + component: () => import('./views/DNSLookupView.vue') + }, + { + path: prefix + '/doh-servers', + name: 'tools-network-doh-servers', + component: () => import('./views/DOHServersView.vue') + }, + { + path: prefix + '/dot-servers', + name: 'tools-network-dot-servers', + component: () => import('./views/DOTServersView.vue') + }, + { + path: prefix + '/udp-servers', + name: 'tools-network-udp-servers', + component: () => import('./views/UDPServersView.vue') + }, + { + path: prefix + '/punycode-tool', + name: 'tools-network-punycode-tool', + component: () => import('./views/PunycodeToolView.vue') + }, + { + path: prefix + '/reverse-ip-lookup', + name: 'tools-network-reverse-ip-lookup', + component: () => import('./views/ReverseIPLookupView.vue') + }, + { + path: prefix + '/my-ip', + name: 'tools-network-my-ip', + component: () => import('./views/MyIPView.vue') + }, + { + path: prefix + '/ip-info', + name: 'tools-network-ip-info-lookup', + component: () => import('./views/IPInfoLookupView.vue') + }, + { + path: prefix + '/ip-info/:ipdomain', + name: 'tools-network-ip-info', + component: () => import('./views/IPInfoView.vue') + }, + { + path: prefix + '/cidr-parse', + name: 'tools-network-cidr-parse', + component: () => import('./views/CIDRParseView.vue'), + meta: { + keepAlive: true + } + }, + { + path: prefix + '/cidr-merge-exclude', + name: 'tools-network-cidr-merge-exclude', + component: () => import('./views/CIDRMergeExcludeView.vue'), + meta: { + keepAlive: true + } + }, + { + path: prefix + '/ip-range-to-cidr', + name: 'tools-network-ip-range-to-cidr', + component: () => import('./views/IPRangeToCIDRView.vue'), + meta: { + keepAlive: true + } + }, + { + path: prefix + '/ip-cidr-normalize', + name: 'tools-network-ip-cidr-normalize', + component: () => import('./views/IPCIDRNormalizeView.vue'), + meta: { + keepAlive: true + } + }, + { + path: prefix + '/mac-to-ipv6-link-local', + name: 'tools-network-mac-to-ipv6-link-local', + component: () => import('./views/MACToIPv6LinkLocalView.vue'), + meta: { + keepAlive: true + } + } +] diff --git a/src/tools/network/utils/dns/common/rcode.ts b/src/tools/network/utils/dns/common/rcode.ts new file mode 100644 index 0000000..62165eb --- /dev/null +++ b/src/tools/network/utils/dns/common/rcode.ts @@ -0,0 +1,88 @@ +export const DNSRCODEs: Record< + number, + { + name: string + description: string + } +> = { + 0: { + name: 'NoError', + description: 'No Error' + }, + 1: { + name: 'FormErr', + description: 'Format Error' + }, + 2: { + name: 'ServFail', + description: 'Server Failure' + }, + 3: { + name: 'NXDomain', + description: 'Non-Existent Domain' + }, + 4: { + name: 'NotImp', + description: 'Not Implemented' + }, + 5: { + name: 'Refused', + description: 'Query Refused' + }, + 6: { + name: 'YXDomain', + description: 'Name Exists when it should not' + }, + 7: { + name: 'YXRRSet', + description: 'RR Set Exists when it should not' + }, + 8: { + name: 'NXRRSet', + description: 'RR Set that should exist does not' + }, + 9: { + name: 'NotAuth', + description: 'Server Not Authoritative for zone' + }, + 10: { + name: 'NotZone', + description: 'Name not contained in zone' + }, + 11: { + name: 'DSOTYPENI', + description: 'DSO-TYPE Not Implemented' + }, + 16: { + name: 'BADVERS', + description: 'Bad OPT Version' + }, + 17: { + name: 'BADKEY', + description: 'Key not recognized' + }, + 18: { + name: 'BADTIME', + description: 'Signature out of time window' + }, + 19: { + name: 'BADMODE', + description: 'Bad TKEY Mode' + }, + 20: { + name: 'BADNAME', + description: 'Duplicate key name' + }, + 21: { + name: 'BADALG', + description: 'Algorithm not supported' + }, + 22: { + name: 'BADTRUNC', + description: 'Bad Truncation' + }, + 23: { + name: 'BADCOOKIE', + description: 'Bad/missing Server Cookie' + } +} diff --git a/src/tools/network/utils/dns/common/record-type.ts b/src/tools/network/utils/dns/common/record-type.ts new file mode 100644 index 0000000..449f46f --- /dev/null +++ b/src/tools/network/utils/dns/common/record-type.ts @@ -0,0 +1,50 @@ +export const DNSRecordTypes: Record = { + 1: 'A', + 2: 'NS', + 5: 'CNAME', + 6: 'SOA', + 12: 'PTR', + 13: 'HINFO', + 15: 'MX', + 16: 'TXT', + 17: 'RP', + 18: 'AFSDB', + 24: 'SIG', + 25: 'KEY', + 28: 'AAAA', + 29: 'LOC', + 33: 'SRV', + 35: 'NAPTR', + 36: 'KX', + 37: 'CERT', + 39: 'DNAME', + 42: 'APL', + 43: 'DS', + 44: 'SSHFP', + 45: 'IPSECKEY', + 46: 'RRSIG', + 47: 'NSEC', + 48: 'DNSKEY', + 49: 'DHCID', + 50: 'NSEC3', + 51: 'NSEC3PARAM', + 52: 'TLSA', + 53: 'SMIMEA', + 55: 'HIP', + 59: 'CDS', + 60: 'CDNSKEY', + 61: 'OPENPGPKEY', + 62: 'CSYNC', + 63: 'ZONEMD', + 64: 'SVCB', + 65: 'HTTPS', + 108: 'EUI48', + 109: 'EUI64', + 249: 'TKEY', + 250: 'TSIG', + 255: 'ANY', + 256: 'URI', + 257: 'CAA', + 32768: 'TA', + 32769: 'DLV' +} diff --git a/src/tools/network/utils/dns/common/reverse-ip-domain.ts b/src/tools/network/utils/dns/common/reverse-ip-domain.ts new file mode 100644 index 0000000..7581124 --- /dev/null +++ b/src/tools/network/utils/dns/common/reverse-ip-domain.ts @@ -0,0 +1,47 @@ +export function ReverseIPDomain(ip: string): string { + let reverseIPDomain = '' + if (ip.includes(':')) { + reverseIPDomain = toIPv6Arpa(ip) + } else { + reverseIPDomain = ip.split('.').reverse().join('.') + '.in-addr.arpa' + } + + return reverseIPDomain +} + +function toIPv6Arpa(ipv6: string): string { + // Step 1: Expand the IPv6 address to its full form + const expandedIPv6 = expandIPv6Address(ipv6) + // Step 2: Remove the colons + const noColons = expandedIPv6.replace(/:/g, '') + // Step 3: Reverse the order of the characters + const reversed = noColons.split('').reverse().join('') + // Step 4: Insert dots between every character + const dotted = reversed.split('').join('.') + // Step 5: Append .ip6.arpa to the end + const arpaFormat = dotted + '.ip6.arpa' + + return arpaFormat +} + +function expandIPv6Address(address: string) { + let fullAddress = '' + const segments = address.split('::') + if (segments.length === 2) { + const segment1 = segments[0].split(':') + const segment2 = segments[1].split(':') + const missingZeroes = 8 - (segment1.length + segment2.length) + fullAddress = segment1.join(':') + for (let i = 0; i < missingZeroes; i++) { + fullAddress += ':0000' + } + fullAddress += ':' + segment2.join(':') + } else { + fullAddress = address + } + const hextets = fullAddress.split(':') + for (let i = 0; i < hextets.length; i++) { + hextets[i] = ('0000' + hextets[i]).slice(-4) + } + return hextets.join(':') +} diff --git a/src/tools/network/utils/dns/doh/dns-json-types.ts b/src/tools/network/utils/dns/doh/dns-json-types.ts new file mode 100644 index 0000000..62348eb --- /dev/null +++ b/src/tools/network/utils/dns/doh/dns-json-types.ts @@ -0,0 +1,25 @@ +export interface DNSJSONResponse { + Status: number + TC: boolean + RD: boolean + RA: boolean + AD: boolean + CD: boolean + Question: DNSJSONQuestion[] + Answer?: DNSJSONAnswer[] + Additional?: DNSJSONAnswer[] + edns_client_subnet?: string + Comment?: string +} + +export interface DNSJSONQuestion { + name: string + type: number +} + +export interface DNSJSONAnswer { + name: string + type: number + TTL: number + data: string +} diff --git a/src/tools/network/utils/dns/doh/index.ts b/src/tools/network/utils/dns/doh/index.ts new file mode 100644 index 0000000..da13ca2 --- /dev/null +++ b/src/tools/network/utils/dns/doh/index.ts @@ -0,0 +1,3 @@ +export { BUILTIN_DOH_SERVERS } from './servers' +export { makeDOHQuery } from './query' +export type { DNSJSONResponse, DNSJSONQuestion, DNSJSONAnswer } from './dns-json-types' diff --git a/src/tools/network/utils/dns/doh/query.ts b/src/tools/network/utils/dns/doh/query.ts new file mode 100644 index 0000000..d3bbda9 --- /dev/null +++ b/src/tools/network/utils/dns/doh/query.ts @@ -0,0 +1,49 @@ +import type { DNSJSONResponse } from './dns-json-types' + +// https://developers.google.com/speed/public-dns/docs/doh/json +export interface DNSQuery { + name: string + type?: string // DNS record type (either empty or one of A, AAAA, CNAME, MX, NS, PTR, SOA, or TXT). + cd?: boolean // The CD (Checking Disabled) flag. Use cd=1, or cd=true to disable DNSSEC validation; use cd=0, cd=false, or no cd parameter to enable DNSSEC validation. + ct?: string // The content type of the request body. Currently only application/dns-json is supported. + do?: boolean // The DO (DNSSEC OK) flag. Use do=1, or do=true to set the DO flag; use do=0, do=false, or no do parameter to unset the DO flag. + edns_client_subnet?: string // The edns0-client-subnet option. Format is an IP address with a subnet mask. Examples: 1.2.3.4/24, 2001:700:300::/48. +} + +export async function makeDOHQuery(server: string, query: DNSQuery): Promise { + const url = new URL(server) + const searchParams: Record = { + name: query.name + } + if (query.type) { + searchParams.type = query.type + } + if (query.cd) { + searchParams.cd = query.cd ? '1' : '0' + } + if (query.ct) { + searchParams.ct = query.ct + } + if (query.do) { + searchParams.do = query.do ? '1' : '0' + } + if (query.edns_client_subnet) { + searchParams.edns_client_subnet = query.edns_client_subnet + } + + url.search = new URLSearchParams(searchParams).toString() + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + Accept: 'application/dns-json' + } + }) + + if (!response.ok) { + throw new Error(`HTTP error: ${response.status}`) + } + + const json = await response.json() + return json +} diff --git a/src/tools/network/utils/dns/doh/servers.ts b/src/tools/network/utils/dns/doh/servers.ts new file mode 100644 index 0000000..8c36090 --- /dev/null +++ b/src/tools/network/utils/dns/doh/servers.ts @@ -0,0 +1,14 @@ +export const BUILTIN_DOH_SERVERS = [ + { + label: 'Cloudflare', + url: 'https://cloudflare-dns.com/dns-query' + }, + { + label: 'Google', + url: 'https://dns.google/resolve' + }, + { + label: 'AliDNS', + url: 'https://dns.alidns.com/resolve' + } +] diff --git a/src/tools/network/utils/ip/get-domain-ips/index.ts b/src/tools/network/utils/ip/get-domain-ips/index.ts new file mode 100644 index 0000000..92215cd --- /dev/null +++ b/src/tools/network/utils/ip/get-domain-ips/index.ts @@ -0,0 +1,30 @@ +import { makeDOHQuery, BUILTIN_DOH_SERVERS } from '../../dns/doh' + +export async function getDomainIPs(domain: string): Promise { + for (const server in BUILTIN_DOH_SERVERS) { + try { + const url = BUILTIN_DOH_SERVERS[server].url + const AResponsePromise = makeDOHQuery(url, { name: domain, type: 'A' }) + const AAAAResponsePromise = makeDOHQuery(url, { + name: domain, + type: 'AAAA' + }) + const [AResponse, AAAAResponse] = await Promise.all([AResponsePromise, AAAAResponsePromise]) + const ips = [] + if (AResponse.Answer) { + ips.push( + ...AResponse.Answer.filter((answer) => answer.type === 1).map((answer) => answer.data) + ) + } + if (AAAAResponse.Answer) { + ips.push( + ...AAAAResponse.Answer.filter((answer) => answer.type === 28).map((answer) => answer.data) + ) + } + return ips + } catch (e) { + console.error(e) + } + } + throw new Error('Failed to get domain IP.') +} diff --git a/src/tools/network/utils/ip/get-ip-info/cloudflare-doh.ts b/src/tools/network/utils/ip/get-ip-info/cloudflare-doh.ts new file mode 100644 index 0000000..1d6dbd6 --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/cloudflare-doh.ts @@ -0,0 +1,37 @@ +import type { IPInfo } from './types' +import { ReverseIPDomain } from './reverse-ip-domain' + +const name = 'cloudflare-doh' +const doh_url = 'https://cloudflare-dns.com/dns-query' + +export async function getIPInfo(ip: string): Promise { + const domain = ReverseIPDomain(ip) + const url = new URL(doh_url) + url.search = new URLSearchParams({ + name: domain, + type: 'PTR' + }).toString() + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + Accept: 'application/dns-json' + } + }) + + if (!response.ok) { + throw new Error(`HTTP error: ${response.status}`) + } + + const data = await response.json() + const hostname = data.Answer[0].data + if (typeof hostname === 'string') { + return { hostname } + } else { + throw new Error(`Invalid response: ${JSON.stringify(data)}`) + } +} + +export default { + name, + getIPInfo +} diff --git a/src/tools/network/utils/ip/get-ip-info/geojs.io-ptr.ts b/src/tools/network/utils/ip/get-ip-info/geojs.io-ptr.ts new file mode 100644 index 0000000..eb030da --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/geojs.io-ptr.ts @@ -0,0 +1,24 @@ +import type { IPInfo } from './types' + +export const name = 'geojs.io-ptr' + +interface APIResult { + ptr: string +} + +export async function getIPInfo(ip: string, options?: RequestInit): Promise { + const response = await fetch(`https://get.geojs.io/v1/dns/ptr/${ip}.json`, options) + const data: APIResult = await response.json() + if (data.ptr === 'Failed to get PTR record') { + throw new Error('Failed to get PTR record') + } + + return { + hostname: data.ptr + } +} + +export default { + name, + getIPInfo +} diff --git a/src/tools/network/utils/ip/get-ip-info/geojs.io.ts b/src/tools/network/utils/ip/get-ip-info/geojs.io.ts new file mode 100644 index 0000000..a8b4780 --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/geojs.io.ts @@ -0,0 +1,35 @@ +import type { IPInfo } from './types' + +export const name = 'geojs.io' + +interface APIResult { + organization_name: string + country_code: string + country_code3: string + continent_code: string + latitude: string + longitude: string + accuracy: number + asn: number + timezone: string + organization: string + ip: string + country: string + area_code: string +} + +export async function getIPInfo(ip: string, options?: RequestInit): Promise { + const response = await fetch(`https://get.geojs.io/v1/ip/geo/${ip}.json`, options) + const data: APIResult = await response.json() + return { + ...data, + latitude: Number(data.latitude), + longitude: Number(data.longitude), + asn_organization: data.organization_name + } +} + +export default { + name, + getIPInfo +} diff --git a/src/tools/network/utils/ip/get-ip-info/google-doh.ts b/src/tools/network/utils/ip/get-ip-info/google-doh.ts new file mode 100644 index 0000000..a3d6acd --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/google-doh.ts @@ -0,0 +1,37 @@ +import type { IPInfo } from './types' +import { ReverseIPDomain } from './reverse-ip-domain' + +const name = 'google-doh' +const doh_url = 'https://dns.google/resolve' + +export async function getIPInfo(ip: string): Promise { + const domain = ReverseIPDomain(ip) + const url = new URL(doh_url) + url.search = new URLSearchParams({ + name: domain, + type: 'PTR' + }).toString() + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + Accept: 'application/dns-json' + } + }) + + if (!response.ok) { + throw new Error(`HTTP error: ${response.status}`) + } + + const data = await response.json() + const hostname = data.Answer[0].data + if (typeof hostname === 'string') { + return { hostname } + } else { + throw new Error(`Invalid response: ${JSON.stringify(data)}`) + } +} + +export default { + name, + getIPInfo +} diff --git a/src/tools/network/utils/ip/get-ip-info/index.ts b/src/tools/network/utils/ip/get-ip-info/index.ts new file mode 100644 index 0000000..244daf7 --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/index.ts @@ -0,0 +1,23 @@ +import IPSB from './ip.sb' +import geojsIo from './geojs.io' +import cloudflareDoh from './cloudflare-doh' +import googleDoh from './google-doh' +import geojsIoPtr from './geojs.io-ptr' +import type { IPInfo } from './types' + +export type { IPInfo } from './types' + +type IPInfoProviderFunction = (ip: string, options?: RequestInit) => Promise + +export type IPInfoProvider = { + name: string + getIPInfo: IPInfoProviderFunction +} + +export const IPInfoProviders: IPInfoProvider[] = [ + geojsIo, + IPSB, + cloudflareDoh, + googleDoh, + geojsIoPtr +] diff --git a/src/tools/network/utils/ip/get-ip-info/ip.sb.ts b/src/tools/network/utils/ip/get-ip-info/ip.sb.ts new file mode 100644 index 0000000..8469df7 --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/ip.sb.ts @@ -0,0 +1,29 @@ +import type { IPInfo } from './types' + +export const name = 'ip.sb' + +interface APIResult { + organization: string + longitude: number + timezone: string + isp: string + offset: number + asn: number + asn_organization: string + country: string + ip: string + latitude: number + continent_code: string + country_code: string +} + +export async function getIPInfo(ip: string, options?: RequestInit): Promise { + const response = await fetch(`https://api.ip.sb/geoip/${ip}`, options) + const data: APIResult = await response.json() + return data +} + +export default { + name, + getIPInfo +} diff --git a/src/tools/network/utils/ip/get-ip-info/reverse-ip-domain.ts b/src/tools/network/utils/ip/get-ip-info/reverse-ip-domain.ts new file mode 100644 index 0000000..7581124 --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/reverse-ip-domain.ts @@ -0,0 +1,47 @@ +export function ReverseIPDomain(ip: string): string { + let reverseIPDomain = '' + if (ip.includes(':')) { + reverseIPDomain = toIPv6Arpa(ip) + } else { + reverseIPDomain = ip.split('.').reverse().join('.') + '.in-addr.arpa' + } + + return reverseIPDomain +} + +function toIPv6Arpa(ipv6: string): string { + // Step 1: Expand the IPv6 address to its full form + const expandedIPv6 = expandIPv6Address(ipv6) + // Step 2: Remove the colons + const noColons = expandedIPv6.replace(/:/g, '') + // Step 3: Reverse the order of the characters + const reversed = noColons.split('').reverse().join('') + // Step 4: Insert dots between every character + const dotted = reversed.split('').join('.') + // Step 5: Append .ip6.arpa to the end + const arpaFormat = dotted + '.ip6.arpa' + + return arpaFormat +} + +function expandIPv6Address(address: string) { + let fullAddress = '' + const segments = address.split('::') + if (segments.length === 2) { + const segment1 = segments[0].split(':') + const segment2 = segments[1].split(':') + const missingZeroes = 8 - (segment1.length + segment2.length) + fullAddress = segment1.join(':') + for (let i = 0; i < missingZeroes; i++) { + fullAddress += ':0000' + } + fullAddress += ':' + segment2.join(':') + } else { + fullAddress = address + } + const hextets = fullAddress.split(':') + for (let i = 0; i < hextets.length; i++) { + hextets[i] = ('0000' + hextets[i]).slice(-4) + } + return hextets.join(':') +} diff --git a/src/tools/network/utils/ip/get-ip-info/types.ts b/src/tools/network/utils/ip/get-ip-info/types.ts new file mode 100644 index 0000000..a6ed4d6 --- /dev/null +++ b/src/tools/network/utils/ip/get-ip-info/types.ts @@ -0,0 +1,16 @@ +export interface IPInfo { + // location + timezone?: string | null + longitude?: number | null + latitude?: number | null + country?: string | null + continent?: string | null + city?: string | null + + // network + hostname?: string | null + asn?: number | null + asn_organization?: string | null + isp?: string | null + organization?: string | null +} diff --git a/src/tools/network/utils/ip/get-my-ip/cloudflare.ts b/src/tools/network/utils/ip/get-my-ip/cloudflare.ts new file mode 100644 index 0000000..196dad0 --- /dev/null +++ b/src/tools/network/utils/ip/get-my-ip/cloudflare.ts @@ -0,0 +1,44 @@ +import { isIPv4, isIPv6 } from 'is-ip' + +export const serverName = 'Cloudflare' + +export async function getMyIPUniversal(options?: RequestInit): Promise { + const api = 'https://cloudflare.com/cdn-cgi/trace' + const response = await fetch(api, options) + const data = await response.text() + const ip = data + .split('\n') + .filter((line) => line.startsWith('ip='))[0] + .split('=')[1] + if (!isIPv4(ip) && !isIPv6(ip)) { + throw new Error(`Invalid IP: ${ip}`) + } + + return ip +} + +export async function getMyIPv4(options?: RequestInit): Promise { + const ip = await getMyIPUniversal(options) + if (!isIPv4(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export async function getMyIPv6(options?: RequestInit): Promise { + const ip = await getMyIPUniversal(options) + if (!isIPv6(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export const getMyIP = { + ipv4: getMyIPv4, + ipv6: getMyIPv6, + name: serverName +} + +export default getMyIP diff --git a/src/tools/network/utils/ip/get-my-ip/geojs.io.ts b/src/tools/network/utils/ip/get-my-ip/geojs.io.ts new file mode 100644 index 0000000..d1820f4 --- /dev/null +++ b/src/tools/network/utils/ip/get-my-ip/geojs.io.ts @@ -0,0 +1,35 @@ +import { isIPv4, isIPv6 } from 'is-ip' + +export const name = 'GeoJS' + +export async function getMyIPv4(options?: RequestInit): Promise { + const api = 'https://ipv4.geojs.io/v1/ip.json' + const response = await fetch(api, options) + const data = await response.json() + const ip = data.ip + if (!isIPv4(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export async function getMyIPv6(options?: RequestInit): Promise { + const api = 'https://ipv6.geojs.io/v1/ip.json' + const response = await fetch(api, options) + const data = await response.json() + const ip = data.ip + if (!isIPv6(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export const getMyIP = { + ipv4: getMyIPv4, + ipv6: getMyIPv6, + name: name +} + +export default getMyIP diff --git a/src/tools/network/utils/ip/get-my-ip/index.ts b/src/tools/network/utils/ip/get-my-ip/index.ts new file mode 100644 index 0000000..1d53e86 --- /dev/null +++ b/src/tools/network/utils/ip/get-my-ip/index.ts @@ -0,0 +1,35 @@ +import IPSB from './ip.sb' +import GEOJSIO from './geojs.io' +import Cloudflare from './cloudflare' +import IPIFY from './ipify.org' + +type getIPFunction = (options?: { signal: AbortSignal }) => Promise +interface GetMyIPProvider { + ipv4?: getIPFunction + ipv6?: getIPFunction + name: string +} + +export const getMyIPProviders: GetMyIPProvider[] = [IPSB, GEOJSIO, Cloudflare, IPIFY] + +export const getMyIPv4 = async (): Promise => { + const controller = new AbortController() + const signal = controller.signal + const ipv4Functions = getMyIPProviders + .filter((provider) => provider.ipv4) + .map((provider) => provider.ipv4) as getIPFunction[] + const response = await Promise.any(ipv4Functions.map((func) => func({ signal }))) + controller.abort() + return response +} + +export const getMyIPv6 = async (): Promise => { + const controller = new AbortController() + const signal = controller.signal + const ipv6Functions = getMyIPProviders + .filter((provider) => provider.ipv6) + .map((provider) => provider.ipv6) as getIPFunction[] + const response = await Promise.any(ipv6Functions.map((func) => func({ signal }))) + controller.abort() + return response +} diff --git a/src/tools/network/utils/ip/get-my-ip/ip.sb.ts b/src/tools/network/utils/ip/get-my-ip/ip.sb.ts new file mode 100644 index 0000000..7ac4cd5 --- /dev/null +++ b/src/tools/network/utils/ip/get-my-ip/ip.sb.ts @@ -0,0 +1,35 @@ +import { isIPv4, isIPv6 } from 'is-ip' + +export const name = 'ip.sb' + +export async function getMyIPv4(options?: RequestInit): Promise { + const api = 'https://api-ipv4.ip.sb/geoip' + const response = await fetch(api, options) + const data = await response.json() + const ip = data.ip + if (!isIPv4(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export async function getMyIPv6(options?: RequestInit): Promise { + const api = 'https://api-ipv6.ip.sb/geoip' + const response = await fetch(api, options) + const data = await response.json() + const ip = data.ip + if (!isIPv6(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export const getMyIP = { + ipv4: getMyIPv4, + ipv6: getMyIPv6, + name: name +} + +export default getMyIP diff --git a/src/tools/network/utils/ip/get-my-ip/ipify.org.ts b/src/tools/network/utils/ip/get-my-ip/ipify.org.ts new file mode 100644 index 0000000..8ee30a6 --- /dev/null +++ b/src/tools/network/utils/ip/get-my-ip/ipify.org.ts @@ -0,0 +1,35 @@ +import { isIPv4, isIPv6 } from 'is-ip' + +export const name = 'ipify' + +export async function getMyIPv4(options?: RequestInit): Promise { + const api = 'https://api.ipify.org?format=json' + const response = await fetch(api, options) + const data = await response.json() + const ip = data.ip + if (!isIPv4(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export async function getMyIPv6(options?: RequestInit): Promise { + const api = 'https://api64.ipify.org?format=json' + const response = await fetch(api, options) + const data = await response.json() + const ip = data.ip + if (!isIPv6(ip)) { + throw new Error(`Invalid IPv4: ${ip}`) + } + + return ip +} + +export const getMyIP = { + ipv4: getMyIPv4, + ipv6: getMyIPv6, + name: name +} + +export default getMyIP diff --git a/src/tools/network/utils/mac/index.ts b/src/tools/network/utils/mac/index.ts new file mode 100644 index 0000000..21cf576 --- /dev/null +++ b/src/tools/network/utils/mac/index.ts @@ -0,0 +1,2 @@ +export { isValidMacAddress } from './is-valid-mac-address' +export { macAddressToIPv6LinkLocalAddress } from './mac-address-to-ipv6-link-local-address' diff --git a/src/tools/network/utils/mac/is-valid-mac-address.ts b/src/tools/network/utils/mac/is-valid-mac-address.ts new file mode 100644 index 0000000..7eaef0f --- /dev/null +++ b/src/tools/network/utils/mac/is-valid-mac-address.ts @@ -0,0 +1,4 @@ +export function isValidMacAddress(mac: string): boolean { + const regex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/ + return regex.test(mac) +} diff --git a/src/tools/network/utils/mac/mac-address-to-ipv6-link-local-address.ts b/src/tools/network/utils/mac/mac-address-to-ipv6-link-local-address.ts new file mode 100644 index 0000000..19d175c --- /dev/null +++ b/src/tools/network/utils/mac/mac-address-to-ipv6-link-local-address.ts @@ -0,0 +1,30 @@ +import { normalize } from 'cidr-tools' + +export function macAddressToIPv6LinkLocalAddress(mac: string): string { + // Remove any separators and convert MAC to array of bytes + const bytes = mac.replace(/[:-]/g, '').match(/.{1,2}/g) + + if (!bytes || bytes.length !== 6) { + throw new Error('Invalid MAC address format') + } + + // Flip the 7th bit of the first byte (U/L bit) + const firstByte = parseInt(bytes[0], 16) + bytes[0] = ((firstByte ^ 0x02) & 0xff).toString(16).padStart(2, '0') + + // Insert ff:fe in the middle of the MAC address + bytes.splice(3, 0, 'ff', 'fe') + + // Combine the bytes into two-byte pairs for IPv6 format + const ipv6Pairs = [] + for (let i = 0; i < bytes.length; i += 2) { + ipv6Pairs.push(bytes[i] + bytes[i + 1]) + } + + // Join the pairs with colons and format as IPv6 link-local + const linkLocal = `fe80::${ipv6Pairs.join(':')}` + + const normalized = normalize(linkLocal).toString() + + return normalized +} diff --git a/src/tools/network/views/CIDRMergeExcludeView.vue b/src/tools/network/views/CIDRMergeExcludeView.vue new file mode 100644 index 0000000..3f97f6c --- /dev/null +++ b/src/tools/network/views/CIDRMergeExcludeView.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/tools/network/views/CIDRParseView.vue b/src/tools/network/views/CIDRParseView.vue new file mode 100644 index 0000000..483d2ca --- /dev/null +++ b/src/tools/network/views/CIDRParseView.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/tools/network/views/DNSLookupView.vue b/src/tools/network/views/DNSLookupView.vue new file mode 100644 index 0000000..229c2dd --- /dev/null +++ b/src/tools/network/views/DNSLookupView.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/tools/network/views/DOHServersView.vue b/src/tools/network/views/DOHServersView.vue new file mode 100644 index 0000000..6a8c5e7 --- /dev/null +++ b/src/tools/network/views/DOHServersView.vue @@ -0,0 +1,119 @@ + + + diff --git a/src/tools/network/views/DOTServersView.vue b/src/tools/network/views/DOTServersView.vue new file mode 100644 index 0000000..443f494 --- /dev/null +++ b/src/tools/network/views/DOTServersView.vue @@ -0,0 +1,68 @@ + + + diff --git a/src/tools/network/views/IPCIDRNormalizeView.vue b/src/tools/network/views/IPCIDRNormalizeView.vue new file mode 100644 index 0000000..77fdeb9 --- /dev/null +++ b/src/tools/network/views/IPCIDRNormalizeView.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/tools/network/views/IPInfoLookupView.vue b/src/tools/network/views/IPInfoLookupView.vue new file mode 100644 index 0000000..3f278d2 --- /dev/null +++ b/src/tools/network/views/IPInfoLookupView.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/tools/network/views/IPInfoView.vue b/src/tools/network/views/IPInfoView.vue new file mode 100644 index 0000000..aed53ac --- /dev/null +++ b/src/tools/network/views/IPInfoView.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/tools/network/views/IPRangeToCIDRView.vue b/src/tools/network/views/IPRangeToCIDRView.vue new file mode 100644 index 0000000..99a39da --- /dev/null +++ b/src/tools/network/views/IPRangeToCIDRView.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/tools/network/views/MACToIPv6LinkLocalView.vue b/src/tools/network/views/MACToIPv6LinkLocalView.vue new file mode 100644 index 0000000..2192439 --- /dev/null +++ b/src/tools/network/views/MACToIPv6LinkLocalView.vue @@ -0,0 +1,31 @@ + + + diff --git a/src/tools/network/views/MyIPView.vue b/src/tools/network/views/MyIPView.vue new file mode 100644 index 0000000..747bab1 --- /dev/null +++ b/src/tools/network/views/MyIPView.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/tools/network/views/NetworkHomeView.vue b/src/tools/network/views/NetworkHomeView.vue new file mode 100644 index 0000000..eed7c94 --- /dev/null +++ b/src/tools/network/views/NetworkHomeView.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/tools/network/views/PunycodeToolView.vue b/src/tools/network/views/PunycodeToolView.vue new file mode 100644 index 0000000..ac803d2 --- /dev/null +++ b/src/tools/network/views/PunycodeToolView.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/tools/network/views/ReverseIPLookupView.vue b/src/tools/network/views/ReverseIPLookupView.vue new file mode 100644 index 0000000..d7cd383 --- /dev/null +++ b/src/tools/network/views/ReverseIPLookupView.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/tools/network/views/UDPServersView.vue b/src/tools/network/views/UDPServersView.vue new file mode 100644 index 0000000..fcdc226 --- /dev/null +++ b/src/tools/network/views/UDPServersView.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/tools/routes.ts b/src/tools/routes.ts index eaa0191..eb3b577 100644 --- a/src/tools/routes.ts +++ b/src/tools/routes.ts @@ -2,6 +2,7 @@ import type { RouteRecordRaw } from 'vue-router' import { routes as uuidRoutes } from './uuid/routes' import { routes as pdfRoutes } from './pdf/routes' import { routes as faviconRoutes } from './favicon/routes' +import { routes as networkRoutes } from './network/routes' export const routes: RouteRecordRaw[] = [ { @@ -11,5 +12,6 @@ export const routes: RouteRecordRaw[] = [ }, ...uuidRoutes, ...pdfRoutes, - ...faviconRoutes + ...faviconRoutes, + ...networkRoutes ] diff --git a/tsconfig.app.json b/tsconfig.app.json index e14c754..276866b 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,14 +1,24 @@ { "extends": "@vue/tsconfig/tsconfig.dom.json", - "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], - "exclude": ["src/**/__tests__/*"], + "include": [ + "env.d.ts", + "src/**/*", + "src/**/*.vue" + ], + "exclude": [ + "src/**/__tests__/*" + ], "compilerOptions": { "composite": true, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "baseUrl": ".", "paths": { - "@/*": ["./src/*"] - } + "@/*": [ + "./src/*" + ] + }, + "lib": [ + "esnext" + ] } -} +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 5304731..07ba558 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,4 +14,4 @@ "compilerOptions": { "module": "NodeNext" } -} +} \ No newline at end of file diff --git a/tsconfig.node.json b/tsconfig.node.json index f094063..f7b2d6f 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -11,9 +11,10 @@ "composite": true, "noEmit": true, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "module": "ESNext", "moduleResolution": "Bundler", - "types": ["node"] + "types": [ + "node" + ] } -} +} \ No newline at end of file diff --git a/tsconfig.vitest.json b/tsconfig.vitest.json index 571995d..f8391e9 100644 --- a/tsconfig.vitest.json +++ b/tsconfig.vitest.json @@ -4,8 +4,12 @@ "compilerOptions": { "composite": true, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", - - "lib": [], - "types": ["node", "jsdom"] + "lib": [ + "esnext" + ], + "types": [ + "node", + "jsdom" + ] } -} +} \ No newline at end of file