ApexZone.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2023 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.Dns.ResourceRecords;
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Net;
  19. using System.Threading;
  20. using System.Threading.Tasks;
  21. using TechnitiumLibrary.Net.Dns;
  22. using TechnitiumLibrary.Net.Dns.ResourceRecords;
  23. namespace DnsServerCore.Dns.Zones
  24. {
  25. public enum AuthZoneTransfer : byte
  26. {
  27. Deny = 0,
  28. Allow = 1,
  29. AllowOnlyZoneNameServers = 2,
  30. AllowOnlySpecifiedNameServers = 3,
  31. AllowBothZoneAndSpecifiedNameServers = 4
  32. }
  33. public enum AuthZoneNotify : byte
  34. {
  35. None = 0,
  36. ZoneNameServers = 1,
  37. SpecifiedNameServers = 2,
  38. BothZoneAndSpecifiedNameServers = 3
  39. }
  40. public enum AuthZoneUpdate : byte
  41. {
  42. Deny = 0,
  43. Allow = 1,
  44. AllowOnlyZoneNameServers = 2,
  45. AllowOnlySpecifiedIpAddresses = 3,
  46. AllowBothZoneNameServersAndSpecifiedIpAddresses = 4
  47. }
  48. abstract class ApexZone : AuthZone, IDisposable
  49. {
  50. #region variables
  51. protected AuthZoneTransfer _zoneTransfer;
  52. protected IReadOnlyCollection<IPAddress> _zoneTransferNameServers;
  53. protected AuthZoneNotify _notify;
  54. protected IReadOnlyCollection<IPAddress> _notifyNameServers;
  55. protected AuthZoneUpdate _update;
  56. protected IReadOnlyCollection<IPAddress> _updateIpAddresses;
  57. protected List<DnsResourceRecord> _zoneHistory; //for IXFR support
  58. protected IReadOnlyDictionary<string, object> _zoneTransferTsigKeyNames;
  59. protected IReadOnlyDictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> _updateSecurityPolicies;
  60. protected AuthZoneDnssecStatus _dnssecStatus;
  61. Timer _notifyTimer;
  62. bool _notifyTimerTriggered;
  63. const int NOTIFY_TIMER_INTERVAL = 10000;
  64. List<string> _notifyList;
  65. List<string> _notifyFailed;
  66. const int NOTIFY_TIMEOUT = 10000;
  67. const int NOTIFY_RETRIES = 5;
  68. protected bool _syncFailed;
  69. #endregion
  70. #region constructor
  71. protected ApexZone(AuthZoneInfo zoneInfo)
  72. : base(zoneInfo)
  73. {
  74. _zoneTransfer = zoneInfo.ZoneTransfer;
  75. _zoneTransferNameServers = zoneInfo.ZoneTransferNameServers;
  76. _notify = zoneInfo.Notify;
  77. _notifyNameServers = zoneInfo.NotifyNameServers;
  78. _update = zoneInfo.Update;
  79. _updateIpAddresses = zoneInfo.UpdateIpAddresses;
  80. if (zoneInfo.ZoneHistory is null)
  81. _zoneHistory = new List<DnsResourceRecord>();
  82. else
  83. _zoneHistory = new List<DnsResourceRecord>(zoneInfo.ZoneHistory);
  84. _zoneTransferTsigKeyNames = zoneInfo.ZoneTransferTsigKeyNames;
  85. _updateSecurityPolicies = zoneInfo.UpdateSecurityPolicies;
  86. }
  87. protected ApexZone(string name)
  88. : base(name)
  89. {
  90. _zoneHistory = new List<DnsResourceRecord>();
  91. }
  92. #endregion
  93. #region IDisposable
  94. bool _disposed;
  95. protected virtual void Dispose(bool disposing)
  96. {
  97. if (_disposed)
  98. return;
  99. if (disposing)
  100. {
  101. if (_notifyTimer is not null)
  102. _notifyTimer.Dispose();
  103. }
  104. _disposed = true;
  105. }
  106. public void Dispose()
  107. {
  108. Dispose(true);
  109. }
  110. #endregion
  111. #region protected
  112. protected void CleanupHistory(List<DnsResourceRecord> history)
  113. {
  114. DnsSOARecordData soa = _entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData;
  115. DateTime expiry = DateTime.UtcNow.AddSeconds(-soa.Expire);
  116. int index = 0;
  117. while (index < history.Count)
  118. {
  119. //check difference sequence
  120. if (history[index].GetAuthRecordInfo().DeletedOn > expiry)
  121. break; //found record to keep
  122. //skip to next difference sequence
  123. index++;
  124. int soaCount = 1;
  125. while (index < history.Count)
  126. {
  127. if (history[index].Type == DnsResourceRecordType.SOA)
  128. {
  129. soaCount++;
  130. if (soaCount == 3)
  131. break;
  132. }
  133. index++;
  134. }
  135. }
  136. if (index == history.Count)
  137. {
  138. //delete entire history
  139. history.Clear();
  140. return;
  141. }
  142. //remove expired records
  143. history.RemoveRange(0, index);
  144. }
  145. protected void InitNotify(DnsServer dnsServer)
  146. {
  147. _notifyTimer = new Timer(NotifyTimerCallback, dnsServer, Timeout.Infinite, Timeout.Infinite);
  148. _notifyList = new List<string>();
  149. _notifyFailed = new List<string>();
  150. }
  151. protected void DisableNotifyTimer()
  152. {
  153. if (_notifyTimer is not null)
  154. _notifyTimer.Change(Timeout.Infinite, Timeout.Infinite);
  155. }
  156. #endregion
  157. #region private
  158. private async void NotifyTimerCallback(object state)
  159. {
  160. DnsServer dnsServer = state as DnsServer;
  161. List<string> notifiedNameServers = new List<string>();
  162. async Task NotifyZoneNameServersAsync(bool onlyFailedNameServers)
  163. {
  164. string primaryNameServer = (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).PrimaryNameServer;
  165. IReadOnlyList<DnsResourceRecord> nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords
  166. //notify all secondary name servers
  167. List<Task> tasks = new List<Task>();
  168. foreach (DnsResourceRecord nsRecord in nsRecords)
  169. {
  170. if (nsRecord.GetAuthRecordInfo().Disabled)
  171. continue;
  172. string nameServerHost = (nsRecord.RDATA as DnsNSRecordData).NameServer;
  173. if (primaryNameServer.Equals(nameServerHost, StringComparison.OrdinalIgnoreCase))
  174. continue; //skip primary name server
  175. if (onlyFailedNameServers)
  176. {
  177. lock (_notifyFailed)
  178. {
  179. if (!_notifyFailed.Contains(nameServerHost))
  180. continue;
  181. }
  182. }
  183. notifiedNameServers.Add(nameServerHost);
  184. List<NameServerAddress> nameServers = new List<NameServerAddress>(2);
  185. await ResolveNameServerAddressesAsync(dnsServer, nsRecord, nameServers);
  186. if (nameServers.Count > 0)
  187. {
  188. tasks.Add(NotifyNameServerAsync(dnsServer, nameServerHost, nameServers));
  189. }
  190. else
  191. {
  192. lock (_notifyFailed)
  193. {
  194. if (!_notifyFailed.Contains(nameServerHost))
  195. _notifyFailed.Add(nameServerHost);
  196. }
  197. dnsServer.LogManager?.Write("DNS Server failed to notify name server '" + nameServerHost + "' due to failure in resolving its IP address for zone: " + (_name == "" ? "<root>" : _name));
  198. }
  199. }
  200. await Task.WhenAll(tasks);
  201. }
  202. async Task NotifySpecifiedNameServersAsync(bool onlyFailedNameServers)
  203. {
  204. IReadOnlyCollection<IPAddress> specifiedNameServers = _notifyNameServers;
  205. if (specifiedNameServers is not null)
  206. {
  207. List<Task> tasks = new List<Task>();
  208. foreach (IPAddress specifiedNameServer in specifiedNameServers)
  209. {
  210. string nameServerHost = specifiedNameServer.ToString();
  211. if (onlyFailedNameServers)
  212. {
  213. lock (_notifyFailed)
  214. {
  215. if (!_notifyFailed.Contains(nameServerHost))
  216. continue;
  217. }
  218. }
  219. notifiedNameServers.Add(nameServerHost);
  220. tasks.Add(NotifyNameServerAsync(dnsServer, nameServerHost, new NameServerAddress[] { new NameServerAddress(specifiedNameServer) }));
  221. }
  222. await Task.WhenAll(tasks);
  223. }
  224. }
  225. try
  226. {
  227. switch (_notify)
  228. {
  229. case AuthZoneNotify.ZoneNameServers:
  230. await NotifyZoneNameServersAsync(!_notifyTimerTriggered);
  231. break;
  232. case AuthZoneNotify.SpecifiedNameServers:
  233. await NotifySpecifiedNameServersAsync(!_notifyTimerTriggered);
  234. break;
  235. case AuthZoneNotify.BothZoneAndSpecifiedNameServers:
  236. Task t1 = NotifyZoneNameServersAsync(!_notifyTimerTriggered);
  237. Task t2 = NotifySpecifiedNameServersAsync(!_notifyTimerTriggered);
  238. await Task.WhenAll(t1, t2);
  239. break;
  240. }
  241. //remove non-existent name servers from notify failed list
  242. lock (_notifyFailed)
  243. {
  244. if (_notifyFailed.Count > 0)
  245. {
  246. List<string> toRemove = new List<string>();
  247. foreach (string failedNameServer in _notifyFailed)
  248. {
  249. if (!notifiedNameServers.Contains(failedNameServer))
  250. toRemove.Add(failedNameServer);
  251. }
  252. foreach (string failedNameServer in toRemove)
  253. _notifyFailed.Remove(failedNameServer);
  254. if (_notifyFailed.Count > 0)
  255. {
  256. //set timer to notify failed name servers again
  257. int retryInterval = (int)((_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).Retry * 1000);
  258. _notifyTimer.Change(retryInterval, Timeout.Infinite);
  259. }
  260. }
  261. }
  262. }
  263. catch (Exception ex)
  264. {
  265. dnsServer.LogManager?.Write(ex);
  266. }
  267. finally
  268. {
  269. _notifyTimerTriggered = false;
  270. }
  271. }
  272. private async Task NotifyNameServerAsync(DnsServer dnsServer, string nameServerHost, IReadOnlyList<NameServerAddress> nameServers)
  273. {
  274. //use notify list to prevent multiple threads from notifying the same name server
  275. lock (_notifyList)
  276. {
  277. if (_notifyList.Contains(nameServerHost))
  278. return; //already notifying the name server in another thread
  279. _notifyList.Add(nameServerHost);
  280. }
  281. try
  282. {
  283. DnsClient client = new DnsClient(nameServers);
  284. client.Proxy = dnsServer.Proxy;
  285. client.Timeout = NOTIFY_TIMEOUT;
  286. client.Retries = NOTIFY_RETRIES;
  287. DnsDatagram notifyRequest = new DnsDatagram(0, false, DnsOpcode.Notify, true, false, false, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord(_name, DnsResourceRecordType.SOA, DnsClass.IN) }, _entries[DnsResourceRecordType.SOA]);
  288. DnsDatagram response = await client.ResolveAsync(notifyRequest);
  289. switch (response.RCODE)
  290. {
  291. case DnsResponseCode.NoError:
  292. case DnsResponseCode.NotImplemented:
  293. {
  294. //transaction complete
  295. lock (_notifyFailed)
  296. {
  297. _notifyFailed.Remove(nameServerHost);
  298. }
  299. LogManager log = dnsServer.LogManager;
  300. if (log is not null)
  301. log.Write("DNS Server successfully notified name server '" + nameServerHost + "' for zone: " + (_name == "" ? "<root>" : _name));
  302. }
  303. break;
  304. default:
  305. {
  306. //transaction failed
  307. lock (_notifyFailed)
  308. {
  309. if (!_notifyFailed.Contains(nameServerHost))
  310. _notifyFailed.Add(nameServerHost);
  311. }
  312. LogManager log = dnsServer.LogManager;
  313. if (log is not null)
  314. log.Write("DNS Server failed to notify name server '" + nameServerHost + "' (RCODE=" + response.RCODE.ToString() + ") for zone : " + (_name == "" ? "<root>" : _name));
  315. }
  316. break;
  317. }
  318. }
  319. catch (Exception ex)
  320. {
  321. lock (_notifyFailed)
  322. {
  323. if (!_notifyFailed.Contains(nameServerHost))
  324. _notifyFailed.Add(nameServerHost);
  325. }
  326. dnsServer.LogManager?.Write("DNS Server failed to notify name server '" + nameServerHost + "' for zone: " + (_name == "" ? "<root>" : _name) + "\r\n" + ex.ToString());
  327. }
  328. finally
  329. {
  330. lock (_notifyList)
  331. {
  332. _notifyList.Remove(nameServerHost);
  333. }
  334. }
  335. }
  336. private static async Task ResolveNameServerAddressesAsync(DnsServer dnsServer, string nsDomain, int port, DnsTransportProtocol protocol, List<NameServerAddress> outNameServers)
  337. {
  338. try
  339. {
  340. DnsDatagram response = await dnsServer.DirectQueryAsync(new DnsQuestionRecord(nsDomain, DnsResourceRecordType.A, DnsClass.IN));
  341. if (response.Answer.Count > 0)
  342. {
  343. IReadOnlyList<IPAddress> addresses = DnsClient.ParseResponseA(response);
  344. foreach (IPAddress address in addresses)
  345. outNameServers.Add(new NameServerAddress(nsDomain, new IPEndPoint(address, port), protocol));
  346. }
  347. }
  348. catch
  349. { }
  350. if (dnsServer.PreferIPv6)
  351. {
  352. try
  353. {
  354. DnsDatagram response = await dnsServer.DirectQueryAsync(new DnsQuestionRecord(nsDomain, DnsResourceRecordType.AAAA, DnsClass.IN));
  355. if (response.Answer.Count > 0)
  356. {
  357. IReadOnlyList<IPAddress> addresses = DnsClient.ParseResponseAAAA(response);
  358. foreach (IPAddress address in addresses)
  359. outNameServers.Add(new NameServerAddress(nsDomain, new IPEndPoint(address, port), protocol));
  360. }
  361. }
  362. catch
  363. { }
  364. }
  365. }
  366. private static Task ResolveNameServerAddressesAsync(DnsServer dnsServer, DnsResourceRecord nsRecord, List<NameServerAddress> outNameServers)
  367. {
  368. string nsDomain = (nsRecord.RDATA as DnsNSRecordData).NameServer;
  369. IReadOnlyList<DnsResourceRecord> glueRecords = nsRecord.GetAuthRecordInfo().GlueRecords;
  370. if (glueRecords is not null)
  371. {
  372. foreach (DnsResourceRecord glueRecord in glueRecords)
  373. {
  374. switch (glueRecord.Type)
  375. {
  376. case DnsResourceRecordType.A:
  377. outNameServers.Add(new NameServerAddress(nsDomain, (glueRecord.RDATA as DnsARecordData).Address));
  378. break;
  379. case DnsResourceRecordType.AAAA:
  380. if (dnsServer.PreferIPv6)
  381. outNameServers.Add(new NameServerAddress(nsDomain, (glueRecord.RDATA as DnsAAAARecordData).Address));
  382. break;
  383. }
  384. }
  385. return Task.CompletedTask;
  386. }
  387. else
  388. {
  389. return ResolveNameServerAddressesAsync(dnsServer, nsDomain, 53, DnsTransportProtocol.Udp, outNameServers);
  390. }
  391. }
  392. internal virtual void UpdateDnssecStatus()
  393. {
  394. if (!_entries.ContainsKey(DnsResourceRecordType.DNSKEY))
  395. _dnssecStatus = AuthZoneDnssecStatus.Unsigned;
  396. else if (_entries.ContainsKey(DnsResourceRecordType.NSEC3PARAM))
  397. _dnssecStatus = AuthZoneDnssecStatus.SignedWithNSEC3;
  398. else
  399. _dnssecStatus = AuthZoneDnssecStatus.SignedWithNSEC;
  400. }
  401. #endregion
  402. #region public
  403. public IReadOnlyList<DnsResourceRecord> GetZoneHistory()
  404. {
  405. lock (_zoneHistory)
  406. {
  407. return _zoneHistory.ToArray();
  408. }
  409. }
  410. public void TriggerNotify()
  411. {
  412. if (_disabled)
  413. return;
  414. if (_notify == AuthZoneNotify.None)
  415. {
  416. if (_notifyFailed is not null)
  417. {
  418. lock (_notifyFailed)
  419. {
  420. _notifyFailed.Clear();
  421. }
  422. }
  423. return;
  424. }
  425. if (_notifyTimerTriggered)
  426. return;
  427. if (_disposed)
  428. return;
  429. if (_notifyTimer is null)
  430. return;
  431. _notifyTimer.Change(NOTIFY_TIMER_INTERVAL, Timeout.Infinite);
  432. _notifyTimerTriggered = true;
  433. }
  434. public async Task<IReadOnlyList<NameServerAddress>> GetPrimaryNameServerAddressesAsync(DnsServer dnsServer)
  435. {
  436. DnsResourceRecord soaRecord = _entries[DnsResourceRecordType.SOA][0];
  437. IReadOnlyList<NameServerAddress> primaryNameServers = soaRecord.GetAuthRecordInfo().PrimaryNameServers;
  438. if (primaryNameServers is not null)
  439. {
  440. List<NameServerAddress> resolvedNameServers = new List<NameServerAddress>(primaryNameServers.Count * 2);
  441. foreach (NameServerAddress nameServer in primaryNameServers)
  442. {
  443. if (nameServer.IsIPEndPointStale)
  444. await ResolveNameServerAddressesAsync(dnsServer, nameServer.Host, nameServer.Port, nameServer.Protocol, resolvedNameServers);
  445. else
  446. resolvedNameServers.Add(nameServer);
  447. }
  448. return resolvedNameServers;
  449. }
  450. string primaryNameServer = (soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer;
  451. IReadOnlyList<DnsResourceRecord> nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords
  452. List<NameServerAddress> nameServers = new List<NameServerAddress>(nsRecords.Count * 2);
  453. foreach (DnsResourceRecord nsRecord in nsRecords)
  454. {
  455. if (nsRecord.GetAuthRecordInfo().Disabled)
  456. continue;
  457. if (primaryNameServer.Equals((nsRecord.RDATA as DnsNSRecordData).NameServer, StringComparison.OrdinalIgnoreCase))
  458. {
  459. //found primary NS
  460. await ResolveNameServerAddressesAsync(dnsServer, nsRecord, nameServers);
  461. break;
  462. }
  463. }
  464. if (nameServers.Count < 1)
  465. await ResolveNameServerAddressesAsync(dnsServer, primaryNameServer, 53, DnsTransportProtocol.Udp, nameServers);
  466. return nameServers;
  467. }
  468. public async Task<IReadOnlyList<NameServerAddress>> GetSecondaryNameServerAddressesAsync(DnsServer dnsServer)
  469. {
  470. string primaryNameServer = (_entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData).PrimaryNameServer;
  471. IReadOnlyList<DnsResourceRecord> nsRecords = GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant use QueryRecords
  472. List<NameServerAddress> nameServers = new List<NameServerAddress>(nsRecords.Count * 2);
  473. foreach (DnsResourceRecord nsRecord in nsRecords)
  474. {
  475. if (nsRecord.GetAuthRecordInfo().Disabled)
  476. continue;
  477. if (primaryNameServer.Equals((nsRecord.RDATA as DnsNSRecordData).NameServer, StringComparison.OrdinalIgnoreCase))
  478. continue; //skip primary name server
  479. await ResolveNameServerAddressesAsync(dnsServer, nsRecord, nameServers);
  480. }
  481. return nameServers;
  482. }
  483. #endregion
  484. #region properties
  485. public virtual AuthZoneTransfer ZoneTransfer
  486. {
  487. get { return _zoneTransfer; }
  488. set { _zoneTransfer = value; }
  489. }
  490. public IReadOnlyCollection<IPAddress> ZoneTransferNameServers
  491. {
  492. get { return _zoneTransferNameServers; }
  493. set
  494. {
  495. if ((value is not null) && (value.Count > byte.MaxValue))
  496. throw new ArgumentOutOfRangeException(nameof(ZoneTransferNameServers), "Name server addresses cannot be more than 255.");
  497. _zoneTransferNameServers = value;
  498. }
  499. }
  500. public virtual AuthZoneNotify Notify
  501. {
  502. get { return _notify; }
  503. set
  504. {
  505. if (_notify != value)
  506. {
  507. _notify = value;
  508. lock (_notifyFailed)
  509. {
  510. _notifyFailed.Clear();
  511. }
  512. }
  513. }
  514. }
  515. public IReadOnlyCollection<IPAddress> NotifyNameServers
  516. {
  517. get { return _notifyNameServers; }
  518. set
  519. {
  520. if ((value is not null) && (value.Count > byte.MaxValue))
  521. throw new ArgumentOutOfRangeException(nameof(NotifyNameServers), "Name server addresses cannot be more than 255.");
  522. if (_notifyNameServers != value)
  523. {
  524. _notifyNameServers = value;
  525. lock (_notifyFailed)
  526. {
  527. _notifyFailed.Clear();
  528. }
  529. }
  530. }
  531. }
  532. public virtual AuthZoneUpdate Update
  533. {
  534. get { return _update; }
  535. set { _update = value; }
  536. }
  537. public IReadOnlyCollection<IPAddress> UpdateIpAddresses
  538. {
  539. get { return _updateIpAddresses; }
  540. set
  541. {
  542. if ((value is not null) && (value.Count > byte.MaxValue))
  543. throw new ArgumentOutOfRangeException(nameof(ZoneTransferNameServers), "IP addresses cannot be more than 255.");
  544. _updateIpAddresses = value;
  545. }
  546. }
  547. public IReadOnlyDictionary<string, object> ZoneTransferTsigKeyNames
  548. {
  549. get { return _zoneTransferTsigKeyNames; }
  550. set { _zoneTransferTsigKeyNames = value; }
  551. }
  552. public IReadOnlyDictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> UpdateSecurityPolicies
  553. {
  554. get { return _updateSecurityPolicies; }
  555. set { _updateSecurityPolicies = value; }
  556. }
  557. public string[] NotifyFailed
  558. {
  559. get
  560. {
  561. if (_notifyFailed is null)
  562. return Array.Empty<string>();
  563. lock (_notifyFailed)
  564. {
  565. if (_notifyFailed.Count > 0)
  566. return _notifyFailed.ToArray();
  567. return Array.Empty<string>();
  568. }
  569. }
  570. }
  571. public bool SyncFailed
  572. { get { return _syncFailed; } }
  573. public AuthZoneDnssecStatus DnssecStatus
  574. { get { return _dnssecStatus; } }
  575. #endregion
  576. }
  577. }