From 68e09fbbafbdd2da0190b40235d0baefec2c0abf Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sun, 26 Oct 2025 08:55:15 +0000 Subject: [PATCH 01/14] Creates lang switching functionality --- package-lock.json | 63 +- package.json | 4 + scripts/check-translations.ts | 309 ++++ scripts/find-hardcoded-text.ts | 246 +++ .../components/furniture/SettingsPanel.svelte | 69 +- .../components/tools/SubnetCalculator.svelte | 31 +- src/lib/i18n/index.ts | 186 +++ src/lib/i18n/lang-detector.ts | 119 ++ src/lib/i18n/supported-languages.ts | 80 + src/lib/i18n/translations/de/common.json | 120 ++ src/lib/i18n/translations/de/nav.json | 59 + src/lib/i18n/translations/de/settings.json | 94 ++ src/lib/i18n/translations/de/tools.json | 75 + src/lib/i18n/translations/en/common.json | 120 ++ src/lib/i18n/translations/en/nav.json | 59 + src/lib/i18n/translations/en/settings.json | 94 ++ src/lib/i18n/translations/en/tools.json | 75 + src/lib/stores/language.ts | 200 +++ src/params/lang.ts | 20 + src/routes/+layout.svelte | 8 + src/routes/[lang]/+layout.svelte | 21 + .../[lang]/about/(sections)/api/+page.svelte | 5 + .../(sections)/attributions/+page.svelte | 5 + .../about/(sections)/author/+page.svelte | 5 + .../about/(sections)/building/+page.svelte | 5 + .../about/(sections)/deploying/+page.svelte | 5 + .../(sections)/self-hosting/+page.svelte | 5 + .../about/(sections)/support/+page.svelte | 5 + src/routes/[lang]/about/+layout.svelte | 23 + src/routes/[lang]/about/+page.svelte | 78 + src/routes/[lang]/about/legal/+layout.svelte | 91 ++ src/routes/[lang]/about/legal/+page.svelte | 176 +++ .../about/legal/accessibility/+page.svelte | 78 + .../[lang]/about/legal/community/+page.svelte | 51 + .../[lang]/about/legal/cookies/+page.svelte | 39 + .../[lang]/about/legal/license/+page.svelte | 5 + .../[lang]/about/legal/privacy/+page.svelte | 142 ++ .../[lang]/about/legal/security/+page.svelte | 50 + src/routes/[lang]/bookmarks/+page.svelte | 5 + src/routes/[lang]/cidr/+page.svelte | 515 ++++++ src/routes/[lang]/cidr/alignment/+page.svelte | 7 + src/routes/[lang]/cidr/allocator/+page.svelte | 5 + src/routes/[lang]/cidr/compare/+page.svelte | 5 + .../[lang]/cidr/deaggregate/+page.svelte | 5 + src/routes/[lang]/cidr/gaps/+page.svelte | 5 + .../[lang]/cidr/mask-converter/+layout.svelte | 524 +++++++ .../[lang]/cidr/mask-converter/+page.svelte | 0 .../cidr-to-subnet-mask/+page.svelte | 140 ++ .../subnet-mask-to-cidr/+page.svelte | 87 ++ .../[lang]/cidr/next-available/+page.svelte | 7 + .../[lang]/cidr/range-to-cidr/+page.svelte | 733 +++++++++ .../[lang]/cidr/set-operations/+layout.svelte | 270 ++++ .../[lang]/cidr/set-operations/+page.svelte | 54 + .../cidr/set-operations/contains/+page.svelte | 52 + .../cidr/set-operations/diff/+page.svelte | 52 + .../cidr/set-operations/overlap/+page.svelte | 52 + src/routes/[lang]/cidr/split/+page.svelte | 299 ++++ src/routes/[lang]/cidr/summarize/+page.svelte | 302 ++++ .../[lang]/cidr/wildcard-mask/+page.svelte | 7 + src/routes/[lang]/dhcp/+page.svelte | 41 + .../dhcp/calculators/lease-time/+page.svelte | 5 + .../[lang]/dhcp/kea-isc-snippets/+page.svelte | 11 + .../dhcp/tools/fingerprinting/+page.svelte | 17 + .../dhcp/v4/options/freeform-tlv/+page.svelte | 5 + .../option119-domain-search/+page.svelte | 5 + .../option121-classless-routes/+page.svelte | 5 + .../option150-tftp-server/+page.svelte | 5 + .../option3-default-gateway/+page.svelte | 5 + .../option43-vendor-specific/+page.svelte | 11 + .../options/option51-lease-time/+page.svelte | 5 + .../option60-vendor-class/+page.svelte | 11 + .../v4/options/option61-clientid/+page.svelte | 5 + .../options/option82-relay-agent/+page.svelte | 5 + .../v4/options/options6-15-dns/+page.svelte | 5 + .../v4/options/pxe-boot-profile/+page.svelte | 5 + .../v6/identity/duid-generator/+page.svelte | 5 + .../v6/identity/iaid-calculator/+page.svelte | 5 + .../v6/options/option23-24-dns/+page.svelte | 5 + .../option25-prefix-delegation/+page.svelte | 17 + .../v6/options/option39-fqdn/+page.svelte | 17 + src/routes/[lang]/diagnostics/+layout.svelte | 125 ++ src/routes/[lang]/diagnostics/+page.svelte | 36 + .../[lang]/diagnostics/dns/+page.svelte | 39 + .../diagnostics/dns/axfr-tester/+page.svelte | 1149 ++++++++++++++ .../dns/blacklist-checker/+page.svelte | 1171 ++++++++++++++ .../dns/caa-effective/+page.svelte | 833 ++++++++++ .../diagnostics/dns/dmarc-check/+page.svelte | 785 ++++++++++ .../dns/dnssec-adflag/+page.svelte | 461 ++++++ .../dns/dnssec-validation-chain/+page.svelte | 786 ++++++++++ .../diagnostics/dns/glue-check/+page.svelte | 638 ++++++++ .../diagnostics/dns/lookup/+page.svelte | 326 ++++ .../diagnostics/dns/ns-soa-check/+page.svelte | 728 +++++++++ .../diagnostics/dns/propagation/+page.svelte | 560 +++++++ .../dns/query-performance/+page.svelte | 1225 +++++++++++++++ .../dns/reverse-lookup/+page.svelte | 516 ++++++ .../diagnostics/dns/soa-serial/+page.svelte | 642 ++++++++ .../dns/spf-evaluator/+page.svelte | 648 ++++++++ .../diagnostics/dns/spf-flatten/+page.svelte | 556 +++++++ .../[lang]/diagnostics/dns/trace/+page.svelte | 543 +++++++ .../email/dmarc-check/+page.svelte | 774 +++++++++ .../email/greylist-tester/+page.svelte | 765 +++++++++ .../email/mail-tls-check/+page.svelte | 616 ++++++++ .../diagnostics/email/mx-health/+page.svelte | 725 +++++++++ .../diagnostics/email/spf-check/+page.svelte | 829 ++++++++++ .../diagnostics/http/compression/+page.svelte | 466 ++++++ .../http/cookie-security/+page.svelte | 712 +++++++++ .../diagnostics/http/cors-check/+page.svelte | 531 +++++++ .../diagnostics/http/headers/+page.svelte | 383 +++++ .../[lang]/diagnostics/http/perf/+page.svelte | 578 +++++++ .../http/redirect-trace/+page.svelte | 525 +++++++ .../diagnostics/http/security/+page.svelte | 470 ++++++ .../network/asn-geo-lookup/+page.svelte | 625 ++++++++ .../network/bgp-route-lookup/+page.svelte | 691 ++++++++ .../network/http-ping/+page.svelte | 682 ++++++++ .../ipv6-connectivity-checker/+page.svelte | 412 +++++ .../network/tcp-port-check/+page.svelte | 532 +++++++ .../[lang]/diagnostics/rdap/asn/+page.svelte | 379 +++++ .../diagnostics/rdap/domain/+page.svelte | 356 +++++ .../[lang]/diagnostics/rdap/ip/+page.svelte | 379 +++++ .../[lang]/diagnostics/tls/alpn/+page.svelte | 616 ++++++++ .../diagnostics/tls/banner/+page.svelte | 559 +++++++ .../diagnostics/tls/certificate/+page.svelte | 490 ++++++ .../tls/cipher-presets/+page.svelte | 581 +++++++ .../tls/ct-log-search/+page.svelte | 812 ++++++++++ .../tls/handshake-analyzer/+page.svelte | 587 +++++++ .../tls/ocsp-stapling/+page.svelte | 613 ++++++++ .../diagnostics/tls/versions/+page.svelte | 537 +++++++ src/routes/[lang]/dns/+page.svelte | 38 + .../dnssec/cds-cdnskey-builder/+page.svelte | 5 + .../[lang]/dns/dnssec/dnskey-tag/+page.svelte | 5 + .../dns/dnssec/ds-generator/+page.svelte | 5 + .../[lang]/dns/dnssec/nsec3-hash/+page.svelte | 5 + .../dns/dnssec/rrsig-planner/+page.svelte | 5 + .../dns/edns-size-estimator/+page.svelte | 5 + src/routes/[lang]/dns/generators/+page.svelte | 38 + .../dns/generators/a-aaaa-bulk/+page.svelte | 9 + .../dns/generators/caa-builder/+page.svelte | 5 + .../dns/generators/cname-builder/+page.svelte | 9 + .../dns/generators/dkim-keygen/+page.svelte | 5 + .../dns/generators/dmarc-builder/+page.svelte | 5 + .../dns/generators/idn-punycode/+page.svelte | 5 + .../dns/generators/loc-builder/+page.svelte | 5 + .../dns/generators/mx-planner/+page.svelte | 9 + .../dns/generators/naptr-builder/+page.svelte | 5 + .../dns/generators/ptr-generator/+page.svelte | 5 + .../dns/generators/rp-builder/+page.svelte | 5 + .../dns/generators/spf-builder/+page.svelte | 9 + .../dns/generators/srv-builder/+page.svelte | 9 + .../generators/sshfp-generator/+page.svelte | 5 + .../svcb-https-builder/+page.svelte | 5 + .../generators/tlsa-generator/+page.svelte | 5 + .../dns/generators/txt-escape/+page.svelte | 9 + .../[lang]/dns/label-normalizer/+page.svelte | 5 + .../[lang]/dns/record-validator/+page.svelte | 5 + .../dns/reverse/ptr-generator/+page.svelte | 5 + .../reverse/ptr-sweep-planner/+page.svelte | 5 + .../dns/reverse/reverse-zones/+page.svelte | 5 + .../dns/reverse/zone-generator/+page.svelte | 5 + .../[lang]/dns/ttl-calculator/+page.svelte | 5 + src/routes/[lang]/dns/zone/+page.svelte | 407 +++++ src/routes/[lang]/dns/zone/diff/+page.svelte | 5 + .../[lang]/dns/zone/linter/+page.svelte | 5 + .../dns/zone/name-length-checker/+page.svelte | 5 + src/routes/[lang]/dns/zone/stats/+page.svelte | 5 + .../[lang]/ip-address-convertor/+page.svelte | 36 + .../distance/+page.svelte | 7 + .../enumerate/+page.svelte | 5 + .../ip-address-convertor/eui64/+page.svelte | 7 + .../families/+layout.svelte | 183 +++ .../families/ipv4-to-ipv6/+page.svelte | 9 + .../families/ipv6-to-ipv4/+page.svelte | 9 + .../ipv6/nat64/+page.svelte | 5 + .../ipv6/solicited-node/+page.svelte | 5 + .../ipv6/teredo/+page.svelte | 5 + .../mac-address/+page.svelte | 1383 +++++++++++++++++ .../notation/+layout.svelte | 286 ++++ .../notation/ipv6-compress/+page.svelte | 45 + .../notation/ipv6-expand/+page.svelte | 45 + .../notation/normalize/+page.svelte | 5 + .../notation/zone-id/+page.svelte | 5 + .../ip-address-convertor/nth-ip/+page.svelte | 7 + .../ip-address-convertor/random/+page.svelte | 7 + .../ip-address-convertor/regex/+page.svelte | 241 +++ .../representations/+page.svelte | 7 + .../ula-generator/+page.svelte | 5 + .../validator/+page.svelte | 5 + src/routes/[lang]/offline/+page.server.ts | 16 + src/routes/[lang]/offline/+page.svelte | 106 ++ src/routes/[lang]/reference/+layout.svelte | 128 ++ src/routes/[lang]/reference/+page.svelte | 32 + .../[lang]/reference/arp-vs-ndp/+page.svelte | 211 +++ src/routes/[lang]/reference/asn/+page.svelte | 215 +++ .../[lang]/reference/cgnat/+page.svelte | 228 +++ src/routes/[lang]/reference/cidr/+page.svelte | 99 ++ .../reference/common-subnets/+page.svelte | 181 +++ src/routes/[lang]/reference/icmp/+page.svelte | 176 +++ .../reference/ipv6-address-types/+page.svelte | 134 ++ .../reference/ipv6-embedded-ipv4/+page.svelte | 157 ++ .../ipv6-prefix-lengths/+page.svelte | 143 ++ .../ipv6-privacy-addresses/+page.svelte | 312 ++++ .../reference/link-local-apipa/+page.svelte | 242 +++ .../[lang]/reference/mtu-mss/+page.svelte | 216 +++ .../[lang]/reference/multicast/+page.svelte | 189 +++ .../reference/network-classes/+page.svelte | 144 ++ .../[lang]/reference/ports/+page.svelte | 165 ++ .../private-vs-public-ip/+page.svelte | 234 +++ .../reference/reserved-ranges/+page.svelte | 99 ++ .../[lang]/reference/reverse-dns/+page.svelte | 192 +++ .../reference/reverse-zones/+page.svelte | 276 ++++ .../reference/special-use-ipv4/+page.svelte | 107 ++ .../reference/supernetting/+page.svelte | 140 ++ src/routes/[lang]/reference/vlsm/+page.svelte | 101 ++ .../reference/wildcard-masks/+page.svelte | 203 +++ src/routes/[lang]/search/+page.svelte | 5 + src/routes/[lang]/settings/+page.svelte | 101 ++ src/routes/[lang]/subnetting/+page.svelte | 494 ++++++ .../ipv4-subnet-calculator/+page.svelte | 7 + .../ipv6-subnet-calculator/+page.svelte | 217 +++ .../[lang]/subnetting/planner/+page.svelte | 5 + .../supernet-calculator/+page.svelte | 190 +++ .../subnetting/vlsm-calculator/+page.svelte | 7 + src/routes/settings/+page.svelte | 9 +- 222 files changed, 42553 insertions(+), 50 deletions(-) create mode 100644 scripts/check-translations.ts create mode 100644 scripts/find-hardcoded-text.ts create mode 100644 src/lib/i18n/index.ts create mode 100644 src/lib/i18n/lang-detector.ts create mode 100644 src/lib/i18n/supported-languages.ts create mode 100644 src/lib/i18n/translations/de/common.json create mode 100644 src/lib/i18n/translations/de/nav.json create mode 100644 src/lib/i18n/translations/de/settings.json create mode 100644 src/lib/i18n/translations/de/tools.json create mode 100644 src/lib/i18n/translations/en/common.json create mode 100644 src/lib/i18n/translations/en/nav.json create mode 100644 src/lib/i18n/translations/en/settings.json create mode 100644 src/lib/i18n/translations/en/tools.json create mode 100644 src/lib/stores/language.ts create mode 100644 src/params/lang.ts create mode 100644 src/routes/[lang]/+layout.svelte create mode 100644 src/routes/[lang]/about/(sections)/api/+page.svelte create mode 100644 src/routes/[lang]/about/(sections)/attributions/+page.svelte create mode 100644 src/routes/[lang]/about/(sections)/author/+page.svelte create mode 100644 src/routes/[lang]/about/(sections)/building/+page.svelte create mode 100644 src/routes/[lang]/about/(sections)/deploying/+page.svelte create mode 100644 src/routes/[lang]/about/(sections)/self-hosting/+page.svelte create mode 100644 src/routes/[lang]/about/(sections)/support/+page.svelte create mode 100644 src/routes/[lang]/about/+layout.svelte create mode 100644 src/routes/[lang]/about/+page.svelte create mode 100644 src/routes/[lang]/about/legal/+layout.svelte create mode 100644 src/routes/[lang]/about/legal/+page.svelte create mode 100644 src/routes/[lang]/about/legal/accessibility/+page.svelte create mode 100644 src/routes/[lang]/about/legal/community/+page.svelte create mode 100644 src/routes/[lang]/about/legal/cookies/+page.svelte create mode 100644 src/routes/[lang]/about/legal/license/+page.svelte create mode 100644 src/routes/[lang]/about/legal/privacy/+page.svelte create mode 100644 src/routes/[lang]/about/legal/security/+page.svelte create mode 100644 src/routes/[lang]/bookmarks/+page.svelte create mode 100644 src/routes/[lang]/cidr/+page.svelte create mode 100644 src/routes/[lang]/cidr/alignment/+page.svelte create mode 100644 src/routes/[lang]/cidr/allocator/+page.svelte create mode 100644 src/routes/[lang]/cidr/compare/+page.svelte create mode 100644 src/routes/[lang]/cidr/deaggregate/+page.svelte create mode 100644 src/routes/[lang]/cidr/gaps/+page.svelte create mode 100644 src/routes/[lang]/cidr/mask-converter/+layout.svelte create mode 100644 src/routes/[lang]/cidr/mask-converter/+page.svelte create mode 100644 src/routes/[lang]/cidr/mask-converter/cidr-to-subnet-mask/+page.svelte create mode 100644 src/routes/[lang]/cidr/mask-converter/subnet-mask-to-cidr/+page.svelte create mode 100644 src/routes/[lang]/cidr/next-available/+page.svelte create mode 100644 src/routes/[lang]/cidr/range-to-cidr/+page.svelte create mode 100644 src/routes/[lang]/cidr/set-operations/+layout.svelte create mode 100644 src/routes/[lang]/cidr/set-operations/+page.svelte create mode 100644 src/routes/[lang]/cidr/set-operations/contains/+page.svelte create mode 100644 src/routes/[lang]/cidr/set-operations/diff/+page.svelte create mode 100644 src/routes/[lang]/cidr/set-operations/overlap/+page.svelte create mode 100644 src/routes/[lang]/cidr/split/+page.svelte create mode 100644 src/routes/[lang]/cidr/summarize/+page.svelte create mode 100644 src/routes/[lang]/cidr/wildcard-mask/+page.svelte create mode 100644 src/routes/[lang]/dhcp/+page.svelte create mode 100644 src/routes/[lang]/dhcp/calculators/lease-time/+page.svelte create mode 100644 src/routes/[lang]/dhcp/kea-isc-snippets/+page.svelte create mode 100644 src/routes/[lang]/dhcp/tools/fingerprinting/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/freeform-tlv/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option119-domain-search/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option121-classless-routes/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option150-tftp-server/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option3-default-gateway/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option43-vendor-specific/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option51-lease-time/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option60-vendor-class/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option61-clientid/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/option82-relay-agent/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/options6-15-dns/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v4/options/pxe-boot-profile/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v6/identity/duid-generator/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v6/identity/iaid-calculator/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v6/options/option23-24-dns/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v6/options/option25-prefix-delegation/+page.svelte create mode 100644 src/routes/[lang]/dhcp/v6/options/option39-fqdn/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/+layout.svelte create mode 100644 src/routes/[lang]/diagnostics/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/axfr-tester/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/blacklist-checker/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/caa-effective/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/dmarc-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/dnssec-adflag/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/dnssec-validation-chain/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/glue-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/lookup/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/ns-soa-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/propagation/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/query-performance/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/reverse-lookup/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/soa-serial/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/spf-evaluator/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/spf-flatten/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/dns/trace/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/email/dmarc-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/email/greylist-tester/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/email/mail-tls-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/email/mx-health/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/email/spf-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/http/compression/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/http/cookie-security/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/http/cors-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/http/headers/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/http/perf/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/http/redirect-trace/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/http/security/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/network/asn-geo-lookup/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/network/bgp-route-lookup/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/network/http-ping/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/network/ipv6-connectivity-checker/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/network/tcp-port-check/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/rdap/asn/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/rdap/domain/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/rdap/ip/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/alpn/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/banner/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/certificate/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/cipher-presets/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/ct-log-search/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/handshake-analyzer/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/ocsp-stapling/+page.svelte create mode 100644 src/routes/[lang]/diagnostics/tls/versions/+page.svelte create mode 100644 src/routes/[lang]/dns/+page.svelte create mode 100644 src/routes/[lang]/dns/dnssec/cds-cdnskey-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/dnssec/dnskey-tag/+page.svelte create mode 100644 src/routes/[lang]/dns/dnssec/ds-generator/+page.svelte create mode 100644 src/routes/[lang]/dns/dnssec/nsec3-hash/+page.svelte create mode 100644 src/routes/[lang]/dns/dnssec/rrsig-planner/+page.svelte create mode 100644 src/routes/[lang]/dns/edns-size-estimator/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/a-aaaa-bulk/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/caa-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/cname-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/dkim-keygen/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/dmarc-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/idn-punycode/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/loc-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/mx-planner/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/naptr-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/ptr-generator/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/rp-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/spf-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/srv-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/sshfp-generator/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/svcb-https-builder/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/tlsa-generator/+page.svelte create mode 100644 src/routes/[lang]/dns/generators/txt-escape/+page.svelte create mode 100644 src/routes/[lang]/dns/label-normalizer/+page.svelte create mode 100644 src/routes/[lang]/dns/record-validator/+page.svelte create mode 100644 src/routes/[lang]/dns/reverse/ptr-generator/+page.svelte create mode 100644 src/routes/[lang]/dns/reverse/ptr-sweep-planner/+page.svelte create mode 100644 src/routes/[lang]/dns/reverse/reverse-zones/+page.svelte create mode 100644 src/routes/[lang]/dns/reverse/zone-generator/+page.svelte create mode 100644 src/routes/[lang]/dns/ttl-calculator/+page.svelte create mode 100644 src/routes/[lang]/dns/zone/+page.svelte create mode 100644 src/routes/[lang]/dns/zone/diff/+page.svelte create mode 100644 src/routes/[lang]/dns/zone/linter/+page.svelte create mode 100644 src/routes/[lang]/dns/zone/name-length-checker/+page.svelte create mode 100644 src/routes/[lang]/dns/zone/stats/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/distance/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/enumerate/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/eui64/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/families/+layout.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/families/ipv4-to-ipv6/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/families/ipv6-to-ipv4/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/ipv6/nat64/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/ipv6/solicited-node/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/ipv6/teredo/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/mac-address/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/notation/+layout.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/notation/ipv6-compress/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/notation/ipv6-expand/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/notation/normalize/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/notation/zone-id/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/nth-ip/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/random/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/regex/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/representations/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/ula-generator/+page.svelte create mode 100644 src/routes/[lang]/ip-address-convertor/validator/+page.svelte create mode 100644 src/routes/[lang]/offline/+page.server.ts create mode 100644 src/routes/[lang]/offline/+page.svelte create mode 100644 src/routes/[lang]/reference/+layout.svelte create mode 100644 src/routes/[lang]/reference/+page.svelte create mode 100644 src/routes/[lang]/reference/arp-vs-ndp/+page.svelte create mode 100644 src/routes/[lang]/reference/asn/+page.svelte create mode 100644 src/routes/[lang]/reference/cgnat/+page.svelte create mode 100644 src/routes/[lang]/reference/cidr/+page.svelte create mode 100644 src/routes/[lang]/reference/common-subnets/+page.svelte create mode 100644 src/routes/[lang]/reference/icmp/+page.svelte create mode 100644 src/routes/[lang]/reference/ipv6-address-types/+page.svelte create mode 100644 src/routes/[lang]/reference/ipv6-embedded-ipv4/+page.svelte create mode 100644 src/routes/[lang]/reference/ipv6-prefix-lengths/+page.svelte create mode 100644 src/routes/[lang]/reference/ipv6-privacy-addresses/+page.svelte create mode 100644 src/routes/[lang]/reference/link-local-apipa/+page.svelte create mode 100644 src/routes/[lang]/reference/mtu-mss/+page.svelte create mode 100644 src/routes/[lang]/reference/multicast/+page.svelte create mode 100644 src/routes/[lang]/reference/network-classes/+page.svelte create mode 100644 src/routes/[lang]/reference/ports/+page.svelte create mode 100644 src/routes/[lang]/reference/private-vs-public-ip/+page.svelte create mode 100644 src/routes/[lang]/reference/reserved-ranges/+page.svelte create mode 100644 src/routes/[lang]/reference/reverse-dns/+page.svelte create mode 100644 src/routes/[lang]/reference/reverse-zones/+page.svelte create mode 100644 src/routes/[lang]/reference/special-use-ipv4/+page.svelte create mode 100644 src/routes/[lang]/reference/supernetting/+page.svelte create mode 100644 src/routes/[lang]/reference/vlsm/+page.svelte create mode 100644 src/routes/[lang]/reference/wildcard-masks/+page.svelte create mode 100644 src/routes/[lang]/search/+page.svelte create mode 100644 src/routes/[lang]/settings/+page.svelte create mode 100644 src/routes/[lang]/subnetting/+page.svelte create mode 100644 src/routes/[lang]/subnetting/ipv4-subnet-calculator/+page.svelte create mode 100644 src/routes/[lang]/subnetting/ipv6-subnet-calculator/+page.svelte create mode 100644 src/routes/[lang]/subnetting/planner/+page.svelte create mode 100644 src/routes/[lang]/subnetting/supernet-calculator/+page.svelte create mode 100644 src/routes/[lang]/subnetting/vlsm-calculator/+page.svelte diff --git a/package-lock.json b/package-lock.json index c4a9d5cb..b196bd0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "networking-toolbox", - "version": "1.4.0", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "networking-toolbox", - "version": "1.4.0", + "version": "1.5.0", "devDependencies": { "@axe-core/playwright": "^4.10.2", "@codecov/rollup-plugin": "^1.9.1", @@ -38,6 +38,7 @@ "sass-embedded": "^1.91.0", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "tsx": "^4.20.6", "typescript": "^5.0.0", "typescript-eslint": "^8.44.0", "vite": "^7.0.4", @@ -4418,6 +4419,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -5937,6 +5951,16 @@ "node": ">=8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/rettime": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.7.0.tgz", @@ -7084,6 +7108,41 @@ "dev": true, "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", diff --git a/package.json b/package.json index 6ca618e8..4ee9f4da 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ "test:coverage": "vitest run --coverage", "test:api": "vitest run --config tests/vitest.api.config.ts", "test:e2e": "playwright test", + "i18n:check": "tsx scripts/check-translations.ts", + "i18n:check:verbose": "tsx scripts/check-translations.ts --verbose", + "i18n:find-hardcoded": "tsx scripts/find-hardcoded-text.ts", "hold-my-beer": "npm run format && npm run lint && npm run types && npm run check && npm run build-check && npm run test" }, "devDependencies": { @@ -52,6 +55,7 @@ "sass-embedded": "^1.91.0", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "tsx": "^4.20.6", "typescript": "^5.0.0", "typescript-eslint": "^8.44.0", "vite": "^7.0.4", diff --git a/scripts/check-translations.ts b/scripts/check-translations.ts new file mode 100644 index 00000000..2de497b5 --- /dev/null +++ b/scripts/check-translations.ts @@ -0,0 +1,309 @@ +#!/usr/bin/env node +/** + * Translation Validation Script + * + * Validates translation files by: + * - Checking for missing keys compared to English (source) + * - Detecting extra keys that don't exist in source + * - Finding empty translation values + * - Reporting coverage statistics + * + * Usage: + * npm run check-translations + * npm run check-translations -- --verbose + * npm run check-translations -- --lang=de + */ + +import { readFileSync, readdirSync, existsSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const TRANSLATIONS_DIR = join(__dirname, '../src/lib/i18n/translations'); +const SOURCE_LANG = 'en'; + +interface TranslationObject { + [key: string]: string | TranslationObject; +} + +interface ValidationResult { + lang: string; + namespace: string; + missing: string[]; + extra: string[]; + empty: string[]; + total: number; + translated: number; +} + +/** + * Recursively flatten nested object into dot-notation keys + */ +function flattenKeys(obj: TranslationObject, prefix = ''): Record { + const result: Record = {}; + + for (const [key, value] of Object.entries(obj)) { + const fullKey = prefix ? `${prefix}.${key}` : key; + + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + Object.assign(result, flattenKeys(value, fullKey)); + } else { + result[fullKey] = String(value); + } + } + + return result; +} + +/** + * Load and parse a translation file + */ +function loadTranslation(lang: string, namespace: string): Record | null { + const filePath = join(TRANSLATIONS_DIR, lang, `${namespace}.json`); + + if (!existsSync(filePath)) { + return null; + } + + try { + const content = readFileSync(filePath, 'utf-8'); + const parsed = JSON.parse(content); + return flattenKeys(parsed); + } catch (error) { + console.error(`Error parsing ${filePath}:`, error); + return null; + } +} + +/** + * Get all available namespaces from source language + */ +function getNamespaces(): string[] { + const sourcePath = join(TRANSLATIONS_DIR, SOURCE_LANG); + + if (!existsSync(sourcePath)) { + throw new Error(`Source language directory not found: ${sourcePath}`); + } + + return readdirSync(sourcePath) + .filter(file => file.endsWith('.json')) + .map(file => file.replace('.json', '')); +} + +/** + * Get all available languages + */ +function getLanguages(): string[] { + if (!existsSync(TRANSLATIONS_DIR)) { + throw new Error(`Translations directory not found: ${TRANSLATIONS_DIR}`); + } + + return readdirSync(TRANSLATIONS_DIR, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => dirent.name); +} + +/** + * Validate a single language namespace against source + */ +function validateNamespace(lang: string, namespace: string): ValidationResult { + const source = loadTranslation(SOURCE_LANG, namespace); + const target = loadTranslation(lang, namespace); + + if (!source) { + throw new Error(`Source translation not found: ${SOURCE_LANG}/${namespace}.json`); + } + + const result: ValidationResult = { + lang, + namespace, + missing: [], + extra: [], + empty: [], + total: Object.keys(source).length, + translated: 0, + }; + + if (!target) { + // Entire namespace is missing + result.missing = Object.keys(source); + return result; + } + + const sourceKeys = new Set(Object.keys(source)); + const targetKeys = new Set(Object.keys(target)); + + // Find missing keys + for (const key of sourceKeys) { + if (!targetKeys.has(key)) { + result.missing.push(key); + } else if (target[key].trim() === '') { + result.empty.push(key); + } else { + result.translated++; + } + } + + // Find extra keys + for (const key of targetKeys) { + if (!sourceKeys.has(key)) { + result.extra.push(key); + } + } + + return result; +} + +/** + * Format validation results + */ +function formatResults(results: ValidationResult[], verbose: boolean): string { + let output = ''; + + // Group by language + const byLang = results.reduce((acc, r) => { + if (!acc[r.lang]) acc[r.lang] = []; + acc[r.lang].push(r); + return acc; + }, {} as Record); + + for (const [lang, langResults] of Object.entries(byLang)) { + const totalKeys = langResults.reduce((sum, r) => sum + r.total, 0); + const translatedKeys = langResults.reduce((sum, r) => sum + r.translated, 0); + const missingKeys = langResults.reduce((sum, r) => sum + r.missing.length, 0); + const emptyKeys = langResults.reduce((sum, r) => sum + r.empty.length, 0); + const extraKeys = langResults.reduce((sum, r) => sum + r.extra.length, 0); + const coverage = totalKeys > 0 ? ((translatedKeys / totalKeys) * 100).toFixed(1) : '0.0'; + + output += `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; + output += `📝 Language: ${lang.toUpperCase()}\n`; + output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; + output += `Coverage: ${coverage}% (${translatedKeys}/${totalKeys} keys)\n`; + + if (missingKeys > 0) { + output += `❌ Missing: ${missingKeys} keys\n`; + } + if (emptyKeys > 0) { + output += `⚠️ Empty: ${emptyKeys} keys\n`; + } + if (extraKeys > 0) { + output += `🔍 Extra: ${extraKeys} keys\n`; + } + + if (missingKeys === 0 && emptyKeys === 0 && extraKeys === 0) { + output += `✅ All translations complete!\n`; + } + + // Detailed breakdown by namespace + if (verbose) { + output += `\nNamespaces:\n`; + for (const result of langResults) { + const nsCoverage = result.total > 0 + ? ((result.translated / result.total) * 100).toFixed(1) + : '0.0'; + + output += ` ${result.namespace}: ${nsCoverage}% (${result.translated}/${result.total})`; + + if (result.missing.length > 0) { + output += ` - Missing: ${result.missing.length}`; + } + if (result.empty.length > 0) { + output += ` - Empty: ${result.empty.length}`; + } + if (result.extra.length > 0) { + output += ` - Extra: ${result.extra.length}`; + } + + output += '\n'; + + // Show missing keys + if (result.missing.length > 0 && result.missing.length <= 10) { + output += ` Missing keys:\n`; + for (const key of result.missing) { + output += ` - ${key}\n`; + } + } else if (result.missing.length > 10) { + output += ` Missing keys (first 10):\n`; + for (const key of result.missing.slice(0, 10)) { + output += ` - ${key}\n`; + } + output += ` ... and ${result.missing.length - 10} more\n`; + } + + // Show empty keys + if (result.empty.length > 0) { + output += ` Empty values:\n`; + for (const key of result.empty) { + output += ` - ${key}\n`; + } + } + + // Show extra keys + if (result.extra.length > 0) { + output += ` Extra keys (not in source):\n`; + for (const key of result.extra) { + output += ` - ${key}\n`; + } + } + } + } + } + + return output; +} + +/** + * Main function + */ +function main() { + const args = process.argv.slice(2); + const verbose = args.includes('--verbose') || args.includes('-v'); + const langFilter = args.find(arg => arg.startsWith('--lang='))?.split('=')[1]; + + console.log('🌍 Translation Validation Script\n'); + console.log(`Source language: ${SOURCE_LANG}`); + console.log(`Translations directory: ${TRANSLATIONS_DIR}\n`); + + try { + const namespaces = getNamespaces(); + const languages = getLanguages().filter(lang => lang !== SOURCE_LANG); + + if (languages.length === 0) { + console.log('No translation languages found.'); + return; + } + + console.log(`Found ${namespaces.length} namespaces: ${namespaces.join(', ')}`); + console.log(`Found ${languages.length} languages: ${languages.join(', ')}\n`); + + const results: ValidationResult[] = []; + + for (const lang of languages) { + if (langFilter && lang !== langFilter) { + continue; + } + + for (const namespace of namespaces) { + const result = validateNamespace(lang, namespace); + results.push(result); + } + } + + const output = formatResults(results, verbose); + console.log(output); + + // Exit with error if there are missing translations + const hasMissing = results.some(r => r.missing.length > 0 || r.empty.length > 0); + if (hasMissing) { + process.exit(1); + } + + } catch (error) { + console.error('Error:', error); + process.exit(1); + } +} + +main(); diff --git a/scripts/find-hardcoded-text.ts b/scripts/find-hardcoded-text.ts new file mode 100644 index 00000000..a84b8e28 --- /dev/null +++ b/scripts/find-hardcoded-text.ts @@ -0,0 +1,246 @@ +#!/usr/bin/env node +/** + * Hardcoded Text Finder + * + * Scans Svelte files for hardcoded English text that should be moved to translation files. + * Helps identify strings that need internationalization. + * + * Usage: + * npm run find-hardcoded + * npm run find-hardcoded -- --path=src/routes/settings + * npm run find-hardcoded -- --min-length=10 + */ + +import { readFileSync, readdirSync, statSync } from 'fs'; +import { join, relative, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const PROJECT_ROOT = join(__dirname, '..'); +const DEFAULT_SCAN_PATH = join(PROJECT_ROOT, 'src'); + +interface HardcodedText { + file: string; + line: number; + text: string; + context: string; +} + +/** + * Patterns to detect hardcoded text in Svelte files + */ +const TEXT_PATTERNS = [ + // HTML text content: >Text< + />([A-Z][^<>{}\n]{3,}) 100) return true; // Too long, likely not a translation + + return EXCLUDE_PATTERNS.some(pattern => pattern.test(trimmed)); +} + +/** + * Extract hardcoded text from file content + */ +function findHardcodedText(filePath: string, content: string): HardcodedText[] { + const results: HardcodedText[] = []; + const lines = content.split('\n'); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const lineNumber = i + 1; + + // Skip script/style blocks content (rough heuristic) + if (line.includes(' { + const grouped = new Map(); + + for (const result of results) { + if (!grouped.has(result.file)) { + grouped.set(result.file, []); + } + grouped.get(result.file)!.push(result); + } + + return grouped; +} + +/** + * Format results for display + */ +function formatResults(results: HardcodedText[], minLength: number): string { + const filtered = results.filter(r => r.text.length >= minLength); + const grouped = groupByFile(filtered); + + let output = ''; + + output += `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; + output += `📝 Hardcoded Text Finder\n`; + output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; + output += `Found ${filtered.length} potential hardcoded strings in ${grouped.size} files\n`; + output += `Minimum length: ${minLength} characters\n\n`; + + if (filtered.length === 0) { + output += `✅ No hardcoded text found!\n`; + return output; + } + + // Sort files by number of issues + const sortedFiles = Array.from(grouped.entries()) + .sort((a, b) => b[1].length - a[1].length); + + for (const [file, items] of sortedFiles) { + output += `\n📄 ${file} (${items.length} items)\n`; + output += `${'─'.repeat(60)}\n`; + + for (const item of items) { + output += ` Line ${item.line}: "${item.text}"\n`; + output += ` Context: ${item.context}\n`; + } + } + + output += `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; + output += `Summary: ${filtered.length} strings need translation\n`; + output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; + + return output; +} + +/** + * Main function + */ +function main() { + const args = process.argv.slice(2); + const pathArg = args.find(arg => arg.startsWith('--path='))?.split('=')[1]; + const minLengthArg = args.find(arg => arg.startsWith('--min-length='))?.split('=')[1]; + + const scanPath = pathArg ? join(PROJECT_ROOT, pathArg) : DEFAULT_SCAN_PATH; + const minLength = minLengthArg ? parseInt(minLengthArg, 10) : 3; + + console.log('🔍 Scanning for hardcoded text...\n'); + console.log(`Path: ${relative(PROJECT_ROOT, scanPath)}`); + console.log(`Min length: ${minLength}\n`); + + try { + const results = scanDirectory(scanPath); + const output = formatResults(results, minLength); + console.log(output); + + // Exit with warning if hardcoded text found + if (results.length > 0) { + console.log('\n💡 Tip: Move these strings to translation files in src/lib/i18n/translations/'); + process.exit(1); + } + + } catch (error) { + console.error('Error:', error); + process.exit(1); + } +} + +main(); diff --git a/src/lib/components/furniture/SettingsPanel.svelte b/src/lib/components/furniture/SettingsPanel.svelte index b22de330..b38dacc1 100644 --- a/src/lib/components/furniture/SettingsPanel.svelte +++ b/src/lib/components/furniture/SettingsPanel.svelte @@ -13,6 +13,8 @@ import { storage } from '$lib/utils/localStorage'; import * as config from '$lib/config/customizable-settings'; import SegmentedControl from '$lib/components/global/SegmentedControl.svelte'; + import { locale, setLanguage, loadNamespaces, t } from '$lib/stores/language'; + import { SUPPORTED_LANGUAGES } from '$lib/i18n/supported-languages'; interface Props { standalone?: boolean; @@ -25,7 +27,6 @@ let showMoreA11y = $state(standalone); let showMoreThemes = $state(standalone); let showCustomColorInput = $state(false); - let selectedLanguage = $state('en'); // Store subscriptions let accessibilitySettings = $state(accessibility); @@ -73,12 +74,6 @@ '#44aaff', '#7777ff', ]; - const LANGUAGES = [ - { code: 'en', name: 'English', available: true, flag: '🇺🇸' }, - { code: 'es', name: 'Español', available: false, flag: '🇪🇸' }, - { code: 'fr', name: 'Français', available: false, flag: '🇫🇷' }, - { code: 'de', name: 'Deutsch', available: false, flag: '🇩🇪' }, - ]; // Derived state const primaryOptions = $derived( @@ -135,6 +130,14 @@ fontScale.setLevel(parseInt((e.currentTarget as HTMLInputElement).value, 10) as FontScaleLevel), linkClick: () => onClose?.(), + languageChange: async (langCode: string) => { + // Update language store and localStorage + setLanguage(langCode); + + // Load necessary translation namespaces for the new language + await loadNamespaces(langCode, ['common', 'nav', 'settings', 'tools']); + }, + applyCustomCss: () => { const validation = customCss.validate(cssInput); validationErrors = validation.errors; @@ -296,7 +299,7 @@
-

Theme

+

{$t('settings.theme.title')}

{#each themes.slice(0, 6) as themeOption (themeOption.id)} @@ -317,14 +320,14 @@ {#if themes.length > 6 && !standalone} {/if}
{#if standalone}
-

Font Scale

+

{$t('settings.font_scale.title')}

-

Language

+

{$t('settings.language.title')}

- {#each LANGUAGES as lang (lang.code)} + {#each SUPPORTED_LANGUAGES as lang (lang.code)} {/if}
@@ -431,11 +432,11 @@ {#if standalone}
-

Site Branding

-

Customize the site title, description, and icon.

+

{$t('settings.site_branding.title')}

+

{$t('settings.site_branding.description')}

- +
- +
- +
-

Primary Color

-

Choose a primary color for the interface.

+

{$t('settings.primary_color.title')}

+

{$t('settings.primary_color.description')}

{#each COLOR_PALETTE as color (color)} @@ -512,8 +513,8 @@
-

Custom CSS

-

Add your own CSS to customize the appearance globally.

+

{$t('settings.custom_css.title')}

+

{$t('settings.custom_css.description')}

+ {#if !customResolversValid} + + Invalid IP address format. Enter valid IPv4 or IPv6 addresses. + + {/if} + +
+ +
+
+ +
+ + + Default: 5000ms +
+ +
+ +

Custom DNS servers must be valid IPv4 or IPv6 addresses. Duplicates will be removed automatically.

+
+
+ +
+ + + {#if error} +
+ +
+

Error

+

{error}

+
+
+ {/if} + + {#if loading} +
+
+
+ +
+

Testing DNS Resolvers

+

Querying {recordType} records for {domain}...

+
+
+
+
+ {/if} + + + {#if results} +
+
+

Performance Results

+
+ + +
+
+ +
+ Fastest + {results.statistics.fastest.resolver} + {results.statistics.fastest.time}ms +
+
+
+ +
+ Average + {results.statistics.average}ms + Median: {results.statistics.median}ms +
+
+
+ +
+ Slowest + {results.statistics.slowest.resolver} + {results.statistics.slowest.time}ms +
+
+
+ +
+ Success Rate + {results.statistics.successRate}% + {successCount}/{totalCount} +
+
+
+ + +
+

Resolver Comparison

+
+ {#each sortedResults as result (result.resolver)} + {@const perf = result.success ? getPerformance(result.responseTime) : null} +
+
+
+ {result.resolverName} + {result.resolver} +
+ {#if perf} +
+ {result.responseTime}ms + {perf.label} +
+ {:else} +
+ + Failed +
+ {/if} +
+ + {#if result.records?.length} +
+ + + View {result.records.length} record{result.records.length === 1 ? '' : 's'} + +
+ {#each result.records as record (record)} + {record} + {/each} +
+
+ {/if} + + {#if result.error} +
+ + {result.error} +
+ {/if} +
+ {/each} +
+
+ + +
+
+ Domain + {results.domain} +
+
+ Record Type + {results.recordType} +
+
+ Total Records + {totalRecords} +
+
+ Resolvers Tested + {successCount} of {totalCount} successful +
+
+ Performance Spread + {performanceSpread}ms +
+
+ Fastest + {fastestResolver} +
+
+ Tested + {formattedTimestamp} +
+
+
+ {/if} +
+ + +
+
+
+

{dnsPerformanceContent.sections.whatIsDnsPerformance.title}

+
+
+

{dnsPerformanceContent.sections.whatIsDnsPerformance.content}

+
+
+ +
+
+

{dnsPerformanceContent.sections.interpretingResults.title}

+
+
+

{dnsPerformanceContent.sections.interpretingResults.content}

+
+ {#each dnsPerformanceContent.sections.interpretingResults.ranges as range (range.range)} +
+
+ {range.range} + {range.performance} +
+

{range.description}

+
+ {/each} +
+
+
+ +
+
+

{dnsPerformanceContent.sections.publicResolvers.title}

+
+
+
+ {#each dnsPerformanceContent.sections.publicResolvers.resolvers as resolver (resolver.name)} +
+
+

+ {resolver.name} ({resolver.ip}) +

+
+

{resolver.description}

+
+
+
+ + Pros +
+
    + {#each resolver.pros as pro (pro)} +
  • {pro}
  • + {/each} +
+
+
+
+ + Cons +
+
    + {#each resolver.cons as con (con)} +
  • {con}
  • + {/each} +
+
+
+
+ + Best for +
+

{resolver.bestFor}

+
+
+
+ {/each} +
+
+
+ +
+
+

{dnsPerformanceContent.sections.optimization.title}

+
+
+
+ {#each dnsPerformanceContent.sections.optimization.tips as tip (tip.tip)} +
+

{tip.tip}

+

{tip.description}

+
+ {/each} +
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/dns/reverse-lookup/+page.svelte b/src/routes/[lang]/diagnostics/dns/reverse-lookup/+page.svelte new file mode 100644 index 00000000..ea74374f --- /dev/null +++ b/src/routes/[lang]/diagnostics/dns/reverse-lookup/+page.svelte @@ -0,0 +1,516 @@ + + +
+
+

Reverse DNS Lookup

+

+ Perform reverse DNS lookups (PTR records) to find hostnames associated with IP addresses. Automatically handles + both IPv4 and IPv6 addresses with proper .in-addr.arpa and .ip6.arpa zone formatting. +

+
+ + +
+
+ + +

Common IP Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

Reverse Lookup Configuration

+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ + + {#if results?.warnings?.length > 0} + {@const res = results as { warnings?: string[] }} +
+
+
+ +
+ {#each res.warnings || [] as warning, warningIndex (warningIndex)} +

{warning}

+ {/each} +
+
+
+
+ {/if} + + + {#if results} +
+
+

Reverse DNS Results

+ {#if results.Answer?.length > 0} + + {/if} +
+
+
+
+ IP Address: + {ipAddress} +
+
+ Reverse Zone: + {results.reverseName} +
+
+ + {#if results.Answer?.length > 0} + {@const resData = results as { Answer: Array<{ data: string; TTL?: number }> }} +
+

PTR Records Found:

+ {#each resData.Answer as record, _i (_i)} +
+
{record.data}
+ {#if record.TTL} +
+ TTL: {record.TTL}s +
+ {/if} +
+ {/each} +
+ {:else} +
+ +

No PTR records found for {ipAddress}

+

This IP address may not have a reverse DNS entry configured.

+
+ {/if} +
+
+ {/if} + + {#if error} +
+
+
+ +
+ Reverse Lookup Failed +

{error}

+
+
+
+
+ {/if} + + +
+
+

About Reverse DNS Lookups

+
+
+
+
+

How it Works

+

+ Reverse DNS converts IP addresses to hostnames using PTR records. IPv4 addresses use .in-addr.arpa zones, + while IPv6 addresses use .ip6.arpa zones with each nibble reversed. +

+
+ +
+

Common Use Cases

+
    +
  • Email server verification
  • +
  • Security analysis and logging
  • +
  • Network troubleshooting
  • +
  • Identifying server ownership
  • +
+
+ +
+

Zone Format Examples

+
+
+ IPv4: 8.8.8.8 → 8.8.8.8.in-addr.arpa +
+
+ IPv6: 2001:db8::1 → 1.0.0.0...b.d.0.1.0.0.2.ip6.arpa +
+
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/dns/soa-serial/+page.svelte b/src/routes/[lang]/diagnostics/dns/soa-serial/+page.svelte new file mode 100644 index 00000000..65c5fccd --- /dev/null +++ b/src/routes/[lang]/diagnostics/dns/soa-serial/+page.svelte @@ -0,0 +1,642 @@ + + +
+
+

SOA Serial Analyzer

+

+ Analyze Start of Authority (SOA) records to interpret serial number formats and examine DNS zone timing + parameters. SOA records contain critical zone metadata including serial numbers for change tracking and timing + values for zone transfers. +

+
+ + +
+
+ + +

Domain Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

SOA Analysis Configuration

+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ + + {#if results} + {@const res = results as { soa?: { serial?: number }; serialAnalysis?: { format?: string } }} + {@const serialInfo = (results as { serialAnalysis?: { formatDescription?: string; explanation?: string } }) + .serialAnalysis} + {@const serialAnalysis = ( + results as { + serialAnalysis?: { + parsed?: { year?: number; month?: number; day?: number; revision?: number; timestamp?: number }; + format?: string; + valid?: boolean; + }; + } + ).serialAnalysis} + {@const soaData = (results as { soa?: { mname?: string; rname?: string; ttl?: number } }).soa} + {@const timingData = (results as { soa?: { refresh?: number; retry?: number; expire?: number; minimum?: number } }) + .soa} +
+
+

SOA Analysis for {results.name}

+ +
+
+
+
+ Domain: + {results.name} +
+
+ DoH Resolver: + {results.resolver} +
+
+ +
+ +
+

Serial Number Analysis

+
+
+ {res.soa?.serial || 'Not available'} + {res.serialAnalysis?.format || 'Unknown'} +
+ +
+
Format:
+
+ {serialInfo?.formatDescription || 'Unknown'} +

{serialInfo?.explanation || 'No analysis available'}

+
+ {#if serialAnalysis?.parsed} +
Parsed Date:
+
+ {#if serialAnalysis.format === 'YYYYMMDDNN'} +
+ Year: {serialAnalysis.parsed.year} + Month: {serialAnalysis.parsed.month} + Day: {serialAnalysis.parsed.day} + Revision: {serialAnalysis.parsed.revision} +
+ {:else if serialAnalysis.format === 'Unix Timestamp'} + {formatDate(serialAnalysis.parsed.timestamp!)} + {/if} +
+ {/if} + +
Validity:
+
+ + {serialAnalysis?.valid ? 'Valid format' : 'Invalid or unusual format'} +
+
+
+
+ + +
+

SOA Record Details

+
+
Primary Server:
+
{soaData?.mname || 'Not available'}
+ +
Contact Email:
+
{soaData?.rname || 'Not available'}
+ +
TTL:
+
+ {#if soaData?.ttl} + {soaData.ttl}s + ({formatDuration(soaData.ttl)}) + {:else} + Not available + {/if} +
+
+
+ + +
+

Zone Timing Parameters

+
+
+
Refresh
+
{timingData?.refresh || 0}s
+
+ {formatDuration(timingData?.refresh || 0)} +

How often secondary servers check for updates

+
+
+ +
+
Retry
+
{timingData?.retry || 0}s
+
+ {formatDuration(timingData?.retry || 0)} +

Retry interval after failed refresh attempts

+
+
+ +
+
Expire
+
{timingData?.expire || 0}s
+
+ {formatDuration(timingData?.expire || 0)} +

When secondary servers stop serving the zone

+
+
+ +
+
Minimum
+
{timingData?.minimum || 0}s
+
+ {formatDuration(timingData?.minimum || 0)} +

Minimum TTL for negative responses

+
+
+
+
+ + + {#if results.assessment?.length} + {@const assessmentData = ( + results as { + assessment?: Array<{ severity: string; aspect: string; message: string; recommendation?: string }>; + } + ).assessment} +
+

Configuration Assessment

+
+ {#each assessmentData || [] as item, itemIndex (itemIndex)} +
+ +
+ {item.aspect} +

{item.message}

+ {#if item.recommendation} + {item.recommendation} + {/if} +
+
+ {/each} +
+
+ {/if} +
+
+
+ {/if} + + {#if error} +
+
+
+ +
+ SOA Analysis Failed +

{error}

+
+

Troubleshooting Tips:

+
    +
  • Ensure the domain name is valid and has a SOA record
  • +
  • Try a different DoH resolver if the current one fails
  • +
  • Some domains may not respond to certain resolvers
  • +
  • Check if the domain exists and is properly configured
  • +
+
+
+
+
+
+ {/if} + + +
+
+

About SOA Records and Serial Numbers

+
+
+
+
+

What is a SOA Record?

+

+ Start of Authority records contain administrative information about a DNS zone, including the primary + server, contact email, and timing parameters that control zone transfers and caching behavior. +

+
+ +
+

Serial Number Formats

+
    +
  • YYYYMMDDNN: Date-based format (e.g., 2024031501 = March 15, 2024, revision 01)
  • +
  • Unix Timestamp: Seconds since epoch (e.g., 1710518400)
  • +
  • Sequential: Simple incrementing numbers (e.g., 1, 2, 3...)
  • +
+
+ +
+

Timing Parameters

+
    +
  • Refresh: How often secondaries check for updates
  • +
  • Retry: Retry interval after failed transfers
  • +
  • Expire: When to stop serving if updates fail
  • +
  • Minimum: TTL for negative (NXDOMAIN) responses
  • +
+
+ +
+

Best Practices

+
    +
  • Use YYYYMMDDNN format for predictable versioning
  • +
  • Set refresh to 3600-7200s for most zones
  • +
  • Retry should be shorter than refresh (1800-3600s)
  • +
  • Expire should be much longer (604800-1209600s)
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/dns/spf-evaluator/+page.svelte b/src/routes/[lang]/diagnostics/dns/spf-evaluator/+page.svelte new file mode 100644 index 00000000..96b3cf1c --- /dev/null +++ b/src/routes/[lang]/diagnostics/dns/spf-evaluator/+page.svelte @@ -0,0 +1,648 @@ + + +
+
+

SPF Record Evaluator

+

+ Analyze SPF (Sender Policy Framework) records with recursive expansion of includes and redirects. Check DNS lookup + limits and identify potential policy issues. +

+
+ + +
+
+ + +

SPF Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

SPF Evaluation

+
+
+
+ +
+ +
+ +
+
+
+ + + {#if results && !results.error} + {@const status = getLookupStatus()} +
+
+

SPF Evaluation Results

+ +
+
+ +
+
+ + {status.message} +
+
+ + + {#if results.record} +
+

Original SPF Record

+
+ {results.record} +
+
+ {/if} + + + {#if (results as { expanded?: { mechanisms?: string[] } }).expanded?.mechanisms?.length} + {@const resultsExpanded = (results as { expanded?: { mechanisms?: string[] } }).expanded} +
+

Direct Mechanisms

+
+ {#each resultsExpanded!.mechanisms! as mechanism, mechanismIndex (mechanismIndex)} + {@const mechInfo = getMechanismType(mechanism as string)} +
+ +
+ {mechanism} + {mechInfo.type} +
+
+ {/each} +
+
+ {/if} + + + {#if results.expanded?.includes?.length > 0} + {@const includesData = (results as { expanded: { includes: unknown[] } }).expanded.includes} +
+

Include Chain

+
+ {#each renderIncludeTree(includesData) as item, itemIndex (itemIndex)} +
+
+ + {item.type}: + {item.domain} + {#if item.result?.error} + + {:else} + + {/if} +
+ + {#if item.result?.error} +
+ + {item.result.error} +
+ {:else if item.result?.record} +
+ {item.result.record} +
+ {/if} +
+ {/each} +
+
+ {/if} + + + {#if results.expanded?.redirects?.length > 0} + {@const redirectsData = ( + results as { + expanded: { redirects: Array<{ domain: string; result?: { error?: string; record?: string } }> }; + } + ).expanded.redirects} +
+

Redirects

+
+ {#each redirectsData as redirect, redirectIndex (redirectIndex)} +
+
+ + redirect to: {redirect.domain} + {#if redirect.result?.error} + + {:else} + + {/if} +
+ + {#if redirect.result?.error} +
+ {redirect.result.error} +
+ {:else if redirect.result?.record} +
+ {redirect.result.record} +
+ {/if} +
+ {/each} +
+
+ {/if} +
+
+ {/if} + + {#if error || results?.error} +
+
+
+ +
+ SPF Evaluation Failed +

{error || results.error}

+
+
+
+
+ {/if} + + +
+
+

Understanding SPF Records

+
+
+
+
+

SPF Mechanisms

+
+
+ all: Matches all addresses (use carefully) +
+
+ ip4/ip6: Matches specific IP addresses or ranges +
+
+ a/mx: Matches A or MX record addresses +
+
+ include: References another domain's SPF record +
+
+ redirect: Redirects to another domain's SPF record +
+
+
+ +
+

SPF Qualifiers

+
+
+ + (Pass): Explicitly allow +
+
+ - (Fail): Explicitly deny +
+
+ ~ (Soft Fail): Mark as suspicious +
+
+ ? (Neutral): No explicit policy +
+
+
+ +
+

DNS Lookup Limits

+

SPF has a limit of 10 DNS lookups to prevent infinite loops and reduce load. This includes:

+
    +
  • Each include mechanism
  • +
  • Each a, mx, exists, ptr mechanism
  • +
  • Lookups from redirect modifiers
  • +
+
+ +
+

Best Practices

+
    +
  • Keep DNS lookups under the 10-lookup limit
  • +
  • End with -all or ~all for security
  • +
  • Use IP addresses when possible to reduce lookups
  • +
  • Avoid excessive nesting of includes
  • +
  • Regularly audit and update SPF records
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/dns/spf-flatten/+page.svelte b/src/routes/[lang]/diagnostics/dns/spf-flatten/+page.svelte new file mode 100644 index 00000000..1a43d674 --- /dev/null +++ b/src/routes/[lang]/diagnostics/dns/spf-flatten/+page.svelte @@ -0,0 +1,556 @@ + + +
+
+

SPF Flatten

+

Resolve include:/redirect= and output a flattened SPF with lookup counts

+
+ + +
+
+ + +

Quick Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

SPF Flatten Configuration

+
+
+
+ +
+ clearExampleSelection()} + onkeydown={(e) => e.key === 'Enter' && flattenSPF()} + /> + +
+
+
+
+ + {#if error} +
+
+
+ +
+ SPF Flatten Failed +

{error}

+
+
+
+
+ {/if} + + {#if loading} +
+
+
+ +
+

Flattening SPF Record

+

Resolving SPF includes and redirects to create a flattened record...

+
+
+
+
+ {/if} + + {#if results} +
+
+

SPF Flatten Results

+
+
+
+ +
+
+

Original SPF Record

+
+
+
+ {results.original} +
+
+
+ + +
+
+
+

Flattened SPF Record

+ +
+
+
+
+ {results.flattened} +
+
+
+ + + {#if results.expansions && results.expansions.length > 0} +
+
+

Expansion Tree

+
+
+
+ {#each results.expansions as expansion, _i (expansion.value)} +
+
+ {expansion.type} + {expansion.value} + + {expansion.lookups} + +
+ + {#if expansion.resolved} +
+ {#each expansion.resolved as item (item)} + {item} + {/each} +
+ {/if} +
+ {/each} +
+
+
+ {/if} + + +
+
+

Statistics

+
+
+
+
+
+ DNS Lookups +
+
7} + class:error={results.stats.dnsLookups > 10} + > + 10 + ? 'alert-circle' + : results.stats.dnsLookups > 7 + ? 'alert-triangle' + : 'check-circle'} + size="sm" + /> + {results.stats.dnsLookups}/10 +
+ {#if results.stats.dnsLookups > 10} +
Exceeds RFC limit!
+ {:else if results.stats.dnsLookups > 7} +
Close to limit
+ {/if} +
+ +
+
+ IPv4 Addresses +
+
{results.stats.ipv4Count}
+
+ +
+
+ IPv6 Addresses +
+
{results.stats.ipv6Count}
+
+ +
+
+ Max Include Depth +
+
{results.stats.includeDepth}
+
+ +
+
+ Record Length +
+
400}> + 450 ? 'alert-triangle' : 'check-circle'} size="sm" /> + {results.stats.recordLength} +
+ {#if results.stats.recordLength > 450} +
May need splitting
+ {/if} +
+ +
+
+ Total Mechanisms +
+
{results.stats.mechanisms}
+
+
+
+
+ + + {#if results.warnings && results.warnings.length > 0} +
+
+

Warnings

+
+
+
+ {#each results.warnings as warning (warning)} +
+ + {warning} +
+ {/each} +
+
+
+ {/if} +
+
+
+ {/if} +
+ + diff --git a/src/routes/[lang]/diagnostics/dns/trace/+page.svelte b/src/routes/[lang]/diagnostics/dns/trace/+page.svelte new file mode 100644 index 00000000..5025da02 --- /dev/null +++ b/src/routes/[lang]/diagnostics/dns/trace/+page.svelte @@ -0,0 +1,543 @@ + + +
+
+

DNS Trace Tool

+

Iterative trace from root to authoritative nameservers via DNS over HTTPS

+
+ + +
+
+ + +

Quick Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

Trace Configuration

+
+
+
+ +
+ clearExampleSelection()} + onkeydown={(e) => e.key === 'Enter' && performTrace()} + /> + +
+
+
+
+ + {#if error} +
+
+
+ +
+ Trace Failed +

{error}

+
+
+
+
+ {/if} + + {#if loading} +
+
+
+ +
+

Performing DNS Trace

+

Following the DNS resolution path from root servers to authoritative nameservers...

+
+
+
+
+ {/if} + + {#if results} +
+
+

Trace Path

+
+
+
+ {#each results.path as step, i (i)} +
+
+ {i + 1} +
+ +
+
+ {step.type} + {formatTiming(step.timing)} +
+ +
+ Query: + {step.query} + {#if step.qtype} + {step.qtype} + {/if} +
+ +
+ Server: + {step.server} + {#if step.serverName} + ({step.serverName}) + {/if} +
+ + {#if step.response} +
+ Response: + {#if step.response.type === 'referral'} + + Referral to {step.response.nameservers.join(', ')} + + {:else if step.response.type === 'answer'} + + {#if Array.isArray(step.response.data)} + {step.response.data.join(', ')} + {:else} + {step.response.data} + {/if} + + {:else if step.response.type === 'nodata'} + No data for this record type + {:else if step.response.type === 'nxdomain'} + Domain does not exist + {/if} +
+ {/if} + + {#if step.flags} +
+ {#if step.flags.aa} + AA + {/if} + {#if step.flags.ad} + AD + {/if} + {#if step.flags.rd} + RD + {/if} + {#if step.flags.ra} + RA + {/if} +
+ {/if} +
+
+ {/each} +
+
+
+ + {#if results.summary} +
+
+

Trace Summary

+
+
+
+
+
Total Time
+
+ + {formatTiming(results.summary.totalTime)} +
+
+
+
DNS Queries
+
{results.summary.queryCount}
+
+ {#if results.summary.finalServer} +
+
+ Final Server +
+
{results.summary.finalServer}
+
+ {/if} + {#if results.summary.recordType} +
+
Record Type
+
{results.summary.recordType}
+
+ {/if} + {#if results.summary.totalHops} +
+
+ Total Hops +
+
{results.summary.totalHops}
+
+ {/if} + {#if results.summary.averageLatency} +
+
Avg Latency
+
{results.summary.averageLatency}ms
+
+ {/if} + {#if results.summary.dnssecValid !== undefined} +
+
+ DNSSEC Status +
+
+ + {results.summary.dnssecValid ? 'Valid' : 'Not Validated'} +
+
+ {/if} + {#if results.summary.authoritativeAnswer !== undefined} +
+
+ Authoritative +
+
+ + {results.summary.authoritativeAnswer ? 'Yes' : 'No'} +
+
+ {/if} + {#if results.summary.resolverPath} +
+
+ Resolution Path +
+
{results.summary.resolverPath}
+
+ {/if} + {#if results.summary.finalAnswer} +
+
+ Final Answer +
+
+ {Array.isArray(results.summary.finalAnswer) + ? results.summary.finalAnswer.join(', ') + : results.summary.finalAnswer} +
+
+ {/if} +
+
+
+ {/if} + {/if} +
+ + diff --git a/src/routes/[lang]/diagnostics/email/dmarc-check/+page.svelte b/src/routes/[lang]/diagnostics/email/dmarc-check/+page.svelte new file mode 100644 index 00000000..f18ba821 --- /dev/null +++ b/src/routes/[lang]/diagnostics/email/dmarc-check/+page.svelte @@ -0,0 +1,774 @@ + + +
+
+

Email DMARC Policy Checker

+

+ Check DMARC (Domain-based Message Authentication, Reporting & Conformance) policies with focus on email + deliverability impact. Understand how DMARC affects your email delivery and reputation. +

+
+ + + ex.domain} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Check DMARC policy for ${ex.domain}`} + /> + + +
+
+

DMARC Policy Check

+
+
+
+ +
+ +
+ +
+
+
+ + + {#if diagnosticState.results && diagnosticState.results.hasRecord} +
+
+

DMARC Policy Analysis

+ +
+
+ {#if diagnosticState.results.parsed && diagnosticState.results.deliverabilityHints} + +
+
+ +
+

Email Deliverability Impact

+

{diagnosticState.results.deliverabilityHints.policyImpact}

+ {#if diagnosticState.results.deliverabilityHints.alignmentComplexity?.strict} +

+ {diagnosticState.results.deliverabilityHints.alignmentComplexity.strict} +

+ {/if} +
+
+ + + {#if diagnosticState.results.deliverabilityHints.recommendations.length > 0} + {@const hintsData = (diagnosticState.results as { deliverabilityHints: { recommendations: string[] } }) + .deliverabilityHints} +
+
Deliverability Recommendations
+
+ {#each hintsData.recommendations as recommendation, recIndex (recIndex)} +
+ + {recommendation} +
+ {/each} +
+
+ {/if} +
+ + +
+

DMARC Record

+
+
_dmarc.{domain}
+ {diagnosticState.results.record} +
+
+ + +
+

Policy Configuration

+
+ +
+
+ + Main Policy +
+
+ {diagnosticState.results.parsed.policy} + + {#if diagnosticState.results.parsed.policy === 'reject'} + Reject non-compliant messages + {:else if diagnosticState.results.parsed.policy === 'quarantine'} + Quarantine suspicious messages + {:else if diagnosticState.results.parsed.policy === 'none'} + Monitor only, no action + {:else} + Unknown policy + {/if} + +
+
+ + + {#if diagnosticState.results.parsed.subdomainPolicy} +
+
+ + Subdomain Policy +
+
+ {diagnosticState.results.parsed.subdomainPolicy} +
+
+ {/if} + + +
+
+ + Coverage +
+
+ {diagnosticState.results.parsed.percentage}% + of messages affected +
+
+ + +
+
+ + DKIM Alignment +
+
+ {diagnosticState.results.parsed.alignment.dkim === 's' ? 'Strict' : 'Relaxed'} + + {diagnosticState.results.parsed.alignment.dkim === 's' + ? 'Exact domain match required' + : 'Organizational domain match allowed'} + +
+
+ + +
+
+ + SPF Alignment +
+
+ {diagnosticState.results.parsed.alignment.spf === 's' ? 'Strict' : 'Relaxed'} + + {diagnosticState.results.parsed.alignment.spf === 's' + ? 'Exact domain match required' + : 'Organizational domain match allowed'} + +
+
+ + +
+
+ + Failure Options +
+
+ {diagnosticState.results.parsed.reporting.failureOptions} + + {#if diagnosticState.results.parsed.reporting.failureOptions === '0'} + DKIM and SPF failure + {:else if diagnosticState.results.parsed.reporting.failureOptions === '1'} + Any alignment failure + {:else if diagnosticState.results.parsed.reporting.failureOptions === 'd'} + DKIM failure only + {:else if diagnosticState.results.parsed.reporting.failureOptions === 's'} + SPF failure only + {:else} + Custom configuration + {/if} + +
+
+
+
+ + +
+

Email Reporting Configuration

+
+
+
+ + Aggregate Reports (RUA) +
+
+ {#if diagnosticState.results.parsed.reporting.aggregate} + + Daily summaries of DMARC activity + {:else} + Not configured + Missing aggregate reporting - consider adding rua= + {/if} +
+
+ +
+
+ + Forensic Reports (RUF) +
+
+ {#if diagnosticState.results.parsed.reporting.forensic} + + Real-time failure reports with message samples + {:else} + Not configured + Optional - provides detailed failure analysis + {/if} +
+
+
+
+ {/if} +
+
+ {/if} + + + {#if diagnosticState.results && diagnosticState.results.hasRecord === false} +
+
+
+ +
+ No DMARC Record Found +

Domain {domain} does not have a DMARC policy configured at _dmarc.{domain}.

+
+
Email Deliverability Impact:
+
    +
  • No protection against email spoofing
  • +
  • May affect email reputation with major providers
  • +
  • Missing visibility into email authentication failures
  • +
  • Consider implementing DMARC starting with p=none for monitoring
  • +
+
+
+
+
+
+ {/if} + + + + +
+
+

Understanding DMARC for Email Delivery

+
+
+
+
+

DMARC Policies & Email Impact

+
+
+ none: Monitor mode - no delivery impact, collect data only +
+
+ quarantine: Failed messages may go to spam/junk folder +
+
+ reject: Failed messages rejected outright - strongest protection +
+
+
+ +
+

Email Delivery Best Practices

+
    +
  • Start with p=none to monitor before enforcement
  • +
  • Gradually increase to p=quarantine then p=reject
  • +
  • Set up aggregate reporting to monitor delivery
  • +
  • Test alignment requirements carefully
  • +
  • Consider subdomain policy for comprehensive coverage
  • +
+
+ +
+

Alignment Modes & Delivery

+
+
+ Relaxed (r): Allows organizational domain matching (safer for delivery) +
+
+ Strict (s): Requires exact domain matching (higher security, delivery risk) +
+
+
+ +
+

Common Delivery Issues

+
    +
  • Strict alignment with third-party senders
  • +
  • Forwarded emails failing DMARC checks
  • +
  • Mailing lists modifying message headers
  • +
  • Percentage rollout causing inconsistent delivery
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/email/greylist-tester/+page.svelte b/src/routes/[lang]/diagnostics/email/greylist-tester/+page.svelte new file mode 100644 index 00000000..d3f036df --- /dev/null +++ b/src/routes/[lang]/diagnostics/email/greylist-tester/+page.svelte @@ -0,0 +1,765 @@ + + +
+
+

{content.title}

+

{content.description}

+
+ + +
+
+

Test Mail Server Greylisting

+
+
+
+
+ + e.key === 'Enter' && testGreylist()} + disabled={diagnosticState.loading} + /> +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + + {#if diagnosticState.loading} +
+
+
+ +
+

Testing Greylisting

+

+ Testing {attempts} connection{attempts > 1 ? 's' : ''} to {domain}:{port} with {delayBetweenAttempts}s + delay... +

+

+ This will take approximately {attempts * delayBetweenAttempts + 10} seconds to complete +

+
+
+
+
+ {/if} + + + + +
+
+ + +

Quick Examples

+
+
+ {#each examplesList as example (example.domain)} + + {/each} +
+
+
+ + + {#if diagnosticState.results} +
+
+

Test Results for {diagnosticState.results.domain}:{diagnosticState.results.port}

+
+
+ +
+ {#if diagnosticState.results.implementsGreylisting} +
+ +
+

Greylisting Detected

+

Server implements greylisting (Confidence: {diagnosticState.results.analysis.confidence})

+
+
+ {:else} +
+ +
+

No Greylisting Detected

+

Server does not appear to implement greylisting

+
+
+ {/if} + + {#if diagnosticState.results.analysis.typicalDelay} +
+ +
+

Typical Delay

+

{diagnosticState.results.analysis.typicalDelay} seconds between rejection and acceptance

+
+
+ {/if} +
+ + +
+

+ + Connection Attempts +

+
+ {#each diagnosticState.results.attempts as attempt (attempt.attemptNumber)} +
+
+ #{attempt.attemptNumber} + {attempt.responseCode || 'Failed'} + {new Date(attempt.timestamp).toLocaleTimeString()} +
+
+ {#if attempt.connected} +
+ {#if attempt.responseCode?.startsWith('2')} + + {/if} + Response: + + {attempt.response} + +
+
+ Duration: + {attempt.duration}ms +
+ {:else} +
+ Error: + + + {attempt.error} + +
+ {/if} +
+
+ {/each} +
+
+ + +
+

+ + Analysis +

+
+
+ Server: + + + {diagnosticState.results.domain}:{diagnosticState.results.port} + +
+
+ Total Attempts: + + + {diagnosticState.results.attempts.length} + +
+
+ Successful Connections: + + 0 ? 'check-circle' : 'x-circle'} size="sm" /> + {connectedCount} / {diagnosticState.results.attempts.length} + +
+
+ Test Duration: + + + {testDuration}s + +
+
+ Initial Connection: + + + {diagnosticState.results.analysis.initialRejected ? 'Temporarily rejected' : 'Accepted immediately'} + +
+
+ Subsequent Attempts: + + + {diagnosticState.results.analysis.subsequentAccepted ? 'Accepted after delay' : 'Still rejected'} + +
+ {#if diagnosticState.results.analysis.typicalDelay} +
+ Delay Duration: + + + {diagnosticState.results.analysis.typicalDelay} seconds + +
+ {/if} +
+ Confidence Level: + + + {diagnosticState.results.analysis.confidence.charAt(0).toUpperCase() + + diagnosticState.results.analysis.confidence.slice(1)} + +
+
+
+
+
+ {/if} + + +
+
+

Understanding Greylisting

+
+
+ {#each [{ title: content.sections.whatIsGreylisting.title, content: content.sections.whatIsGreylisting.content, open: true }, { title: content.sections.howItWorks.title, list: content.sections.howItWorks.steps, listKey: 'step' }, { title: content.sections.smtpCodes.title, codes: content.sections.smtpCodes.codes }, { title: content.sections.confidenceLevels.title, list: content.sections.confidenceLevels.levels, listKey: 'level' }, { title: content.sections.benefits.title, list: content.sections.benefits.points, listKey: 'point' }, { title: content.sections.drawbacks.title, list: content.sections.drawbacks.points, listKey: 'point' }, { title: content.sections.bestPractices.title, simpleList: content.sections.bestPractices.practices }, { title: 'Quick Tips', simpleList: content.quickTips }] as section (section.title)} +
+ + +

{section.title}

+
+
+ {#if section.content} +

{section.content}

+ {:else if section.codes} +
+ {#each section.codes as code (code.code)} +
+
{code.code}
+
+ {code.name} +

{code.desc}

+
+
+ {/each} +
+ {:else if section.list} +
    + {#each section.list as item ('step' in item ? item.step : 'level' in item ? item.level : item.point)} +
  • + {'step' in item ? item.step : 'level' in item ? item.level : item.point}: + {item.desc} +
  • + {/each} +
+ {:else if section.simpleList} +
    + {#each section.simpleList as item, i (i)} +
  • {item}
  • + {/each} +
+ {/if} +
+
+ {/each} +
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/email/mail-tls-check/+page.svelte b/src/routes/[lang]/diagnostics/email/mail-tls-check/+page.svelte new file mode 100644 index 00000000..b7bf7c57 --- /dev/null +++ b/src/routes/[lang]/diagnostics/email/mail-tls-check/+page.svelte @@ -0,0 +1,616 @@ + + +
+
+

{content.title}

+

{content.description}

+
+ + +
+
+

Check Mail Server TLS

+
+
+
+ e.key === 'Enter' && checkTLS()} + disabled={diagnosticState.loading} + /> + + +
+
+
+ + + `${ex.domain}:${ex.port}`} + getDescription={(ex) => ex.desc} + getTooltip={(ex) => `Check TLS for ${ex.domain} on port ${ex.port}`} + /> + + + {#if diagnosticState.loading} +
+
+
+ +
+

Checking TLS Support

+

Testing connection to {domain}:{port}...

+
+
+
+
+ {/if} + + + + + {#if diagnosticState.results} +
+
+

TLS Check Results for {diagnosticState.results.domain}:{diagnosticState.results.port}

+
+
+ +
+ {#if diagnosticState.results.supportsSTARTTLS} +
+ +
+

STARTTLS Supported

+

Server supports upgrading to TLS

+
+
+ {/if} + {#if diagnosticState.results.supportsDirectTLS} +
+ +
+

Direct TLS Supported

+

Server supports implicit TLS

+
+
+ {/if} + {#if !diagnosticState.results.supportsSTARTTLS && !diagnosticState.results.supportsDirectTLS} +
+ +
+

TLS Not Supported

+

Server does not support TLS encryption

+
+
+ {/if} +
+ + + {#if diagnosticState.results.tlsVersion || diagnosticState.results.cipherSuite} +
+

+ + Connection Details +

+
+ {#if diagnosticState.results.tlsVersion} +
+ TLS Version + {diagnosticState.results.tlsVersion} +
+ {/if} + {#if diagnosticState.results.cipherSuite} +
+ Cipher Suite + {diagnosticState.results.cipherSuite} +
+ {/if} +
+
+ {/if} + + + {#if diagnosticState.results.certificate} +
+

+ + Certificate Information +

+
+
+ Common Name + {diagnosticState.results.certificate.commonName} +
+
+ Issuer + {diagnosticState.results.certificate.issuer} +
+
+ Valid From + {new Date(diagnosticState.results.certificate.validFrom).toLocaleDateString()} +
+
+ Valid To + + {new Date(diagnosticState.results.certificate.validTo).toLocaleDateString()} + {#if diagnosticState.results.certificate.daysUntilExpiry < 30} + Expires in {diagnosticState.results.certificate.daysUntilExpiry} days + {/if} + +
+
+ Serial Number + {diagnosticState.results.certificate.serialNumber} +
+
+ Fingerprint + {diagnosticState.results.certificate.fingerprint} +
+ {#if diagnosticState.results.certificate.altNames.length > 0} +
+ Alternative Names ({diagnosticState.results.certificate.altNames.length}) +
+ {#each diagnosticState.results.certificate.altNames as altName (altName)} + {altName} + {/each} +
+
+ {/if} +
+
+ {/if} +
+
+ {/if} + + +
+
+

About SMTP TLS

+
+
+
+ + +

{content.sections.whatIsTLS.title}

+
+
+

{content.sections.whatIsTLS.content}

+
+
+ +
+ + +

{content.sections.portInfo.title}

+
+
+
+ {#each content.sections.portInfo.ports as portInfo (portInfo.port)} +
+
{portInfo.port}
+
+ {portInfo.name} +

{portInfo.desc}

+ {portInfo.security} +
+
+ {/each} +
+
+
+ + {#each [{ title: content.sections.tlsTypes.title, items: content.sections.tlsTypes.types, keys: ['name', 'desc', 'ports'] }, { title: content.sections.certificateFields.title, items: content.sections.certificateFields.fields, keys: ['field', 'desc'] }, { title: content.sections.security.title, items: content.sections.security.points, keys: ['point', 'desc'] }, { title: content.sections.troubleshooting.title, items: content.sections.troubleshooting.issues, keys: ['issue', 'solution'] }] as section (section.title)} +
+ + +

{section.title}

+
+
+
    + {#each section.items as item ((item as any)[section.keys[0]])} +
  • + {(item as any)[section.keys[0]]}: + {(item as any)[section.keys[1]]} + {#if section.keys[2] && (item as any)[section.keys[2]]} + ({(item as any)[section.keys[2]]}) + {/if} +
  • + {/each} +
+
+
+ {/each} + +
+ + +

Quick Tips

+
+
+
    + {#each content.quickTips as tip (tip)} +
  • {tip}
  • + {/each} +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/email/mx-health/+page.svelte b/src/routes/[lang]/diagnostics/email/mx-health/+page.svelte new file mode 100644 index 00000000..c5013706 --- /dev/null +++ b/src/routes/[lang]/diagnostics/email/mx-health/+page.svelte @@ -0,0 +1,725 @@ + + +
+
+

Email MX Health Checker

+

+ Check mail server (MX) health including DNS resolution and optional SMTP port connectivity testing. Verify your + email infrastructure is properly configured and reachable. +

+
+ + + ex.domain} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Check MX health for ${ex.domain}`} + /> + + +
+
+

MX Health Check

+
+
+
+ +
+ +
+ +
+ +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

MX Health Results

+ +
+
+ +
+
+ +
+

+ {#if diagnosticState.results.summary.healthy} + Mail Infrastructure Healthy + {:else} + Mail Infrastructure Issues + {/if} +

+

+ {diagnosticState.results.summary.healthyMX} of {diagnosticState.results.summary.totalMX} MX records resolved + successfully + {#if checkPorts && diagnosticState.results.summary.reachableMX !== null} + • {diagnosticState.results.summary.reachableMX} reachable via SMTP + {/if} +

+
+
+ +
+
+ +
+ MX Records + {diagnosticState.results.summary.totalMX} +
+
+ +
+ +
+ Healthy + {diagnosticState.results.summary.healthyMX} +
+
+ + {#if checkPorts && diagnosticState.results.summary.reachableMX !== null} +
+ +
+ Reachable + {diagnosticState.results.summary.reachableMX} +
+
+ {/if} + +
+ +
+ Redundancy + {diagnosticState.results.summary.hasRedundancy ? 'Yes' : 'No'} +
+
+
+
+ + +
+

MX Records (by priority)

+
+ {#each (diagnosticState.results as { mxRecords: Array<{ error?: string; exchange: string; priority: number; addresses?: { ipv4: string[]; ipv6: string[] }; portChecks?: Array<{ port: number; open: boolean; latency?: number }> }> }).mxRecords as mx, _index (_index)} +
+
+
+
+ + {mx.exchange} + Priority {mx.priority} +
+ {#if mx.error} +
+ + {mx.error} +
+ {/if} +
+ +
+ +
+
+ + {#if mx.addresses && !mx.error} +
+ +
+
+
+ + IPv4 Addresses +
+
+ {#if mx.addresses.ipv4.length > 0} + {#each mx.addresses.ipv4 as ip, ipIndex (ipIndex)} + {ip} + {/each} + {:else} + None + {/if} +
+
+ +
+
+ + IPv6 Addresses +
+
+ {#if mx.addresses.ipv6.length > 0} + {#each mx.addresses.ipv6 as ip, ipIndex (ipIndex)} + {ip} + {/each} + {:else} + None + {/if} +
+
+
+ + + {#if mx.portChecks && checkPorts} +
+
+ + SMTP Port Connectivity +
+
+ {#each mx.portChecks || [] as portCheck, portIndex (portIndex)} +
+
+ {portCheck.port} + {getPortDescription(portCheck.port)} +
+
+ + {portCheck.open ? 'Open' : 'Closed'} + {#if portCheck.latency} + ({portCheck.latency}ms) + {/if} +
+
+ {/each} +
+
+ {/if} +
+ {/if} +
+ {/each} +
+
+
+
+ {/if} + + + + +
+
+

Understanding MX Records

+
+
+
+
+

MX Record Basics

+
    +
  • Priority: Lower numbers have higher priority
  • +
  • Exchange: The mail server hostname
  • +
  • Redundancy: Multiple MX records provide failover
  • +
  • Load balancing: Equal priorities distribute load
  • +
+
+ +
+

SMTP Ports

+
+
+ Port 25: Standard SMTP (server-to-server) +
+
+ Port 587: Mail submission (client-to-server, TLS) +
+
+ Port 465: SMTPS (deprecated but still used) +
+
+
+ +
+

Health Indicators

+
    +
  • All MX records should resolve to IP addresses
  • +
  • At least one SMTP port should be reachable
  • +
  • Multiple MX records provide redundancy
  • +
  • Lower priority servers should be reachable
  • +
+
+ +
+

Common Issues

+
    +
  • MX pointing to non-existent hosts
  • +
  • All SMTP ports blocked by firewall
  • +
  • Single point of failure (one MX record)
  • +
  • Incorrect priority configuration
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/email/spf-check/+page.svelte b/src/routes/[lang]/diagnostics/email/spf-check/+page.svelte new file mode 100644 index 00000000..86201796 --- /dev/null +++ b/src/routes/[lang]/diagnostics/email/spf-check/+page.svelte @@ -0,0 +1,829 @@ + + +
+
+

Email SPF Policy Checker

+

+ Check SPF (Sender Policy Framework) records for email authentication and deliverability. Analyze which servers are + authorized to send email for your domain and assess delivery risk. +

+
+ + +
+
+ + +

SPF Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

SPF Policy Check

+
+
+
+ +
+ +
+ +
+
+
+ + + {#if results} +
+
+

SPF Policy Analysis

+ +
+
+ {#if results.record} + + {#if results.emailAnalysis} +
+
+ +
+

Email Deliverability Risk: {results.emailAnalysis.deliverabilityRisk.toUpperCase()}

+

+ {#if results.emailAnalysis.deliverabilityRisk === 'low'} + Strong SPF policy with hard fail - excellent email security + {:else if results.emailAnalysis.deliverabilityRisk === 'medium'} + Moderate SPF policy with soft fail - good but could be stronger + {:else} + Weak or missing SPF policy - high risk of email spoofing + {/if} +

+
+
+ +
+
+ +
+ Hard Fail (-all) + {results.emailAnalysis.hasHardFail ? 'Enabled' : 'Disabled'} + + {results.emailAnalysis.hasHardFail + ? 'Unauthorized emails will be rejected' + : 'Consider upgrading to -all for better security'} + +
+
+ +
+ +
+ Soft Fail (~all) + {results.emailAnalysis.hasSoftFail ? 'Enabled' : 'Disabled'} + + {results.emailAnalysis.hasSoftFail + ? 'Unauthorized emails marked as suspicious' + : results.emailAnalysis.hasHardFail + ? 'Using stronger hard fail instead' + : 'No SPF enforcement configured'} + +
+
+ + {#if results.emailAnalysis.allowsAll} +
+ +
+ Allows All (+all) + Enabled + WARNING: Any server can send email for this domain +
+
+ {/if} +
+
+ {/if} + + +
+

SPF Record

+
+
TXT record for {domain}
+ {results.record} +
+
+ + + {#if results.expanded} +
+

SPF Policy Breakdown

+ + + {#if results.lookupCount > 8} +
+ +
+ DNS Lookup Limit Exceeded +

+ This SPF record requires {results.lookupCount} DNS lookups, which exceeds the RFC limit of 10. This + may cause delivery failures. +

+
+
+ {:else if results.lookupCount > 6} +
+ +
+ High DNS Lookup Count +

+ This SPF record requires {results.lookupCount} DNS lookups. Consider optimizing to stay well below + the 10-lookup limit. +

+
+
+ {/if} + + + {#if results.expanded.mechanisms.length > 0} + {@const spfExpanded = (results as { expanded: { mechanisms: string[] } }).expanded} +
+
Direct Mechanisms
+
+ {#each spfExpanded.mechanisms as mechanism, mechanismIndex (mechanismIndex)} +
+ {mechanism} + + {#if mechanism.startsWith('v=spf1')} + SPF version identifier + {:else if mechanism.startsWith('ip4:')} + IPv4 address or network: {mechanism.substring(4)} + {:else if mechanism.startsWith('ip6:')} + IPv6 address or network: {mechanism.substring(4)} + {:else if mechanism.startsWith('a:')} + A record lookup for: {mechanism.substring(2)} + {:else if mechanism === 'a'} + A record lookup for domain itself + {:else if mechanism.startsWith('mx:')} + MX record lookup for: {mechanism.substring(3)} + {:else if mechanism === 'mx'} + MX record lookup for domain itself + {:else if mechanism.startsWith('exists:')} + DNS lookup test: {mechanism.substring(7)} + {:else if mechanism === '-all'} + Hard fail - reject unauthorized emails + {:else if mechanism === '~all'} + Soft fail - mark unauthorized emails as suspicious + {:else if mechanism === '+all'} + Pass all - allow any server (dangerous) + {:else if mechanism === '?all'} + Neutral - no policy decision + {:else} + {mechanism} + {/if} + +
+ {/each} +
+
+ {/if} + + + {#if results.expanded.includes.length > 0} + {@const spfIncludes = ( + results as { + expanded: { includes: Array<{ domain: string; result: { record?: string; error?: string } }> }; + } + ).expanded} +
+
Included SPF Policies
+
+ {#each spfIncludes.includes as include, includeIndex (includeIndex)} +
+
+ + {include.domain} +
+ {#if include.result.record} +
+ {include.result.record} +
+ {/if} + {#if include.result.error} +
+ + {include.result.error} +
+ {/if} +
+ {/each} +
+
+ {/if} +
+ {/if} + {:else} +
+
+ +
+

No SPF Record Found

+

Domain {domain} does not have an SPF record configured.

+

+ This means anyone can send email claiming to be from this domain, significantly increasing spoofing + risk. +

+
+
+
+ {/if} +
+
+ {/if} + + {#if error} +
+
+
+ +
+ SPF Check Failed +

{error}

+
+
+
+
+ {/if} + + +
+
+

Understanding SPF for Email

+
+
+
+
+

SPF Mechanisms

+
+
+ ip4/ip6: Authorize specific IP addresses or networks +
+
+ a/mx: Authorize servers from A or MX records +
+
+ include: Include another domain's SPF policy +
+
+ all: Final policy decision (+pass, ~soft fail, -hard fail) +
+
+
+ +
+

Email Deliverability

+
    +
  • Hard Fail (-all): Best security, blocks unauthorized senders
  • +
  • Soft Fail (~all): Marks suspicious, doesn't block delivery
  • +
  • No SPF: High spoofing risk, may affect deliverability
  • +
  • Too many lookups: Can cause delivery failures
  • +
+
+ +
+

Best Practices

+
    +
  • Use -all for hard fail when possible
  • +
  • Keep DNS lookups under 10 (preferably under 5)
  • +
  • Test SPF changes before deployment
  • +
  • Monitor email delivery after SPF changes
  • +
+
+ +
+

Common SPF Examples

+
+
+ v=spf1 include:_spf.google.com ~all + Use Google Workspace with soft fail +
+
+ v=spf1 ip4:192.168.1.1 -all + Only allow specific IP with hard fail +
+
+ v=spf1 a mx -all + Allow A and MX record servers with hard fail +
+
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/http/compression/+page.svelte b/src/routes/[lang]/diagnostics/http/compression/+page.svelte new file mode 100644 index 00000000..eb4e1986 --- /dev/null +++ b/src/routes/[lang]/diagnostics/http/compression/+page.svelte @@ -0,0 +1,466 @@ + + +
+
+

HTTP Compression Check

+

Test gzip, brotli, and deflate compression support and measure size differences

+
+ + + ex.url} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Test compression for ${ex.url}`} + /> + + +
+
+

URL to Test

+
+
+
+ +
+ examples.clear()} + onkeydown={(e) => e.key === 'Enter' && checkCompression()} + /> + +
+
+
+
+ + + + {#if diagnosticState.loading} +
+
+
+ +
+

Testing Compression

+

Checking support for gzip, brotli, and deflate compression...

+
+
+
+
+ {/if} + + {#if diagnosticState.results} +
+
+

Compression Results

+
+
+ +
+
+

Overview

+
+
+
+
+
Server Compression
+
+ + {diagnosticState.results.serverCompression.enabled ? 'Enabled' : 'Disabled'} +
+ {#if diagnosticState.results.serverCompression.encoding} +
{diagnosticState.results.serverCompression.encoding}
+ {/if} +
+ +
+
Best Compression
+
+ + {diagnosticState.results.bestCompression.encoding} +
+
{diagnosticState.results.bestCompression.ratio.toFixed(1)}% reduction
+
+ +
+
Uncompressed Size
+
{formatBytes(diagnosticState.results.uncompressed.size)}
+
+ +
+
Time Taken
+
{diagnosticState.results.timings.total}ms
+
+
+
+
+ + +
+
+

Compression Methods

+
+
+
+ {#each diagnosticState.results.compressionResults as result (result.encoding)} +
+
+
+ + {result.encoding} +
+
+ + {result.supported ? 'Supported' : 'Not Supported'} +
+
+ + {#if result.supported} +
+
+
+
+
+
+
+ {formatBytes(diagnosticState.results.uncompressed.size)} + {formatBytes(result.compressedSize)} +
+
+ +
+
+ Reduction: + {result.ratio.toFixed(1)}% +
+
+ Time: + {result.responseTime}ms +
+
+
+ {/if} +
+ {/each} +
+
+
+ + +
+
+

Response Headers

+
+
+
+ {#each Object.entries(diagnosticState.results.headers) as [key, value] (key)} +
+ {key} + {value} +
+ {/each} +
+
+
+
+
+ {/if} +
+ + diff --git a/src/routes/[lang]/diagnostics/http/cookie-security/+page.svelte b/src/routes/[lang]/diagnostics/http/cookie-security/+page.svelte new file mode 100644 index 00000000..0865e8f9 --- /dev/null +++ b/src/routes/[lang]/diagnostics/http/cookie-security/+page.svelte @@ -0,0 +1,712 @@ + + +
+
+

HTTP Cookie Security Inspector

+

Analyze Set-Cookie headers for Secure, HttpOnly, SameSite, and other security attributes

+
+ + + ex.url} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Inspect cookies for ${ex.url}`} + /> + + +
+
+

URL to Inspect

+
+
+
+ +
+ examples.clear()} + onkeydown={(e) => e.key === 'Enter' && checkCookieSecurity()} + /> + +
+
+
+
+ + + + {#if diagnosticState.loading} +
+
+
+ +
+

Analyzing Cookies

+

Inspecting Set-Cookie headers for security attributes...

+
+
+
+
+ {/if} + + {#if diagnosticState.results} +
+
+

Cookie Security Analysis

+
+
+ +
+
+

Security Score

+
+
+
+
+
{getSecurityGrade(diagnosticState.results.securityScore).grade}
+
+ {diagnosticState.results.securityScore !== null + ? `${diagnosticState.results.securityScore}/100` + : 'No cookies'} +
+
+
+

Overall Assessment

+

{diagnosticState.results.summary}

+
+
+ Total Cookies: + {diagnosticState.results.totalCookies} +
+
+ Secure Cookies: + {diagnosticState.results.secureCookies} +
+
+ HttpOnly Cookies: + {diagnosticState.results.httpOnlyCookies} +
+
+
+
+
+
+ + + {#if diagnosticState.results.cookies && diagnosticState.results.cookies.length > 0} +
+
+

Cookie Details

+
+
+
+ {#each diagnosticState.results.cookies as cookie (cookie.id || cookie.name)} + + {/each} +
+
+
+ {:else} +
+
+
+ +

No Cookies Found

+

The server did not send any Set-Cookie headers in the response.

+
+
+
+ {/if} + + + {#if diagnosticState.results.recommendations && diagnosticState.results.recommendations.length > 0} +
+
+

Security Recommendations

+
+
+
+ {#each diagnosticState.results.recommendations as recommendation, index (index)} +
+ +
+

{recommendation.title}

+

{recommendation.description}

+ {#if recommendation.example} + {recommendation.example} + {/if} +
+
+ {/each} +
+
+
+ {/if} +
+
+ {/if} +
+ + diff --git a/src/routes/[lang]/diagnostics/http/cors-check/+page.svelte b/src/routes/[lang]/diagnostics/http/cors-check/+page.svelte new file mode 100644 index 00000000..52676e73 --- /dev/null +++ b/src/routes/[lang]/diagnostics/http/cors-check/+page.svelte @@ -0,0 +1,531 @@ + + +
+
+

CORS Policy Checker

+

+ Test Cross-Origin Resource Sharing (CORS) policies by sending preflight requests and analyzing the server's CORS + configuration. Check if your origin is allowed to access the target resource. +

+
+ + + ex.url} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Test CORS policy for ${ex.url}`} + /> + + +
+
+

CORS Test Configuration

+
+
+
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+ + Check CORS Policy + +
+
+
+ + + {#if diagnosticState.results} + +
+ +
+
+ +
+ {getCORSStatusText()} +
CORS Status
+
+
+ +
+ +
+ {diagnosticState.results.preflight.status || 'Failed'} +
Preflight Status
+
+
+ + {#if diagnosticState.results.analysis.maxAge} +
+ +
+ {diagnosticState.results.analysis.maxAge}s +
Cache Max Age
+
+
+ {/if} +
+ + +
+

CORS Policy Details

+
+
+ +
+ CORS Enabled +

+ {diagnosticState.results.analysis.corsEnabled + ? 'Server has CORS headers configured' + : 'No CORS headers found - requests will be blocked by browsers'} +

+
+
+ +
+ +
+ Origin Access +

+ {#if diagnosticState.results.analysis.allowsOrigin} + Origin '{origin}' is allowed to access this resource + {:else} + Origin '{origin}' is not allowed to access this resource + {/if} +

+
+
+ +
+ +
+ Credentials Support +

+ {diagnosticState.results.analysis.allowsCredentials + ? 'Cookies and credentials can be sent' + : 'Cookies and credentials cannot be sent'} +

+
+
+
+
+ + + {#if diagnosticState.results.analysis.allowedMethods?.length > 0} +
+

Allowed Methods

+
+ {#each diagnosticState.results.analysis.allowedMethods as allowedMethod, index (index)} + {allowedMethod} + {/each} +
+
+ {/if} + + + {#if diagnosticState.results.analysis.allowedHeaders?.length > 0} +
+

Allowed Headers

+
+ {#each diagnosticState.results.analysis.allowedHeaders as header, index (index)} + {header} + {/each} +
+
+ {/if} + + + {#if Object.keys(diagnosticState.results.preflight.headers || {}).length > 0} +
+

CORS Headers

+
+ {#each Object.entries(diagnosticState.results.preflight.headers) as [name, value] (name)} +
+
+ {name}: + {value} +
+
+ {/each} +
+
+ {:else if diagnosticState.results.analysis.corsEnabled} +
+ +

CORS enabled but no detailed headers available

+
+ {:else} +
+ +

No CORS headers found

+

+ The server does not provide CORS headers - cross-origin requests will be blocked by browsers +

+
+ {/if} +
+
+ {/if} + + + + +
+
+

About CORS

+
+
+
+
+

What is CORS?

+

+ Cross-Origin Resource Sharing (CORS) is a security mechanism that allows or restricts web pages from making + requests to a different domain, protocol, or port than the one serving the web page. +

+
+ +
+

Preflight Requests

+

+ For certain requests, browsers send a preflight OPTIONS request to check if the actual request is allowed. + The server responds with CORS headers indicating permissions. +

+
+ +
+

Common CORS Headers

+
    +
  • Access-Control-Allow-Origin: Allowed origins
  • +
  • Access-Control-Allow-Methods: Allowed HTTP methods
  • +
  • Access-Control-Allow-Headers: Allowed request headers
  • +
  • Access-Control-Allow-Credentials: Cookie support
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/http/headers/+page.svelte b/src/routes/[lang]/diagnostics/http/headers/+page.svelte new file mode 100644 index 00000000..5f76d8a0 --- /dev/null +++ b/src/routes/[lang]/diagnostics/http/headers/+page.svelte @@ -0,0 +1,383 @@ + + +
+
+

HTTP Headers Analyzer

+

+ Analyze HTTP response headers, status codes, and response metadata. Supports custom request methods and headers + for comprehensive HTTP testing. +

+
+ + + `${ex.method} ${ex.url}`} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Analyze headers for ${ex.url}`} + /> + + +
+
+

Request Configuration

+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+ + Analyze Headers + +
+
+
+ + + {#if diagnosticState.results} + + +
+
+ +
+ {diagnosticState.results.status} {diagnosticState.results.statusText} +
HTTP Status
+
+
+ + {#if diagnosticState.results.size} +
+ +
+ {formatBytes(diagnosticState.results.size)} +
Response Size
+
+
+ {/if} + + {#if diagnosticState.results.timings} +
+ +
+ {diagnosticState.results.timings.total.toFixed(0)}ms +
Total Time
+
+
+ {/if} +
+ + +
+

Response Headers

+
+ {#each Object.entries(diagnosticState.results.headers) as [name, value] (name)} +
+
+ {name}: + {value} +
+
+ {/each} +
+
+ + {#if diagnosticState.results.timings} +
+

Performance Timing

+
+
+
+ DNS Resolution: ~{diagnosticState.results.timings.dns.toFixed(1)}ms +
+
+
+
+ TCP Connect: ~{diagnosticState.results.timings.tcp.toFixed(1)}ms +
+
+ {#if diagnosticState.results.timings.tls > 0} +
+
+ TLS Handshake: ~{diagnosticState.results.timings.tls.toFixed(1)}ms +
+
+ {/if} +
+
+ Time to First Byte: ~{diagnosticState.results.timings.ttfb.toFixed(1)}ms +
+
+
+

* Timing values are approximations when not isolated

+
+ {/if} +
+ {/if} + + + + +
+
+

About HTTP Headers

+
+
+
+
+

Response Headers

+

+ HTTP headers provide metadata about the response, including content type, caching instructions, security + policies, and server information. +

+
+ +
+

Status Codes

+
    +
  • 2xx: Success responses
  • +
  • 3xx: Redirection responses
  • +
  • 4xx: Client error responses
  • +
  • 5xx: Server error responses
  • +
+
+ +
+

Common Headers

+
    +
  • Content-Type: MIME type of content
  • +
  • Cache-Control: Caching directives
  • +
  • Set-Cookie: Cookie instructions
  • +
  • Location: Redirect target URL
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/http/perf/+page.svelte b/src/routes/[lang]/diagnostics/http/perf/+page.svelte new file mode 100644 index 00000000..1388e2cf --- /dev/null +++ b/src/routes/[lang]/diagnostics/http/perf/+page.svelte @@ -0,0 +1,578 @@ + + +
+
+

HTTP Performance Analyzer

+

+ Measure HTTP request performance including DNS resolution, TCP connection, TLS handshake, and response times. Get + detailed timing breakdowns and performance insights. +

+
+ + + ex.url} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Measure performance for ${ex.url}`} + /> + + +
+
+

Performance Test Configuration

+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ + + {#if diagnosticState.results} + {@const grade = getPerformanceGrade(diagnosticState.results.timings.total)} +
+
+

Performance Analysis

+ +
+
+ +
+
+ +
+ Grade {grade.grade} +
{diagnosticState.results.timings.total.toFixed(0)}ms Total
+
+
+ +
+ +
+ {diagnosticState.results.status} +
HTTP Status
+
+
+ + {#if diagnosticState.results.size} +
+ +
+ {formatBytes(diagnosticState.results.size)} +
Response Size
+
+
+ {/if} + + {#if diagnosticState.results.size && diagnosticState.results.timings.total} +
+ +
+ {calculateThroughput(diagnosticState.results.size, diagnosticState.results.timings.total)} +
Throughput
+
+
+ {/if} +
+ + +
+

Performance Timing Breakdown

+
+
+
+ + DNS Resolution + {#if diagnosticState.results.timings.dns_note} + ({diagnosticState.results.timings.dns_note}) + {/if} +
+
+
+
+
{diagnosticState.results.timings.dns.toFixed(1)}ms
+
+ +
+
+ + TCP Connect + {#if diagnosticState.results.timings.tcp_note} + ({diagnosticState.results.timings.tcp_note}) + {/if} +
+
+
+
+
{diagnosticState.results.timings.tcp.toFixed(1)}ms
+
+ + {#if diagnosticState.results.timings.tls > 0} +
+
+ + TLS Handshake + {#if diagnosticState.results.timings.tls_note} + ({diagnosticState.results.timings.tls_note}) + {/if} +
+
+
+
+
{diagnosticState.results.timings.tls.toFixed(1)}ms
+
+ {/if} + +
+
+ + Time to First Byte +
+
+
+
+
{diagnosticState.results.timings.ttfb.toFixed(1)}ms
+
+ +
+
+ + Total Time +
+
+
+
+
{diagnosticState.results.timings.total.toFixed(1)}ms
+
+
+
+ + +
+

Connection Features

+
+
+ +
+ HTTPS +

{diagnosticState.results.performance.isHTTPS ? 'Secure connection' : 'Unencrypted connection'}

+
+
+ +
+ +
+ Compression +

+ {diagnosticState.results.performance.hasCompression + ? 'Response is compressed' + : 'No compression detected'} +

+
+
+ +
+ +
+ HTTP Version +

{diagnosticState.results.performance.httpVersion}

+
+
+ +
+ +
+ Connection Reuse +

{diagnosticState.results.performance.connectionReused}

+
+
+
+
+
+
+ {/if} + + + + +
+
+

About HTTP Performance

+
+
+
+
+

Timing Components

+
    +
  • DNS: Domain name resolution time
  • +
  • TCP: TCP connection establishment
  • +
  • TLS: SSL/TLS handshake (HTTPS only)
  • +
  • TTFB: Server processing and response start
  • +
+
+ +
+

Performance Grades

+
    +
  • A (≤200ms): Excellent performance
  • +
  • B (≤500ms): Good performance
  • +
  • C (≤1000ms): Acceptable performance
  • +
  • D/F (>1000ms): Poor performance
  • +
+
+ +
+

Optimization Tips

+

+ Use CDN for faster response times, enable compression, implement HTTP/2, optimize DNS resolution, and + consider connection keep-alive for multiple requests. +

+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/http/redirect-trace/+page.svelte b/src/routes/[lang]/diagnostics/http/redirect-trace/+page.svelte new file mode 100644 index 00000000..2b0104a7 --- /dev/null +++ b/src/routes/[lang]/diagnostics/http/redirect-trace/+page.svelte @@ -0,0 +1,525 @@ + + +
+
+

HTTP Redirect Tracer

+

+ Follow and analyze HTTP redirect chains to understand the complete journey from initial URL to final destination. + Track status codes, locations, and security implications. +

+
+ + + ex.url} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Trace redirects for ${ex.url}`} + /> + + +
+
+

Redirect Trace Configuration

+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

Redirect Chain Analysis

+ +
+
+ +
+
+ +
+ {diagnosticState.results.totalRedirects} +
Total Redirects
+
+
+ +
+ +
+ {diagnosticState.results.finalStatus} +
Final Status
+
+
+ + {#if diagnosticState.results.timings} +
+ +
+ {diagnosticState.results.timings.total.toFixed(0)}ms +
Total Time
+
+
+ {/if} +
+ + + {#if diagnosticState.results.redirectChain?.length > 0} +
+

Redirect Chain

+
+ {#each diagnosticState.results.redirectChain as step, i (i)} +
+
{i + 1}
+
+
+
+ + {step.status} +
+ {#if hasHSTS(step.headers)} +
+ + HSTS +
+ {/if} + {#if i < diagnosticState.results.redirectChain.length - 1 && isSecureRedirect(step.url, step.location)} +
+ + Secure Upgrade +
+ {/if} +
+
{step.url}
+ {#if step.location} +
+ + {step.location} +
+ {/if} +
+
+ + {#if i < diagnosticState.results.redirectChain.length - 1} +
+ +
+ {/if} + {/each} +
+
+ +
+

Final Destination

+
+
+ + {diagnosticState.results.finalStatus} +
+
{diagnosticState.results.finalUrl}
+
+
+ {:else} +
+ +

No redirects found - URL resolved directly

+

Final URL: {diagnosticState.results.finalUrl}

+
+ {/if} +
+
+ {/if} + + + + +
+
+

About HTTP Redirects

+
+
+
+
+

Redirect Types

+
    +
  • 301: Permanent redirect
  • +
  • 302: Temporary redirect
  • +
  • 303: See other (POST → GET)
  • +
  • 307: Temporary (preserve method)
  • +
  • 308: Permanent (preserve method)
  • +
+
+ +
+

Security Considerations

+
    +
  • HSTS: Prevents downgrade attacks
  • +
  • HTTP → HTTPS: Security upgrades
  • +
  • Open Redirects: Potential security risk
  • +
  • Redirect Loops: Infinite chains
  • +
+
+ +
+

Performance Impact

+

+ Each redirect adds latency. Minimize redirect chains for better performance. Use 301/308 for permanent moves + and 302/307 for temporary ones. +

+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/http/security/+page.svelte b/src/routes/[lang]/diagnostics/http/security/+page.svelte new file mode 100644 index 00000000..ddf6d17c --- /dev/null +++ b/src/routes/[lang]/diagnostics/http/security/+page.svelte @@ -0,0 +1,470 @@ + + +
+
+

HTTP Security Headers Analyzer

+

+ Analyze and evaluate security headers to identify potential vulnerabilities and security improvements. Check for + HSTS, CSP, XSS protection, and other essential security headers. +

+
+ + + ex.url} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Analyze security headers for ${ex.url}`} + /> + + +
+
+

Security Analysis

+
+
+
+ +
+ +
+ +
+
+
+ + + {#if diagnosticState.results} + {@const overallScore = getOverallScore()} +
+
+

Security Headers Analysis

+ +
+
+ +
+
+ +
+ Grade {overallScore.grade} +
Security Score: {overallScore.score}%
+
+
+ +
+ +
+ {(diagnosticState.results as { analysis?: Array<{ status: string }> })?.analysis?.filter( + (a) => a.status === 'present', + ).length || 0} +
Headers Present
+
+
+ +
+ +
+ {(diagnosticState.results as { analysis?: Array<{ status: string }> })?.analysis?.filter( + (a) => a.status === 'missing', + ).length || 0} +
Headers Missing
+
+
+
+ + +
+

Security Header Analysis

+
+ {#each diagnosticState.results.analysis as analysis, index (index)} +
+
+
+ + {analysis.header} +
+
+ {analysis.status} +
+
+
{analysis.message}
+ {#if analysis.recommendation} +
+ + {analysis.recommendation} +
+ {/if} +
+ {/each} +
+
+ + + {#if Object.keys(diagnosticState.results.headers || {}).length > 0} +
+

Security Headers Found

+
+ {#each Object.entries(diagnosticState.results.headers) as [name, value], index (index)} +
+
+ {name}: + {value} +
+
+ {/each} +
+
+ {:else} +
+ +

No security headers found

+

This site may be vulnerable to various attacks

+
+ {/if} +
+
+ {/if} + + + + +
+
+

About Security Headers

+
+
+
+
+

Critical Headers

+
    +
  • Strict-Transport-Security: Forces HTTPS connections
  • +
  • Content-Security-Policy: Prevents XSS and injection attacks
  • +
  • X-Frame-Options: Prevents clickjacking attacks
  • +
  • X-Content-Type-Options: Prevents MIME sniffing
  • +
+
+ +
+

Additional Protection

+
    +
  • Referrer-Policy: Controls referrer information
  • +
  • Permissions-Policy: Controls browser features
  • +
  • Cross-Origin-*: CORS and isolation policies
  • +
  • X-XSS-Protection: Legacy XSS protection
  • +
+
+ +
+

Implementation Tips

+

+ Start with basic headers (HSTS, CSP, X-Frame-Options) and gradually add more. Test thoroughly as some + headers may break functionality if misconfigured. +

+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/network/asn-geo-lookup/+page.svelte b/src/routes/[lang]/diagnostics/network/asn-geo-lookup/+page.svelte new file mode 100644 index 00000000..2918a4ab --- /dev/null +++ b/src/routes/[lang]/diagnostics/network/asn-geo-lookup/+page.svelte @@ -0,0 +1,625 @@ + + +
+
+

{asnGeoContent.title}

+

{asnGeoContent.description}

+
+ + +
+
+ + +

Example Lookups

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

IP Lookup

+
+
+
+
+ + { + clearExampleSelection(); + if (ip.trim()) lookupIP(); + }} + /> +
+ +
+
+
+ + + {#if results} +
+
+

Results for {results.ip}

+ +
+
+
+ +
+

Network Information

+
+ {#if results.asn} +
+ +
+ ASN + AS{results.asn} +
+
+ {/if} + {#if results.asnOrg} +
+ +
+ Organization + {results.asnOrg} +
+
+ {/if} + {#if results.isp} +
+ +
+ ISP + {results.isp} +
+
+ {/if} +
+
+ + +
+

Geographic Location

+
+ {#if results.country} +
+ +
+ Country + {results.country} ({results.countryCode}) +
+
+ {/if} + {#if results.regionName} +
+ +
+ Region + {results.regionName} +
+
+ {/if} + {#if results.city} +
+ +
+ City + {results.city} +
+
+ {/if} + {#if results.timezone} +
+ +
+ Timezone + {results.timezone} +
+
+ {/if} +
+
+ + + {#if results.latitude !== undefined && results.longitude !== undefined} +
+

Coordinates

+
+
+
+ +
+
+ Latitude: + {results.latitude.toFixed(4)}° +
+
+ Longitude: + {results.longitude.toFixed(4)}° +
+
+
+ + + View full map + +
+
+ +
+
+
+ {/if} + + +
+

Connection Type

+
+
+ + Mobile Network +
+
+ + Proxy/VPN +
+
+ + Hosting/Datacenter +
+
+
+
+
+
+ {/if} + + {#if error} +
+
+
+ +
+ Lookup Failed +

{error}

+
+
+
+
+ {/if} + + +
+
+

About ASN & Geolocation

+
+
+
+
+

{asnGeoContent.sections.whatIsGeoIP.title}

+

{asnGeoContent.sections.whatIsGeoIP.content}

+
+ +
+

{asnGeoContent.sections.accuracy.title}

+
    + {#each asnGeoContent.sections.accuracy.levels as level (level.level)} +
  • + {level.level} ({level.accuracy}): + {level.description} +
  • + {/each} +
+
+ +
+

{asnGeoContent.sections.asnExplained.title}

+

{asnGeoContent.sections.asnExplained.content}

+
+ +
+

{asnGeoContent.sections.dataSource.title}

+

{asnGeoContent.sections.dataSource.content}

+
+
+ +
+

Quick Tips

+
    + {#each asnGeoContent.quickTips as tip, idx (idx)} +
  • {tip}
  • + {/each} +
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/network/bgp-route-lookup/+page.svelte b/src/routes/[lang]/diagnostics/network/bgp-route-lookup/+page.svelte new file mode 100644 index 00000000..9bb93430 --- /dev/null +++ b/src/routes/[lang]/diagnostics/network/bgp-route-lookup/+page.svelte @@ -0,0 +1,691 @@ + + +
+
+

{bgpContent.title}

+

{bgpContent.description}

+
+ + +
+
+ + +

Example Lookups

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

BGP Lookup

+
+
+
+ +
+ { + clearExampleSelection(); + if (resource.trim()) lookupBGP(); + }} + /> + +
+
+
+
+ + + {#if results} +
+
+

BGP Routing Information for {results.resource}

+ +
+
+ +
+
+ +
+

{results.announced ? 'Announced in BGP' : 'Not Announced'}

+

+ {results.announced + ? 'This resource is actively advertised in the global BGP routing table' + : 'This resource is not currently visible in BGP'} +

+
+
+
+ + +
+ + {#if results.originAS} +
+

Origin Autonomous System

+
+ +
+
AS{results.originAS}
+ {#if results.originName} +
{results.originName}
+ {/if} +
+
+
+ {/if} + + + {#if results.asPath && results.asPath.path.length > 0} +
+

AS Path

+
+ {#each results.asPath.path as asn, index (index)} + + AS{asn} + {#if index < results.asPath.path.length - 1} + + {/if} + + {/each} +
+
+ Path length: {results.asPath.path.length} hops +
+
+ {/if} + + + {#if results.prefixes.length > 0} +
+

BGP Prefixes ({results.prefixes.length})

+ {#each results.prefixes as prefix, index (index)} +
+
+ + {prefix.prefix} +
+
+
+ ASN: + AS{prefix.asn} +
+
+ Holder: + {prefix.holder} +
+ {#if prefix.country} +
+ Country: + {prefix.country} +
+ {/if} +
+
+ {/each} +
+ {/if} + + + {#if results.peers && results.peers.length > 0} +
+

BGP Peers ({results.peers.length})

+
+ {#each results.peers.slice(0, 8) as peer, index (index)} +
+ AS{peer.asn} + {#if peer.country} + {peer.country} + {/if} +
+ {/each} +
+
+ {/if} + + + {#if (results.moreSpecifics && results.moreSpecifics.length > 0) || (results.lessSpecifics && results.lessSpecifics.length > 0)} +
+

Related Prefixes

+ +
+ {/if} +
+
+
+ {/if} + + {#if error} +
+
+
+ +
+ BGP Lookup Failed +

{error}

+
+
+
+
+ {/if} + + +
+
+

About BGP Routing

+
+
+
+
+

{bgpContent.sections.whatIsBGP.title}

+

{bgpContent.sections.whatIsBGP.content}

+
+ +
+

{bgpContent.sections.asPath.title}

+

{bgpContent.sections.asPath.content}

+
    + {#each bgpContent.sections.asPath.attributes as attr (attr.name)} +
  • {attr.name}: {attr.description}
  • + {/each} +
+
+ +
+

{bgpContent.sections.routeTypes.title}

+
    + {#each bgpContent.sections.routeTypes.types as type (type.type)} +
  • + {type.type}: + {type.description} + ({type.indicator}) +
  • + {/each} +
+
+ +
+

{bgpContent.sections.dataSource.title}

+

{bgpContent.sections.dataSource.content}

+
+
+ +
+

Quick Tips

+
    + {#each bgpContent.quickTips as tip, idx (idx)} +
  • {tip}
  • + {/each} +
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/network/http-ping/+page.svelte b/src/routes/[lang]/diagnostics/network/http-ping/+page.svelte new file mode 100644 index 00000000..77d8c777 --- /dev/null +++ b/src/routes/[lang]/diagnostics/network/http-ping/+page.svelte @@ -0,0 +1,682 @@ + + +
+
+

HTTP Ping

+

+ Measure HTTP/HTTPS response latency by sending repeated requests and analyzing timing statistics. Alternative to + ICMP ping for web services and APIs. +

+
+ + + ex.description} + getDescription={(ex) => `${ex.url} (${ex.method})`} + getTooltip={(ex) => `Ping ${ex.url} using ${ex.method} method`} + /> + + +
+
+

HTTP Ping Configuration

+
+
+
+
+ +
+
+ + +
+
+

HTTP Method

+
+ {#each httpMethods as methodOption, index (index)} + + {/each} +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

HTTP Ping Results

+ +
+
+ +
+
+ +
+ {diagnosticState.results.successful}/{diagnosticState.results.count} +

Successful requests

+
+
+ {#if diagnosticState.results.statistics?.avg} +
+ +
+ {diagnosticState.results.statistics.avg}ms +

+ Average latency ({getLatencyDescription(diagnosticState.results.statistics.avg)}) +

+
+
+ {/if} + {#if diagnosticState.results.failed > 0} +
+ +
+ {diagnosticState.results.failed} +

Failed requests

+
+
+ {/if} +
+ + {#if diagnosticState.results.statistics && diagnosticState.results.successful > 0} + +
+

Latency Statistics

+
+
+ Minimum: + {diagnosticState.results.statistics.min}ms +
+
+ Maximum: + {diagnosticState.results.statistics.max}ms +
+
+ Average: + {diagnosticState.results.statistics.avg}ms +
+
+ Median: + {diagnosticState.results.statistics.median}ms +
+
+ 95th Percentile: + {diagnosticState.results.statistics.p95}ms +
+
+ Range: + {diagnosticState.results.statistics.max - diagnosticState.results.statistics.min}ms +
+
+
+ + + {#if diagnosticState.results.latencies?.length > 0} +
+

Individual Request Results

+
+ {#each diagnosticState.results.latencies as latency, i (i)} +
+ #{i + 1} + {latency}ms + {getLatencyDescription(latency)} +
+ {/each} +
+
+ {/if} + {/if} + + + {#if diagnosticState.results.errors?.length > 0} +
+

Request Errors ({diagnosticState.results.errors.length})

+
+ {#each diagnosticState.results.errors as error, i (i)} +
+ #{i + diagnosticState.results.successful + 1} + {error} +
+ {/each} +
+
+ {/if} + + +
+

Request Information

+
+
+ URL: + {diagnosticState.results.url} +
+
+ Method: + {diagnosticState.results.method} +
+
+ Success Rate: + {((diagnosticState.results.successful / diagnosticState.results.count) * 100).toFixed(1)}% +
+
+
+
+
+ {/if} + + + + +
+
+

Understanding HTTP Ping

+
+
+
+
+

HTTP vs ICMP Ping

+
    +
  • HTTP: Tests application layer connectivity
  • +
  • ICMP: Tests network layer connectivity
  • +
  • HTTP ping better reflects real user experience
  • +
  • Works through firewalls that block ICMP
  • +
+
+ +
+

Request Methods

+
    +
  • HEAD: Headers only, fastest and most efficient
  • +
  • GET: Full response, more realistic timing
  • +
  • OPTIONS: Check allowed methods and CORS
  • +
+
+ +
+

Latency Guidelines

+
    +
  • < 100ms: Excellent response time
  • +
  • 100-300ms: Good for most applications
  • +
  • 300-1000ms: Acceptable but noticeable
  • +
  • > 1000ms: Poor, may impact user experience
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/network/ipv6-connectivity-checker/+page.svelte b/src/routes/[lang]/diagnostics/network/ipv6-connectivity-checker/+page.svelte new file mode 100644 index 00000000..6bba74f0 --- /dev/null +++ b/src/routes/[lang]/diagnostics/network/ipv6-connectivity-checker/+page.svelte @@ -0,0 +1,412 @@ + + +
+
+

{content.title}

+

{content.description}

+
+ + +
+
+

Connectivity Test

+
+
+
+ +
+
+
+ + + {#if error} +
+
+
+ + {error} +
+
+
+ {/if} + + + {#if results} +
+
+

Connectivity Results

+ +
+
+ +
+
+ +
+

{results.dualStack ? 'Dual-Stack Available' : 'Single Protocol Only'}

+

+ {results.dualStack + ? 'Both IPv4 and IPv6 connectivity are available' + : results.ipv4.success + ? 'Only IPv4 connectivity is available' + : results.ipv6.success + ? 'Only IPv6 connectivity is available' + : 'No connectivity detected'} +

+
+
+
+ + +
+ +
+

+ + IPv4 Connectivity +

+
+ + {results.ipv4.success ? 'Connected' : 'Not Available'} +
+ {#if results.ipv4.success} +
+
+ +
+ IP Address + {results.ipv4.ip} +
+
+
+ +
+ Latency + {results.ipv4.latency}ms +
+
+
+ {:else if results.ipv4.error && results.ipv4.error !== 'fetch failed'} +
{results.ipv4.error}
+ {:else} +
No IPv4 connectivity available
+ {/if} +
+ + +
+

+ + IPv6 Connectivity +

+
+ + {results.ipv6.success ? 'Connected' : 'Not Available'} +
+ {#if results.ipv6.success} +
+
+ +
+ IP Address + {results.ipv6.ip} +
+
+
+ +
+ Latency + {results.ipv6.latency}ms +
+
+
+ {:else if results.ipv6.error && results.ipv6.error !== 'fetch failed'} +
{results.ipv6.error}
+ {:else} +
No IPv6 connectivity available
+ {/if} +
+ + + {#if results.dualStack} +
+

+ + Connection Summary +

+
+
+ +
+ Dual-Stack + Enabled +
+
+ {#if results.preferredProtocol} +
+ +
+ Preferred Protocol + {results.preferredProtocol} +
+
+
+ + Based on latency comparison +
+ {/if} +
+ +
+ Tested At + {new Date(results.timestamp).toLocaleString()} +
+
+
+
+ {/if} +
+
+
+ {/if} + + +
+
+

About IPv6 Connectivity

+
+
+
+

{content.sections.whatIsIPv6.title}

+

{content.sections.whatIsIPv6.content}

+
+ +
+ +
+

{content.sections.dualStack.title}

+

{content.sections.dualStack.content}

+
    + {#each content.sections.dualStack.benefits as { benefit, description } (benefit)} +
  • {benefit}: {description}
  • + {/each} +
+
+ +
+ +
+

{content.sections.ipv6Advantages.title}

+
    + {#each content.sections.ipv6Advantages.advantages as { advantage, description } (advantage)} +
  • {advantage}: {description}
  • + {/each} +
+
+ +
+ +
+

Quick Tips

+
    + {#each content.quickTips as tip (tip)} +
  • {tip}
  • + {/each} +
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/network/tcp-port-check/+page.svelte b/src/routes/[lang]/diagnostics/network/tcp-port-check/+page.svelte new file mode 100644 index 00000000..62f69728 --- /dev/null +++ b/src/routes/[lang]/diagnostics/network/tcp-port-check/+page.svelte @@ -0,0 +1,532 @@ + + +
+
+

TCP Port Checker

+

+ Test TCP connectivity to one or more host:port combinations. Attempts direct TCP connections to check if ports are + open and measures connection latency. +

+
+ + + ex.description} + getDescription={(ex) => { + const targets = ex.targets.split('\n'); + const preview = targets.slice(0, 3).join(', '); + return targets.length > 3 ? `${preview} (+${targets.length - 3} more)` : preview; + }} + getTooltip={(ex) => `Test ports: ${ex.targets.split('\n').join(', ')}`} + /> + + +
+
+

Port Check Configuration

+
+
+
+
+ +
+
+ + +
+
+

Common Ports

+
+ {#each commonPorts as port, index (index)} + + {/each} +
+
+
+ +
+
+ +
+
+ +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

Port Check Results

+ +
+
+ +
+
+ +
+ {diagnosticState.results.summary.open} Open +

Ports accepting connections

+
+
+
+ +
+ {diagnosticState.results.summary.closed} Closed +

Ports not responding

+
+
+ {#if diagnosticState.results.summary.avgLatency} +
+ +
+ {diagnosticState.results.summary.avgLatency}ms +

Average latency

+
+
+ {/if} +
+ + +
+

Port Status ({diagnosticState.results.results.length} targets)

+
+ {#each diagnosticState.results.results as result, index (index)} + {@const status = getPortStatus(result)} +
+
+
+ + {result.host}:{result.port} +
+ {status.text} +
+ {#if result.error && !result.open} +
+ {result.error} +
+ {/if} +
+ {/each} +
+
+
+
+ {/if} + + + + +
+
+

Understanding TCP Port Connectivity

+
+
+
+
+

Port States

+
    +
  • Open: Port accepts connections and responds
  • +
  • Closed: Port actively refuses connections
  • +
  • Filtered: Port blocked by firewall (appears as timeout)
  • +
  • Timeout: No response within timeout period
  • +
+
+ +
+

Common Ports

+
    +
  • SSH (22): Secure remote access
  • +
  • HTTP (80): Web traffic
  • +
  • HTTPS (443): Secure web traffic
  • +
  • SMTP (25/587): Email sending
  • +
+
+ +
+

Troubleshooting Tips

+
    +
  • Timeouts often indicate firewall blocking
  • +
  • Connection refused means service is not running
  • +
  • Check both client and server firewalls
  • +
  • Verify service is listening on expected port
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/rdap/asn/+page.svelte b/src/routes/[lang]/diagnostics/rdap/asn/+page.svelte new file mode 100644 index 00000000..8ed0139a --- /dev/null +++ b/src/routes/[lang]/diagnostics/rdap/asn/+page.svelte @@ -0,0 +1,379 @@ + + +
+
+

ASN RDAP Lookup

+

+ Query Autonomous System Number allocation and registration data using RDAP through Regional Internet Registries. + ASNs identify networks on the global Internet routing table and are essential for BGP routing operations. +

+
+ + +
+
+ + +

Common ASN Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

RDAP Lookup Configuration

+
+
+
+
+ +
+
+ +
+ +
+
+
+ + + {#if results} +
+
+

RDAP Data for {formatASN(results.asn)}

+ +
+
+
+
+ ASN: + + {formatASN(results.asn)} + +
+
+ RDAP Service: + {results.serviceUrl} +
+
+ +
+ +
+

ASN Information

+
+
Organization Name:
+
{results.data.name || 'Not available'}
+ +
Type:
+
{results.data.type || 'Not available'}
+ +
Country:
+
+ {#if results.data.country} + {results.data.country} + {:else} + Not available + {/if} +
+ +
Registry:
+
{results.data.registry || 'Not available'}
+
+
+ + +
+

Allocation Details

+
+
Status:
+
+ {#if results.data.status?.length} +
+ {#each results.data.status as status, index (index)} + {status} + {/each} +
+ {:else} + Not available + {/if} +
+ +
Allocation Date:
+
{formatDate(results.data.allocation)}
+ +
Last Changed:
+
{formatDate(results.data.lastChanged)}
+
+
+ + + {#if results.data.contacts?.length} +
+

Contact Information

+
+ {#each results.data.contacts as contact, index (index)} +
+
+ {#if contact.roles?.includes('registrant')} + Registrant + {:else if contact.roles?.includes('administrative')} + Administrative + {:else if contact.roles?.includes('technical')} + Technical + {:else if contact.roles?.includes('abuse')} + Abuse + {:else} + Contact + {/if} +
+

{formatContact(contact)}

+ {#if contact.handle} +

Handle: {contact.handle}

+ {/if} + {#if contact.roles} +
+ {#each contact.roles as role, index (index)} + {role} + {/each} +
+ {/if} +
+ {/each} +
+
+ {/if} +
+
+
+ {/if} + + {#if error} +
+
+
+ +
+ RDAP Lookup Failed +

{error}

+
+

Troubleshooting Tips:

+
    +
  • Ensure the ASN format is valid (e.g., AS15169 or just 15169)
  • +
  • Check if the ASN exists and is currently allocated
  • +
  • Private ASNs (64512-65534, 4200000000-4294967294) may not have public RDAP data
  • +
  • Some RIRs may have rate limiting or access restrictions
  • +
  • Try again in a few moments if the service is temporarily unavailable
  • +
+
+
+
+
+
+ {/if} + + +
+
+

About ASN RDAP Lookups

+
+
+
+
+

What is an ASN?

+

+ Autonomous System Numbers identify networks on the global Internet routing table and are essential for BGP + routing operations. Each ASN represents a collection of IP address blocks under unified administrative + control. +

+
+ +
+

ASN Ranges by Registry

+
    +
  • ARIN: AS1 - AS23551, AS393216 - AS394239
  • +
  • RIPE NCC: AS24576 - AS25599, AS34816 - AS35839
  • +
  • APNIC: AS23552 - AS24575, AS37888 - AS38911
  • +
  • LACNIC: AS26592 - AS27647, AS61440 - AS61951
  • +
  • AFRINIC: AS36864 - AS37887, AS327680 - AS328703
  • +
+
+ +
+

What You'll Get

+
    +
  • Organization name and type
  • +
  • Country of registration
  • +
  • Allocation date and status
  • +
  • Contact information (admin, technical, abuse)
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/rdap/domain/+page.svelte b/src/routes/[lang]/diagnostics/rdap/domain/+page.svelte new file mode 100644 index 00000000..81e19830 --- /dev/null +++ b/src/routes/[lang]/diagnostics/rdap/domain/+page.svelte @@ -0,0 +1,356 @@ + + +
+
+

Domain RDAP Lookup

+

+ Query domain registration data using RDAP (Registration Data Access Protocol). RDAP is the modern successor to + WHOIS, providing structured JSON responses through IANA bootstrap registry routing. +

+
+ + +
+
+ + +

Common Domain Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

RDAP Lookup Configuration

+
+
+
+
+ +
+
+ +
+ +
+
+
+ + + {#if results} +
+
+

RDAP Data for {results.domain}

+ +
+
+
+
+ Domain: + {results.data.domain || results.domain} +
+
+ RDAP Service: + {results.serviceUrl} +
+
+ +
+ +
+

Domain Information

+
+
Domain Name:
+
{results.data.domain || results.domain}
+ +
Status:
+
+ {#if results.data.status?.length} +
+ {#each results.data.status as status, index (index)} + {status} + {/each} +
+ {:else} + Not available + {/if} +
+ +
Registrar:
+
{results.data.registrar || 'Not available'}
+
+
+ + +
+

Important Dates

+
+
Registration Date:
+
{formatDate(results.data.created)}
+ +
Last Updated:
+
{formatDate(results.data.updated)}
+ +
Expiration Date:
+
+ {formatDate(results.data.expires)} + {#if results.data.expires && new Date(results.data.expires) < new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)} + Expires Soon! + {/if} +
+
+
+ + + {#if results.data.nameservers?.length} +
+

Nameservers ({results.data.nameservers.length})

+
    + {#each results.data.nameservers as ns, index (index)} +
  • {ns}
  • + {/each} +
+
+ {/if} + + + {#if results.data.contacts?.length} +
+

Contact Information

+
+ {#each results.data.contacts as contact, index (index)} +
+
+ {#if contact.roles?.includes('registrant')} + Registrant + {:else if contact.roles?.includes('administrative')} + Administrative + {:else if contact.roles?.includes('technical')} + Technical + {:else} + Contact + {/if} +
+

{formatContact(contact)}

+ {#if contact.handle} +

Handle: {contact.handle}

+ {/if} +
+ {/each} +
+
+ {/if} +
+
+
+ {/if} + + {#if error} +
+
+
+ +
+ RDAP Lookup Failed +

{error}

+
+

Troubleshooting Tips:

+
    +
  • Ensure the domain name is valid and properly formatted
  • +
  • Check if the domain actually exists and is registered
  • +
  • Some registries may have rate limiting or access restrictions
  • +
  • Try again in a few moments if the service is temporarily unavailable
  • +
+
+
+
+
+
+ {/if} + + +
+
+

About RDAP Domain Lookups

+
+
+
+
+

What is RDAP?

+

+ RDAP (Registration Data Access Protocol) is the modern successor to WHOIS, providing structured JSON + responses for domain registration information through IANA bootstrap registry routing. +

+
+ +
+

What You'll Get

+
    +
  • Registration status and dates
  • +
  • Nameserver information
  • +
  • Registrar details
  • +
  • Contact information (if available)
  • +
+
+ +
+

RDAP vs WHOIS

+
    +
  • Structured JSON instead of free text
  • +
  • Unicode support for internationalized domains
  • +
  • Built-in rate limiting and privacy controls
  • +
  • RESTful API with standard HTTP methods
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/rdap/ip/+page.svelte b/src/routes/[lang]/diagnostics/rdap/ip/+page.svelte new file mode 100644 index 00000000..9de9cbcf --- /dev/null +++ b/src/routes/[lang]/diagnostics/rdap/ip/+page.svelte @@ -0,0 +1,379 @@ + + +
+
+

IP Address RDAP Lookup

+

+ Look up IP address allocation and registration data using RDAP through Regional Internet Registry (RIR) services. + Automatically routes queries to the appropriate RIR based on IP address prefix. +

+
+ + +
+
+ + +

Common IP Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + +
+
+

RDAP Lookup Configuration

+
+
+
+
+ +
+
+ +
+ +
+
+
+ + + {#if results} +
+
+

RDAP Data for {results.ip}

+ +
+
+
+
+ IP Address: + + {results.ip} + {getIPVersion(results.ip)} + +
+
+ RDAP Service: + {results.serviceUrl} +
+
+ +
+ +
+

Network Information

+
+
Network Block:
+
{results.data.network || 'Not available'}
+ +
Network Name:
+
{results.data.name || 'Not available'}
+ +
Type:
+
{results.data.type || 'Not available'}
+ +
Country:
+
+ {#if results.data.country} + {results.data.country} + {:else} + Not available + {/if} +
+ +
Registry:
+
{results.data.registry || 'Not available'}
+
+
+ + +
+

Allocation Details

+
+
Status:
+
+ {#if results.data.status?.length} +
+ {#each results.data.status as status, index (index)} + {status} + {/each} +
+ {:else} + Not available + {/if} +
+ +
Allocation Date:
+
{formatDate(results.data.allocation)}
+ +
Last Changed:
+
{formatDate(results.data.lastChanged)}
+
+
+ + + {#if results.data.contacts?.length} +
+

Contact Information

+
+ {#each results.data.contacts as contact, index (index)} +
+
+ {#if contact.roles?.includes('registrant')} + Registrant + {:else if contact.roles?.includes('administrative')} + Administrative + {:else if contact.roles?.includes('technical')} + Technical + {:else if contact.roles?.includes('abuse')} + Abuse + {:else} + Contact + {/if} +
+

{formatContact(contact)}

+ {#if contact.handle} +

Handle: {contact.handle}

+ {/if} + {#if contact.roles} +
+ {#each contact.roles as role, index (index)} + {role} + {/each} +
+ {/if} +
+ {/each} +
+
+ {/if} +
+
+
+ {/if} + + {#if error} +
+
+
+ +
+ RDAP Lookup Failed +

{error}

+
+

Troubleshooting Tips:

+
    +
  • Ensure the IP address is valid and properly formatted
  • +
  • Private IP addresses (RFC 1918) may not have RDAP data
  • +
  • Some RIRs may have rate limiting or access restrictions
  • +
  • Reserved or special-use addresses may not be publicly queryable
  • +
  • Try again in a few moments if the service is temporarily unavailable
  • +
+
+
+
+
+
+ {/if} + + +
+
+

About IP Address RDAP Lookups

+
+
+
+
+

How it Works

+

+ IP RDAP provides detailed allocation information from Regional Internet Registries (RIRs). The tool + automatically routes queries to the appropriate RIR using IANA bootstrap registries. +

+
+ +
+

Regional Internet Registries

+
    +
  • ARIN: North America, parts of Caribbean
  • +
  • RIPE NCC: Europe, Central Asia, Middle East
  • +
  • APNIC: Asia Pacific region
  • +
  • LACNIC: Latin America, parts of Caribbean
  • +
  • AFRINIC: Africa
  • +
+
+ +
+

What You'll Get

+
    +
  • Network block and CIDR prefix
  • +
  • Allocation type and country
  • +
  • Organization responsible for the block
  • +
  • Contact information (registrant, admin, technical, abuse)
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/tls/alpn/+page.svelte b/src/routes/[lang]/diagnostics/tls/alpn/+page.svelte new file mode 100644 index 00000000..e847aa5b --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/alpn/+page.svelte @@ -0,0 +1,616 @@ + + +
+
+

TLS ALPN Negotiation

+

+ Test Application-Layer Protocol Negotiation (ALPN) to see which protocol a server selects from your offered list. + Commonly used to negotiate HTTP/2, HTTP/3, or other application protocols during TLS handshake. +

+
+ + + ex.host} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Test ALPN for ${ex.host} (${ex.description})`} + /> + + +
+
+

ALPN Negotiation Configuration

+
+
+
+
+ +
+
+ + +
+
+ +
+ Quick select: + {#each commonProtocols as preset, index (index)} + + {/each} +
+
+
+ +
+
+ + {#if useCustomServername} + { + examples.clear(); + if (isInputValid()) probeALPN(); + }} + /> + {/if} +
+
+ +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

ALPN Negotiation Results

+ +
+
+ + {#if diagnosticState.results} + {@const status = getNegotiationStatus()} +
+
+ +
+ Negotiation: {status.status} +

{status.description}

+
+
+ {#if diagnosticState.results.tlsVersion} +
+ +
+ TLS Version: {diagnosticState.results.tlsVersion} +

Connection established successfully

+
+
+ {/if} +
+ {/if} + + +
+

Protocol Negotiation Details

+ +
+
+
Requested Protocols
+
+ {#each diagnosticState.results.requestedProtocols as protocol, i (i)} + {@const protocolInfo = getProtocolInfo(protocol)} +
+
+ {protocolInfo.name} + ({protocol}) + Priority {i + 1} +
+

{protocolInfo.description}

+
+ {/each} +
+
+ + {#if diagnosticState.results.negotiatedProtocol} + {@const selectedProtocol = getProtocolInfo(diagnosticState.results.negotiatedProtocol)} +
+
Selected Protocol
+
+
+
+ + {selectedProtocol.name} + ({diagnosticState.results.negotiatedProtocol}) +
+

{selectedProtocol.description}

+
+
+
+ {:else} +
+
Selected Protocol
+
+ + No protocol was selected by the server +
+
+ {/if} +
+
+ + + {#if diagnosticState.results.servername || diagnosticState.results.tlsVersion} +
+

Connection Information

+
+
+ Server Name: + {diagnosticState.results.servername} +
+ {#if diagnosticState.results.tlsVersion} +
+ TLS Version: + {diagnosticState.results.tlsVersion} +
+ {/if} +
+
+ {/if} +
+
+ {/if} + + + + +
+
+

Understanding ALPN

+
+
+
+
+

What is ALPN?

+

+ Application-Layer Protocol Negotiation (ALPN) is a TLS extension that allows the client and server to + negotiate which application protocol to use during the TLS handshake. +

+
+ +
+

Common Protocols

+
    +
  • h2: HTTP/2 - Binary, multiplexed protocol
  • +
  • h3: HTTP/3 - Latest HTTP over QUIC
  • +
  • http/1.1: Traditional HTTP/1.1
  • +
  • spdy/3.1: Legacy SPDY protocol
  • +
+
+ +
+

Protocol Priority

+

+ Protocols are offered in preference order. The server selects the first protocol from your list that it + supports. Order matters! +

+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/tls/banner/+page.svelte b/src/routes/[lang]/diagnostics/tls/banner/+page.svelte new file mode 100644 index 00000000..7f334eec --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/banner/+page.svelte @@ -0,0 +1,559 @@ + + +
+
+

Service Banner Grabber

+

Retrieve service banners from SSH, SMTP, HTTP, FTP, and other network services

+
+ + + ex.description} + getDescription={(ex) => `${ex.host}:${ex.port}`} + getTooltip={(ex) => `Grab banner from ${ex.host}:${ex.port}`} + /> + + +
+
+

Target Service

+
+
+
+
+ + +
+
+ +
+
+ + examples.clear()} + onkeydown={(e) => e.key === 'Enter' && grabBanner()} + /> +
+
+ + examples.clear()} + onkeydown={(e) => e.key === 'Enter' && grabBanner()} + /> +
+
+ + +
+
+ + + + {#if diagnosticState.loading} +
+
+
+ +
+

Grabbing Banner

+

Connecting to {host}:{port}...

+
+
+
+
+ {/if} + + {#if diagnosticState.results} +
+
+

Banner Information

+
+
+ +
+
+

Connection Details

+
+
+
+
+ Host: + {diagnosticState.results.host} +
+
+ Port: + {diagnosticState.results.port} +
+
+ Protocol: + + + {diagnosticState.results.protocol || 'Unknown'} + +
+
+ Response Time: + {diagnosticState.results.responseTime}ms +
+
+
+
+ + + + + + {#if diagnosticState.results.analysis && (diagnosticState.results.analysis.software || diagnosticState.results.analysis.version || diagnosticState.results.analysis.os || (diagnosticState.results.analysis.security && diagnosticState.results.analysis.security.length > 0))} +
+
+

Service Analysis

+
+
+
+ {#if diagnosticState.results.analysis.software} +
+ +
+

Software

+

{diagnosticState.results.analysis.software}

+
+
+ {/if} + {#if diagnosticState.results.analysis.version} +
+ +
+

Version

+

{diagnosticState.results.analysis.version}

+
+
+ {/if} + {#if diagnosticState.results.analysis.os} +
+ +
+

Operating System

+

{diagnosticState.results.analysis.os}

+
+
+ {/if} + {#if diagnosticState.results.analysis.security && diagnosticState.results.analysis.security.length > 0} +
+ +
+

Security Notes

+
    + {#each diagnosticState.results.analysis.security as note, i (i)} +
  • {note}
  • + {/each} +
+
+
+ {/if} +
+
+
+ {/if} + + + {#if diagnosticState.results.tls} +
+
+

TLS Information

+
+
+
+
+ Protocol: + {diagnosticState.results.tls.protocol} +
+
+ Cipher: + {diagnosticState.results.tls.cipher} +
+ {#if diagnosticState.results.tls.certificate} +
+ Certificate CN: + {diagnosticState.results.tls.certificate.cn} +
+ {/if} +
+
+
+ {/if} +
+
+ {/if} +
+ + diff --git a/src/routes/[lang]/diagnostics/tls/certificate/+page.svelte b/src/routes/[lang]/diagnostics/tls/certificate/+page.svelte new file mode 100644 index 00000000..252a753c --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/certificate/+page.svelte @@ -0,0 +1,490 @@ + + +
+
+

TLS Certificate Analyzer

+

+ Analyze TLS certificates, view certificate chains, check expiration dates, and examine Subject Alternative Names + (SANs). Supports custom SNI servername for multi-domain certificates. +

+
+ + + example.host} + getDescription={(example) => example.description} + getTooltip={(example) => `Analyze certificate for ${example.host} (${example.description})`} + /> + + +
+
+

Certificate Analysis Configuration

+
+
+
+
+ +
+
+ +
+
+ + {#if useCustomServername} + { + examples.clear(); + if (isInputValid()) analyzeCertificate(); + }} + /> + {/if} +
+
+ +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

Certificate Analysis Results

+ +
+
+ + {#if diagnosticState.results.peerCertificate} + {@const cert = diagnosticState.results.peerCertificate} + {@const expiryStatus = getExpiryStatus(cert)} + +
+
+
+ + {expiryStatus.status} +
+
+ + {cert.isNotYetValid ? 'Not yet valid' : 'Currently valid'} +
+
+ + +
+
+

Certificate Information

+
+
+ Common Name: + {cert.subject.CN} +
+
+ Organization: + {cert.subject.O || 'N/A'} +
+
+ Issuer: + {cert.issuer.CN} +
+
+ Serial Number: + {cert.serialNumber} +
+
+ Valid From: + {new Date(cert.validFrom).toLocaleString()} +
+
+ Valid To: + {new Date(cert.validTo).toLocaleString()} +
+
+
+ + + {#if cert.subjectAltNames?.length > 0} +
+

Subject Alternative Names

+
+ {#each cert.subjectAltNames as san, index (index)} + {san} + {/each} +
+
+ {/if} + + +
+

Fingerprints

+
+
+ SHA1: + {cert.fingerprint} +
+
+ SHA256: + {cert.fingerprint256} +
+
+
+
+
+ {/if} + + + {#if diagnosticState.results.chain?.length > 0} +
+

Certificate Chain ({diagnosticState.results.chain.length} certificates)

+
+ {#each diagnosticState.results.chain as chainCert, i (i)} +
+
+ Level {i} + {chainCert.subject.CN} +
+
+ Issuer: {chainCert.issuer.CN} + Expires: {new Date(chainCert.validTo).toLocaleDateString()} +
+
+ {/each} +
+
+ {/if} + + + {#if diagnosticState.results.protocol || diagnosticState.results.cipher || diagnosticState.results.alpnProtocol} +
+

Connection Details

+
+ {#if diagnosticState.results.protocol} +
+ TLS Version: + {diagnosticState.results.protocol} +
+ {/if} + {#if diagnosticState.results.cipher} +
+ Cipher Suite: + {diagnosticState.results.cipher.name} +
+ {/if} + {#if diagnosticState.results.alpnProtocol} +
+ ALPN Protocol: + {diagnosticState.results.alpnProtocol} +
+ {/if} +
+
+ {/if} +
+
+ {/if} + + +
+ + diff --git a/src/routes/[lang]/diagnostics/tls/cipher-presets/+page.svelte b/src/routes/[lang]/diagnostics/tls/cipher-presets/+page.svelte new file mode 100644 index 00000000..4b52b9de --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/cipher-presets/+page.svelte @@ -0,0 +1,581 @@ + + +
+
+

TLS Cipher Presets

+

Probe connectivity with preset cipher lists (modern/intermediate/legacy)

+
+ + + `${ex.host}:${ex.port}`} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Test cipher presets for ${ex.host}:${ex.port}`} + /> + + +
+
+

Cipher Presets Configuration

+
+
+
+ +
+ examples.clear()} + onkeydown={(e) => e.key === 'Enter' && testCiphers()} + class="flex-grow" + /> + examples.clear()} + onkeydown={(e) => e.key === 'Enter' && testCiphers()} + class="port-input" + /> + +
+
+
+
+ + + + {#if diagnosticState.loading} +
+
+
+ +
+

Testing Cipher Presets

+

Testing modern, intermediate, and legacy cipher suites...

+
+
+
+
+ {/if} + + {#if diagnosticState.results} +
+
+

Cipher Presets Results

+
+
+
+
+ {#each diagnosticState.results.presets as preset (preset.name)} +
+
+
+

{preset.name}

+ {preset.level} +
+
+ {getPresetGrade(preset)} +
+
+ +
+ {preset.description} +
+ +
+
+ Supported: + {preset.supportedCiphers.length}/{preset.ciphers.length} +
+
+ Coverage: + {getPresetScore(preset)}% +
+
+ +
+
+
+
+
+ + {#if preset.protocols} +
+ Protocols: +
+ {#each preset.protocols as protocol (protocol.name)} + + {protocol.name} + + {/each} +
+
+ {/if} + + {#if preset.supportedCiphers.length > 0} +
+ Supported Ciphers ({preset.supportedCiphers.length}) +
+ {#each preset.supportedCiphers as cipher (cipher)} +
+ + {cipher} +
+ {/each} +
+
+ {/if} + + {#if preset.unsupportedCiphers && preset.unsupportedCiphers.length > 0} +
+ Unsupported Ciphers ({preset.unsupportedCiphers.length}) +
+ {#each preset.unsupportedCiphers as cipher (cipher)} +
+ + {cipher} +
+ {/each} +
+
+ {/if} + + {#if preset.recommendation} +
+ + {preset.recommendation} +
+ {/if} +
+ {/each} +
+ + {#if diagnosticState.results.summary} +
+

Overall Assessment

+
+
+
+ {diagnosticState.results.summary.overallGrade} +
+
+

{diagnosticState.results.summary.rating}

+

{diagnosticState.results.summary.description}

+
+
+ + {#if diagnosticState.results.summary.recommendations && diagnosticState.results.summary.recommendations.length > 0} +
+

Recommendations

+
    + {#each diagnosticState.results.summary.recommendations as rec (rec)} +
  • {rec}
  • + {/each} +
+
+ {/if} +
+
+ {/if} +
+
+
+ {/if} +
+ + diff --git a/src/routes/[lang]/diagnostics/tls/ct-log-search/+page.svelte b/src/routes/[lang]/diagnostics/tls/ct-log-search/+page.svelte new file mode 100644 index 00000000..5a113004 --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/ct-log-search/+page.svelte @@ -0,0 +1,812 @@ + + +
+
+

{content.title}

+

{content.description}

+
+ + +
+
+

Search CT Logs

+
+
+
+ e.key === 'Enter' && searchCTLogs()} + disabled={diagnosticState.loading} + /> + +
+
+
+ + + ex.domain} + getDescription={(ex) => ex.desc} + /> + + + {#if diagnosticState.loading} +
+
+
+ +
+

Searching Certificate Transparency Logs

+

Querying CT logs for {domain}...

+
+
+
+
+ {/if} + + + + + {#if diagnosticState.results} +
+
+

CT Log Results for {diagnosticState.results.domain}

+ +
+
+ +
+
+ +
+

{diagnosticState.results.totalCertificates} Total Certificates

+

Found in Certificate Transparency logs

+
+
+
+ + +
+ {#each statsConfig as stat (stat.key)} +
+

+ + {stat.label} +

+
+ {stat.isArray + ? (diagnosticState.results[stat.key as keyof CTLogResponse] as any[]).length + : diagnosticState.results[stat.key as keyof CTLogResponse]} +
+

{stat.desc}

+
+ {/each} +
+ + + {#if diagnosticState.results.discoveredHostnames.length > 0} +
+

+ + Discovered Hostnames ({diagnosticState.results.discoveredHostnames.length}) +

+
+ {#each diagnosticState.results.discoveredHostnames.slice(0, 50) as hostname (hostname)} + + {#if hostname.startsWith('*')} + + {/if} + {hostname} + + {/each} + {#if diagnosticState.results.discoveredHostnames.length > 50} + + +{diagnosticState.results.discoveredHostnames.length - 50} more + + {/if} +
+
+ {/if} + + + {#if diagnosticState.results.issuers.length > 0} +
+

+ + Certificate Issuers ({diagnosticState.results.issuers.length}) +

+
+ {#each diagnosticState.results.issuers.slice(0, 10) as issuer (issuer.name)} +
+ {issuer.name} + {issuer.count} +
+ {/each} +
+
+ {/if} + + +
+

+ + Certificates ({diagnosticState.results.certificates.length}) +

+
+ {#each diagnosticState.results.certificates.slice(0, 20) as cert (cert.id)} +
+
toggleCert(cert.id)} + onkeydown={(e) => e.key === 'Enter' && toggleCert(cert.id)} + > +
+ {#if cert.isWildcard} + + {/if} + {cert.commonName} + {#each getCertBadges(cert) as badge (badge.text)} + {badge.text} + {/each} +
+ +
+ + {#if expandedCert === cert.id} +
+
+ {#each certDetailFields as field (field.key)} +
+ +
+ {field.label}{field.isSans ? ` (${cert.sans.length})` : ''} + {#if field.isSans} +
+ {#each cert.sans.slice(0, 10) as san (san)} + {san} + {/each} + {#if cert.sans.length > 10} + +{cert.sans.length - 10} more + {/if} +
+ {:else if field.isLink} + + crt.sh #{cert.id} + + {:else if field.formatter} + {field.formatter(cert)} + {:else} + {cert[field.key as keyof ProcessedCertificate]} + {/if} +
+
+ {/each} +
+
+ {/if} +
+ {/each} + {#if diagnosticState.results.certificates.length > 20} +
+ Showing first 20 of {diagnosticState.results.certificates.length} certificates +
+ {/if} +
+
+
+
+ {/if} + + +
+
+

About Certificate Transparency

+
+
+ +
+ + +

{content.sections.whatIsCT.title}

+
+
+

{content.sections.whatIsCT.content}

+
+
+ + {#each [{ title: content.sections.benefits.title, items: content.sections.benefits.benefits, keys: ['benefit', 'description'] }, { title: content.sections.useCases.title, items: content.sections.useCases.cases, keys: ['useCase', 'description', 'example'] }, { title: content.sections.certificateFields.title, items: content.sections.certificateFields.fields, keys: ['field', 'description'] }, { title: content.sections.security.title, items: content.sections.security.points, keys: ['point', 'description'] }, { title: content.sections.bestPractices.title, items: content.sections.bestPractices.practices, keys: ['practice', 'description'] }] as section (section.title)} +
+ + +

{section.title}

+
+
+
    + {#each section.items as item ((item as any)[section.keys[0]])} +
  • + {(item as any)[section.keys[0]]}: + {(item as any)[section.keys[1]]} + {#if section.keys[2] && (item as any)[section.keys[2]]} + ({(item as any)[section.keys[2]]}) + {/if} +
  • + {/each} +
+
+
+ {/each} + + +
+ + +

Quick Tips

+
+
+
    + {#each content.quickTips as tip (tip)} +
  • {tip}
  • + {/each} +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/tls/handshake-analyzer/+page.svelte b/src/routes/[lang]/diagnostics/tls/handshake-analyzer/+page.svelte new file mode 100644 index 00000000..bff088bd --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/handshake-analyzer/+page.svelte @@ -0,0 +1,587 @@ + + +
+
+

{tlsHandshakeContent.title}

+

{tlsHandshakeContent.description}

+
+ + + ex.hostname} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Analyze ${ex.hostname}`} + /> + + +
+
+

Handshake Analysis

+
+
+
+
+ + { + examples.clear(); + if (hostname.trim()) analyzeHandshake(); + }} + /> +
+
+ + +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

+ Handshake Results for {diagnosticState.results.hostname}:{diagnosticState.results.port} +

+ +
+
+
+ +
+

Connection Summary

+
+
+ +
+ Total Time + {diagnosticState.results.totalTime}ms +
+
+
+ +
+ TLS Version + {diagnosticState.results.tlsVersion} +
+
+
+ +
+ Cipher Suite + {diagnosticState.results.cipherSuite} +
+
+ {#if diagnosticState.results.alpnProtocol} +
+ +
+ ALPN Protocol + {diagnosticState.results.alpnProtocol} +
+
+ {/if} +
+
+ + + {#if diagnosticState.results.certificateInfo} +
+

Certificate Information

+
+
+ +
+ Subject + {diagnosticState.results.certificateInfo.subject} +
+
+
+ +
+ Issuer + {diagnosticState.results.certificateInfo.issuer} +
+
+
+ +
+ Valid + + {new Date(diagnosticState.results.certificateInfo.validFrom).toLocaleDateString()} → + {new Date(diagnosticState.results.certificateInfo.validTo).toLocaleDateString()} + +
+
+ {#if diagnosticState.results.certificateInfo.san} +
+ +
+ SANs + {diagnosticState.results.certificateInfo.san.length} domains +
+
+ {/if} +
+
+ {/if} + + +
+

Handshake Timeline

+
+ {#each diagnosticState.results.phases as phase (phase.phase)} +
+
+
+
+ {phase.phase} + {phase.duration}ms +
+
at {phase.timestamp}ms
+ {#if phase.details && Object.keys(phase.details).length > 0} +
+ {#each Object.entries(phase.details) as [key, value] (key)} + {key}: {value} + {/each} +
+ {/if} +
+
+ {/each} +
+
+
+
+
+ {/if} + + + + +
+
+

About TLS Handshakes

+
+
+
+
+

{tlsHandshakeContent.sections.whatIsHandshake.title}

+

{tlsHandshakeContent.sections.whatIsHandshake.content}

+
+ +
+

{tlsHandshakeContent.sections.tlsVersions.title}

+
    + {#each tlsHandshakeContent.sections.tlsVersions.versions as version (version.version)} +
  • + {version.version} ({version.status}): + {version.description} +
  • + {/each} +
+
+ +
+

{tlsHandshakeContent.sections.performanceFactors.title}

+
    + {#each tlsHandshakeContent.sections.performanceFactors.factors as factor (factor.factor)} +
  • + {factor.factor}: + {factor.description} +
  • + {/each} +
+
+ +
+

{tlsHandshakeContent.sections.optimization.title}

+
    + {#each tlsHandshakeContent.sections.optimization.techniques.slice(0, 3) as technique (technique.technique)} +
  • + {technique.technique}: + {technique.benefit} +
  • + {/each} +
+
+
+ +
+

Quick Tips

+
    + {#each tlsHandshakeContent.quickTips as tip, idx (idx)} +
  • {tip}
  • + {/each} +
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/tls/ocsp-stapling/+page.svelte b/src/routes/[lang]/diagnostics/tls/ocsp-stapling/+page.svelte new file mode 100644 index 00000000..bca359ac --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/ocsp-stapling/+page.svelte @@ -0,0 +1,613 @@ + + +
+
+

OCSP Stapling Check

+

Report if server staples OCSP and basic status info

+
+ + + `${ex.host}:${ex.port}`} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Check OCSP stapling for ${ex.host}:${ex.port}`} + /> + + +
+
+

OCSP Stapling Configuration

+
+
+
+ +
+ examples.clear()} + onkeydown={(e) => e.key === 'Enter' && checkOCSP()} + class="flex-grow" + /> + examples.clear()} + onkeydown={(e) => e.key === 'Enter' && checkOCSP()} + class="port-input" + /> + +
+
+
+
+ + + + {#if diagnosticState.loading} +
+
+
+ +
+

Checking OCSP Stapling

+

Connecting to server and analyzing OCSP response stapling...

+
+
+
+
+ {/if} + + {#if diagnosticState.results} +
+
+

OCSP Stapling Results

+
+
+
+ +
+
+

OCSP Stapling Status

+
+
+ {#if diagnosticState.results.staplingEnabled} +
+ +
+

OCSP Stapling Enabled

+

This server provides OCSP responses with the TLS handshake

+
+
+ {:else} +
+ +
+

OCSP Stapling Not Enabled

+

This server does not staple OCSP responses

+
+
+ {/if} +
+
+ + + {#if diagnosticState.results.staplingEnabled && diagnosticState.results.ocspResponse} +
+
+

OCSP Response Details

+
+
+
+
+
Certificate Status
+
+ + {diagnosticState.results.ocspResponse.certStatus} +
+
+ +
+
Response Status
+
+ + {diagnosticState.results.ocspResponse.responseStatus} +
+
+ + {#if diagnosticState.results.ocspResponse.thisUpdate} +
+
This Update
+
{formatDate(diagnosticState.results.ocspResponse.thisUpdate)}
+
+ {/if} + + {#if diagnosticState.results.ocspResponse.nextUpdate} +
+
Next Update
+
{formatDate(diagnosticState.results.ocspResponse.nextUpdate)}
+
+ {/if} + + {#if diagnosticState.results.ocspResponse.producedAt} +
+
Produced At
+
{formatDate(diagnosticState.results.ocspResponse.producedAt)}
+
+ {/if} + + {#if diagnosticState.results.ocspResponse.responderUrl} +
+
Responder URL
+
{diagnosticState.results.ocspResponse.responderUrl}
+
+ {/if} +
+
+
+ + + {#if diagnosticState.results.ocspResponse.validity} +
+
+

Response Validity

+
+
+
+
+
+
Valid For
+
{diagnosticState.results.ocspResponse.validity.validFor}
+
+ + {#if diagnosticState.results.ocspResponse.validity.expiresIn} +
+
Expires In
+
+ + {diagnosticState.results.ocspResponse.validity.expiresIn} +
+
+ {/if} +
+ + {#if diagnosticState.results.ocspResponse.validity.percentage !== undefined} +
+
+ Validity Period Progress + {diagnosticState.results.ocspResponse.validity.percentage}% +
+
+
+
+
+ {/if} +
+
+
+ {/if} + {/if} + + + {#if diagnosticState.results.certificate} +
+
+

Certificate Information

+
+
+
+
+
Subject
+
{diagnosticState.results.certificate.subject}
+
+ +
+
Issuer
+
{diagnosticState.results.certificate.issuer}
+
+ + {#if diagnosticState.results.certificate.ocspUrls && diagnosticState.results.certificate.ocspUrls.length > 0} +
+
OCSP URLs
+
+ {#each diagnosticState.results.certificate.ocspUrls as url (url)} +
{url}
+ {/each} +
+
+ {/if} +
+
+
+ {/if} + + + {#if diagnosticState.results.recommendations && diagnosticState.results.recommendations.length > 0} +
+
+

Recommendations

+
+
+
+ {#each diagnosticState.results.recommendations as rec (rec)} +
+ + {rec} +
+ {/each} +
+
+
+ {/if} +
+
+
+ {/if} + + +
+
+

Understanding OCSP Stapling

+
+
+
+
+

What is OCSP Stapling?

+

+ OCSP Stapling is a security feature where the server includes a certificate status response during the TLS + handshake. This eliminates the need for clients to contact the Certificate Authority directly to check if a + certificate has been revoked. +

+
+ +
+

Why is it Important?

+
    +
  • Privacy: Prevents CA from tracking user browsing
  • +
  • Performance: Faster connections, no extra DNS lookups
  • +
  • Reliability: Works even if OCSP responder is down
  • +
  • Security: Real-time certificate validation
  • +
+
+ +
+

How It Works

+

+ The server periodically queries the OCSP responder and caches the response. During TLS handshake, the server + "staples" this cached response to the certificate, proving its validity without requiring the client to make + additional network requests. +

+
+ +
+

Checking Status

+

+ This tool connects to servers with OCSP stapling enabled and analyzes the stapled response. It checks + certificate status, response validity, timing information, and provides recommendations for servers without + stapling enabled. +

+
+
+
+
+
+ + diff --git a/src/routes/[lang]/diagnostics/tls/versions/+page.svelte b/src/routes/[lang]/diagnostics/tls/versions/+page.svelte new file mode 100644 index 00000000..2678ddf1 --- /dev/null +++ b/src/routes/[lang]/diagnostics/tls/versions/+page.svelte @@ -0,0 +1,537 @@ + + +
+
+

TLS Versions Probe

+

+ Test which TLS protocol versions a server supports by attempting connections with different TLS version + constraints. Identify deprecated versions and assess overall TLS security posture. +

+
+ + + ex.host} + getDescription={(ex) => ex.description} + getTooltip={(ex) => `Probe TLS versions for ${ex.host} (${ex.description})`} + /> + + +
+
+

TLS Versions Probe Configuration

+
+
+
+
+ +
+
+ +
+
+ + {#if useCustomServername} + { + examples.clear(); + if (isInputValid()) probeTLSVersions(); + }} + /> + {/if} +
+
+ +
+ +
+
+
+ + + {#if diagnosticState.results} +
+
+

TLS Versions Probe Results

+ +
+
+ + {#if diagnosticState.results.supported} + {@const security = getOverallSecurity()} + +
+
+
+ +
+ Security Level: {security.level} +

{security.description}

+
+
+
+ +
+ {diagnosticState.results.totalSupported} Versions Supported +

Out of {tlsVersions.length} tested

+
+
+
+
+ + +
+

TLS Version Support

+
+ {#each tlsVersions as tlsVer (tlsVer.version)} + {@const supported = diagnosticState.results.supported[tlsVer.version]} + {@const status = getVersionStatus(tlsVer.version, supported, tlsVer.deprecated)} + +
+
+
+ +
+ {tlsVer.name} + ({tlsVer.version}) +
+
+ {status.status} +
+ + {#if !supported && diagnosticState.results.errors[tlsVer.version]} +
+ {diagnosticState.results.errors[tlsVer.version]} +
+ {/if} + + {#if tlsVer.deprecated} +
+ + This version is deprecated and should not be used +
+ {/if} +
+ {/each} +
+
+ + + {#if diagnosticState.results.minVersion || diagnosticState.results.maxVersion} +
+

Supported Version Range

+
+
+ Minimum Version: + {diagnosticState.results.minVersion || 'Unknown'} +
+
+ Maximum Version: + {diagnosticState.results.maxVersion || 'Unknown'} +
+
+
+ {/if} + {/if} +
+
+ {/if} + + + + +
+
+

Understanding TLS Versions

+
+
+
+
+

TLS Version Security

+
    +
  • TLS 1.3: Latest version with improved security and performance
  • +
  • TLS 1.2: Widely supported, secure when properly configured
  • +
  • TLS 1.1: Deprecated, should not be used
  • +
  • TLS 1.0: Deprecated, contains security vulnerabilities
  • +
+
+ +
+

Best Practices

+
    +
  • Enable TLS 1.2 and 1.3 only
  • +
  • Disable deprecated versions (TLS 1.0, 1.1)
  • +
  • Regularly update TLS implementations
  • +
  • Use strong cipher suites
  • +
+
+ +
+

Compliance Requirements

+

+ Many compliance standards (PCI DSS, HIPAA) require disabling deprecated TLS versions. Check your specific + requirements. +

+
+
+
+
+
+ + diff --git a/src/routes/[lang]/dns/+page.svelte b/src/routes/[lang]/dns/+page.svelte new file mode 100644 index 00000000..16ad63cf --- /dev/null +++ b/src/routes/[lang]/dns/+page.svelte @@ -0,0 +1,38 @@ + + +
+ + + +
+ + diff --git a/src/routes/[lang]/dns/dnssec/cds-cdnskey-builder/+page.svelte b/src/routes/[lang]/dns/dnssec/cds-cdnskey-builder/+page.svelte new file mode 100644 index 00000000..4c70c2e8 --- /dev/null +++ b/src/routes/[lang]/dns/dnssec/cds-cdnskey-builder/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/dnssec/dnskey-tag/+page.svelte b/src/routes/[lang]/dns/dnssec/dnskey-tag/+page.svelte new file mode 100644 index 00000000..ec50238d --- /dev/null +++ b/src/routes/[lang]/dns/dnssec/dnskey-tag/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/dnssec/ds-generator/+page.svelte b/src/routes/[lang]/dns/dnssec/ds-generator/+page.svelte new file mode 100644 index 00000000..91d9ea06 --- /dev/null +++ b/src/routes/[lang]/dns/dnssec/ds-generator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/dnssec/nsec3-hash/+page.svelte b/src/routes/[lang]/dns/dnssec/nsec3-hash/+page.svelte new file mode 100644 index 00000000..a23d6188 --- /dev/null +++ b/src/routes/[lang]/dns/dnssec/nsec3-hash/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/dnssec/rrsig-planner/+page.svelte b/src/routes/[lang]/dns/dnssec/rrsig-planner/+page.svelte new file mode 100644 index 00000000..49e4d1ae --- /dev/null +++ b/src/routes/[lang]/dns/dnssec/rrsig-planner/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/edns-size-estimator/+page.svelte b/src/routes/[lang]/dns/edns-size-estimator/+page.svelte new file mode 100644 index 00000000..f9e2fa06 --- /dev/null +++ b/src/routes/[lang]/dns/edns-size-estimator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/+page.svelte b/src/routes/[lang]/dns/generators/+page.svelte new file mode 100644 index 00000000..14a6e234 --- /dev/null +++ b/src/routes/[lang]/dns/generators/+page.svelte @@ -0,0 +1,38 @@ + + +
+ + + +
diff --git a/src/routes/[lang]/dns/generators/a-aaaa-bulk/+page.svelte b/src/routes/[lang]/dns/generators/a-aaaa-bulk/+page.svelte new file mode 100644 index 00000000..7924110a --- /dev/null +++ b/src/routes/[lang]/dns/generators/a-aaaa-bulk/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/dns/generators/caa-builder/+page.svelte b/src/routes/[lang]/dns/generators/caa-builder/+page.svelte new file mode 100644 index 00000000..399cf346 --- /dev/null +++ b/src/routes/[lang]/dns/generators/caa-builder/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/cname-builder/+page.svelte b/src/routes/[lang]/dns/generators/cname-builder/+page.svelte new file mode 100644 index 00000000..d984766f --- /dev/null +++ b/src/routes/[lang]/dns/generators/cname-builder/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/dns/generators/dkim-keygen/+page.svelte b/src/routes/[lang]/dns/generators/dkim-keygen/+page.svelte new file mode 100644 index 00000000..a08da4c9 --- /dev/null +++ b/src/routes/[lang]/dns/generators/dkim-keygen/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/dmarc-builder/+page.svelte b/src/routes/[lang]/dns/generators/dmarc-builder/+page.svelte new file mode 100644 index 00000000..a330bb28 --- /dev/null +++ b/src/routes/[lang]/dns/generators/dmarc-builder/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/idn-punycode/+page.svelte b/src/routes/[lang]/dns/generators/idn-punycode/+page.svelte new file mode 100644 index 00000000..25259b0a --- /dev/null +++ b/src/routes/[lang]/dns/generators/idn-punycode/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/loc-builder/+page.svelte b/src/routes/[lang]/dns/generators/loc-builder/+page.svelte new file mode 100644 index 00000000..91e6d4c9 --- /dev/null +++ b/src/routes/[lang]/dns/generators/loc-builder/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/mx-planner/+page.svelte b/src/routes/[lang]/dns/generators/mx-planner/+page.svelte new file mode 100644 index 00000000..d91b3717 --- /dev/null +++ b/src/routes/[lang]/dns/generators/mx-planner/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/dns/generators/naptr-builder/+page.svelte b/src/routes/[lang]/dns/generators/naptr-builder/+page.svelte new file mode 100644 index 00000000..ecd58a1a --- /dev/null +++ b/src/routes/[lang]/dns/generators/naptr-builder/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/ptr-generator/+page.svelte b/src/routes/[lang]/dns/generators/ptr-generator/+page.svelte new file mode 100644 index 00000000..22fc1782 --- /dev/null +++ b/src/routes/[lang]/dns/generators/ptr-generator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/rp-builder/+page.svelte b/src/routes/[lang]/dns/generators/rp-builder/+page.svelte new file mode 100644 index 00000000..aca520f7 --- /dev/null +++ b/src/routes/[lang]/dns/generators/rp-builder/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/spf-builder/+page.svelte b/src/routes/[lang]/dns/generators/spf-builder/+page.svelte new file mode 100644 index 00000000..778a9641 --- /dev/null +++ b/src/routes/[lang]/dns/generators/spf-builder/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/dns/generators/srv-builder/+page.svelte b/src/routes/[lang]/dns/generators/srv-builder/+page.svelte new file mode 100644 index 00000000..92c41e35 --- /dev/null +++ b/src/routes/[lang]/dns/generators/srv-builder/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/dns/generators/sshfp-generator/+page.svelte b/src/routes/[lang]/dns/generators/sshfp-generator/+page.svelte new file mode 100644 index 00000000..3442b179 --- /dev/null +++ b/src/routes/[lang]/dns/generators/sshfp-generator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/svcb-https-builder/+page.svelte b/src/routes/[lang]/dns/generators/svcb-https-builder/+page.svelte new file mode 100644 index 00000000..3c17431b --- /dev/null +++ b/src/routes/[lang]/dns/generators/svcb-https-builder/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/tlsa-generator/+page.svelte b/src/routes/[lang]/dns/generators/tlsa-generator/+page.svelte new file mode 100644 index 00000000..779f6f2d --- /dev/null +++ b/src/routes/[lang]/dns/generators/tlsa-generator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/generators/txt-escape/+page.svelte b/src/routes/[lang]/dns/generators/txt-escape/+page.svelte new file mode 100644 index 00000000..c4b6e8d5 --- /dev/null +++ b/src/routes/[lang]/dns/generators/txt-escape/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/dns/label-normalizer/+page.svelte b/src/routes/[lang]/dns/label-normalizer/+page.svelte new file mode 100644 index 00000000..f3f953c5 --- /dev/null +++ b/src/routes/[lang]/dns/label-normalizer/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/record-validator/+page.svelte b/src/routes/[lang]/dns/record-validator/+page.svelte new file mode 100644 index 00000000..1f65f214 --- /dev/null +++ b/src/routes/[lang]/dns/record-validator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/reverse/ptr-generator/+page.svelte b/src/routes/[lang]/dns/reverse/ptr-generator/+page.svelte new file mode 100644 index 00000000..d8926e4a --- /dev/null +++ b/src/routes/[lang]/dns/reverse/ptr-generator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/reverse/ptr-sweep-planner/+page.svelte b/src/routes/[lang]/dns/reverse/ptr-sweep-planner/+page.svelte new file mode 100644 index 00000000..5fe3a15c --- /dev/null +++ b/src/routes/[lang]/dns/reverse/ptr-sweep-planner/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/reverse/reverse-zones/+page.svelte b/src/routes/[lang]/dns/reverse/reverse-zones/+page.svelte new file mode 100644 index 00000000..26ae7231 --- /dev/null +++ b/src/routes/[lang]/dns/reverse/reverse-zones/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/reverse/zone-generator/+page.svelte b/src/routes/[lang]/dns/reverse/zone-generator/+page.svelte new file mode 100644 index 00000000..8b1dc1aa --- /dev/null +++ b/src/routes/[lang]/dns/reverse/zone-generator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/ttl-calculator/+page.svelte b/src/routes/[lang]/dns/ttl-calculator/+page.svelte new file mode 100644 index 00000000..121c82ca --- /dev/null +++ b/src/routes/[lang]/dns/ttl-calculator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/zone/+page.svelte b/src/routes/[lang]/dns/zone/+page.svelte new file mode 100644 index 00000000..8675c5f5 --- /dev/null +++ b/src/routes/[lang]/dns/zone/+page.svelte @@ -0,0 +1,407 @@ + + +
+
+
+

DNS Zone File Tools

+

+ Professional tools for DNS zone file analysis, validation, and management. Built for network administrators, DNS + engineers, and automation workflows. +

+
+
+ +
+ {#each zoneTools as tool (tool.href)} + +
+ +
+
+

{tool.label}

+

{tool.description}

+
+
+ +
+
+ {/each} +
+ +
+
+
+
+ +
+
+

Zone Validation

+

+ Comprehensive RFC compliance checking with detailed error reporting and recommendations for fixing common + issues. +

+
+
+ +
+
+ +
+
+

Change Tracking

+

+ Compare zone file versions to track DNS changes, plan migrations, and audit modifications with unified diff + support. +

+
+
+ +
+
+ +
+
+

Analytics & Insights

+

+ Deep analysis of record distribution, TTL patterns, name lengths, and zone health metrics for optimization. +

+
+
+ +
+
+ +
+
+

Standards Compliance

+

+ Ensure DNS names meet RFC length limits and zone files follow best practices for reliable DNS operation. +

+
+
+
+
+ +
+

Common Use Cases

+
+
+

DNS Migration Planning

+

+ Compare existing and target zones to understand exactly what changes during migrations and ensure nothing is + missed. +

+
+ +
+

Zone File Cleanup

+

+ Normalize messy zone files with consistent formatting, proper ordering, duplicate removal, and error + correction. +

+
+ +
+

Compliance Auditing

+

+ Validate zone files against DNS standards to identify potential issues before they cause resolution failures. +

+
+ +
+

Configuration Analysis

+

+ Understand zone structure, identify optimization opportunities, and track DNS infrastructure growth over time. +

+
+
+
+ +
+

Zone File Best Practices

+
+
+ + Always include SOA and NS records for proper delegation +
+ +
+ + Use fully qualified domain names ending with dots +
+ +
+ + Maintain consistent TTL values based on change frequency +
+ +
+ + Keep domain names under 63 characters per label +
+ +
+ + Remove duplicate records to avoid confusion +
+ +
+ + Validate zones after changes before deployment +
+
+
+
+ + diff --git a/src/routes/[lang]/dns/zone/diff/+page.svelte b/src/routes/[lang]/dns/zone/diff/+page.svelte new file mode 100644 index 00000000..824000f4 --- /dev/null +++ b/src/routes/[lang]/dns/zone/diff/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/zone/linter/+page.svelte b/src/routes/[lang]/dns/zone/linter/+page.svelte new file mode 100644 index 00000000..d9f8ecca --- /dev/null +++ b/src/routes/[lang]/dns/zone/linter/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/zone/name-length-checker/+page.svelte b/src/routes/[lang]/dns/zone/name-length-checker/+page.svelte new file mode 100644 index 00000000..1b71883d --- /dev/null +++ b/src/routes/[lang]/dns/zone/name-length-checker/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/dns/zone/stats/+page.svelte b/src/routes/[lang]/dns/zone/stats/+page.svelte new file mode 100644 index 00000000..8507a5e3 --- /dev/null +++ b/src/routes/[lang]/dns/zone/stats/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/+page.svelte b/src/routes/[lang]/ip-address-convertor/+page.svelte new file mode 100644 index 00000000..59ce3f3a --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/+page.svelte @@ -0,0 +1,36 @@ + + +
+ + + +
+ + diff --git a/src/routes/[lang]/ip-address-convertor/distance/+page.svelte b/src/routes/[lang]/ip-address-convertor/distance/+page.svelte new file mode 100644 index 00000000..b082e8e4 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/distance/+page.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/routes/[lang]/ip-address-convertor/enumerate/+page.svelte b/src/routes/[lang]/ip-address-convertor/enumerate/+page.svelte new file mode 100644 index 00000000..1a03afca --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/enumerate/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/eui64/+page.svelte b/src/routes/[lang]/ip-address-convertor/eui64/+page.svelte new file mode 100644 index 00000000..112a1477 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/eui64/+page.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/routes/[lang]/ip-address-convertor/families/+layout.svelte b/src/routes/[lang]/ip-address-convertor/families/+layout.svelte new file mode 100644 index 00000000..2c353ef1 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/families/+layout.svelte @@ -0,0 +1,183 @@ + + +
+ + + +
+
+

+ + Understanding IPv4 and IPv6 +

+
+
+ +
+

IPv4 (Internet Protocol version 4)

+

Address Length: 32 bits (4 bytes)

+

Format: Dotted decimal notation (e.g., 192.168.1.1)

+

Total Addresses: ~4.3 billion addresses

+

Example: 203.0.113.45

+

Status: Widely deployed but address space exhausted

+
+ + +
+

IPv6 (Internet Protocol version 6)

+

Address Length: 128 bits (16 bytes)

+

Format: Hexadecimal with colons (e.g., 2001:db8::1)

+

Total Addresses: ~340 undecillion addresses

+

Example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334

+

Status: Modern standard with virtually unlimited address space

+
+ + +
+

IPv4-mapped IPv6

+

Purpose: Represent IPv4 addresses within IPv6 format

+

Format: ::ffff:192.0.2.1 or ::ffff:c000:0201

+

Usage: Transition mechanism and dual-stack implementations

+

Structure: 80 zero bits + 16 one bits (ffff) + 32-bit IPv4 address

+
+
+
+
+ +
+

+ + Conversion Methods & Use Cases +

+
+
+
+

IPv4 to IPv6 Conversion

+
    +
  • IPv4-mapped: Embed IPv4 addresses in IPv6 format
  • +
  • Dual-stack: Run both protocols simultaneously
  • +
  • Tunneling: Encapsulate IPv4 traffic in IPv6 packets
  • +
  • Migration: Gradual transition from IPv4 to IPv6
  • +
+
+ +
+

IPv6 to IPv4 Extraction

+
    +
  • Legacy Support: Extract IPv4 from mapped addresses
  • +
  • Compatibility: Interface with IPv4-only systems
  • +
  • Debugging: Identify original IPv4 addresses
  • +
  • Analysis: Traffic analysis and monitoring
  • +
+
+ +
+

Real-world Applications

+
    +
  • Web Servers: Handle both IPv4 and IPv6 clients
  • +
  • Load Balancers: Route traffic between IP versions
  • +
  • Network Monitoring: Unified logging and analysis
  • +
  • API Integration: Service compatibility layers
  • +
+
+
+
+
+ +
+

+ + Important Considerations +

+
+
+

Limitations & Best Practices

+
    +
  • + IPv4-mapped IPv6: Only works for representing IPv4 addresses, not true IPv6 migration +
  • +
  • Security: IPv4-mapped addresses may bypass IPv6-specific security rules
  • +
  • Performance: Native IPv6 is preferred over IPv4-mapped when possible
  • +
  • Compatibility: Not all applications handle IPv4-mapped IPv6 correctly
  • +
  • Best Practice: Use dual-stack configuration rather than relying solely on mapping
  • +
  • Future-proofing: Plan for IPv6-native implementations
  • +
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/ip-address-convertor/families/ipv4-to-ipv6/+page.svelte b/src/routes/[lang]/ip-address-convertor/families/ipv4-to-ipv6/+page.svelte new file mode 100644 index 00000000..2af2ae68 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/families/ipv4-to-ipv6/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/families/ipv6-to-ipv4/+page.svelte b/src/routes/[lang]/ip-address-convertor/families/ipv6-to-ipv4/+page.svelte new file mode 100644 index 00000000..3f4f8a4b --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/families/ipv6-to-ipv4/+page.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/ipv6/nat64/+page.svelte b/src/routes/[lang]/ip-address-convertor/ipv6/nat64/+page.svelte new file mode 100644 index 00000000..3408217c --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/ipv6/nat64/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/ipv6/solicited-node/+page.svelte b/src/routes/[lang]/ip-address-convertor/ipv6/solicited-node/+page.svelte new file mode 100644 index 00000000..2dbec166 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/ipv6/solicited-node/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/ipv6/teredo/+page.svelte b/src/routes/[lang]/ip-address-convertor/ipv6/teredo/+page.svelte new file mode 100644 index 00000000..f83e0ed8 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/ipv6/teredo/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/mac-address/+page.svelte b/src/routes/[lang]/ip-address-convertor/mac-address/+page.svelte new file mode 100644 index 00000000..7987199d --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/mac-address/+page.svelte @@ -0,0 +1,1383 @@ + + +
+
+

MAC Address Converter & OUI Lookup

+

+ Convert MAC addresses between different formats and identify the manufacturer using the Organizationally Unique + Identifier (OUI) +

+
+ +
+
+
+

MAC Address{isBulkMode ? 'es' : ''}

+ +
+ +
+ {#if isBulkMode} + + +
+ Enter MAC addresses one per line. Supported formats: 00:1A:2B:3C:4D:5E, + 00-1A-2B-3C-4D-5E, 001A.2B3C.4D5E (Cisco), 001A2B3C4D5E +
+ + {:else} + +
+ { + if (e.key === 'Enter') { + handleSubmit(); + } + }} + /> + +
+
+ Supported formats: 00:1A:2B:3C:4D:5E, 00-1A-2B-3C-4D-5E, + 001A.2B3C.4D5E + (Cisco), 001A2B3C4D5E +
+ {/if} +
+
+
+ + +
+
+ + +

Quick Examples

+
+
+ {#each examples as example, i (i)} + + {/each} +
+
+
+ + {#if result} +
+ {#if result.conversions.length > 0} +
+
+

{result.conversions.length === 1 ? 'Address Conversion' : 'Address Conversions'}

+ {#if result.conversions.length > 1} +
+ + +
+ {/if} +
+ + {#each result.conversions as conversion (conversion.input)} +
+ {#if result.conversions.length > 1} +
+
+ + {conversion.input} +
+ {#if conversion.error} +
{conversion.error}
+ {/if} +
+ {:else if !conversion.isValid} +
+
+ + Invalid MAC Address +
+ {#if conversion.error} +
{conversion.error}
+ {/if} +
+ {/if} + + {#if conversion.isValid} + +
+

OUI Information

+
+ {#each ouiFields as field (field.key)} + {@const value = field.render(conversion)} + {#if !field.condition || field.condition(conversion)} + {#if value !== undefined && value !== null && value !== ''} +
+ +
+ {field.label} + {#if field.code} + {#if field.tooltip} + + {value} + + {:else} + + {value} + + {/if} + {:else if field.tooltip} + + {value} + + {:else} + + {value} + + {/if} +
+
+ {/if} + {/if} + {/each} +
+
+ + +
+

Address Details

+
+ {#each detailFields as field (field.label)} + {@const active = field.invert ? !conversion.details[field.key] : conversion.details[field.key]} +
+ + {field.label} +
+ {/each} +
+
+ + +
+

Formats

+
+ {#each formatFields as field (field.key)} + {@const value = field.binary + ? conversion.details.binary + : conversion.formats[field.key as keyof MACFormat]} + {@const copyId = `${field.key}-${conversion.input}`} +
+ {field.label} +
+ {field.binary ? value.match(/.{1,8}/g)?.join(' ') : value} + +
+
+ {/each} +
+
+ {/if} +
+ {/each} +
+ + {#if result.conversions.length > 1} +
+

Conversion Summary

+
+
+ {result.summary.total} + Total +
+
+ {result.summary.valid} + Valid +
+
+ {result.summary.invalid} + Invalid +
+
+ {result.summary.withOUI} + With OUI +
+
+
+ {/if} + {/if} +
+ {/if} +
+ + +
+
+

Understanding MAC Addresses

+
+
+
+ + +

{macAddressContent.sections.whatIsMAC.title}

+
+
+

{macAddressContent.sections.whatIsMAC.content}

+
+
+ +
+ + +

{macAddressContent.sections.structure.title}

+
+
+
    + {#each macAddressContent.sections.structure.components as comp (comp.component)} +
  • {comp.component}: {comp.description}
  • + {/each} +
+

+ Example: {macAddressContent.sections.structure.example.address}
+ + {#each macAddressContent.sections.structure.example.breakdown as line (line)} + • {line}
+ {/each} +
+

+
+
+ +
+ + +

{macAddressContent.sections.addressTypes.title}

+
+
+
    + {#each macAddressContent.sections.addressTypes.types as type (type.type)} +
  • {type.type}: {type.description}
  • + {/each} +
+
+
+ +
+ + +

{macAddressContent.sections.formats.title}

+
+
+
    + {#each macAddressContent.sections.formats.formats as fmt (fmt.format)} +
  • {fmt.format}: {fmt.example} - {fmt.usage}
  • + {/each} +
+
+
+ +
+ + +

{macAddressContent.sections.ouiLookup.title}

+
+
+

{macAddressContent.sections.ouiLookup.content}

+
    + {#each macAddressContent.sections.ouiLookup.blockTypes as block (block.type)} +
  • {block.type}: {block.description}
  • + {/each} +
+

{macAddressContent.sections.ouiLookup.lookupInfo}

+
+
+ +
+ + +

{macAddressContent.sections.specialAddresses.title}

+
+
+
    + {#each macAddressContent.sections.specialAddresses.addresses as addr (addr.type)} +
  • {addr.type}: {addr.address} - {addr.description}
  • + {/each} +
+
+
+ +
+ + +

{macAddressContent.sections.useCases.title}

+
+
+
    + {#each macAddressContent.sections.useCases.cases as useCase (useCase.useCase)} +
  • {useCase.useCase}: {useCase.description}
  • + {/each} +
+
+
+ +
+ + +

Quick Tips

+
+
+
    + {#each macAddressContent.quickTips as tip (tip)} +
  • {tip}
  • + {/each} +
+
+
+
+
+ + diff --git a/src/routes/[lang]/ip-address-convertor/notation/+layout.svelte b/src/routes/[lang]/ip-address-convertor/notation/+layout.svelte new file mode 100644 index 00000000..995fc79f --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/notation/+layout.svelte @@ -0,0 +1,286 @@ + + +
+ + + +
+
+

+ + Understanding IPv6 Address Notation +

+
+
+ +
+

Expanded (Full) Format

+

Structure: All 32 hexadecimal characters with colons every 4 digits

+

Example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334

+

Usage: Debugging, detailed analysis, and when precision is required

+

Benefits: Shows complete address structure, easier to parse programmatically

+
+ + +
+

Compressed (Shortened) Format

+

Structure: Uses :: to represent consecutive zero groups, removes leading zeros

+

Example: 2001:db8:85a3::8a2e:370:7334

+

Usage: Configuration files, user interfaces, documentation

+

Benefits: Shorter, more readable, standard representation

+
+ + +
+

Compression Rules

+

Double Colon (::): Represents one or more consecutive zero groups

+

Single Use: Only one :: allowed per address to avoid ambiguity

+

Leading Zeros: Remove leading zeros from each group (0001 → 1)

+

Preference: Compress the longest sequence of consecutive zeros

+
+
+
+
+ +
+

+ + Conversion Use Cases & Applications +

+
+
+
+

Expand IPv6 Addresses

+
    +
  • Network Analysis: Compare addresses byte-by-byte
  • +
  • Database Storage: Consistent format for indexing
  • +
  • Debugging: See complete address structure
  • +
  • Programming: Easier parsing and manipulation
  • +
  • Security: Avoid address obfuscation issues
  • +
+
+ +
+

Compress IPv6 Addresses

+
    +
  • User Interface: Shorter, more readable addresses
  • +
  • Configuration: Cleaner config files and logs
  • +
  • Documentation: Standard format for examples
  • +
  • URLs: Shorter addresses in IPv6 URLs
  • +
  • Network Equipment: Standard display format
  • +
+
+ +
+

Real-world Scenarios

+
    +
  • Network Monitoring: Consistent address formatting
  • +
  • API Integration: Standardize input/output formats
  • +
  • Data Migration: Convert between address formats
  • +
  • Educational Tools: Demonstrate IPv6 structure
  • +
  • Quality Assurance: Validate address representations
  • +
+
+
+
+
+ +
+

+ + Technical Examples & Standards +

+
+
+
+

Common Address Types

+
+
+
Loopback:
+
+ ::1 + + 0000:0000:0000:0000:0000:0000:0000:0001 +
+
+
+
Link-Local:
+
+ fe80::1 + + fe80:0000:0000:0000:0000:0000:0000:0001 +
+
+
+
Documentation:
+
+ 2001:db8:: + + 2001:0db8:0000:0000:0000:0000:0000:0000 +
+
+
+
+ +
+

Best Practices

+
    +
  • RFC 5952: Follow standard compression guidelines
  • +
  • Consistency: Use same format throughout applications
  • +
  • Validation: Always validate both input and output
  • +
  • Case Sensitivity: Lowercase preferred (RFC 5952)
  • +
  • Leading Zeros: Always remove for compressed form
  • +
+
+
+
+
+
+
+ + diff --git a/src/routes/[lang]/ip-address-convertor/notation/ipv6-compress/+page.svelte b/src/routes/[lang]/ip-address-convertor/notation/ipv6-compress/+page.svelte new file mode 100644 index 00000000..00aa0428 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/notation/ipv6-compress/+page.svelte @@ -0,0 +1,45 @@ + + + + + diff --git a/src/routes/[lang]/ip-address-convertor/notation/ipv6-expand/+page.svelte b/src/routes/[lang]/ip-address-convertor/notation/ipv6-expand/+page.svelte new file mode 100644 index 00000000..f0048083 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/notation/ipv6-expand/+page.svelte @@ -0,0 +1,45 @@ + + + + + diff --git a/src/routes/[lang]/ip-address-convertor/notation/normalize/+page.svelte b/src/routes/[lang]/ip-address-convertor/notation/normalize/+page.svelte new file mode 100644 index 00000000..4513c823 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/notation/normalize/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/notation/zone-id/+page.svelte b/src/routes/[lang]/ip-address-convertor/notation/zone-id/+page.svelte new file mode 100644 index 00000000..f7271c44 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/notation/zone-id/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/nth-ip/+page.svelte b/src/routes/[lang]/ip-address-convertor/nth-ip/+page.svelte new file mode 100644 index 00000000..648b98d5 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/nth-ip/+page.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/routes/[lang]/ip-address-convertor/random/+page.svelte b/src/routes/[lang]/ip-address-convertor/random/+page.svelte new file mode 100644 index 00000000..341124f7 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/random/+page.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/routes/[lang]/ip-address-convertor/regex/+page.svelte b/src/routes/[lang]/ip-address-convertor/regex/+page.svelte new file mode 100644 index 00000000..1dd01fd0 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/regex/+page.svelte @@ -0,0 +1,241 @@ + + + + +
+
+
+

{ipAddressValidationContent.title}

+

{ipAddressValidationContent.description}

+
+ +
+

{ipAddressValidationContent.sections.overview.title}

+

{ipAddressValidationContent.sections.overview.content}

+
+ +
+

{ipAddressValidationContent.sections.ipv4.title}

+

+ + {@html ipAddressValidationContent.sections.ipv4.content + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/•/g, '•')} +

+
+ +
+

{ipAddressValidationContent.sections.ipv6.title}

+

+ + {@html ipAddressValidationContent.sections.ipv6.content + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/•/g, '•')} +

+
+ +
+

{ipAddressValidationContent.sections.regexValidation.title}

+

+ + {@html ipAddressValidationContent.sections.regexValidation.content + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/•/g, '•')} +

+
+ +
+

Example Patterns

+
+ {#each Object.values(ipAddressValidationContent.examples) as example (example.pattern)} +
+

{example.title}

+
+ {example.pattern} +
+

{example.description}

+
+
+ Matches: + {example.matches.join(', ')} +
+
+ Fails: + {example.fails.join(', ')} +
+
+ Limitation: + {example.limitation} +
+
+
+ {/each} +
+
+ +
+

{ipAddressValidationContent.sections.practicalTips.title}

+

+ + {@html ipAddressValidationContent.sections.practicalTips.content + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/•/g, '•')} +

+
+ +
+

Key Recommendations

+
+ {#each ipAddressValidationContent.recommendations as rec (rec.title)} +
+
+ +
+
+

{rec.title}

+

{rec.description}

+
+
+ {/each} +
+
+
+
+ + diff --git a/src/routes/[lang]/ip-address-convertor/representations/+page.svelte b/src/routes/[lang]/ip-address-convertor/representations/+page.svelte new file mode 100644 index 00000000..9abd2e3c --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/representations/+page.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/ula-generator/+page.svelte b/src/routes/[lang]/ip-address-convertor/ula-generator/+page.svelte new file mode 100644 index 00000000..65bba6e9 --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/ula-generator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/ip-address-convertor/validator/+page.svelte b/src/routes/[lang]/ip-address-convertor/validator/+page.svelte new file mode 100644 index 00000000..5795366f --- /dev/null +++ b/src/routes/[lang]/ip-address-convertor/validator/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/offline/+page.server.ts b/src/routes/[lang]/offline/+page.server.ts new file mode 100644 index 00000000..cffca4f2 --- /dev/null +++ b/src/routes/[lang]/offline/+page.server.ts @@ -0,0 +1,16 @@ +// Server-side code for offline page +// This ensures the page can be statically generated and cached without server dependencies + +import type { PageServerLoad } from './$types'; + +export const prerender = true; +export const ssr = false; + +// Override layout load to prevent dependencies and provide fallback data +export const load: PageServerLoad = async () => { + return { + breadcrumbJsonLd: null, // No breadcrumbs needed for offline page + version: '0.2.5', // Hardcoded fallback version for offline page + isOfflinePage: true, // Flag to indicate this is the special offline page + }; +}; diff --git a/src/routes/[lang]/offline/+page.svelte b/src/routes/[lang]/offline/+page.svelte new file mode 100644 index 00000000..e95a75e1 --- /dev/null +++ b/src/routes/[lang]/offline/+page.svelte @@ -0,0 +1,106 @@ + + + + Offline - Networking Toolbox + + + +
+
+ + +

+ {isOnline ? 'Back Online' : "You're Offline"} +

+ +

+ {isOnline ? 'Connection restored! Redirecting...' : 'Your bookmarked tools work offline'} +

+
+ + {#if $bookmarks.length > 0} +
+ +
+ {:else} +
+

No bookmarked tools yet

+ Browse Tools +
+ {/if} +
+ + diff --git a/src/routes/[lang]/reference/+layout.svelte b/src/routes/[lang]/reference/+layout.svelte new file mode 100644 index 00000000..a442132d --- /dev/null +++ b/src/routes/[lang]/reference/+layout.svelte @@ -0,0 +1,128 @@ + + +{@render children?.()} + +{#if isRef} + +{/if} + + diff --git a/src/routes/[lang]/reference/+page.svelte b/src/routes/[lang]/reference/+page.svelte new file mode 100644 index 00000000..3acbdaed --- /dev/null +++ b/src/routes/[lang]/reference/+page.svelte @@ -0,0 +1,32 @@ + + +
+

Networking Pocket Reference

+

+ Offline quick guides, cheat sheets and reference info, for networking concepts, IP addressing, and common protocols +

+
+ + + + diff --git a/src/routes/[lang]/reference/arp-vs-ndp/+page.svelte b/src/routes/[lang]/reference/arp-vs-ndp/+page.svelte new file mode 100644 index 00000000..aeaf32eb --- /dev/null +++ b/src/routes/[lang]/reference/arp-vs-ndp/+page.svelte @@ -0,0 +1,211 @@ + + +
+
+
+

{arpVsNdpContent.title}

+

{arpVsNdpContent.description}

+
+ +
+

{arpVsNdpContent.sections.overview.title}

+

{arpVsNdpContent.sections.overview.content}

+
+ +
+

Side-by-Side Comparison

+ + + + + + + + + + {#each arpVsNdpContent.comparison.basic as item, index (`${item.aspect}-${index}`)} + + + + + + {/each} + +
AspectARP (IPv4)NDP (IPv6)
{item.aspect}{item.arp}{item.ndp}
+
+ +
+

{arpVsNdpContent.arpDetails.title}

+ +

ARP Message Types

+ {#each arpVsNdpContent.arpDetails.messageTypes as type, index (`${type.type}-${index}`)} +
+
{type.type}
+
+
Description: {type.description}
+
Destination: {type.destination}
+
Response: {type.response}
+
+
+ {/each} + +

ARP Process

+
    + {#each arpVsNdpContent.arpDetails.process as step, index (`arp-process-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

ARP Limitations

+
    + {#each arpVsNdpContent.arpDetails.limitations as limitation, index (`arp-limitation-${index}`)} +
  • {limitation}
  • + {/each} +
+
+ +
+

{arpVsNdpContent.ndpDetails.title}

+ +

NDP Message Types

+ {#each arpVsNdpContent.ndpDetails.messageTypes as type, index (`${type.type}-${index}`)} +
+
{type.type}
+
+
ICMP Type: {type.icmpType}
+
Description: {type.description}
+
Destination: {type.destination}
+
Purpose: {type.purpose}
+
+
+ {/each} + +

NDP Process

+
    + {#each arpVsNdpContent.ndpDetails.process as step, index (`ndp-process-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

NDP Advantages Over ARP

+
    + {#each arpVsNdpContent.ndpDetails.advantages as advantage, index (`ndp-advantage-${index}`)} +
  • {advantage}
  • + {/each} +
+
+ +
+

Practical Differences

+ {#each arpVsNdpContent.practicalDifferences as diff, index (`${diff.scenario}-${index}`)} +
+
{diff.scenario}
+
+
ARP (IPv4): {diff.arp}
+
NDP (IPv6): {diff.ndp}
+
Impact: {diff.impact}
+
+
+ {/each} +
+ +
+

Troubleshooting Commands

+ + + + + + + + + + + {#each arpVsNdpContent.troubleshootingCommands as cmd, index (`${cmd.purpose}-${index}`)} + + + + + + + {/each} + +
PurposeIPv4 (ARP)IPv6 (NDP)Windows
{cmd.purpose}{cmd.ipv4}{cmd.ipv6}{cmd.windows}
+
+ +
+

Common Issues

+ {#each arpVsNdpContent.commonIssues as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} ({issue.protocol}) +
+
+

Description: {issue.description}

+

Detection: {issue.detection}

+

Mitigation: {issue.mitigation}

+
+
+ {/each} +
+ +
+

Best Practices

+ {#each arpVsNdpContent.bestPractices as practices, index (`${practices.protocol}-${index}`)} +

{practices.protocol} Best Practices

+
    + {#each practices.practices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+ {/each} +
+ +
+

Quick Reference

+
+
+
ARP Key Points
+ {#each arpVsNdpContent.quickReference.arp as point, index (`arp-point-${index}`)} +
{point}
+ {/each} +
+ +
+
NDP Key Points
+ {#each arpVsNdpContent.quickReference.ndp as point, index (`ndp-point-${index}`)} +
{point}
+ {/each} +
+
+
+ +
+

IPv4 to IPv6 Migration Tips

+
+
Important Considerations
+ {#each arpVsNdpContent.migrationTips as tip, index (`migration-tip-${index}`)} +
+
{tip}
+
+ {/each} +
+ +
+
+ + Key Takeaway +
+
+ While NDP is more complex than ARP, it's also much more capable and efficient. Understanding both protocols is + essential for mixed IPv4/IPv6 environments. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/asn/+page.svelte b/src/routes/[lang]/reference/asn/+page.svelte new file mode 100644 index 00000000..11bf9eff --- /dev/null +++ b/src/routes/[lang]/reference/asn/+page.svelte @@ -0,0 +1,215 @@ + + +
+
+
+

{asnContent.title}

+

{asnContent.description}

+
+ +
+

{asnContent.sections.overview.title}

+

{asnContent.sections.overview.content}

+
+ +
+

{asnContent.sections.asn.title}

+

{asnContent.sections.asn.content}

+
+ +
+

ASN Number Ranges

+ {#each asnContent.asnTypes as type, index (`${type.name}-${index}`)} +
+
{type.name}
+
+
Range: {type.range}
+
Description: {type.description}
+
Usage: {type.usage}
+
Examples:
+ {#each type.examples as example, index (`example-${index}`)} +
{example}
+ {/each} +
+
+ {/each} +
+ +
+

{asnContent.bgpBasics.title}

+

{asnContent.bgpBasics.description}

+ +

Key BGP Concepts

+
+ {#each asnContent.bgpBasics.concepts as concept, index (`${concept.term}-${index}`)} +
+
{concept.term}
+
+ Definition: + {concept.definition}
+ Example: + {concept.example} +
+
+ {/each} +
+ +

BGP Types

+ + + + + + + + + + + {#each asnContent.bgpBasics.types as type, index (`${type.type}-${index}`)} + + + + + + + {/each} + +
TypeDescriptionUsagePort
{type.type}{type.description}{type.usage}{type.port}
+
+ +
+

{asnContent.ipToAsnMapping.title}

+

{asnContent.ipToAsnMapping.description}

+ +

How It Works

+
    + {#each asnContent.ipToAsnMapping.process as step, index (`mapping-process-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

Real-World Examples

+ + + + + + + + + + + {#each asnContent.ipToAsnMapping.examples as example, index (`${example.asn}-${index}`)} + + + + + + + {/each} + +
IP RangeASNOrganizationDescription
{example.ipRange}{example.asn}{example.organization}{example.description}
+
+ +
+

{asnContent.lookupTools.title}

+

{asnContent.lookupTools.description}

+ +
+ {#each asnContent.lookupTools.methods as method, index (`${method.method}-${index}`)} +
+
{method.method}
+
{method.command}
+
+ {method.description}
+ {method.example} +
+
+ {/each} +
+ +

Common Lookup Commands

+
+
Try These Commands
+ {#each asnContent.lookupTools.commonCommands as command, index (`command-${index}`)} +
+ {command} +
+ {/each} +
+
+ +
+

Real-World AS Examples

+ {#each asnContent.realWorldExamples as example, index (`${example.asn}-${index}`)} +
+
{example.scenario}: {example.asn}
+
+
Organization: {example.organization}
+
Role: {example.role}
+
IP Blocks: {example.ipBlocks}
+
Peering: {example.peers}
+
+
+ {/each} +
+ +
+

Benefits of the AS System

+
    + {#each asnContent.benefits as benefit, index (`benefit-${index}`)} +
  • {benefit}
  • + {/each} +
+
+ +
+

Troubleshooting with ASN Information

+ {#each asnContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Likely Cause: {issue.cause}

+

Investigation: {issue.investigation}

+

Solution: {issue.solution}

+
+
+ {/each} +
+ +
+

Getting Started with ASN Knowledge

+ {#each asnContent.gettingStarted as step, index (`${step.step}-${index}`)} +
+
+ + {step.step} +
+
+

{step.description}

+

Action: {step.action}

+
+
+ {/each} +
+ +
+

Quick Facts to Remember

+
+
Key Points
+ {#each asnContent.quickFacts as fact, index (`fact-${index}`)} +
+
{fact}
+
+ {/each} +
+
+
+
diff --git a/src/routes/[lang]/reference/cgnat/+page.svelte b/src/routes/[lang]/reference/cgnat/+page.svelte new file mode 100644 index 00000000..6df13bca --- /dev/null +++ b/src/routes/[lang]/reference/cgnat/+page.svelte @@ -0,0 +1,228 @@ + + +
+
+
+

{cgnatContent.title}

+

{cgnatContent.description}

+
+ +
+

{cgnatContent.sections.overview.title}

+

{cgnatContent.sections.overview.content}

+
+ +
+

{cgnatContent.sections.why.title}

+

{cgnatContent.sections.why.content}

+
+ +
+

CGNAT Address Range

+
+
+ + Shared Address Space +
+
+

Range: {cgnatContent.addressRange.range}

+

Full Range: {cgnatContent.addressRange.fullRange}

+

Total Addresses: {cgnatContent.addressRange.totalAddresses}

+

RFC: {cgnatContent.addressRange.rfc}

+
+
+ +

Address Breakdown

+ + + + + + + + + + {#each cgnatContent.addressRange.breakdown as block, index (`${block.network}-${index}`)} + + + + + + {/each} + +
Network BlockAvailable AddressesTypical Use
{block.network}{block.addresses}{block.use}
+
+ +
+

{cgnatContent.howItWorks.title}

+

{cgnatContent.howItWorks.description}

+ +

Two-Layer NAT System

+ + + + + + + + + + + + {#each cgnatContent.howItWorks.layers as layer, index (`${layer.layer}-${index}`)} + + + + + + + + {/each} + +
LayerLocationInside AddressOutside AddressPurpose
{layer.layer}{layer.location}{layer.inside}{layer.outside}{layer.purpose}
+ +

Traffic Flow

+
    + {#each cgnatContent.howItWorks.flow as step, index (`flow-step-${index}`)} +
  1. {step}
  2. + {/each} +
+
+ +
+

{cgnatContent.identification.title}

+ {#each cgnatContent.identification.methods as method, index (`${method.method}-${index}`)} +
+
{method.method}
+
+
Description: {method.description}
+
+ CGNAT Indicator: {method.cgnatIndicator} +
+
+ Normal Indicator: + {method.normalIndicator} +
+
+
+ {/each} +
+ +
+

Impact on Services

+ +

Negative Impacts

+ {#each cgnatContent.impacts.negative as impact, index (`${impact.impact}-${index}`)} +
+
+ + {impact.impact} +
+
+

Description: {impact.description}

+

Affected Services: {impact.affectedServices.join(', ')}

+

Workaround: {impact.workaround}

+
+
+ {/each} + +

Positive Aspects

+
    + {#each cgnatContent.impacts.positive as positive, index (`positive-${index}`)} +
  • {positive}
  • + {/each} +
+
+ +
+

Workarounds and Solutions

+ {#each cgnatContent.workarounds as solution, index (`${solution.solution}-${index}`)} +
+
{solution.solution}
+
+
Description: {solution.description}
+
Effectiveness: {solution.effectiveness}
+
Cost: {solution.cost}
+
+
+ {/each} +
+ +
+

Troubleshooting Common Issues

+ {#each cgnatContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Cause: {issue.cause}

+

Diagnosis: {issue.diagnosis}

+

Solution: {issue.solution}

+
+
+ {/each} +
+ +
+

Quick CGNAT Check

+ +
+
+
Steps to Check
+
    + {#each cgnatContent.quickCheck.steps as step, index (`quickcheck-step-${index}`)} +
  1. {step}
  2. + {/each} +
+
+ +
+
What to Do Next
+
    + {#each cgnatContent.quickCheck.whatToDo as action, index (`whatToDo-${index}`)} +
  • {action}
  • + {/each} +
+
+
+
+ +
+

Best Practices

+
    + {#each cgnatContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

ISP Perspective

+
+
Why ISPs Use CGNAT
+ {#each cgnatContent.ispPerspective as reason, index (`isp-reason-${index}`)} +
+
{reason}
+
+ {/each} +
+ +
+
+ + Understanding the Trade-off +
+
+ CGNAT is a necessary compromise. It allows ISPs to provide affordable internet service during IPv4 exhaustion, + but at the cost of some functionality. The long-term solution is IPv6 adoption. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/cidr/+page.svelte b/src/routes/[lang]/reference/cidr/+page.svelte new file mode 100644 index 00000000..b0a86123 --- /dev/null +++ b/src/routes/[lang]/reference/cidr/+page.svelte @@ -0,0 +1,99 @@ + + +
+
+
+

{cidrContent.title}

+

{cidrContent.description}

+
+ +
+

{cidrContent.sections.whatIs.title}

+

{cidrContent.sections.whatIs.content}

+ +
+
+ + Quick Example +
+
+ In 192.168.1.0/24, the network is 192.168.1.0 and there are 254 usable host addresses + (192.168.1.1 through 192.168.1.254). +
+
+
+ +
+

{cidrContent.sections.whyReplaced.title}

+

{cidrContent.sections.whyReplaced.content}

+
+ +
+

{cidrContent.sections.howToRead.title}

+

{cidrContent.sections.howToRead.content}

+
+ +
+

Common Examples

+
+
Network Examples
+ {#each cidrContent.examples as example, index (`${example.cidr}-${index}`)} +
+ {example.cidr} + + {example.hosts} +
{example.description}
+
+ {/each} +
+
+ +
+

Prefix Length Reference Table

+ + + + + + + + + + + {#each cidrContent.prefixTable as row, index (`${row.prefix}-${index}`)} + + + + + + + {/each} + +
PrefixSubnet MaskUsable HostsTypical Use
{row.prefix}{row.mask}{row.hosts}{row.typical}
+
+ +
+

Key Points to Remember

+
    + {#each cidrContent.keyPoints as point, index (`point-${index}`)} +
  • {point}
  • + {/each} +
+ +
+
+ + Remember +
+
+ The first and last addresses in any network are reserved (network address and broadcast address), so the + usable host count is always 2 less than the total addresses. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/common-subnets/+page.svelte b/src/routes/[lang]/reference/common-subnets/+page.svelte new file mode 100644 index 00000000..f571761e --- /dev/null +++ b/src/routes/[lang]/reference/common-subnets/+page.svelte @@ -0,0 +1,181 @@ + + + +
+
+

Common Subnets

+

Frequently used CIDR prefixes with masks, host counts, and typical usage.

+
+ +
+
+ CIDR + Subnet Mask + Hosts + Usage +
+ + {#each COMMON_SUBNETS as subnet (`${subnet.cidr}-${subnet.mask}`)} + +
+ /{subnet.cidr} + {subnet.mask} + {subnet.hosts.toLocaleString()} + + {#if subnet.cidr === 8} + Large ISPs + {:else if subnet.cidr === 16} + Universities + {:else if subnet.cidr === 24} + Small businesses + {:else if subnet.cidr === 25} + Departments + {:else if subnet.cidr === 26} + Teams + {:else if subnet.cidr === 27} + Small offices + {:else if subnet.cidr === 28} + Workgroups + {:else if subnet.cidr === 29} + Small groups + {:else if subnet.cidr === 30} + Point-to-point + {:else} + General use + {/if} + +
+
+ {/each} +
+
+ + diff --git a/src/routes/[lang]/reference/icmp/+page.svelte b/src/routes/[lang]/reference/icmp/+page.svelte new file mode 100644 index 00000000..d1bec66b --- /dev/null +++ b/src/routes/[lang]/reference/icmp/+page.svelte @@ -0,0 +1,176 @@ + + +
+
+
+

{icmpContent.title}

+

{icmpContent.description}

+
+ +
+

{icmpContent.sections.overview.title}

+

{icmpContent.sections.overview.content}

+
+ +
+

Common ICMPv4 Types

+ {#each icmpContent.icmpv4Types as type, index (`${type.type}-${index}`)} +
+
Type {type.type}: {type.name}
+
+
Description: {type.description}
+
Common Use: {type.commonUse}
+
Example: {type.example}
+
Troubleshooting: {type.troubleshooting}
+ {#if type.codes} +
Common Codes:
+
    + {#each type.codes as code, index (`code-${code.code}-${index}`)} +
  • Code {code.code}: {code.meaning}
  • + {/each} +
+ {/if} +
+
+ {/each} +
+ +
+

Common ICMPv6 Types

+ {#each icmpContent.icmpv6Types as type, index (`${type.type}-${index}`)} +
+
Type {type.type}: {type.name}
+
+
Description: {type.description}
+
Common Use: {type.commonUse}
+
Example: {type.example}
+
Troubleshooting: {type.troubleshooting}
+ {#if type.codes} +
Common Codes:
+
    + {#each type.codes as code, index (`code-${code.code}-${index}`)} +
  • Code {code.code}: {code.meaning}
  • + {/each} +
+ {/if} +
+
+ {/each} +
+ +
+

Practical Troubleshooting Scenarios

+ {#each icmpContent.practicalExamples as scenario, index (`${scenario.scenario}-${index}`)} +
+
+ + {scenario.scenario} +
+
+

ICMP Types Involved: {scenario.icmpTypes.join(', ')}

+

What to Check:

+
    + {#each scenario.whatToCheck as check, index (`check-${index}`)} +
  • {check}
  • + {/each} +
+

Common Causes: {scenario.commonCauses.join(', ')}

+
+
+ {/each} +
+ +
+

Common ICMP Filtering Issues

+ {#each icmpContent.filteringIssues as issue, index (`${issue.issue}-${index}`)} +
+
{issue.issue}
+
+
Problem: {issue.problem}
+
Solution: {issue.solution}
+
Recommendation: {issue.recommendation}
+
+
+ {/each} +
+ +
+

Troubleshooting Commands

+ + + + + + + + + + {#each icmpContent.troubleshootingCommands as cmd, index (`${cmd.purpose}-${index}`)} + + + + + + {/each} + +
CommandPurposeICMP Type Used
{cmd.command}{cmd.purpose}{cmd.icmpType}
+
+ +
+

Best Practices for ICMP

+
    + {#each icmpContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

ICMP Quick Reference

+ +
+
+
Always Allow These
+ {#each icmpContent.quickReference.mustAllow as type, index (`must-${index}`)} +
{type}
+ {/each} +
+ +
+
Never Filter These
+ {#each icmpContent.quickReference.neverFilter as type, index (`never-${index}`)} +
{type}
+ {/each} +
Critical for proper network operation
+
+
+
+ +
+

Common Mistakes to Avoid

+
+
Don't Do These
+ {#each icmpContent.commonMistakes as mistake, index (`mistake-${index}`)} +
+
{mistake}
+
+ {/each} +
+ +
+
+ + Security vs Functionality +
+
+ Don't block all ICMP for security. Instead, use rate limiting and allow essential types. Blocking ICMP + completely breaks critical network functions like Path MTU Discovery and IPv6 Neighbor Discovery. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/ipv6-address-types/+page.svelte b/src/routes/[lang]/reference/ipv6-address-types/+page.svelte new file mode 100644 index 00000000..3abdf530 --- /dev/null +++ b/src/routes/[lang]/reference/ipv6-address-types/+page.svelte @@ -0,0 +1,134 @@ + + +
+
+
+

{ipv6AddressTypesContent.title}

+

{ipv6AddressTypesContent.description}

+
+ +
+

{ipv6AddressTypesContent.sections.overview.title}

+

{ipv6AddressTypesContent.sections.overview.content}

+
+ +
+

Unicast Address Types

+ {#each ipv6AddressTypesContent.unicastTypes as type, index (`${type.type}-${index}`)} +
+
{type.type}
+
+
Prefix: {type.prefix}
+
Range: {type.range}
+
Description: {type.description}
+
Usage: {type.usage}
+
Example: {type.example}
+
+
+ {/each} +
+ +
+

Special Addresses

+ + + + + + + + + + + {#each ipv6AddressTypesContent.specialAddresses as addr, index (`${addr.address}-${index}`)} + + + + + + + {/each} + +
AddressNameDescriptionUsage
{addr.address}{addr.name}{addr.description}{addr.usage}
+
+ +
+

Multicast Address Scopes

+

All multicast addresses start with ff. The second byte indicates scope:

+ + {#each ipv6AddressTypesContent.multicastTypes as scope, index (`${scope.scope}-${index}`)} +
+
{scope.scope} Scope
+
+
Prefix: {scope.prefix}
+
Description: {scope.description}
+ {#if scope.examples.length > 0} +
Common Addresses:
+ {#each scope.examples as example, index (`example-${index}`)} +
{example}
+ {/each} + {/if} +
+
+ {/each} +
+ +
+

{ipv6AddressTypesContent.anycast.title}

+

{ipv6AddressTypesContent.anycast.description}

+ +
+
+ + Example +
+
+ {ipv6AddressTypesContent.anycast.example} +
+
+ +

Common Anycast Uses

+
    + {#each ipv6AddressTypesContent.anycast.commonUses as use, index (`use-${index}`)} +
  • {use}
  • + {/each} +
+
+ +
+

Reserved Address Ranges

+ + + + + + + + + {#each ipv6AddressTypesContent.reservedRanges as range, index (`${range.prefix}-${index}`)} + + + + + {/each} + +
PrefixPurpose
{range.prefix}{range.purpose}
+
+ +
+

Quick Recognition Tips

+
+
Remember These Patterns
+ {#each ipv6AddressTypesContent.quickTips as tip, index (`tip-${index}`)} +
+
{tip}
+
+ {/each} +
+
+
+
diff --git a/src/routes/[lang]/reference/ipv6-embedded-ipv4/+page.svelte b/src/routes/[lang]/reference/ipv6-embedded-ipv4/+page.svelte new file mode 100644 index 00000000..5a36f77c --- /dev/null +++ b/src/routes/[lang]/reference/ipv6-embedded-ipv4/+page.svelte @@ -0,0 +1,157 @@ + + +
+
+
+

{ipv6EmbeddedIPv4Content.title}

+

{ipv6EmbeddedIPv4Content.description}

+
+ +
+

{ipv6EmbeddedIPv4Content.sections.overview.title}

+

{ipv6EmbeddedIPv4Content.sections.overview.content}

+
+ +
+

IPv4-in-IPv6 Mechanisms

+ {#each ipv6EmbeddedIPv4Content.mechanisms as mechanism, index (`${mechanism.name}-${index}`)} +
+
+ {mechanism.name} + [{mechanism.status}] +
+
+
Prefix: {mechanism.prefix}
+
Purpose: {mechanism.purpose}
+
Format: {mechanism.format}
+
Examples:
+ {#each mechanism.examples as example, index (`example-${index}`)} +
{example}
+ {/each} +
Usage:
+
    + {#each mechanism.usage as use, index (`use-${index}`)} +
  • {use}
  • + {/each} +
+
Note: {mechanism.notes}
+
+
+ {/each} +
+ +
+

{ipv6EmbeddedIPv4Content.recognition.title}

+ + + + + + + + + + {#each ipv6EmbeddedIPv4Content.recognition.patterns as pattern, index (`${pattern.pattern}-${index}`)} + + + + + + {/each} + +
PatternMeaningWhat to Do
{pattern.pattern}{pattern.meaning}{pattern.action}
+
+ +
+

{ipv6EmbeddedIPv4Content.conversion.title}

+

To understand embedded addresses, you need to convert IPv4 addresses to hexadecimal:

+ + + + + + + + + + + {#each ipv6EmbeddedIPv4Content.conversion.examples as example, index (`${example.ipv4}-${index}`)} + + + + + + {/each} + +
IPv4 AddressHex EquivalentBreakdown
{example.ipv4}{example.hex}{example.breakdown}
+ +
+
+ + Quick Tip +
+
+ Each IPv4 octet becomes 2 hex digits. For example: 192 = C0, 168 = A8, so 192.168.1.1 becomes C0A8:0101. +
+
+
+ +
+

Modern Usage Guidelines

+
    + {#each ipv6EmbeddedIPv4Content.modernUsage as guideline, index (`guideline-${index}`)} +
  • {guideline}
  • + {/each} +
+
+ +
+

Common Troubleshooting Issues

+ {#each ipv6EmbeddedIPv4Content.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Cause: {issue.cause}

+

Solution: {issue.solution}

+
+
+ {/each} +
+ +
+

Security Considerations

+
+
Important Security Notes
+ {#each ipv6EmbeddedIPv4Content.securityNotes as note, index (`note-${index}`)} +
+
{note}
+
+ {/each} +
+ +
+
+ + Security Warning +
+
+ Many IPv4-in-IPv6 transition mechanisms have known security vulnerabilities. Disable unused mechanisms and + monitor for unexpected embedded address patterns in your network. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/ipv6-prefix-lengths/+page.svelte b/src/routes/[lang]/reference/ipv6-prefix-lengths/+page.svelte new file mode 100644 index 00000000..b27f8d1c --- /dev/null +++ b/src/routes/[lang]/reference/ipv6-prefix-lengths/+page.svelte @@ -0,0 +1,143 @@ + + +
+
+
+

{ipv6PrefixLengthsContent.title}

+

{ipv6PrefixLengthsContent.description}

+
+ +
+

{ipv6PrefixLengthsContent.sections.overview.title}

+

{ipv6PrefixLengthsContent.sections.overview.content}

+
+ +
+

Common IPv6 Prefix Lengths

+ {#each ipv6PrefixLengthsContent.commonPrefixes as prefix, index (`${prefix.prefix}-${index}`)} +
+
{prefix.prefix} - {prefix.name}
+
+
Capacity: {prefix.hosts}
+
Typical Use: {prefix.typical}
+
Description: {prefix.description}
+
Examples:
+ {#each prefix.examples as example, index (`prefix-example-${index}`)} +
{example}
+ {/each} +
+
+ {/each} +
+ +
+

Usage Guidelines

+ +
+
+
{ipv6PrefixLengthsContent.usageGuidelines.residential.title}
+ {#each ipv6PrefixLengthsContent.usageGuidelines.residential.allocations as alloc, index (`residential-${index}`)} +
{alloc.size}
+
{alloc.description}
+ {/each} +
+ +
+
{ipv6PrefixLengthsContent.usageGuidelines.enterprise.title}
+ {#each ipv6PrefixLengthsContent.usageGuidelines.enterprise.allocations as alloc, index (`enterprise-${index}`)} +
{alloc.size}
+
{alloc.description}
+ {/each} +
+ +
+
{ipv6PrefixLengthsContent.usageGuidelines.subnets.title}
+ {#each ipv6PrefixLengthsContent.usageGuidelines.subnets.allocations as alloc, index (`subnets-${index}`)} +
{alloc.size}
+
{alloc.description}
+ {/each} +
+
+
+ +
+

IPv4 vs IPv6 Comparison

+ + + + + + + + + + {#each ipv6PrefixLengthsContent.comparison.mappings as mapping, index (`${mapping.ipv4}-${index}`)} + + + + + + {/each} + +
IPv4 EquivalentIPv6 UsageNote
{mapping.ipv4}{mapping.ipv6}{mapping.note}
+
+ +
+

Quick Reference Table

+ + + + + + + + + + {#each ipv6PrefixLengthsContent.quickReference as row, index (`${row.prefix}-${index}`)} + + + + + + {/each} + +
PrefixAvailable /64 SubnetsTypical Use
{row.prefix}{row.subnets}{row.note}
+
+ +
+

Best Practices

+
    + {#each ipv6PrefixLengthsContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+ +
+
+ + Key Rule +
+
+ Always use /64 for end-user networks. This is required for SLAAC (Stateless Address Autoconfiguration) and + many IPv6 features. +
+
+
+ +
+

Planning Tips

+
+
Remember These
+ {#each ipv6PrefixLengthsContent.tips as tip, index (`tip-${index}`)} +
+
{tip}
+
+ {/each} +
+
+
+
diff --git a/src/routes/[lang]/reference/ipv6-privacy-addresses/+page.svelte b/src/routes/[lang]/reference/ipv6-privacy-addresses/+page.svelte new file mode 100644 index 00000000..72b6289b --- /dev/null +++ b/src/routes/[lang]/reference/ipv6-privacy-addresses/+page.svelte @@ -0,0 +1,312 @@ + + +
+
+
+

{ipv6PrivacyContent.title}

+

{ipv6PrivacyContent.description}

+
+ +
+

{ipv6PrivacyContent.sections.overview.title}

+

{ipv6PrivacyContent.sections.overview.content}

+
+ +
+

{ipv6PrivacyContent.sections.problem.title}

+

{ipv6PrivacyContent.sections.problem.content}

+
+ +
+

IPv6 Address Types

+ {#each ipv6PrivacyContent.addressTypes as type, index (`${type.type}-${index}`)} +
+
{type.type}
+
+
Formation: {type.formation}
+
Example: {type.example}
+
Privacy Level: {type.privacy}
+ +

Characteristics:

+
    + {#each type.characteristics as characteristic, index (`characteristic-${index}`)} +
  • {characteristic}
  • + {/each} +
+
+
+ {/each} +
+ +
+

{ipv6PrivacyContent.howItWorks.title}

+ +

Address Generation Process

+
    + {#each ipv6PrivacyContent.howItWorks.addressGeneration as step, index (`gen-step-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

Temporary Address Lifecycle

+
    + {#each ipv6PrivacyContent.howItWorks.temporaryLifecycle as step, index (`lifecycle-step-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

Default Operating System Behavior

+
    + {#each ipv6PrivacyContent.howItWorks.defaultBehavior as behavior, index (`behavior-${index}`)} +
  • {behavior}
  • + {/each} +
+
+ +
+

{ipv6PrivacyContent.lifetimes.title}

+ +
+
+
Preferred Lifetime
+
{ipv6PrivacyContent.lifetimes.preferredLifetime.description}
+
Typical: {ipv6PrivacyContent.lifetimes.preferredLifetime.typical}
+
Behavior: {ipv6PrivacyContent.lifetimes.preferredLifetime.behavior}
+
+ +
+
Valid Lifetime
+
{ipv6PrivacyContent.lifetimes.validLifetime.description}
+
Typical: {ipv6PrivacyContent.lifetimes.validLifetime.typical}
+
Behavior: {ipv6PrivacyContent.lifetimes.validLifetime.behavior}
+
+ +
+
Regeneration Interval
+
{ipv6PrivacyContent.lifetimes.regenerationInterval.description}
+
Typical: {ipv6PrivacyContent.lifetimes.regenerationInterval.typical}
+
Behavior: {ipv6PrivacyContent.lifetimes.regenerationInterval.behavior}
+
+ +
+
Max Temporary Addresses
+
{ipv6PrivacyContent.lifetimes.maxTempAddresses.description}
+
Typical: {ipv6PrivacyContent.lifetimes.maxTempAddresses.typical}
+
Behavior: {ipv6PrivacyContent.lifetimes.maxTempAddresses.behavior}
+
+
+
+ +
+

{ipv6PrivacyContent.osImplementations.title}

+ + {#each Object.entries(ipv6PrivacyContent.osImplementations) as [key, os] (key)} + {#if typeof os === 'object' && os.os} +
+
{os.os}
+
+
Default Behavior: {os.defaultBehavior}
+ +

Configuration:

+ {#each os.configuration as config, index (`config-${index}`)} + {config} + {/each} + + {#if (os as OSImplementation).values} +

Values:

+
    + {#each (os as OSImplementation).values! as value, index (`value-${index}`)} +
  • {value}
  • + {/each} +
+ {/if} + +

Useful Commands:

+ {#each (os as OSImplementation).commands as command, index (`command-${index}`)} + {command} + {/each} + + {#if (os as OSImplementation).behavior} +
Behavior: {(os as OSImplementation).behavior}
+ {/if} +
+
+ {/if} + {/each} +
+ +
+

Identifying Address Types

+ + + + + + + + + + + {#each ipv6PrivacyContent.identifyingAddresses as method, index (`method-${index}`)} + + + + + + + {/each} + +
MethodStable AddressTemporary AddressExample
{method.method}{method.stable}{method.temporary}{method.example}
+
+ +
+

Troubleshooting

+ {#each ipv6PrivacyContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Symptoms: {issue.symptoms.join(', ')}

+

Diagnosis: {issue.diagnosis}

+
Solutions:
+
    + {#each issue.solutions as solution, index (`solution-${index}`)} +
  • {solution}
  • + {/each} +
+
+
+ {/each} +
+ +
+

Security Considerations

+ {#each ipv6PrivacyContent.securityConsiderations as security, index (`${security.aspect}-${index}`)} +
+
{security.aspect}
+
+

Benefits:

+
    + {#each security.benefits as benefit, index (`benefit-${index}`)} +
  • {benefit}
  • + {/each} +
+ +

{security.limitations ? 'Limitations' : 'Challenges'}:

+
    + {#each security.limitations || security.challenges as item, index (`limitation-${index}`)} +
  • {item}
  • + {/each} +
+
+
+ {/each} +
+ +
+

When to Use Privacy Addresses

+ {#each ipv6PrivacyContent.whenToUse as scenario, index (`${scenario.scenario}-${index}`)} +
+
{scenario.scenario}
+
+
Recommendation: {scenario.recommendation}
+
Reasoning: {scenario.reasoning}
+
Configuration: {scenario.configuration}
+
+
+ {/each} +
+ +
+

Best Practices

+
    + {#each ipv6PrivacyContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

Common Mistakes

+
    + {#each ipv6PrivacyContent.commonMistakes as mistake, index (`mistake-${index}`)} +
  • {mistake}
  • + {/each} +
+
+ +
+

Quick Reference

+ +
+
+
Address Types
+ {#each ipv6PrivacyContent.quickReference.addressTypes as type, index (`qr-type-${index}`)} +
{type}
+ {/each} +
+ +
+
Identification
+ {#each ipv6PrivacyContent.quickReference.identification as tip, index (`qr-id-${index}`)} +
{tip}
+ {/each} +
+
+ +
+
+
Configuration
+ {#each ipv6PrivacyContent.quickReference.configuration as config, index (`qr-config-${index}`)} +
{config}
+ {/each} +
+ +
+
Troubleshooting
+ {#each ipv6PrivacyContent.quickReference.troubleshooting as tip, index (`qr-trouble-${index}`)} +
{tip}
+ {/each} +
+
+ +
+
+ + Key Point +
+
+ Privacy extensions create multiple IPv6 addresses per interface. Temporary addresses change periodically for + privacy, while stable addresses remain consistent for services. Both can coexist on the same interface. +
+
+
+ +
+

Useful Tools

+
+ {#each ipv6PrivacyContent.tools as tool, index (`${tool.tool}-${index}`)} +
+
{tool.tool}
+
{tool.purpose}
+
+ {/each} +
+
+
+
diff --git a/src/routes/[lang]/reference/link-local-apipa/+page.svelte b/src/routes/[lang]/reference/link-local-apipa/+page.svelte new file mode 100644 index 00000000..8c08cc30 --- /dev/null +++ b/src/routes/[lang]/reference/link-local-apipa/+page.svelte @@ -0,0 +1,242 @@ + + +
+
+
+

{linkLocalApipaContent.title}

+

{linkLocalApipaContent.description}

+
+ +
+

{linkLocalApipaContent.sections.overview.title}

+

{linkLocalApipaContent.sections.overview.content}

+
+ +
+

{linkLocalApipaContent.apipa.title}

+ +
+
Address Range
+
+
Network: {linkLocalApipaContent.apipa.range}
+
Full Range: {linkLocalApipaContent.apipa.fullRange}
+
Usable Range: {linkLocalApipaContent.apipa.usableRange}
+
Reserved: {linkLocalApipaContent.apipa.reservedAddresses.join(', ')}
+
+
+ +

{linkLocalApipaContent.apipa.description}

+ +

When APIPA is Used

+
    + {#each linkLocalApipaContent.apipa.whenUsed as reason, index (`apipa-reason-${index}`)} +
  • {reason}
  • + {/each} +
+ +

How APIPA Works

+
    + {#each linkLocalApipaContent.apipa.howItWorks as step, index (`apipa-step-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

APIPA Characteristics

+
    + {#each linkLocalApipaContent.apipa.characteristics as characteristic, index (`apipa-char-${index}`)} +
  • {characteristic}
  • + {/each} +
+ +

Troubleshooting APIPA Issues

+ {#each linkLocalApipaContent.apipa.troubleshooting as issue, index (`${issue.symptom}-${index}`)} +
+
+ + {issue.symptom} +
+
+

Meaning: {issue.meaning}

+

Solution: {issue.solution}

+
+
+ {/each} +
+ +
+

{linkLocalApipaContent.ipv6LinkLocal.title}

+ +
+
Address Range
+
+
Network: {linkLocalApipaContent.ipv6LinkLocal.range}
+
Full Range: {linkLocalApipaContent.ipv6LinkLocal.fullRange}
+
Common Format: {linkLocalApipaContent.ipv6LinkLocal.commonFormat}
+
+
+ +

{linkLocalApipaContent.ipv6LinkLocal.description}

+ +

Address Formation

+
    + {#each linkLocalApipaContent.ipv6LinkLocal.formation as step, index (`ipv6-formation-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

When IPv6 Link-Local is Used

+
    + {#each linkLocalApipaContent.ipv6LinkLocal.whenUsed as use, index (`ipv6-use-${index}`)} +
  • {use}
  • + {/each} +
+ +

IPv6 Link-Local Characteristics

+
    + {#each linkLocalApipaContent.ipv6LinkLocal.characteristics as characteristic, index (`ipv6-char-${index}`)} +
  • {characteristic}
  • + {/each} +
+ +

Types of IPv6 Link-Local Addresses

+
+ {#each linkLocalApipaContent.ipv6LinkLocal.types as type, index (`${type.type}-${index}`)} +
+
{type.type}
+
{type.description}
+
Example: {type.example}
+
Privacy: {type.privacy}
+
+ {/each} +
+
+ +
+

IPv4 APIPA vs IPv6 Link-Local Comparison

+ + + + + + + + + + + {#each linkLocalApipaContent.comparison as row, index (`${row.aspect}-${index}`)} + + + + + + {/each} + +
AspectIPv4 APIPAIPv6 Link-Local
{row.aspect}{row.ipv4}{row.ipv6}
+
+ +
+

Practical Examples

+ {#each linkLocalApipaContent.practicalExamples as example, index (`${example.scenario}-${index}`)} +
+
{example.scenario}
+
+
IPv4 Behavior: {example.ipv4Behavior}
+
IPv6 Behavior: {example.ipv6Behavior}
+
Impact: {example.impact}
+
+
+ {/each} +
+ +
+

Troubleshooting Commands

+ + + + + + + + + + + + {#each linkLocalApipaContent.troubleshootingCommands as cmd, index (`${cmd.purpose}-${index}`)} + + + + + + + {/each} + +
PurposeWindowsLinuxmacOS
{cmd.purpose}{cmd.windows}{cmd.linux}{cmd.macOS}
+
+ +
+

When to Worry

+ {#each linkLocalApipaContent.whenToWorry as situation, index (`${situation.situation}-${index}`)} +
+
{situation.situation}
+
+
Concern Level: {situation.concern}
+
Action: {situation.action}
+
+
+ {/each} +
+ +
+

Best Practices

+
    + {#each linkLocalApipaContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

Common Mistakes

+
    + {#each linkLocalApipaContent.commonMistakes as mistake, index (`mistake-${index}`)} +
  • {mistake}
  • + {/each} +
+
+ +
+

Quick Reference

+ +
+
+
Recognition
+ {#each linkLocalApipaContent.quickReference.recognition as item, index (`recognition-${index}`)} +
{item}
+ {/each} +
+ +
+
Troubleshooting
+ {#each linkLocalApipaContent.quickReference.troubleshooting as item, index (`qr-trouble-${index}`)} +
{item}
+ {/each} +
+
+ +
+
+ + Key Difference +
+
+ IPv4 APIPA (169.254.x.x) indicates a problem - DHCP failed. IPv6 link-local (fe80::) is normal and required - + every IPv6 interface has one. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/mtu-mss/+page.svelte b/src/routes/[lang]/reference/mtu-mss/+page.svelte new file mode 100644 index 00000000..8542665e --- /dev/null +++ b/src/routes/[lang]/reference/mtu-mss/+page.svelte @@ -0,0 +1,216 @@ + + +
+
+
+

{mtuMssContent.title}

+

{mtuMssContent.description}

+
+ +
+

{mtuMssContent.sections.overview.title}

+

{mtuMssContent.sections.overview.content}

+ +
+
+ + Key Formula +
+
+ MSS = MTU - IP Header - TCP Header
+ For IPv4: MSS = MTU - 20 - 20 = MTU - 40 bytes +
+
+
+ +
+

Common MTU/MSS Values

+ + + + + + + + + + + + {#each mtuMssContent.commonValues as value, index (`${value.medium}-${index}`)} + + + + + + + + {/each} + +
MediumMTUMSSUsageNotes
{value.medium}{value.mtu}{value.mss}{value.usage}{value.notes}
+
+ +
+

{mtuMssContent.calculations.title}

+ {#each mtuMssContent.calculations.examples as example, index (`${example.scenario}-${index}`)} +
+
{example.scenario}
+
+
MTU: {example.mtu} bytes
+
IP Header: {example.ipHeader} bytes
+
TCP Header: {example.tcpHeader} bytes
+
Resulting MSS: {example.mss} bytes
+
Calculation: {example.calculation}
+
+
+ {/each} +
+ +
+

Protocol Overheads

+ + + + + + + + + + {#each mtuMssContent.overheads as overhead, index (`${overhead.protocol}-${index}`)} + + + + + + {/each} + +
Protocol/HeaderOverhead (Bytes)Notes
{overhead.protocol}{overhead.overhead}{overhead.notes}
+
+ +
+

{mtuMssContent.discovery.title}

+

{mtuMssContent.discovery.description}

+ +

PMTU Discovery Process

+
    + {#each mtuMssContent.discovery.process as step, index (`discovery-step-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

Common Issues

+
    + {#each mtuMssContent.discovery.issues as issue, index (`discovery-issue-${index}`)} +
  • {issue}
  • + {/each} +
+
+ +
+

Troubleshooting Common Issues

+ {#each mtuMssContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Cause: {issue.cause}

+

Solution: {issue.solution}

+
+
+ {/each} +
+ +
+

Useful Commands

+ +

Checking MTU Settings

+ + + + + + + + + + {#each mtuMssContent.commands as cmd, index (`${cmd.platform}-${index}`)} + + + + + + {/each} + +
PlatformCommandPurpose
{cmd.platform}{cmd.command}{cmd.purpose}
+ +

Testing MTU Size

+ + + + + + + + + + {#each mtuMssContent.testCommands as cmd, index (`${cmd.purpose}-${index}`)} + + + + + + {/each} + +
PlatformCommandPurpose
{cmd.platform}{cmd.command}{cmd.purpose}
+
+ +
+

Best Practices

+
    + {#each mtuMssContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+ +
+
+ + Performance Tip +
+
+ Mismatched MTU sizes can cause significant performance issues. Always ensure consistent MTU values across your + network path, especially for high-throughput applications. +
+
+
+ +
+

Quick Reference

+
+
Common Values to Remember
+ {#each mtuMssContent.quickReference as value, index (`value-${index}`)} +
+
{value}
+
+ {/each} +
+ +
+
+ + Important Note +
+
+ When troubleshooting connectivity issues, especially with VPNs or tunnels, MTU/MSS mismatches are often the + culprit. Test with smaller packet sizes if large transfers fail but small ones succeed. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/multicast/+page.svelte b/src/routes/[lang]/reference/multicast/+page.svelte new file mode 100644 index 00000000..9ec93bbf --- /dev/null +++ b/src/routes/[lang]/reference/multicast/+page.svelte @@ -0,0 +1,189 @@ + + +
+
+
+

{multicastContent.title}

+

{multicastContent.description}

+
+ +
+

{multicastContent.sections.overview.title}

+

{multicastContent.sections.overview.content}

+
+ +
+

{multicastContent.ipv4Multicast.title}

+

Range: {multicastContent.ipv4Multicast.range}

+ + {#each multicastContent.ipv4Multicast.classes as multicastClass, index (`${multicastClass.name}-${index}`)} +
+
{multicastClass.name}
+
+
Range: {multicastClass.range}
+
Description: {multicastClass.description}
+
Scope: {multicastClass.scope}
+
Examples:
+ {#each multicastClass.examples as example, index (`example-${index}`)} +
{example}
+ {/each} +
+
+ {/each} +
+ +
+

{multicastContent.ipv6Multicast.title}

+

Range: {multicastContent.ipv6Multicast.range}

+ +

Address Structure

+

Format: {multicastContent.ipv6Multicast.structure.format}

+ +
+
+
Flag Bits
+ {#each multicastContent.ipv6Multicast.structure.flags as flag, index (`flag-${index}`)} +
{flag.bit} - {flag.meaning}
+ {/each} +
+ +
+
Scope Values
+ {#each multicastContent.ipv6Multicast.structure.scopes as scope, index (`scope-${index}`)} +
{scope.code} - {scope.name}
+ {/each} +
+
+ +

Well-Known IPv6 Multicast Addresses

+ + + + + + + + + + {#each multicastContent.ipv6Multicast.wellKnown as addr, index (`${addr.address}-${index}`)} + + + + + + {/each} + +
AddressNameDescription
{addr.address}{addr.name}{addr.description}
+
+ +
+

Common Protocol Multicast Addresses

+ + + + + + + + + + + {#each multicastContent.commonProtocols as protocol, index (`${protocol.protocol}-${index}`)} + + + + + + + {/each} + +
ProtocolIPv4IPv6Purpose
{protocol.protocol}{protocol.ipv4}{protocol.ipv6}{protocol.purpose}
+
+ +
+

Important Limitations

+ {#each multicastContent.limitations as limitation, index (`${limitation.title}-${index}`)} +
+
+ + {limitation.title} +
+
+

{limitation.description}

+
    + {#each limitation.details as detail, index (`detail-${index}`)} +
  • {detail}
  • + {/each} +
+
+
+ {/each} +
+ +
+

Troubleshooting Common Issues

+ {#each multicastContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
{issue.issue}
+
+
Common Causes:
+
    + {#each issue.causes as cause, index (`cause-${index}`)} +
  • {cause}
  • + {/each} +
+
Solutions:
+
    + {#each issue.solutions as solution, index (`solution-${index}`)} +
  • {solution}
  • + {/each} +
+
+
+ {/each} +
+ +
+

Best Practices

+
    + {#each multicastContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

Quick Reference

+
+
+
IPv4 Quick List
+ {#each multicastContent.quickReference.ipv4 as addr, index (`ipv4-${index}`)} +
{addr}
+ {/each} +
+ +
+
IPv6 Quick List
+ {#each multicastContent.quickReference.ipv6 as addr, index (`ipv6-${index}`)} +
{addr}
+ {/each} +
+
+ +
+
+ + Key Remember +
+
+ Most multicast addresses are designed for local subnet use only. Without proper multicast routing + configuration, traffic won't cross router boundaries. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/network-classes/+page.svelte b/src/routes/[lang]/reference/network-classes/+page.svelte new file mode 100644 index 00000000..9404170c --- /dev/null +++ b/src/routes/[lang]/reference/network-classes/+page.svelte @@ -0,0 +1,144 @@ + + + +
+
+

Network Classes

+

Class A/B/C overview with default masks, ranges, and typical usage.

+
+ +
+ {#each Object.entries(NETWORK_CLASSES) as [className, classInfo] (className)} + +
+
+
+
+ {className} +
+
+

Class {className}

+ + {classInfo.defaultMask} (/{classInfo.cidr}) + +
+
+ + {classInfo.range.split(' - ')[0]} - {classInfo.range.split(' - ')[1]} + +
+ +

+ {classInfo.description} +

+ +

+ Typical Usage: + {classInfo.usage} +

+
+
+ {/each} +
+
+ + diff --git a/src/routes/[lang]/reference/ports/+page.svelte b/src/routes/[lang]/reference/ports/+page.svelte new file mode 100644 index 00000000..b2fbbd0d --- /dev/null +++ b/src/routes/[lang]/reference/ports/+page.svelte @@ -0,0 +1,165 @@ + + +
+
+
+

{commonPortsContent.title}

+

{commonPortsContent.description}

+
+ +
+

Port Ranges

+ + + + + + + + + + {#each commonPortsContent.ranges as range, index (index)} + + + + + + {/each} + +
RangeNameDescription
{range.range}{range.name}{range.description}
+
+ +
+

Well-Known Ports (0-1023)

+ + + + + + + + + + + {#each commonPortsContent.wellKnown as port (port.port)} + + + + + + + {/each} + +
PortProtocolServiceDescription
{port.port}{port.protocol}{port.service}{port.description}
+
+ +
+

Registered Ports (1024-49151)

+ + + + + + + + + + + {#each commonPortsContent.registered as port (port.port)} + + + + + + + {/each} + +
PortProtocolServiceDescription
{port.port}{port.protocol}{port.service}{port.description}
+
+ +
+

Common Service Categories

+ +
+
+
Web Services
+ {#each commonPortsContent.categories.web as service, index (index)} +
{service.ports} - {service.service}
+
+ {service.secure ? 'Secure' : 'Not secure'} +
+ {/each} +
+ +
+
Email Services
+ {#each commonPortsContent.categories.email as service, index (index)} +
{service.ports} - {service.service}
+
+ {service.secure ? 'Secure' : 'Not secure'} +
+ {/each} +
+ +
+
Remote Access
+ {#each commonPortsContent.categories.remote as service, index (index)} +
{service.ports} - {service.service}
+
+ {service.secure ? 'Secure' : 'Not secure'} +
+ {/each} +
+ +
+
Database Services
+ {#each commonPortsContent.categories.database as service, index (index)} +
{service.ports} - {service.service}
+
+ {service.secure ? 'Secure' : 'Not secure'} +
+ {/each} +
+
+
+ +
+

Important Security Tips

+
+
Remember These
+ {#each commonPortsContent.tips as tip, index (index)} +
+
{tip}
+
+ {/each} +
+ +
+
+ + Security Note +
+
+ Many services have both secure and insecure versions. Always use the secure versions (HTTPS, SSH, FTPS, etc.) + when possible, especially over untrusted networks. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/private-vs-public-ip/+page.svelte b/src/routes/[lang]/reference/private-vs-public-ip/+page.svelte new file mode 100644 index 00000000..3fdf243f --- /dev/null +++ b/src/routes/[lang]/reference/private-vs-public-ip/+page.svelte @@ -0,0 +1,234 @@ + + +
+
+
+

{privateVsPublicContent.title}

+

{privateVsPublicContent.description}

+
+ +
+

{privateVsPublicContent.sections.overview.title}

+

{privateVsPublicContent.sections.overview.content}

+
+ +
+

Private IP Address Ranges (RFC 1918)

+ {#each privateVsPublicContent.privateRanges as range, index (`${range.range}-${index}`)} +
+
{range.range} - {range.class}
+
+
Full Range: {range.fullRange}
+
Total Addresses: {range.addresses}
+
Common Use: {range.commonUse}
+
Examples:
+ {#each range.examples as example, index (`example-${index}`)} + {example} + {/each} +
+
+ {/each} +
+ +
+

Public IP Addresses

+

{privateVsPublicContent.publicRanges.description}

+ +

Characteristics

+
    + {#each privateVsPublicContent.publicRanges.characteristics as characteristic, index (`char-${index}`)} +
  • {characteristic}
  • + {/each} +
+ +

Examples

+ + + + + + + + + {#each privateVsPublicContent.publicRanges.examples as example, index (`public-example-${index}`)} + + + + + {/each} + +
Public IPOwner/Service
{example.ip}{example.owner}
+
+ +
+

{privateVsPublicContent.natImplications.title}

+ +
+
+
{privateVsPublicContent.natImplications.privateToPublic.title}
+
{privateVsPublicContent.natImplications.privateToPublic.description}
+ +

Process:

+
    + {#each privateVsPublicContent.natImplications.privateToPublic.process as step, index (`nat-step-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

Benefits:

+
    + {#each privateVsPublicContent.natImplications.privateToPublic.benefits as benefit, index (`benefit-${index}`)} +
  • {benefit}
  • + {/each} +
+
+ +
+
{privateVsPublicContent.natImplications.publicToPrivate.title}
+
{privateVsPublicContent.natImplications.publicToPrivate.description}
+ +

Challenges:

+
    + {#each privateVsPublicContent.natImplications.publicToPrivate.challenges as challenge, index (`challenge-${index}`)} +
  • {challenge}
  • + {/each} +
+ +

Solutions:

+
    + {#each privateVsPublicContent.natImplications.publicToPrivate.solutions as solution, index (`solution-${index}`)} +
  • {solution}
  • + {/each} +
+
+
+
+ +
+

Quick Identification Methods

+ + + + + + + + + + + + {#each privateVsPublicContent.identification.quickCheck as method, index (`method-${index}`)} + + + + + + + {/each} + +
MethodDescriptionPrivate IndicatorPublic Indicator
{method.method}{method.description}{method.private}{method.public}
+ +

Useful Tools

+
+ {#each privateVsPublicContent.identification.tools as tool, index (`${tool.tool}-${index}`)} +
+
{tool.tool}
+
{tool.purpose}
+
+ {/each} +
+
+ +
+

Common Network Scenarios

+ {#each privateVsPublicContent.commonScenarios as scenario, index (`${scenario.scenario}-${index}`)} +
+
{scenario.scenario}
+
+
Setup: {scenario.setup}
+
Private IPs: {scenario.privateIPs}
+
Public IP: {scenario.publicIP}
+
NAT Behavior: {scenario.natBehavior}
+
+
+ {/each} +
+ +
+

Troubleshooting Common Issues

+ {#each privateVsPublicContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Possible Causes: {issue.possibleCauses.join(', ')}

+

Diagnosis: {issue.diagnosis}

+

Solution: {issue.solution}

+
+
+ {/each} +
+ +
+

Security Considerations

+ {#each privateVsPublicContent.securityConsiderations as security, index (`${security.aspect}-${index}`)} +
+
{security.aspect}
+
+
    + {#each security.considerations as consideration, index (`consideration-${index}`)} +
  • {consideration}
  • + {/each} +
+
+
+ {/each} +
+ +
+

Best Practices

+
    + {#each privateVsPublicContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

Quick Reference

+ +
+
+
Private IP Ranges
+ {#each privateVsPublicContent.quickReference.privateRanges as range, index (`qr-range-${index}`)} +
{range}
+ {/each} +
+ +
+
Identification Tips
+ {#each privateVsPublicContent.quickReference.identificationTips as tip, index (`qr-tip-${index}`)} +
{tip}
+ {/each} +
+
+ +
+
+ + Key Rule +
+
+ If an IP starts with 10, 172.16-31, or 192.168, it's private. Everything else (except other reserved ranges) + is public. Private IPs need NAT to reach the internet. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/reserved-ranges/+page.svelte b/src/routes/[lang]/reference/reserved-ranges/+page.svelte new file mode 100644 index 00000000..00ac49dd --- /dev/null +++ b/src/routes/[lang]/reference/reserved-ranges/+page.svelte @@ -0,0 +1,99 @@ + + + +
+
+

Reserved Ranges

+

Special-purpose IPv4 ranges (loopback, private, link-local, multicast, etc.).

+
+ +
+ {#each Object.entries(RESERVED_RANGES) as [rangeName, rangeInfo] (rangeName)} + +
+
+
+

{rangeInfo.range}

+ {rangeInfo.description} +
+ {rangeInfo.rfc} +
+ + {#if rangeName.includes('PRIVATE')} +
+ Private Network: Not routed on the public Internet +
+ {/if} +
+
+ {/each} +
+
+ + diff --git a/src/routes/[lang]/reference/reverse-dns/+page.svelte b/src/routes/[lang]/reference/reverse-dns/+page.svelte new file mode 100644 index 00000000..32a24906 --- /dev/null +++ b/src/routes/[lang]/reference/reverse-dns/+page.svelte @@ -0,0 +1,192 @@ + + +
+
+
+

{reverseDnsContent.title}

+

{reverseDnsContent.description}

+
+ +
+

{reverseDnsContent.sections.overview.title}

+

{reverseDnsContent.sections.overview.content}

+
+ +
+

{reverseDnsContent.sections.howWorks.title}

+

{reverseDnsContent.sections.howWorks.content}

+
+ +
+

{reverseDnsContent.ipv4Reverse.title}

+ +

Process Steps

+
    + {#each reverseDnsContent.ipv4Reverse.process as step, index (`ipv4-process-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

IPv4 Examples

+ {#each reverseDnsContent.ipv4Reverse.examples as example, index (`${example.ip}-${index}`)} +
+
IP Address: {example.ip}
+
+
Reverse Name: {example.reversed}
+
PTR Record: {example.ptrRecord}
+
Explanation: {example.explanation}
+
+
+ {/each} + +

Network Delegation

+

{reverseDnsContent.ipv4Reverse.delegation.explanation}

+ + + + + + + + + + {#each reverseDnsContent.ipv4Reverse.delegation.examples as example, index (`delegation-${index}`)} + + + + + + {/each} + +
NetworkReverse ZoneDescription
{example.network}{example.zone}{example.description}
+
+ +
+

{reverseDnsContent.ipv6Reverse.title}

+ +

Process Steps

+
    + {#each reverseDnsContent.ipv6Reverse.process as step, index (`ipv6-process-${index}`)} +
  1. {step}
  2. + {/each} +
+ +

IPv6 Examples

+ {#each reverseDnsContent.ipv6Reverse.examples as example, index (`${example.ip}-${index}`)} +
+
IP Address: {example.ip}
+
+
Expanded: {example.expanded}
+
+ Reverse Name: + {example.nibbles} +
+
PTR Record: {example.ptrRecord}
+
Note: {example.explanation}
+
+
+ {/each} + +
+
+ + IPv6 Complexity +
+
+ IPv6 reverse DNS names are much longer than IPv4 because each hex digit becomes a separate label. A single + IPv6 address creates a 72-character reverse DNS name! +
+
+
+ +
+

{reverseDnsContent.practicalExamples.title}

+ +

Command Examples

+ + + + + + + + + + {#each reverseDnsContent.practicalExamples.digExamples as example, index (`dig-${index}`)} + + + + + + {/each} + +
CommandDescriptionExpected Result
{example.command}{example.description}{example.expectedResult}
+ +

Common Use Cases

+
    + {#each reverseDnsContent.practicalExamples.commonChecks as check, index (`check-${index}`)} +
  • {check}
  • + {/each} +
+
+ +
+

Troubleshooting Common Issues

+ {#each reverseDnsContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Causes: {issue.causes.join(', ')}

+

Solutions: {issue.solutions.join(', ')}

+
+
+ {/each} +
+ +
+

Best Practices

+
    + {#each reverseDnsContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

Quick Reference & Tools

+ +
+
+
IPv4 Quick Examples
+ {#each reverseDnsContent.quickReference.ipv4 as example, index (`qr-ipv4-${index}`)} +
{example}
+ {/each} +
+ +
+
IPv6 Quick Examples
+ {#each reverseDnsContent.quickReference.ipv6 as example, index (`qr-ipv6-${index}`)} +
{example}
+ {/each} +
+
+ +

Useful Tools

+
+ {#each reverseDnsContent.tools as tool, index (`${tool.name}-${index}`)} +
+
{tool.name}
+
{tool.description}
+
+ {/each} +
+
+
+
diff --git a/src/routes/[lang]/reference/reverse-zones/+page.svelte b/src/routes/[lang]/reference/reverse-zones/+page.svelte new file mode 100644 index 00000000..c509d016 --- /dev/null +++ b/src/routes/[lang]/reference/reverse-zones/+page.svelte @@ -0,0 +1,276 @@ + + +
+
+
+

{reverseZonesContent.title}

+

{reverseZonesContent.description}

+
+ +
+

{reverseZonesContent.sections.overview.title}

+

{reverseZonesContent.sections.overview.content}

+
+ +
+

{reverseZonesContent.sections.delegation.title}

+

{reverseZonesContent.sections.delegation.content}

+
+ +
+

{reverseZonesContent.ipv4Zones.title}

+ +

Classful Boundaries (Octet-Aligned)

+ + + + + + + + + + + + {#each reverseZonesContent.ipv4Zones.classfullBoundaries as boundary, index (`${boundary.cidr}-${index}`)} + + + + + + + + {/each} + +
CIDRExampleReverse ZoneDescriptionDelegation
{boundary.cidr}{boundary.example}{boundary.reverseZone}{boundary.description}{boundary.delegation}
+ +

Classless Delegation (CNAME Method)

+ {#each reverseZonesContent.ipv4Zones.classlessDelegation as delegation, index (`${delegation.cidr}-${index}`)} +
+
{delegation.cidr} - {delegation.example}
+
+
Addresses: {delegation.addresses}
+
Problem: {delegation.problem}
+
Solution: {delegation.solution}
+
Zone Names:
+ {#each delegation.zones as zone, index (`zone-${index}`)} + {zone} + {/each} +
+
+ {/each} + +

Practical IPv4 Examples

+ {#each reverseZonesContent.ipv4Zones.practicalExamples as example, index (`${example.network}-${index}`)} +
+
{example.scenario}
+
+
Network: {example.network}
+
Reverse Zone: {example.reverseZone}
+ {#if example.reverseZones} +
Reverse Zones:
+ {#each example.reverseZones as zone, index (`rz-${index}`)} + {zone} + {/each} +
Description: {example.description}
+ {:else} +
PTR Records:
+ {#each example.ptrRecords as record, index (`ptr-${index}`)} + {record} + {/each} + {/if} +
Delegation: {example.delegation}
+
+
+ {/each} +
+ +
+

{reverseZonesContent.ipv6Zones.title}

+ +

Nibble Boundaries (4-bit Aligned)

+ + + + + + + + + + + + {#each reverseZonesContent.ipv6Zones.nibbleBoundaries as boundary, index (`${boundary.cidr}-${index}`)} + + + + + + + + {/each} + +
CIDRExampleReverse ZoneDescriptionDelegation
{boundary.cidr}{boundary.example}{boundary.reverseZone}{boundary.description}{boundary.delegation}
+ +

Practical IPv6 Examples

+ {#each reverseZonesContent.ipv6Zones.practicalExamples as example, index (`${example.network}-${index}`)} +
+
{example.scenario}
+
+
Network: {example.network}
+
Master Zone: {example.reverseZone}
+
Sub-zones:
+ {#each example.subZones as zone, index (`subzone-${index}`)} + {zone} + {/each} +
Management: {example.management}
+
+
+ {/each} +
+ +
+

{reverseZonesContent.zoneCreation.title}

+ +
+
+
IPv4 Example ({reverseZonesContent.zoneCreation.ipv4Example.network})
+
Zone Name: {reverseZonesContent.zoneCreation.ipv4Example.zoneName}
+ +

Zone File:

+
{reverseZonesContent.zoneCreation.ipv4Example.zoneFile}
+ +

Explanation:

+
    + {#each reverseZonesContent.zoneCreation.ipv4Example.explanation as point, index (`ipv4-point-${index}`)} +
  • {point}
  • + {/each} +
+
+ +
+
IPv6 Example ({reverseZonesContent.zoneCreation.ipv6Example.network})
+
Zone Name: {reverseZonesContent.zoneCreation.ipv6Example.zoneName}
+ +

Zone File:

+
{reverseZonesContent.zoneCreation.ipv6Example.zoneFile}
+ +

Explanation:

+
    + {#each reverseZonesContent.zoneCreation.ipv6Example.explanation as point, index (`ipv6-point-${index}`)} +
  • {point}
  • + {/each} +
+
+
+
+ +
+

Delegation Scenarios

+ {#each reverseZonesContent.delegationScenarios as scenario, index (`${scenario.scenario}-${index}`)} +
+
{scenario.scenario}
+
+
Delegation: {scenario.delegation}
+ + {#if scenario.customerActions} +
Customer Actions:
+
    + {#each scenario.customerActions as action, index (`customer-${index}`)} +
  • {action}
  • + {/each} +
+ +
ISP Actions:
+
    + {#each scenario.ispActions as action, index (`isp-${index}`)} +
  • {action}
  • + {/each} +
+ {:else} +
Process:
+
    + {#each scenario.process as step, index (`process-${index}`)} +
  1. {step}
  2. + {/each} +
+ {/if} +
+
+ {/each} +
+ +
+

Troubleshooting

+ {#each reverseZonesContent.troubleshooting as issue, index (`${issue.issue}-${index}`)} +
+
+ + {issue.issue} +
+
+

Possible Causes: {issue.causes.join(', ')}

+

Diagnosis: {issue.diagnosis}

+

Solution: {issue.solution}

+
+
+ {/each} +
+ +
+

Best Practices

+
    + {#each reverseZonesContent.bestPractices as practice, index (`practice-${index}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

Quick Reference

+ +
+
+
Zone Name Formulas
+ {#each reverseZonesContent.quickReference.zoneFormulas as formula, index (`formula-${index}`)} +
{formula}
+ {/each} +
+ +
+
Essential Records
+ {#each reverseZonesContent.quickReference.essentialRecords as record, index (`record-${index}`)} +
{record}
+ {/each} +
+
+ +
+
+ + Key Rule +
+
+ IPv4 reverse zones reverse the octets (192.0.2.0/24 → 2.0.192.in-addr.arpa). IPv6 reverse zones reverse the + nibbles (2001:db8::/32 → 8.b.d.0.1.0.0.2.ip6.arpa). +
+
+
+ +
+

Testing Tools

+
+ {#each reverseZonesContent.tools as tool, index (`${tool.tool}-${index}`)} +
+
{tool.tool}
+
{tool.purpose}
+
+ {/each} +
+
+
+
diff --git a/src/routes/[lang]/reference/special-use-ipv4/+page.svelte b/src/routes/[lang]/reference/special-use-ipv4/+page.svelte new file mode 100644 index 00000000..0994f6d1 --- /dev/null +++ b/src/routes/[lang]/reference/special-use-ipv4/+page.svelte @@ -0,0 +1,107 @@ + + +
+
+
+

{specialIPv4Content.title}

+

{specialIPv4Content.description}

+
+ +
+

Complete Special-Use IPv4 Ranges

+ + + + + + + + + + + + {#each specialIPv4Content.ranges as range, rangeIdx (`${range.network}-${rangeIdx}`)} + + + + + + + + {/each} + +
NetworkPurposeRFCRoutableDescription
{range.network}{range.purpose}{range.rfc} + {#if range.routable} + Yes + {:else} + No + {/if} + {range.description}
+
+ +
+

Common Address Categories

+ +
+
+
Private Networks (RFC 1918)
+ {#each specialIPv4Content.categories.private as network, privIdx (`${network}-${privIdx}`)} +
{network}
+ {/each} +
Never routed on the public internet
+
+ +
+
Test Networks (RFC 5737)
+ {#each specialIPv4Content.categories.testing as network, testIdx (`${network}-${testIdx}`)} +
{network}
+ {/each} +
Safe for documentation and examples
+
+ +
+
Carrier-Grade NAT
+ {#each specialIPv4Content.categories.cgnat as network, cgnatIdx (`${network}-${cgnatIdx}`)} +
{network}
+ {/each} +
ISP shared addressing space
+
+ +
+
Special Purpose
+ {#each specialIPv4Content.categories.special as network, specIdx (`${network}-${specIdx}`)} +
{network}
+ {/each} +
Loopback, link-local, multicast
+
+
+
+ +
+

Quick Recognition Tips

+
+
What Each Range Means
+ {#each specialIPv4Content.quickTips as tip, tipIdx (`${tip}-${tipIdx}`)} +
+
{tip}
+
+ {/each} +
+ +
+
+ + Important Note +
+
+ If you see 100.64.x.x addresses, your ISP is using Carrier-Grade NAT (CGNAT). This can cause issues with port + forwarding, gaming, and some applications that require direct connectivity. +
+
+
+
+
diff --git a/src/routes/[lang]/reference/supernetting/+page.svelte b/src/routes/[lang]/reference/supernetting/+page.svelte new file mode 100644 index 00000000..d4d5062f --- /dev/null +++ b/src/routes/[lang]/reference/supernetting/+page.svelte @@ -0,0 +1,140 @@ + + +
+
+
+

{supernetContent.title}

+

{supernetContent.description}

+
+ +
+

{supernetContent.sections.whatIs.title}

+

{supernetContent.sections.whatIs.content}

+ +
+
+ + Main Goal +
+
+ Reduce the number of routes in routing tables while maintaining connectivity to all networks. +
+
+
+ +
+

{supernetContent.sections.requirements.title}

+

{supernetContent.sections.requirements.content}

+
+ +
+

Summarization Examples

+ {#each supernetContent.examples as example, exIdx (`${example.title}-${exIdx}`)} +
+
{example.title}
+
+
Individual Networks:
+ {#each example.networks as network, netIdx (`${network}-${netIdx}`)} +
{network}
+ {/each} +
↓ Summarizes to ↓
+
{example.summary}
+
{example.explanation} - {example.addresses}
+
+
+ {/each} +
+ +
+

{supernetContent.stepByStep.title}

+
    + {#each supernetContent.stepByStep.steps as step, stepIdx (`${step}-${stepIdx}`)} +
  1. {step}
  2. + {/each} +
+
+ +
+

{supernetContent.binaryExample.title}

+

{supernetContent.binaryExample.scenario}

+ + + + + + + + + + {#each supernetContent.binaryExample.binary as row, rowIdx (`${row.network}-${rowIdx}`)} + + + + + {/each} + +
NetworkBinary Representation
{row.network}{row.binary}
+ +
+
+ + Analysis +
+
+ {supernetContent.binaryExample.analysis} +
+
+
+ +
+

Benefits of Route Summarization

+
    + {#each supernetContent.benefits as benefit, benIdx (`${benefit}-${benIdx}`)} +
  • {benefit}
  • + {/each} +
+
+ +
+

Common Pitfalls

+ {#each supernetContent.pitfalls as pitfall, pitIdx (`${pitfall.title}-${pitIdx}`)} +
+
+ + {pitfall.title} +
+
+

Problem: {pitfall.problem}

+

Example: {pitfall.example}

+
+
+ {/each} +
+ +
+

Quick Reference Table

+ + + + + + + + + + {#each supernetContent.quickReference as row, refIdx (`${row.networks}-${refIdx}`)} + + + + + + {/each} + +
Input NetworksSummary PrefixRoutes Saved
{row.networks}{row.summary}{row.saves}
+
+
+
diff --git a/src/routes/[lang]/reference/vlsm/+page.svelte b/src/routes/[lang]/reference/vlsm/+page.svelte new file mode 100644 index 00000000..6ced2c47 --- /dev/null +++ b/src/routes/[lang]/reference/vlsm/+page.svelte @@ -0,0 +1,101 @@ + + +
+
+
+

{vlsmContent.title}

+

{vlsmContent.description}

+
+ +
+

{vlsmContent.sections.whatIs.title}

+

{vlsmContent.sections.whatIs.content}

+
+ +
+

{vlsmContent.sections.whenWhy.title}

+

{vlsmContent.sections.whenWhy.content}

+ +
+
+ + Key Benefit +
+
+ VLSM prevents IP address waste by letting you create subnets that are exactly the right size for each purpose. +
+
+
+ +
+

{vlsmContent.sections.howItWorks.title}

+

{vlsmContent.sections.howItWorks.content}

+
+ +
+

{vlsmContent.example.title}

+

{vlsmContent.example.scenario}

+ +
+
+
Requirements
+
    + {#each vlsmContent.example.requirements as req, reqIdx (`${req.name}-${reqIdx}`)} +
  • {req.name}: {req.hosts} hosts (needs {req.needsPrefix})
  • + {/each} +
+
+ +
+
VLSM Solution
+
    + {#each vlsmContent.example.solution as subnet, subIdx (`${subnet.subnet}-${subIdx}`)} +
  • {subnet.subnet} - {subnet.use} ({subnet.hosts})
  • + {/each} +
+
+
+
+ +
+

Common Pitfalls and Solutions

+ {#each vlsmContent.pitfalls as pitfall, pitIdx (`${pitfall.title}-${pitIdx}`)} +
+
+ + {pitfall.title} +
+
+

Problem: {pitfall.problem}

+

Solution: {pitfall.solution}

+
+
+ {/each} +
+ +
+

Best Practices

+
    + {#each vlsmContent.bestPractices as practice, pracIdx (`${practice}-${pracIdx}`)} +
  • {practice}
  • + {/each} +
+
+ +
+

Quick Tips

+
+
Remember These
+ {#each vlsmContent.tips as tip, tipIdx (`${tip}-${tipIdx}`)} +
+
{tip}
+
+ {/each} +
+
+
+
diff --git a/src/routes/[lang]/reference/wildcard-masks/+page.svelte b/src/routes/[lang]/reference/wildcard-masks/+page.svelte new file mode 100644 index 00000000..fdd65b1c --- /dev/null +++ b/src/routes/[lang]/reference/wildcard-masks/+page.svelte @@ -0,0 +1,203 @@ + + +
+
+
+

{wildcardMasksContent.title}

+

{wildcardMasksContent.description}

+
+ +
+

{wildcardMasksContent.sections.overview.title}

+

{wildcardMasksContent.sections.overview.content}

+
+ +
+

{wildcardMasksContent.sections.difference.title}

+

{wildcardMasksContent.sections.difference.content}

+ +
+
+ + Key Difference +
+
+ Wildcard masks are the bitwise inverse of subnet masks. If you know one, you can calculate the other by + subtracting from 255.255.255.255. +
+
+
+ +
+

Conversion Examples

+ {#each wildcardMasksContent.conversionExamples as example, exIdx (`${example.subnet}-${exIdx}`)} +
+
{example.description}
+
+
Subnet Mask: {example.subnet}
+
Subnet Binary: {example.subnetBinary}
+
Wildcard Mask: {example.wildcard}
+
Wildcard Binary: {example.wildcardBinary}
+
+
+ {/each} +
+ +
+

{wildcardMasksContent.quickConversion.title}

+

Formula: {wildcardMasksContent.quickConversion.formula}

+ +

Steps:

+
    + {#each wildcardMasksContent.quickConversion.steps as step, stepIdx (`${step}-${stepIdx}`)} +
  1. {step}
  2. + {/each} +
+ +

Examples:

+ + + + + + + + + + {#each wildcardMasksContent.quickConversion.examples as example, qexIdx (`${example.subnet}-${qexIdx}`)} + + + + + + {/each} + +
Subnet MaskCalculationWildcard Mask
{example.subnet}{example.calculation}{example.wildcard}
+
+ +
+

ACL Examples by Platform

+ {#each wildcardMasksContent.aclExamples as platform, pIdx (`${platform.title}-${pIdx}`)} +

{platform.title}

+ {#each platform.entries as entry, eIdx (`${entry}-${eIdx}`)} +
+
{entry.meaning}
+
+
ACL Entry: {entry.acl}
+
Explanation: {entry.explanation}
+
+
+ {/each} + {/each} +
+ +
+

Special Cases

+
+ {#each wildcardMasksContent.specialCases as specialCase, scIdx (`${specialCase.case}-${scIdx}`)} +
+
{specialCase.case}
+
Wildcard: {specialCase.wildcard}
+
+ Meaning: + {specialCase.meaning}
+ Usage: + {specialCase.usage} +
+
+ {/each} +
+
+ +
+

Platform Differences

+ + + + + + + + + + + {#each wildcardMasksContent.platformDifferences as platform, pdIdx (`${platform.platform}-${pdIdx}`)} + + + + + + + {/each} + +
PlatformFormatExampleNotes
{platform.platform}{platform.format}{platform.example}{platform.notes}
+
+ +
+

Quick Reference Table

+ + + + + + + + + + + {#each wildcardMasksContent.quickReference as row, refIdx (`${row.subnet}-${refIdx}`)} + + + + + + + {/each} + +
CIDRSubnet MaskWildcard MaskAddresses
{row.prefix}{row.subnet}{row.wildcard}{row.use}
+
+ +
+

Common Mistakes

+ {#each wildcardMasksContent.commonMistakes as mistake, mIdx (`${mistake.mistake}-${mIdx}`)} +
+
+ + {mistake.mistake} +
+
+

Problem: {mistake.problem}

+

Solution: {mistake.solution}

+
+
+ {/each} +
+ +
+

Tips for Success

+
+
Remember These
+ {#each wildcardMasksContent.tips as tip, tipIdx (`${tip}-${tipIdx}`)} +
+
{tip}
+
+ {/each} +
+ +
+
+ + Quick Memory Aid +
+
+ Wildcard 0 = "must match exactly", Wildcard 1 = "don't care". Think of it as a mask where 0 blocks changes and + 1 allows anything. +
+
+
+
+
diff --git a/src/routes/[lang]/search/+page.svelte b/src/routes/[lang]/search/+page.svelte new file mode 100644 index 00000000..41117db0 --- /dev/null +++ b/src/routes/[lang]/search/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/settings/+page.svelte b/src/routes/[lang]/settings/+page.svelte new file mode 100644 index 00000000..188e2cc5 --- /dev/null +++ b/src/routes/[lang]/settings/+page.svelte @@ -0,0 +1,101 @@ + + +
+
+

{$t('settings.title')}

+

{$t('settings.description')}

+
+ + {#if DISABLE_SETTINGS} +
+
+ +
+

{$t('settings.disabled.title')}

+

{$t('settings.disabled.message')}

+
+ {:else} + + {/if} +
+ + diff --git a/src/routes/[lang]/subnetting/+page.svelte b/src/routes/[lang]/subnetting/+page.svelte new file mode 100644 index 00000000..4988b988 --- /dev/null +++ b/src/routes/[lang]/subnetting/+page.svelte @@ -0,0 +1,494 @@ + + +
+ + + + +
+ + +

What's Subnetting?

+
+

+ Subnetting is the process of splitting a large network into smaller, easier-to-manage pieces. Each subnet has its + own network address and range of IPs, which helps organize devices, improve security, and reduce wasted addresses. + It's core to network planning (both small home labs, or managing a large office or campus). +

+

+ These tools aim to make this easier for you, handling the math and planning for you. They calculate network and + broadcast addresses, host ranges, and help design or summarize networks so you can focus on building, not IP + crunching. +

+
+ + +
+

Essential Concepts

+
+ {#each keyConcepts as concept (concept.title)} +
+
+ +

{concept.title}

+
+

{concept.description}

+ {#if concept.example} + {concept.example} + {/if} +
+ {/each} +
+
+ + +
+

Subnetting Techniques

+
+ {#each subnettingTechniques as technique (technique.name)} +
+
+ +

{technique.name}

+
+

{technique.description}

+
+ Best for: + {technique.useCase} +
+
+ {/each} +
+
+ + +
+

Common Subnet Masks

+
+ + + + + + + + + + + {#each commonSubnetMasks as mask (mask.cidr)} + + + + + + + {/each} + +
CIDRSubnet MaskHostsSubnets
/{mask.cidr}{mask.decimal}{formatNumber(mask.hosts)}{formatNumber(mask.networks)}
+
+
+ + +
+
+
+ +

Best Practices

+
+
    + {#each practicalTips as tip (tip)} +
  • {tip}
  • + {/each} +
+
+ +
+
+ +

Common Mistakes

+
+
    + {#each commonMistakes as mistake (mistake)} +
  • {mistake}
  • + {/each} +
+
+
+
+ + diff --git a/src/routes/[lang]/subnetting/ipv4-subnet-calculator/+page.svelte b/src/routes/[lang]/subnetting/ipv4-subnet-calculator/+page.svelte new file mode 100644 index 00000000..30e398f7 --- /dev/null +++ b/src/routes/[lang]/subnetting/ipv4-subnet-calculator/+page.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/routes/[lang]/subnetting/ipv6-subnet-calculator/+page.svelte b/src/routes/[lang]/subnetting/ipv6-subnet-calculator/+page.svelte new file mode 100644 index 00000000..a1bc3fcb --- /dev/null +++ b/src/routes/[lang]/subnetting/ipv6-subnet-calculator/+page.svelte @@ -0,0 +1,217 @@ + + +
+ + +
+

About IPv6 Subnetting

+

+ IPv6 subnetting uses 128-bit addresses and hierarchical prefix-based allocation to provide virtually + unlimited address space. Unlike IPv4, IPv6 simplifies subnet planning with: +

+ +
+
+

Massive Address Space

+

128-bit addressing provides 2^128 addresses - virtually unlimited for any network

+
+
+

Simplified Subnetting

+

Standard /64 subnets eliminate complex subnet calculations

+
+
+

Hierarchical Design

+

Provider-independent addressing with clear network hierarchy

+
+
+

No Broadcast Domain

+

Uses multicast instead of broadcast, improving network efficiency

+
+
+ +
+

IPv6 Address Structure

+
+
+
Global Unicast (2000::/3)
+

Internet-routable addresses for global connectivity

+
+
+
Link-Local (fe80::/10)
+

Local network communication, automatically configured

+
+
+
Unique Local (fc00::/7)
+

Private addresses for internal networks (like RFC 1918)

+
+
+
Multicast (ff00::/8)
+

One-to-many communication replacing broadcast

+
+
+
+ +
+

IPv6 Subnetting Best Practices

+
+
+
Standard /64 Subnets
+

Use /64 for all LAN segments to ensure SLAAC and privacy extensions work properly

+
+
+
Hierarchical Allocation
+

Plan address space hierarchically: /48 sites, /56 small sites, /64 subnets

+
+
+
Address Compression
+

Use :: notation to compress consecutive zero groups for readability

+
+
+
Documentation Prefix
+

Use 2001:db8::/32 for examples and documentation

+
+
+
+ +
+

+ + IPv6 Planning Tip +

+

+ IPv6's massive address space eliminates the need for complex subnetting. Focus on logical network hierarchy + rather than conserving addresses. A single /64 subnet provides more addresses than the entire IPv4 internet. +

+
+
+
+ + diff --git a/src/routes/[lang]/subnetting/planner/+page.svelte b/src/routes/[lang]/subnetting/planner/+page.svelte new file mode 100644 index 00000000..d2942da1 --- /dev/null +++ b/src/routes/[lang]/subnetting/planner/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/[lang]/subnetting/supernet-calculator/+page.svelte b/src/routes/[lang]/subnetting/supernet-calculator/+page.svelte new file mode 100644 index 00000000..a3dad746 --- /dev/null +++ b/src/routes/[lang]/subnetting/supernet-calculator/+page.svelte @@ -0,0 +1,190 @@ + + +
+ + +
+

About Supernetting

+

+ Supernetting (also called route aggregation or CIDR block aggregation) is the process of combining + multiple smaller networks into a single larger network. This technique is essential for: +

+ +
+
+

Reduced Routing Tables

+

Fewer routes mean faster lookups and reduced memory usage in routers

+
+
+

Improved Scalability

+

Internet routing scales better with aggregated routes instead of individual subnets

+
+
+

Better Performance

+

Reduced route advertisements and faster convergence in routing protocols

+
+
+

Easier Management

+

Simplified network policies and access control lists

+
+
+ +
+

When to Use Supernetting

+
+
+
ISP Route Aggregation
+

Combining customer routes for BGP advertisements

+
+
+
Enterprise Networks
+

Summarizing branch office networks at headquarters

+
+
+
Data Centers
+

Aggregating server farm subnets for external advertisement

+
+
+
Network Redesign
+

Optimizing existing IP allocations for better summarization

+
+
+
+ +
+

+ + Pro Tip +

+

+ For optimal supernetting, design your IP allocation strategy from the beginning. Contiguous, power-of-2 sized + networks aggregate much more efficiently than scattered allocations. +

+
+
+
+ + diff --git a/src/routes/[lang]/subnetting/vlsm-calculator/+page.svelte b/src/routes/[lang]/subnetting/vlsm-calculator/+page.svelte new file mode 100644 index 00000000..db83cb27 --- /dev/null +++ b/src/routes/[lang]/subnetting/vlsm-calculator/+page.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte index 21bf7813..188e2cc5 100644 --- a/src/routes/settings/+page.svelte +++ b/src/routes/settings/+page.svelte @@ -7,6 +7,7 @@ import { navbarDisplay } from '$lib/stores/navbarDisplay'; import { homepageLayout } from '$lib/stores/homepageLayout'; import { DISABLE_SETTINGS } from '$lib/config/customizable-settings'; + import { t } from '$lib/stores/language'; onMount(() => { // Initialize stores @@ -19,8 +20,8 @@
-

Settings

-

Customize your experience with themes, layouts, and accessibility options.

+

{$t('settings.title')}

+

{$t('settings.description')}

{#if DISABLE_SETTINGS} @@ -28,8 +29,8 @@
-

Settings Disabled

-

Settings for this instance have been disabled by your administrator.

+

{$t('settings.disabled.title')}

+

{$t('settings.disabled.message')}

{:else} From ae7733ba00ee63d4f7086f05b7b5aeb3790a7b18 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sun, 26 Oct 2025 18:19:02 +0000 Subject: [PATCH 02/14] Mass-translation effort of diagnostic and DHCP tools --- .../components/furniture/SettingsPanel.svelte | 14 +- src/lib/components/tools/CAABuilder.svelte | 142 +++++---- src/lib/components/tools/CIDRAlignment.svelte | 122 +++---- src/lib/components/tools/CIDRAllocator.svelte | 91 +++--- src/lib/components/tools/CIDRCompare.svelte | 89 +++--- src/lib/components/tools/CIDRContains.svelte | 124 ++++---- .../components/tools/CIDRDeaggregate.svelte | 105 ++++--- src/lib/components/tools/CIDRDiff.svelte | 126 +++++--- src/lib/components/tools/CIDROverlap.svelte | 109 ++++--- src/lib/components/tools/CIDRSplitter.svelte | 111 ++++--- .../components/tools/CIDRSummarizer.svelte | 87 ++--- .../components/tools/ClientIDOption61.svelte | 163 ++++++---- .../tools/DHCPFingerprinting.svelte | 184 ++++++----- .../tools/DHCPOption119Builder.svelte | 204 +++++++----- .../tools/DHCPOption121Builder.svelte | 215 ++++++++----- .../tools/DHCPOption150Builder.svelte | 268 ++++++++++------ .../tools/DHCPOption43Generator.svelte | 117 +++---- .../tools/DHCPOption60Builder.svelte | 162 +++++----- .../tools/DHCPOption82Builder.svelte | 195 +++++++----- .../tools/DHCPSnippetsGenerator.svelte | 106 ++++--- .../components/tools/DHCPv6DNSBuilder.svelte | 137 ++++---- src/lib/components/tools/DHCPv6FQDN.svelte | 117 ++++--- .../components/tools/DKIMKeyGenerator.svelte | 144 +++++---- src/lib/components/tools/DMARCBuilder.svelte | 215 ++++++------- src/lib/components/tools/DNSAAAAABulk.svelte | 74 +++-- .../components/tools/DNSCNAMEBuilder.svelte | 116 ++++--- src/lib/components/tools/DNSKEYKeyTag.svelte | 105 +++---- .../tools/DNSLabelNormalizer.svelte | 103 +++--- src/lib/components/tools/DNSMXPlanner.svelte | 117 +++---- .../components/tools/DNSOptions6And15.svelte | 170 ++++++---- .../tools/DNSRecordValidator.svelte | 169 +++++----- src/lib/components/tools/DNSSPFBuilder.svelte | 135 ++++---- src/lib/components/tools/DNSSRVBuilder.svelte | 183 ++++++----- src/lib/components/tools/DNSTXTEscape.svelte | 116 ++++--- src/lib/components/tools/DUIDGenerator.svelte | 171 ++++++---- .../tools/FreeformTLVBuilder.svelte | 213 +++++++++---- .../components/tools/GatewayOption3.svelte | 155 +++++---- .../components/tools/IAIDCalculator.svelte | 94 +++--- src/lib/components/tools/IPConverter.svelte | 297 ++++++++++++------ .../tools/LeaseTimeCalculator.svelte | 106 +++++-- .../components/tools/LeaseTimeOption51.svelte | 199 ++++++++---- src/lib/components/tools/NAPTRBuilder.svelte | 239 +++++++++----- .../components/tools/PXEProfileBuilder.svelte | 217 ++++++++----- .../components/tools/PrefixDelegation.svelte | 100 +++--- src/lib/components/tools/RandomIP.svelte | 172 ++++++---- .../tools/SupernetCalculator.svelte | 122 +++---- src/lib/components/tools/WildcardMask.svelte | 186 +++++++---- src/lib/content/examples/toolName.ts | 1 + src/lib/i18n/translations/en/common.json | 10 +- .../en/diagnostics/dns-caa-effective.json | 148 +++++++++ .../en/diagnostics/dns-lookup.json | 130 ++++++++ .../en/diagnostics/dns-ns-soa-check.json | 135 ++++++++ .../en/diagnostics/dns-propagation.json | 140 +++++++++ .../en/diagnostics/dns-reverse-lookup.json | 91 ++++++ .../en/diagnostics/dns-soa-serial.json | 156 +++++++++ .../en/diagnostics/dns-trace.json | 131 ++++++++ .../en/diagnostics/email-dmarc-check.json | 154 +++++++++ .../diagnostics/network-asn-geo-lookup.json | 95 ++++++ .../diagnostics/network-tcp-port-check.json | 149 +++++++++ .../en/diagnostics/tls-banner.json | 143 +++++++++ .../translations/en/tools/caa-builder.json | 119 +++++++ .../translations/en/tools/cidr-alignment.json | 71 +++++ .../translations/en/tools/cidr-allocator.json | 77 +++++ .../translations/en/tools/cidr-compare.json | 63 ++++ .../translations/en/tools/cidr-contains.json | 80 +++++ .../en/tools/cidr-deaggregate.json | 64 ++++ .../i18n/translations/en/tools/cidr-diff.json | 74 +++++ .../translations/en/tools/cidr-overlap.json | 67 ++++ .../translations/en/tools/cidr-splitter.json | 67 ++++ .../en/tools/cidr-summarizer.json | 69 ++++ .../en/tools/clientid-option61.json | 108 +++++++ .../en/tools/dhcp-duid-generator.json | 95 ++++++ .../en/tools/dhcp-fingerprinting.json | 122 +++++++ .../en/tools/dhcp-lease-time-calculator.json | 68 ++++ .../en/tools/dhcp-option119-builder.json | 126 ++++++++ .../en/tools/dhcp-option121-builder.json | 136 ++++++++ .../en/tools/dhcp-option150-builder.json | 170 ++++++++++ .../en/tools/dhcp-option43-generator.json | 83 +++++ .../en/tools/dhcp-option60-builder.json | 140 +++++++++ .../en/tools/dhcp-option82-builder.json | 122 +++++++ .../en/tools/dhcp-options-6-15.json | 76 +++++ .../en/tools/dhcp-snippets-generator.json | 70 +++++ .../en/tools/dhcpv6-dns-builder.json | 78 +++++ .../translations/en/tools/dhcpv6-fqdn.json | 69 ++++ .../en/tools/dkim-key-generator.json | 93 ++++++ .../translations/en/tools/dmarc-builder.json | 159 ++++++++++ .../translations/en/tools/dns-aaaa-bulk.json | 52 +++ .../en/tools/dns-cname-builder.json | 88 ++++++ .../en/tools/dns-label-normalizer.json | 86 +++++ .../translations/en/tools/dns-mx-planner.json | 83 +++++ .../en/tools/dns-record-validator.json | 122 +++++++ .../en/tools/dns-srv-builder.json | 112 +++++++ .../translations/en/tools/dns-txt-escape.json | 79 +++++ .../translations/en/tools/dnskey-key-tag.json | 84 +++++ .../en/tools/freeform-tlv-builder.json | 122 +++++++ .../en/tools/gateway-option3.json | 87 +++++ .../en/tools/iaid-calculator.json | 80 +++++ .../translations/en/tools/ip-converter.json | 135 ++++++++ .../en/tools/lease-time-option51.json | 100 ++++++ .../translations/en/tools/naptr-builder.json | 138 ++++++++ .../en/tools/prefix-delegation.json | 65 ++++ .../en/tools/pxe-profile-builder.json | 147 +++++++++ .../i18n/translations/en/tools/random-ip.json | 139 ++++++++ .../translations/en/tools/spf-builder.json | 124 ++++++++ .../en/tools/supernet-calculator.json | 98 ++++++ .../translations/en/tools/wildcard-mask.json | 165 ++++++++++ src/lib/stores/language.ts | 248 +++++++++++++-- .../dns/caa-effective/+page.svelte | 208 ++++++------ .../diagnostics/dns/lookup/+page.svelte | 176 ++++++++--- .../diagnostics/dns/ns-soa-check/+page.svelte | 260 ++++++++++----- .../diagnostics/dns/propagation/+page.svelte | 203 ++++++++---- .../dns/reverse-lookup/+page.svelte | 131 ++++---- .../diagnostics/dns/soa-serial/+page.svelte | 246 ++++++++++----- src/routes/diagnostics/dns/trace/+page.svelte | 168 ++++++---- .../email/dmarc-check/+page.svelte | 211 ++++++++----- .../network/asn-geo-lookup/+page.svelte | 118 ++++--- .../network/tcp-port-check/+page.svelte | 216 +++++++++---- .../diagnostics/tls/banner/+page.svelte | 200 +++++++----- 118 files changed, 11820 insertions(+), 3526 deletions(-) create mode 100644 src/lib/content/examples/toolName.ts create mode 100644 src/lib/i18n/translations/en/diagnostics/dns-caa-effective.json create mode 100644 src/lib/i18n/translations/en/diagnostics/dns-lookup.json create mode 100644 src/lib/i18n/translations/en/diagnostics/dns-ns-soa-check.json create mode 100644 src/lib/i18n/translations/en/diagnostics/dns-propagation.json create mode 100644 src/lib/i18n/translations/en/diagnostics/dns-reverse-lookup.json create mode 100644 src/lib/i18n/translations/en/diagnostics/dns-soa-serial.json create mode 100644 src/lib/i18n/translations/en/diagnostics/dns-trace.json create mode 100644 src/lib/i18n/translations/en/diagnostics/email-dmarc-check.json create mode 100644 src/lib/i18n/translations/en/diagnostics/network-asn-geo-lookup.json create mode 100644 src/lib/i18n/translations/en/diagnostics/network-tcp-port-check.json create mode 100644 src/lib/i18n/translations/en/diagnostics/tls-banner.json create mode 100644 src/lib/i18n/translations/en/tools/caa-builder.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-alignment.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-allocator.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-compare.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-contains.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-deaggregate.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-diff.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-overlap.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-splitter.json create mode 100644 src/lib/i18n/translations/en/tools/cidr-summarizer.json create mode 100644 src/lib/i18n/translations/en/tools/clientid-option61.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-duid-generator.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-fingerprinting.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-lease-time-calculator.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-option119-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-option121-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-option150-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-option43-generator.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-option60-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-option82-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-options-6-15.json create mode 100644 src/lib/i18n/translations/en/tools/dhcp-snippets-generator.json create mode 100644 src/lib/i18n/translations/en/tools/dhcpv6-dns-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dhcpv6-fqdn.json create mode 100644 src/lib/i18n/translations/en/tools/dkim-key-generator.json create mode 100644 src/lib/i18n/translations/en/tools/dmarc-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dns-aaaa-bulk.json create mode 100644 src/lib/i18n/translations/en/tools/dns-cname-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dns-label-normalizer.json create mode 100644 src/lib/i18n/translations/en/tools/dns-mx-planner.json create mode 100644 src/lib/i18n/translations/en/tools/dns-record-validator.json create mode 100644 src/lib/i18n/translations/en/tools/dns-srv-builder.json create mode 100644 src/lib/i18n/translations/en/tools/dns-txt-escape.json create mode 100644 src/lib/i18n/translations/en/tools/dnskey-key-tag.json create mode 100644 src/lib/i18n/translations/en/tools/freeform-tlv-builder.json create mode 100644 src/lib/i18n/translations/en/tools/gateway-option3.json create mode 100644 src/lib/i18n/translations/en/tools/iaid-calculator.json create mode 100644 src/lib/i18n/translations/en/tools/ip-converter.json create mode 100644 src/lib/i18n/translations/en/tools/lease-time-option51.json create mode 100644 src/lib/i18n/translations/en/tools/naptr-builder.json create mode 100644 src/lib/i18n/translations/en/tools/prefix-delegation.json create mode 100644 src/lib/i18n/translations/en/tools/pxe-profile-builder.json create mode 100644 src/lib/i18n/translations/en/tools/random-ip.json create mode 100644 src/lib/i18n/translations/en/tools/spf-builder.json create mode 100644 src/lib/i18n/translations/en/tools/supernet-calculator.json create mode 100644 src/lib/i18n/translations/en/tools/wildcard-mask.json diff --git a/src/lib/components/furniture/SettingsPanel.svelte b/src/lib/components/furniture/SettingsPanel.svelte index b38dacc1..50103b2d 100644 --- a/src/lib/components/furniture/SettingsPanel.svelte +++ b/src/lib/components/furniture/SettingsPanel.svelte @@ -950,7 +950,19 @@ EOF' { return records @@ -71,14 +72,14 @@ // Check domain format if (!domain.trim()) { - errors.push('Domain is required'); + errors.push($t('tools/caa-builder.validation.errors.domainRequired')); } else if (!domain.includes('.')) { - warnings.push('Domain should include TLD (e.g., .com, .org)'); + warnings.push($t('tools/caa-builder.validation.warnings.domainNoTLD')); } // Check if any records are enabled if (enabledRecords.length === 0) { - warnings.push('No CAA records enabled - this will not provide any protection'); + warnings.push($t('tools/caa-builder.validation.warnings.noRecordsEnabled')); } // Check for issue records @@ -86,12 +87,12 @@ const issuewildRecords = enabledRecords.filter((r) => r.tag === 'issuewild'); if (issueRecords.length === 0 && issuewildRecords.length === 0) { - warnings.push('No issue or issuewild records - certificates can be issued by any CA'); + warnings.push($t('tools/caa-builder.validation.warnings.noIssueRecords')); } // Check for wildcard without base issue if (issuewildRecords.length > 0 && issueRecords.length === 0) { - warnings.push('Wildcard authorization without base domain authorization may cause issues'); + warnings.push($t('tools/caa-builder.validation.warnings.wildcardWithoutBase')); } // Check for deny-all configuration @@ -99,7 +100,7 @@ const hasIssuewildNone = issuewildRecords.some((r) => r.value.trim() === ';'); if (hasIssueNone && hasIssuewildNone) { - warnings.push('Both issue and issuewild set to ";" - this will block ALL certificate issuance'); + warnings.push($t('tools/caa-builder.validation.warnings.denyAll')); } // Validate iodef records @@ -110,10 +111,10 @@ if (value.includes('@')) { // Email format if (!value.includes('@') || (!value.startsWith('mailto:') && value.indexOf('@') === -1)) { - errors.push('Invalid iodef email format - use "mailto:user@domain.com" or "user@domain.com"'); + errors.push($t('tools/caa-builder.validation.errors.invalidIodefEmail')); } } else if (!value.startsWith('http://') && !value.startsWith('https://')) { - warnings.push('iodef URL should start with http:// or https://'); + warnings.push($t('tools/caa-builder.validation.warnings.iodefURLFormat')); } } } @@ -131,7 +132,11 @@ } if (duplicateValues.size > 0) { - warnings.push(`Duplicate CAA records found: ${Array.from(duplicateValues).join(', ')}`); + warnings.push( + $t('tools/caa-builder.validation.warnings.duplicateRecords', { + duplicates: Array.from(duplicateValues).join(', '), + }), + ); } return { @@ -189,10 +194,10 @@ showButtonSuccess('export-caa'); } - const exampleConfigurations = [ + const exampleConfigurations = $derived([ { - name: "Let's Encrypt Only", - description: "Allow only Let's Encrypt certificates", + name: $t('tools/caa-builder.examples.letsEncryptOnly.name'), + description: $t('tools/caa-builder.examples.letsEncryptOnly.description'), domain: 'example.com', records: [ { flag: 0, tag: 'issue' as const, value: 'letsencrypt.org', enabled: true }, @@ -200,8 +205,8 @@ ], }, { - name: 'Multiple CAs', - description: 'Allow certificates from multiple providers', + name: $t('tools/caa-builder.examples.multipleCAs.name'), + description: $t('tools/caa-builder.examples.multipleCAs.description'), domain: 'mycompany.com', records: [ { flag: 0, tag: 'issue' as const, value: 'letsencrypt.org', enabled: true }, @@ -211,8 +216,8 @@ ], }, { - name: 'No Certificates', - description: 'Block all certificate issuance', + name: $t('tools/caa-builder.examples.noCertificates.name'), + description: $t('tools/caa-builder.examples.noCertificates.description'), domain: 'secure.example.com', records: [ { flag: 0, tag: 'issue' as const, value: ';', enabled: true }, @@ -220,7 +225,7 @@ { flag: 0, tag: 'iodef' as const, value: 'security@example.com', enabled: true }, ], }, - ]; + ]); function loadExample(example: (typeof exampleConfigurations)[0]): void { domain = example.domain; @@ -244,21 +249,14 @@ showExamples = false; } - const securityTips = [ - 'Start with monitoring: Add iodef records first to receive notifications', - 'Use specific CAs: Only authorize certificate authorities you actually use', - 'Include wildcards: Add issuewild records if you use wildcard certificates', - 'Monitor regularly: Check iodef notifications for unauthorized issuance attempts', - 'Test thoroughly: Verify legitimate certificate renewals still work after deployment', - ]; + const securityTips = $derived($t('tools/caa-builder.securityGuide.tips'));
-

CAA Record Builder

+

{$t('tools/caa-builder.title')}

- Build CAA (Certificate Authority Authorization) records to control which CAs can issue certificates for your - domain. + {$t('tools/caa-builder.description')}

@@ -268,13 +266,15 @@

- Domain Configuration + {$t('tools/caa-builder.domain.title')}

- - + +
@@ -282,35 +282,35 @@

- CAA Records + {$t('tools/caa-builder.records.title')}

@@ -330,8 +330,8 @@
@@ -339,7 +339,7 @@ type="button" class="remove-btn" onclick={() => removeRecord(index)} - use:tooltip={'Remove this record'} + use:tooltip={$t('tools/caa-builder.records.removeTooltip')} > @@ -352,16 +352,16 @@ bind:value={record.value} disabled={!record.enabled} placeholder={record.tag === 'issue' - ? 'letsencrypt.org or ; (to deny all)' + ? $t('tools/caa-builder.placeholders.issue') : record.tag === 'issuewild' - ? 'letsencrypt.org or ; (to deny all)' - : 'security@example.com or https://example.com/security'} + ? $t('tools/caa-builder.placeholders.issuewild') + : $t('tools/caa-builder.placeholders.iodef')} class="record-input" /> {#if (record.tag === 'issue' || record.tag === 'issuewild') && record.enabled}
- Common CAs: + {$t('tools/caa-builder.caShortcuts.label')}
{#each commonCAs.slice(0, 4) as ca (ca.name)}
@@ -394,29 +394,33 @@
-

Generated CAA Records

+

{$t('tools/caa-builder.output.title')}

@@ -432,7 +436,7 @@ {:else}
- Enable and configure CAA records to see output + {$t('tools/caa-builder.output.noRecords')}
{/if}
@@ -441,19 +445,21 @@

- Policy Validation + {$t('tools/caa-builder.validation.title')}

- Active Records: + {$t('tools/caa-builder.validation.activeRecordsLabel')} {validation.recordCount}
- Status: + {$t('tools/caa-builder.validation.statusLabel')} - {validation.isValid ? 'Valid' : 'Invalid'} + {validation.isValid + ? $t('tools/caa-builder.validation.valid') + : $t('tools/caa-builder.validation.invalid')}
@@ -483,7 +489,7 @@ {#if validation.isValid && validation.errors.length === 0 && validation.warnings.length === 0}
-
CAA configuration is valid and ready to deploy!
+
{$t('tools/caa-builder.validation.success')}
{/if}
@@ -492,7 +498,7 @@

- Security Tips + {$t('tools/caa-builder.securityGuide.title')}

@@ -511,7 +517,7 @@
- Example Configurations + {$t('tools/caa-builder.examples.title')}
{#each exampleConfigurations as example (example.name)} diff --git a/src/lib/components/tools/CIDRAlignment.svelte b/src/lib/components/tools/CIDRAlignment.svelte index 9be19f9e..c4447162 100644 --- a/src/lib/components/tools/CIDRAlignment.svelte +++ b/src/lib/components/tools/CIDRAlignment.svelte @@ -1,6 +1,7 @@ @@ -185,18 +186,18 @@ {#if activeTab === 'build'}
-

Build Client Identifier

-

Configure DHCPv4 Client Identifier for device identification

+

{$t('tools/clientid-option61.build.title')}

+

{$t('tools/clientid-option61.build.helpText')}

@@ -204,29 +205,52 @@
- - Hardware address in any common format + + {$t('tools/clientid-option61.build.macAddress.helpText')}
{/if} @@ -234,26 +258,34 @@
- {opaqueFormat === 'hex' ? 'Hexadecimal string (even length)' : 'Plain text identifier'} + {opaqueFormat === 'hex' + ? $t('tools/clientid-option61.build.opaqueData.helpTextHex') + : $t('tools/clientid-option61.build.opaqueData.helpTextText')}
{/if}
@@ -261,17 +293,22 @@ {:else}
-

Decode Client Identifier

-

Decode hex-encoded Client Identifier back to fields

+

{$t('tools/clientid-option61.decode.title')}

+

{$t('tools/clientid-option61.decode.helpText')}

- - Paste hex-encoded Client Identifier to decode + + {$t('tools/clientid-option61.decode.hexData.helpText')}
@@ -279,7 +316,7 @@ {#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/clientid-option61.errors.title')}

{#each validationErrors as error, i (i)}
@@ -291,14 +328,22 @@ {#if activeTab === 'build' && buildResult && validationErrors.length === 0}
-

Generated Client Identifier

+

{$t('tools/clientid-option61.results.buildTitle')}

-
Mode: {buildResult.mode === 'hardware' ? 'Hardware Type + MAC' : 'Opaque Data'}
-
Length: {buildResult.length} bytes
+
+ {$t('tools/clientid-option61.results.mode')} + {buildResult.mode === 'hardware' + ? $t('tools/clientid-option61.results.modeHardware') + : $t('tools/clientid-option61.results.modeOpaque')} +
+
+ {$t('tools/clientid-option61.results.length')} + {$t('tools/clientid-option61.results.lengthBytes', { length: buildResult.length })} +
- {#each [{ title: 'Hexadecimal', content: buildResult.hex, key: 'hex' }, { title: 'Wire Format (Spaced)', content: buildResult.wireFormat, key: 'wire' }] as output (output.key)} + {#each [{ title: $t('tools/clientid-option61.results.outputs.hexadecimal'), content: buildResult.hex, key: 'hex' }, { title: $t('tools/clientid-option61.results.outputs.wireFormat'), content: buildResult.wireFormat, key: 'wire' }] as output (output.key)}

{output.title}

@@ -309,7 +354,9 @@ onclick={() => clipboard.copy(output.content, output.key)} > - {clipboard.isCopied(output.key) ? 'Copied' : 'Copy'} + {clipboard.isCopied(output.key) + ? $t('tools/clientid-option61.buttons.copied') + : $t('tools/clientid-option61.buttons.copy')}
{output.content}
@@ -318,7 +365,7 @@ {#if buildResult.breakdown && buildResult.breakdown.length > 0}
-

Breakdown

+

{$t('tools/clientid-option61.results.breakdown')}

{#each buildResult.breakdown as item, i (i)}
{item.field}
@@ -334,7 +381,7 @@ {/if}
- {#each [{ title: 'ISC DHCPd Configuration', content: buildResult.configExamples?.iscDhcpd, key: 'isc' }, { title: 'Kea DHCPv4 Configuration', content: buildResult.configExamples?.keaDhcp4, key: 'kea' }] as config (config.key)} + {#each [{ title: $t('tools/clientid-option61.results.configs.iscDhcpd'), content: buildResult.configExamples?.iscDhcpd, key: 'isc' }, { title: $t('tools/clientid-option61.results.configs.keaDhcp4'), content: buildResult.configExamples?.keaDhcp4, key: 'kea' }] as config (config.key)} {#if config.content}
@@ -346,7 +393,9 @@ onclick={() => clipboard.copy(config.content!, config.key)} > - {clipboard.isCopied(config.key) ? 'Copied' : 'Copy'} + {clipboard.isCopied(config.key) + ? $t('tools/clientid-option61.buttons.copied') + : $t('tools/clientid-option61.buttons.copy')}
{config.content}
@@ -357,30 +406,36 @@ {#if activeTab === 'decode' && decodeResult && validationErrors.length === 0}
-

Decoded Client Identifier

+

{$t('tools/clientid-option61.results.decodeTitle')}

- Detected Mode: - {decodeResult.mode === 'hardware' ? 'Hardware Type + MAC' : 'Opaque Data'} + {$t('tools/clientid-option61.results.detectedMode')} + {decodeResult.mode === 'hardware' + ? $t('tools/clientid-option61.results.modeHardware') + : $t('tools/clientid-option61.results.modeOpaque')} +
+
+ {$t('tools/clientid-option61.results.length')} + {$t('tools/clientid-option61.results.lengthBytes', { length: decodeResult.length })}
-
Length: {decodeResult.length} bytes
{#if decodeResult.decoded}
{#if decodeResult.decoded.hardwareType !== undefined}
-
Hardware Type
+
{$t('tools/clientid-option61.results.fields.hardwareType')}
- {decodeResult.decoded.hardwareType} ({decodeResult.decoded.hardwareTypeName || 'Unknown'}) + {decodeResult.decoded.hardwareType} ({decodeResult.decoded.hardwareTypeName || + $t('tools/clientid-option61.results.fields.hardwareTypeUnknown')})
{/if} {#if decodeResult.decoded.macAddress}
-
MAC Address
+
{$t('tools/clientid-option61.results.fields.macAddress')}
{decodeResult.decoded.macAddress}
- Hex Encoded: + {$t('tools/dhcp-fingerprinting.lookup.requestedOptions.hexEncoded')} {formatParameterListToHex(parsedParams)}
@@ -261,14 +266,16 @@ {#each parsedParams as param, i (i)}
{param} - {DHCP_OPTION_NAMES[param] || 'Unknown'} + {DHCP_OPTION_NAMES[param] || $t('tools/dhcp-fingerprinting.lookup.requestedOptions.unknown')}
{/each}
{#if analysis && analysis.warnings.length > 0}
-

Security Warnings

+

{$t('tools/dhcp-fingerprinting.lookup.securityWarnings.title')}

{#each analysis.warnings as warning, i (i)}
⚠️ {warning}
{/each} @@ -277,24 +284,34 @@ {#if analysis && (analysis.unusual.length > 0 || (analysis.missing.length > 0 && matches.length > 0))}
-

Option Analysis

+

{$t('tools/dhcp-fingerprinting.lookup.optionAnalysis.title')}

{#if analysis.unusual.length > 0}
-

Unusual Options Detected

-

These options may indicate vendor-specific configurations:

+

{$t('tools/dhcp-fingerprinting.lookup.optionAnalysis.unusualTitle')}

+

{$t('tools/dhcp-fingerprinting.lookup.optionAnalysis.unusualDescription')}

{analysis.unusual.map((o) => `${o} (${DHCP_OPTION_NAMES[o] || 'Unknown'})`).join(', ')}{analysis.unusual + .map( + (o) => + `${o} (${DHCP_OPTION_NAMES[o] || $t('tools/dhcp-fingerprinting.lookup.requestedOptions.unknown')})`, + ) + .join(', ')}
{/if} {#if analysis.missing.length > 0 && matches.length > 0}
-

Missing Options (vs. Best Match)

-

Options present in the best match but not in your fingerprint:

+

{$t('tools/dhcp-fingerprinting.lookup.optionAnalysis.missingTitle')}

+

{$t('tools/dhcp-fingerprinting.lookup.optionAnalysis.missingDescription')}

{analysis.missing.map((o) => `${o} (${DHCP_OPTION_NAMES[o] || 'Unknown'})`).join(', ')}{analysis.missing + .map( + (o) => + `${o} (${DHCP_OPTION_NAMES[o] || $t('tools/dhcp-fingerprinting.lookup.requestedOptions.unknown')})`, + ) + .join(', ')}
{/if} @@ -306,10 +323,14 @@ {#if matches.length > 0}
-

Matching Devices ({matches.length})

+

{$t('tools/dhcp-fingerprinting.lookup.matches.title', { count: matches.length })}

- - + +
@@ -327,32 +348,36 @@

{match.fingerprint.device}

{confidenceBadges[match.fingerprint.confidence] || ''} - {match.fingerprint.confidence} confidence + {$t('tools/dhcp-fingerprinting.lookup.matches.confidence', { level: match.fingerprint.confidence })}
- {match.matchScore.toFixed(0)}% - Match + {$t('tools/dhcp-fingerprinting.lookup.matches.matchScore', { + score: match.matchScore.toFixed(0), + })} + {$t('tools/dhcp-fingerprinting.lookup.matches.matchLabel')}
- OS: + {$t('tools/dhcp-fingerprinting.lookup.matches.osLabel')} {match.fingerprint.os}
- Matched On: + {$t('tools/dhcp-fingerprinting.lookup.matches.matchedOnLabel')} {match.matchedOn.join(', ')}
{#if match.fingerprint.description}
- Description: + {$t('tools/dhcp-fingerprinting.lookup.matches.descriptionLabel')} {match.fingerprint.description}
{/if}
- Known Parameters: + {$t('tools/dhcp-fingerprinting.lookup.matches.knownParamsLabel')} {formatParameterListDisplay(match.fingerprint.parameterRequestList)}
@@ -362,36 +387,41 @@
{:else if parsedParams.length > 0 && !error}
-

No Matches Found

-

The provided fingerprint doesn't match any known devices in the database. This could be:

+

{$t('tools/dhcp-fingerprinting.lookup.noMatches.title')}

+

{$t('tools/dhcp-fingerprinting.lookup.noMatches.description')}

    -
  • A custom DHCP client configuration
  • -
  • An uncommon device or operating system
  • -
  • A device with a modified DHCP request list
  • +
  • {$t('tools/dhcp-fingerprinting.lookup.noMatches.reasons.custom')}
  • +
  • {$t('tools/dhcp-fingerprinting.lookup.noMatches.reasons.uncommon')}
  • +
  • {$t('tools/dhcp-fingerprinting.lookup.noMatches.reasons.modified')}
-

Try adding the Vendor Class Identifier if available.

+

{$t('tools/dhcp-fingerprinting.lookup.noMatches.hint')}

{/if} {:else}
-

Search by Device or OS

+

{$t('tools/dhcp-fingerprinting.reverse.title')}

- + - Search the database by device name, OS, or vendor + {$t('tools/dhcp-fingerprinting.reverse.search.hint')}
{#if reverseResults.length > 0}
-

Found {reverseResults.length} Device{reverseResults.length > 1 ? 's' : ''}

+

+ {$t('tools/dhcp-fingerprinting.reverse.results.title', { + count: reverseResults.length, + plural: reverseResults.length > 1 ? 's' : '', + })} +

{#each reverseResults as device, i (i)}
@@ -405,37 +435,39 @@

{device.device}

{confidenceBadges[device.confidence] || ''} - {device.confidence} confidence + {$t('tools/dhcp-fingerprinting.reverse.results.confidence', { level: device.confidence })}
- OS: + {$t('tools/dhcp-fingerprinting.reverse.results.osLabel')} {device.os}
{#if device.description}
- Description: + {$t('tools/dhcp-fingerprinting.reverse.results.descriptionLabel')} {device.description}
{/if}
- Parameter Request List: + {$t('tools/dhcp-fingerprinting.reverse.results.parameterListLabel')} {formatParameterListDisplay(device.parameterRequestList)}
{#if device.vendorClassPattern}
- Vendor Class Pattern: + {$t('tools/dhcp-fingerprinting.reverse.results.vendorPatternLabel')} {device.vendorClassPattern}
{/if} @@ -445,8 +477,8 @@
{:else if reverseQuery.trim()}
-

No Devices Found

-

No devices matched "{reverseQuery}". Try a different search term.

+

{$t('tools/dhcp-fingerprinting.reverse.noResults.title')}

+

{$t('tools/dhcp-fingerprinting.reverse.noResults.description', { query: reverseQuery })}

{/if} {/if} diff --git a/src/lib/components/tools/DHCPOption119Builder.svelte b/src/lib/components/tools/DHCPOption119Builder.svelte index 7fdd3117..082ccba9 100644 --- a/src/lib/components/tools/DHCPOption119Builder.svelte +++ b/src/lib/components/tools/DHCPOption119Builder.svelte @@ -4,6 +4,7 @@ import ToolContentContainer from '$lib/components/global/ToolContentContainer.svelte'; import ExamplesCard from '$lib/components/common/ExamplesCard.svelte'; import { useClipboard } from '$lib/composables'; + import { t } from '$lib/stores/language'; import { buildOption119, parseOption119, @@ -13,10 +14,10 @@ type ParsedDomainSearch, } from '$lib/utils/dhcp-option119.js'; - const modeOptions = [ - { value: 'encode' as const, label: 'Encode', icon: 'wrench' }, - { value: 'decode' as const, label: 'Decode', icon: 'search' }, - ]; + const modeOptions = $derived([ + { value: 'encode' as const, label: $t('tools/dhcp-option119-builder.modes.encode'), icon: 'wrench' }, + { value: 'decode' as const, label: $t('tools/dhcp-option119-builder.modes.decode'), icon: 'search' }, + ]); let mode = $state<'encode' | 'decode'>('encode'); let config = $state({ @@ -49,41 +50,41 @@ description: string; } - const encodeExamples: EncodeExample[] = [ + const encodeExamples = $derived([ { - label: 'Corporate', + label: $t('tools/dhcp-option119-builder.encodeExamples.corporate.label'), domains: ['corp.example.com', 'example.com'], - description: 'Corporate network with domain compression', + description: $t('tools/dhcp-option119-builder.encodeExamples.corporate.description'), }, { - label: 'Multi-site', + label: $t('tools/dhcp-option119-builder.encodeExamples.multiSite.label'), domains: ['site1.example.com', 'site2.example.com', 'example.com'], - description: 'Multiple sites sharing common suffix', + description: $t('tools/dhcp-option119-builder.encodeExamples.multiSite.description'), }, { - label: 'Development', + label: $t('tools/dhcp-option119-builder.encodeExamples.development.label'), domains: ['dev.example.com', 'staging.example.com', 'example.com'], - description: 'Development environments', + description: $t('tools/dhcp-option119-builder.encodeExamples.development.description'), }, - ]; + ]); - const decodeExamples: DecodeExample[] = [ + const decodeExamples = $derived([ { - label: 'Corporate', + label: $t('tools/dhcp-option119-builder.decodeExamples.corporate.label'), hexInput: '04636f7270076578616d706c6503636f6d00c005', - description: 'corp.example.com, example.com (with compression)', + description: $t('tools/dhcp-option119-builder.decodeExamples.corporate.description'), }, { - label: 'Multi-site', + label: $t('tools/dhcp-option119-builder.decodeExamples.multiSite.label'), hexInput: '057369746531076578616d706c6503636f6d00057369746532c006c006', - description: 'site1.example.com, site2.example.com, example.com', + description: $t('tools/dhcp-option119-builder.decodeExamples.multiSite.description'), }, { - label: 'Single Domain', + label: $t('tools/dhcp-option119-builder.decodeExamples.singleDomain.label'), hexInput: '076578616d706c6503636f6d00', - description: 'example.com (no compression)', + description: $t('tools/dhcp-option119-builder.decodeExamples.singleDomain.description'), }, - ]; + ]); // Reactive generation - use untrack to prevent infinite loop $effect(() => { @@ -120,49 +121,49 @@ // Validate domains if (cfg.domains.length === 0) { - domainErrors.push('At least one domain is required'); + domainErrors.push($t('tools/dhcp-option119-builder.errors.atLeastOneDomain')); } for (let i = 0; i < cfg.domains.length; i++) { const domain = cfg.domains[i]; if (!domain.trim()) { - domainErrors.push(`Domain ${i + 1}: Value is required`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.domainRequired', { number: i + 1 })); continue; } if (!/^[a-zA-Z0-9.-]+$/.test(domain)) { - domainErrors.push(`Domain ${i + 1}: Invalid characters (use only letters, numbers, dots, hyphens)`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.invalidCharacters', { number: i + 1 })); continue; } if (domain.startsWith('.') || domain.endsWith('.')) { - domainErrors.push(`Domain ${i + 1}: Cannot start or end with a dot`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.cannotStartOrEndWithDot', { number: i + 1 })); continue; } if (domain.includes('..')) { - domainErrors.push(`Domain ${i + 1}: Cannot contain consecutive dots`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.consecutiveDots', { number: i + 1 })); continue; } if (domain.length > 253) { - domainErrors.push(`Domain ${i + 1}: Exceeds maximum length of 253 characters`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.exceedsMaxLength', { number: i + 1 })); continue; } const labels = domain.split('.'); for (const label of labels) { if (label.length === 0) { - domainErrors.push(`Domain ${i + 1}: Empty label found`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.emptyLabel', { number: i + 1 })); break; } if (label.length > 63) { - domainErrors.push(`Domain ${i + 1}: Label "${label}" exceeds maximum length of 63 characters`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.labelTooLong', { number: i + 1, label })); break; } if (label.startsWith('-') || label.endsWith('-')) { - domainErrors.push(`Domain ${i + 1}: Label "${label}" cannot start or end with hyphen`); + domainErrors.push($t('tools/dhcp-option119-builder.errors.labelInvalidHyphen', { number: i + 1, label })); break; } } @@ -173,19 +174,19 @@ const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; if (cfg.network.subnet && cfg.network.subnet.trim() && !ipv4Regex.test(cfg.network.subnet)) { - netErrors.push('Invalid subnet address'); + netErrors.push($t('tools/dhcp-option119-builder.errors.invalidSubnet')); } if (cfg.network.netmask && cfg.network.netmask.trim() && !ipv4Regex.test(cfg.network.netmask)) { - netErrors.push('Invalid netmask'); + netErrors.push($t('tools/dhcp-option119-builder.errors.invalidNetmask')); } if (cfg.network.rangeStart && cfg.network.rangeStart.trim() && !ipv4Regex.test(cfg.network.rangeStart)) { - netErrors.push('Invalid range start address'); + netErrors.push($t('tools/dhcp-option119-builder.errors.invalidRangeStart')); } if (cfg.network.rangeEnd && cfg.network.rangeEnd.trim() && !ipv4Regex.test(cfg.network.rangeEnd)) { - netErrors.push('Invalid range end address'); + netErrors.push($t('tools/dhcp-option119-builder.errors.invalidRangeEnd')); } } @@ -196,7 +197,9 @@ try { result = buildOption119(cfg); } catch (error) { - validationErrors = [error instanceof Error ? error.message : 'Encoding failed']; + validationErrors = [ + error instanceof Error ? error.message : $t('tools/dhcp-option119-builder.errors.encodingFailed'), + ]; result = null; } } else { @@ -212,7 +215,7 @@ } if (!/^[0-9a-fA-F\s:]+$/.test(decodeInput)) { - validationErrors = ['Invalid hex input: only hexadecimal characters allowed']; + validationErrors = [$t('tools/dhcp-option119-builder.errors.invalidHex')]; decodeResult = null; return; } @@ -221,7 +224,9 @@ validationErrors = []; decodeResult = parseOption119(decodeInput); } catch (error) { - validationErrors = [error instanceof Error ? error.message : 'Decoding failed']; + validationErrors = [ + error instanceof Error ? error.message : $t('tools/dhcp-option119-builder.errors.decodingFailed'), + ]; decodeResult = null; } } @@ -288,8 +293,8 @@ @@ -314,16 +319,18 @@ {#if mode === 'encode'}
-

Domain List

+

{$t('tools/dhcp-option119-builder.encode.domainListTitle')}

{#each config.domains as _, i (`domain-${i}`)}

- Domain {i + 1} + {$t('tools/dhcp-option119-builder.encode.domain.title', { + number: i + 1, + })}

- {#if config.domains.length > 1} @@ -334,21 +341,26 @@
- +
{/each}
{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option119-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -360,11 +372,11 @@ {#if result && validationErrors.length === 0}
-

Encoded Option 119

+

{$t('tools/dhcp-option119-builder.results.encodeTitle')}

-

Hex-Encoded (Compact)

+

{$t('tools/dhcp-option119-builder.results.hexEncoded.title')}

{result.hexEncoded}
@@ -380,7 +394,7 @@
-

Wire Format (Spaced)

+

{$t('tools/dhcp-option119-builder.results.wireFormat.title')}

{result.wireFormat}
-
Total Length: {result.totalLength} bytes
-
Domains: {result.domainList.length}
+
+ {$t('tools/dhcp-option119-builder.results.summary.totalLength')} + {$t('tools/dhcp-option119-builder.results.summary.bytes', { length: result.totalLength })} +
+
+ {$t('tools/dhcp-option119-builder.results.summary.domains')} + {result.domainList.length} +
{/if} @@ -406,25 +428,35 @@ {#if result}
-

Network Settings (Optional)

-

Customize network values for configuration examples below

+

{$t('tools/dhcp-option119-builder.encode.networkSettings.title')}

+

{$t('tools/dhcp-option119-builder.encode.networkSettings.help')}

- +
- +
@@ -432,24 +464,34 @@
- +
- +
{#if networkValidationErrors.length > 0}
-

Network Settings Errors

+

{$t('tools/dhcp-option119-builder.encode.networkSettings.errorsTitle')}

{#each networkValidationErrors as error, i (i)}
@@ -463,12 +505,12 @@ {#if result && networkValidationErrors.length === 0}
-

Configuration Examples

+

{$t('tools/dhcp-option119-builder.results.configExamplesTitle')}

{#if result.examples.iscDhcpd}
-

ISC dhcpd Configuration

+

{$t('tools/dhcp-option119-builder.results.formats.iscDhcpd')}

{result.examples.iscDhcpd}
@@ -486,7 +530,7 @@ {#if result.examples.keaDhcp4}
-

Kea DHCPv4 Configuration

+

{$t('tools/dhcp-option119-builder.results.formats.keaDhcp4')}

{result.examples.keaDhcp4}
@@ -505,31 +551,31 @@ {:else}
-

Decode Option 119 Hex

+

{$t('tools/dhcp-option119-builder.decode.title')}

{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option119-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -541,15 +587,21 @@ {#if decodeResult && validationErrors.length === 0}
-

Decoded Domain Search List

+

{$t('tools/dhcp-option119-builder.results.decodeTitle')}

-
Total Length: {decodeResult.totalLength} bytes
-
Domains Found: {decodeResult.domains.length}
+
+ {$t('tools/dhcp-option119-builder.results.summary.totalLength')} + {$t('tools/dhcp-option119-builder.results.summary.bytes', { length: decodeResult.totalLength })} +
+
+ {$t('tools/dhcp-option119-builder.results.domainsFound')} + {decodeResult.domains.length} +
-

Domain List

+

{$t('tools/dhcp-option119-builder.results.domainListTitle')}

{#each decodeResult.domains as domain, i (i)}
diff --git a/src/lib/components/tools/DHCPOption121Builder.svelte b/src/lib/components/tools/DHCPOption121Builder.svelte index f1bedc3c..8e877bdc 100644 --- a/src/lib/components/tools/DHCPOption121Builder.svelte +++ b/src/lib/components/tools/DHCPOption121Builder.svelte @@ -4,6 +4,7 @@ import ToolContentContainer from '$lib/components/global/ToolContentContainer.svelte'; import ExamplesCard from '$lib/components/common/ExamplesCard.svelte'; import { useClipboard } from '$lib/composables'; + import { t } from '$lib/stores/language'; import { buildOption121, parseOption121, @@ -13,10 +14,10 @@ type ParsedClasslessRoutes, } from '$lib/utils/dhcp-option121.js'; - const modeOptions = [ - { value: 'encode' as const, label: 'Encode', icon: 'wrench' }, - { value: 'decode' as const, label: 'Decode', icon: 'search' }, - ]; + const modeOptions = $derived([ + { value: 'encode' as const, label: $t('tools/dhcp-option121-builder.modes.encode'), icon: 'wrench' }, + { value: 'decode' as const, label: $t('tools/dhcp-option121-builder.modes.decode'), icon: 'search' }, + ]); let mode = $state<'encode' | 'decode'>('encode'); let config = $state({ @@ -49,51 +50,51 @@ description: string; } - const encodeExamples: EncodeExample[] = [ + const encodeExamples = $derived([ { - label: 'Private Networks', + label: $t('tools/dhcp-option121-builder.encodeExamples.privateNetworks.label'), routes: [ { destination: '10.0.0.0/8', gateway: '192.168.1.1' }, { destination: '172.16.0.0/12', gateway: '192.168.1.1' }, ], - description: 'Routes to RFC 1918 private networks', + description: $t('tools/dhcp-option121-builder.encodeExamples.privateNetworks.description'), }, { - label: 'Default + Specific', + label: $t('tools/dhcp-option121-builder.encodeExamples.defaultSpecific.label'), routes: [ { destination: '0.0.0.0/0', gateway: '192.168.1.1' }, { destination: '10.10.0.0/16', gateway: '192.168.1.254' }, ], - description: 'Default route with specific override', + description: $t('tools/dhcp-option121-builder.encodeExamples.defaultSpecific.description'), }, { - label: 'Multi-site VPN', + label: $t('tools/dhcp-option121-builder.encodeExamples.multiSiteVPN.label'), routes: [ { destination: '10.1.0.0/16', gateway: '192.168.1.10' }, { destination: '10.2.0.0/16', gateway: '192.168.1.20' }, { destination: '10.3.0.0/16', gateway: '192.168.1.30' }, ], - description: 'Multiple VPN site routes', + description: $t('tools/dhcp-option121-builder.encodeExamples.multiSiteVPN.description'), }, - ]; + ]); - const decodeExamples: DecodeExample[] = [ + const decodeExamples = $derived([ { - label: 'Private Networks', + label: $t('tools/dhcp-option121-builder.decodeExamples.privateNetworks.label'), hexInput: '080ac0a801010cac10c0a80101', - description: '10.0.0.0/8 and 172.16.0.0/12 via 192.168.1.1', + description: $t('tools/dhcp-option121-builder.decodeExamples.privateNetworks.description'), }, { - label: 'Default Route', + label: $t('tools/dhcp-option121-builder.decodeExamples.defaultRoute.label'), hexInput: '00c0a80101', - description: '0.0.0.0/0 via 192.168.1.1', + description: $t('tools/dhcp-option121-builder.decodeExamples.defaultRoute.description'), }, { - label: 'Specific /24', + label: $t('tools/dhcp-option121-builder.decodeExamples.specific24.label'), hexInput: '18c0a80ac0a80101', - description: '192.168.10.0/24 via 192.168.1.1', + description: $t('tools/dhcp-option121-builder.decodeExamples.specific24.description'), }, - ]; + ]); // Reactive generation - use untrack to prevent infinite loop $effect(() => { @@ -130,26 +131,26 @@ // Validate routes if (cfg.routes.length === 0) { - routeErrors.push('At least one route is required'); + routeErrors.push($t('tools/dhcp-option121-builder.errors.atLeastOneRoute')); } for (let i = 0; i < cfg.routes.length; i++) { const route = cfg.routes[i]; if (!route.destination.trim()) { - routeErrors.push(`Route ${i + 1}: Destination is required`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.destinationRequired', { number: i + 1 })); continue; } if (!route.gateway.trim()) { - routeErrors.push(`Route ${i + 1}: Gateway is required`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.gatewayRequired', { number: i + 1 })); continue; } // Validate CIDR format const cidrMatch = route.destination.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/); if (!cidrMatch) { - routeErrors.push(`Route ${i + 1}: Invalid CIDR notation (use format: x.x.x.x/y)`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.invalidCIDR', { number: i + 1 })); continue; } @@ -157,32 +158,32 @@ const prefixLen = parseInt(prefixLenStr, 10); if (prefixLen < 0 || prefixLen > 32) { - routeErrors.push(`Route ${i + 1}: Prefix length must be 0-32`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.invalidPrefixLength', { number: i + 1 })); continue; } // Validate IPv4 address in CIDR const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; if (!ipv4Regex.test(prefix)) { - routeErrors.push(`Route ${i + 1}: Invalid IPv4 address in destination`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.invalidIPv4Dest', { number: i + 1 })); continue; } const octets = prefix.split('.').map((o) => parseInt(o, 10)); if (octets.some((o) => o > 255)) { - routeErrors.push(`Route ${i + 1}: Invalid IPv4 address (octets must be 0-255)`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.invalidIPv4Octets', { number: i + 1 })); continue; } // Validate gateway if (!ipv4Regex.test(route.gateway)) { - routeErrors.push(`Route ${i + 1}: Invalid gateway IPv4 address`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.invalidGateway', { number: i + 1 })); continue; } const gwOctets = route.gateway.split('.').map((o) => parseInt(o, 10)); if (gwOctets.some((o) => o > 255)) { - routeErrors.push(`Route ${i + 1}: Invalid gateway address (octets must be 0-255)`); + routeErrors.push($t('tools/dhcp-option121-builder.errors.invalidGatewayOctets', { number: i + 1 })); continue; } } @@ -192,19 +193,19 @@ const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; if (cfg.network.subnet && cfg.network.subnet.trim() && !ipv4Regex.test(cfg.network.subnet)) { - netErrors.push('Invalid subnet address'); + netErrors.push($t('tools/dhcp-option121-builder.errors.invalidSubnet')); } if (cfg.network.netmask && cfg.network.netmask.trim() && !ipv4Regex.test(cfg.network.netmask)) { - netErrors.push('Invalid netmask'); + netErrors.push($t('tools/dhcp-option121-builder.errors.invalidNetmask')); } if (cfg.network.rangeStart && cfg.network.rangeStart.trim() && !ipv4Regex.test(cfg.network.rangeStart)) { - netErrors.push('Invalid range start address'); + netErrors.push($t('tools/dhcp-option121-builder.errors.invalidRangeStart')); } if (cfg.network.rangeEnd && cfg.network.rangeEnd.trim() && !ipv4Regex.test(cfg.network.rangeEnd)) { - netErrors.push('Invalid range end address'); + netErrors.push($t('tools/dhcp-option121-builder.errors.invalidRangeEnd')); } } @@ -215,7 +216,9 @@ try { result = buildOption121(cfg); } catch (error) { - validationErrors = [error instanceof Error ? error.message : 'Encoding failed']; + validationErrors = [ + error instanceof Error ? error.message : $t('tools/dhcp-option121-builder.errors.encodingFailed'), + ]; result = null; } } else { @@ -231,7 +234,7 @@ } if (!/^[0-9a-fA-F\s:]+$/.test(decodeInput)) { - validationErrors = ['Invalid hex input: only hexadecimal characters allowed']; + validationErrors = [$t('tools/dhcp-option121-builder.errors.invalidHex')]; decodeResult = null; return; } @@ -240,7 +243,9 @@ validationErrors = []; decodeResult = parseOption121(decodeInput); } catch (error) { - validationErrors = [error instanceof Error ? error.message : 'Decoding failed']; + validationErrors = [ + error instanceof Error ? error.message : $t('tools/dhcp-option121-builder.errors.decodingFailed'), + ]; decodeResult = null; } } @@ -310,8 +315,8 @@ @@ -336,14 +341,16 @@ {#if mode === 'encode'}
-

Static Routes

+

{$t('tools/dhcp-option121-builder.encode.staticRoutesTitle')}

{#each config.routes as _, i (`route-${i}`)}

- Route {i + 1} + {$t('tools/dhcp-option121-builder.encode.route.title', { + number: i + 1, + })}

{#if config.routes.length > 1}
@@ -379,14 +391,14 @@
{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option121-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -398,11 +410,11 @@ {#if result && validationErrors.length === 0}
-

Encoded Option 121/249

+

{$t('tools/dhcp-option121-builder.results.encodeTitle')}

-

Hex-Encoded (Compact)

+

{$t('tools/dhcp-option121-builder.results.hexEncoded.title')}

{result.hexEncoded}
@@ -418,7 +432,7 @@
-

Wire Format (Spaced)

+

{$t('tools/dhcp-option121-builder.results.wireFormat.title')}

{result.wireFormat}
-
Total Length: {result.totalLength} bytes
-
Routes: {result.routes.length}
+
+ {$t('tools/dhcp-option121-builder.results.summary.totalLength')} + {$t('tools/dhcp-option121-builder.results.summary.bytes', { length: result.totalLength })} +
+
{$t('tools/dhcp-option121-builder.results.summary.routes')} {result.routes.length}
{/if} @@ -444,25 +463,35 @@ {#if result}
-

Network Settings (Optional)

-

Customize network values for configuration examples below

+

{$t('tools/dhcp-option121-builder.encode.networkSettings.title')}

+

{$t('tools/dhcp-option121-builder.encode.networkSettings.help')}

- +
- +
@@ -470,24 +499,34 @@
- +
- +
{#if networkValidationErrors.length > 0}
-

Network Settings Errors

+

{$t('tools/dhcp-option121-builder.encode.networkSettings.errorsTitle')}

{#each networkValidationErrors as error, i (i)}
@@ -501,12 +540,12 @@ {#if result && networkValidationErrors.length === 0}
-

Configuration Examples

+

{$t('tools/dhcp-option121-builder.results.configExamplesTitle')}

{#if result.examples.iscDhcpd}
-

ISC dhcpd Configuration (Option 121)

+

{$t('tools/dhcp-option121-builder.results.formats.iscDhcpd')}

{result.examples.iscDhcpd}
@@ -524,7 +565,7 @@ {#if result.examples.keaDhcp4}
-

Kea DHCPv4 Configuration

+

{$t('tools/dhcp-option121-builder.results.formats.keaDhcp4')}

{result.examples.keaDhcp4}
@@ -542,7 +585,7 @@ {#if result.examples.msftOption249}
-

Microsoft Option 249 Configuration

+

{$t('tools/dhcp-option121-builder.results.formats.msftOption249')}

{result.examples.msftOption249}
@@ -561,31 +606,31 @@ {:else}
-

Decode Option 121/249 Hex

+

{$t('tools/dhcp-option121-builder.decode.title')}

{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option121-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -597,25 +642,31 @@ {#if decodeResult && validationErrors.length === 0}
-

Decoded Classless Static Routes

+

{$t('tools/dhcp-option121-builder.results.decodeTitle')}

-
Total Length: {decodeResult.totalLength} bytes
-
Routes Found: {decodeResult.routes.length}
+
+ {$t('tools/dhcp-option121-builder.results.summary.totalLength')} + {$t('tools/dhcp-option121-builder.results.summary.bytes', { length: decodeResult.totalLength })} +
+
+ {$t('tools/dhcp-option121-builder.results.routesFound')} + {decodeResult.routes.length} +
-

Route List

+

{$t('tools/dhcp-option121-builder.results.routeListTitle')}

{#each decodeResult.routes as route, i (i)}
- Destination: + {$t('tools/dhcp-option121-builder.results.destination')} {route.destination}
- Gateway: + {$t('tools/dhcp-option121-builder.results.gateway')} {route.gateway}
diff --git a/src/lib/components/tools/DHCPOption150Builder.svelte b/src/lib/components/tools/DHCPOption150Builder.svelte index 4d868fa0..808d639c 100644 --- a/src/lib/components/tools/DHCPOption150Builder.svelte +++ b/src/lib/components/tools/DHCPOption150Builder.svelte @@ -4,6 +4,7 @@ import ToolContentContainer from '$lib/components/global/ToolContentContainer.svelte'; import ExamplesCard from '$lib/components/common/ExamplesCard.svelte'; import { useClipboard } from '$lib/composables'; + import { t } from '$lib/stores/language'; import { buildTFTPOptions, parseOption150, @@ -16,10 +17,10 @@ type ParsedStringOption, } from '$lib/utils/dhcp-option150.js'; - const modeOptions = [ - { value: 'encode' as const, label: 'Encode', icon: 'wrench' }, - { value: 'decode' as const, label: 'Decode', icon: 'search' }, - ]; + const modeOptions = $derived([ + { value: 'encode' as const, label: $t('tools/dhcp-option150-builder.modes.encode'), icon: 'wrench' }, + { value: 'decode' as const, label: $t('tools/dhcp-option150-builder.modes.decode'), icon: 'search' }, + ]); let mode = $state<'encode' | 'decode'>('encode'); let config = $state({ @@ -58,58 +59,58 @@ description: string; } - const encodeExamples: EncodeExample[] = [ + const encodeExamples: EncodeExample[] = $derived([ { - label: 'Cisco IP Phones', + label: $t('tools/dhcp-option150-builder.encodeExamples.ciscoIPPhones.label'), option150Servers: ['192.168.1.10', '192.168.1.11'], - description: 'Redundant TFTP servers for Cisco IP phone configuration', + description: $t('tools/dhcp-option150-builder.encodeExamples.ciscoIPPhones.description'), }, { - label: 'PXE Boot (Standard)', + label: $t('tools/dhcp-option150-builder.encodeExamples.pxeBootStandard.label'), option66Server: 'pxe.example.com', option67Bootfile: 'pxelinux.0', - description: 'Standard PXE boot with single TFTP server', + description: $t('tools/dhcp-option150-builder.encodeExamples.pxeBootStandard.description'), }, { - label: 'PXE Boot (UEFI)', + label: $t('tools/dhcp-option150-builder.encodeExamples.pxeBootUEFI.label'), option66Server: '192.168.1.10', option67Bootfile: 'bootx64.efi', - description: 'UEFI PXE boot configuration', + description: $t('tools/dhcp-option150-builder.encodeExamples.pxeBootUEFI.description'), }, { - label: 'Combined (Option 150 + 67)', + label: $t('tools/dhcp-option150-builder.encodeExamples.combined.label'), option150Servers: ['192.168.1.10', '192.168.1.11'], option67Bootfile: 'SEP{MAC}.cnf.xml', - description: 'Cisco phones with redundant TFTP and config template', + description: $t('tools/dhcp-option150-builder.encodeExamples.combined.description'), }, - ]; + ]); - const decodeExamples: DecodeExample[] = [ + const decodeExamples: DecodeExample[] = $derived([ { - label: 'Option 150: Dual TFTP', + label: $t('tools/dhcp-option150-builder.decodeExamples.option150DualTFTP.label'), mode: 'option150', hexInput: 'c0a8010ac0a8010b', - description: '192.168.1.10 and 192.168.1.11', + description: $t('tools/dhcp-option150-builder.decodeExamples.option150DualTFTP.description'), }, { - label: 'Option 66: Hostname', + label: $t('tools/dhcp-option150-builder.decodeExamples.option66Hostname.label'), mode: 'option66', hexInput: '7078652e6578616d706c652e636f6d', - description: 'pxe.example.com', + description: $t('tools/dhcp-option150-builder.decodeExamples.option66Hostname.description'), }, { - label: 'Option 67: PXE Boot', + label: $t('tools/dhcp-option150-builder.decodeExamples.option67PXEBoot.label'), mode: 'option67', hexInput: '7078656c696e75782e30', - description: 'pxelinux.0', + description: $t('tools/dhcp-option150-builder.decodeExamples.option67PXEBoot.description'), }, { - label: 'Option 67: UEFI Boot', + label: $t('tools/dhcp-option150-builder.decodeExamples.option67UEFIBoot.label'), mode: 'option67', hexInput: '626f6f747836 42e656669', - description: 'bootx64.efi', + description: $t('tools/dhcp-option150-builder.decodeExamples.option67UEFIBoot.description'), }, - ]; + ]); // Reactive generation $effect(() => { @@ -345,8 +346,8 @@ @@ -371,8 +372,8 @@ {#if mode === 'encode'}
-

Option 150: Cisco TFTP Server List

-

Multiple IPv4 addresses for redundant TFTP servers (Cisco IP phones)

+

{$t('tools/dhcp-option150-builder.encode.option150.title')}

+

{$t('tools/dhcp-option150-builder.encode.option150.helpText')}

{#if config.option150Servers && config.option150Servers.length > 0} @@ -381,13 +382,13 @@
-

Option 66: TFTP Server Name (Standard)

-

Single hostname or IP address for standard PXE boot

+

{$t('tools/dhcp-option150-builder.encode.option66.title')}

+

{$t('tools/dhcp-option150-builder.encode.option66.helpText')}

@@ -427,23 +428,28 @@
-

Option 67: Bootfile Name

-

Filename to boot from TFTP server (e.g., pxelinux.0 for BIOS, bootx64.efi for UEFI)

+

{$t('tools/dhcp-option150-builder.encode.option67.title')}

+

{$t('tools/dhcp-option150-builder.encode.option67.helpText')}

- +
{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option150-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -456,11 +462,11 @@ {#if result && validationErrors.length === 0} {#if result.option150}
-

Option 150: TFTP Server List

+

{$t('tools/dhcp-option150-builder.results.option150.title')}

-

Hex-Encoded (Compact)

+

{$t('tools/dhcp-option150-builder.results.option150.hexEncodedTitle')}

{result.option150.hexEncoded}
@@ -476,7 +484,7 @@
-

Wire Format (Spaced)

+

{$t('tools/dhcp-option150-builder.results.option150.wireFormatTitle')}

{result.option150.wireFormat}
-
Total Length: {result.option150.totalLength} bytes
-
Servers: {result.option150.servers.length}
+
+ {$t('tools/dhcp-option150-builder.results.option150.totalLength')} + {$t('tools/dhcp-option150-builder.results.option150.bytes', { length: result.option150.totalLength })} +
+
+ {$t('tools/dhcp-option150-builder.results.option150.servers')} + {$t('tools/dhcp-option150-builder.results.option150.serverCount', { + count: result.option150.servers.length, + })} +
{/if} {#if result.option66}
-

Option 66: TFTP Server Name

+

{$t('tools/dhcp-option150-builder.results.option66.title')}

-

Value

+

{$t('tools/dhcp-option150-builder.results.option66.valueTitle')}

{result.option66.value}
@@ -519,7 +539,7 @@
-

Hex-Encoded

+

{$t('tools/dhcp-option150-builder.results.option66.hexEncodedTitle')}

{result.option66.hexEncoded}
-
Total Length: {result.option66.totalLength} bytes
+
+ {$t('tools/dhcp-option150-builder.results.option66.totalLength')} + {$t('tools/dhcp-option150-builder.results.option66.bytes', { length: result.option66.totalLength })} +
{/if} {#if result.option67}
-

Option 67: Bootfile Name

+

{$t('tools/dhcp-option150-builder.results.option67.title')}

-

Value

+

{$t('tools/dhcp-option150-builder.results.option67.valueTitle')}

{result.option67.value}
@@ -561,7 +588,7 @@
-

Hex-Encoded

+

{$t('tools/dhcp-option150-builder.results.option67.hexEncodedTitle')}

{result.option67.hexEncoded}
-
Total Length: {result.option67.totalLength} bytes
+
+ {$t('tools/dhcp-option150-builder.results.option67.totalLength')} + {$t('tools/dhcp-option150-builder.results.option67.bytes', { length: result.option67.totalLength })} +
{/if} @@ -587,25 +619,35 @@ {#if result}
-

Network Settings (Optional)

-

Customize network values for configuration examples below

+

{$t('tools/dhcp-option150-builder.encode.networkSettings.title')}

+

{$t('tools/dhcp-option150-builder.encode.networkSettings.helpText')}

- +
- +
@@ -613,24 +655,34 @@
- +
- +
{#if networkValidationErrors.length > 0}
-

Network Settings Errors

+

{$t('tools/dhcp-option150-builder.encode.networkSettings.errorsTitle')}

{#each networkValidationErrors as error, i (i)}
@@ -644,12 +696,12 @@ {#if result && networkValidationErrors.length === 0}
-

Configuration Examples

+

{$t('tools/dhcp-option150-builder.results.configExamples.title')}

{#if result.examples.iscDhcpd}
-

ISC dhcpd Configuration

+

{$t('tools/dhcp-option150-builder.results.configExamples.iscDhcpd')}

{result.examples.iscDhcpd}
@@ -667,7 +721,7 @@ {#if result.examples.keaDhcp4}
-

Kea DHCPv4 Configuration

+

{$t('tools/dhcp-option150-builder.results.configExamples.keaDhcp4')}

{result.examples.keaDhcp4}
@@ -685,7 +741,7 @@ {#if result.examples.ciscoIos}
-

Cisco IOS Configuration

+

{$t('tools/dhcp-option150-builder.results.configExamples.ciscoIos')}

{result.examples.ciscoIos}
@@ -704,43 +762,43 @@ {:else}
-

Decode TFTP Option

+

{$t('tools/dhcp-option150-builder.decode.title')}

{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option150-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -752,19 +810,29 @@ {#if decodeResult150 && validationErrors.length === 0}
-

Decoded Option 150: TFTP Server List

+

{$t('tools/dhcp-option150-builder.decodeResults.option150.title')}

-
Total Length: {decodeResult150.totalLength} bytes
-
Servers Found: {decodeResult150.servers.length}
+
+ {$t('tools/dhcp-option150-builder.decodeResults.option150.totalLength')} + {$t('tools/dhcp-option150-builder.decodeResults.option150.bytes', { length: decodeResult150.totalLength })} +
+
+ {$t('tools/dhcp-option150-builder.decodeResults.option150.serversFound')} + {$t('tools/dhcp-option150-builder.decodeResults.option150.serverCount', { + count: decodeResult150.servers.length, + })} +
-

TFTP Servers

+

{$t('tools/dhcp-option150-builder.decodeResults.option150.serversTitle')}

{#each decodeResult150.servers as server, i (i)}
- Server {i + 1}: + {$t('tools/dhcp-option150-builder.decodeResults.option150.serverLabel', { number: i + 1 })} {server}
{/each} @@ -774,14 +842,17 @@ {#if decodeResult66 && validationErrors.length === 0}
-

Decoded Option 66: TFTP Server Name

+

{$t('tools/dhcp-option150-builder.decodeResults.option66.title')}

-
Total Length: {decodeResult66.totalLength} bytes
+
+ {$t('tools/dhcp-option150-builder.decodeResults.option66.totalLength')} + {$t('tools/dhcp-option150-builder.decodeResults.option66.bytes', { length: decodeResult66.totalLength })} +
-

TFTP Server

+

{$t('tools/dhcp-option150-builder.decodeResults.option66.serverTitle')}

{decodeResult66.value} @@ -792,14 +863,17 @@ {#if decodeResult67 && validationErrors.length === 0}
-

Decoded Option 67: Bootfile Name

+

{$t('tools/dhcp-option150-builder.decodeResults.option67.title')}

-
Total Length: {decodeResult67.totalLength} bytes
+
+ {$t('tools/dhcp-option150-builder.decodeResults.option67.totalLength')} + {$t('tools/dhcp-option150-builder.decodeResults.option67.bytes', { length: decodeResult67.totalLength })} +
-

Bootfile

+

{$t('tools/dhcp-option150-builder.decodeResults.option67.bootfileTitle')}

{decodeResult67.value} diff --git a/src/lib/components/tools/DHCPOption43Generator.svelte b/src/lib/components/tools/DHCPOption43Generator.svelte index e47e2263..981caa3a 100644 --- a/src/lib/components/tools/DHCPOption43Generator.svelte +++ b/src/lib/components/tools/DHCPOption43Generator.svelte @@ -10,6 +10,7 @@ import { useClipboard, useExamples } from '$lib/composables'; import Icon from '$lib/components/global/Icon.svelte'; import ExamplesCard from '$lib/components/common/ExamplesCard.svelte'; + import { t } from '$lib/stores/language'; let vendorType = $state('cisco-catalyst'); let ipInput = $state(''); @@ -23,32 +24,32 @@ { vendor: 'cisco-catalyst' as VendorType, ips: '192.168.1.10\n192.168.1.11', - description: 'Cisco Catalyst with dual controllers', + description: $t('tools/dhcp-option43-generator.examples.ciscoCatalyst'), }, { vendor: 'cisco-meraki' as VendorType, ips: '192.168.10.5', - description: 'Single Meraki cloud controller', + description: $t('tools/dhcp-option43-generator.examples.ciscoMeraki'), }, { vendor: 'ruckus-smartzone' as VendorType, ips: '10.0.0.100', - description: 'Ruckus SmartZone controller', + description: $t('tools/dhcp-option43-generator.examples.ruckusSmartzone'), }, { vendor: 'aruba' as VendorType, ips: '172.16.1.50', - description: 'Aruba wireless controller', + description: $t('tools/dhcp-option43-generator.examples.aruba'), }, { vendor: 'unifi' as VendorType, ips: '192.168.1.20', - description: 'UniFi Network Controller', + description: $t('tools/dhcp-option43-generator.examples.unifi'), }, { vendor: 'ruckus-zonedirector' as VendorType, ips: '10.50.100.200', - description: 'Ruckus ZoneDirector (legacy)', + description: $t('tools/dhcp-option43-generator.examples.ruckusZonedirector'), }, ]; @@ -61,22 +62,24 @@ const ips = parseIPList(ipInput); if (ips.length === 0) { - errors = ['Please enter at least one IP address']; + errors = [$t('tools/dhcp-option43-generator.errors.noIPs')]; return; } // Validate each IP const invalidIPs = ips.filter((ip) => !isValidIPv4(ip)); if (invalidIPs.length > 0) { - errors = [`Invalid IP address format: ${invalidIPs.join(', ')}`]; + errors = [$t('tools/dhcp-option43-generator.errors.invalidIP', { ips: invalidIPs.join(', ') })]; return; } // Check max IPs for vendor if (ips.length > vendorInfo.maxIPs) { - errors = [ - `${vendorInfo.name} supports maximum ${vendorInfo.maxIPs} controller${vendorInfo.maxIPs > 1 ? 's' : ''}. You entered ${ips.length}.`, - ]; + const errorKey = + vendorInfo.maxIPs > 1 + ? 'tools/dhcp-option43-generator.errors.maxExceededPlural' + : 'tools/dhcp-option43-generator.errors.maxExceeded'; + errors = [$t(errorKey, { vendor: vendorInfo.name, max: vendorInfo.maxIPs, count: ips.length })]; return; } @@ -102,20 +105,20 @@ onSelect={loadExample} getLabel={(ex) => VENDOR_INFO[ex.vendor].name} getDescription={(ex) => ex.description} - getTooltip={(ex) => `Generate Option 43 for ${VENDOR_INFO[ex.vendor].name}`} + getTooltip={(ex) => $t('tools/dhcp-option43-generator.examples.tooltip', { vendor: VENDOR_INFO[ex.vendor].name })} />
-

Generator Configuration

+

{$t('tools/dhcp-option43-generator.input.title')}

@@ -171,14 +178,14 @@ {#if result}
-

Generated Option 43 Values

+

{$t('tools/dhcp-option43-generator.results.title')}

{#if result.iosCommand && result.workings}

- {result.commandLabel || 'DHCP Server Command'} + {result.commandLabel || $t('tools/dhcp-option43-generator.results.commandSection.defaultLabel')}

{result.iosCommand}
-
How this value is calculated:
+
{$t('tools/dhcp-option43-generator.results.commandSection.calculationTitle')}
    {#each result.workings as working, i (i)}
  • {working}
  • @@ -211,7 +220,7 @@
    -

    Hexadecimal String

    +

    {$t('tools/dhcp-option43-generator.results.formats.hex.title')}

    {result.hex} -

    Raw hexadecimal - used in most DHCP server configurations

    +

    {$t('tools/dhcp-option43-generator.results.formats.hex.hint')}

    -

    Colon-Separated Hex

    +

    {$t('tools/dhcp-option43-generator.results.formats.colonHex.title')}

    {result.colonHex} -

    Used by Infoblox and some network appliances

    +

    {$t('tools/dhcp-option43-generator.results.formats.colonHex.hint')}

    -

    Windows DHCP Binary

    +

    {$t('tools/dhcp-option43-generator.results.formats.windowsBinary.title')}

    {result.windowsBinary} -

    Enter in Windows DHCP Server's Binary field for Option 43

    +

    {$t('tools/dhcp-option43-generator.results.formats.windowsBinary.hint')}

    -

    ISC DHCP Configuration

    +

    {$t('tools/dhcp-option43-generator.results.formats.iscDhcp.title')}

    {result.iscDhcp}
    -

    Add to dhcpd.conf for ISC DHCP server

    +

    {$t('tools/dhcp-option43-generator.results.formats.iscDhcp.hint')}

    -

    Mikrotik Configuration

    +

    {$t('tools/dhcp-option43-generator.results.formats.mikrotik.title')}

    {result.mikrotik} -

    RouterOS DHCP option configuration command

    +

    {$t('tools/dhcp-option43-generator.results.formats.mikrotik.hint')}

-

Important Notes

+

{$t('tools/dhcp-option43-generator.notes.title')}

    -
  • - DHCP Option 43 is vendor-specific and must match the AP manufacturer's expected format -
  • -
  • - Some vendors require Option 60 (Vendor Class Identifier) to be set in addition to Option 43 -
  • -
  • Ensure controller IPs are reachable from the AP management network
  • -
  • - For high availability, configure multiple controller IPs when supported -
  • -
  • Changes to DHCP options require AP to renew lease or reboot to take effect
  • -
  • Always test in a controlled environment before deploying to production networks
  • +
  • {$t('tools/dhcp-option43-generator.notes.list.vendorSpecific')}
  • +
  • {$t('tools/dhcp-option43-generator.notes.list.option60')}
  • +
  • {$t('tools/dhcp-option43-generator.notes.list.reachability')}
  • +
  • {$t('tools/dhcp-option43-generator.notes.list.highAvailability')}
  • +
  • {$t('tools/dhcp-option43-generator.notes.list.leaseRenewal')}
  • +
  • {$t('tools/dhcp-option43-generator.notes.list.testing')}
{/if} @@ -630,10 +643,6 @@ li { line-height: 1.6; color: var(--text-primary); - - strong { - color: var(--color-primary); - } } } } diff --git a/src/lib/components/tools/DHCPOption60Builder.svelte b/src/lib/components/tools/DHCPOption60Builder.svelte index f983dcb5..a62ad2fc 100644 --- a/src/lib/components/tools/DHCPOption60Builder.svelte +++ b/src/lib/components/tools/DHCPOption60Builder.svelte @@ -11,6 +11,7 @@ } from '$lib/utils/dhcp-option60.js'; import { useClipboard, useExamples } from '$lib/composables'; import ExamplesCard from '$lib/components/common/ExamplesCard.svelte'; + import { t } from '$lib/stores/language'; let selectedPreset = $state('cisco-phone'); let customValue = $state(''); @@ -23,38 +24,38 @@ const clipboard = useClipboard(); const examplesList = [ - { preset: 'cisco-phone' as VendorPreset, description: 'Cisco IP Phones with TFTP' }, - { preset: 'cisco-ap' as VendorPreset, description: 'Cisco APs with Option 43' }, - { preset: 'pxe-client' as VendorPreset, description: 'PXE network boot' }, - { preset: 'aruba-ap' as VendorPreset, description: 'Aruba wireless APs' }, + { preset: 'cisco-phone' as VendorPreset, description: $t('tools/dhcp-option60-builder.examples.ciscoPhone') }, + { preset: 'cisco-ap' as VendorPreset, description: $t('tools/dhcp-option60-builder.examples.ciscoAp') }, + { preset: 'pxe-client' as VendorPreset, description: $t('tools/dhcp-option60-builder.examples.pxeClient') }, + { preset: 'aruba-ap' as VendorPreset, description: $t('tools/dhcp-option60-builder.examples.arubaAp') }, ]; const examples = useExamples(examplesList); - const importantNotes = [ - 'Option 60 (Vendor Class Identifier) allows DHCP servers to provide different configurations based on client type', - 'Class-based policies enable separate IP pools and options for different device types', - 'Wireless APs typically require both Option 60 and Option 43 for controller discovery', - 'Test configurations in a lab environment before deploying to production networks', - 'Adjust subnet addresses, pool ranges, and option values to match your network design', - ]; + const importantNotes = $derived([ + $t('tools/dhcp-option60-builder.notes.list.vendorClass'), + $t('tools/dhcp-option60-builder.notes.list.classPolicies'), + $t('tools/dhcp-option60-builder.notes.list.option43'), + $t('tools/dhcp-option60-builder.notes.list.testing'), + $t('tools/dhcp-option60-builder.notes.list.customize'), + ]); const poolFields = $derived([ { id: 'poolStart', icon: 'arrow-right', - label: 'Pool Start', - help: 'First IP in matching pool', - placeholder: '192.168.10.100', + label: $t('tools/dhcp-option60-builder.input.poolStart.label'), + help: $t('tools/dhcp-option60-builder.input.poolStart.help'), + placeholder: $t('tools/dhcp-option60-builder.input.poolStart.placeholder'), bind: () => networkConfig.poolStart, set: (v: string) => (networkConfig.poolStart = v), }, { id: 'poolEnd', icon: 'arrow-left', - label: 'Pool End', - help: 'Last IP in matching pool', - placeholder: '192.168.10.200', + label: $t('tools/dhcp-option60-builder.input.poolEnd.label'), + help: $t('tools/dhcp-option60-builder.input.poolEnd.help'), + placeholder: $t('tools/dhcp-option60-builder.input.poolEnd.placeholder'), bind: () => networkConfig.poolEnd, set: (v: string) => (networkConfig.poolEnd = v), }, @@ -64,18 +65,18 @@ { id: 'nonMatchingPoolStart', icon: 'arrow-right', - label: 'Non-Matching Pool Start', - help: 'First IP for non-matching clients', - placeholder: '192.168.10.50', + label: $t('tools/dhcp-option60-builder.input.nonMatchingPoolStart.label'), + help: $t('tools/dhcp-option60-builder.input.nonMatchingPoolStart.help'), + placeholder: $t('tools/dhcp-option60-builder.input.nonMatchingPoolStart.placeholder'), bind: () => networkConfig.nonMatchingPoolStart, set: (v: string) => (networkConfig.nonMatchingPoolStart = v), }, { id: 'nonMatchingPoolEnd', icon: 'arrow-left', - label: 'Non-Matching Pool End', - help: 'Last IP for non-matching clients', - placeholder: '192.168.10.99', + label: $t('tools/dhcp-option60-builder.input.nonMatchingPoolEnd.label'), + help: $t('tools/dhcp-option60-builder.input.nonMatchingPoolEnd.help'), + placeholder: $t('tools/dhcp-option60-builder.input.nonMatchingPoolEnd.placeholder'), bind: () => networkConfig.nonMatchingPoolEnd, set: (v: string) => (networkConfig.nonMatchingPoolEnd = v), }, @@ -131,43 +132,43 @@ // Validate subnet if (!isValidCIDR(networkConfig.subnet)) { - validationErrors.push('Invalid subnet CIDR notation (e.g., 192.168.10.0/24)'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidSubnet')); } // Validate pool IPs if (!isValidIPv4(networkConfig.poolStart)) { - validationErrors.push('Invalid pool start IP address'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidPoolStart')); } if (!isValidIPv4(networkConfig.poolEnd)) { - validationErrors.push('Invalid pool end IP address'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidPoolEnd')); } // Validate non-matching pool if provided if (networkConfig.nonMatchingPoolStart && !isValidIPv4(networkConfig.nonMatchingPoolStart)) { - validationErrors.push('Invalid non-matching pool start IP address'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidNonMatchingPoolStart')); } if (networkConfig.nonMatchingPoolEnd && !isValidIPv4(networkConfig.nonMatchingPoolEnd)) { - validationErrors.push('Invalid non-matching pool end IP address'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidNonMatchingPoolEnd')); } // Validate server IP if needed and provided if (needsServerIp && networkConfig.serverIp && !isValidIPv4(networkConfig.serverIp)) { - validationErrors.push('Invalid server IP address'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidServerIp')); } // Validate boot filename if needed and provided if (needsBootFilename && networkConfig.bootFilename && !isValidFilename(networkConfig.bootFilename)) { - validationErrors.push('Invalid boot filename'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidBootFilename')); } // Validate MikroTik server name if (networkConfig.mikrotikServerName && networkConfig.mikrotikServerName.trim().length === 0) { - validationErrors.push('MikroTik server name cannot be empty'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidMikrotikServerName')); } // Validate lease time format (basic check) if (networkConfig.leaseTime && !/^\d+[smhd]$/.test(networkConfig.leaseTime.trim())) { - validationErrors.push('Invalid lease time format (e.g., 24h, 1h, 30m)'); + validationErrors.push($t('tools/dhcp-option60-builder.errors.invalidLeaseTime')); } return validationErrors; @@ -181,11 +182,11 @@ // Validate custom input if custom preset if (selectedPreset === 'custom') { if (!customValue.trim()) { - errors = ['Custom vendor class identifier is required']; + errors = [$t('tools/dhcp-option60-builder.errors.customRequired')]; return; } if (!isValidVendorClass(customValue)) { - errors = ['Invalid vendor class identifier. Must be 1-255 printable ASCII characters.']; + errors = [$t('tools/dhcp-option60-builder.errors.invalidVendorClass')]; return; } } @@ -199,7 +200,7 @@ result = generateOption60(selectedPreset, customValue || undefined, networkConfig); } catch (err: unknown) { - errors = [err instanceof Error ? err.message : 'Failed to generate configuration']; + errors = [err instanceof Error ? err.message : $t('tools/dhcp-option60-builder.errors.failedToGenerate')]; } } @@ -219,20 +220,20 @@ onSelect={loadExample} getLabel={(ex) => VENDOR_PRESETS[ex.preset].name} getDescription={(ex) => ex.description} - getTooltip={(ex) => `Generate config for ${VENDOR_PRESETS[ex.preset].name}`} + getTooltip={(ex) => $t('tools/dhcp-option60-builder.examples.tooltip', { vendor: VENDOR_PRESETS[ex.preset].name })} />
-

Vendor Class Configuration

+

{$t('tools/dhcp-option60-builder.input.title')}

- 1-255 printable ASCII characters + + {$t('tools/dhcp-option60-builder.input.custom.help')}
{/if}
-

Advanced Options

+

{$t('tools/dhcp-option60-builder.input.advancedOptions')}

- - Network address in CIDR notation + + {$t('tools/dhcp-option60-builder.input.subnet.help')}
@@ -321,14 +333,12 @@
- IP address of the provisioning server + {$t('tools/dhcp-option60-builder.input.serverIp.help')}
{/if} @@ -337,19 +347,21 @@
- Name of the configuration or boot file + {$t('tools/dhcp-option60-builder.input.bootFilename.help')}
{/if} @@ -357,25 +369,30 @@
- Name of DHCP server in MikroTik config + {$t('tools/dhcp-option60-builder.input.mikrotikServerName.help')}
- - DHCP lease time (e.g., 24h, 1h, 30m) + + {$t('tools/dhcp-option60-builder.input.leaseTime.help')}
@@ -397,14 +414,14 @@ {#if result}
-

Generated Configurations

+

{$t('tools/dhcp-option60-builder.results.title')}

- Vendor Class Identifier (Option 60) + {$t('tools/dhcp-option60-builder.results.vendorClass.title')}

{result.vendorClass}
-

Use Case: {result.useCase}

+

{$t('tools/dhcp-option60-builder.results.vendorClass.useCase')} {result.useCase}

- {#each [{ id: 'isc', title: 'ISC DHCP Server', content: result.iscDhcpConfig, hint: 'Add to /etc/dhcp/dhcpd.conf' }, { id: 'kea', title: 'Kea DHCP Server', content: result.keaConfig, hint: 'Add to Kea configuration JSON' }, { id: 'windows', title: 'Windows DHCP Server', content: result.windowsConfig, hint: 'Run PowerShell commands as Administrator' }, { id: 'dnsmasq', title: 'dnsmasq', content: result.dnsmasqConfig, hint: 'Add to /etc/dnsmasq.conf' }, { id: 'mikrotik', title: 'MikroTik RouterOS', content: result.mikrotikConfig, hint: 'RouterOS CLI commands' }] as config (config.id)} + {#each [{ id: 'isc', title: $t('tools/dhcp-option60-builder.results.formats.isc.title'), content: result.iscDhcpConfig, hint: $t('tools/dhcp-option60-builder.results.formats.isc.hint') }, { id: 'kea', title: $t('tools/dhcp-option60-builder.results.formats.kea.title'), content: result.keaConfig, hint: $t('tools/dhcp-option60-builder.results.formats.kea.hint') }, { id: 'windows', title: $t('tools/dhcp-option60-builder.results.formats.windows.title'), content: result.windowsConfig, hint: $t('tools/dhcp-option60-builder.results.formats.windows.hint') }, { id: 'dnsmasq', title: $t('tools/dhcp-option60-builder.results.formats.dnsmasq.title'), content: result.dnsmasqConfig, hint: $t('tools/dhcp-option60-builder.results.formats.dnsmasq.hint') }, { id: 'mikrotik', title: $t('tools/dhcp-option60-builder.results.formats.mikrotik.title'), content: result.mikrotikConfig, hint: $t('tools/dhcp-option60-builder.results.formats.mikrotik.hint') }] as config (config.id)}

{config.title}

@@ -437,7 +456,9 @@ onclick={() => clipboard.copy(config.content, config.id)} > - {clipboard.isCopied(config.id) ? 'Copied' : 'Copy'} + {clipboard.isCopied(config.id) + ? $t('tools/dhcp-option60-builder.buttons.copied') + : $t('tools/dhcp-option60-builder.buttons.copy')}
{config.content}
@@ -448,11 +469,10 @@
-

Important Notes

+

{$t('tools/dhcp-option60-builder.notes.title')}

    {#each importantNotes as note (note)} - -
  • {@html note}
  • +
  • {note}
  • {/each}
diff --git a/src/lib/components/tools/DHCPOption82Builder.svelte b/src/lib/components/tools/DHCPOption82Builder.svelte index 217352e5..56bd0e24 100644 --- a/src/lib/components/tools/DHCPOption82Builder.svelte +++ b/src/lib/components/tools/DHCPOption82Builder.svelte @@ -4,6 +4,7 @@ import ToolContentContainer from '$lib/components/global/ToolContentContainer.svelte'; import ExamplesCard from '$lib/components/common/ExamplesCard.svelte'; import { useClipboard } from '$lib/composables'; + import { t } from '$lib/stores/language'; import { buildOption82, parseOption82, @@ -15,10 +16,10 @@ type EncodingFormat, } from '$lib/utils/dhcp-option82.js'; - const modeOptions = [ - { value: 'build' as const, label: 'Build', icon: 'wrench' }, - { value: 'parse' as const, label: 'Parse', icon: 'search' }, - ]; + const modeOptions = $derived([ + { value: 'build' as const, label: $t('tools/dhcp-option82-builder.modes.build'), icon: 'wrench' }, + { value: 'parse' as const, label: $t('tools/dhcp-option82-builder.modes.parse'), icon: 'search' }, + ]); let mode = $state<'build' | 'parse'>('build'); let config = $state(getDefaultOption82Config()); @@ -30,12 +31,12 @@ const clipboard = useClipboard(); - const formatOptions: Array<{ value: EncodingFormat; label: string }> = [ - { value: 'ascii', label: 'ASCII Text' }, - { value: 'hex', label: 'Hexadecimal' }, - { value: 'vlan-id', label: 'VLAN ID' }, - { value: 'hostname-port', label: 'Hostname:Port' }, - ]; + const formatOptions = $derived>([ + { value: 'ascii', label: $t('tools/dhcp-option82-builder.formats.ascii') }, + { value: 'hex', label: $t('tools/dhcp-option82-builder.formats.hex') }, + { value: 'vlan-id', label: $t('tools/dhcp-option82-builder.formats.vlanId') }, + { value: 'hostname-port', label: $t('tools/dhcp-option82-builder.formats.hostnamePort') }, + ]); interface BuildExample { label: string; @@ -51,68 +52,68 @@ description: string; } - const buildExamples: BuildExample[] = [ + const buildExamples = $derived([ { - label: 'VLAN 100', + label: $t('tools/dhcp-option82-builder.buildExamples.vlan100.label'), type: 'circuit-id', format: 'vlan-id', value: '100', - description: 'Circuit-ID as VLAN ID 100', + description: $t('tools/dhcp-option82-builder.buildExamples.vlan100.description'), }, { - label: 'Switch Port', + label: $t('tools/dhcp-option82-builder.buildExamples.switchPort.label'), type: 'circuit-id', format: 'hostname-port', value: 'sw1:Gi0/1', - description: 'Circuit-ID as hostname:port', + description: $t('tools/dhcp-option82-builder.buildExamples.switchPort.description'), }, { - label: 'Custom Circuit', + label: $t('tools/dhcp-option82-builder.buildExamples.customCircuit.label'), type: 'circuit-id', format: 'ascii', value: 'building-a-floor-3', - description: 'Circuit-ID as custom ASCII text', + description: $t('tools/dhcp-option82-builder.buildExamples.customCircuit.description'), }, { - label: 'Switch Hostname', + label: $t('tools/dhcp-option82-builder.buildExamples.switchHostname.label'), type: 'remote-id', format: 'ascii', value: 'relay-sw1.example.com', - description: 'Remote-ID as hostname', + description: $t('tools/dhcp-option82-builder.buildExamples.switchHostname.description'), }, { - label: 'MAC Address', + label: $t('tools/dhcp-option82-builder.buildExamples.macAddress.label'), type: 'remote-id', format: 'hex', value: '001122334455', - description: 'Remote-ID as MAC address', + description: $t('tools/dhcp-option82-builder.buildExamples.macAddress.description'), }, { - label: 'Agent ID', + label: $t('tools/dhcp-option82-builder.buildExamples.agentId.label'), type: 'remote-id', format: 'ascii', value: 'DHCP-RELAY-01', - description: 'Remote-ID as relay agent identifier', + description: $t('tools/dhcp-option82-builder.buildExamples.agentId.description'), }, - ]; + ]); - const parseExamples: ParseExample[] = [ + const parseExamples = $derived([ { - label: 'VLAN + Hostname', + label: $t('tools/dhcp-option82-builder.parseExamples.vlanHostname.label'), hexInput: '01020064020c7377312e6578616d706c65', - description: 'Circuit-ID (VLAN 100) + Remote-ID (sw1.example)', + description: $t('tools/dhcp-option82-builder.parseExamples.vlanHostname.description'), }, { - label: 'Switch Port', + label: $t('tools/dhcp-option82-builder.parseExamples.switchPort.label'), hexInput: '01094769302f31020c7377312e6578616d706c65', - description: 'Circuit-ID (Gi0/1) + Remote-ID (sw1.example)', + description: $t('tools/dhcp-option82-builder.parseExamples.switchPort.description'), }, { - label: 'MAC Address', + label: $t('tools/dhcp-option82-builder.parseExamples.macAddress.label'), hexInput: '0206001122334455', - description: 'Remote-ID as MAC address (00:11:22:33:44:55)', + description: $t('tools/dhcp-option82-builder.parseExamples.macAddress.description'), }, - ]; + ]); // Reactive generation - use untrack to prevent infinite loop $effect(() => { @@ -153,20 +154,20 @@ const sub = cfg.suboptions[i]; if (!sub.value.trim()) { - errors.push(`Suboption ${i + 1}: Value is required`); + errors.push($t('tools/dhcp-option82-builder.errors.valueRequired', { number: i + 1 })); continue; } if (sub.format === 'vlan-id') { const vlan = parseInt(sub.value, 10); if (isNaN(vlan) || vlan < 0 || vlan > 4095) { - errors.push(`Suboption ${i + 1}: VLAN ID must be between 0 and 4095`); + errors.push($t('tools/dhcp-option82-builder.errors.vlanRange', { number: i + 1 })); } } if (sub.format === 'hex') { if (!/^[0-9a-fA-F:]+$/.test(sub.value.replace(/\s/g, ''))) { - errors.push(`Suboption ${i + 1}: Invalid hex format`); + errors.push($t('tools/dhcp-option82-builder.errors.invalidHex', { number: i + 1 })); } } } @@ -188,7 +189,7 @@ } if (!/^[0-9a-fA-F\s:]+$/.test(parseInput)) { - validationErrors = ['Invalid hex input: only hexadecimal characters allowed']; + validationErrors = [$t('tools/dhcp-option82-builder.errors.invalidHexInput')]; parseResult = null; return; } @@ -267,8 +268,8 @@ @@ -293,13 +294,13 @@ {#if mode === 'build'}
-

Configuration

+

{$t('tools/dhcp-option82-builder.build.configurationTitle')}

{#each config.suboptions as suboption, i (`sub-${i}-${suboption.type}`)}
-

Suboption {i + 1}

+

{$t('tools/dhcp-option82-builder.build.suboption.title', { number: i + 1 })}

{#if config.suboptions.length > 1}
@@ -353,14 +354,14 @@
{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option82-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -372,11 +373,11 @@ {#if result && validationErrors.length === 0}
-

Generated Option 82

+

{$t('tools/dhcp-option82-builder.build.results.title')}

-

Hex-Encoded Value

+

{$t('tools/dhcp-option82-builder.build.results.hexEncoded')}

{result.hexEncoded}
-

Breakdown

+

{$t('tools/dhcp-option82-builder.build.results.breakdown')}

{#each result.breakdown as breakdown, i (i)}
- {breakdown.type} (Code {breakdown.typeCode}) - Length: {breakdown.length} bytes + {$t('tools/dhcp-option82-builder.build.results.typeCode', { + type: breakdown.type, + code: breakdown.typeCode, + })} + {$t('tools/dhcp-option82-builder.build.results.length', { length: breakdown.length })}

{breakdown.description}

-
Value: {breakdown.value}
-
Hex: {breakdown.hexValue}
+
+ {$t('tools/dhcp-option82-builder.build.results.valueLabel')} + {breakdown.value} +
+
+ {$t('tools/dhcp-option82-builder.build.results.hexLabel')} + {breakdown.hexValue} +
{/each} @@ -410,7 +426,7 @@ {#if result.examples.iscDhcpd}
-

ISC dhcpd Configuration Example

+

{$t('tools/dhcp-option82-builder.build.results.examples.iscDhcpd')}

{result.examples.iscDhcpd}
@@ -428,7 +446,7 @@ {#if result.examples.keaDhcp4}
-

Kea DHCPv4 Configuration Example

+

{$t('tools/dhcp-option82-builder.build.results.examples.keaDhcp4')}

{result.examples.keaDhcp4}
@@ -446,7 +466,7 @@ {#if result.examples.ciscoRelay}
-

Cisco Relay Agent Example

+

{$t('tools/dhcp-option82-builder.build.results.examples.ciscoRelay')}

{result.examples.ciscoRelay}
@@ -465,31 +487,31 @@ {:else}
-

Parse Option 82 Hex

+

{$t('tools/dhcp-option82-builder.parse.title')}

{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcp-option82-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -501,25 +523,44 @@ {#if parseResult && validationErrors.length === 0}
-

Parsed Option 82

+

{$t('tools/dhcp-option82-builder.parse.results.title')}

-
Total Length: {parseResult.totalLength} bytes
-
Suboptions Found: {parseResult.suboptions.length}
+
+ {$t('tools/dhcp-option82-builder.parse.results.totalLength')} + {$t('tools/dhcp-option82-builder.parse.results.bytes', { length: parseResult.totalLength })} +
+
+ {$t('tools/dhcp-option82-builder.parse.results.suboptionsFound')} + {$t('tools/dhcp-option82-builder.parse.results.count', { count: parseResult.suboptions.length })} +
-

Suboptions

+

{$t('tools/dhcp-option82-builder.parse.results.suboptionsTitle')}

{#each parseResult.suboptions as suboption, i (i)}
- {suboption.type} (Code {suboption.typeCode}) - Length: {suboption.length} bytes + {$t('tools/dhcp-option82-builder.parse.results.typeCode', { + type: suboption.type, + code: suboption.typeCode, + })} + {$t('tools/dhcp-option82-builder.parse.results.length', { length: suboption.length })}

{suboption.description}

-
Decoded Value: {suboption.value}
-
Hex Value: {suboption.hexValue}
+
+ {$t('tools/dhcp-option82-builder.parse.results.decodedValue')} + {suboption.value} +
+
+ {$t('tools/dhcp-option82-builder.parse.results.hexValue')} + {suboption.hexValue} +
{/each} diff --git a/src/lib/components/tools/DHCPSnippetsGenerator.svelte b/src/lib/components/tools/DHCPSnippetsGenerator.svelte index d461c559..26afb37e 100644 --- a/src/lib/components/tools/DHCPSnippetsGenerator.svelte +++ b/src/lib/components/tools/DHCPSnippetsGenerator.svelte @@ -1,6 +1,7 @@
-

Option 23: DNS Recursive Name Servers

-

IPv6 addresses of DNS servers for client name resolution

+

{$t('tools/dhcpv6-dns-builder.option23.title')}

+

{$t('tools/dhcpv6-dns-builder.option23.helpText')}

{#each config.dnsServers as _, i (`dns-${i}`)} @@ -172,13 +173,13 @@
@@ -195,15 +196,15 @@
-

Option 24: Domain Search List

-

DNS search domains for hostname resolution

+

{$t('tools/dhcpv6-dns-builder.option24.title')}

+

{$t('tools/dhcpv6-dns-builder.option24.helpText')}

{#each config.searchDomains as _, i (`domain-${i}`)} @@ -211,16 +212,21 @@
- +
@@ -229,14 +235,14 @@
{#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcpv6-dns-builder.errors.title')}

{#each validationErrors as error, i (i)}
@@ -249,19 +255,25 @@ {#if result && validationErrors.length === 0} {#if result.option23}
-

Option 23: DNS Recursive Name Servers

+

{$t('tools/dhcpv6-dns-builder.results.option23Title')}

-
Total Length: {result.option23.totalLength} bytes
-
Servers: {result.option23.servers.length}
+
+ {$t('tools/dhcpv6-dns-builder.results.totalLength')} + {$t('tools/dhcpv6-dns-builder.results.lengthBytes', { length: result.option23.totalLength })} +
+
+ {$t('tools/dhcpv6-dns-builder.results.servers')} + {$t('tools/dhcpv6-dns-builder.results.serversCount', { count: result.option23.servers.length })} +
-

DNS Servers

+

{$t('tools/dhcpv6-dns-builder.results.dnsServersHeading')}

{#each result.option23.servers as server, i (i)}
- Server {i + 1}: + {$t('tools/dhcpv6-dns-builder.results.serverLabel', { number: i + 1 })} {server}
{/each} @@ -269,7 +281,7 @@
-

Hex-Encoded (Compact)

+

{$t('tools/dhcpv6-dns-builder.results.hexEncodedTitle')}

{result.option23.hexEncoded}
@@ -285,7 +299,7 @@
-

Wire Format (Spaced)

+

{$t('tools/dhcpv6-dns-builder.results.wireFormatTitle')}

{result.option23.wireFormat}
@@ -303,19 +319,25 @@ {#if result.option24}
-

Option 24: Domain Search List

+

{$t('tools/dhcpv6-dns-builder.results.option24Title')}

-
Total Length: {result.option24.totalLength} bytes
-
Domains: {result.option24.domains.length}
+
+ {$t('tools/dhcpv6-dns-builder.results.totalLength')} + {$t('tools/dhcpv6-dns-builder.results.lengthBytes', { length: result.option24.totalLength })} +
+
+ {$t('tools/dhcpv6-dns-builder.results.domains')} + {$t('tools/dhcpv6-dns-builder.results.domainsCount', { count: result.option24.domains.length })} +
-

Search Domains

+

{$t('tools/dhcpv6-dns-builder.results.searchDomainsHeading')}

{#each result.option24.domains as domain, i (i)}
- Domain {i + 1}: + {$t('tools/dhcpv6-dns-builder.results.domainLabel', { number: i + 1 })} {domain}
{/each} @@ -323,7 +345,7 @@
-

Hex-Encoded (Compact)

+

{$t('tools/dhcpv6-dns-builder.results.hexEncodedTitle')}

{result.option24.hexEncoded}
@@ -339,7 +363,7 @@
-

Wire Format (Spaced)

+

{$t('tools/dhcpv6-dns-builder.results.wireFormatTitle')}

{result.option24.wireFormat}
@@ -355,7 +381,7 @@ {#if result.option24.breakdown.length > 0}
-

Domain Encoding Breakdown

+

{$t('tools/dhcpv6-dns-builder.results.breakdownTitle')}

{#each result.option24.breakdown as item, i (i)}
{item.domain}
@@ -369,11 +395,11 @@ {#if result.examples.keaDhcp6}
-

Configuration Example

+

{$t('tools/dhcpv6-dns-builder.results.configExampleTitle')}

-

Kea DHCPv6 Configuration

+

{$t('tools/dhcpv6-dns-builder.results.keaDhcpv6Title')}

{result.examples.keaDhcp6}
@@ -390,23 +418,22 @@ {/if}
-

About RFC 3646

+

{$t('tools/dhcpv6-dns-builder.about.title')}

- RFC 3646 defines DNS configuration options for DHCPv6, allowing IPv6 clients to automatically discover DNS - servers and search domains. + {$t('tools/dhcpv6-dns-builder.about.intro')}

  • - Option 23: DNS Recursive Name Server - List of IPv6 DNS server addresses (16 bytes each) + Option 23: + {$t('tools/dhcpv6-dns-builder.about.option23Description')}
  • - Option 24: Domain Search List - DNS search domains encoded in DNS wire format (length-prefixed - labels) + Option 24: + {$t('tools/dhcpv6-dns-builder.about.option24Description')}

- These options are essential for IPv6 network autoconfiguration, enabling clients to resolve hostnames without - manual DNS configuration. + {$t('tools/dhcpv6-dns-builder.about.conclusion')}

{/if} diff --git a/src/lib/components/tools/DHCPv6FQDN.svelte b/src/lib/components/tools/DHCPv6FQDN.svelte index b2e1ba24..5f24b66e 100644 --- a/src/lib/components/tools/DHCPv6FQDN.svelte +++ b/src/lib/components/tools/DHCPv6FQDN.svelte @@ -4,6 +4,7 @@ import ToolContentContainer from '$lib/components/global/ToolContentContainer.svelte'; import ExamplesCard from '$lib/components/common/ExamplesCard.svelte'; import { useClipboard } from '$lib/composables'; + import { t } from '$lib/stores/language'; import { type FQDNConfig, type FQDNResult, @@ -92,10 +93,7 @@ }); - +
-

FQDN Configuration

-

Fully Qualified Domain Name for the DHCPv6 client

+

{$t('tools/dhcpv6-fqdn.configuration.fqdnTitle')}

+

{$t('tools/dhcpv6-fqdn.configuration.fqdnHelpText')}

- +
-

DNS Update Flags

-

Control how DNS updates are performed

+

{$t('tools/dhcpv6-fqdn.configuration.flagsTitle')}

+

{$t('tools/dhcpv6-fqdn.configuration.flagsHelpText')}

@@ -131,8 +134,8 @@
@@ -142,8 +145,8 @@
@@ -153,8 +156,8 @@
@@ -163,7 +166,7 @@ {#if validationErrors.length > 0}
-

Validation Errors

+

{$t('tools/dhcpv6-fqdn.errors.title')}

{#each validationErrors as error, i (i)}
@@ -175,35 +178,50 @@ {#if result && validationErrors.length === 0}
-

Option 39: Client FQDN

+

{$t('tools/dhcpv6-fqdn.results.title')}

-
FQDN: {result.fqdn}
-
Total Length: {result.totalLength} bytes
+
{$t('tools/dhcpv6-fqdn.results.fqdn')} {result.fqdn}
+
+ {$t('tools/dhcpv6-fqdn.results.totalLength')} + {$t('tools/dhcpv6-fqdn.results.lengthBytes', { length: result.totalLength })} +
-

Flags Breakdown

+

{$t('tools/dhcpv6-fqdn.results.flagsBreakdown')}

- S Flag - {result.flags.S ? 'Set' : 'Not Set'} + {$t('tools/dhcpv6-fqdn.results.sFlag')} + {result.flags.S + ? $t('tools/dhcpv6-fqdn.results.flagSet') + : $t('tools/dhcpv6-fqdn.results.flagNotSet')}
- O Flag - {result.flags.O ? 'Set' : 'Not Set'} + {$t('tools/dhcpv6-fqdn.results.oFlag')} + {result.flags.O + ? $t('tools/dhcpv6-fqdn.results.flagSet') + : $t('tools/dhcpv6-fqdn.results.flagNotSet')}
- N Flag - {result.flags.N ? 'Set' : 'Not Set'} + {$t('tools/dhcpv6-fqdn.results.nFlag')} + {result.flags.N + ? $t('tools/dhcpv6-fqdn.results.flagSet') + : $t('tools/dhcpv6-fqdn.results.flagNotSet')}
@@ -219,7 +237,7 @@
-

Flags Byte

+

{$t('tools/dhcpv6-fqdn.results.flagsByte')}

{result.flags.flagsByte}
@@ -236,7 +256,7 @@
-

Hex-Encoded (Compact)

+

{$t('tools/dhcpv6-fqdn.results.hexEncoded')}

{result.hexEncoded}
@@ -252,7 +272,7 @@
-

Wire Format (Spaced)

+

{$t('tools/dhcpv6-fqdn.results.wireFormat')}

{result.wireFormat}
-

Encoding Breakdown

+

{$t('tools/dhcpv6-fqdn.results.encodingBreakdown')}

- Flags: + {$t('tools/dhcpv6-fqdn.results.flags')} {result.breakdown.flags}
- FQDN: + {$t('tools/dhcpv6-fqdn.results.fqdnLabel')} {result.breakdown.fqdn}
@@ -283,11 +303,11 @@ {#if result.examples.keaDhcp6}
-

Configuration Example

+

{$t('tools/dhcpv6-fqdn.results.configExample')}

-

Kea DHCPv6 Configuration

+

{$t('tools/dhcpv6-fqdn.results.keaDhcpv6')}

{result.examples.keaDhcp6}
@@ -304,25 +326,26 @@ {/if}
-

About RFC 4704

+

{$t('tools/dhcpv6-fqdn.about.title')}

- RFC 4704 defines the Client FQDN Option for DHCPv6, enabling clients and servers to negotiate dynamic DNS - updates for IPv6 addresses. + {$t('tools/dhcpv6-fqdn.about.intro')}

  • - S Flag (Bit 0): Server should perform DNS updates + S Flag (Bit 0): + {$t('tools/dhcpv6-fqdn.about.sFlagDescription')}
  • - O Flag (Bit 1): Server can override client preferences + O Flag (Bit 1): + {$t('tools/dhcpv6-fqdn.about.oFlagDescription')}
  • - N Flag (Bit 2): Client requests server to perform updates (client will NOT update) + N Flag (Bit 2): + {$t('tools/dhcpv6-fqdn.about.nFlagDescription')}

- The FQDN is encoded using DNS wire format with length-prefixed labels, enabling automated hostname registration - in DNS for IPv6 networks. + {$t('tools/dhcpv6-fqdn.about.conclusion')}

{/if} diff --git a/src/lib/components/tools/DKIMKeyGenerator.svelte b/src/lib/components/tools/DKIMKeyGenerator.svelte index 5c1dc1fa..f0cca3d6 100644 --- a/src/lib/components/tools/DKIMKeyGenerator.svelte +++ b/src/lib/components/tools/DKIMKeyGenerator.svelte @@ -1,6 +1,7 @@
-

DMARC Policy Builder

+

{$t('tools/dmarc-builder.title')}

- Create DMARC policies with alignment options, reporting addresses, and failure handling configuration. + {$t('tools/dmarc-builder.description')}

@@ -258,13 +251,20 @@

- Domain Configuration + {$t('tools/dmarc-builder.domain.title')}

- - + +
@@ -272,19 +272,19 @@

- Policy Configuration + {$t('tools/dmarc-builder.policy.title')}

-