SecondaryZone.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  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.Threading;
  19. using System.Threading.Tasks;
  20. using TechnitiumLibrary;
  21. using TechnitiumLibrary.Net.Dns;
  22. using TechnitiumLibrary.Net.Dns.ResourceRecords;
  23. namespace DnsServerCore.Dns.Zones
  24. {
  25. class SecondaryZone : ApexZone
  26. {
  27. #region variables
  28. readonly DnsServer _dnsServer;
  29. readonly object _refreshTimerLock = new object();
  30. Timer _refreshTimer;
  31. bool _refreshTimerTriggered;
  32. const int REFRESH_TIMER_INTERVAL = 5000;
  33. const int REFRESH_SOA_TIMEOUT = 10000;
  34. const int REFRESH_XFR_TIMEOUT = 120000;
  35. const int REFRESH_RETRIES = 5;
  36. const int REFRESH_TSIG_FUDGE = 300;
  37. DateTime _expiry;
  38. bool _isExpired;
  39. bool _resync;
  40. #endregion
  41. #region constructor
  42. public SecondaryZone(DnsServer dnsServer, AuthZoneInfo zoneInfo)
  43. : base(zoneInfo)
  44. {
  45. _dnsServer = dnsServer;
  46. _expiry = zoneInfo.Expiry;
  47. _isExpired = DateTime.UtcNow > _expiry;
  48. _refreshTimer = new Timer(RefreshTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
  49. InitNotify(_dnsServer);
  50. }
  51. private SecondaryZone(DnsServer dnsServer, string name)
  52. : base(name)
  53. {
  54. _dnsServer = dnsServer;
  55. _zoneTransfer = AuthZoneTransfer.Deny;
  56. _notify = AuthZoneNotify.None;
  57. _update = AuthZoneUpdate.Deny;
  58. InitNotify(_dnsServer);
  59. }
  60. #endregion
  61. #region static
  62. public static async Task<SecondaryZone> CreateAsync(DnsServer dnsServer, string name, string primaryNameServerAddresses = null, DnsTransportProtocol zoneTransferProtocol = DnsTransportProtocol.Tcp, string tsigKeyName = null)
  63. {
  64. switch (zoneTransferProtocol)
  65. {
  66. case DnsTransportProtocol.Tcp:
  67. case DnsTransportProtocol.Tls:
  68. case DnsTransportProtocol.Quic:
  69. break;
  70. default:
  71. throw new NotSupportedException("Zone transfer protocol is not supported: XFR-over-" + zoneTransferProtocol.ToString().ToUpper());
  72. }
  73. SecondaryZone secondaryZone = new SecondaryZone(dnsServer, name);
  74. DnsQuestionRecord soaQuestion = new DnsQuestionRecord(name, DnsResourceRecordType.SOA, DnsClass.IN);
  75. DnsDatagram soaResponse;
  76. NameServerAddress[] primaryNameServers = null;
  77. if (string.IsNullOrEmpty(primaryNameServerAddresses))
  78. {
  79. soaResponse = await secondaryZone._dnsServer.DirectQueryAsync(soaQuestion);
  80. }
  81. else
  82. {
  83. primaryNameServers = primaryNameServerAddresses.Split(delegate (string address)
  84. {
  85. NameServerAddress nameServer = NameServerAddress.Parse(address);
  86. if (nameServer.Protocol != zoneTransferProtocol)
  87. nameServer = nameServer.ChangeProtocol(zoneTransferProtocol);
  88. return nameServer;
  89. }, ',');
  90. DnsClient dnsClient = new DnsClient(primaryNameServers);
  91. foreach (NameServerAddress nameServerAddress in dnsClient.Servers)
  92. {
  93. if (nameServerAddress.IsIPEndPointStale)
  94. await nameServerAddress.ResolveIPAddressAsync(secondaryZone._dnsServer, secondaryZone._dnsServer.PreferIPv6);
  95. }
  96. dnsClient.Proxy = secondaryZone._dnsServer.Proxy;
  97. dnsClient.PreferIPv6 = secondaryZone._dnsServer.PreferIPv6;
  98. DnsDatagram soaRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, false, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { soaQuestion }, null, null, null, dnsServer.UdpPayloadSize);
  99. if (string.IsNullOrEmpty(tsigKeyName))
  100. soaResponse = await dnsClient.ResolveAsync(soaRequest);
  101. else if ((dnsServer.TsigKeys is not null) && dnsServer.TsigKeys.TryGetValue(tsigKeyName, out TsigKey key))
  102. soaResponse = await dnsClient.ResolveAsync(soaRequest, key, REFRESH_TSIG_FUDGE);
  103. else
  104. throw new DnsServerException("No such TSIG key was found configured: " + tsigKeyName);
  105. }
  106. if ((soaResponse.Answer.Count == 0) || (soaResponse.Answer[0].Type != DnsResourceRecordType.SOA))
  107. throw new DnsServerException("DNS Server failed to find SOA record for: " + name);
  108. DnsSOARecordData receivedSoa = soaResponse.Answer[0].RDATA as DnsSOARecordData;
  109. DnsSOARecordData soa = new DnsSOARecordData(receivedSoa.PrimaryNameServer, receivedSoa.ResponsiblePerson, 0u, receivedSoa.Refresh, receivedSoa.Retry, receivedSoa.Expire, receivedSoa.Minimum);
  110. DnsResourceRecord[] soaRR = new DnsResourceRecord[] { new DnsResourceRecord(secondaryZone._name, DnsResourceRecordType.SOA, DnsClass.IN, soa.Refresh, soa) };
  111. AuthRecordInfo authRecordInfo = soaRR[0].GetAuthRecordInfo();
  112. authRecordInfo.PrimaryNameServers = primaryNameServers;
  113. authRecordInfo.ZoneTransferProtocol = zoneTransferProtocol;
  114. authRecordInfo.TsigKeyName = tsigKeyName;
  115. secondaryZone._entries[DnsResourceRecordType.SOA] = soaRR;
  116. secondaryZone._isExpired = true; //new secondary zone is considered expired till it refreshes
  117. secondaryZone._refreshTimer = new Timer(secondaryZone.RefreshTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
  118. return secondaryZone;
  119. }
  120. #endregion
  121. #region IDisposable
  122. bool _disposed;
  123. protected override void Dispose(bool disposing)
  124. {
  125. try
  126. {
  127. if (_disposed)
  128. return;
  129. if (disposing)
  130. {
  131. lock (_refreshTimerLock)
  132. {
  133. if (_refreshTimer != null)
  134. {
  135. _refreshTimer.Dispose();
  136. _refreshTimer = null;
  137. }
  138. }
  139. }
  140. _disposed = true;
  141. }
  142. finally
  143. {
  144. base.Dispose(disposing);
  145. }
  146. }
  147. #endregion
  148. #region private
  149. private async void RefreshTimerCallback(object state)
  150. {
  151. try
  152. {
  153. if (_disabled && !_resync)
  154. return;
  155. _isExpired = DateTime.UtcNow > _expiry;
  156. //get primary name server addresses
  157. IReadOnlyList<NameServerAddress> primaryNameServers = await GetPrimaryNameServerAddressesAsync(_dnsServer);
  158. DnsResourceRecord currentSoaRecord = _entries[DnsResourceRecordType.SOA][0];
  159. DnsSOARecordData currentSoa = currentSoaRecord.RDATA as DnsSOARecordData;
  160. if (primaryNameServers.Count == 0)
  161. {
  162. LogManager log = _dnsServer.LogManager;
  163. if (log != null)
  164. log.Write("DNS Server could not find primary name server IP addresses for secondary zone: " + (_name == "" ? "<root>" : _name));
  165. //set timer for retry
  166. ResetRefreshTimer(currentSoa.Retry * 1000);
  167. _syncFailed = true;
  168. return;
  169. }
  170. AuthRecordInfo recordInfo = currentSoaRecord.GetAuthRecordInfo();
  171. TsigKey key = null;
  172. if (!string.IsNullOrEmpty(recordInfo.TsigKeyName) && ((_dnsServer.TsigKeys is null) || !_dnsServer.TsigKeys.TryGetValue(recordInfo.TsigKeyName, out key)))
  173. {
  174. LogManager log = _dnsServer.LogManager;
  175. if (log != null)
  176. log.Write("DNS Server does not have TSIG key '" + recordInfo.TsigKeyName + "' configured for refreshing secondary zone: " + (_name == "" ? "<root>" : _name));
  177. //set timer for retry
  178. ResetRefreshTimer(currentSoa.Retry * 1000);
  179. _syncFailed = true;
  180. return;
  181. }
  182. //refresh zone
  183. if (await RefreshZoneAsync(primaryNameServers, recordInfo.ZoneTransferProtocol, key))
  184. {
  185. //zone refreshed; set timer for refresh
  186. DnsSOARecordData latestSoa = _entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData;
  187. ResetRefreshTimer(latestSoa.Refresh * 1000);
  188. _syncFailed = false;
  189. _expiry = DateTime.UtcNow.AddSeconds(latestSoa.Expire);
  190. _isExpired = false;
  191. _resync = false;
  192. _dnsServer.AuthZoneManager.SaveZoneFile(_name);
  193. return;
  194. }
  195. //no response from any of the name servers; set timer for retry
  196. DnsSOARecordData soa = _entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData;
  197. ResetRefreshTimer(soa.Retry * 1000);
  198. _syncFailed = true;
  199. }
  200. catch (Exception ex)
  201. {
  202. LogManager log = _dnsServer.LogManager;
  203. if (log != null)
  204. log.Write(ex);
  205. //set timer for retry
  206. DnsSOARecordData soa = _entries[DnsResourceRecordType.SOA][0].RDATA as DnsSOARecordData;
  207. ResetRefreshTimer(soa.Retry * 1000);
  208. _syncFailed = true;
  209. }
  210. finally
  211. {
  212. _refreshTimerTriggered = false;
  213. }
  214. }
  215. private void ResetRefreshTimer(long dueTime)
  216. {
  217. lock (_refreshTimerLock)
  218. {
  219. if (_refreshTimer != null)
  220. _refreshTimer.Change(dueTime, Timeout.Infinite);
  221. }
  222. }
  223. private async Task<bool> RefreshZoneAsync(IReadOnlyList<NameServerAddress> primaryNameServers, DnsTransportProtocol zoneTransferProtocol, TsigKey key)
  224. {
  225. try
  226. {
  227. {
  228. LogManager log = _dnsServer.LogManager;
  229. if (log != null)
  230. log.Write("DNS Server has started zone refresh for secondary zone: " + (_name == "" ? "<root>" : _name));
  231. }
  232. DnsResourceRecord currentSoaRecord = _entries[DnsResourceRecordType.SOA][0];
  233. DnsSOARecordData currentSoa = currentSoaRecord.RDATA as DnsSOARecordData;
  234. if (!_resync)
  235. {
  236. //check for update; use UDP transport
  237. List<NameServerAddress> udpNameServers = new List<NameServerAddress>(primaryNameServers.Count);
  238. foreach (NameServerAddress primaryNameServer in primaryNameServers)
  239. {
  240. if (primaryNameServer.Protocol == DnsTransportProtocol.Udp)
  241. udpNameServers.Add(primaryNameServer);
  242. else
  243. udpNameServers.Add(primaryNameServer.ChangeProtocol(DnsTransportProtocol.Udp));
  244. }
  245. DnsClient client = new DnsClient(udpNameServers);
  246. client.Proxy = _dnsServer.Proxy;
  247. client.PreferIPv6 = _dnsServer.PreferIPv6;
  248. client.Timeout = REFRESH_SOA_TIMEOUT;
  249. client.Retries = REFRESH_RETRIES;
  250. client.Concurrency = 1;
  251. DnsDatagram soaRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, false, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { new DnsQuestionRecord(_name, DnsResourceRecordType.SOA, DnsClass.IN) }, null, null, null, _dnsServer.UdpPayloadSize);
  252. DnsDatagram soaResponse;
  253. if (key is null)
  254. soaResponse = await client.ResolveAsync(soaRequest);
  255. else
  256. soaResponse = await client.ResolveAsync(soaRequest, key, REFRESH_TSIG_FUDGE);
  257. if (soaResponse.RCODE != DnsResponseCode.NoError)
  258. {
  259. LogManager log = _dnsServer.LogManager;
  260. if (log != null)
  261. log.Write("DNS Server received RCODE=" + soaResponse.RCODE.ToString() + " for '" + (_name == "" ? "<root>" : _name) + "' secondary zone refresh from: " + soaResponse.Metadata.NameServer.ToString());
  262. return false;
  263. }
  264. if ((soaResponse.Answer.Count < 1) || (soaResponse.Answer[0].Type != DnsResourceRecordType.SOA) || !_name.Equals(soaResponse.Answer[0].Name, StringComparison.OrdinalIgnoreCase))
  265. {
  266. LogManager log = _dnsServer.LogManager;
  267. if (log != null)
  268. log.Write("DNS Server received an empty response for SOA query for '" + (_name == "" ? "<root>" : _name) + "' secondary zone refresh from: " + soaResponse.Metadata.NameServer.ToString());
  269. return false;
  270. }
  271. DnsResourceRecord receivedSoaRecord = soaResponse.Answer[0];
  272. DnsSOARecordData receivedSoa = receivedSoaRecord.RDATA as DnsSOARecordData;
  273. //compare using sequence space arithmetic
  274. if (!currentSoa.IsZoneUpdateAvailable(receivedSoa))
  275. {
  276. LogManager log = _dnsServer.LogManager;
  277. if (log != null)
  278. log.Write("DNS Server successfully checked for '" + (_name == "" ? "<root>" : _name) + "' secondary zone update from: " + soaResponse.Metadata.NameServer.ToString());
  279. return true;
  280. }
  281. }
  282. //update available; do zone transfer with TLS, QUIC, or TCP transport
  283. List<NameServerAddress> updatedNameServers = new List<NameServerAddress>(primaryNameServers.Count);
  284. switch (zoneTransferProtocol)
  285. {
  286. case DnsTransportProtocol.Tls:
  287. case DnsTransportProtocol.Quic:
  288. //change name server protocol to TLS/QUIC
  289. foreach (NameServerAddress primaryNameServer in primaryNameServers)
  290. {
  291. if (primaryNameServer.Protocol == zoneTransferProtocol)
  292. updatedNameServers.Add(primaryNameServer);
  293. else
  294. updatedNameServers.Add(primaryNameServer.ChangeProtocol(zoneTransferProtocol));
  295. }
  296. break;
  297. default:
  298. //change name server protocol to TCP
  299. foreach (NameServerAddress primaryNameServer in primaryNameServers)
  300. {
  301. if (primaryNameServer.Protocol == DnsTransportProtocol.Tcp)
  302. updatedNameServers.Add(primaryNameServer);
  303. else
  304. updatedNameServers.Add(primaryNameServer.ChangeProtocol(DnsTransportProtocol.Tcp));
  305. }
  306. break;
  307. }
  308. DnsClient xfrClient = new DnsClient(updatedNameServers);
  309. xfrClient.Proxy = _dnsServer.Proxy;
  310. xfrClient.PreferIPv6 = _dnsServer.PreferIPv6;
  311. xfrClient.Timeout = REFRESH_XFR_TIMEOUT;
  312. xfrClient.Retries = REFRESH_RETRIES;
  313. xfrClient.Concurrency = 1;
  314. bool doIXFR = !_isExpired && !_resync;
  315. while (true)
  316. {
  317. DnsQuestionRecord xfrQuestion;
  318. IReadOnlyList<DnsResourceRecord> xfrAuthority;
  319. if (doIXFR)
  320. {
  321. xfrQuestion = new DnsQuestionRecord(_name, DnsResourceRecordType.IXFR, DnsClass.IN);
  322. xfrAuthority = new DnsResourceRecord[] { currentSoaRecord };
  323. }
  324. else
  325. {
  326. xfrQuestion = new DnsQuestionRecord(_name, DnsResourceRecordType.AXFR, DnsClass.IN);
  327. xfrAuthority = null;
  328. }
  329. DnsDatagram xfrRequest = new DnsDatagram(0, false, DnsOpcode.StandardQuery, false, false, false, false, false, false, DnsResponseCode.NoError, new DnsQuestionRecord[] { xfrQuestion }, null, xfrAuthority);
  330. DnsDatagram xfrResponse;
  331. if (key is null)
  332. xfrResponse = await xfrClient.ResolveAsync(xfrRequest);
  333. else
  334. xfrResponse = await xfrClient.ResolveAsync(xfrRequest, key, REFRESH_TSIG_FUDGE);
  335. if (doIXFR && (xfrResponse.RCODE == DnsResponseCode.NotImplemented))
  336. {
  337. doIXFR = false;
  338. continue;
  339. }
  340. if (xfrResponse.RCODE != DnsResponseCode.NoError)
  341. {
  342. LogManager log = _dnsServer.LogManager;
  343. if (log != null)
  344. log.Write("DNS Server received a zone transfer response (RCODE=" + xfrResponse.RCODE.ToString() + ") for '" + (_name == "" ? "<root>" : _name) + "' secondary zone from: " + xfrResponse.Metadata.NameServer.ToString());
  345. return false;
  346. }
  347. if (xfrResponse.Answer.Count < 1)
  348. {
  349. LogManager log = _dnsServer.LogManager;
  350. if (log != null)
  351. log.Write("DNS Server received an empty response for zone transfer query for '" + (_name == "" ? "<root>" : _name) + "' secondary zone from: " + xfrResponse.Metadata.NameServer.ToString());
  352. return false;
  353. }
  354. if (!_name.Equals(xfrResponse.Answer[0].Name, StringComparison.OrdinalIgnoreCase) || (xfrResponse.Answer[0].Type != DnsResourceRecordType.SOA) || (xfrResponse.Answer[0].RDATA is not DnsSOARecordData xfrSoa))
  355. {
  356. LogManager log = _dnsServer.LogManager;
  357. if (log != null)
  358. log.Write("DNS Server received invalid response for zone transfer query for '" + (_name == "" ? "<root>" : _name) + "' secondary zone from: " + xfrResponse.Metadata.NameServer.ToString());
  359. return false;
  360. }
  361. if (_resync || currentSoa.IsZoneUpdateAvailable(xfrSoa))
  362. {
  363. xfrResponse = xfrResponse.Join(); //join multi message response
  364. if (doIXFR)
  365. {
  366. IReadOnlyList<DnsResourceRecord> historyRecords = _dnsServer.AuthZoneManager.SyncIncrementalZoneTransferRecords(_name, xfrResponse.Answer);
  367. if (historyRecords.Count > 0)
  368. CommitZoneHistory(historyRecords);
  369. else
  370. ClearZoneHistory(); //AXFR response was received
  371. }
  372. else
  373. {
  374. _dnsServer.AuthZoneManager.SyncZoneTransferRecords(_name, xfrResponse.Answer);
  375. ClearZoneHistory();
  376. }
  377. //trigger notify
  378. TriggerNotify();
  379. LogManager log = _dnsServer.LogManager;
  380. if (log != null)
  381. log.Write("DNS Server successfully refreshed '" + (_name == "" ? "<root>" : _name) + "' secondary zone from: " + xfrResponse.Metadata.NameServer.ToString());
  382. }
  383. else
  384. {
  385. LogManager log = _dnsServer.LogManager;
  386. if (log != null)
  387. log.Write("DNS Server successfully checked for '" + (_name == "" ? "<root>" : _name) + "' secondary zone update from: " + xfrResponse.Metadata.NameServer.ToString());
  388. }
  389. return true;
  390. }
  391. }
  392. catch (Exception ex)
  393. {
  394. LogManager log = _dnsServer.LogManager;
  395. if (log != null)
  396. {
  397. string strNameServers = null;
  398. foreach (NameServerAddress nameServer in primaryNameServers)
  399. {
  400. if (strNameServers == null)
  401. strNameServers = nameServer.ToString();
  402. else
  403. strNameServers += ", " + nameServer.ToString();
  404. }
  405. log.Write("DNS Server failed to refresh '" + (_name == "" ? "<root>" : _name) + "' secondary zone from: " + strNameServers + "\r\n" + ex.ToString());
  406. }
  407. return false;
  408. }
  409. }
  410. private void CommitZoneHistory(IReadOnlyList<DnsResourceRecord> historyRecords)
  411. {
  412. lock (_zoneHistory)
  413. {
  414. historyRecords[0].GetAuthRecordInfo().DeletedOn = DateTime.UtcNow;
  415. //write history
  416. _zoneHistory.AddRange(historyRecords);
  417. CleanupHistory(_zoneHistory);
  418. }
  419. }
  420. private void ClearZoneHistory()
  421. {
  422. lock (_zoneHistory)
  423. {
  424. _zoneHistory.Clear();
  425. }
  426. }
  427. #endregion
  428. #region public
  429. public void TriggerRefresh(int refreshInterval = REFRESH_TIMER_INTERVAL)
  430. {
  431. if (_disabled)
  432. return;
  433. if (_refreshTimerTriggered)
  434. return;
  435. _refreshTimerTriggered = true;
  436. ResetRefreshTimer(refreshInterval);
  437. }
  438. public void TriggerResync()
  439. {
  440. if (_refreshTimerTriggered)
  441. return;
  442. _resync = true;
  443. _refreshTimerTriggered = true;
  444. ResetRefreshTimer(0);
  445. }
  446. public override void SetRecords(DnsResourceRecordType type, IReadOnlyList<DnsResourceRecord> records)
  447. {
  448. switch (type)
  449. {
  450. case DnsResourceRecordType.SOA:
  451. if ((records.Count != 1) || !records[0].Name.Equals(_name, StringComparison.OrdinalIgnoreCase))
  452. throw new InvalidOperationException("Invalid SOA record.");
  453. DnsResourceRecord existingSoaRecord = _entries[DnsResourceRecordType.SOA][0];
  454. DnsResourceRecord newSoaRecord = records[0];
  455. existingSoaRecord.CopyRecordInfoFrom(newSoaRecord);
  456. break;
  457. default:
  458. throw new InvalidOperationException("Cannot set records in secondary zone.");
  459. }
  460. }
  461. public override void AddRecord(DnsResourceRecord record)
  462. {
  463. throw new InvalidOperationException("Cannot add record in secondary zone.");
  464. }
  465. public override bool DeleteRecord(DnsResourceRecordType type, DnsResourceRecordData record)
  466. {
  467. throw new InvalidOperationException("Cannot delete record in secondary zone.");
  468. }
  469. public override bool DeleteRecords(DnsResourceRecordType type)
  470. {
  471. throw new InvalidOperationException("Cannot delete records in secondary zone.");
  472. }
  473. public override void UpdateRecord(DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
  474. {
  475. throw new InvalidOperationException("Cannot update record in secondary zone.");
  476. }
  477. #endregion
  478. #region properties
  479. public override AuthZoneUpdate Update
  480. {
  481. get { return _update; }
  482. set { throw new InvalidOperationException(); }
  483. }
  484. public DateTime Expiry
  485. { get { return _expiry; } }
  486. public bool IsExpired
  487. { get { return _isExpired; } }
  488. public override bool Disabled
  489. {
  490. get { return _disabled; }
  491. set
  492. {
  493. if (_disabled != value)
  494. {
  495. _disabled = value;
  496. if (_disabled)
  497. {
  498. DisableNotifyTimer();
  499. ResetRefreshTimer(Timeout.Infinite);
  500. }
  501. else
  502. {
  503. TriggerNotify();
  504. TriggerRefresh();
  505. }
  506. }
  507. }
  508. }
  509. public override bool IsActive
  510. {
  511. get { return !_disabled && !_isExpired; }
  512. }
  513. #endregion
  514. }
  515. }