Files
dnsmasq-webui/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/Validation/RaParamSemanticHandler.cs
T
alex 181cbbed48 Option semantics: handlers, shared syntax, RaParam interface validation
- Add IOptionSemanticHandler implementations for DNS/DHCP/auth/interface options
- Add shared syntax helpers (DnsmasqRrTypeSyntax, DnsmasqScopedDomainSyntax, etc.)
- Wire semantics and registry for multi/single options; metadata-only semantics
- RaParam: use DnsmasqRelaySyntax.IsInterfaceNameWithOptionalTrailingWildcard so
  interface names allow only trailing *, not asterisks in the middle (eth*0, *eth0)
- Tests: RaParam trailing-wildcard and invalid-asterisk cases
2026-03-11 09:38:36 +10:00

72 lines
2.5 KiB
C#

using DnsmasqWebUI.Infrastructure.Helpers.Config;
namespace DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Validation;
/// <summary>
/// Specialized semantic behavior for <c>ra-param</c> values.
/// Uses conservative validation for interface, mtu/priority, interval, and optional lifetime fields.
/// </summary>
public sealed class RaParamSemanticHandler : IOptionSemanticHandler
{
public bool CanHandle(string optionName) =>
optionName == DnsmasqConfKeys.RaParam;
public string? ValidateSingle(object? value) => null;
public string? ValidateMultiItem(string? value)
{
var s = value?.Trim() ?? "";
if (s.Length == 0)
return "Value cannot be empty.";
var tokens = s.Split(',').Select(t => t.Trim()).ToArray();
if (tokens.Length == 0 || tokens.Any(t => t.Length == 0))
return "ra-param contains an empty comma-separated segment.";
if (!DnsmasqRelaySyntax.IsInterfaceNameWithOptionalTrailingWildcard(tokens[0]))
return "ra-param must start with an interface name (optionally ending with *).";
var seenPriority = false;
var seenMtu = false;
var numericCount = 0;
for (var i = 1; i < tokens.Length; i++)
{
var token = tokens[i];
if (token is "high" or "low")
{
if (seenPriority)
return "ra-param can only include one priority token.";
seenPriority = true;
continue;
}
if (token.Equals("off", StringComparison.OrdinalIgnoreCase) ||
token.StartsWith("mtu:", StringComparison.OrdinalIgnoreCase))
{
if (seenMtu)
return "ra-param can only include one mtu token.";
seenMtu = true;
if (token.StartsWith("mtu:", StringComparison.OrdinalIgnoreCase))
{
var mtuValue = token["mtu:".Length..];
if (mtuValue.Length == 0)
return "ra-param mtu: value cannot be empty.";
}
continue;
}
if (int.TryParse(token, out _))
{
numericCount++;
if (numericCount > 2)
return "ra-param can include at most interval and lifetime numeric values.";
continue;
}
return $"Invalid ra-param segment '{token}'.";
}
return null;
}
}