| 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; } } } } }