12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229 |
- /*
- Technitium DNS Server
- Copyright (C) 2020 Shreyas Zare (shreyas@technitium.com)
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- using DnsServerCore.Dhcp.Options;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Net.NetworkInformation;
- using System.Net.Sockets;
- using System.Text;
- using TechnitiumLibrary.IO;
- using TechnitiumLibrary.Net;
- using TechnitiumLibrary.Net.Dns;
- namespace DnsServerCore.Dhcp
- {
- public class Scope : IComparable<Scope>
- {
- #region variables
- //required parameters
- string _name;
- bool _enabled;
- IPAddress _startingAddress;
- IPAddress _endingAddress;
- IPAddress _subnetMask;
- ushort _leaseTimeDays = 1; //default 1 day lease
- byte _leaseTimeHours = 0;
- byte _leaseTimeMinutes = 0;
- ushort _offerDelayTime;
- //dhcp options
- string _domainName;
- uint _dnsTtl = 900;
- IPAddress _routerAddress;
- bool _useThisDnsServer;
- ICollection<IPAddress> _dnsServers;
- ICollection<IPAddress> _winsServers;
- ICollection<IPAddress> _ntpServers;
- ICollection<ClasslessStaticRouteOption.Route> _staticRoutes;
- //advanced options
- ICollection<Exclusion> _exclusions;
- readonly ConcurrentDictionary<ClientIdentifierOption, Lease> _reservedLeases = new ConcurrentDictionary<ClientIdentifierOption, Lease>();
- bool _allowOnlyReservedLeases;
- //leases
- readonly ConcurrentDictionary<ClientIdentifierOption, Lease> _leases = new ConcurrentDictionary<ClientIdentifierOption, Lease>();
- //internal computed parameters
- IPAddress _networkAddress;
- IPAddress _broadcastAddress;
- //internal parameters
- const int OFFER_EXPIRY_SECONDS = 60; //1 mins offer expiry
- readonly ConcurrentDictionary<ClientIdentifierOption, Lease> _offers = new ConcurrentDictionary<ClientIdentifierOption, Lease>();
- IPAddress _lastAddressOffered;
- readonly object _lastAddressOfferedLock = new object();
- IPAddress _interfaceAddress;
- int _interfaceIndex;
- DateTime _lastModified = DateTime.UtcNow;
- #endregion
- #region constructor
- public Scope(string name, bool enabled, IPAddress startingAddress, IPAddress endingAddress, IPAddress subnetMask)
- {
- _name = name;
- _enabled = enabled;
- ChangeNetwork(startingAddress, endingAddress, subnetMask);
- }
- public Scope(BinaryReader bR)
- {
- if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "SC")
- throw new InvalidDataException("DhcpServer scope file format is invalid.");
- switch (bR.ReadByte())
- {
- case 1:
- _name = bR.ReadShortString();
- _enabled = bR.ReadBoolean();
- ChangeNetwork(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR));
- _leaseTimeDays = bR.ReadUInt16();
- _leaseTimeHours = bR.ReadByte();
- _leaseTimeMinutes = bR.ReadByte();
- _offerDelayTime = bR.ReadUInt16();
- _domainName = bR.ReadShortString();
- if (string.IsNullOrWhiteSpace(_domainName))
- _domainName = null;
- _dnsTtl = bR.ReadUInt32();
- _routerAddress = IPAddressExtension.Parse(bR);
- if (_routerAddress.Equals(IPAddress.Any))
- _routerAddress = null;
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- if (count == 255)
- {
- _useThisDnsServer = true;
- FindThisDnsServerAddress();
- }
- else
- {
- IPAddress[] dnsServers = new IPAddress[count];
- for (int i = 0; i < count; i++)
- dnsServers[i] = IPAddressExtension.Parse(bR);
- _dnsServers = dnsServers;
- }
- }
- }
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- IPAddress[] winsServers = new IPAddress[count];
- for (int i = 0; i < count; i++)
- winsServers[i] = IPAddressExtension.Parse(bR);
- _winsServers = winsServers;
- }
- }
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- IPAddress[] ntpServers = new IPAddress[count];
- for (int i = 0; i < count; i++)
- ntpServers[i] = IPAddressExtension.Parse(bR);
- _ntpServers = ntpServers;
- }
- }
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- ClasslessStaticRouteOption.Route[] staticRoutes = new ClasslessStaticRouteOption.Route[count];
- for (int i = 0; i < count; i++)
- staticRoutes[i] = new ClasslessStaticRouteOption.Route(bR.BaseStream);
- _staticRoutes = staticRoutes;
- }
- }
- {
- int count = bR.ReadByte();
- if (count > 0)
- {
- Exclusion[] exclusions = new Exclusion[count];
- for (int i = 0; i < count; i++)
- exclusions[i] = new Exclusion(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR));
- _exclusions = exclusions;
- }
- }
- {
- int count = bR.ReadInt32();
- if (count > 0)
- {
- for (int i = 0; i < count; i++)
- {
- Lease reservedLease = new Lease(bR);
- _reservedLeases.TryAdd(reservedLease.ClientIdentifier, reservedLease);
- }
- }
- _allowOnlyReservedLeases = bR.ReadBoolean();
- }
- {
- int count = bR.ReadInt32();
- if (count > 0)
- {
- for (int i = 0; i < count; i++)
- {
- Lease lease = new Lease(bR);
- _leases.TryAdd(lease.ClientIdentifier, lease);
- }
- }
- }
- break;
- default:
- throw new InvalidDataException("Scope data format version not supported.");
- }
- }
- #endregion
- #region static
- public static bool IsAddressInRange(IPAddress address, IPAddress startingAddress, IPAddress endingAddress)
- {
- uint addressNumber = address.ConvertIpToNumber();
- uint startingAddressNumber = startingAddress.ConvertIpToNumber();
- uint endingAddressNumber = endingAddress.ConvertIpToNumber();
- return (startingAddressNumber <= addressNumber) && (addressNumber <= endingAddressNumber);
- }
- #endregion
- #region private
- private uint GetLeaseTime()
- {
- return Convert.ToUInt32((_leaseTimeDays * 24 * 60 * 60) + (_leaseTimeHours * 60 * 60) + (_leaseTimeMinutes * 60));
- }
- private bool IsAddressAvailable(ref IPAddress address)
- {
- if (address.Equals(_routerAddress))
- return false;
- if ((_dnsServers != null) && _dnsServers.Contains(address))
- return false;
- if ((_winsServers != null) && _winsServers.Contains(address))
- return false;
- if ((_ntpServers != null) && _ntpServers.Contains(address))
- return false;
- if (_exclusions != null)
- {
- foreach (Exclusion exclusion in _exclusions)
- {
- if (IsAddressInRange(address, exclusion.StartingAddress, exclusion.EndingAddress))
- {
- address = exclusion.EndingAddress;
- return false;
- }
- }
- }
- foreach (Lease reservedLease in _reservedLeases.Values)
- {
- if (address.Equals(reservedLease.Address))
- return false;
- }
- foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
- {
- if (address.Equals(lease.Value.Address))
- return false;
- }
- foreach (KeyValuePair<ClientIdentifierOption, Lease> offer in _offers)
- {
- if (address.Equals(offer.Value.Address))
- return false;
- }
- return true;
- }
- private bool IsAddressAlreadyAllocated(Lease reservedLease)
- {
- foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
- {
- if (reservedLease.Address.Equals(lease.Value.Address))
- return !lease.Key.Equals(reservedLease.ClientIdentifier);
- }
- foreach (KeyValuePair<ClientIdentifierOption, Lease> offer in _offers)
- {
- if (reservedLease.Address.Equals(offer.Value.Address))
- return !offer.Key.Equals(reservedLease.ClientIdentifier);
- }
- return false;
- }
- private ClientFullyQualifiedDomainNameOption GetClientFullyQualifiedDomainNameOption(DhcpMessage request)
- {
- ClientFullyQualifiedDomainNameFlags responseFlags = ClientFullyQualifiedDomainNameFlags.None;
- if (request.ClientFullyQualifiedDomainName.Flags.HasFlag(ClientFullyQualifiedDomainNameFlags.EncodeUsingCanonicalWireFormat))
- responseFlags |= ClientFullyQualifiedDomainNameFlags.EncodeUsingCanonicalWireFormat;
- if (request.ClientFullyQualifiedDomainName.Flags.HasFlag(ClientFullyQualifiedDomainNameFlags.NoDnsUpdate))
- {
- responseFlags |= ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns;
- responseFlags |= ClientFullyQualifiedDomainNameFlags.OverrideByServer;
- }
- else if (request.ClientFullyQualifiedDomainName.Flags.HasFlag(ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns))
- {
- responseFlags |= ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns;
- }
- else
- {
- responseFlags |= ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns;
- responseFlags |= ClientFullyQualifiedDomainNameFlags.OverrideByServer;
- }
- string clientDomainName;
- if (string.IsNullOrWhiteSpace(request.ClientFullyQualifiedDomainName.DomainName))
- {
- //client domain empty and expects server for a fqdn domain name
- if (request.HostName == null)
- return null; //server unable to decide a name for client
- clientDomainName = request.HostName.HostName + "." + _domainName;
- }
- else if (request.ClientFullyQualifiedDomainName.DomainName.Contains("."))
- {
- //client domain is fqdn
- if (request.ClientFullyQualifiedDomainName.DomainName.EndsWith("." + _domainName, StringComparison.OrdinalIgnoreCase))
- {
- clientDomainName = request.ClientFullyQualifiedDomainName.DomainName;
- }
- else
- {
- string[] parts = request.ClientFullyQualifiedDomainName.DomainName.Split('.');
- clientDomainName = parts[0] + "." + _domainName;
- }
- }
- else
- {
- //client domain is just hostname
- clientDomainName = request.ClientFullyQualifiedDomainName.DomainName + "." + _domainName;
- }
- return new ClientFullyQualifiedDomainNameOption(responseFlags, 255, 255, clientDomainName);
- }
- #endregion
- #region internal
- internal bool FindInterface()
- {
- //find network with static ip address in scope range
- uint networkAddressNumber = _networkAddress.ConvertIpToNumber();
- uint subnetMaskNumber = _subnetMask.ConvertIpToNumber();
- foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
- {
- if (nic.OperationalStatus != OperationalStatus.Up)
- continue;
- IPInterfaceProperties ipInterface = nic.GetIPProperties();
- foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
- {
- if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- uint addressNumber = ip.Address.ConvertIpToNumber();
- if ((addressNumber & subnetMaskNumber) == networkAddressNumber)
- {
- //found interface for this scope range
- //check if address is static
- if (ipInterface.DhcpServerAddresses.Count > 0)
- throw new DhcpServerException("DHCP Server requires static IP address to work correctly but the network interface was found to have DHCP configured.");
- _interfaceAddress = ip.Address;
- _interfaceIndex = ipInterface.GetIPv4Properties().Index;
- return true;
- }
- }
- }
- }
- //check if at least one interface has static ip address
- foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
- {
- if (nic.OperationalStatus != OperationalStatus.Up)
- continue;
- IPInterfaceProperties ipInterface = nic.GetIPProperties();
- foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
- {
- if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- //check if address is static
- if (ipInterface.DhcpServerAddresses.Count < 1)
- {
- //found static ip address so this scope can be activated
- //using ANY ip address for this scope interface since we dont know the relay agent network
- _interfaceAddress = IPAddress.Any;
- _interfaceIndex = -1;
- return true;
- }
- }
- }
- }
- //server has no static ip address configured
- return false;
- }
- internal void FindThisDnsServerAddress()
- {
- NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
- //find interface in current scope range
- uint networkAddressNumber = _networkAddress.ConvertIpToNumber();
- uint subnetMaskNumber = _subnetMask.ConvertIpToNumber();
- foreach (NetworkInterface nic in networkInterfaces)
- {
- if (nic.OperationalStatus != OperationalStatus.Up)
- continue;
- IPInterfaceProperties ipInterface = nic.GetIPProperties();
- foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
- {
- if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- uint addressNumber = ip.Address.ConvertIpToNumber();
- if ((addressNumber & subnetMaskNumber) == networkAddressNumber)
- {
- //found address in this scope range to use as dns server
- _dnsServers = new IPAddress[] { ip.Address };
- return;
- }
- }
- }
- }
- //find unicast ip address on an interface which has gateway
- foreach (NetworkInterface nic in networkInterfaces)
- {
- if (nic.OperationalStatus != OperationalStatus.Up)
- continue;
- IPInterfaceProperties ipInterface = nic.GetIPProperties();
- if (ipInterface.GatewayAddresses.Count > 0)
- {
- foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
- {
- if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- //use this address for dns
- _dnsServers = new IPAddress[] { ip.Address };
- return;
- }
- }
- }
- }
- //find any unicast ip address available
- foreach (NetworkInterface nic in networkInterfaces)
- {
- if (nic.OperationalStatus != OperationalStatus.Up)
- continue;
- IPInterfaceProperties ipInterface = nic.GetIPProperties();
- foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
- {
- if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- //use this address for dns
- _dnsServers = new IPAddress[] { ip.Address };
- return;
- }
- }
- }
- //no useable address was found
- _dnsServers = null;
- }
- internal bool IsAddressInRange(IPAddress address)
- {
- return IsAddressInRange(address, _startingAddress, _endingAddress);
- }
- internal Lease GetReservedLease(DhcpMessage request)
- {
- return GetReservedLease(new ClientIdentifierOption((byte)request.HardwareAddressType, request.ClientHardwareAddress));
- }
- internal Lease GetReservedLease(ClientIdentifierOption clientIdentifier)
- {
- if (_reservedLeases.TryGetValue(clientIdentifier, out Lease reservedLease))
- {
- //reserved address exists
- if (IsAddressAlreadyAllocated(reservedLease))
- return null; //reserved lease address is already allocated so ignore reserved lease
- return reservedLease;
- }
- return null;
- }
- internal Lease GetOffer(DhcpMessage request)
- {
- if (_leases.TryGetValue(request.ClientIdentifier, out Lease existingLease))
- {
- //lease already exists
- return existingLease;
- }
- Lease reservedLease = GetReservedLease(request);
- if (reservedLease != null)
- {
- Lease reservedOffer = new Lease(LeaseType.Reserved, request.ClientIdentifier, request.HostName?.HostName, request.ClientHardwareAddress, reservedLease.Address, null, GetLeaseTime());
- _offers[request.ClientIdentifier] = reservedOffer;
- return reservedOffer;
- }
- if (_allowOnlyReservedLeases)
- throw new DhcpServerException("DHCP Server failed to offer IP address to " + request.GetClientFullIdentifier() + ": scope allows only reserved lease allocations.");
- Lease dummyOffer = new Lease(LeaseType.None, null, null, null, null, null, 0);
- Lease existingOffer = _offers.GetOrAdd(request.ClientIdentifier, dummyOffer);
- if (dummyOffer != existingOffer)
- {
- if (existingOffer.Type == LeaseType.None)
- return null; //dummy offer so another thread is handling offer; do nothing
- //offer already exists
- existingOffer.ExtendLease(GetLeaseTime());
- return existingOffer;
- }
- //find offer ip address
- IPAddress offerAddress = null;
- if (request.RequestedIpAddress != null)
- {
- //client wish to get this address
- IPAddress requestedAddress = request.RequestedIpAddress.Address;
- if (IsAddressInRange(requestedAddress) && IsAddressAvailable(ref requestedAddress))
- offerAddress = requestedAddress;
- }
- if (offerAddress == null)
- {
- lock (_lastAddressOfferedLock)
- {
- //find free address from scope
- offerAddress = _lastAddressOffered;
- uint endingAddressNumber = _endingAddress.ConvertIpToNumber();
- bool offerAddressWasResetFromEnd = false;
- while (true)
- {
- uint nextOfferAddressNumber = offerAddress.ConvertIpToNumber() + 1u;
- if (nextOfferAddressNumber > endingAddressNumber)
- {
- if (offerAddressWasResetFromEnd)
- throw new DhcpServerException("DHCP Server failed to offer IP address to " + request.GetClientFullIdentifier() + ": address unavailable due to address pool exhaustion.");
- offerAddress = IPAddressExtension.ConvertNumberToIp(_startingAddress.ConvertIpToNumber() - 1u);
- offerAddressWasResetFromEnd = true;
- continue;
- }
- offerAddress = IPAddressExtension.ConvertNumberToIp(nextOfferAddressNumber);
- if (IsAddressAvailable(ref offerAddress))
- break;
- }
- _lastAddressOffered = offerAddress;
- }
- }
- Lease offerLease = new Lease(LeaseType.Dynamic, request.ClientIdentifier, request.HostName?.HostName, request.ClientHardwareAddress, offerAddress, null, GetLeaseTime());
- return _offers[request.ClientIdentifier] = offerLease;
- }
- internal Lease GetExistingLeaseOrOffer(DhcpMessage request)
- {
- if (_leases.TryGetValue(request.ClientIdentifier, out Lease existingLease))
- return existingLease;
- if (_offers.TryGetValue(request.ClientIdentifier, out Lease existingOffer))
- return existingOffer;
- return null;
- }
- internal List<DhcpOption> GetOptions(DhcpMessage request, IPAddress serverIdentifierAddress)
- {
- List<DhcpOption> options = new List<DhcpOption>();
- switch (request.DhcpMessageType.Type)
- {
- case DhcpMessageType.Discover:
- options.Add(new DhcpMessageTypeOption(DhcpMessageType.Offer));
- break;
- case DhcpMessageType.Request:
- case DhcpMessageType.Inform:
- options.Add(new DhcpMessageTypeOption(DhcpMessageType.Ack));
- break;
- default:
- return null;
- }
- options.Add(new ServerIdentifierOption(serverIdentifierAddress));
- switch (request.DhcpMessageType.Type)
- {
- case DhcpMessageType.Discover:
- case DhcpMessageType.Request:
- uint leaseTime = GetLeaseTime();
- options.Add(new IpAddressLeaseTimeOption(leaseTime));
- options.Add(new RenewalTimeValueOption(leaseTime / 2));
- options.Add(new RebindingTimeValueOption(Convert.ToUInt32(leaseTime * 0.875)));
- break;
- }
- if (request.ParameterRequestList == null)
- {
- options.Add(new SubnetMaskOption(_subnetMask));
- options.Add(new BroadcastAddressOption(_broadcastAddress));
- if (!string.IsNullOrEmpty(_domainName))
- {
- options.Add(new DomainNameOption(_domainName));
- if (request.ClientFullyQualifiedDomainName != null)
- options.Add(GetClientFullyQualifiedDomainNameOption(request));
- }
- if (_routerAddress != null)
- options.Add(new RouterOption(new IPAddress[] { _routerAddress }));
- if (_dnsServers != null)
- options.Add(new DomainNameServerOption(_dnsServers));
- if (_winsServers != null)
- options.Add(new NetBiosNameServerOption(_winsServers));
- if (_ntpServers != null)
- options.Add(new NetworkTimeProtocolServersOption(_ntpServers));
- if (_staticRoutes != null)
- options.Add(new ClasslessStaticRouteOption(_staticRoutes));
- }
- else
- {
- foreach (DhcpOptionCode optionCode in request.ParameterRequestList.OptionCodes)
- {
- switch (optionCode)
- {
- case DhcpOptionCode.SubnetMask:
- options.Add(new SubnetMaskOption(_subnetMask));
- options.Add(new BroadcastAddressOption(_broadcastAddress));
- break;
- case DhcpOptionCode.DomainName:
- if (!string.IsNullOrEmpty(_domainName))
- {
- options.Add(new DomainNameOption(_domainName));
- if (request.ClientFullyQualifiedDomainName != null)
- options.Add(GetClientFullyQualifiedDomainNameOption(request));
- }
- break;
- case DhcpOptionCode.Router:
- if (_routerAddress != null)
- options.Add(new RouterOption(new IPAddress[] { _routerAddress }));
- break;
- case DhcpOptionCode.DomainNameServer:
- if (_dnsServers != null)
- options.Add(new DomainNameServerOption(_dnsServers));
- break;
- case DhcpOptionCode.NetBiosOverTcpIpNameServer:
- if (_winsServers != null)
- options.Add(new NetBiosNameServerOption(_winsServers));
- break;
- case DhcpOptionCode.NetworkTimeProtocolServers:
- if (_ntpServers != null)
- options.Add(new NetworkTimeProtocolServersOption(_ntpServers));
- break;
- case DhcpOptionCode.ClasslessStaticRoute:
- if (_staticRoutes != null)
- options.Add(new ClasslessStaticRouteOption(_staticRoutes));
- break;
- }
- }
- }
- options.Add(DhcpOption.CreateEndOption());
- return options;
- }
- internal void CommitLease(Lease lease)
- {
- lease.ExtendLease(GetLeaseTime());
- _leases[lease.ClientIdentifier] = lease;
- _offers.TryRemove(lease.ClientIdentifier, out _);
- _lastModified = DateTime.UtcNow;
- }
- internal void ReleaseLease(Lease lease)
- {
- _leases.TryRemove(lease.ClientIdentifier, out _);
- _lastModified = DateTime.UtcNow;
- }
- internal void SetEnabled(bool enabled)
- {
- _enabled = enabled;
- if (!enabled)
- {
- _interfaceAddress = null;
- _interfaceIndex = 0;
- }
- }
- internal void RemoveExpiredOffers()
- {
- List<ClientIdentifierOption> expiredOffers = new List<ClientIdentifierOption>();
- DateTime utcNow = DateTime.UtcNow;
- foreach (KeyValuePair<ClientIdentifierOption, Lease> offer in _offers)
- {
- if (utcNow > offer.Value.LeaseObtained.AddSeconds(OFFER_EXPIRY_SECONDS))
- {
- //offer expired
- expiredOffers.Add(offer.Key);
- }
- }
- foreach (ClientIdentifierOption expiredOffer in expiredOffers)
- _offers.TryRemove(expiredOffer, out _);
- }
- internal List<Lease> RemoveExpiredLeases()
- {
- List<ClientIdentifierOption> expiredLeaseKeys = new List<ClientIdentifierOption>();
- DateTime utcNow = DateTime.UtcNow;
- foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
- {
- if (utcNow > lease.Value.LeaseExpires)
- {
- //lease expired
- expiredLeaseKeys.Add(lease.Key);
- }
- }
- List<Lease> expiredLeases = new List<Lease>();
- foreach (ClientIdentifierOption expiredLeaseKey in expiredLeaseKeys)
- {
- if (_leases.TryRemove(expiredLeaseKey, out Lease expiredLease))
- expiredLeases.Add(expiredLease);
- }
- if (expiredLeaseKeys.Count > 0)
- _lastModified = DateTime.UtcNow;
- return expiredLeases;
- }
- #endregion
- #region public
- public void ChangeNetwork(IPAddress startingAddress, IPAddress endingAddress, IPAddress subnetMask)
- {
- if (startingAddress.AddressFamily != AddressFamily.InterNetwork)
- throw new ArgumentException("Address family not supported.", nameof(startingAddress));
- if (endingAddress.AddressFamily != AddressFamily.InterNetwork)
- throw new ArgumentException("Address family not supported.", nameof(endingAddress));
- if (subnetMask.AddressFamily != AddressFamily.InterNetwork)
- throw new ArgumentException("Address family not supported.", nameof(subnetMask));
- uint startingAddressNumber = startingAddress.ConvertIpToNumber();
- uint endingAddressNumber = endingAddress.ConvertIpToNumber();
- if (startingAddressNumber >= endingAddressNumber)
- throw new ArgumentException("Ending address must be greater than starting address.");
- _startingAddress = startingAddress;
- _endingAddress = endingAddress;
- _subnetMask = subnetMask;
- //compute other parameters
- uint subnetMaskNumber = _subnetMask.ConvertIpToNumber();
- uint networkAddressNumber = startingAddressNumber & subnetMaskNumber;
- uint broadcastAddressNumber = networkAddressNumber | ~subnetMaskNumber;
- if (networkAddressNumber == startingAddressNumber)
- throw new ArgumentException("Starting address cannot be same as the network address.");
- if (broadcastAddressNumber == endingAddressNumber)
- throw new ArgumentException("Ending address cannot be same as the broadcast address.");
- _networkAddress = IPAddressExtension.ConvertNumberToIp(networkAddressNumber);
- _broadcastAddress = IPAddressExtension.ConvertNumberToIp(broadcastAddressNumber);
- lock (_lastAddressOfferedLock)
- {
- _lastAddressOffered = IPAddressExtension.ConvertNumberToIp(startingAddressNumber - 1u);
- }
- }
- public void WriteTo(BinaryWriter bW)
- {
- bW.Write(Encoding.ASCII.GetBytes("SC"));
- bW.Write((byte)1); //version
- bW.WriteShortString(_name);
- bW.Write(_enabled);
- _startingAddress.WriteTo(bW);
- _endingAddress.WriteTo(bW);
- _subnetMask.WriteTo(bW);
- bW.Write(_leaseTimeDays);
- bW.Write(_leaseTimeHours);
- bW.Write(_leaseTimeMinutes);
- bW.Write(_offerDelayTime);
- if (string.IsNullOrWhiteSpace(_domainName))
- bW.Write((byte)0);
- else
- bW.WriteShortString(_domainName);
- bW.Write(_dnsTtl);
- if (_routerAddress == null)
- IPAddress.Any.WriteTo(bW);
- else
- _routerAddress.WriteTo(bW);
- if (_useThisDnsServer)
- {
- bW.Write((byte)255);
- }
- else if (_dnsServers == null)
- {
- bW.Write((byte)0);
- }
- else
- {
- bW.Write(Convert.ToByte(_dnsServers.Count));
- foreach (IPAddress dnsServer in _dnsServers)
- dnsServer.WriteTo(bW);
- }
- if (_winsServers == null)
- {
- bW.Write((byte)0);
- }
- else
- {
- bW.Write(Convert.ToByte(_winsServers.Count));
- foreach (IPAddress winsServer in _winsServers)
- winsServer.WriteTo(bW);
- }
- if (_ntpServers == null)
- {
- bW.Write((byte)0);
- }
- else
- {
- bW.Write(Convert.ToByte(_ntpServers.Count));
- foreach (IPAddress ntpServer in _ntpServers)
- ntpServer.WriteTo(bW);
- }
- if (_staticRoutes == null)
- {
- bW.Write((byte)0);
- }
- else
- {
- bW.Write(Convert.ToByte(_staticRoutes.Count));
- foreach (ClasslessStaticRouteOption.Route route in _staticRoutes)
- route.WriteTo(bW.BaseStream);
- }
- if (_exclusions == null)
- {
- bW.Write((byte)0);
- }
- else
- {
- bW.Write(Convert.ToByte(_exclusions.Count));
- foreach (Exclusion exclusion in _exclusions)
- {
- exclusion.StartingAddress.WriteTo(bW);
- exclusion.EndingAddress.WriteTo(bW);
- }
- }
- bW.Write(_reservedLeases.Count);
- foreach (Lease reservedLease in _reservedLeases.Values)
- reservedLease.WriteTo(bW);
- bW.Write(_allowOnlyReservedLeases);
- {
- bW.Write(_leases.Count);
- foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
- lease.Value.WriteTo(bW);
- }
- }
- public override bool Equals(object obj)
- {
- if (obj is null)
- return false;
- if (ReferenceEquals(this, obj))
- return true;
- return Equals(obj as Scope);
- }
- public bool Equals(Scope other)
- {
- if (other is null)
- return false;
- if (!_startingAddress.Equals(other._startingAddress))
- return false;
- if (!_endingAddress.Equals(other._endingAddress))
- return false;
- return true;
- }
- public override int GetHashCode()
- {
- var hashCode = 206027136;
- hashCode = hashCode * -1521134295 + _startingAddress.GetHashCode();
- hashCode = hashCode * -1521134295 + _endingAddress.GetHashCode();
- hashCode = hashCode * -1521134295 + _subnetMask.GetHashCode();
- return hashCode;
- }
- public override string ToString()
- {
- return _name;
- }
- public int CompareTo(Scope other)
- {
- return _name.CompareTo(other._name);
- }
- #endregion
- #region properties
- public string Name
- {
- get { return _name; }
- set { _name = value; }
- }
- public bool Enabled
- { get { return _enabled; } }
- public IPAddress StartingAddress
- { get { return _startingAddress; } }
- public IPAddress EndingAddress
- { get { return _endingAddress; } }
- public IPAddress SubnetMask
- { get { return _subnetMask; } }
- public ushort LeaseTimeDays
- {
- get { return _leaseTimeDays; }
- set
- {
- if (value > 999)
- throw new ArgumentOutOfRangeException("Lease time in days must be between 0 to 999.");
- _leaseTimeDays = value;
- }
- }
- public byte LeaseTimeHours
- {
- get { return _leaseTimeHours; }
- set
- {
- if (value > 23)
- throw new ArgumentOutOfRangeException("Lease time in hours must be between 0 to 23.");
- _leaseTimeHours = value;
- }
- }
- public byte LeaseTimeMinutes
- {
- get { return _leaseTimeMinutes; }
- set
- {
- if (value > 59)
- throw new ArgumentOutOfRangeException("Lease time in minutes must be between 0 to 59.");
- _leaseTimeMinutes = value;
- }
- }
- public ushort OfferDelayTime
- {
- get { return _offerDelayTime; }
- set { _offerDelayTime = value; }
- }
- public string DomainName
- {
- get { return _domainName; }
- set
- {
- if (value != null)
- DnsClient.IsDomainNameValid(value, true);
- _domainName = value;
- }
- }
- public uint DnsTtl
- {
- get { return _dnsTtl; }
- set { _dnsTtl = value; }
- }
- public IPAddress RouterAddress
- {
- get { return _routerAddress; }
- set { _routerAddress = value; }
- }
- public bool UseThisDnsServer
- {
- get { return _useThisDnsServer; }
- set
- {
- _useThisDnsServer = value;
- if (_useThisDnsServer)
- FindThisDnsServerAddress();
- }
- }
- public ICollection<IPAddress> DnsServers
- {
- get { return _dnsServers; }
- set
- {
- _dnsServers = value;
- if ((_dnsServers != null) && _dnsServers.Count > 0)
- _useThisDnsServer = false;
- }
- }
- public ICollection<IPAddress> WinsServers
- {
- get { return _winsServers; }
- set { _winsServers = value; }
- }
- public ICollection<IPAddress> NtpServers
- {
- get { return _ntpServers; }
- set { _ntpServers = value; }
- }
- public ICollection<ClasslessStaticRouteOption.Route> StaticRoutes
- {
- get { return _staticRoutes; }
- set { _staticRoutes = value; }
- }
- public ICollection<Exclusion> Exclusions
- {
- get { return _exclusions; }
- set
- {
- if (value == null)
- {
- _exclusions = null;
- }
- else
- {
- foreach (Exclusion exclusion in value)
- {
- if (!IsAddressInRange(exclusion.StartingAddress))
- throw new ArgumentOutOfRangeException("Exclusion starting address must be in scope range.");
- if (!IsAddressInRange(exclusion.EndingAddress))
- throw new ArgumentOutOfRangeException("Exclusion ending address must be in scope range.");
- }
- _exclusions = value;
- }
- }
- }
- public ICollection<Lease> ReservedLeases
- {
- get { return _reservedLeases.Values; }
- set
- {
- if (value == null)
- {
- _reservedLeases.Clear();
- }
- else
- {
- foreach (Lease reservedLease in value)
- {
- if (!IsAddressInRange(reservedLease.Address))
- throw new ArgumentOutOfRangeException("Reserved address must be in scope range.");
- }
- _reservedLeases.Clear();
- foreach (Lease reservedLease in value)
- _reservedLeases.TryAdd(reservedLease.ClientIdentifier, reservedLease);
- }
- }
- }
- public bool AllowOnlyReservedLeases
- {
- get { return _allowOnlyReservedLeases; }
- set { _allowOnlyReservedLeases = value; }
- }
- public ICollection<Lease> Leases
- { get { return _leases.Values; } }
- public IPAddress NetworkAddress
- { get { return _networkAddress; } }
- public IPAddress BroadcastAddress
- { get { return _broadcastAddress; } }
- public IPAddress InterfaceAddress
- { get { return _interfaceAddress; } }
- internal int InterfaceIndex
- { get { return _interfaceIndex; } }
- internal DateTime LastModified
- { get { return _lastModified; } }
- #endregion
- }
- }
|