Ejemplo n.º 1
0
        /// <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--;
                }
            }
        }
Ejemplo n.º 2
0
 public override string ToString()
 {
     return(String.Format("{0} {1} {2}", QNAME, QCLASS.ToString(), QTYPE.ToString()));
 }