diff --git a/DnsServerCore/Dns/DnsServer.cs b/DnsServerCore/Dns/DnsServer.cs index c53adb1c..336ed41d 100644 --- a/DnsServerCore/Dns/DnsServer.cs +++ b/DnsServerCore/Dns/DnsServer.cs @@ -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 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); diff --git a/DnsServerCore/Dns/Zone.cs b/DnsServerCore/Dns/Zone.cs index 52770fb2..762dfd7d 100644 --- a/DnsServerCore/Dns/Zone.cs +++ b/DnsServerCore/Dns/Zone.cs @@ -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 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 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 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(); - 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; } diff --git a/DnsServerCore/WebService.cs b/DnsServerCore/WebService.cs index 27ada0e9..dc49c9bc 100644 --- a/DnsServerCore/WebService.cs +++ b/DnsServerCore/WebService.cs @@ -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 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 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 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 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 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 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 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 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 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 records, JsonTextWriter jsonWriter, bool authoritativeZoneRecords) { if (records == null) { @@ -2453,7 +2456,7 @@ namespace DnsServerCore return; } - Array.Sort(records); + records.Sort(); Dictionary>> 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 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()