diff --git a/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs
new file mode 100644
index 00000000..35b903b8
--- /dev/null
+++ b/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs
@@ -0,0 +1,962 @@
+/*
+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 .
+
+*/
+
+using DnsServerCore.Dns.ResourceRecords;
+using DnsServerCore.Dns.Zones;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using TechnitiumLibrary.Net.Dns;
+using TechnitiumLibrary.Net.Dns.ResourceRecords;
+
+namespace DnsServerCore.Dns.ZoneManagers
+{
+ public class AuthZoneManager
+ {
+ #region variables
+
+ readonly DnsServer _dnsServer;
+ readonly string _serverDomain;
+
+ readonly ZoneTree _root = new ZoneTree();
+
+ #endregion
+
+ #region constructor
+
+ public AuthZoneManager(DnsServer dnsServer)
+ {
+ _dnsServer = dnsServer;
+ if (_dnsServer == null)
+ return; //allowed or blocked zone usage
+
+ _serverDomain = _dnsServer.ServerDomain;
+
+ LoadAllZoneFiles();
+ }
+
+ #endregion
+
+ #region private
+
+ private void UpdateServerDomain(string serverDomain)
+ {
+ //update authoritative zone SOA and NS records
+ List zones = ListZones();
+
+ foreach (AuthZoneInfo zone in zones)
+ {
+ if (zone.Type != AuthZoneType.Primary)
+ continue;
+
+ DnsResourceRecord record = zone.QueryRecords(DnsResourceRecordType.SOA)[0];
+ DnsSOARecord soa = record.RDATA as DnsSOARecord;
+
+ if (soa.MasterNameServer.Equals(_serverDomain, StringComparison.OrdinalIgnoreCase))
+ {
+ string responsiblePerson = soa.ResponsiblePerson;
+ if (responsiblePerson.EndsWith(_serverDomain))
+ responsiblePerson = responsiblePerson.Replace(_serverDomain, serverDomain);
+
+ SetRecords(record.Name, record.Type, record.TtlValue, new DnsResourceRecordData[] { new DnsSOARecord(serverDomain, responsiblePerson, soa.Serial, soa.Refresh, soa.Retry, soa.Expire, soa.Minimum) });
+
+ //update NS records
+ IReadOnlyList nsResourceRecords = zone.QueryRecords(DnsResourceRecordType.NS);
+
+ foreach (DnsResourceRecord nsResourceRecord in nsResourceRecords)
+ {
+ if ((nsResourceRecord.RDATA as DnsNSRecord).NSDomainName.Equals(_serverDomain, StringComparison.OrdinalIgnoreCase))
+ {
+ UpdateRecord(nsResourceRecord, new DnsResourceRecord(nsResourceRecord.Name, nsResourceRecord.Type, nsResourceRecord.Class, nsResourceRecord.TtlValue, new DnsNSRecord(serverDomain)) { Tag = nsResourceRecord.Tag });
+ break;
+ }
+ }
+
+ if (zone.Internal)
+ continue; //dont save internal zones to disk
+
+ try
+ {
+ SaveZoneFile(zone.Name);
+ }
+ catch (Exception ex)
+ {
+ LogManager log = _dnsServer.LogManager;
+ if (log != null)
+ log.Write(ex);
+ }
+ }
+ }
+ }
+
+ private AuthZone CreateEmptyZone(AuthZoneInfo zoneInfo)
+ {
+ AuthZone zone;
+
+ switch (zoneInfo.Type)
+ {
+ case AuthZoneType.Primary:
+ zone = new PrimaryZone(_dnsServer, zoneInfo);
+ break;
+
+ case AuthZoneType.Secondary:
+ zone = new SecondaryZone(_dnsServer, zoneInfo);
+ break;
+
+ case AuthZoneType.Stub:
+ zone = new StubZone(_dnsServer, zoneInfo);
+ break;
+
+ case AuthZoneType.Forwarder:
+ zone = new ForwarderZone(zoneInfo);
+ break;
+
+ default:
+ throw new InvalidDataException("DNS zone type not supported.");
+ }
+
+ if (_root.TryAdd(zone))
+ return zone;
+
+ throw new DnsServerException("Zone already exists: " + zoneInfo.Name);
+ }
+
+ private void LoadRecords(IReadOnlyList records)
+ {
+ Dictionary>> groupedByDomainRecords = DnsResourceRecord.GroupRecords(records);
+
+ foreach (KeyValuePair>> groupedByTypeRecords in groupedByDomainRecords)
+ {
+ AuthZone zone = GetOrAddZone(groupedByTypeRecords.Key);
+
+ foreach (KeyValuePair> groupedRecords in groupedByTypeRecords.Value)
+ zone.LoadRecords(groupedRecords.Key, groupedRecords.Value);
+
+ if (zone is SubDomainZone)
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+ }
+
+ private AuthZone GetAuthoritativeZone(string domain)
+ {
+ _ = _root.FindZone(domain, out _, out AuthZone authority, out _);
+ if (authority == null)
+ return null;
+
+ return authority;
+ }
+
+ private AuthZone GetOrAddZone(string domain)
+ {
+ return _root.GetOrAdd(domain, delegate (string key)
+ {
+ AuthZone authZone = GetAuthoritativeZone(domain);
+ if (authZone == null)
+ throw new DnsServerException("Zone not found.");
+
+ if (authZone is PrimaryZone)
+ return new PrimarySubDomainZone(authZone as PrimaryZone, domain);
+ else if (authZone is SecondaryZone)
+ return new SecondarySubDomainZone(domain);
+ else if (authZone is StubZone)
+ return new StubSubDomainZone(domain);
+ else if (authZone is ForwarderZone)
+ return new ForwarderSubDomainZone(domain);
+
+ throw new DnsServerException("Zone cannot have sub domains.");
+ });
+ }
+
+ private IReadOnlyList GetAdditionalRecords(IReadOnlyList nsRecords)
+ {
+ IReadOnlyList glueRecords = nsRecords.GetGlueRecords();
+ if (glueRecords.Count > 0)
+ return glueRecords;
+
+ List additionalRecords = new List();
+
+ foreach (DnsResourceRecord nsRecord in nsRecords)
+ {
+ if (nsRecord.Type != DnsResourceRecordType.NS)
+ continue;
+
+ AuthZone authZone = _root.FindZone((nsRecord.RDATA as DnsNSRecord).NSDomainName, out _, out _, out _);
+ if ((authZone != null) && !authZone.Disabled)
+ {
+ {
+ IReadOnlyList records = authZone.QueryRecords(DnsResourceRecordType.A);
+ if ((records.Count > 0) && (records[0].RDATA is DnsARecord))
+ additionalRecords.AddRange(records);
+ }
+
+ {
+ IReadOnlyList records = authZone.QueryRecords(DnsResourceRecordType.AAAA);
+ if ((records.Count > 0) && (records[0].RDATA is DnsAAAARecord))
+ additionalRecords.AddRange(records);
+ }
+ }
+ }
+
+ return additionalRecords;
+ }
+
+ private void LoadAllZoneFiles()
+ {
+ string zonesFolder = Path.Combine(_dnsServer.ConfigFolder, "zones");
+ if (!Directory.Exists(zonesFolder))
+ Directory.CreateDirectory(zonesFolder);
+
+ //move zone files to new folder
+ {
+ string[] oldZoneFiles = Directory.GetFiles(_dnsServer.ConfigFolder, "*.zone");
+
+ foreach (string oldZoneFile in oldZoneFiles)
+ File.Move(oldZoneFile, Path.Combine(zonesFolder, Path.GetFileName(oldZoneFile)));
+ }
+
+ //remove old internal zones
+ {
+ string[] oldZoneFiles = new string[] { "localhost.zone", "1.0.0.127.in-addr.arpa.zone", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.zone" };
+
+ foreach (string oldZoneFile in oldZoneFiles)
+ {
+ string filePath = Path.Combine(zonesFolder, oldZoneFile);
+
+ if (File.Exists(filePath))
+ {
+ try
+ {
+ File.Delete(filePath);
+ }
+ catch
+ { }
+ }
+ }
+ }
+
+ //load system zones
+ {
+ {
+ CreatePrimaryZone("localhost", _dnsServer.ServerDomain, true);
+ SetRecords("localhost", DnsResourceRecordType.A, 3600, new DnsResourceRecordData[] { new DnsARecord(IPAddress.Loopback) });
+ SetRecords("localhost", DnsResourceRecordType.AAAA, 3600, new DnsResourceRecordData[] { new DnsAAAARecord(IPAddress.IPv6Loopback) });
+ }
+
+ {
+ string prtDomain = "0.in-addr.arpa";
+
+ CreatePrimaryZone(prtDomain, _dnsServer.ServerDomain, true);
+ }
+
+ {
+ string prtDomain = "255.in-addr.arpa";
+
+ CreatePrimaryZone(prtDomain, _dnsServer.ServerDomain, true);
+ }
+
+ {
+ string prtDomain = "127.in-addr.arpa";
+
+ CreatePrimaryZone(prtDomain, _dnsServer.ServerDomain, true);
+ SetRecords("1.0.0.127.in-addr.arpa", DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
+ }
+
+ {
+ string prtDomain = new DnsQuestionRecord(IPAddress.IPv6Loopback, DnsClass.IN).Name;
+
+ CreatePrimaryZone(prtDomain, _dnsServer.ServerDomain, true);
+ SetRecords(prtDomain, DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
+ }
+ }
+
+ //load zone files
+ string[] zoneFiles = Directory.GetFiles(zonesFolder, "*.zone");
+
+ foreach (string zoneFile in zoneFiles)
+ {
+ try
+ {
+ using (FileStream fS = new FileStream(zoneFile, FileMode.Open, FileAccess.Read))
+ {
+ LoadZoneFrom(fS);
+ }
+
+ LogManager log = _dnsServer.LogManager;
+ if (log != null)
+ log.Write("DNS Server successfully loaded zone file: " + zoneFile);
+ }
+ catch (Exception ex)
+ {
+ LogManager log = _dnsServer.LogManager;
+ if (log != null)
+ log.Write("DNS Server failed to load zone file: " + zoneFile + "\r\n" + ex.ToString());
+ }
+ }
+ }
+
+ private DnsDatagram GetReferralResponse(DnsDatagram request, AuthZone delegationZone)
+ {
+ IReadOnlyList authority = delegationZone.QueryRecords(DnsResourceRecordType.NS);
+ IReadOnlyList additional = GetAdditionalRecords(authority);
+
+ return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, null, authority, additional);
+ }
+
+ private DnsDatagram GetForwarderResponse(DnsDatagram request, AuthZone forwarderZone)
+ {
+ IReadOnlyList authority = forwarderZone.QueryRecords(DnsResourceRecordType.FWD);
+
+ return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, null, authority);
+ }
+
+ #endregion
+
+ #region public
+
+ public AuthZoneInfo CreatePrimaryZone(string domain, string masterNameServer, bool @internal)
+ {
+ AuthZone authZone = new PrimaryZone(_dnsServer, domain, new DnsSOARecord(masterNameServer, "hostmaster." + masterNameServer, 1, 14400, 3600, 604800, 900), @internal);
+
+ if (_root.TryAdd(authZone))
+ return new AuthZoneInfo(authZone);
+
+ return null;
+ }
+
+ public AuthZoneInfo CreatePrimaryZone(string domain, DnsSOARecord soaRecord, DnsNSRecord ns, bool @internal)
+ {
+ AuthZone authZone = new PrimaryZone(_dnsServer, domain, soaRecord, ns, @internal);
+
+ if (_root.TryAdd(authZone))
+ return new AuthZoneInfo(authZone);
+
+ return null;
+ }
+
+ public AuthZoneInfo CreateSecondaryZone(string domain, string masterNameServer)
+ {
+ AuthZone authZone = new SecondaryZone(_dnsServer, domain, new DnsSOARecord(masterNameServer, "hostmaster." + masterNameServer, 1, 14400, 3600, 604800, 900));
+
+ if (_root.TryAdd(authZone))
+ {
+ (authZone as SecondaryZone).RefreshZone();
+ return new AuthZoneInfo(authZone);
+ }
+
+ return null;
+ }
+
+ public AuthZoneInfo CreateStubZone(string domain, string masterNameServer)
+ {
+ AuthZone authZone = new StubZone(_dnsServer, domain, new DnsSOARecord(masterNameServer, "hostmaster." + masterNameServer, 1, 14400, 3600, 604800, 900));
+
+ if (_root.TryAdd(authZone))
+ {
+ (authZone as StubZone).RefreshZone();
+ return new AuthZoneInfo(authZone);
+ }
+
+ return null;
+ }
+
+ public AuthZoneInfo CreateForwarderZone(string domain, DnsTransportProtocol forwarderProtocol, string forwarder)
+ {
+ AuthZone authZone = new ForwarderZone(domain);
+
+ DnsResourceRecord record = new DnsResourceRecord(domain, DnsResourceRecordType.FWD, DnsClass.IN, 0, new DnsForwarderRecord(forwarderProtocol, forwarder));
+ authZone.LoadRecords(DnsResourceRecordType.FWD, new DnsResourceRecord[] { record });
+
+ if (_root.TryAdd(authZone))
+ return new AuthZoneInfo(authZone);
+
+ return null;
+ }
+
+ public bool DeleteZone(string domain)
+ {
+ if (_root.TryRemove(domain, out AuthZone authZone))
+ {
+ authZone.Dispose();
+ return true;
+ }
+
+ return false;
+ }
+
+ public AuthZoneInfo GetZoneInfo(string domain)
+ {
+ _ = _root.FindZone(domain, out _, out AuthZone authority, out _);
+ if (authority == null)
+ return null;
+
+ return new AuthZoneInfo(authority);
+ }
+
+ public List ListAllRecords(string domain)
+ {
+ List records = new List();
+
+ foreach (AuthZone zone in _root.GetZoneWithSubDomainZones(domain))
+ records.AddRange(zone.ListAllRecords());
+
+ return records;
+ }
+
+ public IReadOnlyList QueryRecords(string domain, DnsResourceRecordType type)
+ {
+ if (_root.TryGet(domain, out AuthZone zone))
+ return zone.QueryRecords(type);
+
+ return Array.Empty();
+ }
+
+ public IReadOnlyList QueryZoneTransferRecords(string domain)
+ {
+ List axfrRecords = new List();
+
+ List zones = _root.GetZoneWithSubDomainZones(domain);
+
+ if ((zones.Count > 0) && (zones[0] is PrimaryZone) && !zones[0].Disabled)
+ {
+ //only primary zones support zone transfer
+ DnsResourceRecord soaRecord = zones[0].QueryRecords(DnsResourceRecordType.SOA)[0];
+
+ axfrRecords.Add(soaRecord);
+
+ foreach (Zone zone in zones)
+ {
+ foreach (DnsResourceRecord record in zone.ListAllRecords())
+ {
+ if (record.IsDisabled())
+ continue;
+
+ switch (record.Type)
+ {
+ case DnsResourceRecordType.SOA:
+ //skip
+ break;
+
+ case DnsResourceRecordType.NS:
+ axfrRecords.Add(record);
+ axfrRecords.AddRange(record.GetGlueRecords());
+ break;
+
+ default:
+ axfrRecords.Add(record);
+ break;
+ }
+ }
+ }
+
+ axfrRecords.Add(soaRecord);
+ }
+
+ return axfrRecords;
+ }
+
+ public void SyncRecords(string domain, IReadOnlyList syncRecords, IReadOnlyList additionalRecords = null, bool dontRemove = false)
+ {
+ List newRecords = new List(syncRecords.Count);
+ List glueRecords = new List();
+
+ if (additionalRecords != null)
+ glueRecords.AddRange(additionalRecords);
+
+ int i = 0;
+
+ if (syncRecords[0].Type == DnsResourceRecordType.SOA)
+ i = 1; //skip first SOA in AXFR
+
+ for (; i < syncRecords.Count; i++)
+ {
+ DnsResourceRecord record = syncRecords[i];
+
+ if (record.Name.EndsWith(domain, StringComparison.OrdinalIgnoreCase))
+ newRecords.Add(record);
+ else
+ glueRecords.Add(record);
+ }
+
+ if (glueRecords.Count > 0)
+ {
+ foreach (DnsResourceRecord record in newRecords)
+ {
+ if (record.Type == DnsResourceRecordType.NS)
+ record.SetGlueRecords(glueRecords);
+ }
+ }
+
+ List oldRecords = ListAllRecords(domain);
+
+ Dictionary>> newRecordsGroupedByDomain = DnsResourceRecord.GroupRecords(newRecords);
+ Dictionary>> oldRecordsGroupedByDomain = DnsResourceRecord.GroupRecords(oldRecords);
+
+ if (!dontRemove)
+ {
+ //remove domains that do not exists in new records
+ foreach (string oldDomain in oldRecordsGroupedByDomain.Keys)
+ {
+ if (!newRecordsGroupedByDomain.ContainsKey(oldDomain))
+ _root.TryRemove(oldDomain, out _);
+ }
+ }
+
+ //sync new records
+ foreach (KeyValuePair>> newEntries in newRecordsGroupedByDomain)
+ {
+ AuthZone zone = GetOrAddZone(newEntries.Key);
+ zone.SyncRecords(newEntries.Value, dontRemove);
+ }
+ }
+
+ public void SetRecords(string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData[] records)
+ {
+ DnsResourceRecord[] resourceRecords = new DnsResourceRecord[records.Length];
+
+ for (int i = 0; i < records.Length; i++)
+ resourceRecords[i] = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, records[i]);
+
+ AuthZone zone = GetOrAddZone(domain);
+
+ zone.SetRecords(type, resourceRecords);
+
+ if (zone is SubDomainZone)
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+
+ public void AddRecord(string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData record)
+ {
+ AuthZone zone = GetOrAddZone(domain);
+
+ zone.AddRecord(new DnsResourceRecord(zone.Name, type, DnsClass.IN, ttl, record));
+
+ if (zone is SubDomainZone)
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+
+ public void UpdateRecord(DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
+ {
+ if (oldRecord.Type != newRecord.Type)
+ throw new DnsServerException("Cannot update record: new record must be of same type.");
+
+ if (oldRecord.Type == DnsResourceRecordType.SOA)
+ throw new DnsServerException("Cannot update record: use SetRecords() for updating SOA record.");
+
+ if (!_root.TryGet(oldRecord.Name, out AuthZone zone))
+ throw new DnsServerException("Cannot update record: zone does not exists.");
+
+ switch (oldRecord.Type)
+ {
+ case DnsResourceRecordType.CNAME:
+ case DnsResourceRecordType.PTR:
+ if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
+ {
+ zone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
+
+ if (zone is SubDomainZone)
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+ else
+ {
+ zone.DeleteRecords(oldRecord.Type);
+
+ if (zone is SubDomainZone)
+ {
+ if (zone.IsEmpty)
+ _root.TryRemove(oldRecord.Name, out _); //remove empty sub zone
+ else
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+
+ AuthZone newZone = GetOrAddZone(newRecord.Name);
+
+ newZone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
+
+ if (newZone is SubDomainZone)
+ (newZone as SubDomainZone).AutoUpdateState();
+ }
+ break;
+
+ default:
+ if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
+ {
+ zone.DeleteRecord(oldRecord.Type, oldRecord.RDATA);
+ zone.AddRecord(newRecord);
+
+ if (zone is SubDomainZone)
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+ else
+ {
+ zone.DeleteRecord(oldRecord.Type, oldRecord.RDATA);
+
+ if (zone is SubDomainZone)
+ {
+ if (zone.IsEmpty)
+ _root.TryRemove(oldRecord.Name, out _); //remove empty sub zone
+ else
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+
+ AuthZone newZone = GetOrAddZone(newRecord.Name);
+
+ newZone.AddRecord(newRecord);
+
+ if (newZone is SubDomainZone)
+ (newZone as SubDomainZone).AutoUpdateState();
+ }
+ break;
+ }
+ }
+
+ public void DeleteRecord(string domain, DnsResourceRecordType type, DnsResourceRecordData record)
+ {
+ if (_root.TryGet(domain, out AuthZone zone))
+ {
+ zone.DeleteRecord(type, record);
+
+ if (zone is SubDomainZone)
+ {
+ if (zone.IsEmpty)
+ _root.TryRemove(domain, out _); //remove empty sub zone
+ else
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+ }
+ }
+
+ public void DeleteRecords(string domain, DnsResourceRecordType type)
+ {
+ if (_root.TryGet(domain, out AuthZone zone))
+ {
+ zone.DeleteRecords(type);
+
+ if (zone is SubDomainZone)
+ {
+ if (zone.IsEmpty)
+ _root.TryRemove(domain, out _); //remove empty sub zone
+ else
+ (zone as SubDomainZone).AutoUpdateState();
+ }
+ }
+ }
+
+ public List ListZones()
+ {
+ List zones = new List();
+
+ foreach (AuthZone zone in _root)
+ {
+ AuthZoneInfo zoneInfo = new AuthZoneInfo(zone);
+ switch (zoneInfo.Type)
+ {
+ case AuthZoneType.Primary:
+ case AuthZoneType.Secondary:
+ case AuthZoneType.Stub:
+ case AuthZoneType.Forwarder:
+ zones.Add(zoneInfo);
+ break;
+ }
+ }
+
+ return zones;
+ }
+
+ public List ListSubDomains(string domain)
+ {
+ return _root.ListSubDomains(domain);
+ }
+
+ public DnsDatagram Query(DnsDatagram request)
+ {
+ AuthZone zone = _root.FindZone(request.Question[0].Name, out AuthZone delegation, out AuthZone authZone, out bool hasSubDomains);
+
+ if ((authZone == null) || authZone.Disabled) //no authority for requested zone
+ return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.Refused, request.Question);
+
+ if ((delegation != null) && !delegation.Disabled)
+ return GetReferralResponse(request, delegation);
+
+ if ((zone == null) || zone.Disabled)
+ {
+ //zone not found
+ if (authZone is StubZone)
+ return GetReferralResponse(request, authZone);
+ else if (authZone is ForwarderZone)
+ return GetForwarderResponse(request, authZone);
+
+ DnsResponseCode rCode = hasSubDomains ? DnsResponseCode.NoError : DnsResponseCode.NameError;
+ IReadOnlyList authority = authZone.QueryRecords(DnsResourceRecordType.SOA);
+
+ return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, false, false, false, rCode, request.Question, null, authority);
+ }
+ else
+ {
+ //zone found
+ IReadOnlyList authority;
+ IReadOnlyList additional;
+
+ IReadOnlyList answers = zone.QueryRecords(request.Question[0].Type);
+ if (answers.Count == 0)
+ {
+ //record type not found
+ if (authZone is StubZone)
+ return GetReferralResponse(request, authZone);
+ else if (authZone is ForwarderZone)
+ return GetForwarderResponse(request, authZone);
+
+ authority = authZone.QueryRecords(DnsResourceRecordType.SOA);
+ additional = null;
+ }
+ else
+ {
+ //record type found
+ if (zone.Name.Contains("*"))
+ {
+ //wildcard zone; generate new answer records
+ DnsResourceRecord[] wildcardAnswers = new DnsResourceRecord[answers.Count];
+
+ for (int i = 0; i < answers.Count; i++)
+ wildcardAnswers[i] = new DnsResourceRecord(request.Question[0].Name, answers[i].Type, answers[i].Class, answers[i].TtlValue, answers[i].RDATA) { Tag = answers[i].Tag };
+
+ answers = wildcardAnswers;
+ }
+
+ switch (request.Question[0].Type)
+ {
+ case DnsResourceRecordType.NS:
+ authority = null;
+ additional = GetAdditionalRecords(answers);
+ break;
+
+ case DnsResourceRecordType.ANY:
+ authority = null;
+ additional = null;
+ break;
+
+ default:
+ authority = authZone.QueryRecords(DnsResourceRecordType.NS);
+ additional = GetAdditionalRecords(authority);
+ break;
+ }
+ }
+
+ return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, answers, authority, additional);
+ }
+ }
+
+ public void LoadZoneFrom(Stream s)
+ {
+ BinaryReader bR = new BinaryReader(s);
+
+ if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DZ")
+ throw new InvalidDataException("DnsServer zone file format is invalid.");
+
+ switch (bR.ReadByte())
+ {
+ case 2:
+ {
+ DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
+ if (records.Length > 0)
+ {
+ for (int i = 0; i < records.Length; i++)
+ records[i] = new DnsResourceRecord(s);
+
+ //make zone info
+ AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, AuthZoneType.Primary, false);
+
+ //create zone
+ Zone authZone = CreateEmptyZone(zoneInfo);
+
+ //load records
+ LoadRecords(records);
+
+ //init zone
+ switch (zoneInfo.Type)
+ {
+ case AuthZoneType.Primary:
+ (authZone as PrimaryZone).NotifyNameServers();
+ break;
+ }
+ }
+ }
+ break;
+
+ case 3:
+ {
+ bool zoneDisabled = bR.ReadBoolean();
+ DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
+ if (records.Length > 0)
+ {
+ for (int i = 0; i < records.Length; i++)
+ {
+ records[i] = new DnsResourceRecord(s);
+ records[i].Tag = new DnsResourceRecordInfo(bR);
+ }
+
+ //make zone info
+ AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, AuthZoneType.Primary, zoneDisabled);
+
+ //create zone
+ Zone authZone = CreateEmptyZone(zoneInfo);
+
+ //load records
+ LoadRecords(records);
+
+ //init zone
+ switch (zoneInfo.Type)
+ {
+ case AuthZoneType.Primary:
+ (authZone as PrimaryZone).NotifyNameServers();
+ break;
+ }
+ }
+ }
+ break;
+
+ case 4:
+ {
+ //read zone info
+ AuthZoneInfo zoneInfo = new AuthZoneInfo(bR);
+
+ //create zone
+ Zone authZone = CreateEmptyZone(zoneInfo);
+
+ //read all zone records
+ DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
+ if (records.Length > 0)
+ {
+ for (int i = 0; i < records.Length; i++)
+ {
+ records[i] = new DnsResourceRecord(s);
+ records[i].Tag = new DnsResourceRecordInfo(bR);
+ }
+
+ //load records
+ LoadRecords(records);
+
+ //init zone
+ switch (zoneInfo.Type)
+ {
+ case AuthZoneType.Primary:
+ (authZone as PrimaryZone).NotifyNameServers();
+ break;
+
+ case AuthZoneType.Secondary:
+ (authZone as SecondaryZone).RefreshZone();
+ break;
+
+ case AuthZoneType.Stub:
+ (authZone as StubZone).RefreshZone();
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+ throw new InvalidDataException("DNS Zone file version not supported.");
+ }
+ }
+
+ public void WriteZoneTo(string domain, Stream s)
+ {
+ List zones = _root.GetZoneWithSubDomainZones(domain);
+ if (zones.Count == 0)
+ throw new DnsServerException("Zone was not found: " + domain);
+
+ //serialize zone
+ BinaryWriter bW = new BinaryWriter(s);
+
+ bW.Write(Encoding.ASCII.GetBytes("DZ")); //format
+ bW.Write((byte)4); //version
+
+ //write zone info
+ AuthZoneInfo zoneInfo = new AuthZoneInfo(zones[0]);
+ zoneInfo.WriteTo(bW);
+
+ //write all zone records
+ List records = new List();
+
+ foreach (AuthZone zone in zones)
+ records.AddRange(zone.ListAllRecords());
+
+ bW.Write(records.Count);
+
+ foreach (DnsResourceRecord record in records)
+ {
+ record.WriteTo(s);
+
+ DnsResourceRecordInfo rrInfo = record.Tag as DnsResourceRecordInfo;
+ if (rrInfo == null)
+ rrInfo = new DnsResourceRecordInfo(); //default info
+
+ rrInfo.WriteTo(bW);
+ }
+ }
+
+ public void SaveZoneFile(string domain)
+ {
+ domain = domain.ToLower();
+
+ using (MemoryStream mS = new MemoryStream())
+ {
+ //serialize zone
+ WriteZoneTo(domain, mS);
+
+ //write to zone file
+ mS.Position = 0;
+
+ using (FileStream fS = new FileStream(Path.Combine(_dnsServer.ConfigFolder, "zones", domain + ".zone"), FileMode.Create, FileAccess.Write))
+ {
+ mS.CopyTo(fS);
+ }
+ }
+
+ LogManager log = _dnsServer.LogManager;
+ if (log != null)
+ log.Write("Saved zone file for domain: " + domain);
+ }
+
+ public void DeleteZoneFile(string domain)
+ {
+ domain = domain.ToLower();
+
+ File.Delete(Path.Combine(_dnsServer.ConfigFolder, "zones", domain + ".zone"));
+
+ LogManager log = _dnsServer.LogManager;
+ if (log != null)
+ log.Write("Deleted zone file for domain: " + domain);
+ }
+
+ #endregion
+
+ #region properties
+
+ public string ServerDomain
+ {
+ get { return _serverDomain; }
+ set { UpdateServerDomain(value); }
+ }
+
+ #endregion
+ }
+}
diff --git a/DnsServerCore/Dns/Zones/AuthZoneManager.cs b/DnsServerCore/Dns/Zones/AuthZoneManager.cs
deleted file mode 100644
index c51dded6..00000000
--- a/DnsServerCore/Dns/Zones/AuthZoneManager.cs
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
-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 .
-
-*/
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-using TechnitiumLibrary.Net.Dns;
-using TechnitiumLibrary.Net.Dns.ResourceRecords;
-
-namespace DnsServerCore.Dns.Zones
-{
- public class AuthZoneManager
- {
- #region variables
-
- readonly ZoneTree _root = new ZoneTree();
-
- #endregion
-
- #region private
-
- private void CreateZone(AuthZoneInfo zoneInfo)
- {
- //create zone
- switch (zoneInfo.Type)
- {
- case AuthZoneType.Primary:
- if (!_root.TryAdd(new PrimaryZone(zoneInfo.Name, zoneInfo.Disabled)))
- throw new DnsServerException("Zone already exists: " + zoneInfo.Name);
-
- break;
-
- case AuthZoneType.Secondary:
- if (!_root.TryAdd(new SecondaryZone(zoneInfo.Name, zoneInfo.Disabled)))
- throw new DnsServerException("Zone already exists: " + zoneInfo.Name);
-
- break;
-
- case AuthZoneType.Stub:
- if (!_root.TryAdd(new StubZone(zoneInfo.Name, zoneInfo.Disabled)))
- throw new DnsServerException("Zone already exists: " + zoneInfo.Name);
-
- break;
-
- default:
- throw new InvalidDataException("DNS Zone type not supported.");
- }
- }
-
- private IReadOnlyList GetAdditionalRecords(IReadOnlyList nsRecords)
- {
- IReadOnlyList glueRecords = nsRecords.GetGlueRecords();
- if (glueRecords.Count > 0)
- return glueRecords;
-
- List additionalRecords = new List();
-
- foreach (DnsResourceRecord nsRecord in nsRecords)
- {
- if (nsRecord.Type != DnsResourceRecordType.NS)
- continue;
-
- AuthZone authZone = _root.FindZone((nsRecord.RDATA as DnsNSRecord).NSDomainName, out _, out _, out _);
- if ((authZone != null) && !authZone.Disabled)
- {
- {
- IReadOnlyList records = authZone.QueryRecords(DnsResourceRecordType.A);
- if ((records.Count > 0) && (records[0].RDATA is DnsARecord))
- additionalRecords.AddRange(records);
- }
-
- {
- IReadOnlyList records = authZone.QueryRecords(DnsResourceRecordType.AAAA);
- if ((records.Count > 0) && (records[0].RDATA is DnsAAAARecord))
- additionalRecords.AddRange(records);
- }
- }
- }
-
- return additionalRecords;
- }
-
- #endregion
-
- #region public
-
- public bool CreatePrimaryZone(string domain, string masterNameServer, bool @internal)
- {
- return _root.TryAdd(new PrimaryZone(domain, new DnsSOARecord(masterNameServer, "hostmaster." + masterNameServer, 1, 14400, 3600, 604800, 900), @internal));
- }
-
- public bool CreatePrimaryZone(string domain, DnsSOARecord soaRecord, DnsNSRecord ns, bool @internal)
- {
- return _root.TryAdd(new PrimaryZone(domain, soaRecord, ns, @internal));
- }
-
- public bool CreateSecondaryZone(string domain, string masterNameServer)
- {
- return _root.TryAdd(new SecondaryZone(domain, new DnsSOARecord(masterNameServer, "hostmaster." + masterNameServer, 1, 14400, 3600, 604800, 900)));
- }
-
- public bool CreateStubZone(string domain, string masterNameServer)
- {
- return _root.TryAdd(new StubZone(domain, new DnsSOARecord(masterNameServer, "hostmaster." + masterNameServer, 1, 14400, 3600, 604800, 900)));
- }
-
- public bool DeleteZone(string domain)
- {
- return _root.TryRemove(domain, out _);
- }
-
- public AuthZoneInfo GetZoneInfo(string domain)
- {
- _ = _root.FindZone(domain, out _, out AuthZone authority, out _);
- if (authority == null)
- return null;
-
- return new AuthZoneInfo(authority);
- }
-
- public bool ZoneExistsAndEnabled(string domain)
- {
- if (_root.TryGet(domain, out AuthZone zone))
- return !zone.Disabled;
-
- return false;
- }
-
- public void DisableZone(string domain)
- {
- if (_root.TryGet(domain, out AuthZone zone))
- zone.Disabled = true;
- }
-
- public List ListAllRecords(string domain)
- {
- List records = new List();
-
- foreach (AuthZone zone in _root.GetZoneWithSubDomainZones(domain))
- records.AddRange(zone.ListAllRecords());
-
- return records;
- }
-
- public IReadOnlyList QueryRecords(string domain, DnsResourceRecordType type)
- {
- if (_root.TryGet(domain, out AuthZone zone))
- return zone.QueryRecords(type);
-
- return Array.Empty();
- }
-
- public List GetZoneTransferRecords(string domain)
- {
- List axfrRecords = new List();
-
- List zones = _root.GetZoneWithSubDomainZones(domain);
-
- if ((zones.Count > 0) && (zones[0] is PrimaryZone) && !zones[0].Disabled)
- {
- //only primary zones support zone transfer
- DnsResourceRecord soaRecord = zones[0].QueryRecords(DnsResourceRecordType.SOA)[0];
-
- axfrRecords.Add(soaRecord);
-
- foreach (Zone zone in zones)
- {
- foreach (DnsResourceRecord record in zone.ListAllRecords())
- {
- if (record.Type != DnsResourceRecordType.SOA)
- axfrRecords.Add(record);
- }
- }
-
- axfrRecords.Add(soaRecord);
- }
-
- return axfrRecords;
- }
-
- public void SetRecords(string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData[] records)
- {
- AuthZone zone = _root.GetOrAdd(domain, delegate (string key)
- {
- return new SubDomainZone(domain);
- });
-
- DnsResourceRecord[] resourceRecords = new DnsResourceRecord[records.Length];
-
- for (int i = 0; i < records.Length; i++)
- resourceRecords[i] = new DnsResourceRecord(zone.Name, type, DnsClass.IN, ttl, records[i]);
-
- zone.SetRecords(type, resourceRecords);
-
- if (zone is SubDomainZone)
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
-
- public void SetRecords(IReadOnlyList resourceRecords)
- {
- if (resourceRecords.Count == 1)
- {
- AuthZone zone = _root.GetOrAdd(resourceRecords[0].Name, delegate (string key)
- {
- return new SubDomainZone(resourceRecords[0].Name);
- });
-
- zone.SetRecords(resourceRecords[0].Type, resourceRecords);
-
- if (zone is SubDomainZone)
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
- else
- {
- Dictionary>> groupedByDomainRecords = DnsResourceRecord.GroupRecords(resourceRecords);
-
- //add grouped records
- foreach (KeyValuePair>> groupedByTypeRecords in groupedByDomainRecords)
- {
- AuthZone zone = _root.GetOrAdd(groupedByTypeRecords.Key, delegate (string key)
- {
- return new SubDomainZone(groupedByTypeRecords.Key);
- });
-
- foreach (KeyValuePair> groupedRecords in groupedByTypeRecords.Value)
- zone.SetRecords(groupedRecords.Key, groupedRecords.Value);
-
- if (zone is SubDomainZone)
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
- }
- }
-
- public void AddRecord(string domain, DnsResourceRecordType type, uint ttl, DnsResourceRecordData record)
- {
- AuthZone zone = _root.GetOrAdd(domain, delegate (string key)
- {
- return new SubDomainZone(domain);
- });
-
- zone.AddRecord(new DnsResourceRecord(zone.Name, type, DnsClass.IN, ttl, record));
-
- if (zone is SubDomainZone)
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
-
- public void UpdateRecord(DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
- {
- if (oldRecord.Type != newRecord.Type)
- throw new DnsServerException("Cannot update record: new record must be of same type.");
-
- if (oldRecord.Type == DnsResourceRecordType.SOA)
- throw new DnsServerException("Cannot update record: use SetRecords() for updating SOA record.");
-
- if (!_root.TryGet(oldRecord.Name, out AuthZone zone))
- throw new DnsServerException("Cannot update record: zone does not exists.");
-
- switch (oldRecord.Type)
- {
- case DnsResourceRecordType.CNAME:
- case DnsResourceRecordType.PTR:
- if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
- {
- zone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
-
- if (zone is SubDomainZone)
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
- else
- {
- zone.DeleteRecords(oldRecord.Type);
-
- if (zone is SubDomainZone)
- {
- if (zone.IsEmpty)
- _root.TryRemove(oldRecord.Name, out _); //remove empty sub zone
- else
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
-
- AuthZone newZone = _root.GetOrAdd(newRecord.Name, delegate (string key)
- {
- return new SubDomainZone(newRecord.Name);
- });
-
- newZone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
-
- if (newZone is SubDomainZone)
- newZone.Disabled = zone.AreAllRecordsDisabled();
- }
- break;
-
- default:
- if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
- {
- zone.DeleteRecord(oldRecord.Type, oldRecord.RDATA);
- zone.AddRecord(newRecord);
-
- if (zone is SubDomainZone)
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
- else
- {
- zone.DeleteRecord(oldRecord.Type, oldRecord.RDATA);
-
- if (zone is SubDomainZone)
- {
- if (zone.IsEmpty)
- _root.TryRemove(oldRecord.Name, out _); //remove empty sub zone
- else
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
-
- AuthZone newZone = _root.GetOrAdd(newRecord.Name, delegate (string key)
- {
- return new SubDomainZone(newRecord.Name);
- });
-
- newZone.AddRecord(newRecord);
-
- if (newZone is SubDomainZone)
- newZone.Disabled = zone.AreAllRecordsDisabled();
- }
- break;
- }
- }
-
- public void DeleteRecord(string domain, DnsResourceRecordType type, DnsResourceRecordData record)
- {
- if (_root.TryGet(domain, out AuthZone zone))
- {
- zone.DeleteRecord(type, record);
-
- if (zone is SubDomainZone)
- {
- if (zone.IsEmpty)
- _root.TryRemove(domain, out _); //remove empty sub zone
- else
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
- }
- }
-
- public void DeleteRecords(string domain, DnsResourceRecordType type)
- {
- if (_root.TryGet(domain, out AuthZone zone))
- {
- zone.DeleteRecords(type);
-
- if (zone is SubDomainZone)
- {
- if (zone.IsEmpty)
- _root.TryRemove(domain, out _); //remove empty sub zone
- else
- zone.Disabled = zone.AreAllRecordsDisabled();
- }
- }
- }
-
- public List ListZones()
- {
- List zones = new List();
-
- foreach (AuthZone zone in _root)
- {
- AuthZoneInfo zoneInfo = new AuthZoneInfo(zone);
- switch (zoneInfo.Type)
- {
- case AuthZoneType.Primary:
- case AuthZoneType.Secondary:
- case AuthZoneType.Stub:
- zones.Add(zoneInfo);
- break;
- }
- }
-
- return zones;
- }
-
- public List ListSubDomains(string domain)
- {
- return _root.ListSubDomains(domain);
- }
-
- public DnsDatagram Query(DnsDatagram request)
- {
- AuthZone zone = _root.FindZone(request.Question[0].Name, out AuthZone delegation, out AuthZone authZone, out bool hasSubDomains);
-
- if ((authZone == null) || authZone.Disabled) //no authority for requested zone
- return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.Refused, request.Question);
-
- if ((authZone is StubZone) || ((delegation != null) && !delegation.Disabled))
- {
- //zone is delegation
- IReadOnlyList authority = delegation.QueryRecords(DnsResourceRecordType.NS);
- IReadOnlyList additional = GetAdditionalRecords(authority);
-
- return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, null, authority, additional);
- }
-
- if ((zone == null) || zone.Disabled)
- {
- //zone not found
- DnsResponseCode rCode = hasSubDomains ? DnsResponseCode.NoError : DnsResponseCode.NameError;
- IReadOnlyList authority = authZone.QueryRecords(DnsResourceRecordType.SOA);
-
- return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, false, false, false, rCode, request.Question, null, authority);
- }
-
- //zone found
- if ((authZone is PrimaryZone) || (authZone is SecondaryZone))
- {
- IReadOnlyList authority;
- IReadOnlyList additional;
-
- IReadOnlyList answers = zone.QueryRecords(request.Question[0].Type);
- if (answers.Count == 0)
- {
- //record type not found
- authority = authZone.QueryRecords(DnsResourceRecordType.SOA);
- additional = null;
- }
- else
- {
- //record type found
- if (zone.Name.Contains("*"))
- {
- //wildcard zone; generate new answer records
- DnsResourceRecord[] wildcardAnswers = new DnsResourceRecord[answers.Count];
-
- for (int i = 0; i < answers.Count; i++)
- wildcardAnswers[i] = new DnsResourceRecord(request.Question[0].Name, answers[i].Type, answers[i].Class, answers[i].TtlValue, answers[i].RDATA) { Tag = answers[i].Tag };
-
- answers = wildcardAnswers;
- }
-
- switch (request.Question[0].Type)
- {
- case DnsResourceRecordType.NS:
- authority = null;
- additional = GetAdditionalRecords(answers);
- break;
-
- case DnsResourceRecordType.ANY:
- authority = null;
- additional = null;
- break;
-
- default:
- authority = authZone.QueryRecords(DnsResourceRecordType.NS);
- additional = GetAdditionalRecords(authority);
- break;
- }
- }
-
- return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, answers, authority, additional);
- }
-
- //unknown zone type encountered
- return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NotImplemented, request.Question);
- }
-
- public void LoadZoneFrom(Stream s)
- {
- BinaryReader bR = new BinaryReader(s);
-
- if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DZ")
- throw new InvalidDataException("DnsServer zone file format is invalid.");
-
- switch (bR.ReadByte())
- {
- case 2:
- {
- DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
- if (records.Length > 0)
- {
- for (int i = 0; i < records.Length; i++)
- records[i] = new DnsResourceRecord(s);
-
- //make zone info
- AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, AuthZoneType.Primary, false);
-
- //create zone
- CreateZone(zoneInfo);
-
- //set records
- SetRecords(records);
- }
- }
- break;
-
- case 3:
- {
- bool zoneDisabled = bR.ReadBoolean();
- DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
- if (records.Length > 0)
- {
- for (int i = 0; i < records.Length; i++)
- {
- records[i] = new DnsResourceRecord(s);
- records[i].Tag = new DnsResourceRecordInfo(bR);
- }
-
- //make zone info
- AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, AuthZoneType.Primary, zoneDisabled);
-
- //create zone
- CreateZone(zoneInfo);
-
- //set records
- SetRecords(records);
- }
- }
- break;
-
- case 4:
- {
- //read zone info
- AuthZoneInfo zoneInfo = new AuthZoneInfo(bR);
-
- //create zone
- CreateZone(zoneInfo);
-
- //read all zone records
- DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
- if (records.Length > 0)
- {
- for (int i = 0; i < records.Length; i++)
- {
- records[i] = new DnsResourceRecord(s);
- records[i].Tag = new DnsResourceRecordInfo(bR);
- }
-
- //set records
- SetRecords(records);
- }
- }
- break;
-
- default:
- throw new InvalidDataException("DNS Zone file version not supported.");
- }
- }
-
- public void WriteZoneTo(string domain, Stream s)
- {
- List zones = _root.GetZoneWithSubDomainZones(domain);
- if (zones.Count == 0)
- throw new DnsServerException("Zone was not found: " + domain);
-
- //serialize zone
- BinaryWriter bW = new BinaryWriter(s);
-
- bW.Write(Encoding.ASCII.GetBytes("DZ")); //format
- bW.Write((byte)4); //version
-
- //write zone info
- AuthZoneInfo zoneInfo = new AuthZoneInfo(zones[0]);
- zoneInfo.WriteTo(bW);
-
- //write all zone records
- List records = new List();
-
- foreach (AuthZone zone in zones)
- records.AddRange(zone.ListAllRecords());
-
- bW.Write(records.Count);
-
- foreach (DnsResourceRecord record in records)
- {
- record.WriteTo(s);
-
- DnsResourceRecordInfo rrInfo = record.Tag as DnsResourceRecordInfo;
- if (rrInfo == null)
- rrInfo = new DnsResourceRecordInfo(); //default info
-
- rrInfo.WriteTo(bW);
- }
- }
-
- #endregion
- }
-}