Пример #1
0
        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);
                    }
                }
            }
        }
Пример #2
0
        private void CacheEntry(string domain, DnsResourceRecordType type, DnsResourceRecord[] records)
        {
            domain = domain.ToLower();

            DnsCacheEntry entry = _cache.GetOrAdd(domain, delegate(string key)
            {
                return(new DnsCacheEntry());
            });

            entry.Add(type, records);
        }
Пример #3
0
        /// <inheritdoc/>
        public async Task <DnsAnswer> Query(DnsHeader query, CancellationToken token)
        {
            string cacheKey = GetCacheKey(query);

            DnsAnswer answer;

            var cacheEntry = (DnsCacheEntry)_objectCache.Get(cacheKey);

            if (cacheEntry != null)
            {
                _logger.LogTrace("Returned cached DNS result for {Domain} (expires in: {ExpiryTime})", query.Host, cacheEntry.Expires);

                answer = DnsByteExtensions.FromBytes <DnsAnswer>(cacheEntry.Data);

                // Replace the ID
                answer.Header.Id = query.Id;

                // Adjust the TTLs to be correct
                foreach (var record in answer.Answers)
                {
                    record.TimeToLive -= cacheEntry.Age;
                }

                return(answer);
            }

            answer = await _dnsClient.Query(new DnsHeader
            {
                Id              = query.Id,
                Host            = query.Host,
                IsQueryResponse = false,
                RecusionDesired = query.RecusionDesired,
                QueryClass      = query.QueryClass,
                QueryType       = query.QueryType,
                QuestionCount   = query.QuestionCount
            }, token);

            _logger.LogTrace("Returned fresh DNS result for {Domain}", query.Host);

            if (answer.Answers.Count > 0)
            {
                cacheEntry = new DnsCacheEntry(answer);
                _objectCache.Add(cacheKey, cacheEntry, new CacheItemPolicy {
                    AbsoluteExpiration = cacheEntry.Expiry
                });
            }

            return(answer);
        }
Пример #4
0
        private List <DnsCacheEntry> ResolveRedir(DnsCacheEntry CurEntry, int Level = 0)
        {
            List <DnsCacheEntry>          EntryList = new List <DnsCacheEntry>();
            CloneableList <DnsCacheEntry> NameEntries;

            if (Level >= 4 || !cacheByStr.TryGetValue(CurEntry.HostName, out NameEntries)) // dont get trapped in an endles recursion
            {
                EntryList.Add(CurEntry);
            }
            else
            {
                foreach (DnsCacheEntry NameEntry in NameEntries)
                {
                    EntryList.AddRange(ResolveRedir(NameEntry, Level + 1));
                }
            }
            return(EntryList);
        }
Пример #5
0
        public void CleanupCache()
        {
            DateTime ExpirationLimit = DateTime.Now.AddMinutes(App.GetConfigInt("DnsInspector", "CacheRetention", 15));

            foreach (var Name in dnsCache.Keys.ToList())
            {
                CloneableList <DnsCacheEntry> curEntries = dnsCache[Name];
                for (int i = 0; i < curEntries.Count; i++)
                {
                    DnsCacheEntry curEntry = curEntries[i];
                    if (curEntry.ExpirationTime < ExpirationLimit)
                    {
                        if (curEntry.RecordType == DnsApi.DnsRecordType.A || curEntry.RecordType == DnsApi.DnsRecordType.AAAA)
                        {
                            if (curEntry.Address != null)
                            {
                                cacheByIP.Remove(curEntry.Address, curEntry);
                            }
                        }
                        else // CNAME, SRV, MX, DNAME
                        {
                            if (curEntry.ResolvedString != null)
                            {
                                cacheByStr.Remove(curEntry.ResolvedString, curEntry);
                            }
                        }

                        curEntries.RemoveAt(i--);
                    }
                }
                if (curEntries.Count == 0)
                {
                    dnsCache.Remove(Name);
                }
            }
        }
Пример #6
0
        private void AddCacheEntry(CloneableList <DnsCacheEntry> curEntries, DnsCacheEntry curEntry)
        {
            curEntries.Add(curEntry);

            if ((curEntry.RecordType == DnsApi.DnsRecordType.A || curEntry.RecordType == DnsApi.DnsRecordType.AAAA) && curEntry.Address != null)
            {
                if (curEntry.Address != null)
                {
                    cacheByIP.Add(curEntry.Address, curEntry);
                }
            }
            else if (curEntry.ResolvedString != null) // CNAME, SRV, MX, DNAME
            {
                if (curEntry.ResolvedString != null)
                {
                    cacheByStr.Add(curEntry.ResolvedString, curEntry);
                }
            }

            DnsCacheEvent?.Invoke(this, new DnsEvent()
            {
                Entry = curEntry
            });
        }
        public UInt32[] ResolveHostNameToIpAddresses(string name, out string canonicalName, Int64 timeoutInMachineTicks)
        {
            UInt32[] dnsServerAddresses = _ipv4Layer.DnsServerAddresses;
            if (dnsServerAddresses.Length < 1)
            {
                /* could not resolve DNS entry */
                throw Utility.NewSocketException(SocketError.NoRecovery); /* no DNS server is configured */
            }

            /* NOTE: this DNS resolver assumes that all names are Internet domains. */
            // ensure that the domain is rooted.
            if (name.Substring(name.Length - 1) != ".")
            {
                name += ".";
            }

            /* first, return a response from our dns cache if a valid response is already cached */
            lock (_dnsCacheLock)
            {
                DnsCacheEntry dnsEntry = (DnsCacheEntry)_dnsCache[name.ToLower()];

                // if we retrieved an entry, make sure it has not timed out.
                if (dnsEntry != null)
                {
                    Int64 nowTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
                    if (dnsEntry.ExpirationTicks < nowTicks)
                    {
                        // if the entry has timed out, dispose of it; we will re-query the target
                        _dnsCache.Remove(name.ToLower());
                        dnsEntry = null;
                    }
                    else
                    {
                        dnsEntry.LastUsedTicks = nowTicks; // update "last used" timestamp
                        canonicalName          = dnsEntry.CanonicalName;
                        return(dnsEntry.IpAddresses);
                    }
                }
            }

            // by default, set our canonicalName to the passed-in name
            canonicalName = name;

            Int64 expirationInMachineTicks = Int64.MaxValue;

            DnsResponse response = null;

            for (int iDnsServer = 0; iDnsServer < dnsServerAddresses.Length; iDnsServer++)
            {
                // set our query timeout to the maximum of DNS_QUERY_TIMEOUT_MS and the hard timeout passed into our function.
                Int64 queryTimeoutInMachineTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (DNS_QUERY_TIMEOUT_MS * TimeSpan.TicksPerMillisecond);
                if (queryTimeoutInMachineTicks > timeoutInMachineTicks)
                {
                    queryTimeoutInMachineTicks = timeoutInMachineTicks;
                }

                // query this DNS server and wait for a response (until a maximum time of queryTimeoutInMachineTicks)
                bool success = SendDnsQueryAndWaitForResponse(dnsServerAddresses[iDnsServer], DnsRecordType.A, name, out response, queryTimeoutInMachineTicks);
                if (success)
                {
                    break;
                }
            }

            if (response == null)
            {
                throw Utility.NewSocketException(SocketError.TryAgain); /* no response received from any dns server */
            }

            switch (response.ResponseCode)
            {
            case DnsResponseCode.NoError:
                break;     /* success */

            case DnsResponseCode.NXDomain:
                throw Utility.NewSocketException(SocketError.HostNotFound);

            case DnsResponseCode.Refused:
                throw Utility.NewSocketException(SocketError.NoRecovery);

            case DnsResponseCode.ServFail:
                throw Utility.NewSocketException(SocketError.TryAgain);

            default:
                break;     /* in theory, some errors could actually have valid data, so ignore other errors for now. */
            }

            System.Collections.ArrayList ipAddressList = new System.Collections.ArrayList();
            for (int iRecord = 0; iRecord < response.AnswerRecords.Length; iRecord++)
            {
                if (response.AnswerRecords[iRecord].RecordType == DnsRecordType.A)
                {
                    canonicalName = response.AnswerRecords[iRecord].Name;
                    Int64 currentTimeoutTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (response.AnswerRecords[iRecord].TimeToLive * TimeSpan.TicksPerSecond);
                    if (currentTimeoutTicks < expirationInMachineTicks)
                    {
                        expirationInMachineTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (response.AnswerRecords[iRecord].TimeToLive * TimeSpan.TicksPerSecond);
                    }
                    ipAddressList.Add(
                        ((UInt32)response.AnswerRecords[iRecord].Data[0] << 24) +
                        ((UInt32)response.AnswerRecords[iRecord].Data[1] << 16) +
                        ((UInt32)response.AnswerRecords[iRecord].Data[2] << 8) +
                        ((UInt32)response.AnswerRecords[iRecord].Data[3])
                        );
                }
            }

            if (ipAddressList.Count > 0)
            {
                // add the host to our DNS cache
                lock (_dnsCacheLock)
                {
                    // first make sure that our cache table is not full; if it is full then remove the oldest entry (based on LastAccessedInMachineTicks)
                    if (_dnsCache.Count >= DNS_CACHE_MAXIMUM_ENTRIES)
                    {
                        Int64  oldestLastUsedTicks = Int64.MaxValue;
                        string oldestKey           = string.Empty;
                        foreach (string key in _dnsCache.Keys)
                        {
                            if (((DnsCacheEntry)_dnsCache[key]).LastUsedTicks < oldestLastUsedTicks)
                            {
                                oldestKey           = key;
                                oldestLastUsedTicks = ((DnsCacheEntry)_dnsCache[key]).LastUsedTicks;
                            }
                        }
                        _dnsCache.Remove(oldestKey);
                    }

                    DnsCacheEntry dnsEntry = new DnsCacheEntry(name, canonicalName, (UInt32[])ipAddressList.ToArray(typeof(UInt32)), expirationInMachineTicks, Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks);
                    _dnsCache.Add(name.ToLower(), dnsEntry);
                }
            }

            if (ipAddressList.Count == 0)
            {
                throw Utility.NewSocketException(SocketError.NoData);
            }

            UInt32[] addresses = (UInt32[])ipAddressList.ToArray(typeof(UInt32));
            return(addresses);
        }
Пример #8
0
        public void SyncCache()
        {
            var dnsCacheDataTable = IntPtr.Zero;

            if (DnsGetCacheDataTable_I == null || !DnsGetCacheDataTable_I(out dnsCacheDataTable))
            {
                return;
            }

            DateTime CurrentTime = DateTime.Now;

            // cache domains
            //var oldCache = new HashSet<string>(dnsCache.Keys);

            foreach (DnsCacheEntry cacheEntry in dnsCache.GetAllValues())
            {
                if (cacheEntry.ExpirationTime > CurrentTime)
                {
                    cacheEntry.ExpirationTime = CurrentTime;
                }
                // will be reset in the loop, efectivly timeouting all flushed entries imminetly
            }

            for (var tablePtr = dnsCacheDataTable; tablePtr != IntPtr.Zero;)
            {
                var entry = (DnsApi.DnsCacheEntry)Marshal.PtrToStructure(tablePtr, typeof(DnsApi.DnsCacheEntry));
                tablePtr = entry.Next;

                // Note: DnsGetCacheDataTable_I should only return one result per domain name in cache mo mater how many entries there are
                //          DnsQuery wil retrive all entries of any type for a given domain name thanks to DNS_QUERY_UN_DOCUMENTED

                var  resultPtr = IntPtr.Zero;
                uint ret       = DnsApi.DnsQuery(entry.Name, entry.Type, DnsApi.DnsQueryType.DNS_QUERY_NO_WIRE_QUERY | DnsApi.DnsQueryType.DNS_QUERY_UN_DOCUMENTED, IntPtr.Zero, ref resultPtr, IntPtr.Zero);
                if (ret != DnsApi.ERROR_SUCCESS)
                {
                    continue;
                }

                //AppLog.Debug("DnsEntries: " + entry.Name);

                // get all entries for thisdomain name

                /*CloneableList<DnsCacheEntry> curEntries = null;
                 * if (!dnsCache.TryGetValue(entry.Name, out curEntries))
                 * {
                 *  curEntries = new CloneableList<DnsCacheEntry>();
                 *  dnsCache.Add(entry.Name, curEntries);
                 * }
                 * else
                 *  oldCache.Remove(entry.Name);*/

                for (var recordIndexPtr = resultPtr; recordIndexPtr != IntPtr.Zero;)
                {
                    var    record = (DnsApi.DnsRecord)Marshal.PtrToStructure(recordIndexPtr, typeof(DnsApi.DnsRecord));
                    int    offset = Marshal.OffsetOf(typeof(DnsApi.DnsRecord), "Data").ToInt32();
                    IntPtr data   = recordIndexPtr + offset;
                    recordIndexPtr = record.Next;

                    string    HostName       = record.Name;
                    IPAddress Address        = null;
                    string    ResolvedString = null;

                    CloneableList <DnsCacheEntry> curEntries = GetEntriesFor(HostName);

                    DnsCacheEntry curEntry = null;

                    if (record.Type == DnsApi.DnsRecordType.A || record.Type == DnsApi.DnsRecordType.AAAA)
                    {
                        switch (record.Type)
                        {
                        case DnsApi.DnsRecordType.A:
                        {
                            var ptr = (DnsApi.DnsARecord)Marshal.PtrToStructure(data, typeof(DnsApi.DnsARecord));
                            Address = new IPAddress((UInt32)ptr.IpAddress);
                            break;
                        }

                        case DnsApi.DnsRecordType.AAAA:
                        {
                            var ptr = (DnsApi.DnsAAAARecord)Marshal.PtrToStructure(data, typeof(DnsApi.DnsAAAARecord));
                            Address = new IPAddress(ptr.IpAddress);
                            break;
                        }
                        }

                        if (Address.Equals(IPAddress.Any) || Address.Equals(IPAddress.IPv6Any)) // thats wht we get from a pi hole dns proxy if the domain is blocked
                        {
                            Address = null;
                        }

                        curEntry = curEntries.FirstOrDefault(e => { return(e.RecordType == record.Type && MiscFunc.IsEqual(e.Address, Address)); });
                    }
                    else // CNAME, SRV, MX, DNAME
                    {
                        switch (record.Type)
                        {
                        //case DnsApi.DnsRecordType.PTR:
                        //    Address = RevDnsHost2Address(HostName);
                        //    goto case DnsApi.DnsRecordType.CNAME;
                        //case DnsApi.DnsRecordType.DNAME: // entire zone
                        case DnsApi.DnsRecordType.CNAME:     // one host
                        {
                            var ptr = (DnsApi.DnsPTRRecord)Marshal.PtrToStructure(data, typeof(DnsApi.DnsPTRRecord));
                            ResolvedString = ptr.NameHost;
                            break;
                        }

                        /*case DnsApi.DnsRecordType.SRV:
                         * {
                         *  var ptr = (DnsApi.DnsSRVRecord)Marshal.PtrToStructure(data, typeof(DnsApi.DnsSRVRecord));
                         *  ResolvedString = ptr.NameTarget + ":" + ptr.Port;
                         *  break;
                         * }
                         * case DnsApi.DnsRecordType.MX:
                         * {
                         *  var ptr = (DnsApi.DnsMXRecord)Marshal.PtrToStructure(data, typeof(DnsApi.DnsMXRecord));
                         *  ResolvedString = ptr.NameExchange;
                         *  break;
                         * }*/
                        default:
                            continue;
                        }

                        if (ResolvedString.Equals("null.arpa")) // I invented that or the DnsProxyServer so probably no one else uses it
                        {
                            ResolvedString = null;
                        }

                        curEntry = curEntries.FirstOrDefault(e => { return(e.RecordType == record.Type && MiscFunc.IsEqual(e.ResolvedString, ResolvedString)); });
                    }

                    if (curEntry == null)
                    {
                        curEntry = new DnsCacheEntry()
                        {
                            HostName = HostName, RecordType = record.Type
                        };
                        curEntry.ExpirationTime = CurrentTime.AddSeconds(record.Ttl);
                        if (Address == null && ResolvedString == null)
                        {
                            curEntry.State = DnsCacheEntry.States.Blocked;
                        }
                        else
                        {
                            curEntry.State = DnsCacheEntry.States.Resolved;
                        }
                        curEntry.Address        = Address;
                        curEntry.ResolvedString = ResolvedString;

                        AddCacheEntry(curEntries, curEntry);
                    }
                    else // just update
                    {
                        curEntry.ExpirationTime = CurrentTime.AddSeconds(record.Ttl);
                    }
                }

                if (resultPtr != IntPtr.Zero)
                {
                    DnsApi.DnsRecordListFree(resultPtr, DnsApi.DnsFreeType.DnsFreeRecordList);
                }
            }

            /*DateTime ExpirationLimit = CurrentTime.AddMinutes(App.GetConfigInt("DnsInspector", "CacheRetention", 15));
             * // remove old entries
             * foreach (var Name in oldCache)
             * {
             *  CloneableList<DnsCacheEntry> curEntries = dnsCache[Name];
             *  for (int i = 0; i < curEntries.Count; i++)
             *  {
             *      DnsCacheEntry curEntry = curEntries[i];
             *      if (curEntry.ExpirationTime < ExpirationLimit)
             *      {
             *          if (curEntry.RecordType == DnsApi.DnsRecordType.A || curEntry.RecordType == DnsApi.DnsRecordType.AAAA)
             *              cacheByIP.Remove(curEntry.Address, curEntry);
             *          else // CNAME, SRV, MX, DNAME
             *              cacheByStr.Remove(curEntry.ResolvedString, curEntry);
             *
             *          curEntries.RemoveAt(i--);
             *      }
             *  }
             *  if (curEntries.Count == 0)
             *      dnsCache.Remove(Name);
             * }*/

            DnsApi.DnsRecordListFree(dnsCacheDataTable, DnsApi.DnsFreeType.DnsFreeRecordList);
        }
Пример #9
0
        private void OnDnsQueryEvent(Microsoft.O365.Security.ETW.IEventRecord record)
        {
            // WARNING: this function is called from the worker thread

            if (record.Id != 1001)
            {
                return;
            }

            if (record.GetUInt32("Status", 0) != 0)
            {
                return;
            }

            int ProcessId = (int)record.ProcessId;
            int ThreadId  = (int)record.ThreadId;
            var HostName  = record.GetUnicodeString("NodeName", null);
            var Results   = record.GetUnicodeString("Result", null);

            if (ProcessId == ProcFunc.CurID)
            {
                return; // ignore these events as thay are the result of reverse dns querries....
            }

            /*
             * "192.168.163.1" "192.168.163.1;"
             * "localhost" "[::1]:8307;127.0.0.1:8307;" <- wtf is this why is there a port?!
             * "DESKTOP" "fe80::189a:f1c3:3e87:be81%12;192.168.10.12;"
             * "telemetry.malwarebytes.com" "54.149.69.204;54.200.191.52;54.70.191.27;54.149.66.105;54.244.17.248;54.148.98.86;"
             * "web.whatsapp.com" "31.13.84.51;"
             */

            App.engine?.RunInEngineThread(() =>
            {
                // Note: this happens in the engine thread

                DnsQueryEvent?.Invoke(this, new DnsEvent()
                {
                    ProcessId = ProcessId, HostName = HostName
                });

                foreach (string Result in Results.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    IPAddress Address = null;
                    if (!IPAddress.TryParse(Result, out Address) && !IPAddress.TryParse(TextHelpers.Split2(Result, ":", true).Item1, out Address))
                    {
                        continue;
                    }

                    OnHostName(ProcessId, Address, HostName);

                    Dictionary <IPAddress, Dictionary <string, DnsCacheEntry> > dnsCache = dnsQueryCache.GetOrCreate(ProcessId);

                    Dictionary <string, DnsCacheEntry> cacheEntries = dnsCache.GetOrCreate(Address);

                    DnsCacheEntry cacheEntry = cacheEntries.GetOrCreate(HostName);

                    cacheEntry.Counter++;
                }
            });
        }
Пример #10
0
        public UInt32[] ResolveHostNameToIpAddresses(string name, out string canonicalName, Int64 timeoutInMachineTicks)
        {
            UInt32[] dnsServerAddresses = _ipv4Layer.DnsServerAddresses;
            if (dnsServerAddresses.Length < 1)
            {
                /* could not resolve DNS entry */
                throw Utility.NewSocketException(SocketError.NoRecovery); /* no DNS server is configured */
            }

            /* NOTE: this DNS resolver assumes that all names are Internet domains. */
            // ensure that the domain is rooted.
            if (name.Substring(name.Length - 1) != ".")
            {
                name += ".";
            }

            /* first, return a response from our dns cache if a valid response is already cached */
            lock (_dnsCacheLock)
            {
                DnsCacheEntry dnsEntry = (DnsCacheEntry)_dnsCache[name.ToLower()];

                // if we retrieved an entry, make sure it has not timed out.
                if (dnsEntry != null)
                {
                    Int64 nowTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
                    if (dnsEntry.ExpirationTicks < nowTicks)
                    {
                        // if the entry has timed out, dispose of it; we will re-query the target
                        _dnsCache.Remove(name.ToLower());
                        dnsEntry = null;
                    }
                    else
                    {
                        dnsEntry.LastUsedTicks = nowTicks; // update "last used" timestamp
                        canonicalName = dnsEntry.CanonicalName;
                        return dnsEntry.IpAddresses;
                    }
                }
            }

            // by default, set our canonicalName to the passed-in name
            canonicalName = name;

            Int64 expirationInMachineTicks = Int64.MaxValue;

            double millisecondsWaitTimeBetweenDnsQueries = (4000.0 / Math.Max((double)(dnsServerAddresses.Length - 1), 1.0));
            DnsResponse response = null;
            for (int iDnsServer = 0; iDnsServer < dnsServerAddresses.Length; iDnsServer++)
            {
                response = SendDnsQueryAndWaitForResponse(dnsServerAddresses[iDnsServer], DnsRecordType.A, name, timeoutInMachineTicks);

                if (response != null)
                    break;

                Int32 waitTimeInMilliseconds = (Int32)System.Math.Min(((timeoutInMachineTicks - Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks) / TimeSpan.TicksPerMillisecond), millisecondsWaitTimeBetweenDnsQueries);
                if (waitTimeInMilliseconds > 0)
                    Thread.Sleep(waitTimeInMilliseconds);
            }

            if (response == null)
            {
                throw Utility.NewSocketException(SocketError.TryAgain); /* no response received from any dns server */
            }

            switch (response.ResponseCode)
            {
                case DnsResponseCode.NoError:
                    break; /* success */
                case DnsResponseCode.NXDomain:
                    throw Utility.NewSocketException(SocketError.HostNotFound);
                case DnsResponseCode.Refused:
                    throw Utility.NewSocketException(SocketError.NoRecovery);
                case DnsResponseCode.ServFail:
                    throw Utility.NewSocketException(SocketError.TryAgain);
                default:
                    break; /* in theory, some errors could actually have valid data, so ignore other errors for now. */
            }

            System.Collections.ArrayList ipAddressList = new System.Collections.ArrayList();
            for (int iRecord = 0; iRecord < response.AnswerRecords.Length; iRecord++)
            {
                if (response.AnswerRecords[iRecord].RecordType == DnsRecordType.A)
                {
                    canonicalName = response.AnswerRecords[iRecord].Name;
                    Int64 currentTimeoutTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (response.AnswerRecords[iRecord].TimeToLive * TimeSpan.TicksPerSecond);
                    if (currentTimeoutTicks < expirationInMachineTicks)
                    {
                        expirationInMachineTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (response.AnswerRecords[iRecord].TimeToLive * TimeSpan.TicksPerSecond);
                    }
                    ipAddressList.Add(
                    ((UInt32)response.AnswerRecords[iRecord].Data[0] << 24) +
                    ((UInt32)response.AnswerRecords[iRecord].Data[1] << 16) +
                    ((UInt32)response.AnswerRecords[iRecord].Data[2] << 8) +
                    ((UInt32)response.AnswerRecords[iRecord].Data[3])
                    );
                }
            }

            if (ipAddressList.Count > 0)
            {
                // add the host to our DNS cache
                lock (_dnsCacheLock)
                {
                    // first make sure that our cache table is not full; if it is full then remove the oldest entry (based on LastAccessedInMachineTicks)
                    if (_dnsCache.Count >= DNS_CACHE_MAXIMUM_ENTRIES)
                    {
                        Int64 oldestLastUsedTicks = Int64.MaxValue;
                        string oldestKey = string.Empty;
                        foreach (string key in _dnsCache.Keys)
                        {
                            if (((DnsCacheEntry)_dnsCache[key]).LastUsedTicks < oldestLastUsedTicks)
                            {
                                oldestKey = key;
                                oldestLastUsedTicks = ((DnsCacheEntry)_dnsCache[key]).LastUsedTicks;
                            }
                        }
                        _dnsCache.Remove(oldestKey);
                    }

                    DnsCacheEntry dnsEntry = new DnsCacheEntry(name, canonicalName, (UInt32[])ipAddressList.ToArray(typeof(UInt32)), expirationInMachineTicks, Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks);
                    _dnsCache.Add(name.ToLower(), dnsEntry);
                }
            }

            if (ipAddressList.Count == 0)
                throw Utility.NewSocketException(SocketError.NoData);

            UInt32[] addresses = (UInt32[])ipAddressList.ToArray(typeof(UInt32));
            return addresses;
        }