Skip to content

Commit 8fe100a

Browse files
BornToBeRootclaude
andauthored
Feature: RDAP improvements (#3642)
* Feature: RDAP auto query type and validation of inherited host - New query type "Auto" (default) that detects the type from the query. - The inherited host of a profile is validated against the query type. - Numeric TLDs (e.g. "8.8.8.8" as domain) and entity handles without a service provider tag are rejected. - Watermark per query type in the profile dialog (shared converter). - Imported profiles use the type "Auto". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: RDAP bootstrap files are repaired and retried, null items in responses - A bootstrap file that has not been downloaded yet is tried again with every query instead of being blocked for an hour after a failure. - A cached bootstrap file that can't be parsed is downloaded again (unconditional request) with the same query. - Null items in response arrays (links, entities, events, ...) are skipped instead of failing the whole query. - Docs: retry behaviour and the fixed timeout of the bootstrap files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: RDAP cancel, localized timeout and copy context menu - A running query can be canceled (button switches to Cancel, Esc) and is stopped when the tab is closed. - Timeouts have their own error kind (next server is tried) and a localized message instead of a hard-coded English text. - Copy context menu for the events, nameservers, DNSSEC and contacts grids (single row), Export for multiple rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: RDAP nameserver lookups and networks/AS numbers of an entity - New query type "Nameserver" (RFC 9082 3.1.4), resolved via the registry of the nameserver's TLD. IP addresses of a nameserver are shown in the details. - Networks and AS numbers of an entity (RFC 9083 5.1, e.g. sent by RIPE, APNIC and ARIN for an organization) are shown in two new tables with copy context menu and included in the text export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: RDAP referral behind a proxy, private address check for NAT64/6to4 - If the referral goes through the system proxy, the host name is not resolved locally (the proxy resolves it), so referrals work in networks where only the proxy resolves public names. IP addresses in the URL are still checked. - The private address check also covers IPv6 addresses that contain an IPv4 address (NAT64 64:ff9b::/96, 6to4 2002::/16), the rest of 64:ff9b::/32 (local-use NAT64), 192.0.0.0/24 and 198.18.0.0/15. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: Query IP addresses in RDAP from IP Scanner and Traceroute New context menu item "RDAP" in IP Scanner (result) and Traceroute (hop) that opens a new RDAP tab with the IP address. Disabled for hops that timed out (no IP address). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Feature: RDAP submenu (IP address, hostname, ASN) and parent domain lookup - IP Scanner and Traceroute: "RDAP..." submenu like "DNS Lookup..." with IP address and hostname; Traceroute also with the AS number of the IP geolocation. Entries without data are disabled. - If a domain is not found (e.g. a hostname like www.borntoberoot.net), its parent domain is queried (up to 3 levels), since registries only know registered domains. The status shows which domain is displayed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: RDAP parent domain on 400, hostname without domain, docs - Parent domain lookup also on 400 Bad Request (PIR, Identity Digital, SWITCH and auDA answer a subdomain with 400, not 404). - IP Scanner: RDAP > Hostname only for hostnames with a domain (not for NetBIOS names like "PC-01"). - Text export: the handle of a network is left out if it is the range. - Docs: only RIPE organizations can be queried with their networks; AS numbers only from Traceroute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: RDAP registered domain via public suffix list, review fixes - A hostname is queried as its registered domain, determined with the public suffix list (ICANN section, MPL-2.0, generated by Scripts/Create-PublicSuffixListFromWeb.ps1). Replaces the parent domain lookup, which stopped after 3 levels and could show a public suffix like co.uk (a registry object) for an unregistered domain. - A missing bootstrap file is retried after 1 minute instead of with every query; bootstrap files are parsed once per query. - Tabs opened from other tools or the run command use the type Auto. - Traceroute: RDAP > Hostname only for hostnames with a domain. - Public suffix list listed in About > Resources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix: RDAP bootstrap retry, errors for the registered domain, referral check with proxy - The cached bootstrap files are read once per session and again only if a file changed, so a corrupt dns.json with an unreachable IANA is retried with the regular wait time instead of with every query. - Error messages and "Open in Whois" use the registered domain that was queried (e.g. foo.com for www.foo.com). - The referral target is always checked locally; only if it can't be resolved locally and a proxy is used, it is followed without the check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: Add PR #3642 to changelog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 138eb70 commit 8fe100a

37 files changed

Lines changed: 8151 additions & 101 deletions

‎AGENTS.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ REPOSITORY_ROOT/
4141
│ ├── Create-FlagFromSVG.ps1 # Create flag images
4242
│ ├── Create-OUIListFromWeb.ps1 # Update OUI (MAC vendor) list
4343
│ ├── Create-PortListFromWeb.ps1 # Update port list database
44+
│ ├── Create-PublicSuffixListFromWeb.ps1 # Update public suffix list (RDAP: registered domain of a hostname)
4445
│ ├── Create-ReleaseTable.ps1 # Create the SHA256 sums / file table for release notes
4546
│ ├── Create-WhoisServerListFromWebAndWhois.ps1 # Update WHOIS servers
4647
│ ├── Create-WorldMapFromWeb.ps1 # Create the world map (Traceroute map)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Filepath in the resources
2+
[string]$OutFilePath = Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath "Source\NETworkManager.Models\Resources\PublicSuffixList.dat"
3+
4+
# Download the public suffix list (MPL 2.0) from publicsuffix.org
5+
$List = (Invoke-WebRequest -Uri "https://publicsuffix.org/list/public_suffix_list.dat").Content -split "[`r`n]+"
6+
7+
# Keep the license notice, version and commit of the list
8+
$Header = $List | Where-Object { $_ -match "^// (This Source Code Form|License, v\. 2\.0|file, You can obtain|VERSION:|COMMIT:)" }
9+
10+
# Only the ICANN section is used, since the private section (e.g. github.io) doesn't contain registered domains
11+
$IsICANN = $false
12+
$IdnMapping = New-Object System.Globalization.IdnMapping
13+
14+
$Rules = foreach ($Line in $List) {
15+
if ($Line -eq "// ===BEGIN ICANN DOMAINS===") {
16+
$IsICANN = $true
17+
continue
18+
}
19+
20+
if ($Line -eq "// ===END ICANN DOMAINS===") {
21+
break
22+
}
23+
24+
$Rule = $Line.Trim()
25+
26+
if (-not $IsICANN -or [string]::IsNullOrEmpty($Rule) -or $Rule.StartsWith("//")) {
27+
continue
28+
}
29+
30+
# Rules are converted to A-labels (punycode), like the queries. Wildcard (*.) and exception (!) prefixes are kept.
31+
$Prefix = if ($Rule.StartsWith("*.")) { "*." } elseif ($Rule.StartsWith("!")) { "!" } else { "" }
32+
33+
$Prefix + $IdnMapping.GetAscii($Rule.Substring($Prefix.Length)).ToLowerInvariant()
34+
}
35+
36+
$Description = @"
37+
$($Header -join "`n")
38+
//
39+
// ICANN section of the public suffix list from https://publicsuffix.org/list/public_suffix_list.dat, with the
40+
// rules converted to A-labels (punycode).
41+
// Generated $(Get-Date) by Scripts/Create-PublicSuffixListFromWeb.ps1
42+
"@
43+
44+
Set-Content -Path $OutFilePath -Value (@($Description) + $Rules) -Encoding utf8NoBOM
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
5+
namespace NETworkManager.Converters;
6+
7+
/// <summary>
8+
/// Convert a hostname to <see cref="bool" />: true if it has a domain (e.g. "server01.borntoberoot.net"), false for
9+
/// a single label like a NetBIOS name ("SERVER01").
10+
/// </summary>
11+
public sealed class HostnameHasDomainToBooleanConverter : IValueConverter
12+
{
13+
/// <summary>
14+
/// Convert a hostname to <see cref="bool" />.
15+
/// </summary>
16+
/// <param name="value">Hostname as <see cref="string" />.</param>
17+
/// <param name="targetType"></param>
18+
/// <param name="parameter"></param>
19+
/// <param name="culture"></param>
20+
/// <returns>True if the hostname has a domain, otherwise false.</returns>
21+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
22+
{
23+
return value is string hostname && hostname.Trim('.').Contains('.');
24+
}
25+
26+
/// <summary>
27+
/// !!! Method not implemented !!!
28+
/// </summary>
29+
/// <param name="value"></param>
30+
/// <param name="targetType"></param>
31+
/// <param name="parameter"></param>
32+
/// <param name="culture"></param>
33+
/// <returns></returns>
34+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
35+
{
36+
throw new NotImplementedException();
37+
}
38+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
using NETworkManager.Localization.Resources;
5+
using NETworkManager.Models.RDAP;
6+
7+
namespace NETworkManager.Converters;
8+
9+
/// <summary>
10+
/// Convert <see cref="RDAPQueryType" /> to an example input (watermark).
11+
/// </summary>
12+
public sealed class RDAPQueryTypeToWatermarkConverter : IValueConverter
13+
{
14+
/// <summary>
15+
/// Convert <see cref="RDAPQueryType" /> to an example input (watermark).
16+
/// </summary>
17+
/// <param name="value">Object from type <see cref="RDAPQueryType" />.</param>
18+
/// <param name="targetType"></param>
19+
/// <param name="parameter"></param>
20+
/// <param name="culture"></param>
21+
/// <returns>Example input for the <see cref="RDAPQueryType" />.</returns>
22+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
23+
{
24+
return value switch
25+
{
26+
RDAPQueryType.Nameserver => StaticStrings.ExampleRDAPNameserver,
27+
RDAPQueryType.TLD => StaticStrings.ExampleTLD,
28+
RDAPQueryType.IPAddress => StaticStrings.ExampleIPAddressOrCIDR,
29+
RDAPQueryType.ASN => StaticStrings.ExampleASN,
30+
RDAPQueryType.Entity => StaticStrings.ExampleRDAPEntityHandle,
31+
_ => StaticStrings.ExampleDomain
32+
};
33+
}
34+
35+
/// <summary>
36+
/// !!! Method not implemented !!!
37+
/// </summary>
38+
/// <param name="value"></param>
39+
/// <param name="targetType"></param>
40+
/// <param name="parameter"></param>
41+
/// <param name="culture"></param>
42+
/// <returns></returns>
43+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
44+
{
45+
throw new NotImplementedException();
46+
}
47+
}

‎Source/NETworkManager.Documentation/ResourceManager.cs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public static class ResourceManager
2727
Strings.Resource_ListTLD_Description),
2828

2929
new("natural-earth-geojson", "https://github.com/martynafford/natural-earth-geojson",
30-
Strings.Resource_NaturalEarth_Description)
30+
Strings.Resource_NaturalEarth_Description),
31+
32+
new("Public Suffix List", "https://publicsuffix.org/",
33+
Strings.Resource_PublicSuffixList_Description)
3134
];
3235
}

‎Source/NETworkManager.Localization/Resources/StaticStrings.Designer.cs‎

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Source/NETworkManager.Localization/Resources/StaticStrings.resx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,4 +378,7 @@
378378
<data name="ExampleRDAPEntityHandle" xml:space="preserve">
379379
<value>ORG-RIEN1-RIPE</value>
380380
</data>
381+
<data name="ExampleRDAPNameserver" xml:space="preserve">
382+
<value>a.iana-servers.net</value>
383+
</data>
381384
</root>

‎Source/NETworkManager.Localization/Resources/Strings.Designer.cs‎

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Source/NETworkManager.Localization/Resources/Strings.resx‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4830,4 +4830,31 @@ Cloudflare may log your IP address and network information. See Cloudflare's pri
48304830
<data name="DeleteRulesX" xml:space="preserve">
48314831
<value>Delete rules ({0})</value>
48324832
</data>
4833+
<data name="RDAPQueryType_Auto" xml:space="preserve">
4834+
<value>Auto</value>
4835+
</data>
4836+
<data name="EnterValidRDAPQuery" xml:space="preserve">
4837+
<value>Enter a valid domain, IP address, AS number or entity handle!</value>
4838+
</data>
4839+
<data name="RDAPTimeoutAfterXSeconds" xml:space="preserve">
4840+
<value>The RDAP server did not respond within {0} seconds.</value>
4841+
</data>
4842+
<data name="RDAPQueryType_Nameserver" xml:space="preserve">
4843+
<value>Nameserver</value>
4844+
</data>
4845+
<data name="EnterValidNameserver" xml:space="preserve">
4846+
<value>Enter a valid nameserver (e.g., "ns1.example.com")!</value>
4847+
</data>
4848+
<data name="ASNumbers" xml:space="preserve">
4849+
<value>AS numbers</value>
4850+
</data>
4851+
<data name="RDAPDots" xml:space="preserve">
4852+
<value>RDAP...</value>
4853+
</data>
4854+
<data name="RDAPXIsNotARegisteredDomainShowingY" xml:space="preserve">
4855+
<value>"{0}" is not a registered domain, showing the registration data of "{1}".</value>
4856+
</data>
4857+
<data name="Resource_PublicSuffixList_Description" xml:space="preserve">
4858+
<value>List of public suffixes (e.g. "co.uk") from publicsuffix.org (MPL-2.0), which is used to get the registered domain of a hostname for RDAP queries.</value>
4859+
</data>
48334860
</root>

‎Source/NETworkManager.Models/NETworkManager.Models.csproj‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<None Remove="Resources\OUI.xml" />
1616
<None Remove="Resources\Ports.xml" />
1717
<None Remove="Resources\PSDiscoveryProtocol.psm1" />
18+
<None Remove="Resources\PublicSuffixList.dat" />
1819
<None Remove="Resources\RDAPStealthEndpoints.json" />
1920
<None Remove="Resources\WhoisServers.xml" />
2021
</ItemGroup>
@@ -23,6 +24,7 @@
2324
</ItemGroup>
2425
<ItemGroup>
2526
<EmbeddedResource Include="Resources\PSDiscoveryProtocol.psm1" />
27+
<EmbeddedResource Include="Resources\PublicSuffixList.dat" />
2628
<EmbeddedResource Include="Resources\RDAPStealthEndpoints.json" />
2729
</ItemGroup>
2830
<ItemGroup>

0 commit comments

Comments
 (0)