Effective config: do-0x20-encode revert fix, validation UI, custom displays, option help

- Fix do-0x20-encode: clearing pending when toggling back to original (Disabled/Default)
  - Do0x20EncodeDisplay: uncheck uses pending.OldValue when present
  - EffectiveConfigFieldDisplay: use pending.OldValue as _valueAtActivation when re-entering with pending
- Effective config: validation errors/warnings in toolbar and save modal, revert from modal
- Custom displays: Do0x20EncodeDisplay (tri-state), KeyOnlyOrValueDisplay
- ExplicitToggleState, config/apply support for do-0x20-encode, option help and tests
This commit is contained in:
2026-03-08 12:52:46 +10:00
parent 6cf4c07695
commit a03e93bcd3
89 changed files with 2229 additions and 67 deletions
@@ -148,4 +148,63 @@ public static class DnsmasqConfKeys
// --- Niche / platform (Linux conntrack mark for UBus/query filtering) ---
public const string Conntrack = "conntrack";
public const string ConnmarkAllowlistEnable = "connmark-allowlist-enable";
public const string NoRoundRobin = "no-round-robin";
public const string DnssecNoTimecheck = "dnssec-no-timecheck";
public const string DnssecDebug = "dnssec-debug";
public const string Leasequery = "leasequery";
public const string DhcpGenerateNames = "dhcp-generate-names";
public const string DhcpBroadcast = "dhcp-broadcast";
public const string DhcpSequentialIp = "dhcp-sequential-ip";
public const string DhcpIgnoreClid = "dhcp-ignore-clid";
public const string BootpDynamic = "bootp-dynamic";
public const string NoPing = "no-ping";
public const string ScriptArp = "script-arp";
public const string ScriptOnRenewal = "script-on-renewal";
public const string DhcpNoOverride = "dhcp-no-override";
public const string QuietDhcp = "quiet-dhcp";
public const string QuietDhcp6 = "quiet-dhcp6";
public const string QuietRa = "quiet-ra";
public const string QuietTftp = "quiet-tftp";
public const string DnsForwardMax = "dns-forward-max";
public const string Dumpfile = "dumpfile";
public const string Dumpmask = "dumpmask";
public const string AddCpeId = "add-cpe-id";
public const string DnssecTimestamp = "dnssec-timestamp";
public const string DnssecLimits = "dnssec-limits";
public const string DhcpAlternatePort = "dhcp-alternate-port";
public const string DhcpDuid = "dhcp-duid";
public const string DhcpLuascript = "dhcp-luascript";
public const string DhcpScriptuser = "dhcp-scriptuser";
public const string DhcpPxeVendor = "dhcp-pxe-vendor";
public const string ConnmarkAllowlist = "connmark-allowlist";
public const string CaaRecord = "caa-record";
public const string DnsRr = "dns-rr";
public const string SynthDomain = "synth-domain";
public const string AuthZone = "auth-zone";
public const string AuthSoa = "auth-soa";
public const string AuthSecServers = "auth-sec-servers";
public const string AuthPeer = "auth-peer";
public const string DhcpRelay = "dhcp-relay";
public const string DhcpCircuitid = "dhcp-circuitid";
public const string DhcpRemoteid = "dhcp-remoteid";
public const string DhcpSubscrid = "dhcp-subscrid";
public const string DhcpProxy = "dhcp-proxy";
public const string TagIf = "tag-if";
public const string BridgeInterface = "bridge-interface";
public const string SharedNetwork = "shared-network";
public const string DhcpOptionPxe = "dhcp-option-pxe";
public const string UseStaleCache = "use-stale-cache";
public const string AddMac = "add-mac";
public const string StripMac = "strip-mac";
public const string AddSubnet = "add-subnet";
public const string StripSubnet = "strip-subnet";
public const string Umbrella = "umbrella";
public const string Do0x20Encode = "do-0x20-encode";
public const string No0x20Encode = "no-0x20-encode";
}
@@ -57,6 +57,11 @@ public static class DnsmasqOptionTooltips
[DnsmasqConfKeys.NoDhcpv6Interface] = "No DHCPv6/RA on these interfaces. Can repeat.",
[DnsmasqConfKeys.Ipset] = "Add resolved IPs to ipset (domain, set names). Can repeat.",
[DnsmasqConfKeys.Nftset] = "Add resolved IPs to nftables sets. Can repeat.",
[DnsmasqConfKeys.ConnmarkAllowlistEnable] = "Use conntrack marks to allowlist which queries are forwarded (with connmark-allowlist).",
[DnsmasqConfKeys.NoRoundRobin] = "Do not rotate the order of upstream servers for each query.",
[DnsmasqConfKeys.DnsForwardMax] = "Maximum number of concurrent DNS forwards (default 150).",
[DnsmasqConfKeys.ConnmarkAllowlist] = "Conntrack marks that allowlist which queries are forwarded (with connmark-allowlist-enable). Can repeat.",
[DnsmasqConfKeys.Do0x20Encode] = "Use 0x20 encoding in queries to detect DNS poisoning (Enabled), disable it (Disabled), or default (not set).",
// --- DNS records ---
[DnsmasqConfKeys.Domain] = "Local domain(s); optional IP. Can repeat (e.g. domain=home,192.168.1.1).",
@@ -72,12 +77,33 @@ public static class DnsmasqOptionTooltips
[DnsmasqConfKeys.HostRecord] = "A/AAAA host records (name, IP). Can repeat.",
[DnsmasqConfKeys.DynamicHost] = "Dynamic host entries. Can repeat.",
[DnsmasqConfKeys.InterfaceName] = "Name per interface (for localise-queries). Can repeat.",
[DnsmasqConfKeys.CaaRecord] = "CAA record (domain, flags, tag, value). Can repeat.",
[DnsmasqConfKeys.DnsRr] = "Arbitrary DNS resource record. Can repeat.",
[DnsmasqConfKeys.SynthDomain] = "Synthesize answers for a domain. Can repeat.",
[DnsmasqConfKeys.AuthZone] = "Authoritative zone (domain, subnet). Can repeat.",
[DnsmasqConfKeys.AuthSoa] = "SOA record for authoritative zone. Can repeat.",
[DnsmasqConfKeys.AuthSecServers] = "Secondary servers for zone transfer. Can repeat.",
[DnsmasqConfKeys.AuthPeer] = "Peers for zone transfer. Can repeat.",
// --- DHCP ---
[DnsmasqConfKeys.DhcpAuthoritative] = "DHCP server is authoritative; required for DHCP to work.",
[DnsmasqConfKeys.DhcpRapidCommit] = "DHCPv4 Rapid Commit (RFC 4039); use only if single server or all commit.",
[DnsmasqConfKeys.Leasequery] = "Respond to DHCPLEASEQUERY messages (e.g. from relay).",
[DnsmasqConfKeys.DhcpGenerateNames] = "Generate names for DHCP clients from their hostname or MAC.",
[DnsmasqConfKeys.DhcpBroadcast] = "Use broadcast to communicate with DHCP clients when unicast is not possible.",
[DnsmasqConfKeys.DhcpSequentialIp] = "Allocate DHCP addresses sequentially from the range.",
[DnsmasqConfKeys.DhcpIgnoreClid] = "Ignore client identifier (use MAC only) for DHCP.",
[DnsmasqConfKeys.BootpDynamic] = "Allow dynamic allocation for BOOTP clients.",
[DnsmasqConfKeys.NoPing] = "Do not ping the gateway before offering a DHCP lease.",
[DnsmasqConfKeys.ScriptArp] = "Call dhcp-script with extra 'arp' or 'arp-old arp-new' for ARP table updates.",
[DnsmasqConfKeys.ScriptOnRenewal] = "Call dhcp-script on lease renewal (in addition to first assignment).",
[DnsmasqConfKeys.DhcpNoOverride] = "Do not replace existing DNS/DHCP data for a client (e.g. from /etc/hosts).",
[DnsmasqConfKeys.DhcpScript] = "Script run on lease add/delete (add|del, MAC, IP, hostname).",
[DnsmasqConfKeys.LeasefileRo] = "Do not create or truncate the DHCP lease file.",
[DnsmasqConfKeys.DhcpAlternatePort] = "Use alternate port for DHCP (e.g. 67).",
[DnsmasqConfKeys.DhcpDuid] = "DHCP unique identifier (DUID) for the server.",
[DnsmasqConfKeys.DhcpLuascript] = "Lua script for DHCP (e.g. for custom logic).",
[DnsmasqConfKeys.DhcpScriptuser] = "User to run dhcp-script as (requires root).",
[DnsmasqConfKeys.DhcpLeasefile] = "Path to the DHCP lease file.",
[DnsmasqConfKeys.DhcpLeaseMax] = "Maximum number of DHCP leases to hold.",
[DnsmasqConfKeys.DhcpTtl] = "TTL for DHCP names in the DNS cache.",
@@ -98,6 +124,14 @@ public static class DnsmasqOptionTooltips
[DnsmasqConfKeys.DhcpUserclass] = "Match by DHCP user class. Can repeat.",
[DnsmasqConfKeys.RaParam] = "Router advertisement parameters (DHCPv6/RA). Can repeat.",
[DnsmasqConfKeys.Slaac] = "SLAAC host part for DHCPv6/RA. Can repeat.",
[DnsmasqConfKeys.DhcpRelay] = "Relay DHCP to another server. Can repeat.",
[DnsmasqConfKeys.DhcpCircuitid] = "Circuit ID for DHCP relay. Can repeat.",
[DnsmasqConfKeys.DhcpRemoteid] = "Remote ID for DHCP relay. Can repeat.",
[DnsmasqConfKeys.DhcpSubscrid] = "Subscriber ID for DHCP relay. Can repeat.",
[DnsmasqConfKeys.DhcpProxy] = "DHCP proxy (relay) configuration. Can repeat.",
[DnsmasqConfKeys.TagIf] = "Set tag when request from interface. Can repeat.",
[DnsmasqConfKeys.BridgeInterface] = "Bridge interfaces for DHCP. Can repeat.",
[DnsmasqConfKeys.SharedNetwork] = "Shared network for DHCP. Can repeat.",
// --- TFTP / PXE ---
[DnsmasqConfKeys.EnableTftp] = "Enable the built-in TFTP server.",
@@ -107,10 +141,17 @@ public static class DnsmasqOptionTooltips
[DnsmasqConfKeys.TftpRoot] = "Root directory for TFTP files.",
[DnsmasqConfKeys.PxePrompt] = "PXE boot prompt (e.g. timeout,0 to disable).",
[DnsmasqConfKeys.PxeService] = "PXE service definitions. Can repeat.",
[DnsmasqConfKeys.DhcpPxeVendor] = "PXE vendor class string (e.g. for PXE matching).",
[DnsmasqConfKeys.DhcpOptionPxe] = "PXE option (e.g. option 43). Can repeat.",
// --- DNSSEC ---
[DnsmasqConfKeys.Dnssec] = "Validate DNSSEC on replies from upstream.",
[DnsmasqConfKeys.DnssecCheckUnsigned] = "Treat unsigned zones as bogus.",
[DnsmasqConfKeys.DnssecNoTimecheck] = "Do not validate DNSSEC signatures against the current time (e.g. for testing).",
[DnsmasqConfKeys.DnssecDebug] = "Log DNSSEC validation failures at debug level.",
[DnsmasqConfKeys.AddCpeId] = "Add CPE-ID to DHCP requests (for identification).",
[DnsmasqConfKeys.DnssecTimestamp] = "Path to file for DNSSEC timestamp (e.g. for validation).",
[DnsmasqConfKeys.DnssecLimits] = "DNSSEC validation limits (e.g. max signed zones).",
[DnsmasqConfKeys.TrustAnchor] = "DNSSEC trust anchors. Can repeat.",
// --- Cache ---
@@ -121,6 +162,12 @@ public static class DnsmasqOptionTooltips
[DnsmasqConfKeys.MaxTtl] = "Maximum TTL for cached responses (cap on upstream TTL).",
[DnsmasqConfKeys.MaxCacheTtl] = "Maximum TTL for positive cache entries.",
[DnsmasqConfKeys.MinCacheTtl] = "Minimum TTL for positive cache entries.",
[DnsmasqConfKeys.Dumpfile] = "Dump cache to this file (for debugging).",
[DnsmasqConfKeys.Dumpmask] = "Mask for cache dump (which data to include).",
[DnsmasqConfKeys.UseStaleCache] = "Use stale cache when upstream is unavailable. Off, On (key-only), or custom seconds.",
[DnsmasqConfKeys.AddMac] = "Add MAC address to DNS queries. Off, On (key-only), or custom value.",
[DnsmasqConfKeys.AddSubnet] = "Add subnet to DNS queries. Off, On (key-only), or custom value.",
[DnsmasqConfKeys.Umbrella] = "Cisco Umbrella device identity. Off, On (key-only), or custom value.",
// --- Process & networking ---
[DnsmasqConfKeys.NoPoll] = "Do not poll /etc/resolv.conf for changes.",
@@ -143,6 +190,10 @@ public static class DnsmasqOptionTooltips
[DnsmasqConfKeys.KeepInForeground] = "Do not daemonise; stay in foreground (e.g. under systemd).",
[DnsmasqConfKeys.NoDaemon] = "Debug mode: no fork, no pid file, log to stderr.",
[DnsmasqConfKeys.Conntrack] = "Linux connection-tracking mark for UBus/query filtering (platform-dependent).",
[DnsmasqConfKeys.QuietDhcp] = "Suppress routine DHCP logging.",
[DnsmasqConfKeys.QuietDhcp6] = "Suppress routine DHCPv6 logging.",
[DnsmasqConfKeys.QuietRa] = "Suppress routine router advertisement logging.",
[DnsmasqConfKeys.QuietTftp] = "Suppress routine TFTP logging.",
}.ToFrozenDictionary();
/// <summary>Returns a short tooltip for the option, or null if none is defined.</summary>
@@ -87,6 +87,11 @@ public static class EffectiveConfigFieldBuilder
list.AddDescriptor(registry, DnsmasqConfKeys.FastDnsRetry, status, s => Config(s)?.FastDnsRetry, s => Sources(s)?.FastDnsRetry, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Ipset, status, null, null, Items(ec => ec?.IpsetValues, src => src?.IpsetValues));
list.AddDescriptor(registry, DnsmasqConfKeys.Nftset, status, null, null, Items(ec => ec?.NftsetValues, src => src?.NftsetValues));
list.AddDescriptor(registry, DnsmasqConfKeys.ConnmarkAllowlistEnable, status, s => Config(s)?.ConnmarkAllowlistEnable, s => Sources(s)?.ConnmarkAllowlistEnable, null);
list.AddDescriptor(registry, DnsmasqConfKeys.NoRoundRobin, status, s => Config(s)?.NoRoundRobin, s => Sources(s)?.NoRoundRobin, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DnsForwardMax, status, s => Config(s)?.DnsForwardMax, s => Sources(s)?.DnsForwardMax, null);
list.AddDescriptor(registry, DnsmasqConfKeys.ConnmarkAllowlist, status, null, null, Items(ec => ec?.ConnmarkAllowlistValues, src => src?.ConnmarkAllowlistValues));
list.AddDescriptor(registry, DnsmasqConfKeys.Do0x20Encode, status, s => Config(s)?.Do0x20EncodeState, s => Sources(s)?.Do0x20Encode, null);
// DNS records
list.AddDescriptor(registry, DnsmasqConfKeys.Domain, status, null, null, Items(ec => ec?.DomainValues, src => src?.DomainValues));
@@ -99,6 +104,13 @@ public static class EffectiveConfigFieldBuilder
list.AddDescriptor(registry, DnsmasqConfKeys.HostRecord, status, null, null, Items(ec => ec?.HostRecordValues, src => src?.HostRecordValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DynamicHost, status, null, null, Items(ec => ec?.DynamicHostValues, src => src?.DynamicHostValues));
list.AddDescriptor(registry, DnsmasqConfKeys.InterfaceName, status, null, null, Items(ec => ec?.InterfaceNameValues, src => src?.InterfaceNameValues));
list.AddDescriptor(registry, DnsmasqConfKeys.CaaRecord, status, null, null, Items(ec => ec?.CaaRecordValues, src => src?.CaaRecordValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DnsRr, status, null, null, Items(ec => ec?.DnsRrValues, src => src?.DnsRrValues));
list.AddDescriptor(registry, DnsmasqConfKeys.SynthDomain, status, null, null, Items(ec => ec?.SynthDomainValues, src => src?.SynthDomainValues));
list.AddDescriptor(registry, DnsmasqConfKeys.AuthZone, status, null, null, Items(ec => ec?.AuthZoneValues, src => src?.AuthZoneValues));
list.AddDescriptor(registry, DnsmasqConfKeys.AuthSoa, status, null, null, Items(ec => ec?.AuthSoaValues, src => src?.AuthSoaValues));
list.AddDescriptor(registry, DnsmasqConfKeys.AuthSecServers, status, null, null, Items(ec => ec?.AuthSecServersValues, src => src?.AuthSecServersValues));
list.AddDescriptor(registry, DnsmasqConfKeys.AuthPeer, status, null, null, Items(ec => ec?.AuthPeerValues, src => src?.AuthPeerValues));
list.AddDescriptor(registry, DnsmasqConfKeys.MxTarget, status, s => Config(s)?.MxTarget, s => Sources(s)?.MxTarget, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Localmx, status, s => Config(s)?.Localmx, s => Sources(s)?.Localmx, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Selfmx, status, s => Config(s)?.Selfmx, s => Sources(s)?.Selfmx, null);
@@ -107,7 +119,21 @@ public static class EffectiveConfigFieldBuilder
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpAuthoritative, status, s => Config(s)?.DhcpAuthoritative, s => Sources(s)?.DhcpAuthoritative, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpRapidCommit, status, s => Config(s)?.DhcpRapidCommit, s => Sources(s)?.DhcpRapidCommit, null);
list.AddDescriptor(registry, DnsmasqConfKeys.LeasefileRo, status, s => Config(s)?.LeasefileRo, s => Sources(s)?.LeasefileRo, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Leasequery, status, s => Config(s)?.Leasequery, s => Sources(s)?.Leasequery, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpGenerateNames, status, s => Config(s)?.DhcpGenerateNames, s => Sources(s)?.DhcpGenerateNames, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpBroadcast, status, s => Config(s)?.DhcpBroadcast, s => Sources(s)?.DhcpBroadcast, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpSequentialIp, status, s => Config(s)?.DhcpSequentialIp, s => Sources(s)?.DhcpSequentialIp, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpIgnoreClid, status, s => Config(s)?.DhcpIgnoreClid, s => Sources(s)?.DhcpIgnoreClid, null);
list.AddDescriptor(registry, DnsmasqConfKeys.BootpDynamic, status, s => Config(s)?.BootpDynamic, s => Sources(s)?.BootpDynamic, null);
list.AddDescriptor(registry, DnsmasqConfKeys.NoPing, status, s => Config(s)?.NoPing, s => Sources(s)?.NoPing, null);
list.AddDescriptor(registry, DnsmasqConfKeys.ScriptArp, status, s => Config(s)?.ScriptArp, s => Sources(s)?.ScriptArp, null);
list.AddDescriptor(registry, DnsmasqConfKeys.ScriptOnRenewal, status, s => Config(s)?.ScriptOnRenewal, s => Sources(s)?.ScriptOnRenewal, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpNoOverride, status, s => Config(s)?.DhcpNoOverride, s => Sources(s)?.DhcpNoOverride, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpScript, status, s => Config(s)?.DhcpScriptPath, s => Sources(s)?.DhcpScriptPath, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpAlternatePort, status, s => Config(s)?.DhcpAlternatePort, s => Sources(s)?.DhcpAlternatePort, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpDuid, status, s => Config(s)?.DhcpDuid, s => Sources(s)?.DhcpDuid, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpLuascript, status, s => Config(s)?.DhcpLuascriptPath, s => Sources(s)?.DhcpLuascriptPath, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpScriptuser, status, s => Config(s)?.DhcpScriptuser, s => Sources(s)?.DhcpScriptuser, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpLeasefile, status, s => Config(s)?.DhcpLeaseFilePath, s => Sources(s)?.DhcpLeaseFilePath, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpLeaseMax, status, s => Config(s)?.DhcpLeaseMax, s => Sources(s)?.DhcpLeaseMax, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpTtl, status, s => Config(s)?.DhcpTtl, s => Sources(s)?.DhcpTtl, null);
@@ -122,6 +148,14 @@ public static class EffectiveConfigFieldBuilder
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpHostsfile, status, null, null, Items(ec => ec?.DhcpHostsfilePaths, src => src?.DhcpHostsfilePaths));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpOptsfile, status, null, null, Items(ec => ec?.DhcpOptsfilePaths, src => src?.DhcpOptsfilePaths));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpHostsdir, status, null, null, Items(ec => ec?.DhcpHostsdirPaths, src => src?.DhcpHostsdirPaths));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpRelay, status, null, null, Items(ec => ec?.DhcpRelayValues, src => src?.DhcpRelayValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpCircuitid, status, null, null, Items(ec => ec?.DhcpCircuitidValues, src => src?.DhcpCircuitidValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpRemoteid, status, null, null, Items(ec => ec?.DhcpRemoteidValues, src => src?.DhcpRemoteidValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpSubscrid, status, null, null, Items(ec => ec?.DhcpSubscridValues, src => src?.DhcpSubscridValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpProxy, status, null, null, Items(ec => ec?.DhcpProxyValues, src => src?.DhcpProxyValues));
list.AddDescriptor(registry, DnsmasqConfKeys.TagIf, status, null, null, Items(ec => ec?.TagIfValues, src => src?.TagIfValues));
list.AddDescriptor(registry, DnsmasqConfKeys.BridgeInterface, status, null, null, Items(ec => ec?.BridgeInterfaceValues, src => src?.BridgeInterfaceValues));
list.AddDescriptor(registry, DnsmasqConfKeys.SharedNetwork, status, null, null, Items(ec => ec?.SharedNetworkValues, src => src?.SharedNetworkValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpBoot, status, null, null, Items(ec => ec?.DhcpBootValues, src => src?.DhcpBootValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpIgnore, status, null, null, Items(ec => ec?.DhcpIgnoreValues, src => src?.DhcpIgnoreValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpVendorclass, status, null, null, Items(ec => ec?.DhcpVendorclassValues, src => src?.DhcpVendorclassValues));
@@ -137,11 +171,18 @@ public static class EffectiveConfigFieldBuilder
list.AddDescriptor(registry, DnsmasqConfKeys.TftpRoot, status, s => Config(s)?.TftpRootPath, s => Sources(s)?.TftpRootPath, null);
list.AddDescriptor(registry, DnsmasqConfKeys.PxePrompt, status, s => Config(s)?.PxePrompt, s => Sources(s)?.PxePrompt, null);
list.AddDescriptor(registry, DnsmasqConfKeys.PxeService, status, null, null, Items(ec => ec?.PxeServiceValues, src => src?.PxeServiceValues));
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpPxeVendor, status, s => Config(s)?.DhcpPxeVendor, s => Sources(s)?.DhcpPxeVendor, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DhcpOptionPxe, status, null, null, Items(ec => ec?.DhcpOptionPxeValues, src => src?.DhcpOptionPxeValues));
// DNSSEC
list.AddDescriptor(registry, DnsmasqConfKeys.Dnssec, status, s => Config(s)?.Dnssec, s => Sources(s)?.Dnssec, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DnssecCheckUnsigned, status, s => Config(s)?.DnssecCheckUnsigned, s => Sources(s)?.DnssecCheckUnsigned, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DnssecNoTimecheck, status, s => Config(s)?.DnssecNoTimecheck, s => Sources(s)?.DnssecNoTimecheck, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DnssecDebug, status, s => Config(s)?.DnssecDebug, s => Sources(s)?.DnssecDebug, null);
list.AddDescriptor(registry, DnsmasqConfKeys.ProxyDnssec, status, s => Config(s)?.ProxyDnssec, s => Sources(s)?.ProxyDnssec, null);
list.AddDescriptor(registry, DnsmasqConfKeys.AddCpeId, status, s => Config(s)?.AddCpeId, s => Sources(s)?.AddCpeId, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DnssecTimestamp, status, s => Config(s)?.DnssecTimestamp, s => Sources(s)?.DnssecTimestamp, null);
list.AddDescriptor(registry, DnsmasqConfKeys.DnssecLimits, status, s => Config(s)?.DnssecLimits, s => Sources(s)?.DnssecLimits, null);
list.AddDescriptor(registry, DnsmasqConfKeys.TrustAnchor, status, null, null, Items(ec => ec?.TrustAnchorValues, src => src?.TrustAnchorValues));
// Cache
@@ -149,6 +190,12 @@ public static class EffectiveConfigFieldBuilder
list.AddDescriptor(registry, DnsmasqConfKeys.CacheSize, status, s => Config(s)?.CacheSize, s => Sources(s)?.CacheSize, null);
list.AddDescriptor(registry, DnsmasqConfKeys.LocalTtl, status, s => Config(s)?.LocalTtl, s => Sources(s)?.LocalTtl, null);
list.AddDescriptor(registry, DnsmasqConfKeys.NoNegcache, status, s => Config(s)?.NoNegcache, s => Sources(s)?.NoNegcache, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Dumpfile, status, s => Config(s)?.DumpfilePath, s => Sources(s)?.DumpfilePath, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Dumpmask, status, s => Config(s)?.Dumpmask, s => Sources(s)?.Dumpmask, null);
list.AddDescriptor(registry, DnsmasqConfKeys.UseStaleCache, status, s => Config(s)?.UseStaleCache, s => Sources(s)?.UseStaleCache, null);
list.AddDescriptor(registry, DnsmasqConfKeys.AddMac, status, s => Config(s)?.AddMac, s => Sources(s)?.AddMac, null);
list.AddDescriptor(registry, DnsmasqConfKeys.AddSubnet, status, s => Config(s)?.AddSubnet, s => Sources(s)?.AddSubnet, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Umbrella, status, s => Config(s)?.Umbrella, s => Sources(s)?.Umbrella, null);
list.AddDescriptor(registry, DnsmasqConfKeys.NegTtl, status, s => Config(s)?.NegTtl, s => Sources(s)?.NegTtl, null);
list.AddDescriptor(registry, DnsmasqConfKeys.MaxTtl, status, s => Config(s)?.MaxTtl, s => Sources(s)?.MaxTtl, null);
list.AddDescriptor(registry, DnsmasqConfKeys.MaxCacheTtl, status, s => Config(s)?.MaxCacheTtl, s => Sources(s)?.MaxCacheTtl, null);
@@ -179,6 +226,10 @@ public static class EffectiveConfigFieldBuilder
list.AddDescriptor(registry, DnsmasqConfKeys.KeepInForeground, status, s => Config(s)?.KeepInForeground, s => Sources(s)?.KeepInForeground, null);
list.AddDescriptor(registry, DnsmasqConfKeys.NoDaemon, status, s => Config(s)?.NoDaemon, s => Sources(s)?.NoDaemon, null);
list.AddDescriptor(registry, DnsmasqConfKeys.Conntrack, status, s => Config(s)?.Conntrack, s => Sources(s)?.Conntrack, null);
list.AddDescriptor(registry, DnsmasqConfKeys.QuietDhcp, status, s => Config(s)?.QuietDhcp, s => Sources(s)?.QuietDhcp, null);
list.AddDescriptor(registry, DnsmasqConfKeys.QuietDhcp6, status, s => Config(s)?.QuietDhcp6, s => Sources(s)?.QuietDhcp6, null);
list.AddDescriptor(registry, DnsmasqConfKeys.QuietRa, status, s => Config(s)?.QuietRa, s => Sources(s)?.QuietRa, null);
list.AddDescriptor(registry, DnsmasqConfKeys.QuietTftp, status, s => Config(s)?.QuietTftp, s => Sources(s)?.QuietTftp, null);
return list;
}
@@ -151,6 +151,57 @@ public static class EffectiveConfigOptionKindMap
[DnsmasqConfKeys.KeepInForeground] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.NoDaemon] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.Conntrack] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.ConnmarkAllowlistEnable] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.NoRoundRobin] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DnssecNoTimecheck] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DnssecDebug] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.Leasequery] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpGenerateNames] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpBroadcast] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpSequentialIp] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpIgnoreClid] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.BootpDynamic] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.NoPing] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.ScriptArp] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.ScriptOnRenewal] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpNoOverride] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.QuietDhcp] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.QuietDhcp6] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.QuietRa] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.QuietTftp] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DnsForwardMax] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.Dumpfile] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.Dumpmask] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.AddCpeId] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DnssecTimestamp] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DnssecLimits] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpAlternatePort] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpDuid] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpLuascript] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpScriptuser] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.DhcpPxeVendor] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.ConnmarkAllowlist] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.CaaRecord] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.DnsRr] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.SynthDomain] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.AuthZone] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.AuthSoa] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.AuthSecServers] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.AuthPeer] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.DhcpRelay] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.DhcpCircuitid] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.DhcpRemoteid] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.DhcpSubscrid] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.DhcpProxy] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.TagIf] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.BridgeInterface] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.SharedNetwork] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.DhcpOptionPxe] = EffectiveConfigFieldKind.Multi,
[DnsmasqConfKeys.UseStaleCache] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.AddMac] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.AddSubnet] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.Umbrella] = EffectiveConfigFieldKind.Single,
[DnsmasqConfKeys.Do0x20Encode] = EffectiveConfigFieldKind.Single,
};
/// <summary>Returns Single or Multi for the given option name; defaults to Single if unknown.</summary>
@@ -287,9 +338,107 @@ public static class EffectiveConfigParserBehaviorMap
[DnsmasqConfKeys.KeepInForeground] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.NoDaemon] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.Conntrack] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.ConnmarkAllowlistEnable] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.NoRoundRobin] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DnssecNoTimecheck] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DnssecDebug] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.Leasequery] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DhcpGenerateNames] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DhcpBroadcast] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DhcpSequentialIp] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DhcpIgnoreClid] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.BootpDynamic] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.NoPing] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.ScriptArp] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.ScriptOnRenewal] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DhcpNoOverride] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.QuietDhcp] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.QuietDhcp6] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.QuietRa] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.QuietTftp] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.DnsForwardMax] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.Dumpfile] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.Dumpmask] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.AddCpeId] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.DnssecTimestamp] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.DnssecLimits] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.DhcpAlternatePort] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.DhcpDuid] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.DhcpLuascript] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.DhcpScriptuser] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.DhcpPxeVendor] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.ConnmarkAllowlist] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.CaaRecord] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.DnsRr] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.SynthDomain] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.AuthZone] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.AuthSoa] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.AuthSecServers] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.AuthPeer] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.DhcpRelay] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.DhcpCircuitid] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.DhcpRemoteid] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.DhcpSubscrid] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.DhcpProxy] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.TagIf] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.BridgeInterface] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.SharedNetwork] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.DhcpOptionPxe] = EffectiveConfigParserBehavior.Multi,
[DnsmasqConfKeys.UseStaleCache] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.AddMac] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.AddSubnet] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.Umbrella] = EffectiveConfigParserBehavior.LastWins,
[DnsmasqConfKeys.Do0x20Encode] = EffectiveConfigParserBehavior.Flag,
[DnsmasqConfKeys.No0x20Encode] = EffectiveConfigParserBehavior.Flag,
};
/// <summary>Returns LastWins, Flag, or Multi for the given option name; defaults to LastWins if unknown.</summary>
public static EffectiveConfigParserBehavior GetBehavior(string optionName) =>
BehaviorByOptionName.TryGetValue(optionName, out var b) ? b : EffectiveConfigParserBehavior.LastWins;
}
/// <summary>
/// How to serialize an option when writing the managed config file.
/// Used by DnsmasqConfigService so KeyOnlyOrValue and InversePair options serialize correctly.
/// </summary>
public enum EffectiveConfigWriteBehavior
{
Flag,
SingleValue,
MultiValue,
KeyOnlyOrValue,
InversePair,
}
/// <summary>
/// Maps dnsmasq option names to write behavior. Options not in the map default to SingleValue
/// (or are inferred from parser behavior in the service). Add entries for KeyOnlyOrValue and InversePair options.
/// </summary>
public static class EffectiveConfigWriteBehaviorMap
{
private static readonly IReadOnlyDictionary<string, EffectiveConfigWriteBehavior> BehaviorByOptionName =
new Dictionary<string, EffectiveConfigWriteBehavior>(StringComparer.Ordinal)
{
[DnsmasqConfKeys.Conntrack] = EffectiveConfigWriteBehavior.Flag,
[DnsmasqConfKeys.UseStaleCache] = EffectiveConfigWriteBehavior.KeyOnlyOrValue,
[DnsmasqConfKeys.AddMac] = EffectiveConfigWriteBehavior.KeyOnlyOrValue,
[DnsmasqConfKeys.AddSubnet] = EffectiveConfigWriteBehavior.KeyOnlyOrValue,
[DnsmasqConfKeys.Umbrella] = EffectiveConfigWriteBehavior.KeyOnlyOrValue,
[DnsmasqConfKeys.Do0x20Encode] = EffectiveConfigWriteBehavior.InversePair,
};
/// <summary>For InversePair options: (key for "enabled", key for "disabled"). Null if not an InversePair or unknown.</summary>
private static readonly IReadOnlyDictionary<string, (string KeyA, string KeyB)> InversePairKeysByOptionName =
new Dictionary<string, (string, string)>(StringComparer.Ordinal)
{
[DnsmasqConfKeys.Do0x20Encode] = (DnsmasqConfKeys.Do0x20Encode, DnsmasqConfKeys.No0x20Encode),
};
/// <summary>Returns write behavior for the option; defaults to SingleValue if unknown.</summary>
public static EffectiveConfigWriteBehavior GetBehavior(string optionName) =>
BehaviorByOptionName.TryGetValue(optionName, out var b) ? b : EffectiveConfigWriteBehavior.SingleValue;
/// <summary>Returns the pair of config keys (enabled, disabled) for an InversePair option; null otherwise.</summary>
public static (string KeyA, string KeyB)? GetInversePairKeys(string optionName) =>
InversePairKeysByOptionName.TryGetValue(optionName, out var pair) ? pair : null;
}
@@ -67,6 +67,11 @@ public static class EffectiveConfigSections
DnsmasqConfKeys.FastDnsRetry,
DnsmasqConfKeys.Ipset,
DnsmasqConfKeys.Nftset,
DnsmasqConfKeys.ConnmarkAllowlistEnable,
DnsmasqConfKeys.NoRoundRobin,
DnsmasqConfKeys.DnsForwardMax,
DnsmasqConfKeys.ConnmarkAllowlist,
DnsmasqConfKeys.Do0x20Encode,
]),
new SectionDef(SectionDnsRecords, "DNS records", [
DnsmasqConfKeys.Domain,
@@ -79,6 +84,13 @@ public static class EffectiveConfigSections
DnsmasqConfKeys.HostRecord,
DnsmasqConfKeys.DynamicHost,
DnsmasqConfKeys.InterfaceName,
DnsmasqConfKeys.CaaRecord,
DnsmasqConfKeys.DnsRr,
DnsmasqConfKeys.SynthDomain,
DnsmasqConfKeys.AuthZone,
DnsmasqConfKeys.AuthSoa,
DnsmasqConfKeys.AuthSecServers,
DnsmasqConfKeys.AuthPeer,
DnsmasqConfKeys.MxTarget,
DnsmasqConfKeys.Localmx,
DnsmasqConfKeys.Selfmx,
@@ -87,7 +99,21 @@ public static class EffectiveConfigSections
DnsmasqConfKeys.DhcpAuthoritative,
DnsmasqConfKeys.DhcpRapidCommit,
DnsmasqConfKeys.LeasefileRo,
DnsmasqConfKeys.Leasequery,
DnsmasqConfKeys.DhcpGenerateNames,
DnsmasqConfKeys.DhcpBroadcast,
DnsmasqConfKeys.DhcpSequentialIp,
DnsmasqConfKeys.DhcpIgnoreClid,
DnsmasqConfKeys.BootpDynamic,
DnsmasqConfKeys.NoPing,
DnsmasqConfKeys.ScriptArp,
DnsmasqConfKeys.ScriptOnRenewal,
DnsmasqConfKeys.DhcpNoOverride,
DnsmasqConfKeys.DhcpScript,
DnsmasqConfKeys.DhcpAlternatePort,
DnsmasqConfKeys.DhcpDuid,
DnsmasqConfKeys.DhcpLuascript,
DnsmasqConfKeys.DhcpScriptuser,
DnsmasqConfKeys.DhcpLeasefile,
DnsmasqConfKeys.DhcpLeaseMax,
DnsmasqConfKeys.DhcpTtl,
@@ -106,6 +132,14 @@ public static class EffectiveConfigSections
DnsmasqConfKeys.DhcpIgnore,
DnsmasqConfKeys.DhcpVendorclass,
DnsmasqConfKeys.DhcpUserclass,
DnsmasqConfKeys.DhcpRelay,
DnsmasqConfKeys.DhcpCircuitid,
DnsmasqConfKeys.DhcpRemoteid,
DnsmasqConfKeys.DhcpSubscrid,
DnsmasqConfKeys.DhcpProxy,
DnsmasqConfKeys.TagIf,
DnsmasqConfKeys.BridgeInterface,
DnsmasqConfKeys.SharedNetwork,
DnsmasqConfKeys.RaParam,
DnsmasqConfKeys.Slaac,
]),
@@ -117,11 +151,18 @@ public static class EffectiveConfigSections
DnsmasqConfKeys.TftpRoot,
DnsmasqConfKeys.PxePrompt,
DnsmasqConfKeys.PxeService,
DnsmasqConfKeys.DhcpPxeVendor,
DnsmasqConfKeys.DhcpOptionPxe,
]),
new SectionDef(SectionDnssec, "DNSSEC", [
DnsmasqConfKeys.Dnssec,
DnsmasqConfKeys.DnssecCheckUnsigned,
DnsmasqConfKeys.DnssecNoTimecheck,
DnsmasqConfKeys.DnssecDebug,
DnsmasqConfKeys.ProxyDnssec,
DnsmasqConfKeys.AddCpeId,
DnsmasqConfKeys.DnssecTimestamp,
DnsmasqConfKeys.DnssecLimits,
DnsmasqConfKeys.TrustAnchor,
]),
new SectionDef(SectionCache, "Cache", [
@@ -133,6 +174,12 @@ public static class EffectiveConfigSections
DnsmasqConfKeys.MaxTtl,
DnsmasqConfKeys.MaxCacheTtl,
DnsmasqConfKeys.MinCacheTtl,
DnsmasqConfKeys.Dumpfile,
DnsmasqConfKeys.Dumpmask,
DnsmasqConfKeys.UseStaleCache,
DnsmasqConfKeys.AddMac,
DnsmasqConfKeys.AddSubnet,
DnsmasqConfKeys.Umbrella,
]),
new SectionDef(SectionProcess, "Process & networking", [
DnsmasqConfKeys.NoPoll,
@@ -159,6 +206,10 @@ public static class EffectiveConfigSections
DnsmasqConfKeys.KeepInForeground,
DnsmasqConfKeys.NoDaemon,
DnsmasqConfKeys.Conntrack,
DnsmasqConfKeys.QuietDhcp,
DnsmasqConfKeys.QuietDhcp6,
DnsmasqConfKeys.QuietRa,
DnsmasqConfKeys.QuietTftp,
]),
];
@@ -293,6 +293,23 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var dhcpHostsfilePaths = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpHostsfile);
var dhcpOptsfilePaths = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpOptsfile);
var dhcpHostsdirPaths = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpHostsdir);
var connmarkAllowlistValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.ConnmarkAllowlist);
var caaRecordValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.CaaRecord);
var dnsRrValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DnsRr);
var synthDomainValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.SynthDomain);
var authZoneValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.AuthZone);
var authSoaValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.AuthSoa);
var authSecServersValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.AuthSecServers);
var authPeerValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.AuthPeer);
var dhcpRelayValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpRelay);
var dhcpCircuitidValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpCircuitid);
var dhcpRemoteidValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpRemoteid);
var dhcpSubscridValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpSubscrid);
var dhcpProxyValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpProxy);
var tagIfValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.TagIf);
var bridgeInterfaceValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.BridgeInterface);
var sharedNetworkValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.SharedNetwork);
var dhcpOptionPxeValues = (IReadOnlyList<string>)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpOptionPxe);
var expandHosts = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.ExpandHosts);
var bogusPriv = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.BogusPriv);
@@ -330,6 +347,24 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var keepInForeground = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.KeepInForeground);
var noDaemon = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.NoDaemon);
var proxyDnssec = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.ProxyDnssec);
var connmarkAllowlistEnable = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.ConnmarkAllowlistEnable);
var noRoundRobin = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.NoRoundRobin);
var dnssecNoTimecheck = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DnssecNoTimecheck);
var dnssecDebug = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DnssecDebug);
var leasequery = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Leasequery);
var dhcpGenerateNames = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpGenerateNames);
var dhcpBroadcast = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpBroadcast);
var dhcpSequentialIp = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpSequentialIp);
var dhcpIgnoreClid = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpIgnoreClid);
var bootpDynamic = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.BootpDynamic);
var noPing = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.NoPing);
var scriptArp = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.ScriptArp);
var scriptOnRenewal = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.ScriptOnRenewal);
var dhcpNoOverride = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpNoOverride);
var quietDhcp = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.QuietDhcp);
var quietDhcp6 = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.QuietDhcp6);
var quietRa = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.QuietRa);
var quietTftp = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.QuietTftp);
var dhcpLeaseFilePath = DnsmasqConfIncludeParser.GetDhcpLeaseFilePathFromConfigFiles(paths, pathToLines);
@@ -391,6 +426,39 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var dhcpScriptPath = string.IsNullOrWhiteSpace(dhcpScriptVal) ? null : DnsmasqConfIncludeParser.ResolvePath(dhcpScriptVal?.Trim(), dhcpScriptDir);
var (mxTargetVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.MxTarget);
var mxTarget = string.IsNullOrWhiteSpace(mxTargetVal) ? null : mxTargetVal.Trim();
var (dnsForwardMaxVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DnsForwardMax);
var dnsForwardMax = TryParseInt(dnsForwardMaxVal);
var (dumpfileVal, dumpfileDir) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Dumpfile);
var dumpfilePath = string.IsNullOrWhiteSpace(dumpfileVal) ? null : DnsmasqConfIncludeParser.ResolvePath(dumpfileVal.Trim(), dumpfileDir);
var (dumpmaskVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Dumpmask);
var dumpmask = string.IsNullOrWhiteSpace(dumpmaskVal) ? null : dumpmaskVal.Trim();
var (addCpeIdVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.AddCpeId);
var addCpeId = string.IsNullOrWhiteSpace(addCpeIdVal) ? null : addCpeIdVal.Trim();
var (dnssecTimestampVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DnssecTimestamp);
var dnssecTimestamp = string.IsNullOrWhiteSpace(dnssecTimestampVal) ? null : dnssecTimestampVal.Trim();
var (dnssecLimitsVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DnssecLimits);
var dnssecLimits = string.IsNullOrWhiteSpace(dnssecLimitsVal) ? null : dnssecLimitsVal.Trim();
var (dhcpAlternatePortVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpAlternatePort);
var dhcpAlternatePort = string.IsNullOrWhiteSpace(dhcpAlternatePortVal) ? null : dhcpAlternatePortVal.Trim();
var (dhcpDuidVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpDuid);
var dhcpDuid = string.IsNullOrWhiteSpace(dhcpDuidVal) ? null : dhcpDuidVal.Trim();
var (dhcpLuascriptVal, dhcpLuascriptDir) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpLuascript);
var dhcpLuascriptPath = string.IsNullOrWhiteSpace(dhcpLuascriptVal) ? null : DnsmasqConfIncludeParser.ResolvePath(dhcpLuascriptVal?.Trim(), dhcpLuascriptDir);
var (dhcpScriptuserVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpScriptuser);
var dhcpScriptuser = string.IsNullOrWhiteSpace(dhcpScriptuserVal) ? null : dhcpScriptuserVal.Trim();
var (dhcpPxeVendorVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.DhcpPxeVendor);
var dhcpPxeVendor = string.IsNullOrWhiteSpace(dhcpPxeVendorVal) ? null : dhcpPxeVendorVal.Trim();
var (useStaleCacheVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.UseStaleCache);
var useStaleCache = useStaleCacheVal == null ? null : (string.IsNullOrWhiteSpace(useStaleCacheVal) ? "" : useStaleCacheVal.Trim());
var (addMacVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.AddMac);
var addMac = addMacVal == null ? null : (string.IsNullOrWhiteSpace(addMacVal) ? "" : addMacVal.Trim());
var (addSubnetVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.AddSubnet);
var addSubnet = addSubnetVal == null ? null : (string.IsNullOrWhiteSpace(addSubnetVal) ? "" : addSubnetVal.Trim());
var (umbrellaVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Umbrella);
var umbrella = umbrellaVal == null ? null : (string.IsNullOrWhiteSpace(umbrellaVal) ? "" : umbrellaVal.Trim());
var do0x20 = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Do0x20Encode);
var no0x20 = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.No0x20Encode);
var do0x20EncodeState = do0x20 ? ExplicitToggleState.Enabled : (no0x20 ? ExplicitToggleState.Disabled : ExplicitToggleState.Default);
var conntrack = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Conntrack);
return new EffectiveDnsmasqConfig(
@@ -401,14 +469,18 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
domainValues, cnameValues, mxHostValues, srvValues, ptrRecordValues, txtRecordValues, naptrRecordValues, hostRecordValues, dynamicHostValues, interfaceNameValues,
dhcpOptionForceLines, ipsetValues, nftsetValues, dhcpMacValues, dhcpNameMatchValues, dhcpIgnoreNamesValues,
dhcpHostsfilePaths, dhcpOptsfilePaths, dhcpHostsdirPaths,
connmarkAllowlistValues, caaRecordValues, dnsRrValues, synthDomainValues, authZoneValues, authSoaValues, authSecServersValues, authPeerValues,
dhcpRelayValues, dhcpCircuitidValues, dhcpRemoteidValues, dhcpSubscridValues, dhcpProxyValues, tagIfValues, bridgeInterfaceValues, sharedNetworkValues, dhcpOptionPxeValues,
expandHosts, bogusPriv, strictOrder, allServers, noResolv, domainNeeded, noPoll, bindInterfaces, bindDynamic, noNegcache,
dnsLoopDetect, stopDnsRebind, rebindLocalhostOk, clearOnReload, filterwin2k, filterA, filterAaaa, localiseQueries, logDebug, dhcpAuthoritative, leasefileRo,
enableTftp, tftpSecure, tftpNoFail, tftpNoBlocksize, dnssec, dnssecCheckUnsigned, readEthers, dhcpRapidCommit, localmx, selfmx, enableRa, logDhcp,
keepInForeground, noDaemon, proxyDnssec,
connmarkAllowlistEnable, noRoundRobin, dnssecNoTimecheck, dnssecDebug, leasequery, dhcpGenerateNames, dhcpBroadcast, dhcpSequentialIp, dhcpIgnoreClid, bootpDynamic, noPing, scriptArp, scriptOnRenewal, dhcpNoOverride, quietDhcp, quietDhcp6, quietRa, quietTftp,
dhcpLeaseFilePath, cacheSize, port, localTtl, pidFilePath, user, group, logFacility, logQueries,
authTtl, ednsPacketMax, queryPort, portLimit, minPort, maxPort, logAsync, localService, dhcpLeaseMax,
negTtl, maxTtl, maxCacheTtl, minCacheTtl, dhcpTtl, tftpRootPath, pxePrompt, enableDbus, enableUbus, fastDnsRetry,
dhcpScriptPath, mxTarget, conntrack
dhcpScriptPath, mxTarget, dnsForwardMax, dumpfilePath, dumpmask, addCpeId, dnssecTimestamp, dnssecLimits, dhcpAlternatePort, dhcpDuid, dhcpLuascriptPath, dhcpScriptuser, dhcpPxeVendor,
useStaleCache, addMac, addSubnet, umbrella, do0x20EncodeState, conntrack
);
}
@@ -466,6 +538,23 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var dhcpHostsfileWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpHostsfile, managedFilePath);
var dhcpOptsfileWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpOptsfile, managedFilePath);
var dhcpHostsdirWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpHostsdir, managedFilePath);
var connmarkAllowlistWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.ConnmarkAllowlist, managedFilePath);
var caaRecordWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.CaaRecord, managedFilePath);
var dnsRrWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DnsRr, managedFilePath);
var synthDomainWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.SynthDomain, managedFilePath);
var authZoneWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.AuthZone, managedFilePath);
var authSoaWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.AuthSoa, managedFilePath);
var authSecServersWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.AuthSecServers, managedFilePath);
var authPeerWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.AuthPeer, managedFilePath);
var dhcpRelayWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpRelay, managedFilePath);
var dhcpCircuitidWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpCircuitid, managedFilePath);
var dhcpRemoteidWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpRemoteid, managedFilePath);
var dhcpSubscridWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpSubscrid, managedFilePath);
var dhcpProxyWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpProxy, managedFilePath);
var tagIfWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.TagIf, managedFilePath);
var bridgeInterfaceWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.BridgeInterface, managedFilePath);
var sharedNetworkWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.SharedNetwork, managedFilePath);
var dhcpOptionPxeWithSource = (IReadOnlyList<(string Value, ConfigValueSource Source)>)ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpOptionPxe, managedFilePath);
var (_, expandHostsSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.ExpandHosts, managedFilePath);
var (_, bogusPrivSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.BogusPriv, managedFilePath);
@@ -503,6 +592,24 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var (_, keepInForegroundSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.KeepInForeground, managedFilePath);
var (_, noDaemonSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.NoDaemon, managedFilePath);
var (_, proxyDnssecSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.ProxyDnssec, managedFilePath);
var (_, connmarkAllowlistEnableSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.ConnmarkAllowlistEnable, managedFilePath);
var (_, noRoundRobinSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.NoRoundRobin, managedFilePath);
var (_, dnssecNoTimecheckSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DnssecNoTimecheck, managedFilePath);
var (_, dnssecDebugSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DnssecDebug, managedFilePath);
var (_, leasequerySource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Leasequery, managedFilePath);
var (_, dhcpGenerateNamesSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpGenerateNames, managedFilePath);
var (_, dhcpBroadcastSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpBroadcast, managedFilePath);
var (_, dhcpSequentialIpSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpSequentialIp, managedFilePath);
var (_, dhcpIgnoreClidSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpIgnoreClid, managedFilePath);
var (_, bootpDynamicSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.BootpDynamic, managedFilePath);
var (_, noPingSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.NoPing, managedFilePath);
var (_, scriptArpSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.ScriptArp, managedFilePath);
var (_, scriptOnRenewalSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.ScriptOnRenewal, managedFilePath);
var (_, dhcpNoOverrideSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpNoOverride, managedFilePath);
var (_, quietDhcpSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.QuietDhcp, managedFilePath);
var (_, quietDhcp6Source) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.QuietDhcp6, managedFilePath);
var (_, quietRaSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.QuietRa, managedFilePath);
var (_, quietTftpSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.QuietTftp, managedFilePath);
var (_, dhcpLeaseFilePathSource) = DnsmasqConfIncludeParser.GetDhcpLeaseFilePathFromConfigFilesWithSource(paths, pathToLines, managedFilePath);
var (_, cacheSizeSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.CacheSize, managedFilePath);
@@ -534,6 +641,24 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
var (_, fastDnsRetrySource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.FastDnsRetry, managedFilePath);
var (_, dhcpScriptPathSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpScript, managedFilePath);
var (_, mxTargetSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.MxTarget, managedFilePath);
var (_, dnsForwardMaxSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DnsForwardMax, managedFilePath);
var (_, dumpfilePathSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Dumpfile, managedFilePath);
var (_, dumpmaskSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Dumpmask, managedFilePath);
var (_, addCpeIdSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.AddCpeId, managedFilePath);
var (_, dnssecTimestampSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DnssecTimestamp, managedFilePath);
var (_, dnssecLimitsSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DnssecLimits, managedFilePath);
var (_, dhcpAlternatePortSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpAlternatePort, managedFilePath);
var (_, dhcpDuidSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpDuid, managedFilePath);
var (_, dhcpLuascriptPathSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpLuascript, managedFilePath);
var (_, dhcpScriptuserSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpScriptuser, managedFilePath);
var (_, dhcpPxeVendorSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpPxeVendor, managedFilePath);
var (_, useStaleCacheSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.UseStaleCache, managedFilePath);
var (_, addMacSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.AddMac, managedFilePath);
var (_, addSubnetSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.AddSubnet, managedFilePath);
var (_, umbrellaSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Umbrella, managedFilePath);
var (do0x20Set, do0x20EncodeSourceA) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Do0x20Encode, managedFilePath);
var (no0x20Set, no0x20EncodeSourceB) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.No0x20Encode, managedFilePath);
var do0x20EncodeSource = do0x20Set ? do0x20EncodeSourceA : (no0x20Set ? no0x20EncodeSourceB : null);
var (_, conntrackSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Conntrack, managedFilePath);
return new EffectiveConfigSources(
@@ -587,6 +712,23 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
dhcpHostsfileWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpOptsfileWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpHostsdirWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
connmarkAllowlistWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
caaRecordWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dnsRrWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
synthDomainWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
authZoneWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
authSoaWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
authSecServersWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
authPeerWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpRelayWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpCircuitidWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpRemoteidWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpSubscridWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpProxyWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
tagIfWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
bridgeInterfaceWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
sharedNetworkWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
dhcpOptionPxeWithSource.Select(t => new ValueWithSource(t.Value, t.Source)).ToList(),
expandHostsSource,
bogusPrivSource,
strictOrderSource,
@@ -623,6 +765,24 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
keepInForegroundSource,
noDaemonSource,
proxyDnssecSource,
connmarkAllowlistEnableSource,
noRoundRobinSource,
dnssecNoTimecheckSource,
dnssecDebugSource,
leasequerySource,
dhcpGenerateNamesSource,
dhcpBroadcastSource,
dhcpSequentialIpSource,
dhcpIgnoreClidSource,
bootpDynamicSource,
noPingSource,
scriptArpSource,
scriptOnRenewalSource,
dhcpNoOverrideSource,
quietDhcpSource,
quietDhcp6Source,
quietRaSource,
quietTftpSource,
dhcpLeaseFilePathSource,
cacheSizeSource,
portSource,
@@ -653,6 +813,22 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
fastDnsRetrySource,
dhcpScriptPathSource,
mxTargetSource,
dnsForwardMaxSource,
dumpfilePathSource,
dumpmaskSource,
addCpeIdSource,
dnssecTimestampSource,
dnssecLimitsSource,
dhcpAlternatePortSource,
dhcpDuidSource,
dhcpLuascriptPathSource,
dhcpScriptuserSource,
dhcpPxeVendorSource,
useStaleCacheSource,
addMacSource,
addSubnetSource,
umbrellaSource,
do0x20EncodeSource,
conntrackSource
);
}
@@ -707,17 +883,23 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
DhcpOptionForceLines: Array.Empty<string>(), IpsetValues: Array.Empty<string>(), NftsetValues: Array.Empty<string>(),
DhcpMacValues: Array.Empty<string>(), DhcpNameMatchValues: Array.Empty<string>(), DhcpIgnoreNamesValues: Array.Empty<string>(),
DhcpHostsfilePaths: Array.Empty<string>(), DhcpOptsfilePaths: Array.Empty<string>(), DhcpHostsdirPaths: Array.Empty<string>(),
ConnmarkAllowlistValues: Array.Empty<string>(), CaaRecordValues: Array.Empty<string>(), DnsRrValues: Array.Empty<string>(), SynthDomainValues: Array.Empty<string>(),
AuthZoneValues: Array.Empty<string>(), AuthSoaValues: Array.Empty<string>(), AuthSecServersValues: Array.Empty<string>(), AuthPeerValues: Array.Empty<string>(),
DhcpRelayValues: Array.Empty<string>(), DhcpCircuitidValues: Array.Empty<string>(), DhcpRemoteidValues: Array.Empty<string>(), DhcpSubscridValues: Array.Empty<string>(), DhcpProxyValues: Array.Empty<string>(),
TagIfValues: Array.Empty<string>(), BridgeInterfaceValues: Array.Empty<string>(), SharedNetworkValues: Array.Empty<string>(), DhcpOptionPxeValues: Array.Empty<string>(),
ExpandHosts: false, BogusPriv: false, StrictOrder: false, AllServers: false, NoResolv: false, DomainNeeded: false, NoPoll: false,
BindInterfaces: false, BindDynamic: false, NoNegcache: false, DnsLoopDetect: false, StopDnsRebind: false, RebindLocalhostOk: false, ClearOnReload: false,
Filterwin2k: false, FilterA: false, FilterAaaa: false, LocaliseQueries: false, LogDebug: false, DhcpAuthoritative: false, LeasefileRo: false,
EnableTftp: false, TftpSecure: false, TftpNoFail: false, TftpNoBlocksize: false, Dnssec: false, DnssecCheckUnsigned: false,
ReadEthers: false, DhcpRapidCommit: false, Localmx: false, Selfmx: false, EnableRa: false, LogDhcp: false,
KeepInForeground: false, NoDaemon: false, ProxyDnssec: false,
ConnmarkAllowlistEnable: false, NoRoundRobin: false, DnssecNoTimecheck: false, DnssecDebug: false, Leasequery: false, DhcpGenerateNames: false, DhcpBroadcast: false, DhcpSequentialIp: false, DhcpIgnoreClid: false, BootpDynamic: false, NoPing: false, ScriptArp: false, ScriptOnRenewal: false, DhcpNoOverride: false, QuietDhcp: false, QuietDhcp6: false, QuietRa: false, QuietTftp: false,
DhcpLeaseFilePath: null, CacheSize: null, Port: null, LocalTtl: null, PidFilePath: null, User: null, Group: null,
LogFacility: null, LogQueries: null, AuthTtl: null, EdnsPacketMax: null, QueryPort: null, PortLimit: null, MinPort: null, MaxPort: null, LogAsync: null, LocalService: null,
DhcpLeaseMax: null, NegTtl: null, MaxTtl: null, MaxCacheTtl: null, MinCacheTtl: null, DhcpTtl: null,
TftpRootPath: null, PxePrompt: null, EnableDbus: null, EnableUbus: null, FastDnsRetry: null,
DhcpScriptPath: null, MxTarget: null, Conntrack: false
DhcpScriptPath: null, MxTarget: null, DnsForwardMax: null, DumpfilePath: null, Dumpmask: null, AddCpeId: null, DnssecTimestamp: null, DnssecLimits: null, DhcpAlternatePort: null, DhcpDuid: null, DhcpLuascriptPath: null, DhcpScriptuser: null, DhcpPxeVendor: null,
UseStaleCache: null, AddMac: null, AddSubnet: null, Umbrella: null, Do0x20EncodeState: ExplicitToggleState.Default, Conntrack: false
);
private static EffectiveConfigSources CreateDefaultEffectiveConfigSources() =>
@@ -742,17 +924,23 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
DhcpOptionForceLines: Array.Empty<ValueWithSource>(), IpsetValues: Array.Empty<ValueWithSource>(), NftsetValues: Array.Empty<ValueWithSource>(),
DhcpMacValues: Array.Empty<ValueWithSource>(), DhcpNameMatchValues: Array.Empty<ValueWithSource>(), DhcpIgnoreNamesValues: Array.Empty<ValueWithSource>(),
DhcpHostsfilePaths: Array.Empty<ValueWithSource>(), DhcpOptsfilePaths: Array.Empty<ValueWithSource>(), DhcpHostsdirPaths: Array.Empty<ValueWithSource>(),
ConnmarkAllowlistValues: Array.Empty<ValueWithSource>(), CaaRecordValues: Array.Empty<ValueWithSource>(), DnsRrValues: Array.Empty<ValueWithSource>(), SynthDomainValues: Array.Empty<ValueWithSource>(),
AuthZoneValues: Array.Empty<ValueWithSource>(), AuthSoaValues: Array.Empty<ValueWithSource>(), AuthSecServersValues: Array.Empty<ValueWithSource>(), AuthPeerValues: Array.Empty<ValueWithSource>(),
DhcpRelayValues: Array.Empty<ValueWithSource>(), DhcpCircuitidValues: Array.Empty<ValueWithSource>(), DhcpRemoteidValues: Array.Empty<ValueWithSource>(), DhcpSubscridValues: Array.Empty<ValueWithSource>(), DhcpProxyValues: Array.Empty<ValueWithSource>(),
TagIfValues: Array.Empty<ValueWithSource>(), BridgeInterfaceValues: Array.Empty<ValueWithSource>(), SharedNetworkValues: Array.Empty<ValueWithSource>(), DhcpOptionPxeValues: Array.Empty<ValueWithSource>(),
ExpandHosts: null, BogusPriv: null, StrictOrder: null, AllServers: null, NoResolv: null, DomainNeeded: null, NoPoll: null,
BindInterfaces: null, BindDynamic: null, NoNegcache: null, DnsLoopDetect: null, StopDnsRebind: null, RebindLocalhostOk: null, ClearOnReload: null,
Filterwin2k: null, FilterA: null, FilterAaaa: null, LocaliseQueries: null, LogDebug: null, DhcpAuthoritative: null, LeasefileRo: null,
EnableTftp: null, TftpSecure: null, TftpNoFail: null, TftpNoBlocksize: null, Dnssec: null, DnssecCheckUnsigned: null,
ReadEthers: null, DhcpRapidCommit: null, Localmx: null, Selfmx: null, EnableRa: null, LogDhcp: null,
KeepInForeground: null, NoDaemon: null, ProxyDnssec: null,
ConnmarkAllowlistEnable: null, NoRoundRobin: null, DnssecNoTimecheck: null, DnssecDebug: null, Leasequery: null, DhcpGenerateNames: null, DhcpBroadcast: null, DhcpSequentialIp: null, DhcpIgnoreClid: null, BootpDynamic: null, NoPing: null, ScriptArp: null, ScriptOnRenewal: null, DhcpNoOverride: null, QuietDhcp: null, QuietDhcp6: null, QuietRa: null, QuietTftp: null,
DhcpLeaseFilePath: null, CacheSize: null, Port: null, LocalTtl: null, PidFilePath: null, User: null, Group: null,
LogFacility: null, LogQueries: null, AuthTtl: null, EdnsPacketMax: null, QueryPort: null, PortLimit: null, MinPort: null, MaxPort: null, LogAsync: null, LocalService: null,
DhcpLeaseMax: null, NegTtl: null, MaxTtl: null, MaxCacheTtl: null, MinCacheTtl: null, DhcpTtl: null,
TftpRootPath: null, PxePrompt: null, EnableDbus: null, EnableUbus: null, FastDnsRetry: null,
DhcpScriptPath: null, MxTarget: null, Conntrack: null
DhcpScriptPath: null, MxTarget: null, DnsForwardMax: null, DumpfilePath: null, Dumpmask: null, AddCpeId: null, DnssecTimestamp: null, DnssecLimits: null, DhcpAlternatePort: null, DhcpDuid: null, DhcpLuascriptPath: null, DhcpScriptuser: null, DhcpPxeVendor: null,
UseStaleCache: null, AddMac: null, AddSubnet: null, Umbrella: null, Do0x20Encode: null, Conntrack: null
);
private static int? TryParseInt(string? value)
@@ -231,8 +231,7 @@ public class DnsmasqConfigService : IDnsmasqConfigService
var maxLineNumber = list.Count > 0 ? list.Max(l => l.LineNumber) : 0;
foreach (var c in changes)
{
var behavior = EffectiveConfigParserBehaviorMap.GetBehavior(c.OptionName);
var isFlag = behavior == EffectiveConfigParserBehavior.Flag;
var writeBehavior = EffectiveConfigWriteBehaviorMap.GetBehavior(c.OptionName);
var confKey = c.OptionName;
bool MatchesOption(DnsmasqConfLine line)
@@ -242,6 +241,43 @@ public class DnsmasqConfigService : IDnsmasqConfigService
return kv != null && string.Equals(kv.Value.key, confKey, StringComparison.Ordinal);
}
if (writeBehavior == EffectiveConfigWriteBehavior.KeyOnlyOrValue)
{
RemoveAllMatchingLines(list, MatchesOption);
var value = c.NewValue as string;
if (value is null)
continue;
var lineText = value.Length == 0 ? confKey : $"{confKey}={value.Trim()}";
list.Add(new OtherLine { LineNumber = maxLineNumber + 1, RawLine = lineText });
maxLineNumber++;
continue;
}
if (writeBehavior == EffectiveConfigWriteBehavior.InversePair)
{
var pair = EffectiveConfigWriteBehaviorMap.GetInversePairKeys(confKey);
if (pair is null)
continue;
var (pairKeyA, pairKeyB) = pair.Value;
bool MatchesPair(DnsmasqConfLine line)
{
var raw = DnsmasqConfFileLineParser.ToLine(line);
var kv = DnsmasqConfDirectiveParser.TryParseKeyValue(raw);
return kv != null && (string.Equals(kv.Value.key, pairKeyA, StringComparison.Ordinal) || string.Equals(kv.Value.key, pairKeyB, StringComparison.Ordinal));
}
RemoveAllMatchingLines(list, MatchesPair);
if (c.NewValue is ExplicitToggleState state && state != ExplicitToggleState.Default)
{
var lineKey = state == ExplicitToggleState.Enabled ? pairKeyA : pairKeyB;
list.Add(new OtherLine { LineNumber = maxLineNumber + 1, RawLine = lineKey });
maxLineNumber++;
}
continue;
}
var behavior = EffectiveConfigParserBehaviorMap.GetBehavior(c.OptionName);
var isFlag = behavior == EffectiveConfigParserBehavior.Flag;
if (behavior == EffectiveConfigParserBehavior.Multi && TryGetMultiValues(c.NewValue, out var values))
{
IReadOnlyList<string> readonlyValues = readonlyByOption.TryGetValue(confKey, out var listValues) ? listValues : Array.Empty<string>();
@@ -299,6 +335,15 @@ public class DnsmasqConfigService : IDnsmasqConfigService
await WriteManagedConfigAsync(list, ct);
}
private static void RemoveAllMatchingLines(List<DnsmasqConfLine> list, Predicate<DnsmasqConfLine> match)
{
for (var i = list.Count - 1; i >= 0; i--)
{
if (match(list[i]))
list.RemoveAt(i);
}
}
/// <summary>
/// Keeps only values that should be written to managed config by subtracting values provided by
/// non-managed files (multiset subtraction, order preserved).
@@ -34,6 +34,7 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterInteger(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.PortLimit, max: 65535);
RegisterInteger(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.MinPort, min: 1, max: 65535);
RegisterInteger(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.MaxPort, min: 1, max: 65535);
RegisterInteger(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.DnsForwardMax, min: 1);
RegisterInteger(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.CacheSize, min: 0);
RegisterInteger(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.LocalTtl, unit: "seconds");
RegisterInteger(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.NegTtl, unit: "seconds");
@@ -82,6 +83,33 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.NoDaemon);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.Conntrack);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDnssec, DnsmasqConfKeys.ProxyDnssec);
RegisterFlag(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.ConnmarkAllowlistEnable);
RegisterFlag(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.NoRoundRobin);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDnssec, DnsmasqConfKeys.DnssecNoTimecheck);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDnssec, DnsmasqConfKeys.DnssecDebug);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.Leasequery);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpGenerateNames);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpBroadcast);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpSequentialIp);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpIgnoreClid);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.BootpDynamic);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.NoPing);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.ScriptArp);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.ScriptOnRenewal);
RegisterFlag(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpNoOverride);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.QuietDhcp);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.QuietDhcp6);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.QuietRa);
RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.QuietTftp);
// do-0x20-encode / no-0x20-encode: tri-state dropdown (Default / Enabled / Disabled).
RegisterComponent(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.Do0x20Encode, typeof(Do0x20EncodeDisplay));
// Key-only or key=value options: checkbox (On) + optional value input.
RegisterComponent(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.UseStaleCache, typeof(KeyOnlyOrValueDisplay));
RegisterComponent(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.AddMac, typeof(KeyOnlyOrValueDisplay));
RegisterComponent(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.AddSubnet, typeof(KeyOnlyOrValueDisplay));
RegisterComponent(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.Umbrella, typeof(KeyOnlyOrValueDisplay));
// log-queries: dropdown (Off / On / extra / proto / auth).
RegisterComponent(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.LogQueries, typeof(LogQueriesDisplay));
@@ -113,6 +141,7 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.FilterRr);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.Ipset);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.Nftset);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionResolver, DnsmasqConfKeys.ConnmarkAllowlist);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.Domain);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.Cname);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.MxHost);
@@ -123,6 +152,13 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.HostRecord);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.DynamicHost);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.InterfaceName);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.CaaRecord);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.DnsRr);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.SynthDomain);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.AuthZone);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.AuthSoa);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.AuthSecServers);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnsRecords, DnsmasqConfKeys.AuthPeer);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpRange);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpHost);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpOption);
@@ -134,6 +170,14 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpHostsfile);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpOptsfile);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpHostsdir);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpRelay);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpCircuitid);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpRemoteid);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpSubscrid);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpProxy);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.TagIf);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.BridgeInterface);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.SharedNetwork);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpBoot);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpIgnore);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.DhcpVendorclass);
@@ -141,6 +185,7 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.RaParam);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDhcp, DnsmasqConfKeys.Slaac);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionTftpPxe, DnsmasqConfKeys.PxeService);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionTftpPxe, DnsmasqConfKeys.DhcpOptionPxe);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionDnssec, DnsmasqConfKeys.TrustAnchor);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionCache, DnsmasqConfKeys.CacheRr);
RegisterMultiDescriptor(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.Interface);
@@ -19,7 +19,7 @@ public static class EffectiveConfigCrossOptionEvaluator
{
var issues = new List<FieldIssue>();
var noResolv = GetEffectiveNoResolv(status, pending);
var noResolv = GetEffectiveBool(status, pending, DnsmasqConfKeys.NoResolv, s => s?.EffectiveConfig?.NoResolv ?? false);
var serverValues = GetEffectiveServerValues(status, pending);
// no-resolv set with no upstream servers: DNS may not work
@@ -33,17 +33,51 @@ public static class EffectiveConfigCrossOptionEvaluator
ItemIndex: null));
}
// conntrack cannot be combined with query-port (dnsmasq man page)
var conntrack = GetEffectiveBool(status, pending, DnsmasqConfKeys.Conntrack, s => s?.EffectiveConfig?.Conntrack ?? false);
var queryPort = GetEffectiveInt(status, pending, DnsmasqConfKeys.QueryPort, s => s?.EffectiveConfig?.QueryPort);
if (conntrack && queryPort is not null)
{
issues.Add(new FieldIssue(
$"{EffectiveConfigSections.SectionResolver}:{DnsmasqConfKeys.QueryPort}",
"query-port cannot be combined with conntrack.",
FieldIssueSeverity.Error,
null));
}
return issues;
}
private static bool GetEffectiveNoResolv(DnsmasqServiceStatus? status, IReadOnlyList<PendingEffectiveConfigChange> pending)
private static bool GetEffectiveBool(
DnsmasqServiceStatus? status,
IReadOnlyList<PendingEffectiveConfigChange>? pending,
string optionName,
Func<DnsmasqServiceStatus?, bool> fromConfig)
{
var fromConfig = status?.EffectiveConfig?.NoResolv ?? false;
var pendingChange = pending?.FirstOrDefault(c =>
string.Equals(c.OptionName, DnsmasqConfKeys.NoResolv, StringComparison.Ordinal));
if (pendingChange?.NewValue is bool b)
return b;
return fromConfig;
var pendingChange = pending?.FirstOrDefault(c => string.Equals(c.OptionName, optionName, StringComparison.Ordinal));
if (pendingChange != null)
{
if (pendingChange.NewValue is bool b)
return b;
return false; // pending change to clear or invalid; treat as off for cross-option purposes
}
return fromConfig(status);
}
private static int? GetEffectiveInt(
DnsmasqServiceStatus? status,
IReadOnlyList<PendingEffectiveConfigChange>? pending,
string optionName,
Func<DnsmasqServiceStatus?, int?> fromConfig)
{
var pendingChange = pending?.FirstOrDefault(c => string.Equals(c.OptionName, optionName, StringComparison.Ordinal));
if (pendingChange != null)
{
if (pendingChange.NewValue is int i)
return i;
return null; // pending change to clear the value; use null so conntrack+query-port rule no longer blocks
}
return fromConfig(status);
}
private static IReadOnlyList<string>? GetEffectiveServerValues(DnsmasqServiceStatus? status, IReadOnlyList<PendingEffectiveConfigChange> pending)