Merge branch 'secondary-zones' into develop

This commit is contained in:
Shreyas Zare
2020-01-04 14:12:09 +05:30
3 changed files with 186 additions and 65 deletions

View File

@@ -1,6 +1,6 @@
/*
Technitium DNS Server
Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com)
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
@@ -867,17 +867,22 @@ namespace DnsServerCore.Dns
if ((request.Question.Length != 1) || (request.Question[0].Class != DnsClass.IN))
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.Refused, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null);
switch (request.Question[0].Type)
{
case DnsResourceRecordType.IXFR:
case DnsResourceRecordType.AXFR:
case DnsResourceRecordType.MAILB:
case DnsResourceRecordType.MAILA:
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NotImplemented, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null);
}
try
{
switch (request.Question[0].Type)
{
case DnsResourceRecordType.IXFR:
case DnsResourceRecordType.AXFR:
if (protocol == DnsTransportProtocol.Udp)
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.FormatError, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null);
return ProcessZoneTransferQuery(request, remoteEP, isRecursionAllowed);
case DnsResourceRecordType.MAILB:
case DnsResourceRecordType.MAILA:
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NotImplemented, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null);
}
//query authoritative zone
DnsDatagram authoritativeResponse = ProcessAuthoritativeQuery(request, isRecursionAllowed);
@@ -945,6 +950,87 @@ namespace DnsServerCore.Dns
}
}
private DnsDatagram ProcessZoneTransferQuery(DnsDatagram request, EndPoint remoteEP, bool isRecursionAllowed)
{
string zoneName = request.Question[0].Name;
if (!_authoritativeZoneRoot.ZoneExistsAndEnabled(zoneName))
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NameError, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null) { Tag = "authHit" };
List<DnsResourceRecord> axfrRecords = _authoritativeZoneRoot.GetAllRecords(zoneName, DnsResourceRecordType.AXFR, true, true);
if (axfrRecords.Count == 0)
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NameError, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null) { Tag = "authHit" };
bool isAxfrAllowed = false;
DnsResourceRecordType type;
switch (remoteEP.AddressFamily)
{
case AddressFamily.InterNetwork:
type = DnsResourceRecordType.A;
break;
case AddressFamily.InterNetworkV6:
type = DnsResourceRecordType.AAAA;
break;
default:
throw new NotSupportedException("AddressFamily not supported.");
}
IPAddress remoteAddress = (remoteEP as IPEndPoint).Address;
if (IPAddress.IsLoopback(remoteAddress))
{
isAxfrAllowed = true;
}
else
{
foreach (DnsResourceRecord rr in axfrRecords)
{
if (rr.Type == DnsResourceRecordType.NS)
{
string nameServer = (rr.RDATA as DnsNSRecord).NSDomainName;
try
{
DnsDatagram response = DirectQuery(new DnsQuestionRecord(nameServer, type, DnsClass.IN));
if (response == null)
continue;
IPAddress[] addresses;
if (type == DnsResourceRecordType.A)
addresses = DnsClient.ParseResponseA(response);
else
addresses = DnsClient.ParseResponseAAAA(response);
foreach (IPAddress address in addresses)
{
if (remoteAddress.Equals(address))
{
isAxfrAllowed = true;
break;
}
}
}
catch
{
//ignore error
}
}
if (isAxfrAllowed)
break;
}
}
if (!isAxfrAllowed)
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.Refused, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null) { Tag = "authHit" };
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, true, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NoError, request.Header.QDCOUNT, Convert.ToUInt16(axfrRecords.Count), 0, 0), request.Question, axfrRecords.ToArray(), null, null) { Tag = "authHit" };
}
private DnsDatagram ProcessAuthoritativeQuery(DnsDatagram request, bool isRecursionAllowed)
{
DnsDatagram response = _authoritativeZoneRoot.Query(request);

View File

@@ -1,6 +1,6 @@
/*
Technitium DNS Server
Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com)
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
@@ -25,10 +25,19 @@ using TechnitiumLibrary.Net.Dns.ResourceRecords;
namespace DnsServerCore.Dns
{
public enum ZoneType
{
Cache = 0,
Primary = 1,
Secondary = 2,
Stub = 3
}
public class Zone
{
#region variables
readonly ZoneType zoneType;
readonly bool _authoritativeZone;
readonly Zone _parentZone;
@@ -345,6 +354,21 @@ namespace DnsServerCore.Dns
allRecords.AddRange(entry.Value);
}
}
else if (type == DnsResourceRecordType.AXFR)
{
includeSubDomains = true;
if (!_entries.TryGetValue(DnsResourceRecordType.SOA, out DnsResourceRecord[] soaRecord))
throw new DnsServerException("No SOA record found for AXFR in current zone.");
allRecords.Add(soaRecord[0]);
foreach (KeyValuePair<DnsResourceRecordType, DnsResourceRecord[]> entry in _entries)
{
if (entry.Key != DnsResourceRecordType.SOA)
allRecords.AddRange(entry.Value);
}
}
else if (_entries.TryGetValue(type, out DnsResourceRecord[] existingRecords))
{
allRecords.AddRange(existingRecords);
@@ -352,13 +376,23 @@ namespace DnsServerCore.Dns
if (includeSubDomains)
{
DnsResourceRecordType subType;
if (type == DnsResourceRecordType.AXFR)
subType = DnsResourceRecordType.ANY;
else
subType = type;
foreach (KeyValuePair<string, Zone> zone in _zones)
{
if (!zone.Value._entries.ContainsKey(DnsResourceRecordType.SOA))
allRecords.AddRange(zone.Value.GetAllRecords(type, true));
allRecords.AddRange(zone.Value.GetAllRecords(subType, true));
}
}
if (type == DnsResourceRecordType.AXFR)
allRecords.Add(allRecords[0]);
return allRecords;
}
@@ -1009,13 +1043,13 @@ namespace DnsServerCore.Dns
currentZone.DeleteRecords(type);
}
public DnsResourceRecord[] GetAllRecords(string domain = "", DnsResourceRecordType type = DnsResourceRecordType.ANY, bool includeSubDomains = true, bool authoritative = false)
public List<DnsResourceRecord> GetAllRecords(string domain = "", DnsResourceRecordType type = DnsResourceRecordType.ANY, bool includeSubDomains = true, bool authoritative = false)
{
Zone currentZone = GetZone(this, domain, authoritative);
if (currentZone == null)
return new DnsResourceRecord[] { };
return new List<DnsResourceRecord>();
return currentZone.GetAllRecords(type, includeSubDomains).ToArray();
return currentZone.GetAllRecords(type, includeSubDomains);
}
public string[] ListSubZones(string domain = "")
@@ -1070,21 +1104,21 @@ namespace DnsServerCore.Dns
public void DisableZone(string domain)
{
Zone currentZone = GetZone(this, domain, false);
Zone currentZone = GetZone(this, domain, true);
if (currentZone != null)
currentZone._disabled = true;
}
public void EnableZone(string domain)
{
Zone currentZone = GetZone(this, domain, false);
Zone currentZone = GetZone(this, domain, true);
if (currentZone != null)
currentZone._disabled = false;
}
public bool IsZoneDisabled(string domain)
{
Zone currentZone = GetZone(this, domain, false);
Zone currentZone = GetZone(this, domain, true);
if (currentZone != null)
return currentZone._disabled;
@@ -1093,13 +1127,13 @@ namespace DnsServerCore.Dns
public bool ZoneExists(string domain)
{
Zone currentZone = GetZone(this, domain, false);
Zone currentZone = GetZone(this, domain, true);
return (currentZone != null);
}
public bool ZoneExistsAndEnabled(string domain)
{
Zone currentZone = GetZone(this, domain, false);
Zone currentZone = GetZone(this, domain, true);
return (currentZone != null) && !currentZone._disabled;
}

View File

@@ -1117,8 +1117,8 @@ namespace DnsServerCore
foreach (ZoneInfo zone in zones)
{
DnsResourceRecord[] soaResourceRecords = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Length > 0)
List<DnsResourceRecord> soaResourceRecords = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Count > 0)
{
DnsResourceRecord soaRecord = soaResourceRecords[0];
DnsSOARecord soaRecordData = soaRecord.RDATA as DnsSOARecord;
@@ -1132,7 +1132,7 @@ namespace DnsServerCore
_dnsServer.AuthoritativeZoneRoot.SetRecords(soaRecord.Name, soaRecord.Type, soaRecord.TtlValue, new DnsResourceRecordData[] { new DnsSOARecord(strServerDomain, responsiblePerson, soaRecordData.Serial, soaRecordData.Refresh, soaRecordData.Retry, soaRecordData.Expire, soaRecordData.Minimum) });
//update NS records
DnsResourceRecord[] nsResourceRecords = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.NS, false, true);
List<DnsResourceRecord> nsResourceRecords = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.NS, false, true);
foreach (DnsResourceRecord nsResourceRecord in nsResourceRecords)
{
@@ -1159,8 +1159,8 @@ namespace DnsServerCore
foreach (ZoneInfo zone in zones)
{
DnsResourceRecord[] soaResourceRecords = _dnsServer.AllowedZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Length > 0)
List<DnsResourceRecord> soaResourceRecords = _dnsServer.AllowedZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Count > 0)
{
DnsResourceRecord soaRecord = soaResourceRecords[0];
DnsSOARecord soaRecordData = soaRecord.RDATA as DnsSOARecord;
@@ -1176,8 +1176,8 @@ namespace DnsServerCore
foreach (ZoneInfo zone in zones)
{
DnsResourceRecord[] soaResourceRecords = _customBlockedZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Length > 0)
List<DnsResourceRecord> soaResourceRecords = _customBlockedZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Count > 0)
{
DnsResourceRecord soaRecord = soaResourceRecords[0];
DnsSOARecord soaRecordData = soaRecord.RDATA as DnsSOARecord;
@@ -1193,8 +1193,8 @@ namespace DnsServerCore
foreach (ZoneInfo zone in zones)
{
DnsResourceRecord[] soaResourceRecords = _dnsServer.BlockedZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Length > 0)
List<DnsResourceRecord> soaResourceRecords = _dnsServer.BlockedZoneRoot.GetAllRecords(zone.ZoneName, DnsResourceRecordType.SOA, false, true);
if (soaResourceRecords.Count > 0)
{
DnsResourceRecord soaRecord = soaResourceRecords[0];
DnsSOARecord soaRecordData = soaRecord.RDATA as DnsSOARecord;
@@ -1804,14 +1804,14 @@ namespace DnsServerCore
string direction = request.QueryString["direction"];
string[] subZones;
DnsResourceRecord[] records;
List<DnsResourceRecord> records;
while (true)
{
subZones = _dnsServer.CacheZoneRoot.ListSubZones(domain);
records = _dnsServer.CacheZoneRoot.GetAllRecords(domain, DnsResourceRecordType.ANY, false);
if (records.Length > 0)
if (records.Count > 0)
break;
if (subZones.Length != 1)
@@ -1877,14 +1877,14 @@ namespace DnsServerCore
string direction = request.QueryString["direction"];
string[] subZones;
DnsResourceRecord[] records;
List<DnsResourceRecord> records;
while (true)
{
subZones = _dnsServer.AllowedZoneRoot.ListSubZones(domain);
records = _dnsServer.AllowedZoneRoot.GetAllRecords(domain, DnsResourceRecordType.ANY, false);
if (records.Length > 0)
if (records.Count > 0)
break;
if (subZones.Length != 1)
@@ -2026,14 +2026,14 @@ namespace DnsServerCore
string direction = request.QueryString["direction"];
string[] subZones;
DnsResourceRecord[] records;
List<DnsResourceRecord> records;
while (true)
{
subZones = _dnsServer.BlockedZoneRoot.ListSubZones(domain);
records = _dnsServer.BlockedZoneRoot.GetAllRecords(domain, DnsResourceRecordType.ANY, false);
if (records.Length > 0)
if (records.Count > 0)
break;
if (subZones.Length != 1)
@@ -2252,10 +2252,13 @@ namespace DnsServerCore
jsonWriter.WriteValue(domain);
}
private void CreateZone(string domain)
private void CreateZone(string domain, bool internalZone = false)
{
_dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, 1, 14400, 3600, 604800, 900) });
_dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(_dnsServer.ServerDomain) });
if (internalZone)
_dnsServer.AuthoritativeZoneRoot.MakeZoneInternal(domain);
}
private void DeleteZone(HttpListenerRequest request)
@@ -2435,14 +2438,14 @@ namespace DnsServerCore
if (domain.EndsWith("."))
domain = domain.Substring(0, domain.Length - 1);
DnsResourceRecord[] records = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(domain);
if (records.Length == 0)
List<DnsResourceRecord> records = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(domain);
if (records.Count == 0)
throw new WebServiceException("Zone '" + domain + "' was not found.");
WriteRecordsAsJson(records, jsonWriter, true);
}
private void WriteRecordsAsJson(DnsResourceRecord[] records, JsonTextWriter jsonWriter, bool authoritativeZoneRecords)
private void WriteRecordsAsJson(List<DnsResourceRecord> records, JsonTextWriter jsonWriter, bool authoritativeZoneRecords)
{
if (records == null)
{
@@ -2453,7 +2456,7 @@ namespace DnsServerCore
return;
}
Array.Sort(records);
records.Sort();
Dictionary<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> groupedByDomainRecords = DnsResourceRecord.GroupRecords(records);
@@ -3689,7 +3692,7 @@ namespace DnsServerCore
if (zoneFiles.Length == 0)
{
{
CreateZone("localhost");
CreateZone("localhost", true);
_dnsServer.AuthoritativeZoneRoot.SetRecords("localhost", DnsResourceRecordType.A, 3600, new DnsResourceRecordData[] { new DnsARecord(IPAddress.Loopback) });
_dnsServer.AuthoritativeZoneRoot.SetRecords("localhost", DnsResourceRecordType.AAAA, 3600, new DnsResourceRecordData[] { new DnsAAAARecord(IPAddress.IPv6Loopback) });
@@ -3697,10 +3700,24 @@ namespace DnsServerCore
}
{
string prtDomain = new DnsQuestionRecord(IPAddress.Loopback, DnsClass.IN).Name;
string prtDomain = "0.in-addr.arpa";
CreateZone(prtDomain);
_dnsServer.AuthoritativeZoneRoot.SetRecords(prtDomain, DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
CreateZone(prtDomain, true);
SaveZoneFile(prtDomain);
}
{
string prtDomain = "255.in-addr.arpa";
CreateZone(prtDomain, true);
SaveZoneFile(prtDomain);
}
{
string prtDomain = "127.in-addr.arpa";
CreateZone(prtDomain, true);
_dnsServer.AuthoritativeZoneRoot.SetRecords("1.0.0.127.in-addr.arpa", DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
SaveZoneFile(prtDomain);
}
@@ -3708,7 +3725,7 @@ namespace DnsServerCore
{
string prtDomain = new DnsQuestionRecord(IPAddress.IPv6Loopback, DnsClass.IN).Name;
CreateZone(prtDomain);
CreateZone(prtDomain, true);
_dnsServer.AuthoritativeZoneRoot.SetRecords(prtDomain, DnsResourceRecordType.PTR, 3600, new DnsResourceRecordData[] { new DnsPTRRecord("localhost") });
SaveZoneFile(prtDomain);
@@ -3814,15 +3831,13 @@ namespace DnsServerCore
private void SaveZoneFile(string domain)
{
domain = domain.ToLower();
DnsResourceRecord[] records = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(domain, DnsResourceRecordType.ANY, true, true);
if (records.Length == 0)
List<DnsResourceRecord> records = _dnsServer.AuthoritativeZoneRoot.GetAllRecords(domain, DnsResourceRecordType.ANY, true, true);
if (records.Count == 0)
throw new WebServiceException("Zone '" + domain + "' was not found.");
string authZone = records[0].Name.ToLower();
ZoneInfo zoneInfo = _dnsServer.AuthoritativeZoneRoot.GetZoneInfo(domain);
if (zoneInfo.Internal)
return;
using (MemoryStream mS = new MemoryStream())
{
@@ -3833,7 +3848,8 @@ namespace DnsServerCore
bW.Write((byte)3); //version
bW.Write(_dnsServer.AuthoritativeZoneRoot.IsZoneDisabled(domain));
bW.Write(records.Length);
//store internal zone boolean
bW.Write(records.Count);
foreach (DnsResourceRecord record in records)
{
@@ -4857,21 +4873,6 @@ namespace DnsServerCore
_log.Write("Failed to start Web Service (v" + _currentVersion + ")\r\n" + ex.ToString());
throw;
}
//Scope scope = new Scope("test", IPAddress.Parse("192.168.120.1"), IPAddress.Parse("192.168.120.100"), IPAddress.Parse("255.255.255.0"), true);
//scope.RouterAddress = IPAddress.Parse("192.168.120.1");
//scope.DnsServers = new IPAddress[] { IPAddress.Parse("192.168.10.4") };
//scope.WinsServers = new IPAddress[] { IPAddress.Parse("192.168.10.4") };
//scope.NtpServers = new IPAddress[] { IPAddress.Parse("192.168.10.4") };
//scope.StaticRoutes = new Dhcp.Options.ClasslessStaticRouteOption.Route[] { new Dhcp.Options.ClasslessStaticRouteOption.Route(IPAddress.Parse("192.168.10.0"), IPAddress.Parse("255.255.255.0"), IPAddress.Parse("192.168.10.4")) };
//scope.OfferDelayTime = 2;
//scope.DomainName = "local";
//scope.Enabled = true;
//scope.AddExclusion(IPAddress.Parse("192.168.120.1"), IPAddress.Parse("192.168.120.10"));
//scope.AddReservedLease(new byte[] { 0x00, 0x0C, 0x29, 0x36, 0xC9, 0x84 }, IPAddress.Parse("192.168.120.50"));
//_dhcpServer.AddScope(scope);
}
public void Stop()