Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions TechnitiumLibrary.Net/Dns/ResourceRecords/DnsNAPTRRecordData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,64 @@ public class DnsNAPTRRecordData : DnsResourceRecordData

public DnsNAPTRRecordData(ushort order, ushort preference, string flags, string services, string regexp, string replacement)
{
ArgumentNullException.ThrowIfNull(flags);

ArgumentNullException.ThrowIfNull(services);

ArgumentNullException.ThrowIfNull(regexp);

ArgumentNullException.ThrowIfNull(replacement);

// RFC 3403: REGEXP and REPLACEMENT are mutually exclusive
// If both are present (non-empty), the record is in error and MUST be rejected or ignored.
if (regexp.Length > 0 && replacement.Length > 0)
throw new ArgumentException(
"REGEXP and REPLACEMENT are mutually exclusive per RFC 3403.");

// RFC 3403: FLAGS validation
// Flags are single characters from A–Z and 0–9, case-insensitive.
for (int i = 0; i < flags.Length; i++)
{
char c = flags[i];
if (!(char.IsAsciiLetter(c) || char.IsAsciiDigit(c)))
throw new ArgumentException(
$"Invalid NAPTR flag '{c}'. Allowed set is A–Z and 0–9.",
nameof(flags));
}

// RFC 3403: SERVICES is a DNS <character-string>
// RFC intentionally does NOT define semantics here; only basic sanity checks.
// Enforce non-control UTF-16 chars; deeper validation is application-specific.
for (int i = 0; i < services.Length; i++)
{
if (char.IsControl(services[i]))
throw new ArgumentException(
"SERVICES contains control characters, which are not permitted.",
nameof(services));
}

// RFC 3403: REPLACEMENT must be a fully qualified domain name
if (replacement.Length > 0)
{
// Must end with a root label (trailing dot)
// But zoneFile.PopDomainAsync() removes the trailing dot.
// Therefore the check is useless.

// No name compression, no empty labels except the root
if (replacement.Contains("..", StringComparison.Ordinal))
throw new ArgumentException(
"REPLACEMENT contains empty DNS labels.",
nameof(replacement));
}

// RFC 3403: REGEXP sanity
// The RFC requires POSIX ERE semantics but does not mandate compile-time validation.
// Enforce only that it is non-empty when used.
if (regexp.Length == 0 && replacement.Length == 0)
throw new ArgumentException(
"Either REGEXP or REPLACEMENT must be specified per RFC 3403.");

// DNS <character-string> constraints
if (DnsClient.IsDomainNameUnicode(replacement))
replacement = DnsClient.ConvertDomainNameToAscii(replacement);

Expand Down