/// <summary> /// Resolves a DNS name. /// </summary> /// <param name="Name">Domain Name to resolve</param> /// <param name="TYPE">Resource Record Type of interest.</param> /// <param name="ExceptionType">If no answer of type <paramref name="TYPE"/> is found, records of this type can also be accepted.</param> /// <param name="CLASS">Resource Record Class of interest.</param> /// <returns>Answer to the query</returns> /// <exception cref="IOException">If the domain name could not be resolved for the TYPE and CLASS provided.</exception> public static async Task <ResourceRecord[]> Resolve(string Name, QTYPE TYPE, TYPE?ExceptionType, QCLASS CLASS) { LinkedList <KeyValuePair <string, IPEndPoint> > Backup = null; TYPE? ExpectedType; IPEndPoint Destination = null; string LastName = null; int Timeout = 2000; // Local timeout if (Enum.TryParse <TYPE>(TYPE.ToString(), out TYPE T)) { ExpectedType = T; } else { ExpectedType = null; } lock (synchObject) { if (nestingDepth == 0 && networkChanged) { networkChanged = false; client?.Dispose(); client = null; } if (client is null) { client = new DnsUdpClient(); } nestingDepth++; } try { while (true) { DnsResponse Response = null; DnsMessage Message; if (LastName is null || LastName != Name) { LastName = Name; try { Response = await Database.FindFirstDeleteRest <DnsResponse>(new FilterAnd( new FilterFieldEqualTo("Name", Name), new FilterFieldEqualTo("Type", TYPE), new FilterFieldEqualTo("Class", CLASS))); if (!(Response is null) && Response.Expires <= DateTime.Now) { await Database.Delete(Response); Response = null; } } catch (Exception) { // Some inconsistency in database. Clear collection to get fresh set of DNS entries. await Database.Clear("DnsCache"); Response = null; } } if (Response is null) { try { Message = await client.SendRequestAsync(OpCode.Query, true, new Question[] { new Question(Name, TYPE, CLASS) }, Destination, Timeout); switch (Message.RCode) { case RCode.NXDomain: throw new ArgumentException("Domain name does not exist.", nameof(Name)); } } catch (TimeoutException) { Message = null; } if (Message is null || Message.RCode != RCode.NoError) { Destination = await NextDestination(Backup); if (Destination is null) { throw new IOException("Unable to resolve DNS query."); } continue; // Check an alternative } uint Ttl = 60 * 60 * 24 * 30; // Maximum TTL = 30 days Response = new DnsResponse() { Name = Name, Type = TYPE, Class = CLASS, Answer = CheckTtl(ref Ttl, Message.Answer), Authority = CheckTtl(ref Ttl, Message.Authority), Additional = CheckTtl(ref Ttl, Message.Additional) }; Response.Expires = DateTime.Now.AddSeconds(Ttl); await Database.Insert(Response); } string CName = null; if (!(Response.Answer is null)) { if (!ExpectedType.HasValue) { return(Response.Answer); } foreach (ResourceRecord RR in Response.Answer) { if (RR.Type == ExpectedType.Value) { return(Response.Answer); } if (CName is null && RR.Type == Enumerations.TYPE.CNAME && RR is CNAME CNAME) { CName = CNAME.Name2; } } if (ExceptionType.HasValue) { foreach (ResourceRecord RR in Response.Answer) { if (RR.Type == ExceptionType.Value) { return(Response.Answer); } } } if (!(CName is null)) { Name = CName; Backup = null; continue; } } if (!(Response.Authority is null)) { foreach (ResourceRecord RR in Response.Authority) { if (RR is NS NS) { string Authority = NS.Name2; IPAddress AuthorityAddress = null; if (!(Response.Additional is null)) { foreach (ResourceRecord RR2 in Response.Additional) { if (RR2 is A A) { AuthorityAddress = A.Address; break; } else if (RR2 is AAAA AAAA) { AuthorityAddress = AAAA.Address; break; } } } if (Backup is null) { Backup = new LinkedList <KeyValuePair <string, IPEndPoint> >(); } if (AuthorityAddress is null) { Backup.AddLast(new KeyValuePair <string, IPEndPoint>(Authority, null)); } else { Backup.AddLast(new KeyValuePair <string, IPEndPoint>(null, new IPEndPoint(AuthorityAddress, DefaultDnsPort))); } } } } Destination = await NextDestination(Backup); if (Destination is null) { throw new IOException("Unable to resolve DNS query."); } Timeout = 5000; } } finally { lock (synchObject) { nestingDepth--; } } }
public override string ToString() { return(String.Format("{0} {1} {2}", QNAME, QCLASS.ToString(), QTYPE.ToString())); }