diff --git a/package.json b/package.json index d6620df..4d01168 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kevins-data-toolbox", - "version": "2.3.1", + "version": "2.4.0", "private": true, "dependencies": { "@loadable/component": "^5.15.0", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index d445707..e251c64 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -18,7 +18,8 @@ "categories": { "everything": "Alles", "cryptography": "Kryptografie", - "encodings": "Kodierungen" + "encodings": "Kodierungen", + "networking": "Netzwerke" }, "cryptography": { "common": { @@ -33,27 +34,40 @@ } }, "encodings": { - "common": { - "encoded": "Kodiert", - "decoded": "Dekodiert" - }, - "base64": { - "title": "Base64", - "description": "Base64-Codierung wird oft genutzt um beliebige Binärdaten in pure Zeichenfolgen zu schreiben, indem die Daten auf 64 ASCII-Zeichen projeziert werden." - }, - "numberbase": { - "title": "Basis-Konverter", - "description": "Basis-Konverter konvertiert Zahlen in eine andere Basis (z.B. Binär, Hexadezimal, etc.)", - "decimal": "Dezimal", - "binary": "Binär", - "octal": "Oktal", - "hexadecimal": "Hexadezimal", - "nanWarning": "Die Eingabe ist keine gültige Zahl!" - }, - "urlencode": { - "title": "URL-Kodierung", - "description": "Kodiere und dekodiere URL-kodierte (or Prozent-kodierte) Zeichenfolgen." - } + "common": { + "encoded": "Kodiert", + "decoded": "Dekodiert" + }, + "base64": { + "title": "Base64", + "description": "Base64-Codierung wird oft genutzt um beliebige Binärdaten in pure Zeichenfolgen zu schreiben, indem die Daten auf 64 ASCII-Zeichen projeziert werden." + }, + "numberbase": { + "title": "Basis-Konverter", + "description": "Basis-Konverter konvertiert Zahlen in eine andere Basis (z.B. Binär, Hexadezimal, etc.)", + "decimal": "Dezimal", + "binary": "Binär", + "octal": "Oktal", + "hexadecimal": "Hexadezimal", + "nanWarning": "Die Eingabe ist keine gültige Zahl!" + }, + "urlencode": { + "title": "URL-Kodierung", + "description": "Kodiere und dekodiere URL-kodierte (or Prozent-kodierte) Zeichenfolgen." + } + }, + "networking": { + "ipv4subnetting": { + "title": "IPv4-Subnetzrechner", + "description": "Der IPv4-Subnetzrechner visualisiert IPv4-Subnetze und berechnet die erste und letzte Adresse aus einem Subnetz nach CIDR-Notation." + }, + "common": { + "ipv4addr": "IPv4-Adresse", + "firstAddr": "Erste Adresse", + "lastAddr": "Letzte Adresse", + "subnetMask": "Subnetzmaske", + "binary": "Binär" + } } }, "about": { diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 6dfa661..e98b293 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -18,7 +18,8 @@ "categories": { "everything": "Everything", "cryptography": "Cryptography", - "encodings": "Encodings" + "encodings": "Encodings", + "networking": "Networking" }, "cryptography": { "common": { @@ -33,27 +34,40 @@ } }, "encodings": { - "common": { - "encoded": "Encoded", - "decoded": "Decoded" - }, - "base64": { - "title": "Base64", - "description": "Base64-encoding is commonly used for storing arbitrary binary data in pure strings, by mapping it to 64 ASCII characters." - }, - "numberbase": { - "title": "Base Converter", - "description": "Convert between decimal, binary, octal and hexadecimal numbers.", - "decimal": "Decimal", - "binary": "Binary", - "octal": "Octal", - "hexadecimal": "Hexadecimal", - "nanWarning": "The input is not a valid number!" - }, - "urlencode": { - "title": "URL-Encoding", - "description": "Encode and decode strings to and from URL-encoding (or Percent-encoding)." - } + "common": { + "encoded": "Encoded", + "decoded": "Decoded" + }, + "base64": { + "title": "Base64", + "description": "Base64-encoding is commonly used for storing arbitrary binary data in pure strings, by mapping it to 64 ASCII characters." + }, + "numberbase": { + "title": "Base Converter", + "description": "Convert between decimal, binary, octal and hexadecimal numbers.", + "decimal": "Decimal", + "binary": "Binary", + "octal": "Octal", + "hexadecimal": "Hexadecimal", + "nanWarning": "The input is not a valid number!" + }, + "urlencode": { + "title": "URL-Encoding", + "description": "Encode and decode strings to and from URL-encoding (or Percent-encoding)." + } + }, + "networking": { + "ipv4subnetting": { + "title": "IPv4 Subnetter", + "description": "The IPv4 subnetter visualizes IPv4 subnets and calculates the first and last address from a CIDR-notated subnet." + }, + "common": { + "ipv4addr": "IPv4 address", + "firstAddr": "First address", + "lastAddr": "Last address", + "subnetMask": "Subnet mask", + "binary": "Binary" + } } }, "about": { diff --git a/src/pages/Tools.tsx b/src/pages/Tools.tsx index 977df8b..566dedf 100644 --- a/src/pages/Tools.tsx +++ b/src/pages/Tools.tsx @@ -1,6 +1,6 @@ import React from "react"; import { useParams } from "react-router-dom"; -import {Lock, Binary, List} from 'lucide-react'; +import {Lock, Binary, List, Network} from 'lucide-react'; import { useTranslation } from 'react-i18next'; @@ -31,6 +31,7 @@ const ToolsPage = () => { } small={true} highlight={category == null} /> } small={true} highlight={category === "cryptography"} /> } small={true} highlight={category === "encodings"} /> + } small={true} highlight={category === "networking"} /> {/**/} diff --git a/src/tools/ToolLoader.tsx b/src/tools/ToolLoader.tsx index c2f9ed4..06f2c7d 100644 --- a/src/tools/ToolLoader.tsx +++ b/src/tools/ToolLoader.tsx @@ -15,6 +15,7 @@ const RotTool = prerenderedLoadable(() => import('./cyphers_and_cryptography/rot const Base64Tool = prerenderedLoadable(() => import('./encodings/base64/Base64Tool')); const NumberBaseTool = prerenderedLoadable(() => import('./encodings/numberbase/NumberBaseTool')); const URLEncodeTool = prerenderedLoadable(() => import('./encodings/urlencode/URLEncodeTool')); +const IPv4SubnettingTool = prerenderedLoadable(() => import('./networking/ipv4subnetting/IPv4SubnettingTool')); type ErrorBoundaryProps = { resetFunction: () => void @@ -76,6 +77,9 @@ const ToolLoader = () => { case "urlencode": return ; + case "ipv4subnetting": + return ; + default: return ; } diff --git a/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.module.scss b/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.module.scss new file mode 100644 index 0000000..3da84d9 --- /dev/null +++ b/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.module.scss @@ -0,0 +1,44 @@ +@import "../../../common"; + +.fakeLabel { + display: block; + + margin-top: $layoutPadding; + margin-bottom: (-$layoutPadding + 5px); +} + +.fakeField { + @include boxStyle; + + display: block; + + width: 368px; + opacity: .75; + background: rgba(0, 0, 0, .25); + + @media(prefers-color-scheme: dark) { + background: rgba(255, 255, 255, .025); + } +} + +.combiInput { + @include boxStyle; + + display: flex; + align-items: baseline; + padding: 0; + width: fit-content; + overflow: hidden; + + + input { + @include boxStyle; + margin: 0; + width: 70px; + border-radius: 0; + border: none; + background: transparent !important; + box-shadow: none; + text-align: center; + } +} \ No newline at end of file diff --git a/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.module.scss.d.ts b/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.module.scss.d.ts new file mode 100644 index 0000000..06ae574 --- /dev/null +++ b/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.module.scss.d.ts @@ -0,0 +1,7 @@ +export const center: string; +export const combiInput: string; +export const fakeField: string; +export const fakeLabel: string; +export const flexList: string; +export const layoutBox: string; +export const title: string; diff --git a/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.tsx b/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.tsx new file mode 100644 index 0000000..bdb0a26 --- /dev/null +++ b/src/tools/networking/ipv4subnetting/IPv4SubnettingTool.tsx @@ -0,0 +1,183 @@ +import React, { useEffect, useRef, useState } from "react"; +import { useTranslation, Trans } from "react-i18next"; +import * as styles from "./IPv4SubnettingTool.module.scss" +import { Helmet } from "react-helmet"; + +const intToIPv4String = (ip: number): string => { + let out = "" + + for (let group = 0; group < 4; group++) { + let groupBin = ip & 0xff + + out = groupBin.toString()+"."+out + + ip >>= 8; + } + + return out.substring(0, out.length-1) +} + +const IPv4SubnettingTool = () => { + const { t } = useTranslation(); + + let [ipPart1, setIPPart1] = useState(""); + let [ipPart2, setIPPart2] = useState(""); + let [ipPart3, setIPPart3] = useState(""); + let [ipPart4, setIPPart4] = useState(""); + let [subnet, setSubnet] = useState(""); + + let ipInput1 = useRef(null) + let ipInput2 = useRef(null) + let ipInput3 = useRef(null) + let ipInput4 = useRef(null) + let subnetInput = useRef(null) + + let [ipBinary, setIpBinary] = useState(undefined); + let [subnetNumber, setSubnetMaskNumber] = useState(undefined); + let [subnetBinary, setSubnetMaskBinaryString] = useState(undefined); + let [subnetMask, setSubnetMask] = useState(undefined); + let [firstAddr, setFirstAddr] = useState(undefined); + let [lastAddr, setLastAddr] = useState(undefined); + + useEffect(() => { + let myIpPart1 = ipPart1; + let myIpPart2 = ipPart2; + let myIpPart3 = ipPart3; + let myIpPart4 = ipPart4; + let mySubnet = subnet; + + + if(myIpPart1.indexOf(".") !== -1) { + let split = myIpPart1.split(".") + myIpPart1 = split.shift()! + myIpPart2 = split.join(".") + ipInput2.current?.focus() + } + if(myIpPart2.indexOf(".") !== -1) { + let split = myIpPart2.split(".") + myIpPart2 = split.shift()! + myIpPart3 = split.join(".") + ipInput3.current?.focus() + } + if(myIpPart3.indexOf(".") !== -1) { + let split = myIpPart3.split(".") + myIpPart3 = split.shift()! + myIpPart4 = split.join(".") + ipInput4.current?.focus() + } + if(myIpPart4.indexOf("/") !== -1) { + let split = myIpPart4.split("/") + myIpPart4 = split.shift()! + mySubnet = split.join("/") + subnetInput.current?.focus() + } + if(myIpPart4.indexOf(".") !== -1) { + let split = myIpPart4.split(".") + myIpPart4 = split.shift()! + mySubnet = split.join(".") + subnetInput.current?.focus() + } + + + let ipPart1Num = parseInt(myIpPart1); + let ipPart2Num = parseInt(myIpPart2); + let ipPart3Num = parseInt(myIpPart3); + let ipPart4Num = parseInt(myIpPart4); + let subnetNum = parseInt(mySubnet); + + + + + if(!isNaN(ipPart1Num)) setIPPart1(ipPart1Num.toString()); + if(!isNaN(ipPart2Num)) setIPPart2(ipPart2Num.toString()); + if(!isNaN(ipPart3Num)) setIPPart3(ipPart3Num.toString()); + if(!isNaN(ipPart4Num)) setIPPart4(ipPart4Num.toString()); + if(!isNaN(subnetNum)) setSubnet(subnetNum.toString()); + + setIpBinary(undefined); + setFirstAddr(undefined); + setLastAddr(undefined); + setSubnetMaskBinaryString(undefined); + setSubnetMaskNumber(undefined); + setSubnetMask(undefined); + + if(!isNaN(ipPart1Num) && !isNaN(ipPart2Num) && !isNaN(ipPart3Num) && !isNaN(ipPart4Num)) { + let ipBinary = ipPart1Num * Math.pow(2, 24) + ipPart2Num * Math.pow(2, 16) + ipPart3Num * Math.pow(2, 8) + ipPart4Num; + setIpBinary(ipBinary); + + if (!isNaN(subnetNum)) { + setSubnetMaskNumber(subnetNum); + + + let subnetMaskBinary = new Uint32Array(1); + + for (let index = 0; index < 32 - subnetNum; index++) { + subnetMaskBinary[0] = (subnetMaskBinary[0] << 1) | 0b1; + } + + subnetMaskBinary[0] ^= 0xffffffff + + setSubnetMaskBinaryString(subnetMaskBinary[0].toString(2)) + setSubnetMask(subnetMaskBinary[0]) + + setFirstAddr(ipBinary & subnetMaskBinary[0]) + setLastAddr(ipBinary | (subnetMaskBinary[0] ^ 0xffffffff)) + } + } + }, [ipPart1, ipPart2, ipPart3, ipPart4, subnet]) + + return ( +
+ + {t("tools.networking.ipv4subnetting.title")} | {t("site.title")} + + +
+

{t("tools.networking.ipv4subnetting.title")}

+ +

+ + +
+ {setIPPart1(e.currentTarget.value); if((e.currentTarget.value.length >= 3 && e.currentTarget.value.length > ipPart1.length) || e.currentTarget.value[e.currentTarget.value.length - 1] === ".") ipInput2.current?.focus()}} value={ipPart1} /> + . + {setIPPart2(e.currentTarget.value); if((e.currentTarget.value.length >= 3 && e.currentTarget.value.length > ipPart2.length) || e.currentTarget.value[e.currentTarget.value.length - 1] === ".") ipInput3.current?.focus()}} value={ipPart2} /> + . + {setIPPart3(e.currentTarget.value); if((e.currentTarget.value.length >= 3 && e.currentTarget.value.length > ipPart3.length) || e.currentTarget.value[e.currentTarget.value.length - 1] === ".") ipInput4.current?.focus()}} value={ipPart3} /> + . + {setIPPart4(e.currentTarget.value); if((e.currentTarget.value.length >= 3 && e.currentTarget.value.length > ipPart4.length) || e.currentTarget.value[e.currentTarget.value.length - 1] === "/" || e.currentTarget.value[e.currentTarget.value.length - 1] === ".") subnetInput.current?.focus()}} value={ipPart4} /> + / + {setSubnet(e.currentTarget.value);}} value={subnet} /> +
+ + {t("tools.networking.common.subnetMask")} + {subnetMask ? intToIPv4String(subnetMask) : "???"} + + {t("tools.networking.common.firstAddr")} + {firstAddr ? intToIPv4String(firstAddr) : "???"} + + {t("tools.networking.common.lastAddr")} + {lastAddr ? intToIPv4String(lastAddr) : "???"} + + {t("tools.networking.common.ipv4addr")} ({t("tools.networking.common.binary")}) + + {ipBinary ? ipBinary.toString(2).padStart(32, "0").split("").map((bit, index) => { + return = (subnetNumber||32) ? "red" : undefined}} key={"bit"+index}>{bit} + }) : "???"} + + + {t("tools.networking.common.subnetMask")} ({t("tools.networking.common.binary")}) + + {subnetBinary ? subnetBinary.padStart(32, "0").split("").map((bit, index) => { + return {bit} + }) : "???"} + + + + +
+
+ ) +} + +export default IPv4SubnettingTool; \ No newline at end of file diff --git a/src/tools/tools.ts b/src/tools/tools.ts index be3472f..eacb4d5 100644 --- a/src/tools/tools.ts +++ b/src/tools/tools.ts @@ -1,4 +1,4 @@ -import {Smile, PlusSquare, Binary, Hash, Globe} from "lucide-react" +import {Smile, PlusSquare, Binary, Hash, Globe, Network} from "lucide-react" const tools = [ { @@ -44,6 +44,15 @@ const tools = [ "category": "encodings", "hidden": false, "keywords": "URL, urlencode, urldecode, URI, uriencode, uridecode, encoding, decoding, encoder, decoder, tool" + }, + { + "name": "tools.networking.ipv4subnetting.title", + "external": false, + "urlname": "ipv4subnetting", + "icon": Network, + "category": "networking", + "hidden": false, + "keywords": "subnetting, networking, ipv4, ip4, ip, internet protocol, subnet, subnetter, netmask, tool" } ]