/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: DnsClient.cs // using System; using System.Collections; using System.Diagnostics; using System.Text; using System.Net.IP; using Drivers.Net; using NetStack.Protocols; using Dns = NetStack.Protocols.Dns; using Microsoft.Singularity.Io; namespace NetStack.Runtime { /// /// Dns Client for UDP lookups. /// public class BasicDnsClient { private IPv4 dnsServer; private TimeSpan timeout; private Dns.Flags flags; public const int DefaultTimeoutMillis = 500; public enum StatusCode : int { Success = 0, NoNameServer = 1, TransportError = 2, RequestFailed = 3, Timeout = 4, OverSizeMessage = 5 } public BasicDnsClient() : this(IPv4.Any) { } public BasicDnsClient(IPv4 dnsServer) { this.dnsServer = dnsServer; this.timeout = TimeSpan.FromMilliseconds(DefaultTimeoutMillis); this.flags = Dns.Flags.RD; } public IPv4 DnsServer { get { if (dnsServer != IPv4.Any) return dnsServer; IPModule ip = (IPModule!) Core.Instance().GetProtocolByName("IP"); return ip.HostConfiguration.GetCurrentNameServer(); } } public TimeSpan Timeout { get { return timeout; } set { timeout = value; } } public Dns.Flags Flags { get { return flags; } set { flags = value; } } [ System.Diagnostics.Conditional("DEBUG_DNS_CLIENT") ] private void DebugPrint(string format, params object [] arguments) { Core.Log("DnsClient: "); Core.Log(format, arguments); } [ System.Diagnostics.Conditional("DEBUG_DNS_CLIENT") ] private static void Dump(byte []! data, int offset) { for (int i = offset; i < data.Length; i += 16) { Core.Log("{0:x4} ", i); int n = Math.Min(16, data.Length - i) + i; for (int j = i; j < n; j++) Core.Log("{0:x2} ", data[j]); for (int j = n; j != i + 16; j++) Core.Log(" "); Core.Log(" "); for (int j = i; j < n; j++) { char c = '.'; if (data[j] > 31 && data[j] < 128) c = (char)data[j]; Core.Log("{0}", c); } Core.Log("\n"); } } /// /// Gets the DNS information for the specified DNS host name. /// public StatusCode GetHostByName(string name, out IPv4HostEntry hostEntry) { hostEntry = null; IPv4 dnsServer = DnsServer; if (dnsServer == IPv4.Zero) return StatusCode.NoNameServer; Dns.Query q = new Dns.Query(name, Dns.Type.A, Dns.Class.Internet); Dns.Format answer; StatusCode askResponse = Ask(dnsServer, q, out answer); if (askResponse != StatusCode.Success) return askResponse; assert answer != null; if (answer.GetRCode() != Dns.RCode.NoError) { DebugPrint("Dns query failed: RCode = {0}\n", answer.GetRCode()); return StatusCode.RequestFailed; } hostEntry = new IPv4HostEntry( new string [] { name }, new IPv4[] {} ); ArrayList addressList = new ArrayList(); foreach (Dns.ResourceRecord! rr in answer.AnswerRRs) { if (rr.Type != Dns.Type.A || rr.Class != Dns.Class.Internet) continue; byte [] rdata = rr.RData; if (rdata == null) continue; if ((rdata.Length % IPv4.Length) != 0) continue; int offset = 0; while (offset != rdata.Length) { addressList.Add(IPv4.ParseBytes(rdata, offset)); offset += 4; } } IPv4[] al = hostEntry.AddressList = new IPv4 [addressList.Count]; for (int i = 0; i < addressList.Count; i++) al[i] = (IPv4) (!) addressList[i]; return StatusCode.Success; } /// /// Gets DNS host information for an IP address. /// public StatusCode GetHostByAddress(IPv4 address, out IPv4HostEntry hostEntry) { hostEntry = null; IPv4 dnsServer = DnsServer; if (dnsServer == IPv4.Zero) return StatusCode.NoNameServer; // Formulate request a.b.c.d -> d.b.c.a IPv4 reversed = new IPv4(ByteOrder.Swap((uint)address)); string name = String.Format("{0}.IN-ADDR.ARPA", reversed); DebugPrint("Looking up {0}\n", name); Dns.Query q = new Dns.Query(name, Dns.Type.PTR, Dns.Class.Internet); Dns.Format answer; StatusCode askResponse = Ask(dnsServer, q, out answer); if (askResponse != StatusCode.Success) return askResponse; assert answer != null; if (answer.GetRCode() != Dns.RCode.NoError) { DebugPrint("Dns query failed: RCode = {0}\n", answer.GetRCode()); return StatusCode.RequestFailed; } hostEntry = new IPv4HostEntry(new string[]{}, new IPv4 [] { address }); ArrayList aliases = new ArrayList(); foreach (Dns.ResourceRecord! rr in answer.AnswerRRs) { DebugPrint("Answer: Type = {0} Class = {1} TTL = {2}\n", rr.Type, rr.Class, rr.TtlSeconds); if ((rr.Type != Dns.Type.PTR && rr.Type != Dns.Type.CNAME) || rr.Class != Dns.Class.Internet) continue; byte [] rdata = rr.RData; if (rdata == null) continue; int offset = 0; while (offset != rdata.Length) { string alias; offset += Dns.LabelEncoding.GetString(rdata, offset, out alias); aliases.Add(alias); } } hostEntry.Aliases = (string []!) aliases.ToArray(typeof(string)); return StatusCode.Success; } /// /// Resolves a DNS host name or IP address to an IPv4HostEntry instance. /// public StatusCode Resolve(string hostName, out IPv4HostEntry hostEntry) { IPv4 hostAddress; if (IPv4.Parse(hostName, out hostAddress) == true) { StatusCode status = GetHostByAddress(hostAddress, out hostEntry); if (status != StatusCode.Success) { // As a convenience to the caller, instead of failing // outright, at least give back the parsed version of // the net address. hostEntry = new IPv4HostEntry(new string [] { hostName }, new IPv4 [] { hostAddress }); return StatusCode.Success; } } return GetHostByName(hostName, out hostEntry); } private StatusCode Ask(IPv4 dnsServer, Dns.Query []! queries, out Dns.Format answer) { answer = null; Dns.Format outFrame = new Dns.Format(); outFrame.SetFlags(flags); outFrame.Queries.AddRange(queries); byte[] outData = new byte [outFrame.Size]; if (outData.Length > Dns.Format.MaxUdpMessageLength) { return StatusCode.OverSizeMessage; } int offset = 0; outFrame.Write(outData, ref offset); byte[] rcvData; StatusCode askResponse = AskDnsServer(dnsServer, outData, out rcvData); if (askResponse != StatusCode.Success) { return askResponse; } assert rcvData != null; try { offset = 0; Dump(rcvData, offset); answer = Dns.Format.Parse(rcvData, ref offset); } catch (Exception e) { DebugPrint("Parser threw {0}", e); return StatusCode.TransportError; } return StatusCode.Success; } public StatusCode Ask(IPv4 dnsServer, Dns.Query query, out Dns.Format answer) { return Ask(dnsServer, new Dns.Query [] { query }, out answer); } private StatusCode AskDnsServer(IPv4 dnsServer, byte[]! outData, out byte[] rcvData) { Core core = Core.Instance(); UdpModule udpModule = core.GetProtocolByName("UDP") as UdpModule; if (udpModule == null) { rcvData = null; return StatusCode.TransportError; } UdpSession udpSession = udpModule.CreateBoundSession(IPv4.Any, 0, dnsServer, Dns.Format.ServerPort); udpSession.WriteData(outData); rcvData = udpSession.PollCopyData(TimeSpan.FromTicks(timeout.Ticks)); if (rcvData == null) return StatusCode.Timeout; return StatusCode.Success; } } }