File: FrameworkFork\System.ServiceModel\System\ServiceModel\Channels\DnsCache.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Net;
using System.Net.Sockets;
using System.Runtime;
using System.Threading.Tasks;
#if FEATURE_NETNATIVE
using System.Collections.Generic;
 
using Windows.Networking;
using Windows.Networking.Connectivity;
using Windows.Networking.Sockets;
using RTSocketError = Windows.Networking.Sockets.SocketError;
#endif
 
namespace System.ServiceModel.Channels
{
    internal static class DnsCache
    {
        private const int mruWatermark = 64;
        private static MruCache<string, DnsCacheEntry> s_resolveCache = new MruCache<string, DnsCacheEntry>(mruWatermark);
        private static readonly TimeSpan s_cacheTimeout = TimeSpan.FromSeconds(2);
 
        // Double-checked locking pattern requires volatile for read/write synchronization
        private static volatile string s_machineName;
 
        private static object ThisLock
        {
            get
            {
                return s_resolveCache;
            }
        }
 
        public static string MachineName
        {
            get
            {
                if (s_machineName == null)
                {
                    lock (ThisLock)
                    {
                        if (s_machineName == null)
                        {
                            try
                            {
#if FEATURE_NETNATIVE
                                var hostNamesList = NetworkInformation.GetHostNames();
                                foreach (var entry in hostNamesList)
                                {
                                    if (entry.Type == HostNameType.DomainName)
                                    {
                                        s_machineName = entry.CanonicalName;
                                        break;
                                    }
                                }
#else
                                s_machineName = Dns.GetHostEntryAsync(String.Empty).GetAwaiter().GetResult().HostName;
#endif
                            }
                            catch (SocketException)
                            {
                                throw;
                            }
                        }
                    }
                }
 
                return s_machineName;
            }
        }
 
        public static async Task<IPAddress[]> ResolveAsync(Uri uri)
        {
            string hostName = uri.DnsSafeHost;
            IPAddress[] hostAddresses = null;
            DateTime now = DateTime.UtcNow;
 
            lock (ThisLock)
            {
                DnsCacheEntry cacheEntry;
                if (s_resolveCache.TryGetValue(hostName, out cacheEntry))
                {
                    if (now.Subtract(cacheEntry.TimeStamp) > s_cacheTimeout)
                    {
                        s_resolveCache.Remove(hostName);
                        cacheEntry = null;
                    }
                    else
                    {
                        if (cacheEntry.AddressList == null)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                new EndpointNotFoundException(string.Format(SRServiceModel.DnsResolveFailed, hostName)));
                        }
                        hostAddresses = cacheEntry.AddressList;
                    }
                }
            }
 
            if (hostAddresses == null)
            {
                SocketException dnsException = null;
                try
                {
                    hostAddresses = await LookupHostName(hostName);
                }
                catch (SocketException e)
                {
                    dnsException = e;
                }
 
                lock (ThisLock)
                {
                    // MruCache doesn't have a this[] operator, so we first remove (just in case it exists already)
                    s_resolveCache.Remove(hostName);
                    s_resolveCache.Add(hostName, new DnsCacheEntry(hostAddresses, now));
                }
 
                if (dnsException != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new EndpointNotFoundException(string.Format(SRServiceModel.DnsResolveFailed, hostName), dnsException));
                }
            }
 
            return hostAddresses;
        }
 
#if FEATURE_NETNATIVE
        internal static async Task<IPAddress[]> LookupHostName(string hostName)
        {
            try
            {
                IReadOnlyList<EndpointPair> data = await DatagramSocket.GetEndpointPairsAsync(new HostName(hostName), "0").AsTask();
                List<IPAddress> addresses = new List<IPAddress>(data.Count);
                if (data != null && data.Count > 0)
                {
                    foreach (EndpointPair item in data)
                    {
                        if (item != null && item.RemoteHostName != null &&
                                      (item.RemoteHostName.Type == HostNameType.Ipv4 || item.RemoteHostName.Type == HostNameType.Ipv6))
                        {
                            IPAddress address;
                            if(IPAddress.TryParse(item.RemoteHostName.CanonicalName, out address))
                            {
                                addresses.Add(address);
                            }
                        }
                    }
                }
                return addresses.ToArray();
            }
            catch (Exception exception)
            {
                if (RTSocketError.GetStatus(exception.HResult) != SocketErrorStatus.Unknown)
                {
                    throw new SocketException(exception.HResult & 0x0000FFFF);
                }
                throw;
            }
        }
#else
        internal static async Task<IPAddress[]> LookupHostName(string hostName)
        {
            return (await Dns.GetHostEntryAsync(hostName)).AddressList;
        }
#endif
 
        internal class DnsCacheEntry
        {
            private DateTime _timeStamp;
            private IPAddress[] _addressList;
 
            public DnsCacheEntry(IPAddress[] addressList, DateTime timeStamp)
            {
                _timeStamp = timeStamp;
                _addressList = addressList;
            }
 
            public IPAddress[] AddressList
            {
                get
                {
                    return _addressList;
                }
            }
 
            public DateTime TimeStamp
            {
                get
                {
                    return _timeStamp;
                }
            }
        }
    }
}