public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return(false); } if (ReferenceEquals(this, obj)) { return(true); } DnsResourceRecord other = obj as DnsResourceRecord; if (other == null) { return(false); } if (!this._name.Equals(other._name, StringComparison.CurrentCultureIgnoreCase)) { return(false); } if (this._type != other._type) { return(false); } if (this._class != other._class) { return(false); } if (this._ttl != other._ttl) { return(false); } return(this._data.Equals(other._data)); }
private void ResolveAdditionalRecords(DnsResourceRecord refRecord, string domain, List <DnsResourceRecord> additionalRecords) { IReadOnlyList <DnsResourceRecord> glueRecords = GetGlueRecordsFrom(refRecord); if (glueRecords.Count > 0) { bool added = false; foreach (DnsResourceRecord glueRecord in glueRecords) { if (!glueRecord.IsStale) { added = true; additionalRecords.Add(glueRecord); } } if (added) { return; } } if (_cache.TryGetValue(domain.ToLower(), out DnsCacheEntry entry)) { IReadOnlyList <DnsResourceRecord> glueAs = entry.QueryRecords(DnsResourceRecordType.A, true); if ((glueAs.Count > 0) && (glueAs[0].Type == DnsResourceRecordType.A)) { additionalRecords.AddRange(glueAs); } IReadOnlyList <DnsResourceRecord> glueAAAAs = entry.QueryRecords(DnsResourceRecordType.AAAA, true); if ((glueAAAAs.Count > 0) && (glueAAAAs[0].Type == DnsResourceRecordType.AAAA)) { additionalRecords.AddRange(glueAAAAs); } } }
private void ResolveAdditionalRecords(DnsResourceRecord refRecord, string domain, List <DnsResourceRecord> additionalRecords) { IReadOnlyList <DnsResourceRecord> glueRecords = GetGlueRecordsFrom(refRecord); if (glueRecords.Count > 0) { bool added = false; foreach (DnsResourceRecord glueRecord in glueRecords) { if (!glueRecord.IsStale) { added = true; additionalRecords.Add(glueRecord); } } if (added) { return; } } IReadOnlyList <DnsResourceRecord> glueAs = GetRecords(domain, DnsResourceRecordType.A); if ((glueAs != null) && (glueAs.Count > 0) && (glueAs[0].RDATA is DnsARecord)) { additionalRecords.AddRange(glueAs); } IReadOnlyList <DnsResourceRecord> glueAAAAs = GetRecords(domain, DnsResourceRecordType.AAAA); if ((glueAAAAs != null) && (glueAAAAs.Count > 0) && (glueAAAAs[0].RDATA is DnsAAAARecord)) { additionalRecords.AddRange(glueAAAAs); } }
public void CacheResponse(DnsDatagram response) { if (!response.Header.IsResponse) { return; } //combine all records in the response List <DnsResourceRecord> allRecords = new List <DnsResourceRecord>(); switch (response.Header.RCODE) { case DnsResponseCode.NameError: if (response.Authority.Length > 0) { DnsResourceRecord authority = response.Authority[0]; if (authority.Type == DnsResourceRecordType.SOA) { foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsNXRecord(authority)); record.SetExpiry(); CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record }); } } } break; case DnsResponseCode.NoError: if ((response.Answer.Length == 0) && (response.Authority.Length > 0)) { DnsResourceRecord authority = response.Authority[0]; if (authority.Type == DnsResourceRecordType.SOA) { foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(authority)); record.SetExpiry(); CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record }); } } } else { allRecords.AddRange(response.Answer); } break; default: return; //nothing to do } allRecords.AddRange(response.Authority); allRecords.AddRange(response.Additional); #region group all records by domain and type Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntries = new Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > >(); foreach (DnsResourceRecord record in allRecords) { Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntries; if (cacheEntries.ContainsKey(record.Name)) { cacheTypeEntries = cacheEntries[record.Name]; } else { cacheTypeEntries = new Dictionary <DnsResourceRecordType, List <DnsResourceRecord> >(); cacheEntries.Add(record.Name, cacheTypeEntries); } List <DnsResourceRecord> cacheRREntries; if (cacheTypeEntries.ContainsKey(record.Type)) { cacheRREntries = cacheTypeEntries[record.Type]; } else { cacheRREntries = new List <DnsResourceRecord>(); cacheTypeEntries.Add(record.Type, cacheRREntries); } cacheRREntries.Add(record); } #endregion //add grouped entries into cache foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntry in cacheEntries) { string domain = cacheEntry.Key; foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntry in cacheEntry.Value) { DnsResourceRecordType type = cacheTypeEntry.Key; DnsResourceRecord[] records = cacheTypeEntry.Value.ToArray(); foreach (DnsResourceRecord record in records) { record.SetExpiry(); } CacheEntry(domain, type, records); } } //cache for ANY request if (response.Question[0].Type == DnsResourceRecordType.ANY) { CacheEntry(response.Question[0].Name, DnsResourceRecordType.ANY, response.Answer); } }
public void CacheResponse(DnsDatagram response) { if (!response.Header.IsResponse || response.Header.Truncation || (response.Question.Length == 0)) { return; //ineligible response } switch (response.Header.RCODE) { case DnsResponseCode.NoError: case DnsResponseCode.NameError: //cache response after this switch break; default: //cache as failure record with RCODE foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsFailureRecord(response.Header.RCODE)); record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); CacheRecords(new DnsResourceRecord[] { record }); } return; } //cache only NoError and NameError responses //combine all records in the response List <DnsResourceRecord> cachableRecords = new List <DnsResourceRecord>(); //get cachable answer records foreach (DnsQuestionRecord question in response.Question) { string qName = question.Name; foreach (DnsResourceRecord answer in response.Answer) { if (answer.Name.Equals(qName, StringComparison.OrdinalIgnoreCase)) { cachableRecords.Add(answer); switch (answer.Type) { case DnsResourceRecordType.CNAME: qName = (answer.RDATA as DnsCNAMERecord).CNAMEDomainName; break; case DnsResourceRecordType.NS: string nsDomain = (answer.RDATA as DnsNSRecord).NSDomainName; if (!nsDomain.EndsWith(".root-servers.net", StringComparison.OrdinalIgnoreCase)) { foreach (DnsResourceRecord record in response.Additional) { if (nsDomain.Equals(record.Name, StringComparison.OrdinalIgnoreCase)) { cachableRecords.Add(record); } } } break; case DnsResourceRecordType.MX: string mxExchange = (answer.RDATA as DnsMXRecord).Exchange; foreach (DnsResourceRecord record in response.Additional) { if (mxExchange.Equals(record.Name, StringComparison.OrdinalIgnoreCase)) { cachableRecords.Add(record); } } break; } } } } //get cachable authority records if (response.Authority.Length > 0) { DnsResourceRecord authority = response.Authority[0]; if (authority.Type == DnsResourceRecordType.SOA) { authority.SetExpiry(_minimumRecordTtl, _serveStaleTtl); if (response.Answer.Length == 0) { //empty response with authority foreach (DnsQuestionRecord question in response.Question) { if (question.Name.Equals(authority.Name, StringComparison.OrdinalIgnoreCase) || question.Name.EndsWith("." + authority.Name, StringComparison.OrdinalIgnoreCase) || authority.Name.Equals("", StringComparison.OrdinalIgnoreCase)) { DnsResourceRecord record = null; switch (response.Header.RCODE) { case DnsResponseCode.NameError: record = new DnsResourceRecord(question.Name, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsNXRecord(authority)); break; case DnsResponseCode.NoError: record = new DnsResourceRecord(question.Name, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsEmptyRecord(authority)); break; } if (record != null) { record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); CacheRecords(new DnsResourceRecord[] { record }); } } } } else { //answer response with authority DnsResourceRecord lastAnswer = response.Answer[response.Answer.Length - 1]; if (lastAnswer.Type == DnsResourceRecordType.CNAME) { foreach (DnsQuestionRecord question in response.Question) { if (question.Name.Equals(authority.Name, StringComparison.OrdinalIgnoreCase) || question.Name.EndsWith("." + authority.Name, StringComparison.OrdinalIgnoreCase)) { DnsResourceRecord record = null; switch (response.Header.RCODE) { case DnsResponseCode.NameError: record = new DnsResourceRecord((lastAnswer.RDATA as DnsCNAMERecord).CNAMEDomainName, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsNXRecord(authority)); break; case DnsResponseCode.NoError: record = new DnsResourceRecord((lastAnswer.RDATA as DnsCNAMERecord).CNAMEDomainName, question.Type, question.Class, (authority.RDATA as DnsSOARecord).Minimum, new DnsEmptyRecord(authority)); break; } if (record != null) { record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); CacheRecords(new DnsResourceRecord[] { record }); } break; } } } } } else if (authority.Type == DnsResourceRecordType.NS) { if (response.Answer.Length == 0) { foreach (DnsQuestionRecord question in response.Question) { foreach (DnsResourceRecord authorityRecord in response.Authority) { if ((authorityRecord.Type == DnsResourceRecordType.NS) && (authorityRecord.RDATA as DnsNSRecord).NSDomainName.Equals(response.Metadata.NameServerAddress.Host, StringComparison.OrdinalIgnoreCase)) { //empty response from authority name server that was queried DnsResourceRecord record = null; switch (response.Header.RCODE) { case DnsResponseCode.NameError: record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsNXRecord(authority)); break; case DnsResponseCode.NoError: record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsEmptyRecord(authority)); break; } if (record != null) { record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); CacheRecords(new DnsResourceRecord[] { record }); } break; } } } } //cache suitable NS records if ((response.Question[0].Type != DnsResourceRecordType.NS) || (response.Answer.Length == 0)) { foreach (DnsQuestionRecord question in response.Question) { foreach (DnsResourceRecord authorityRecords in response.Authority) { if ((authorityRecords.Type == DnsResourceRecordType.NS) && (question.Name.Equals(authorityRecords.Name, StringComparison.OrdinalIgnoreCase) || question.Name.EndsWith("." + authorityRecords.Name, StringComparison.OrdinalIgnoreCase))) { cachableRecords.Add(authorityRecords); string nsDomain = (authorityRecords.RDATA as DnsNSRecord).NSDomainName; if (!nsDomain.EndsWith(".root-servers.net", StringComparison.OrdinalIgnoreCase)) { foreach (DnsResourceRecord record in response.Additional) { if (nsDomain.Equals(record.Name, StringComparison.OrdinalIgnoreCase)) { cachableRecords.Add(record); } } } } } } } } } else { //no authority records if (response.Answer.Length == 0) { //empty response with no authority foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = null; switch (response.Header.RCODE) { case DnsResponseCode.NameError: record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsNXRecord(null)); break; case DnsResponseCode.NoError: record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsEmptyRecord(null)); break; } if (record != null) { record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); CacheRecords(new DnsResourceRecord[] { record }); } } } } //cache for ANY request if (response.Header.RCODE == DnsResponseCode.NoError) { if ((response.Question.Length == 1) && (response.Question[0].Type == DnsResourceRecordType.ANY)) { DnsResourceRecord record = new DnsResourceRecord(response.Question[0].Name, DnsResourceRecordType.ANY, response.Question[0].Class, _negativeRecordTtl, new DnsANYRecord(response.Answer)); record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); CacheRecords(new DnsResourceRecord[] { record }); } else { foreach (DnsQuestionRecord question in response.Question) { if (question.Type == DnsResourceRecordType.ANY) { List <DnsResourceRecord> answerRecords = new List <DnsResourceRecord>(); foreach (DnsResourceRecord answerRecord in response.Answer) { if (answerRecord.Name.Equals(question.Name, StringComparison.OrdinalIgnoreCase)) { answerRecords.Add(answerRecord); } } DnsResourceRecord record = new DnsResourceRecord(question.Name, DnsResourceRecordType.ANY, question.Class, _negativeRecordTtl, new DnsANYRecord(answerRecords.ToArray())); record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); CacheRecords(new DnsResourceRecord[] { record }); } } } } if (cachableRecords.Count < 1) { return; //nothing to cache } //set expiry for cached records foreach (DnsResourceRecord record in cachableRecords) { record.SetExpiry(_minimumRecordTtl, _serveStaleTtl); } CacheRecords(cachableRecords); }
protected virtual void CacheRecords(IReadOnlyList <DnsResourceRecord> resourceRecords) { if (resourceRecords.Count == 1) { DnsCacheEntry entry = _cache.GetOrAdd(resourceRecords[0].Name.ToLower(), delegate(string key) { return(new DnsCacheEntry()); }); entry.SetRecords(resourceRecords[0].Type, resourceRecords); } else { Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntries = DnsResourceRecord.GroupRecords(resourceRecords); //add grouped entries into cache foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntry in cacheEntries) { DnsCacheEntry entry = _cache.GetOrAdd(cacheEntry.Key.ToLower(), delegate(string key) { return(new DnsCacheEntry()); }); foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntry in cacheEntry.Value) { entry.SetRecords(cacheTypeEntry.Key, cacheTypeEntry.Value); } } } }
public virtual DnsDatagram Query(DnsDatagram request, bool serveStale = false) { if (serveStale) { throw new NotImplementedException("DnsCache does not implement serve stale."); } DnsQuestionRecord question = request.Question[0]; IReadOnlyList <DnsResourceRecord> answerRecords = GetRecords(question.Name, question.Type); if (answerRecords != null) { if (answerRecords[0].RDATA is DnsEmptyRecord) { DnsResourceRecord[] responseAuthority; DnsResourceRecord authority = (answerRecords[0].RDATA as DnsEmptyRecord).Authority; if (authority == null) { responseAuthority = Array.Empty <DnsResourceRecord>(); } else { responseAuthority = new DnsResourceRecord[] { authority } }; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, responseAuthority)); } if (answerRecords[0].RDATA is DnsNXRecord) { DnsResourceRecord[] responseAuthority; DnsResourceRecord authority = (answerRecords[0].RDATA as DnsNXRecord).Authority; if (authority == null) { responseAuthority = Array.Empty <DnsResourceRecord>(); } else { responseAuthority = new DnsResourceRecord[] { authority } }; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NameError, request.Question, null, responseAuthority)); } if (answerRecords[0].RDATA is DnsANYRecord) { DnsANYRecord anyRR = answerRecords[0].RDATA as DnsANYRecord; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, anyRR.Records)); } if (answerRecords[0].RDATA is DnsFailureRecord) { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, (answerRecords[0].RDATA as DnsFailureRecord).RCODE, request.Question)); } IReadOnlyList <DnsResourceRecord> additionalRecords = null; switch (question.Type) { case DnsResourceRecordType.NS: case DnsResourceRecordType.MX: case DnsResourceRecordType.SRV: additionalRecords = GetAdditionalRecords(answerRecords); break; } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, answerRecords, null, additionalRecords)); } IReadOnlyList <DnsResourceRecord> closestAuthority = GetClosestNameServers(question.Name); if (closestAuthority != null) { IReadOnlyList <DnsResourceRecord> additionalRecords = GetAdditionalRecords(closestAuthority); return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, closestAuthority, additionalRecords)); } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.Refused, request.Question)); }
public virtual DnsDatagram Query(DnsDatagram request, bool serveStale = false) { if (serveStale) { throw new NotImplementedException("DnsCache does not implement serve stale."); } DnsQuestionRecord question = request.Question[0]; IReadOnlyList <DnsResourceRecord> answerRecords = GetRecords(question.Name, question.Type); if (answerRecords != null) { if (answerRecords[0].RDATA is DnsEmptyRecord) { DnsResourceRecord[] responseAuthority; DnsResourceRecord authority = (answerRecords[0].RDATA as DnsEmptyRecord).Authority; if (authority == null) { responseAuthority = Array.Empty <DnsResourceRecord>(); } else { responseAuthority = new DnsResourceRecord[] { authority } }; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, responseAuthority)); } if (answerRecords[0].RDATA is DnsNXRecord) { DnsResourceRecord[] responseAuthority; DnsResourceRecord authority = (answerRecords[0].RDATA as DnsNXRecord).Authority; if (authority == null) { responseAuthority = Array.Empty <DnsResourceRecord>(); } else { responseAuthority = new DnsResourceRecord[] { authority } }; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NameError, request.Question, null, responseAuthority)); } if (answerRecords[0].RDATA is DnsANYRecord) { DnsANYRecord anyRR = answerRecords[0].RDATA as DnsANYRecord; return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, anyRR.Records)); } if (answerRecords[0].RDATA is DnsFailureRecord) { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, (answerRecords[0].RDATA as DnsFailureRecord).RCODE, request.Question)); } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, answerRecords)); } string currentZone = question.Name; while (currentZone != null) { IReadOnlyList <DnsResourceRecord> nameServers = GetClosestNameServers(currentZone); if (nameServers == null) { break; } List <DnsResourceRecord> glueRecords = new List <DnsResourceRecord>(); foreach (DnsResourceRecord nameServer in nameServers) { string nsDomain = (nameServer.RDATA as DnsNSRecord).NameServer; IReadOnlyList <DnsResourceRecord> glueAs = GetRecords(nsDomain, DnsResourceRecordType.A); if ((glueAs != null) && (glueAs.Count > 0) && (glueAs[0].RDATA is DnsARecord)) { glueRecords.AddRange(glueAs); } IReadOnlyList <DnsResourceRecord> glueAAAAs = GetRecords(nsDomain, DnsResourceRecordType.AAAA); if ((glueAAAAs != null) && (glueAAAAs.Count > 0) && (glueAAAAs[0].RDATA is DnsAAAARecord)) { glueRecords.AddRange(glueAAAAs); } } if (glueRecords.Count > 0) { return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.NoError, request.Question, null, nameServers, glueRecords)); } currentZone = GetParentZone(currentZone); } return(new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, true, false, false, DnsResponseCode.Refused, request.Question)); }
public DnsDatagram Query(DnsDatagram request) { DnsQuestionRecord question = request.Question[0]; string domain = question.Name.ToLower(); DnsResourceRecord[] records = GetRecords(domain, question.Type); if (records != null) { if (records[0].RDATA is DnsEmptyRecord) { DnsResourceRecord[] responseAuthority; DnsResourceRecord authority = (records[0].RDATA as DnsEmptyRecord).Authority; if (authority == null) { responseAuthority = new DnsResourceRecord[] { } } ; else { responseAuthority = new DnsResourceRecord[] { authority } }; return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NoError, 1, 0, 1, 0), request.Question, new DnsResourceRecord[] { }, responseAuthority, new DnsResourceRecord[] { })); } if (records[0].RDATA is DnsNXRecord) { return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NameError, 1, 0, 1, 0), request.Question, new DnsResourceRecord[] { }, new DnsResourceRecord[] { (records[0].RDATA as DnsNXRecord).Authority }, new DnsResourceRecord[] { })); } return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NoError, 1, (ushort)records.Length, 0, 0), request.Question, records, new DnsResourceRecord[] { }, new DnsResourceRecord[] { })); } DnsResourceRecord[] nameServers = GetNearestNameServers(domain); if (nameServers != null) { List <DnsResourceRecord> glueRecords = new List <DnsResourceRecord>(); foreach (DnsResourceRecord nameServer in nameServers) { string nsDomain = (nameServer.RDATA as DnsNSRecord).NSDomainName; DnsResourceRecord[] glueAs = GetRecords(nsDomain, DnsResourceRecordType.A); if (glueAs != null) { glueRecords.AddRange(glueAs); } DnsResourceRecord[] glueAAAAs = GetRecords(nsDomain, DnsResourceRecordType.AAAA); if (glueAAAAs != null) { glueRecords.AddRange(glueAAAAs); } } DnsResourceRecord[] additional = glueRecords.ToArray(); return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NoError, 1, 0, (ushort)nameServers.Length, (ushort)additional.Length), request.Question, new DnsResourceRecord[] { }, nameServers, additional)); } return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.Refused, 1, 0, 0, 0), request.Question, new DnsResourceRecord[] { }, new DnsResourceRecord[] { }, new DnsResourceRecord[] { })); }
protected override void CacheRecords(ICollection <DnsResourceRecord> resourceRecords) { if (resourceRecords.Count == 1) { foreach (DnsResourceRecord resourceRecord in resourceRecords) { CacheEntry(resourceRecord.Name, resourceRecord.Type, new DnsResourceRecord[] { resourceRecord }); } } else { Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntries = DnsResourceRecord.GroupRecords(resourceRecords); //add grouped entries into cache foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntry in cacheEntries) { string domain = cacheEntry.Key; foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntry in cacheEntry.Value) { DnsResourceRecordType type = cacheTypeEntry.Key; DnsResourceRecord[] records = cacheTypeEntry.Value.ToArray(); CacheEntry(domain, type, records); } } } }
public static async Task <DnsDatagram> ReadFromTcpAsync(Stream stream, MemoryStream sharedBuffer, CancellationToken cancellationToken = default) { DnsDatagram datagram = new DnsDatagram(); List <DnsQuestionRecord> question = null; List <DnsResourceRecord> answer = null; List <DnsResourceRecord> authority = null; List <DnsResourceRecord> additional = null; while (true) { if (question != null) { if (question.Count == 0) { break; } if (question[0].Type != DnsResourceRecordType.AXFR) { break; } if (answer.Count == 0) { break; } DnsResourceRecord lastAnswer = answer[answer.Count - 1]; if ((lastAnswer.Type == DnsResourceRecordType.SOA) && lastAnswer.Name.Equals(question[0].Name, StringComparison.OrdinalIgnoreCase)) { break; //zone transfer complete } } ushort length = await ReadUInt16NetworkOrderAsync(stream, cancellationToken); sharedBuffer.SetLength(0); await stream.CopyToAsync(sharedBuffer, 512, length, cancellationToken); datagram._size += length; sharedBuffer.Position = 0; if (question == null) { datagram._ID = ReadUInt16NetworkOrder(sharedBuffer); } else { ushort ID = ReadUInt16NetworkOrder(sharedBuffer); if (ID != datagram._ID) { throw new DnsClientException("Error while parsing TCP response: response ID does not match with previous response ID."); } } int lB = sharedBuffer.ReadByte(); datagram._QR = Convert.ToByte((lB & 0x80) >> 7); datagram._OPCODE = (DnsOpcode)Convert.ToByte((lB & 0x78) >> 3); datagram._AA = Convert.ToByte((lB & 0x4) >> 2); datagram._TC = Convert.ToByte((lB & 0x2) >> 1); datagram._RD = Convert.ToByte(lB & 0x1); int rB = sharedBuffer.ReadByte(); datagram._RA = Convert.ToByte((rB & 0x80) >> 7); datagram._Z = Convert.ToByte((rB & 0x40) >> 6); datagram._AD = Convert.ToByte((rB & 0x20) >> 5); datagram._CD = Convert.ToByte((rB & 0x10) >> 4); datagram._RCODE = (DnsResponseCode)(rB & 0xf); ushort QDCOUNT = ReadUInt16NetworkOrder(sharedBuffer); ushort ANCOUNT = ReadUInt16NetworkOrder(sharedBuffer); ushort NSCOUNT = ReadUInt16NetworkOrder(sharedBuffer); ushort ARCOUNT = ReadUInt16NetworkOrder(sharedBuffer); if (question == null) { question = new List <DnsQuestionRecord>(QDCOUNT); } if (answer == null) { answer = new List <DnsResourceRecord>(ANCOUNT); } if (authority == null) { authority = new List <DnsResourceRecord>(NSCOUNT); } if (additional == null) { additional = new List <DnsResourceRecord>(ARCOUNT); } try { for (int i = 0; i < QDCOUNT; i++) { question.Add(new DnsQuestionRecord(sharedBuffer)); } for (int i = 0; i < ANCOUNT; i++) { answer.Add(new DnsResourceRecord(sharedBuffer)); } for (int i = 0; i < NSCOUNT; i++) { authority.Add(new DnsResourceRecord(sharedBuffer)); } for (int i = 0; i < ARCOUNT; i++) { additional.Add(new DnsResourceRecord(sharedBuffer)); } } catch (Exception ex) { datagram._parsingException = ex; break; } } datagram._question = question; datagram._answer = answer; datagram._authority = authority; datagram._additional = additional; return(datagram); }
public void SetRecords(DnsResourceRecordType type, IReadOnlyList <DnsResourceRecord> records) { if ((records.Count > 0) && (records[0].RDATA is DnsSpecialCacheRecord splRecord) && splRecord.IsFailure) { //call trying to cache failure record if (_entries.TryGetValue(type, out IReadOnlyList <DnsResourceRecord> existingRecords)) { if ((existingRecords.Count > 0) && !(existingRecords[0].RDATA is DnsSpecialCacheRecord existingSplRecord && existingSplRecord.IsFailure) && !DnsResourceRecord.IsRRSetStale(existingRecords)) { return; //skip to avoid overwriting a useful record with a failure record } } } _entries[type] = records; }
public void CacheResponse(DnsDatagram response) { if (!response.IsResponse || response.Truncation || (response.Question.Count == 0)) { return; //ineligible response } switch (response.RCODE) { case DnsResponseCode.NoError: case DnsResponseCode.NxDomain: case DnsResponseCode.YXDomain: //cache response after this switch break; default: //cache as failure record foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _failureRecordTtl, new DnsSpecialCacheRecord(response, true)); record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); InternalCacheRecords(new DnsResourceRecord[] { record }); } return; } //combine all records in the response List <DnsResourceRecord> cachableRecords = new List <DnsResourceRecord>(); //get cachable answer records foreach (DnsQuestionRecord question in response.Question) { string qName = question.Name; foreach (DnsResourceRecord answer in response.Answer) { if (answer.Name.Equals(qName, StringComparison.OrdinalIgnoreCase)) { cachableRecords.Add(answer); switch (answer.Type) { case DnsResourceRecordType.CNAME: qName = (answer.RDATA as DnsCNAMERecord).Domain; break; case DnsResourceRecordType.NS: if (response.Authority.Count == 0) { //add glue from additional section string nsDomain = (answer.RDATA as DnsNSRecord).NameServer; foreach (DnsResourceRecord additional in response.Additional) { if (nsDomain.Equals(additional.Name, StringComparison.OrdinalIgnoreCase)) { switch (additional.Type) { case DnsResourceRecordType.A: if (IPAddress.IsLoopback((additional.RDATA as DnsARecord).Address)) { continue; } break; case DnsResourceRecordType.AAAA: if (IPAddress.IsLoopback((additional.RDATA as DnsAAAARecord).Address)) { continue; } break; } AddGlueRecordTo(answer, additional); } } } break; case DnsResourceRecordType.MX: if (response.Authority.Count == 0) { //add glue from additional section string mxExchange = (answer.RDATA as DnsMXRecord).Exchange; foreach (DnsResourceRecord additional in response.Additional) { if (mxExchange.Equals(additional.Name, StringComparison.OrdinalIgnoreCase)) { AddGlueRecordTo(answer, additional); } } } break; case DnsResourceRecordType.SRV: if (response.Authority.Count == 0) { //add glue from additional section string srvTarget = (answer.RDATA as DnsSRVRecord).Target; foreach (DnsResourceRecord additional in response.Additional) { if (srvTarget.Equals(additional.Name, StringComparison.OrdinalIgnoreCase)) { AddGlueRecordTo(answer, additional); } } } break; } } else if ((answer.Type == DnsResourceRecordType.DNAME) && qName.EndsWith("." + answer.Name, StringComparison.OrdinalIgnoreCase)) { cachableRecords.Add(answer); } } } //get cachable authority records if (response.Authority.Count > 0) { foreach (DnsResourceRecord authority in response.Authority) { authority.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); } DnsResourceRecord firstAuthority = response.Authority[0]; if (firstAuthority.Type == DnsResourceRecordType.SOA) { if (response.Answer.Count == 0) { //empty response with authority foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, (firstAuthority.RDATA as DnsSOARecord).Minimum, new DnsSpecialCacheRecord(response)); record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); InternalCacheRecords(new DnsResourceRecord[] { record }); } } else { //answer response with authority DnsResourceRecord lastAnswer = response.Answer[response.Answer.Count - 1]; if (lastAnswer.Type == DnsResourceRecordType.CNAME) { if ((response.RCODE != DnsResponseCode.NxDomain) || (response.Answer.Count == 1)) { //negative cache only when RCODE is not NXDOMAIN or when RCODE is NXDOMAIN and there is only 1 CNAME in answer foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord((lastAnswer.RDATA as DnsCNAMERecord).Domain, question.Type, question.Class, (firstAuthority.RDATA as DnsSOARecord).Minimum, new DnsSpecialCacheRecord(response)); record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); InternalCacheRecords(new DnsResourceRecord[] { record }); } } } } } else if (firstAuthority.Type == DnsResourceRecordType.NS) { if (response.Answer.Count == 0) { //response is probably referral response bool isReferralResponse = true; foreach (DnsQuestionRecord question in response.Question) { foreach (DnsResourceRecord authority in response.Authority) { if ((authority.Type == DnsResourceRecordType.NS) && (authority.RDATA as DnsNSRecord).NameServer.Equals(response.Metadata.NameServerAddress.Host, StringComparison.OrdinalIgnoreCase)) { //empty response from authority name server that was queried DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsSpecialCacheRecord(response)); record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); InternalCacheRecords(new DnsResourceRecord[] { record }); isReferralResponse = false; break; } } } if (isReferralResponse) { //cache and glue suitable NS records foreach (DnsResourceRecord authority in response.Authority) { if (authority.Type != DnsResourceRecordType.NS) { continue; } cachableRecords.Add(authority); //add glue from additional section string nsDomain = (authority.RDATA as DnsNSRecord).NameServer; foreach (DnsResourceRecord additional in response.Additional) { if (nsDomain.Equals(additional.Name, StringComparison.OrdinalIgnoreCase)) { switch (additional.Type) { case DnsResourceRecordType.A: if (IPAddress.IsLoopback((additional.RDATA as DnsARecord).Address)) { continue; } break; case DnsResourceRecordType.AAAA: if (IPAddress.IsLoopback((additional.RDATA as DnsAAAARecord).Address)) { continue; } break; } AddGlueRecordTo(authority, additional); } } } } } } } else { //no authority records if (response.Answer.Count == 0) { //empty response with no authority foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, question.Class, _negativeRecordTtl, new DnsSpecialCacheRecord(response)); record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); InternalCacheRecords(new DnsResourceRecord[] { record }); } } } if (cachableRecords.Count < 1) { return; //nothing to cache } //set expiry for cached records foreach (DnsResourceRecord record in cachableRecords) { record.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); foreach (DnsResourceRecord glueRecord in GetGlueRecordsFrom(record)) { glueRecord.SetExpiry(_minimumRecordTtl, _maximumRecordTtl, _serveStaleTtl); } } InternalCacheRecords(cachableRecords); }
public void CacheResponse(DnsDatagram response) { if (!response.Header.IsResponse) { return; } //combine all records in the response List <DnsResourceRecord> allRecords = new List <DnsResourceRecord>(); switch (response.Header.RCODE) { case DnsResponseCode.NameError: if (response.Authority.Length > 0) { DnsResourceRecord authority = response.Authority[0]; if (authority.Type == DnsResourceRecordType.SOA) { authority.SetExpiry(MINIMUM_RECORD_TTL, 0u); foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsNXRecord(authority)); record.SetExpiry(MINIMUM_RECORD_TTL, 0u); CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record }); } } } break; case DnsResponseCode.NoError: if (response.Answer.Length > 0) { foreach (DnsQuestionRecord question in response.Question) { string qName = question.Name; foreach (DnsResourceRecord answer in response.Answer) { if (answer.Name.Equals(qName, StringComparison.CurrentCultureIgnoreCase)) { allRecords.Add(answer); switch (answer.Type) { case DnsResourceRecordType.CNAME: qName = (answer.RDATA as DnsCNAMERecord).CNAMEDomainName; break; case DnsResourceRecordType.NS: string nsDomain = (answer.RDATA as DnsNSRecord).NSDomainName; if (!nsDomain.EndsWith(".root-servers.net", StringComparison.CurrentCultureIgnoreCase)) { foreach (DnsResourceRecord record in response.Additional) { if (nsDomain.Equals(record.Name, StringComparison.CurrentCultureIgnoreCase)) { allRecords.Add(record); } } } break; case DnsResourceRecordType.MX: string mxExchange = (answer.RDATA as DnsMXRecord).Exchange; foreach (DnsResourceRecord record in response.Additional) { if (mxExchange.Equals(record.Name, StringComparison.CurrentCultureIgnoreCase)) { allRecords.Add(record); } } break; } } } } } else if (response.Authority.Length > 0) { DnsResourceRecord authority = response.Authority[0]; if (authority.Type == DnsResourceRecordType.SOA) { authority.SetExpiry(MINIMUM_RECORD_TTL, 0u); //empty response with authority foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(authority)); record.SetExpiry(MINIMUM_RECORD_TTL, 0u); CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record }); } } else { foreach (DnsQuestionRecord question in response.Question) { foreach (DnsResourceRecord authorityRecord in response.Authority) { if ((authorityRecord.Type == DnsResourceRecordType.NS) && question.Name.Equals(authorityRecord.Name, StringComparison.CurrentCultureIgnoreCase) && (authorityRecord.RDATA as DnsNSRecord).NSDomainName.Equals(response.Metadata.NameServerAddress.Host, StringComparison.CurrentCultureIgnoreCase)) { //empty response from authority name server DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(null)); record.SetExpiry(MINIMUM_RECORD_TTL, 0u); CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record }); break; } } } } } else { //empty response with no authority foreach (DnsQuestionRecord question in response.Question) { DnsResourceRecord record = new DnsResourceRecord(question.Name, question.Type, DnsClass.IN, DEFAULT_RECORD_TTL, new DnsEmptyRecord(null)); record.SetExpiry(MINIMUM_RECORD_TTL, 0u); CacheEntry(question.Name, question.Type, new DnsResourceRecord[] { record }); } } break; default: return; //nothing to do } if ((response.Question.Length > 0) && (response.Question[0].Type != DnsResourceRecordType.NS)) { foreach (DnsQuestionRecord question in response.Question) { foreach (DnsResourceRecord authority in response.Authority) { if (question.Name.Equals(authority.Name, StringComparison.CurrentCultureIgnoreCase) || question.Name.EndsWith("." + authority.Name, StringComparison.CurrentCultureIgnoreCase)) { allRecords.Add(authority); if (authority.Type == DnsResourceRecordType.NS) { string nsDomain = (authority.RDATA as DnsNSRecord).NSDomainName; if (!nsDomain.EndsWith(".root-servers.net", StringComparison.CurrentCultureIgnoreCase)) { foreach (DnsResourceRecord record in response.Additional) { if (nsDomain.Equals(record.Name, StringComparison.CurrentCultureIgnoreCase)) { allRecords.Add(record); } } } } } } } } //set expiry for cached records foreach (DnsResourceRecord record in allRecords) { record.SetExpiry(MINIMUM_RECORD_TTL, 0u); } #region group all records by domain and type Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntries = new Dictionary <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > >(); foreach (DnsResourceRecord record in allRecords) { Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntries; if (cacheEntries.ContainsKey(record.Name)) { cacheTypeEntries = cacheEntries[record.Name]; } else { cacheTypeEntries = new Dictionary <DnsResourceRecordType, List <DnsResourceRecord> >(); cacheEntries.Add(record.Name, cacheTypeEntries); } List <DnsResourceRecord> cacheRREntries; if (cacheTypeEntries.ContainsKey(record.Type)) { cacheRREntries = cacheTypeEntries[record.Type]; } else { cacheRREntries = new List <DnsResourceRecord>(); cacheTypeEntries.Add(record.Type, cacheRREntries); } cacheRREntries.Add(record); } #endregion //add grouped entries into cache foreach (KeyValuePair <string, Dictionary <DnsResourceRecordType, List <DnsResourceRecord> > > cacheEntry in cacheEntries) { string domain = cacheEntry.Key; foreach (KeyValuePair <DnsResourceRecordType, List <DnsResourceRecord> > cacheTypeEntry in cacheEntry.Value) { DnsResourceRecordType type = cacheTypeEntry.Key; DnsResourceRecord[] records = cacheTypeEntry.Value.ToArray(); CacheEntry(domain, type, records); } } //cache for ANY request if (response.Question[0].Type == DnsResourceRecordType.ANY) { CacheEntry(response.Question[0].Name, DnsResourceRecordType.ANY, response.Answer); } }
public DnsNXRecord(DnsResourceRecord authority) { _authority = authority; }
public DnsEmptyRecord(DnsResourceRecord authority) { _authority = authority; }
public override DnsDatagram Query(DnsDatagram request) { DnsQuestionRecord question = request.Question[0]; DnsResourceRecord[] answerRecords = GetRecords(question.Name, question.Type); if (answerRecords != null) { if (answerRecords[0].RDATA is DnsEmptyRecord) { DnsResourceRecord[] responseAuthority; DnsResourceRecord authority = (answerRecords[0].RDATA as DnsEmptyRecord).Authority; if (authority == null) { responseAuthority = new DnsResourceRecord[] { } } ; else { responseAuthority = new DnsResourceRecord[] { authority } }; return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NoError, 1, 0, 1, 0), request.Question, new DnsResourceRecord[] { }, responseAuthority, new DnsResourceRecord[] { })); } if (answerRecords[0].RDATA is DnsNXRecord) { DnsResourceRecord[] responseAuthority; DnsResourceRecord authority = (answerRecords[0].RDATA as DnsNXRecord).Authority; if (authority == null) { responseAuthority = new DnsResourceRecord[] { } } ; else { responseAuthority = new DnsResourceRecord[] { authority } }; return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NameError, 1, 0, 1, 0), request.Question, new DnsResourceRecord[] { }, responseAuthority, new DnsResourceRecord[] { })); } if (answerRecords[0].RDATA is DnsANYRecord) { DnsANYRecord anyRR = answerRecords[0].RDATA as DnsANYRecord; return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NoError, 1, (ushort)anyRR.Records.Length, 0, 0), request.Question, anyRR.Records, new DnsResourceRecord[] { }, new DnsResourceRecord[] { })); } if (answerRecords[0].RDATA is DnsFailureRecord) { return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, (answerRecords[0].RDATA as DnsFailureRecord).RCODE, 1, 0, 0, 0), request.Question, new DnsResourceRecord[] { }, new DnsResourceRecord[] { }, new DnsResourceRecord[] { })); } return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NoError, 1, (ushort)answerRecords.Length, 0, 0), request.Question, answerRecords, new DnsResourceRecord[] { }, new DnsResourceRecord[] { })); } string currentZone = question.Name; while (currentZone != null) { DnsResourceRecord[] nameServers = GetClosestNameServers(currentZone); if (nameServers == null) { break; } List <DnsResourceRecord> glueRecords = new List <DnsResourceRecord>(); foreach (DnsResourceRecord nameServer in nameServers) { string nsDomain = (nameServer.RDATA as DnsNSRecord).NSDomainName; DnsResourceRecord[] glueAs = GetRecords(nsDomain, DnsResourceRecordType.A); if ((glueAs != null) && (glueAs.Length > 0) && (glueAs[0].RDATA is DnsARecord)) { glueRecords.AddRange(glueAs); } DnsResourceRecord[] glueAAAAs = GetRecords(nsDomain, DnsResourceRecordType.AAAA); if ((glueAAAAs != null) && (glueAAAAs.Length > 0) && (glueAAAAs[0].RDATA is DnsAAAARecord)) { glueRecords.AddRange(glueAAAAs); } } if (glueRecords.Count > 0) { DnsResourceRecord[] additional = glueRecords.ToArray(); return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.NoError, 1, 0, (ushort)nameServers.Length, (ushort)additional.Length), request.Question, new DnsResourceRecord[] { }, nameServers, additional)); } currentZone = GetParentZone(currentZone); } return(new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, true, false, false, DnsResponseCode.Refused, 1, 0, 0, 0), request.Question, new DnsResourceRecord[] { }, new DnsResourceRecord[] { }, new DnsResourceRecord[] { })); }