public void WriteZoneTo(string domain, Stream s) { List <AuthZone> 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]); if (zoneInfo.Internal) { throw new InvalidOperationException("Cannot save zones marked as internal."); } zoneInfo.WriteTo(bW); //write all zone records List <DnsResourceRecord> records = new List <DnsResourceRecord>(); 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); } }
protected override void CacheRecords(IReadOnlyList <DnsResourceRecord> resourceRecords) { List <DnsResourceRecord> dnameRecords = null; //read and set glue records from base class; also collect any DNAME records found foreach (DnsResourceRecord resourceRecord in resourceRecords) { IReadOnlyList <DnsResourceRecord> glueRecords = GetGlueRecordsFrom(resourceRecord); IReadOnlyList <DnsResourceRecord> rrsigRecords = GetRRSIGRecordsFrom(resourceRecord); IReadOnlyList <DnsResourceRecord> nsecRecords = GetNSECRecordsFrom(resourceRecord); if ((glueRecords is not null) || (rrsigRecords is not null) || (nsecRecords is not null)) { DnsResourceRecordInfo rrInfo = resourceRecord.GetRecordInfo(); rrInfo.GlueRecords = glueRecords; rrInfo.RRSIGRecords = rrsigRecords; rrInfo.NSECRecords = nsecRecords; if (glueRecords is not null) { foreach (DnsResourceRecord glueRecord in glueRecords) { IReadOnlyList <DnsResourceRecord> glueRRSIGRecords = GetRRSIGRecordsFrom(glueRecord); if (glueRRSIGRecords is not null) { glueRecord.GetRecordInfo().RRSIGRecords = glueRRSIGRecords; } } } if (nsecRecords is not null) { foreach (DnsResourceRecord nsecRecord in nsecRecords) { IReadOnlyList <DnsResourceRecord> nsecRRSIGRecords = GetRRSIGRecordsFrom(nsecRecord); if (nsecRRSIGRecords is not null) { nsecRecord.GetRecordInfo().RRSIGRecords = nsecRRSIGRecords; } } } } if (resourceRecord.Type == DnsResourceRecordType.DNAME) { if (dnameRecords is null) { dnameRecords = new List <DnsResourceRecord>(1); } dnameRecords.Add(resourceRecord); } } if (resourceRecords.Count == 1) { DnsResourceRecord resourceRecord = resourceRecords[0]; CacheZone zone = _root.GetOrAdd(resourceRecord.Name, delegate(string key) { return(new CacheZone(resourceRecord.Name, 1)); }); if (zone.SetRecords(resourceRecord.Type, resourceRecords, _dnsServer.ServeStale)) { Interlocked.Increment(ref _totalEntries); } } else { Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByDomainRecords = DnsResourceRecord.GroupRecords(resourceRecords); bool serveStale = _dnsServer.ServeStale; int addedEntries = 0; //add grouped records foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > groupedByTypeRecords in groupedByDomainRecords) { if (dnameRecords is not null) { bool foundSynthesizedCNAME = false; foreach (DnsResourceRecord dnameRecord in dnameRecords) { if (groupedByTypeRecords.Key.EndsWith("." + dnameRecord.Name, StringComparison.OrdinalIgnoreCase)) { foundSynthesizedCNAME = true; break; } } if (foundSynthesizedCNAME) { continue; //do not cache synthesized CNAME } } CacheZone zone = _root.GetOrAdd(groupedByTypeRecords.Key, delegate(string key) { return(new CacheZone(groupedByTypeRecords.Key, groupedByTypeRecords.Value.Count)); }); foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > groupedRecords in groupedByTypeRecords.Value) { if (zone.SetRecords(groupedRecords.Key, groupedRecords.Value, serveStale)) { addedEntries++; } } } if (addedEntries > 0) { Interlocked.Add(ref _totalEntries, addedEntries); } } }
public void WriteTo(BinaryWriter bW) { if (_zone == null) { throw new InvalidOperationException(); } bW.Write((byte)4); //version bW.WriteShortString(_name); bW.Write((byte)_type); bW.Write(_disabled); bW.Write((byte)_zoneTransfer); if (_zoneTransferNameServers is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(_zoneTransferNameServers.Count)); foreach (IPAddress nameServer in _zoneTransferNameServers) { nameServer.WriteTo(bW); } } bW.Write((byte)_notify); if (_notifyNameServers is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(_notifyNameServers.Count)); foreach (IPAddress nameServer in _notifyNameServers) { nameServer.WriteTo(bW); } } switch (_type) { case AuthZoneType.Primary: if (_zoneHistory is null) { bW.Write(0); } else { bW.Write(_zoneHistory.Count); foreach (DnsResourceRecord record in _zoneHistory) { record.WriteTo(bW.BaseStream); if (record.Tag is not DnsResourceRecordInfo rrInfo) { rrInfo = new DnsResourceRecordInfo(); //default info } rrInfo.WriteTo(bW); } } if (_tsigKeyNames is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(_tsigKeyNames.Count)); foreach (KeyValuePair <string, object> tsigKeyName in _tsigKeyNames) { bW.WriteShortString(tsigKeyName.Key); } } break; case AuthZoneType.Secondary: bW.Write(_expiry); if (_zoneHistory is null) { bW.Write(0); } else { bW.Write(_zoneHistory.Count); foreach (DnsResourceRecord record in _zoneHistory) { record.WriteTo(bW.BaseStream); if (record.Tag is not DnsResourceRecordInfo rrInfo) { rrInfo = new DnsResourceRecordInfo(); //default info } rrInfo.WriteTo(bW); } } if (_tsigKeyNames is null) { bW.Write((byte)0); } else { bW.Write(Convert.ToByte(_tsigKeyNames.Count)); foreach (KeyValuePair <string, object> tsigKeyName in _tsigKeyNames) { bW.WriteShortString(tsigKeyName.Key); } } break; case AuthZoneType.Stub: bW.Write(_expiry); break; } }
public override DnsDatagram Query(DnsDatagram request, bool serveStaleAndResetExpiry = false, bool findClosestNameServers = false) { DnsQuestionRecord question = request.Question[0]; CacheZone zone; CacheZone closest = null; CacheZone delegation = null; if (findClosestNameServers) { zone = _root.FindZone(question.Name, out closest, out delegation); } else { if (!_root.TryGet(question.Name, out zone)) { _ = _root.FindZone(question.Name, out closest, out _); //zone not found; attempt to find closest } } if (zone is not null) { //zone found IReadOnlyList <DnsResourceRecord> answers = zone.QueryRecords(question.Type, serveStaleAndResetExpiry, false); if (answers.Count > 0) { //answer found in cache DnsResourceRecord firstRR = answers[0]; if (firstRR.RDATA is DnsSpecialCacheRecord dnsSpecialCacheRecord) { IReadOnlyList <EDnsOption> specialOptions = null; if (serveStaleAndResetExpiry) { if (firstRR.IsStale) { firstRR.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } if (dnsSpecialCacheRecord.Authority is not null) { foreach (DnsResourceRecord record in dnsSpecialCacheRecord.Authority) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } } if (dnsSpecialCacheRecord.RCODE == DnsResponseCode.NxDomain) { List <EDnsOption> newOptions = new List <EDnsOption>(dnsSpecialCacheRecord.EDnsOptions.Count + 1); newOptions.AddRange(dnsSpecialCacheRecord.EDnsOptions); newOptions.Add(new EDnsOption(EDnsOptionCode.EXTENDED_DNS_ERROR, new EDnsExtendedDnsErrorOption(EDnsExtendedDnsErrorCode.StaleNxDomainAnswer, null))); specialOptions = newOptions; } } if (specialOptions is null) { specialOptions = dnsSpecialCacheRecord.EDnsOptions; } if (request.DnssecOk) { bool authenticData; switch (dnsSpecialCacheRecord.Type) { case DnsSpecialCacheRecordType.NegativeCache: authenticData = true; break; default: authenticData = false; break; } if (request.CheckingDisabled) { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, authenticData, request.CheckingDisabled, dnsSpecialCacheRecord.OriginalRCODE, request.Question, dnsSpecialCacheRecord.OriginalAnswer, dnsSpecialCacheRecord.OriginalAuthority, dnsSpecialCacheRecord.Additional, _dnsServer.UdpPayloadSize, EDnsHeaderFlags.DNSSEC_OK, specialOptions)); } else { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, authenticData, request.CheckingDisabled, dnsSpecialCacheRecord.RCODE, request.Question, null, dnsSpecialCacheRecord.Authority, null, _dnsServer.UdpPayloadSize, EDnsHeaderFlags.DNSSEC_OK, specialOptions)); } } else { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, dnsSpecialCacheRecord.RCODE, request.Question, null, dnsSpecialCacheRecord.NoDnssecAuthority, null, request.EDNS is null ? ushort.MinValue : _dnsServer.UdpPayloadSize, EDnsHeaderFlags.None, specialOptions)); } } DnsResourceRecord lastRR = answers[answers.Count - 1]; if ((lastRR.Type != question.Type) && (lastRR.Type == DnsResourceRecordType.CNAME) && (question.Type != DnsResourceRecordType.ANY)) { List <DnsResourceRecord> newAnswers = new List <DnsResourceRecord>(answers.Count + 3); newAnswers.AddRange(answers); ResolveCNAME(question, lastRR, serveStaleAndResetExpiry, newAnswers); answers = newAnswers; } IReadOnlyList <DnsResourceRecord> authority = null; EDnsHeaderFlags ednsFlags = EDnsHeaderFlags.None; if (request.DnssecOk) { //DNSSEC enabled; insert RRSIG records List <DnsResourceRecord> newAnswers = new List <DnsResourceRecord>(answers.Count * 2); List <DnsResourceRecord> newAuthority = null; foreach (DnsResourceRecord answer in answers) { newAnswers.Add(answer); DnsResourceRecordInfo rrInfo = answer.GetRecordInfo(); IReadOnlyList <DnsResourceRecord> rrsigRecords = rrInfo.RRSIGRecords; if (rrsigRecords is not null) { newAnswers.AddRange(rrsigRecords); foreach (DnsResourceRecord rrsigRecord in rrsigRecords) { if (!DnsRRSIGRecordData.IsWildcard(rrsigRecord)) { continue; } //add NSEC/NSEC3 for the wildcard proof if (newAuthority is null) { newAuthority = new List <DnsResourceRecord>(2); } IReadOnlyList <DnsResourceRecord> nsecRecords = rrInfo.NSECRecords; if (nsecRecords is not null) { foreach (DnsResourceRecord nsecRecord in nsecRecords) { newAuthority.Add(nsecRecord); IReadOnlyList <DnsResourceRecord> nsecRRSIGRecords = nsecRecord.GetRecordInfo().RRSIGRecords; if (nsecRRSIGRecords is not null) { newAuthority.AddRange(nsecRRSIGRecords); } } } } } } answers = newAnswers; authority = newAuthority; ednsFlags = EDnsHeaderFlags.DNSSEC_OK; } IReadOnlyList <DnsResourceRecord> additional = null; switch (question.Type) { case DnsResourceRecordType.NS: case DnsResourceRecordType.MX: case DnsResourceRecordType.SRV: additional = GetAdditionalRecords(answers, serveStaleAndResetExpiry, request.DnssecOk); break; } EDnsOption[] options = null; if (serveStaleAndResetExpiry) { foreach (DnsResourceRecord record in answers) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } if (additional is not null) { foreach (DnsResourceRecord record in additional) { if (record.IsStale) { record.ResetExpiry(30); //reset expiry by 30 seconds so that resolver tries again only after 30 seconds as per draft-ietf-dnsop-serve-stale-04 } } } options = new EDnsOption[] { new EDnsOption(EDnsOptionCode.EXTENDED_DNS_ERROR, new EDnsExtendedDnsErrorOption(EDnsExtendedDnsErrorCode.StaleAnswer, null)) }; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, answers[0].DnssecStatus == DnssecStatus.Secure, request.CheckingDisabled, DnsResponseCode.NoError, request.Question, answers, authority, additional, request.EDNS is null ? ushort.MinValue : _dnsServer.UdpPayloadSize, ednsFlags, options)); } } else { //zone not found //check for DNAME in closest zone if (closest is not null) { IReadOnlyList <DnsResourceRecord> answer = closest.QueryRecords(DnsResourceRecordType.DNAME, serveStaleAndResetExpiry, true); if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME)) { DnsResponseCode rCode; if (DoDNAMESubstitution(question, answer, serveStaleAndResetExpiry, out answer)) { rCode = DnsResponseCode.NoError; } else { rCode = DnsResponseCode.YXDomain; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, answer[0].DnssecStatus == DnssecStatus.Secure, request.CheckingDisabled, rCode, request.Question, answer)); } } } //no answer in cache //check for closest delegation if any if (findClosestNameServers && (delegation is not null)) { //return closest name servers in delegation if (question.Type == DnsResourceRecordType.DS) { //find parent delegation string domain = AuthZoneManager.GetParentZone(question.Name); if (domain is null) { return(null); //dont find NS for root } _ = _root.FindZone(domain, out _, out delegation); if (delegation is null) { return(null); //no cached delegation found } } IReadOnlyList <DnsResourceRecord> closestAuthority = delegation.QueryRecords(DnsResourceRecordType.NS, serveStaleAndResetExpiry, true); if ((closestAuthority.Count > 0) && (closestAuthority[0].Type == DnsResourceRecordType.NS) && (closestAuthority[0].Name.Length > 0)) //dont trust root name servers from cache! { if (request.DnssecOk) { closestAuthority = AddDSRecordsTo(delegation, serveStaleAndResetExpiry, closestAuthority); } IReadOnlyList <DnsResourceRecord> additional = GetAdditionalRecords(closestAuthority, serveStaleAndResetExpiry, request.DnssecOk); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, closestAuthority[0].DnssecStatus == DnssecStatus.Secure, request.CheckingDisabled, DnsResponseCode.NoError, request.Question, null, closestAuthority, additional)); } } //no cached delegation found return(null); }