Scope.cs 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2020 Shreyas Zare (shreyas@technitium.com)
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. using DnsServerCore.Dhcp.Options;
  16. using System;
  17. using System.Collections.Concurrent;
  18. using System.Collections.Generic;
  19. using System.IO;
  20. using System.Net;
  21. using System.Net.NetworkInformation;
  22. using System.Net.Sockets;
  23. using System.Text;
  24. using TechnitiumLibrary.IO;
  25. using TechnitiumLibrary.Net;
  26. using TechnitiumLibrary.Net.Dns;
  27. namespace DnsServerCore.Dhcp
  28. {
  29. public class Scope : IComparable<Scope>
  30. {
  31. #region variables
  32. //required parameters
  33. string _name;
  34. bool _enabled;
  35. IPAddress _startingAddress;
  36. IPAddress _endingAddress;
  37. IPAddress _subnetMask;
  38. ushort _leaseTimeDays = 1; //default 1 day lease
  39. byte _leaseTimeHours = 0;
  40. byte _leaseTimeMinutes = 0;
  41. ushort _offerDelayTime;
  42. //dhcp options
  43. string _domainName;
  44. uint _dnsTtl = 900;
  45. IPAddress _routerAddress;
  46. bool _useThisDnsServer;
  47. ICollection<IPAddress> _dnsServers;
  48. ICollection<IPAddress> _winsServers;
  49. ICollection<IPAddress> _ntpServers;
  50. ICollection<ClasslessStaticRouteOption.Route> _staticRoutes;
  51. //advanced options
  52. ICollection<Exclusion> _exclusions;
  53. readonly ConcurrentDictionary<ClientIdentifierOption, Lease> _reservedLeases = new ConcurrentDictionary<ClientIdentifierOption, Lease>();
  54. bool _allowOnlyReservedLeases;
  55. //leases
  56. readonly ConcurrentDictionary<ClientIdentifierOption, Lease> _leases = new ConcurrentDictionary<ClientIdentifierOption, Lease>();
  57. //internal computed parameters
  58. IPAddress _networkAddress;
  59. IPAddress _broadcastAddress;
  60. //internal parameters
  61. const int OFFER_EXPIRY_SECONDS = 60; //1 mins offer expiry
  62. readonly ConcurrentDictionary<ClientIdentifierOption, Lease> _offers = new ConcurrentDictionary<ClientIdentifierOption, Lease>();
  63. IPAddress _lastAddressOffered;
  64. readonly object _lastAddressOfferedLock = new object();
  65. IPAddress _interfaceAddress;
  66. int _interfaceIndex;
  67. DateTime _lastModified = DateTime.UtcNow;
  68. #endregion
  69. #region constructor
  70. public Scope(string name, bool enabled, IPAddress startingAddress, IPAddress endingAddress, IPAddress subnetMask)
  71. {
  72. _name = name;
  73. _enabled = enabled;
  74. ChangeNetwork(startingAddress, endingAddress, subnetMask);
  75. }
  76. public Scope(BinaryReader bR)
  77. {
  78. if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "SC")
  79. throw new InvalidDataException("DhcpServer scope file format is invalid.");
  80. switch (bR.ReadByte())
  81. {
  82. case 1:
  83. _name = bR.ReadShortString();
  84. _enabled = bR.ReadBoolean();
  85. ChangeNetwork(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR));
  86. _leaseTimeDays = bR.ReadUInt16();
  87. _leaseTimeHours = bR.ReadByte();
  88. _leaseTimeMinutes = bR.ReadByte();
  89. _offerDelayTime = bR.ReadUInt16();
  90. _domainName = bR.ReadShortString();
  91. if (string.IsNullOrWhiteSpace(_domainName))
  92. _domainName = null;
  93. _dnsTtl = bR.ReadUInt32();
  94. _routerAddress = IPAddressExtension.Parse(bR);
  95. if (_routerAddress.Equals(IPAddress.Any))
  96. _routerAddress = null;
  97. {
  98. int count = bR.ReadByte();
  99. if (count > 0)
  100. {
  101. if (count == 255)
  102. {
  103. _useThisDnsServer = true;
  104. FindThisDnsServerAddress();
  105. }
  106. else
  107. {
  108. IPAddress[] dnsServers = new IPAddress[count];
  109. for (int i = 0; i < count; i++)
  110. dnsServers[i] = IPAddressExtension.Parse(bR);
  111. _dnsServers = dnsServers;
  112. }
  113. }
  114. }
  115. {
  116. int count = bR.ReadByte();
  117. if (count > 0)
  118. {
  119. IPAddress[] winsServers = new IPAddress[count];
  120. for (int i = 0; i < count; i++)
  121. winsServers[i] = IPAddressExtension.Parse(bR);
  122. _winsServers = winsServers;
  123. }
  124. }
  125. {
  126. int count = bR.ReadByte();
  127. if (count > 0)
  128. {
  129. IPAddress[] ntpServers = new IPAddress[count];
  130. for (int i = 0; i < count; i++)
  131. ntpServers[i] = IPAddressExtension.Parse(bR);
  132. _ntpServers = ntpServers;
  133. }
  134. }
  135. {
  136. int count = bR.ReadByte();
  137. if (count > 0)
  138. {
  139. ClasslessStaticRouteOption.Route[] staticRoutes = new ClasslessStaticRouteOption.Route[count];
  140. for (int i = 0; i < count; i++)
  141. staticRoutes[i] = new ClasslessStaticRouteOption.Route(bR.BaseStream);
  142. _staticRoutes = staticRoutes;
  143. }
  144. }
  145. {
  146. int count = bR.ReadByte();
  147. if (count > 0)
  148. {
  149. Exclusion[] exclusions = new Exclusion[count];
  150. for (int i = 0; i < count; i++)
  151. exclusions[i] = new Exclusion(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR));
  152. _exclusions = exclusions;
  153. }
  154. }
  155. {
  156. int count = bR.ReadInt32();
  157. if (count > 0)
  158. {
  159. for (int i = 0; i < count; i++)
  160. {
  161. Lease reservedLease = new Lease(bR);
  162. _reservedLeases.TryAdd(reservedLease.ClientIdentifier, reservedLease);
  163. }
  164. }
  165. _allowOnlyReservedLeases = bR.ReadBoolean();
  166. }
  167. {
  168. int count = bR.ReadInt32();
  169. if (count > 0)
  170. {
  171. for (int i = 0; i < count; i++)
  172. {
  173. Lease lease = new Lease(bR);
  174. _leases.TryAdd(lease.ClientIdentifier, lease);
  175. }
  176. }
  177. }
  178. break;
  179. default:
  180. throw new InvalidDataException("Scope data format version not supported.");
  181. }
  182. }
  183. #endregion
  184. #region static
  185. public static bool IsAddressInRange(IPAddress address, IPAddress startingAddress, IPAddress endingAddress)
  186. {
  187. uint addressNumber = address.ConvertIpToNumber();
  188. uint startingAddressNumber = startingAddress.ConvertIpToNumber();
  189. uint endingAddressNumber = endingAddress.ConvertIpToNumber();
  190. return (startingAddressNumber <= addressNumber) && (addressNumber <= endingAddressNumber);
  191. }
  192. #endregion
  193. #region private
  194. private uint GetLeaseTime()
  195. {
  196. return Convert.ToUInt32((_leaseTimeDays * 24 * 60 * 60) + (_leaseTimeHours * 60 * 60) + (_leaseTimeMinutes * 60));
  197. }
  198. private bool IsAddressAvailable(ref IPAddress address)
  199. {
  200. if (address.Equals(_routerAddress))
  201. return false;
  202. if ((_dnsServers != null) && _dnsServers.Contains(address))
  203. return false;
  204. if ((_winsServers != null) && _winsServers.Contains(address))
  205. return false;
  206. if ((_ntpServers != null) && _ntpServers.Contains(address))
  207. return false;
  208. if (_exclusions != null)
  209. {
  210. foreach (Exclusion exclusion in _exclusions)
  211. {
  212. if (IsAddressInRange(address, exclusion.StartingAddress, exclusion.EndingAddress))
  213. {
  214. address = exclusion.EndingAddress;
  215. return false;
  216. }
  217. }
  218. }
  219. foreach (Lease reservedLease in _reservedLeases.Values)
  220. {
  221. if (address.Equals(reservedLease.Address))
  222. return false;
  223. }
  224. foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
  225. {
  226. if (address.Equals(lease.Value.Address))
  227. return false;
  228. }
  229. foreach (KeyValuePair<ClientIdentifierOption, Lease> offer in _offers)
  230. {
  231. if (address.Equals(offer.Value.Address))
  232. return false;
  233. }
  234. return true;
  235. }
  236. private bool IsAddressAlreadyAllocated(Lease reservedLease)
  237. {
  238. foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
  239. {
  240. if (reservedLease.Address.Equals(lease.Value.Address))
  241. return !lease.Key.Equals(reservedLease.ClientIdentifier);
  242. }
  243. foreach (KeyValuePair<ClientIdentifierOption, Lease> offer in _offers)
  244. {
  245. if (reservedLease.Address.Equals(offer.Value.Address))
  246. return !offer.Key.Equals(reservedLease.ClientIdentifier);
  247. }
  248. return false;
  249. }
  250. private ClientFullyQualifiedDomainNameOption GetClientFullyQualifiedDomainNameOption(DhcpMessage request)
  251. {
  252. ClientFullyQualifiedDomainNameFlags responseFlags = ClientFullyQualifiedDomainNameFlags.None;
  253. if (request.ClientFullyQualifiedDomainName.Flags.HasFlag(ClientFullyQualifiedDomainNameFlags.EncodeUsingCanonicalWireFormat))
  254. responseFlags |= ClientFullyQualifiedDomainNameFlags.EncodeUsingCanonicalWireFormat;
  255. if (request.ClientFullyQualifiedDomainName.Flags.HasFlag(ClientFullyQualifiedDomainNameFlags.NoDnsUpdate))
  256. {
  257. responseFlags |= ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns;
  258. responseFlags |= ClientFullyQualifiedDomainNameFlags.OverrideByServer;
  259. }
  260. else if (request.ClientFullyQualifiedDomainName.Flags.HasFlag(ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns))
  261. {
  262. responseFlags |= ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns;
  263. }
  264. else
  265. {
  266. responseFlags |= ClientFullyQualifiedDomainNameFlags.ShouldUpdateDns;
  267. responseFlags |= ClientFullyQualifiedDomainNameFlags.OverrideByServer;
  268. }
  269. string clientDomainName;
  270. if (string.IsNullOrWhiteSpace(request.ClientFullyQualifiedDomainName.DomainName))
  271. {
  272. //client domain empty and expects server for a fqdn domain name
  273. if (request.HostName == null)
  274. return null; //server unable to decide a name for client
  275. clientDomainName = request.HostName.HostName + "." + _domainName;
  276. }
  277. else if (request.ClientFullyQualifiedDomainName.DomainName.Contains("."))
  278. {
  279. //client domain is fqdn
  280. if (request.ClientFullyQualifiedDomainName.DomainName.EndsWith("." + _domainName, StringComparison.OrdinalIgnoreCase))
  281. {
  282. clientDomainName = request.ClientFullyQualifiedDomainName.DomainName;
  283. }
  284. else
  285. {
  286. string[] parts = request.ClientFullyQualifiedDomainName.DomainName.Split('.');
  287. clientDomainName = parts[0] + "." + _domainName;
  288. }
  289. }
  290. else
  291. {
  292. //client domain is just hostname
  293. clientDomainName = request.ClientFullyQualifiedDomainName.DomainName + "." + _domainName;
  294. }
  295. return new ClientFullyQualifiedDomainNameOption(responseFlags, 255, 255, clientDomainName);
  296. }
  297. #endregion
  298. #region internal
  299. internal bool FindInterface()
  300. {
  301. //find network with static ip address in scope range
  302. uint networkAddressNumber = _networkAddress.ConvertIpToNumber();
  303. uint subnetMaskNumber = _subnetMask.ConvertIpToNumber();
  304. foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
  305. {
  306. if (nic.OperationalStatus != OperationalStatus.Up)
  307. continue;
  308. IPInterfaceProperties ipInterface = nic.GetIPProperties();
  309. foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
  310. {
  311. if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
  312. {
  313. uint addressNumber = ip.Address.ConvertIpToNumber();
  314. if ((addressNumber & subnetMaskNumber) == networkAddressNumber)
  315. {
  316. //found interface for this scope range
  317. //check if address is static
  318. if (ipInterface.DhcpServerAddresses.Count > 0)
  319. throw new DhcpServerException("DHCP Server requires static IP address to work correctly but the network interface was found to have DHCP configured.");
  320. _interfaceAddress = ip.Address;
  321. _interfaceIndex = ipInterface.GetIPv4Properties().Index;
  322. return true;
  323. }
  324. }
  325. }
  326. }
  327. //check if at least one interface has static ip address
  328. foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
  329. {
  330. if (nic.OperationalStatus != OperationalStatus.Up)
  331. continue;
  332. IPInterfaceProperties ipInterface = nic.GetIPProperties();
  333. foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
  334. {
  335. if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
  336. {
  337. //check if address is static
  338. if (ipInterface.DhcpServerAddresses.Count < 1)
  339. {
  340. //found static ip address so this scope can be activated
  341. //using ANY ip address for this scope interface since we dont know the relay agent network
  342. _interfaceAddress = IPAddress.Any;
  343. _interfaceIndex = -1;
  344. return true;
  345. }
  346. }
  347. }
  348. }
  349. //server has no static ip address configured
  350. return false;
  351. }
  352. internal void FindThisDnsServerAddress()
  353. {
  354. NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
  355. //find interface in current scope range
  356. uint networkAddressNumber = _networkAddress.ConvertIpToNumber();
  357. uint subnetMaskNumber = _subnetMask.ConvertIpToNumber();
  358. foreach (NetworkInterface nic in networkInterfaces)
  359. {
  360. if (nic.OperationalStatus != OperationalStatus.Up)
  361. continue;
  362. IPInterfaceProperties ipInterface = nic.GetIPProperties();
  363. foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
  364. {
  365. if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
  366. {
  367. uint addressNumber = ip.Address.ConvertIpToNumber();
  368. if ((addressNumber & subnetMaskNumber) == networkAddressNumber)
  369. {
  370. //found address in this scope range to use as dns server
  371. _dnsServers = new IPAddress[] { ip.Address };
  372. return;
  373. }
  374. }
  375. }
  376. }
  377. //find unicast ip address on an interface which has gateway
  378. foreach (NetworkInterface nic in networkInterfaces)
  379. {
  380. if (nic.OperationalStatus != OperationalStatus.Up)
  381. continue;
  382. IPInterfaceProperties ipInterface = nic.GetIPProperties();
  383. if (ipInterface.GatewayAddresses.Count > 0)
  384. {
  385. foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
  386. {
  387. if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
  388. {
  389. //use this address for dns
  390. _dnsServers = new IPAddress[] { ip.Address };
  391. return;
  392. }
  393. }
  394. }
  395. }
  396. //find any unicast ip address available
  397. foreach (NetworkInterface nic in networkInterfaces)
  398. {
  399. if (nic.OperationalStatus != OperationalStatus.Up)
  400. continue;
  401. IPInterfaceProperties ipInterface = nic.GetIPProperties();
  402. foreach (UnicastIPAddressInformation ip in ipInterface.UnicastAddresses)
  403. {
  404. if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
  405. {
  406. //use this address for dns
  407. _dnsServers = new IPAddress[] { ip.Address };
  408. return;
  409. }
  410. }
  411. }
  412. //no useable address was found
  413. _dnsServers = null;
  414. }
  415. internal bool IsAddressInRange(IPAddress address)
  416. {
  417. return IsAddressInRange(address, _startingAddress, _endingAddress);
  418. }
  419. internal Lease GetReservedLease(DhcpMessage request)
  420. {
  421. return GetReservedLease(new ClientIdentifierOption((byte)request.HardwareAddressType, request.ClientHardwareAddress));
  422. }
  423. internal Lease GetReservedLease(ClientIdentifierOption clientIdentifier)
  424. {
  425. if (_reservedLeases.TryGetValue(clientIdentifier, out Lease reservedLease))
  426. {
  427. //reserved address exists
  428. if (IsAddressAlreadyAllocated(reservedLease))
  429. return null; //reserved lease address is already allocated so ignore reserved lease
  430. return reservedLease;
  431. }
  432. return null;
  433. }
  434. internal Lease GetOffer(DhcpMessage request)
  435. {
  436. if (_leases.TryGetValue(request.ClientIdentifier, out Lease existingLease))
  437. {
  438. //lease already exists
  439. return existingLease;
  440. }
  441. Lease reservedLease = GetReservedLease(request);
  442. if (reservedLease != null)
  443. {
  444. Lease reservedOffer = new Lease(LeaseType.Reserved, request.ClientIdentifier, request.HostName?.HostName, request.ClientHardwareAddress, reservedLease.Address, null, GetLeaseTime());
  445. _offers[request.ClientIdentifier] = reservedOffer;
  446. return reservedOffer;
  447. }
  448. if (_allowOnlyReservedLeases)
  449. throw new DhcpServerException("DHCP Server failed to offer IP address to " + request.GetClientFullIdentifier() + ": scope allows only reserved lease allocations.");
  450. Lease dummyOffer = new Lease(LeaseType.None, null, null, null, null, null, 0);
  451. Lease existingOffer = _offers.GetOrAdd(request.ClientIdentifier, dummyOffer);
  452. if (dummyOffer != existingOffer)
  453. {
  454. if (existingOffer.Type == LeaseType.None)
  455. return null; //dummy offer so another thread is handling offer; do nothing
  456. //offer already exists
  457. existingOffer.ExtendLease(GetLeaseTime());
  458. return existingOffer;
  459. }
  460. //find offer ip address
  461. IPAddress offerAddress = null;
  462. if (request.RequestedIpAddress != null)
  463. {
  464. //client wish to get this address
  465. IPAddress requestedAddress = request.RequestedIpAddress.Address;
  466. if (IsAddressInRange(requestedAddress) && IsAddressAvailable(ref requestedAddress))
  467. offerAddress = requestedAddress;
  468. }
  469. if (offerAddress == null)
  470. {
  471. lock (_lastAddressOfferedLock)
  472. {
  473. //find free address from scope
  474. offerAddress = _lastAddressOffered;
  475. uint endingAddressNumber = _endingAddress.ConvertIpToNumber();
  476. bool offerAddressWasResetFromEnd = false;
  477. while (true)
  478. {
  479. uint nextOfferAddressNumber = offerAddress.ConvertIpToNumber() + 1u;
  480. if (nextOfferAddressNumber > endingAddressNumber)
  481. {
  482. if (offerAddressWasResetFromEnd)
  483. throw new DhcpServerException("DHCP Server failed to offer IP address to " + request.GetClientFullIdentifier() + ": address unavailable due to address pool exhaustion.");
  484. offerAddress = IPAddressExtension.ConvertNumberToIp(_startingAddress.ConvertIpToNumber() - 1u);
  485. offerAddressWasResetFromEnd = true;
  486. continue;
  487. }
  488. offerAddress = IPAddressExtension.ConvertNumberToIp(nextOfferAddressNumber);
  489. if (IsAddressAvailable(ref offerAddress))
  490. break;
  491. }
  492. _lastAddressOffered = offerAddress;
  493. }
  494. }
  495. Lease offerLease = new Lease(LeaseType.Dynamic, request.ClientIdentifier, request.HostName?.HostName, request.ClientHardwareAddress, offerAddress, null, GetLeaseTime());
  496. return _offers[request.ClientIdentifier] = offerLease;
  497. }
  498. internal Lease GetExistingLeaseOrOffer(DhcpMessage request)
  499. {
  500. if (_leases.TryGetValue(request.ClientIdentifier, out Lease existingLease))
  501. return existingLease;
  502. if (_offers.TryGetValue(request.ClientIdentifier, out Lease existingOffer))
  503. return existingOffer;
  504. return null;
  505. }
  506. internal List<DhcpOption> GetOptions(DhcpMessage request, IPAddress serverIdentifierAddress)
  507. {
  508. List<DhcpOption> options = new List<DhcpOption>();
  509. switch (request.DhcpMessageType.Type)
  510. {
  511. case DhcpMessageType.Discover:
  512. options.Add(new DhcpMessageTypeOption(DhcpMessageType.Offer));
  513. break;
  514. case DhcpMessageType.Request:
  515. case DhcpMessageType.Inform:
  516. options.Add(new DhcpMessageTypeOption(DhcpMessageType.Ack));
  517. break;
  518. default:
  519. return null;
  520. }
  521. options.Add(new ServerIdentifierOption(serverIdentifierAddress));
  522. switch (request.DhcpMessageType.Type)
  523. {
  524. case DhcpMessageType.Discover:
  525. case DhcpMessageType.Request:
  526. uint leaseTime = GetLeaseTime();
  527. options.Add(new IpAddressLeaseTimeOption(leaseTime));
  528. options.Add(new RenewalTimeValueOption(leaseTime / 2));
  529. options.Add(new RebindingTimeValueOption(Convert.ToUInt32(leaseTime * 0.875)));
  530. break;
  531. }
  532. if (request.ParameterRequestList == null)
  533. {
  534. options.Add(new SubnetMaskOption(_subnetMask));
  535. options.Add(new BroadcastAddressOption(_broadcastAddress));
  536. if (!string.IsNullOrEmpty(_domainName))
  537. {
  538. options.Add(new DomainNameOption(_domainName));
  539. if (request.ClientFullyQualifiedDomainName != null)
  540. options.Add(GetClientFullyQualifiedDomainNameOption(request));
  541. }
  542. if (_routerAddress != null)
  543. options.Add(new RouterOption(new IPAddress[] { _routerAddress }));
  544. if (_dnsServers != null)
  545. options.Add(new DomainNameServerOption(_dnsServers));
  546. if (_winsServers != null)
  547. options.Add(new NetBiosNameServerOption(_winsServers));
  548. if (_ntpServers != null)
  549. options.Add(new NetworkTimeProtocolServersOption(_ntpServers));
  550. if (_staticRoutes != null)
  551. options.Add(new ClasslessStaticRouteOption(_staticRoutes));
  552. }
  553. else
  554. {
  555. foreach (DhcpOptionCode optionCode in request.ParameterRequestList.OptionCodes)
  556. {
  557. switch (optionCode)
  558. {
  559. case DhcpOptionCode.SubnetMask:
  560. options.Add(new SubnetMaskOption(_subnetMask));
  561. options.Add(new BroadcastAddressOption(_broadcastAddress));
  562. break;
  563. case DhcpOptionCode.DomainName:
  564. if (!string.IsNullOrEmpty(_domainName))
  565. {
  566. options.Add(new DomainNameOption(_domainName));
  567. if (request.ClientFullyQualifiedDomainName != null)
  568. options.Add(GetClientFullyQualifiedDomainNameOption(request));
  569. }
  570. break;
  571. case DhcpOptionCode.Router:
  572. if (_routerAddress != null)
  573. options.Add(new RouterOption(new IPAddress[] { _routerAddress }));
  574. break;
  575. case DhcpOptionCode.DomainNameServer:
  576. if (_dnsServers != null)
  577. options.Add(new DomainNameServerOption(_dnsServers));
  578. break;
  579. case DhcpOptionCode.NetBiosOverTcpIpNameServer:
  580. if (_winsServers != null)
  581. options.Add(new NetBiosNameServerOption(_winsServers));
  582. break;
  583. case DhcpOptionCode.NetworkTimeProtocolServers:
  584. if (_ntpServers != null)
  585. options.Add(new NetworkTimeProtocolServersOption(_ntpServers));
  586. break;
  587. case DhcpOptionCode.ClasslessStaticRoute:
  588. if (_staticRoutes != null)
  589. options.Add(new ClasslessStaticRouteOption(_staticRoutes));
  590. break;
  591. }
  592. }
  593. }
  594. options.Add(DhcpOption.CreateEndOption());
  595. return options;
  596. }
  597. internal void CommitLease(Lease lease)
  598. {
  599. lease.ExtendLease(GetLeaseTime());
  600. _leases[lease.ClientIdentifier] = lease;
  601. _offers.TryRemove(lease.ClientIdentifier, out _);
  602. _lastModified = DateTime.UtcNow;
  603. }
  604. internal void ReleaseLease(Lease lease)
  605. {
  606. _leases.TryRemove(lease.ClientIdentifier, out _);
  607. _lastModified = DateTime.UtcNow;
  608. }
  609. internal void SetEnabled(bool enabled)
  610. {
  611. _enabled = enabled;
  612. if (!enabled)
  613. {
  614. _interfaceAddress = null;
  615. _interfaceIndex = 0;
  616. }
  617. }
  618. internal void RemoveExpiredOffers()
  619. {
  620. List<ClientIdentifierOption> expiredOffers = new List<ClientIdentifierOption>();
  621. DateTime utcNow = DateTime.UtcNow;
  622. foreach (KeyValuePair<ClientIdentifierOption, Lease> offer in _offers)
  623. {
  624. if (utcNow > offer.Value.LeaseObtained.AddSeconds(OFFER_EXPIRY_SECONDS))
  625. {
  626. //offer expired
  627. expiredOffers.Add(offer.Key);
  628. }
  629. }
  630. foreach (ClientIdentifierOption expiredOffer in expiredOffers)
  631. _offers.TryRemove(expiredOffer, out _);
  632. }
  633. internal List<Lease> RemoveExpiredLeases()
  634. {
  635. List<ClientIdentifierOption> expiredLeaseKeys = new List<ClientIdentifierOption>();
  636. DateTime utcNow = DateTime.UtcNow;
  637. foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
  638. {
  639. if (utcNow > lease.Value.LeaseExpires)
  640. {
  641. //lease expired
  642. expiredLeaseKeys.Add(lease.Key);
  643. }
  644. }
  645. List<Lease> expiredLeases = new List<Lease>();
  646. foreach (ClientIdentifierOption expiredLeaseKey in expiredLeaseKeys)
  647. {
  648. if (_leases.TryRemove(expiredLeaseKey, out Lease expiredLease))
  649. expiredLeases.Add(expiredLease);
  650. }
  651. if (expiredLeaseKeys.Count > 0)
  652. _lastModified = DateTime.UtcNow;
  653. return expiredLeases;
  654. }
  655. #endregion
  656. #region public
  657. public void ChangeNetwork(IPAddress startingAddress, IPAddress endingAddress, IPAddress subnetMask)
  658. {
  659. if (startingAddress.AddressFamily != AddressFamily.InterNetwork)
  660. throw new ArgumentException("Address family not supported.", nameof(startingAddress));
  661. if (endingAddress.AddressFamily != AddressFamily.InterNetwork)
  662. throw new ArgumentException("Address family not supported.", nameof(endingAddress));
  663. if (subnetMask.AddressFamily != AddressFamily.InterNetwork)
  664. throw new ArgumentException("Address family not supported.", nameof(subnetMask));
  665. uint startingAddressNumber = startingAddress.ConvertIpToNumber();
  666. uint endingAddressNumber = endingAddress.ConvertIpToNumber();
  667. if (startingAddressNumber >= endingAddressNumber)
  668. throw new ArgumentException("Ending address must be greater than starting address.");
  669. _startingAddress = startingAddress;
  670. _endingAddress = endingAddress;
  671. _subnetMask = subnetMask;
  672. //compute other parameters
  673. uint subnetMaskNumber = _subnetMask.ConvertIpToNumber();
  674. uint networkAddressNumber = startingAddressNumber & subnetMaskNumber;
  675. uint broadcastAddressNumber = networkAddressNumber | ~subnetMaskNumber;
  676. if (networkAddressNumber == startingAddressNumber)
  677. throw new ArgumentException("Starting address cannot be same as the network address.");
  678. if (broadcastAddressNumber == endingAddressNumber)
  679. throw new ArgumentException("Ending address cannot be same as the broadcast address.");
  680. _networkAddress = IPAddressExtension.ConvertNumberToIp(networkAddressNumber);
  681. _broadcastAddress = IPAddressExtension.ConvertNumberToIp(broadcastAddressNumber);
  682. lock (_lastAddressOfferedLock)
  683. {
  684. _lastAddressOffered = IPAddressExtension.ConvertNumberToIp(startingAddressNumber - 1u);
  685. }
  686. }
  687. public void WriteTo(BinaryWriter bW)
  688. {
  689. bW.Write(Encoding.ASCII.GetBytes("SC"));
  690. bW.Write((byte)1); //version
  691. bW.WriteShortString(_name);
  692. bW.Write(_enabled);
  693. _startingAddress.WriteTo(bW);
  694. _endingAddress.WriteTo(bW);
  695. _subnetMask.WriteTo(bW);
  696. bW.Write(_leaseTimeDays);
  697. bW.Write(_leaseTimeHours);
  698. bW.Write(_leaseTimeMinutes);
  699. bW.Write(_offerDelayTime);
  700. if (string.IsNullOrWhiteSpace(_domainName))
  701. bW.Write((byte)0);
  702. else
  703. bW.WriteShortString(_domainName);
  704. bW.Write(_dnsTtl);
  705. if (_routerAddress == null)
  706. IPAddress.Any.WriteTo(bW);
  707. else
  708. _routerAddress.WriteTo(bW);
  709. if (_useThisDnsServer)
  710. {
  711. bW.Write((byte)255);
  712. }
  713. else if (_dnsServers == null)
  714. {
  715. bW.Write((byte)0);
  716. }
  717. else
  718. {
  719. bW.Write(Convert.ToByte(_dnsServers.Count));
  720. foreach (IPAddress dnsServer in _dnsServers)
  721. dnsServer.WriteTo(bW);
  722. }
  723. if (_winsServers == null)
  724. {
  725. bW.Write((byte)0);
  726. }
  727. else
  728. {
  729. bW.Write(Convert.ToByte(_winsServers.Count));
  730. foreach (IPAddress winsServer in _winsServers)
  731. winsServer.WriteTo(bW);
  732. }
  733. if (_ntpServers == null)
  734. {
  735. bW.Write((byte)0);
  736. }
  737. else
  738. {
  739. bW.Write(Convert.ToByte(_ntpServers.Count));
  740. foreach (IPAddress ntpServer in _ntpServers)
  741. ntpServer.WriteTo(bW);
  742. }
  743. if (_staticRoutes == null)
  744. {
  745. bW.Write((byte)0);
  746. }
  747. else
  748. {
  749. bW.Write(Convert.ToByte(_staticRoutes.Count));
  750. foreach (ClasslessStaticRouteOption.Route route in _staticRoutes)
  751. route.WriteTo(bW.BaseStream);
  752. }
  753. if (_exclusions == null)
  754. {
  755. bW.Write((byte)0);
  756. }
  757. else
  758. {
  759. bW.Write(Convert.ToByte(_exclusions.Count));
  760. foreach (Exclusion exclusion in _exclusions)
  761. {
  762. exclusion.StartingAddress.WriteTo(bW);
  763. exclusion.EndingAddress.WriteTo(bW);
  764. }
  765. }
  766. bW.Write(_reservedLeases.Count);
  767. foreach (Lease reservedLease in _reservedLeases.Values)
  768. reservedLease.WriteTo(bW);
  769. bW.Write(_allowOnlyReservedLeases);
  770. {
  771. bW.Write(_leases.Count);
  772. foreach (KeyValuePair<ClientIdentifierOption, Lease> lease in _leases)
  773. lease.Value.WriteTo(bW);
  774. }
  775. }
  776. public override bool Equals(object obj)
  777. {
  778. if (obj is null)
  779. return false;
  780. if (ReferenceEquals(this, obj))
  781. return true;
  782. return Equals(obj as Scope);
  783. }
  784. public bool Equals(Scope other)
  785. {
  786. if (other is null)
  787. return false;
  788. if (!_startingAddress.Equals(other._startingAddress))
  789. return false;
  790. if (!_endingAddress.Equals(other._endingAddress))
  791. return false;
  792. return true;
  793. }
  794. public override int GetHashCode()
  795. {
  796. var hashCode = 206027136;
  797. hashCode = hashCode * -1521134295 + _startingAddress.GetHashCode();
  798. hashCode = hashCode * -1521134295 + _endingAddress.GetHashCode();
  799. hashCode = hashCode * -1521134295 + _subnetMask.GetHashCode();
  800. return hashCode;
  801. }
  802. public override string ToString()
  803. {
  804. return _name;
  805. }
  806. public int CompareTo(Scope other)
  807. {
  808. return _name.CompareTo(other._name);
  809. }
  810. #endregion
  811. #region properties
  812. public string Name
  813. {
  814. get { return _name; }
  815. set { _name = value; }
  816. }
  817. public bool Enabled
  818. { get { return _enabled; } }
  819. public IPAddress StartingAddress
  820. { get { return _startingAddress; } }
  821. public IPAddress EndingAddress
  822. { get { return _endingAddress; } }
  823. public IPAddress SubnetMask
  824. { get { return _subnetMask; } }
  825. public ushort LeaseTimeDays
  826. {
  827. get { return _leaseTimeDays; }
  828. set
  829. {
  830. if (value > 999)
  831. throw new ArgumentOutOfRangeException("Lease time in days must be between 0 to 999.");
  832. _leaseTimeDays = value;
  833. }
  834. }
  835. public byte LeaseTimeHours
  836. {
  837. get { return _leaseTimeHours; }
  838. set
  839. {
  840. if (value > 23)
  841. throw new ArgumentOutOfRangeException("Lease time in hours must be between 0 to 23.");
  842. _leaseTimeHours = value;
  843. }
  844. }
  845. public byte LeaseTimeMinutes
  846. {
  847. get { return _leaseTimeMinutes; }
  848. set
  849. {
  850. if (value > 59)
  851. throw new ArgumentOutOfRangeException("Lease time in minutes must be between 0 to 59.");
  852. _leaseTimeMinutes = value;
  853. }
  854. }
  855. public ushort OfferDelayTime
  856. {
  857. get { return _offerDelayTime; }
  858. set { _offerDelayTime = value; }
  859. }
  860. public string DomainName
  861. {
  862. get { return _domainName; }
  863. set
  864. {
  865. if (value != null)
  866. DnsClient.IsDomainNameValid(value, true);
  867. _domainName = value;
  868. }
  869. }
  870. public uint DnsTtl
  871. {
  872. get { return _dnsTtl; }
  873. set { _dnsTtl = value; }
  874. }
  875. public IPAddress RouterAddress
  876. {
  877. get { return _routerAddress; }
  878. set { _routerAddress = value; }
  879. }
  880. public bool UseThisDnsServer
  881. {
  882. get { return _useThisDnsServer; }
  883. set
  884. {
  885. _useThisDnsServer = value;
  886. if (_useThisDnsServer)
  887. FindThisDnsServerAddress();
  888. }
  889. }
  890. public ICollection<IPAddress> DnsServers
  891. {
  892. get { return _dnsServers; }
  893. set
  894. {
  895. _dnsServers = value;
  896. if ((_dnsServers != null) && _dnsServers.Count > 0)
  897. _useThisDnsServer = false;
  898. }
  899. }
  900. public ICollection<IPAddress> WinsServers
  901. {
  902. get { return _winsServers; }
  903. set { _winsServers = value; }
  904. }
  905. public ICollection<IPAddress> NtpServers
  906. {
  907. get { return _ntpServers; }
  908. set { _ntpServers = value; }
  909. }
  910. public ICollection<ClasslessStaticRouteOption.Route> StaticRoutes
  911. {
  912. get { return _staticRoutes; }
  913. set { _staticRoutes = value; }
  914. }
  915. public ICollection<Exclusion> Exclusions
  916. {
  917. get { return _exclusions; }
  918. set
  919. {
  920. if (value == null)
  921. {
  922. _exclusions = null;
  923. }
  924. else
  925. {
  926. foreach (Exclusion exclusion in value)
  927. {
  928. if (!IsAddressInRange(exclusion.StartingAddress))
  929. throw new ArgumentOutOfRangeException("Exclusion starting address must be in scope range.");
  930. if (!IsAddressInRange(exclusion.EndingAddress))
  931. throw new ArgumentOutOfRangeException("Exclusion ending address must be in scope range.");
  932. }
  933. _exclusions = value;
  934. }
  935. }
  936. }
  937. public ICollection<Lease> ReservedLeases
  938. {
  939. get { return _reservedLeases.Values; }
  940. set
  941. {
  942. if (value == null)
  943. {
  944. _reservedLeases.Clear();
  945. }
  946. else
  947. {
  948. foreach (Lease reservedLease in value)
  949. {
  950. if (!IsAddressInRange(reservedLease.Address))
  951. throw new ArgumentOutOfRangeException("Reserved address must be in scope range.");
  952. }
  953. _reservedLeases.Clear();
  954. foreach (Lease reservedLease in value)
  955. _reservedLeases.TryAdd(reservedLease.ClientIdentifier, reservedLease);
  956. }
  957. }
  958. }
  959. public bool AllowOnlyReservedLeases
  960. {
  961. get { return _allowOnlyReservedLeases; }
  962. set { _allowOnlyReservedLeases = value; }
  963. }
  964. public ICollection<Lease> Leases
  965. { get { return _leases.Values; } }
  966. public IPAddress NetworkAddress
  967. { get { return _networkAddress; } }
  968. public IPAddress BroadcastAddress
  969. { get { return _broadcastAddress; } }
  970. public IPAddress InterfaceAddress
  971. { get { return _interfaceAddress; } }
  972. internal int InterfaceIndex
  973. { get { return _interfaceIndex; } }
  974. internal DateTime LastModified
  975. { get { return _lastModified; } }
  976. #endregion
  977. }
  978. }