From 6f89326acdbe5983bc2d932d2f0225871a458e12 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sat, 12 Nov 2022 14:57:08 +0530 Subject: [PATCH] Scope: implemented domain search and CAPWAPAP option support. Added option to disable dns updates. Added feature to support using domain names for NTP option. Added missing validation checks in properties. --- DnsServerCore/Dhcp/Scope.cs | 291 +++++++++++++++++++++++++++++++----- 1 file changed, 254 insertions(+), 37 deletions(-) diff --git a/DnsServerCore/Dhcp/Scope.cs b/DnsServerCore/Dhcp/Scope.cs index f57be868..f3c5e3b0 100644 --- a/DnsServerCore/Dhcp/Scope.cs +++ b/DnsServerCore/Dhcp/Scope.cs @@ -18,6 +18,7 @@ along with this program. If not, see . */ using DnsServerCore.Dhcp.Options; +using DnsServerCore.Dns; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -33,6 +34,7 @@ using TechnitiumLibrary; using TechnitiumLibrary.IO; using TechnitiumLibrary.Net; using TechnitiumLibrary.Net.Dns; +using TechnitiumLibrary.Net.Dns.ResourceRecords; namespace DnsServerCore.Dhcp { @@ -59,6 +61,8 @@ namespace DnsServerCore.Dhcp //dhcp options string _domainName; + IReadOnlyCollection _domainSearchList; + bool _dnsUpdates = true; uint _dnsTtl = 900; IPAddress _serverAddress; string _serverHostName; @@ -68,8 +72,10 @@ namespace DnsServerCore.Dhcp IReadOnlyCollection _dnsServers; IReadOnlyCollection _winsServers; IReadOnlyCollection _ntpServers; + IReadOnlyCollection _ntpServerDomainNames; IReadOnlyCollection _staticRoutes; IReadOnlyDictionary _vendorInfo; + IReadOnlyCollection _capwapAcIpAddresses; //advanced options IReadOnlyCollection _exclusions; @@ -123,6 +129,7 @@ namespace DnsServerCore.Dhcp case 4: case 5: case 6: + case 7: _name = bR.ReadShortString(); _enabled = bR.ReadBoolean(); @@ -145,6 +152,22 @@ namespace DnsServerCore.Dhcp if (string.IsNullOrWhiteSpace(_domainName)) _domainName = null; + if (version >= 7) + { + int count = bR.ReadByte(); + if (count > 0) + { + string[] domainSearchStrings = new string[count]; + + for (int i = 0; i < count; i++) + domainSearchStrings[i] = bR.ReadShortString(); + + _domainSearchList = domainSearchStrings; + } + + _dnsUpdates = bR.ReadBoolean(); + } + _dnsTtl = bR.ReadUInt32(); if (version >= 2) @@ -216,6 +239,20 @@ namespace DnsServerCore.Dhcp } } + if (version >= 7) + { + int count = bR.ReadByte(); + if (count > 0) + { + string[] ntpServerDomainNames = new string[count]; + + for (int i = 0; i < count; i++) + ntpServerDomainNames[i] = bR.ReadShortString(); + + _ntpServerDomainNames = ntpServerDomainNames; + } + } + { int count = bR.ReadByte(); if (count > 0) @@ -248,6 +285,20 @@ namespace DnsServerCore.Dhcp } } + if (version >= 7) + { + int count = bR.ReadByte(); + if (count > 0) + { + IPAddress[] capwapAcIpAddresses = new IPAddress[count]; + + for (int i = 0; i < count; i++) + capwapAcIpAddresses[i] = IPAddressExtension.ReadFrom(bR); + + _capwapAcIpAddresses = capwapAcIpAddresses; + } + } + { int count = bR.ReadByte(); if (count > 0) @@ -323,7 +374,7 @@ namespace DnsServerCore.Dhcp #region static - public static void ValidateScopeName(string name) + internal static void ValidateScopeName(string name) { foreach (char invalidChar in Path.GetInvalidFileNameChars()) { @@ -332,7 +383,7 @@ namespace DnsServerCore.Dhcp } } - public static bool IsAddressInRange(IPAddress address, IPAddress startingAddress, IPAddress endingAddress) + private static bool IsAddressInRange(IPAddress address, IPAddress startingAddress, IPAddress endingAddress) { uint addressNumber = address.ConvertIpToNumber(); uint startingAddressNumber = startingAddress.ConvertIpToNumber(); @@ -341,6 +392,24 @@ namespace DnsServerCore.Dhcp return (startingAddressNumber <= addressNumber) && (addressNumber <= endingAddressNumber); } + private static void ValidateIpv4(IReadOnlyCollection value, string paramName) + { + if (value is not null) + { + foreach (IPAddress ip in value) + { + if (ip.AddressFamily != AddressFamily.InterNetwork) + throw new ArgumentException("The address must be an IPv4 address: " + ip.ToString(), paramName); + } + } + } + + private static void ValidateIpv4(IPAddress value, string paramName) + { + if ((value is not null) && (value.AddressFamily != AddressFamily.InterNetwork)) + throw new ArgumentException("The address must be an IPv4 address: " + value.ToString(), paramName); + } + #endregion #region private @@ -463,7 +532,7 @@ namespace DnsServerCore.Dhcp else if (string.IsNullOrWhiteSpace(request.ClientFullyQualifiedDomainName.DomainName)) { //client domain empty and expects server for a fqdn domain name - if (request.HostName == null) + if (request.HostName is null) return null; //server unable to decide a name for client clientDomainName = request.HostName.HostName + "." + _domainName; @@ -856,7 +925,7 @@ namespace DnsServerCore.Dhcp } } - if (offerAddress == null) + if (offerAddress is null) { await _lastAddressOfferedLock.WaitAsync(); try @@ -919,7 +988,7 @@ namespace DnsServerCore.Dhcp return null; } - internal List GetOptions(DhcpMessage request, IPAddress serverIdentifierAddress, string overrideClientDomainName) + internal async Task> GetOptionsAsync(DhcpMessage request, IPAddress serverIdentifierAddress, string overrideClientDomainName, DnsServer dnsServer) { List options = new List(); @@ -952,7 +1021,7 @@ namespace DnsServerCore.Dhcp break; } - if (request.ParameterRequestList == null) + if (request.ParameterRequestList is null) { options.Add(new SubnetMaskOption(_subnetMask)); options.Add(new BroadcastAddressOption(_broadcastAddress)); @@ -965,19 +1034,22 @@ namespace DnsServerCore.Dhcp options.Add(GetClientFullyQualifiedDomainNameOption(request, overrideClientDomainName)); } - if (_routerAddress != null) + if (_domainSearchList is not null) + options.Add(new DomainSearchOption(_domainSearchList)); + + if (_routerAddress is not null) options.Add(new RouterOption(new IPAddress[] { _routerAddress })); - if (_dnsServers != null) + if (_dnsServers is not null) options.Add(new DomainNameServerOption(_dnsServers)); - if (_winsServers != null) + if (_winsServers is not null) options.Add(new NetBiosNameServerOption(_winsServers)); - if (_ntpServers != null) - options.Add(new NetworkTimeProtocolServersOption(_ntpServers)); + if ((_ntpServers is not null) || (_ntpServerDomainNames is not null)) + options.Add(await GetNetworkTimeProtocolServersOptionAsync(dnsServer)); - if (_staticRoutes != null) + if (_staticRoutes is not null) options.Add(new ClasslessStaticRouteOption(_staticRoutes)); } else @@ -1002,40 +1074,52 @@ namespace DnsServerCore.Dhcp break; + case DhcpOptionCode.DomainSearch: + if (_domainSearchList is not null) + options.Add(new DomainSearchOption(_domainSearchList)); + + break; + case DhcpOptionCode.Router: - if (_routerAddress != null) + if (_routerAddress is not null) options.Add(new RouterOption(new IPAddress[] { _routerAddress })); break; case DhcpOptionCode.DomainNameServer: - if (_dnsServers != null) + if (_dnsServers is not null) options.Add(new DomainNameServerOption(_dnsServers)); break; case DhcpOptionCode.NetBiosOverTcpIpNameServer: - if (_winsServers != null) + if (_winsServers is not null) options.Add(new NetBiosNameServerOption(_winsServers)); break; case DhcpOptionCode.NetworkTimeProtocolServers: - if (_ntpServers != null) - options.Add(new NetworkTimeProtocolServersOption(_ntpServers)); + if ((_ntpServers is not null) || (_ntpServerDomainNames is not null)) + options.Add(await GetNetworkTimeProtocolServersOptionAsync(dnsServer)); break; case DhcpOptionCode.ClasslessStaticRoute: - if (_staticRoutes != null) + if (_staticRoutes is not null) options.Add(new ClasslessStaticRouteOption(_staticRoutes)); break; + + case DhcpOptionCode.CAPWAPAccessControllerAddresses: + if (_capwapAcIpAddresses is not null) + options.Add(new CAPWAPAccessControllerOption(_capwapAcIpAddresses)); + + break; } } } - if ((_vendorInfo != null) && (request.VendorClassIdentifier != null)) + if ((_vendorInfo is not null) && (request.VendorClassIdentifier is not null)) { if (_vendorInfo.TryGetValue(request.VendorClassIdentifier.Identifier, out VendorSpecificInformationOption vendorSpecificInformationOption) || _vendorInfo.TryGetValue("", out vendorSpecificInformationOption)) { @@ -1092,6 +1176,39 @@ namespace DnsServerCore.Dhcp return options; } + private async Task GetNetworkTimeProtocolServersOptionAsync(DnsServer dnsServer) + { + if (_ntpServerDomainNames is not null) + { + Task[] tasks = new Task[_ntpServerDomainNames.Count]; + int i = 0; + + foreach (string ntpServerDomainName in _ntpServerDomainNames) + tasks[i++] = dnsServer.DirectQueryAsync(new DnsQuestionRecord(ntpServerDomainName, DnsResourceRecordType.A, DnsClass.IN), 1000); + + List ntpServers = new List(_ntpServerDomainNames.Count + (_ntpServers is null ? 0 : _ntpServers.Count)); + + if (_ntpServers is not null) + ntpServers.AddRange(_ntpServers); + + foreach (Task task in tasks) + { + try + { + ntpServers.AddRange(DnsClient.ParseResponseA(await task)); + } + catch + { } + } + + return new NetworkTimeProtocolServersOption(ntpServers); + } + else + { + return new NetworkTimeProtocolServersOption(_ntpServers); + } + } + internal void CommitLease(Lease lease) { lease.ExtendLease(GetLeaseTime()); @@ -1162,13 +1279,13 @@ namespace DnsServerCore.Dhcp public void ChangeNetwork(IPAddress startingAddress, IPAddress endingAddress, IPAddress subnetMask) { if (startingAddress.AddressFamily != AddressFamily.InterNetwork) - throw new ArgumentException("Address family not supported.", nameof(startingAddress)); + throw new ArgumentException("The address must be an IPv4 address: " + startingAddress.ToString(), nameof(startingAddress)); if (endingAddress.AddressFamily != AddressFamily.InterNetwork) - throw new ArgumentException("Address family not supported.", nameof(endingAddress)); + throw new ArgumentException("The address must be an IPv4 address: " + endingAddress.ToString(), nameof(endingAddress)); if (subnetMask.AddressFamily != AddressFamily.InterNetwork) - throw new ArgumentException("Address family not supported.", nameof(subnetMask)); + throw new ArgumentException("The address must be an IPv4 address: " + subnetMask.ToString(), nameof(subnetMask)); uint startingAddressNumber = startingAddress.ConvertIpToNumber(); uint endingAddressNumber = endingAddress.ConvertIpToNumber(); @@ -1306,7 +1423,7 @@ namespace DnsServerCore.Dhcp public void WriteTo(BinaryWriter bW) { bW.Write(Encoding.ASCII.GetBytes("SC")); - bW.Write((byte)6); //version + bW.Write((byte)7); //version bW.WriteShortString(_name); bW.Write(_enabled); @@ -1327,9 +1444,22 @@ namespace DnsServerCore.Dhcp else bW.WriteShortString(_domainName); + if (_domainSearchList is null) + { + bW.Write((byte)0); + } + else + { + bW.Write(Convert.ToByte(_domainSearchList.Count)); + + foreach (string domainSearchString in _domainSearchList) + bW.WriteShortString(domainSearchString); + } + + bW.Write(_dnsUpdates); bW.Write(_dnsTtl); - if (_serverAddress == null) + if (_serverAddress is null) IPAddress.Any.WriteTo(bW); else _serverAddress.WriteTo(bW); @@ -1344,7 +1474,7 @@ namespace DnsServerCore.Dhcp else bW.WriteShortString(_bootFileName); - if (_routerAddress == null) + if (_routerAddress is null) IPAddress.Any.WriteTo(bW); else _routerAddress.WriteTo(bW); @@ -1353,7 +1483,7 @@ namespace DnsServerCore.Dhcp { bW.Write((byte)255); } - else if (_dnsServers == null) + else if (_dnsServers is null) { bW.Write((byte)0); } @@ -1365,7 +1495,7 @@ namespace DnsServerCore.Dhcp dnsServer.WriteTo(bW); } - if (_winsServers == null) + if (_winsServers is null) { bW.Write((byte)0); } @@ -1377,7 +1507,7 @@ namespace DnsServerCore.Dhcp winsServer.WriteTo(bW); } - if (_ntpServers == null) + if (_ntpServers is null) { bW.Write((byte)0); } @@ -1389,7 +1519,19 @@ namespace DnsServerCore.Dhcp ntpServer.WriteTo(bW); } - if (_staticRoutes == null) + if (_ntpServerDomainNames is null) + { + bW.Write((byte)0); + } + else + { + bW.Write(Convert.ToByte(_ntpServerDomainNames.Count)); + + foreach (string ntpServerDomainName in _ntpServerDomainNames) + bW.WriteShortString(ntpServerDomainName); + } + + if (_staticRoutes is null) { bW.Write((byte)0); } @@ -1401,7 +1543,7 @@ namespace DnsServerCore.Dhcp route.WriteTo(bW.BaseStream); } - if (_vendorInfo == null) + if (_vendorInfo is null) { bW.Write((byte)0); } @@ -1416,7 +1558,19 @@ namespace DnsServerCore.Dhcp } } - if (_exclusions == null) + if (_capwapAcIpAddresses is null) + { + bW.Write((byte)0); + } + else + { + bW.Write(Convert.ToByte(_capwapAcIpAddresses.Count)); + + foreach (IPAddress capwapAcIpAddress in _capwapAcIpAddresses) + capwapAcIpAddress.WriteTo(bW); + } + + if (_exclusions is null) { bW.Write((byte)0); } @@ -1585,6 +1739,27 @@ namespace DnsServerCore.Dhcp } } + public IReadOnlyCollection DomainSearchList + { + get { return _domainSearchList; } + set + { + if (value is not null) + { + foreach (string domainSearchString in value) + DnsClient.IsDomainNameValid(domainSearchString, true); + } + + _domainSearchList = value; + } + } + + public bool DnsUpdates + { + get { return _dnsUpdates; } + set { _dnsUpdates = value; } + } + public uint DnsTtl { get { return _dnsTtl; } @@ -1594,7 +1769,11 @@ namespace DnsServerCore.Dhcp public IPAddress ServerAddress { get { return _serverAddress; } - set { _serverAddress = value; } + set + { + ValidateIpv4(value, nameof(ServerAddress)); + _serverAddress = value; + } } public string ServerHostName @@ -1624,7 +1803,11 @@ namespace DnsServerCore.Dhcp public IPAddress RouterAddress { get { return _routerAddress; } - set { _routerAddress = value; } + set + { + ValidateIpv4(value, nameof(RouterAddress)); + _routerAddress = value; + } } public bool UseThisDnsServer @@ -1644,6 +1827,7 @@ namespace DnsServerCore.Dhcp get { return _dnsServers; } set { + ValidateIpv4(value, nameof(DnsServers)); _dnsServers = value; if ((_dnsServers != null) && _dnsServers.Count > 0) @@ -1654,13 +1838,36 @@ namespace DnsServerCore.Dhcp public IReadOnlyCollection WinsServers { get { return _winsServers; } - set { _winsServers = value; } + set + { + ValidateIpv4(value, nameof(WinsServers)); + _winsServers = value; + } } public IReadOnlyCollection NtpServers { get { return _ntpServers; } - set { _ntpServers = value; } + set + { + ValidateIpv4(value, nameof(NtpServers)); + _ntpServers = value; + } + } + + public IReadOnlyCollection NtpServerDomainNames + { + get { return _ntpServerDomainNames; } + set + { + if (value is not null) + { + foreach (string ntpServerDomainName in value) + DnsClient.IsDomainNameValid(ntpServerDomainName, true); + } + + _ntpServerDomainNames = value; + } } public IReadOnlyCollection StaticRoutes @@ -1675,12 +1882,22 @@ namespace DnsServerCore.Dhcp set { _vendorInfo = value; } } + public IReadOnlyCollection CAPWAPAcIpAddresses + { + get { return _capwapAcIpAddresses; } + set + { + ValidateIpv4(value, nameof(CAPWAPAcIpAddresses)); + _capwapAcIpAddresses = value; + } + } + public IReadOnlyCollection Exclusions { get { return _exclusions; } set { - if (value == null) + if (value is null) { _exclusions = null; } @@ -1714,7 +1931,7 @@ namespace DnsServerCore.Dhcp } set { - if (value == null) + if (value is null) { _reservedLeases.Clear(); }