//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Dns.cs // // Notes: // Ported from the Coriolis code base by maiken, with extensive changes to use // the channel interface to our NetStack instead of native calls. // namespace System.Net { using System.Collections; using System.Text; using System.Net.Sockets; using System.Threading; using System.Net.IP; using NetStack.Contracts; using NetStack.Channels.Public; using Microsoft.SingSharp; using Microsoft.Singularity; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; public sealed class Dns { // This is the slot we use to store the per-thread channel to the NetStack private static LocalDataStoreSlot DnsSlot = null; private static LocalDataStoreSlot IPSlot = null; private static object DnsSlotLock = new object(); // Used for initialization private static object IPSlotLock = new object(); // ditto // // Constructor is private to prevent instantiation // private Dns() { // Shouldn't ever be called } // All threads share a LocalDataStoreSlot object that identifies a thread-local // data "slot" that each thread can use. This object must be created in a thread- // safe way the first time it becomes necessary. See Thread.AllocateDataSlot(). internal static LocalDataStoreSlot SafeSlotFetchOrInitialize(ref LocalDataStoreSlot slot, object slotLock) { // This initialization sequence is meant to be thread-safe if (slot == null) { lock (slotLock) { if (slot == null) { slot = Thread.AllocateDataSlot(); } } } return slot; } // Must be balanced with a call to ReleaseDnsConnection()! internal static DNSContract.Imp! GetDnsConnection() { LocalDataStoreSlot slot = SafeSlotFetchOrInitialize(ref DnsSlot, DnsSlotLock); if (Thread.GetData(slot) == null) { // We haven't created a channel for this thread yet. Create one. DNSContract.Imp! dnsImp; DNSContract.Exp! dnsExp; DirectoryServiceContract.Imp epNS = DirectoryService.NewClientEndpoint(); DNSContract.NewChannel(out dnsImp, out dnsExp); try { epNS.SendBind(Bitter.FromString2(DNSContract.ModuleName), dnsExp); switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : if (rejectedEP != null) { delete rejectedEP; } delete dnsImp; // Do nothing; we will return null below. break; case epNS.AckBind() : // Success; put our remaining end of the channel // into thread-local storage. dnsImp.RecvReady(); TRef dnsConnHolder = new TRef(dnsImp); Thread.SetData(slot, dnsConnHolder); break; } } finally { delete epNS; } } // By now there should definitely be a channel in our thread-local storage. TRef! connHolder = (TRef!)Thread.GetData(slot); return connHolder.Acquire(); } internal static void ReleaseDnsConnection([Claims] DNSContract.Imp:ReadyState! connection) { // Put the channel back into thread-local storage. TRef! dnsConnHolder = (TRef!)Thread.GetData(DnsSlot); dnsConnHolder.Release(connection); } // Must be balanced with a call to ReleaseIPConnection()! internal static IPContract.Imp! GetIPConnection() { LocalDataStoreSlot slot = SafeSlotFetchOrInitialize(ref IPSlot, IPSlotLock); if (Thread.GetData(slot) == null) { IPContract.Imp! ipImp; IPContract.Exp! ipExp; DirectoryServiceContract.Imp epNS = DirectoryService.NewClientEndpoint(); IPContract.NewChannel(out ipImp, out ipExp); try { epNS.SendBind(Bitter.FromString2(IPContract.ModuleName), ipExp); switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : // Do nothing; we will return null below. delete ipImp; if (rejectedEP != null) { delete rejectedEP; } break; case epNS.AckBind() : // Success; put our remaining end of the channel // into thread-local storage. ipImp.RecvReady(); TRef ipConnHolder = new TRef(ipImp); Thread.SetData(slot, ipConnHolder); break; } } finally { delete epNS; } } TRef! connHolder = (TRef!)Thread.GetData(slot); return connHolder.Acquire(); } internal static void ReleaseIPConnection([Claims] IPContract.Imp:ReadyState! connection) { // Put the channel back into thread-local storage. TRef! ipConnHolder = (TRef!)Thread.GetData(IPSlot); ipConnHolder.Release(connection); } public static IPHostEntry GetHostByName(string hostName) { if (hostName == null) { throw new ArgumentNullException("hostName"); } return InternalGetHostByName(hostName); } internal static IPHostEntry InternalGetHostByName(string hostName) { return InternalResolve(hostName); } // GetHostByName public static IPAddress[] GetLocalHostAddresses() { IPAddress[] retval; IPContract.Imp ipConn = GetIPConnection(); try { char[][]! in ExHeap ifList; ArrayList addresses = new ArrayList(); ipConn.SendGetInterfaces(); ipConn.RecvInterfaceList(out ifList); try { for (int i = 0; i < ifList.Length; ++i) { expose(ifList[i]) { ipConn.SendGetInterfaceState((!)ifList[i]); ifList[i] = null; switch receive { case ipConn.InterfaceNotFound() : throw new SocketException(SocketErrors.SocketError); break; case ipConn.InterfaceState(InterfaceInfo ifInfo) : { // Copy out all of this interface's addresses InterfaceIPInfo[] in ExHeap ipConfigs = ifInfo.ipConfigs; if (ipConfigs != null) { for (int j = 0; j < ipConfigs.Length; ++j) { addresses.Add(new IPAddress(ipConfigs[j].address)); } } ifInfo.Dispose(); } break; } } } } finally { delete ifList; // This should hold only NULLs by now } return (IPAddress[])addresses.ToArray(typeof(IPAddress)); } finally { ReleaseIPConnection(ipConn); } } // GetLocalHostAddresses public static IPHostEntry GetHostByAddress(string address) { if (address == null) { throw new ArgumentNullException("address"); } IPHostEntry ipHostEntry = InternalGetHostByAddress(IPAddress.Parse(address)); return ipHostEntry; } // GetHostByAddress public static IPHostEntry GetHostByAddress(IPAddress address) { if (address == null) { throw new ArgumentNullException("address"); } IPHostEntry ipHostEntry = InternalGetHostByAddress(address); return ipHostEntry; } // GetHostByAddress internal static IPHostEntry InternalGetHostByAddress(IPAddress! address) { // We only support IPv4 addresses under Singularity! if ( address.AddressFamily != AddressFamily.InterNetwork ) { // // Protocol not supported // throw new SocketException(SocketErrors.WSAEPROTONOSUPPORT); } // Turn the address into a string and resolve it return InternalResolve(address.ToString()); return null; } // InternalGetHostByAddress // host can be either a host's textual name, or the string of an address internal static IPHostEntry! InternalResolve(string host) { DNSContract.Imp dnsConn = GetDnsConnection(); try { string[] aliases; IPv4[] addrs; if (DNSImpConnection.Resolve(dnsConn, host, out aliases, out addrs)) { assert aliases != null; assert addrs != null; return DNSInfoToIPHostEntry(aliases, addrs); } else { throw new SocketException(SocketErrors.WSAHOST_NOT_FOUND); } } finally { ReleaseDnsConnection(dnsConn); } } // InternalResolve internal static IPHostEntry! DNSInfoToIPHostEntry(string[]! aliases, IPv4[]! addrs) { IPHostEntry retval = new IPHostEntry(); IPAddress[] addressList = new IPAddress[addrs.Length]; for (int i = 0; i < addrs.Length ; ++i) { addressList[i] = new IPAddress(addrs[i]); } string[] resultAliases = new string[aliases.Length - 1]; for (int i = 1; i < aliases.Length; ++i) { resultAliases[i] = aliases[i]; } retval.Aliases = resultAliases; retval.AddressList = addressList; retval.HostName = aliases[0]; return retval; } public static string GetHostName() { IPContract.Imp ipConn = GetIPConnection(); try { char[]! in ExHeap hostName; ipConn.SendGetHostName(); ipConn.RecvHostName(out hostName); string strHostName = Bitter.ToString(hostName); delete hostName; return strHostName; } finally { ReleaseIPConnection(ipConn); } } public static IPAddress[] ResolveToAddresses(string hostName) { IPHostEntry entry = Resolve(hostName); return entry.AddressList; } // // Note that in the case of multiple IP addresses, the address returned is // chosen arbitrarily. // public static IPHostEntry! Resolve(string hostName) { if (hostName == null) { throw new ArgumentNullException("hostName"); } return InternalResolve(hostName); } } }