// System.Net.Dns.cs // // Author: Mads Pultz (mpultz@diku.dk) // // (C) Mads Pultz, 2001 using System; using System.Net.Sockets; using System.Text; using System.Collections; using System.Threading; using System.Runtime.InteropServices; using System.Runtime.Remoting.Messaging; namespace System.Net { public sealed class Dns { /// /// Helper class /// private sealed class DnsAsyncResult: IAsyncResult { private object state; private WaitHandle waitHandle; private bool completedSync, completed; private Worker worker; public DnsAsyncResult(object state) { this.state = state; waitHandle = new ManualResetEvent(false); completedSync = completed = false; } public object AsyncState { get { return state; } } public WaitHandle AsyncWaitHandle { set { waitHandle = value; } get { return waitHandle; } } public bool CompletedSynchronously { get { return completedSync; } } public bool IsCompleted { set { completed = value; } get { return completed; } } public Worker Worker { set { worker = value; } get { return worker; } } } /// /// Helper class for asynchronous calls to DNS server /// private sealed class Worker { private AsyncCallback reqCallback; private DnsAsyncResult reqRes; private string req; private IPHostEntry result; public Worker(string req, AsyncCallback reqCallback, DnsAsyncResult reqRes) { this.req = req; this.reqCallback = reqCallback; this.reqRes = reqRes; } private void End() { reqCallback(reqRes); ((ManualResetEvent)reqRes.AsyncWaitHandle).Set(); reqRes.IsCompleted = true; } public void GetHostByName() { lock(reqRes) { result = Dns.GetHostByName(req); End(); } } public void Resolve() { lock(reqRes) { result = Dns.Resolve(req); End(); } } public IPHostEntry Result { get { return result; } } } /// /// This class conforms to the C structure hostent and is used /// by the Dns class when doing native calls. /// [StructLayout(LayoutKind.Sequential)] private unsafe class Hostent { public string h_name; /* official name */ public byte** h_aliases; /* alias list */ public short h_addrtype; /* address type */ public short h_length; /* address length */ public byte** h_addr_list; /* address list */ } public static IAsyncResult BeginGetHostByName(string hostName, AsyncCallback requestCallback, object stateObject) { DnsAsyncResult requestResult = new DnsAsyncResult(stateObject); Worker worker = new Worker(hostName, requestCallback, requestResult); Thread child = new Thread(new ThreadStart(worker.GetHostByName)); child.Start(); return requestResult; } public static IAsyncResult BeginResolve(string hostName, AsyncCallback requestCallback, object stateObject) { // TODO throw new NotImplementedException(); } public static IPHostEntry EndGetHostByName(IAsyncResult asyncResult) { return ((DnsAsyncResult)asyncResult).Worker.Result; } public static IPHostEntry EndResolve(IAsyncResult asyncResult) { // TODO throw new NotImplementedException(); } /// /// IP address in network byte order (e.g. Big-Endian). /// /// /// Length of IP address. /// /// /// Type (should be 2, equals AF_INET). /// [DllImport("cygwin1", EntryPoint="gethostbyaddr")] private static extern IntPtr _GetHostByAddress(byte[] hostName, short length, short type); /// /// IP address in network byte order (e.g. Big-Endian). /// private static IPHostEntry GetHostByAddress(long address) { short length = 4; if (address > uint.MaxValue) length = 8; byte[] addr = new byte[length]; for(int i = length - 1, j = 0; i >= 0; --i, ++j) { byte b = (byte)(address >> i * 8); // Console.WriteLine(b); addr[j] = b; } IntPtr p = _GetHostByAddress(addr, length, 2); // TODO: set type if (p == IntPtr.Zero) throw new SocketException(); // TODO: set error code Hostent h = new Hostent(); System.Runtime.InteropServices.Marshal.PtrToStructure(p, h); return ToIPHostEntry(h); } public static IPHostEntry GetHostByAddress(IPAddress address) { if (address == null) throw new ArgumentNullException(); return GetHostByAddress(IPAddress.HostToNetworkOrder(address.Address)); } public static IPHostEntry GetHostByAddress(string address) { if (address == null) throw new ArgumentNullException(); return GetHostByAddress(CreateAddress(address)); } [DllImport("cygwin1", EntryPoint="gethostbyname")] private static extern IntPtr _GetHostByName(string hostName); public static IPHostEntry GetHostByName(string hostName) { if (hostName == null) throw new ArgumentNullException(); IntPtr p = _GetHostByName(hostName); // int errNo = _h_errno; if (p == IntPtr.Zero) throw new SocketException(); // TODO: set error code Hostent h = new Hostent(); System.Runtime.InteropServices.Marshal.PtrToStructure(p, h); return ToIPHostEntry(h); } /// /// This method returns the host name associated with the local host. /// public static string GetHostName() { IPHostEntry h = GetHostByAddress("127.0.0.1"); return h.HostName; } /// /// IP address in Little-Endian byte order. /// /// /// IP address in dotted notation form. /// public static string IpToString(int address) { address = IPAddress.HostToNetworkOrder(address); StringBuilder res = new StringBuilder(); for(int i = 3; i > 0; --i) { byte b = (byte)(address >> i * 8); res.Append(b); res.Append('.'); } res.Append((byte)address); return res.ToString(); } /// /// This method resovles a DNS-style host name or IP /// address. /// /// /// A string containing either a DNS-style host name (e.g. /// www.go-mono.com) or IP address (e.g. 129.250.184.233). /// public static IPHostEntry Resolve(string hostName) { if (hostName == null) throw new ArgumentNullException(); try { long addr = CreateAddress(hostName); if (addr > uint.MaxValue) throw new FormatException("Only IP version 4 addresses are supported"); return GetHostByAddress(addr); } catch (FormatException) { return GetHostByName(hostName); } } /// /// Utility method. This method converts a Hostent instance to a /// IPHostEntry instance. /// /// /// Object which should be mapped to a IPHostEntry instance. /// private static unsafe IPHostEntry ToIPHostEntry(Hostent h) { IPHostEntry res = new IPHostEntry(); // Set host name res.HostName = h.h_name; // Set IP address list byte** p = h.h_addr_list; ArrayList tmp = new ArrayList(1); while (*p != null) { tmp.Add(CreateIPAddress(*p, h.h_length)); ++p; } IPAddress[] addr_list = new IPAddress[tmp.Count]; for(int i = 0; i < tmp.Count; ++i) addr_list[i] = (IPAddress)tmp[i]; res.AddressList = addr_list; // Set IP aliases p = h.h_aliases; tmp.Clear(); while (*p != null) { tmp.Add(new string((sbyte*)*p)); ++p; } string[] aliases = new string[tmp.Count]; for(int i = 0; i < tmp.Count; ++i) aliases[i] = (string)tmp[i]; res.Aliases = aliases; return res; } /// /// Utility method. Convert IP address in dotted notation /// to IP address. /// private static long CreateAddress(string address) { string[] tokens = address.Split('.'); if (tokens.Length % 4 != 0) throw new FormatException("IP address has invalid length"); long addr = 0; for(int i = 0, j = tokens.Length - 1; i < tokens.Length; ++i, --j) { try { addr = addr | (((long)byte.Parse(tokens[i])) << j * 8); } catch (OverflowException) { throw new FormatException("Invalid IP address format"); } } return addr; } /// /// Utility method. This method creates a IP address. /// /// /// IP address in network byte order (e.g. Big-Endian). /// /// /// Length of IP address (4 or 8 bytes). /// private static unsafe IPAddress CreateIPAddress(byte* addr, short length) { byte* p = addr; long res = 0; for(int i = 0, j = length - 1; i < length; ++i, --j) { res += *p << j * 8; ++p; } if (res > uint.MaxValue) return new IPAddress(IPAddress.NetworkToHostOrder(res)); else return new IPAddress(IPAddress.NetworkToHostOrder((int)res)); } } }