using System; using System.IO; using System.Collections.Generic; using System.Net; using System.Text; using System.Net.Sockets; using System.Net.NetworkInformation; using System.Diagnostics; using System.Runtime.Remoting.Messaging; /* * Network Working Group P. Mockapetris * Request for Comments: 1035 ISI * November 1987 * * DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION * */ namespace Heijden.DNS { /// /// Resolver is the main class to do DNS query lookups /// public class Resolver { /// /// Version of this set of routines, when not in a library /// public string Version { get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); } } /// /// Default DNS port /// public const int DefaultPort = 53; /// /// Gets list of OPENDNS servers /// public static readonly IPEndPoint[] DefaultDnsServers = { new IPEndPoint(IPAddress.Parse("208.67.222.222"), DefaultPort), new IPEndPoint(IPAddress.Parse("208.67.220.220"), DefaultPort) }; private ushort m_Unique; private bool m_UseCache; private bool m_Recursion; private int m_Retries; private int m_Timeout; private TransportType m_TransportType; private List m_DnsServers; private Dictionary m_ResponseCache; /// /// Constructor of Resolver using DNS servers specified. /// /// Set of DNS servers public Resolver(IPEndPoint[] DnsServers) { m_ResponseCache = new Dictionary(); m_DnsServers = new List(); m_DnsServers.AddRange(DnsServers); m_Unique = (ushort)(new Random()).Next(); m_Retries = 3; m_Timeout = 1; m_Recursion = true; m_UseCache = true; m_TransportType = TransportType.Udp; } /// /// Constructor of Resolver using DNS server specified. /// /// DNS server to use public Resolver(IPEndPoint DnsServer) : this(new IPEndPoint[] { DnsServer }) { } /// /// Constructor of Resolver using DNS server and port specified. /// /// DNS server to use /// DNS port to use public Resolver(IPAddress ServerIpAddress, int ServerPortNumber) : this(new IPEndPoint(ServerIpAddress,ServerPortNumber)) { } /// /// Constructor of Resolver using DNS address and port specified. /// /// DNS server address to use /// DNS port to use public Resolver(string ServerIpAddress, int ServerPortNumber) : this(IPAddress.Parse(ServerIpAddress), ServerPortNumber) { } /// /// Constructor of Resolver using DNS address. /// /// DNS server address to use public Resolver(string ServerIpAddress) : this(IPAddress.Parse(ServerIpAddress), DefaultPort) { } /// /// Resolver constructor, using DNS servers specified by Windows /// public Resolver() : this(GetDnsServers()) { } public class VerboseOutputEventArgs : EventArgs { public string Message; public VerboseOutputEventArgs(string Message) { this.Message = Message; } } private void Verbose(string format, params object[] args) { if (OnVerbose != null) OnVerbose(this, new VerboseEventArgs(string.Format(format, args))); } /// /// Verbose messages from internal operations /// public event VerboseEventHandler OnVerbose; public delegate void VerboseEventHandler(object sender, VerboseEventArgs e); public class VerboseEventArgs : EventArgs { public string Message; public VerboseEventArgs(string Message) { this.Message = Message; } } /// /// Gets or sets timeout in milliseconds /// public int TimeOut { get { return m_Timeout; } set { m_Timeout = value; } } /// /// Gets or sets number of retries before giving up /// public int Retries { get { return m_Retries; } set { if(value>=1) m_Retries = value; } } /// /// Gets or set recursion for doing queries /// public bool Recursion { get { return m_Recursion; } set { m_Recursion = value; } } /// /// Gets or sets protocol to use /// public TransportType TransportType { get { return m_TransportType; } set { m_TransportType = value; } } /// /// Gets or sets list of DNS servers to use /// public IPEndPoint[] DnsServers { get { return m_DnsServers.ToArray(); } set { m_DnsServers.Clear(); m_DnsServers.AddRange(value); } } /// /// Gets first DNS server address or sets single DNS server to use /// public string DnsServer { get { return m_DnsServers[0].Address.ToString(); } set { IPAddress ip; if (IPAddress.TryParse(value, out ip)) { m_DnsServers.Clear(); m_DnsServers.Add(new IPEndPoint(ip, DefaultPort)); return; } Response response = Query(value, QType.A); if (response.RecordsA.Length > 0) { m_DnsServers.Clear(); m_DnsServers.Add(new IPEndPoint(response.RecordsA[0].Address, DefaultPort)); } } } public bool UseCache { get { return m_UseCache; } set { m_UseCache = value; if (!m_UseCache) m_ResponseCache.Clear(); } } /// /// Clear the resolver cache /// public void ClearCache() { m_ResponseCache.Clear(); } private Response SearchInCache(Question question) { if (!m_UseCache) return null; string strKey = question.QClass + "-" + question.QType + "-" + question.QName; Response response = null; lock (m_ResponseCache) { if (!m_ResponseCache.ContainsKey(strKey)) return null; response = m_ResponseCache[strKey]; } int TimeLived = (int)((DateTime.Now.Ticks - response.TimeStamp.Ticks) / TimeSpan.TicksPerSecond); foreach (RR rr in response.RecordsRR) { rr.TimeLived = TimeLived; // The TTL property calculates its actual time to live if (rr.TTL == 0) return null; // out of date } return response; } private void AddToCache(Response response) { if (!m_UseCache) return; // No question, no caching if (response.Questions.Count == 0) return; // Only cached non-error responses if (response.header.RCODE != RCode.NoError) return; Question question = response.Questions[0]; string strKey = question.QClass + "-" + question.QType + "-" + question.QName; lock (m_ResponseCache) { if (m_ResponseCache.ContainsKey(strKey)) m_ResponseCache.Remove(strKey); m_ResponseCache.Add(strKey, response); } } private Response UdpRequest(Request request) { // RFC1035 max. size of a UDP datagram is 512 bytes byte[] responseMessage = new byte[512]; for (int intAttempts = 0; intAttempts < m_Retries; intAttempts++) { for (int intDnsServer = 0; intDnsServer < m_DnsServers.Count; intDnsServer++) { Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, m_Timeout * 1000); try { socket.SendTo(request.Data, m_DnsServers[intDnsServer]); int intReceived = socket.Receive(responseMessage); byte[] data = new byte[intReceived]; Array.Copy(responseMessage, data, intReceived); Response response = new Response(m_DnsServers[intDnsServer], data); AddToCache(response); return response; } catch (SocketException) { Verbose(string.Format(";; Connection to nameserver {0} failed", (intDnsServer + 1))); continue; // next try } finally { m_Unique++; // close the socket socket.Close(); } } } Response responseTimeout = new Response(); responseTimeout.Error = "Timeout Error"; return responseTimeout; } private Response TcpRequest(Request request) { //System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); //sw.Start(); byte[] responseMessage = new byte[512]; for (int intAttempts = 0; intAttempts < m_Retries; intAttempts++) { for (int intDnsServer = 0; intDnsServer < m_DnsServers.Count; intDnsServer++) { TcpClient tcpClient = new TcpClient(); tcpClient.ReceiveTimeout = m_Timeout * 1000; try { IAsyncResult result = tcpClient.BeginConnect(m_DnsServers[intDnsServer].Address, m_DnsServers[intDnsServer].Port, null, null); bool success = result.AsyncWaitHandle.WaitOne(m_Timeout*1000, true); if (!success || !tcpClient.Connected) { tcpClient.Close(); Verbose(string.Format(";; Connection to nameserver {0} failed", (intDnsServer + 1))); continue; } BufferedStream bs = new BufferedStream(tcpClient.GetStream()); byte[] data = request.Data; bs.WriteByte((byte)((data.Length >> 8) & 0xff)); bs.WriteByte((byte)(data.Length & 0xff)); bs.Write(data, 0, data.Length); bs.Flush(); Response TransferResponse = new Response(); int intSoa = 0; int intMessageSize = 0; //Debug.WriteLine("Sending "+ (request.Length+2) + " bytes in "+ sw.ElapsedMilliseconds+" mS"); while (true) { int intLength = bs.ReadByte() << 8 | bs.ReadByte(); if (intLength <= 0) { tcpClient.Close(); Verbose(string.Format(";; Connection to nameserver {0} failed", (intDnsServer + 1))); throw new SocketException(); // next try } intMessageSize += intLength; data = new byte[intLength]; bs.Read(data, 0, intLength); Response response = new Response(m_DnsServers[intDnsServer], data); //Debug.WriteLine("Received "+ (intLength+2)+" bytes in "+sw.ElapsedMilliseconds +" mS"); if (response.header.RCODE != RCode.NoError) return response; if (response.Questions[0].QType != QType.AXFR) { AddToCache(response); return response; } // Zone transfer!! if(TransferResponse.Questions.Count==0) TransferResponse.Questions.AddRange(response.Questions); TransferResponse.Answers.AddRange(response.Answers); TransferResponse.Authorities.AddRange(response.Authorities); TransferResponse.Additionals.AddRange(response.Additionals); if (response.Answers[0].Type == Type.SOA) intSoa++; if (intSoa == 2) { TransferResponse.header.QDCOUNT = (ushort)TransferResponse.Questions.Count; TransferResponse.header.ANCOUNT = (ushort)TransferResponse.Answers.Count; TransferResponse.header.NSCOUNT = (ushort)TransferResponse.Authorities.Count; TransferResponse.header.ARCOUNT = (ushort)TransferResponse.Additionals.Count; TransferResponse.MessageSize = intMessageSize; return TransferResponse; } } } // try catch (SocketException) { continue; // next try } finally { m_Unique++; // close the socket tcpClient.Close(); } } } Response responseTimeout = new Response(); responseTimeout.Error = "Timeout Error"; return responseTimeout; } /// /// Do Query on specified DNS servers /// /// Name to query /// Question type /// Class type /// Response of the query public Response Query(string name, QType qtype, QClass qclass) { Question question = new Question(name, qtype, qclass); Response response = SearchInCache(question); if (response != null) return response; Request request = new Request(); request.AddQuestion(question); return GetResponse(request); } /// /// Do an QClass=IN Query on specified DNS servers /// /// Name to query /// Question type /// Response of the query public Response Query(string name, QType qtype) { Question question = new Question(name, qtype, QClass.IN); Response response = SearchInCache(question); if (response != null) return response; Request request = new Request(); request.AddQuestion(question); return GetResponse(request); } private Response GetResponse(Request request) { request.header.ID = m_Unique; request.header.RD = m_Recursion; if (m_TransportType == TransportType.Udp) return UdpRequest(request); if (m_TransportType == TransportType.Tcp) return TcpRequest(request); Response response = new Response(); response.Error = "Unknown TransportType"; return response; } /// /// Gets a list of default DNS servers used on the Windows machine. /// /// public static IPEndPoint[] GetDnsServers() { List list = new List(); NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface n in adapters) { if (n.OperationalStatus == OperationalStatus.Up) { IPInterfaceProperties ipProps = n.GetIPProperties(); // thanks to Jon Webster on May 20, 2008 foreach (IPAddress ipAddr in ipProps.DnsAddresses) { IPEndPoint entry = new IPEndPoint(ipAddr, DefaultPort); if (!list.Contains(entry)) list.Add(entry); } } } return list.ToArray(); } // private IPHostEntry MakeEntry(string HostName) { IPHostEntry entry = new IPHostEntry(); entry.HostName = HostName; Response response = Query(HostName, QType.A, QClass.IN); // fill AddressList and aliases List AddressList = new List(); List Aliases = new List(); foreach (AnswerRR answerRR in response.Answers) { if (answerRR.Type == Type.A) { // answerRR.RECORD.ToString() == (answerRR.RECORD as RecordA).Address AddressList.Add(IPAddress.Parse((answerRR.RECORD.ToString()))); entry.HostName = answerRR.NAME; } else { if (answerRR.Type == Type.CNAME) Aliases.Add(answerRR.NAME); } } entry.AddressList = AddressList.ToArray(); entry.Aliases = Aliases.ToArray(); return entry; } /// /// Translates the IPV4 or IPV6 address into an arpa address /// /// IP address to get the arpa address form /// The 'mirrored' IPV4 or IPV6 arpa address public static string GetArpaFromIp(IPAddress ip) { if (ip.AddressFamily == AddressFamily.InterNetwork) { StringBuilder sb = new StringBuilder(); sb.Append("in-addr.arpa."); foreach (byte b in ip.GetAddressBytes()) { sb.Insert(0, string.Format("{0}.", b)); } return sb.ToString(); } if (ip.AddressFamily == AddressFamily.InterNetworkV6) { StringBuilder sb = new StringBuilder(); sb.Append("ip6.arpa."); foreach (byte b in ip.GetAddressBytes()) { sb.Insert(0, string.Format("{0:x}.", (b >> 4) & 0xf)); sb.Insert(0, string.Format("{0:x}.", (b >> 0) & 0xf)); } return sb.ToString(); } return "?"; } public static string GetArpaFromEnum(string strEnum) { StringBuilder sb = new StringBuilder(); string Number = System.Text.RegularExpressions.Regex.Replace(strEnum, "[^0-9]", ""); sb.Append("e164.arpa."); foreach (char c in Number) { sb.Insert(0, string.Format("{0}.", c)); } return sb.ToString(); } #region Deprecated methods in the original System.Net.DNS class /// /// Returns the Internet Protocol (IP) addresses for the specified host. /// /// The host name or IP address to resolve. /// /// An array of type System.Net.IPAddress that holds the IP addresses for the /// host that is specified by the hostNameOrAddress parameter. /// public IPAddress[] GetHostAddresses(string hostNameOrAddress) { IPHostEntry entry = GetHostEntry(hostNameOrAddress); return entry.AddressList; } private delegate IPAddress[] GetHostAddressesDelegate(string hostNameOrAddress); /// /// Asynchronously returns the Internet Protocol (IP) addresses for the specified /// host. /// /// The host name or IP address to resolve. /// /// An System.AsyncCallback delegate that references the method to invoke when /// the operation is complete. /// /// /// A user-defined object that contains information about the operation. This /// object is passed to the requestCallback delegate when the operation is complete. /// /// An System.IAsyncResult instance that references the asynchronous request. public IAsyncResult BeginGetHostAddresses(string hostNameOrAddress, AsyncCallback requestCallback, object stateObject) { GetHostAddressesDelegate g = new GetHostAddressesDelegate(GetHostAddresses); return g.BeginInvoke(hostNameOrAddress, requestCallback, stateObject); } /// /// Ends an asynchronous request for DNS information. /// /// /// An System.IAsyncResult instance returned by a call to the Heijden.Dns.Resolver.BeginGetHostAddresses(System.String,System.AsyncCallback,System.Object) /// method. /// /// public IPAddress[] EndGetHostAddresses(IAsyncResult AsyncResult) { AsyncResult aResult = (AsyncResult)AsyncResult; GetHostAddressesDelegate g = (GetHostAddressesDelegate)aResult.AsyncDelegate; return g.EndInvoke(AsyncResult); } /// /// Creates an System.Net.IPHostEntry instance from the specified System.Net.IPAddress. /// /// An System.Net.IPAddress. /// An System.Net.IPHostEntry. public IPHostEntry GetHostByAddress(IPAddress ip) { return GetHostEntry(ip); } /// /// Creates an System.Net.IPHostEntry instance from an IP address. /// /// An IP address. /// An System.Net.IPHostEntry instance. public IPHostEntry GetHostByAddress(string address) { return GetHostEntry(address); } /// /// Gets the DNS information for the specified DNS host name. /// /// The DNS name of the host /// An System.Net.IPHostEntry object that contains host information for the address specified in hostName. public IPHostEntry GetHostByName(string hostName) { return MakeEntry(hostName); } private delegate IPHostEntry GetHostByNameDelegate(string hostName); /// /// Asynchronously resolves an IP address to an System.Net.IPHostEntry instance. /// /// The DNS name of the host /// An System.AsyncCallback delegate that references the method to invoke when the operation is complete. /// /// A user-defined object that contains information about the operation. This /// object is passed to the requestCallback delegate when the operation is complete. /// /// An System.IAsyncResult instance that references the asynchronous request. public IAsyncResult BeginGetHostByName(string hostName, AsyncCallback requestCallback, object stateObject) { GetHostByNameDelegate g = new GetHostByNameDelegate(GetHostByName); return g.BeginInvoke(hostName, requestCallback, stateObject); } /// /// Ends an asynchronous request for DNS information. /// /// /// An System.IAsyncResult instance returned by a call to an /// Heijden.Dns.Resolver.BeginGetHostByName method. /// /// public IPHostEntry EndGetHostByName(IAsyncResult AsyncResult) { AsyncResult aResult = (AsyncResult)AsyncResult; GetHostByNameDelegate g = (GetHostByNameDelegate)aResult.AsyncDelegate; return g.EndInvoke(AsyncResult); } /// /// Resolves a host name or IP address to an System.Net.IPHostEntry instance. /// /// A DNS-style host name or IP address. /// //[Obsolete("no problem",false)] public IPHostEntry Resolve(string hostName) { return MakeEntry(hostName); } private delegate IPHostEntry ResolveDelegate(string hostName); /// /// Begins an asynchronous request to resolve a DNS host name or IP address to /// an System.Net.IPAddress instance. /// /// The DNS name of the host. /// /// An System.AsyncCallback delegate that references the method to invoke when /// the operation is complete. /// /// /// A user-defined object that contains information about the operation. This /// object is passed to the requestCallback delegate when the operation is complete. /// /// An System.IAsyncResult instance that references the asynchronous request. public IAsyncResult BeginResolve(string hostName, AsyncCallback requestCallback, object stateObject) { ResolveDelegate g = new ResolveDelegate(Resolve); return g.BeginInvoke(hostName, requestCallback, stateObject); } /// /// Ends an asynchronous request for DNS information. /// /// /// An System.IAsyncResult instance that is returned by a call to the System.Net.Dns.BeginResolve(System.String,System.AsyncCallback,System.Object) /// method. /// /// An System.Net.IPHostEntry object that contains DNS information about a host. public IPHostEntry EndResolve(IAsyncResult AsyncResult) { AsyncResult aResult = (AsyncResult)AsyncResult; ResolveDelegate g = (ResolveDelegate)aResult.AsyncDelegate; return g.EndInvoke(AsyncResult); } #endregion /// /// Resolves an IP address to an System.Net.IPHostEntry instance. /// /// An IP address. /// /// An System.Net.IPHostEntry instance that contains address information about /// the host specified in address. /// public IPHostEntry GetHostEntry(IPAddress ip) { Response response = Query(GetArpaFromIp(ip), QType.PTR, QClass.IN); if (response.RecordsPTR.Length > 0) return MakeEntry(response.RecordsPTR[0].PTRDNAME); else return new IPHostEntry(); } /// /// Resolves a host name or IP address to an System.Net.IPHostEntry instance. /// /// The host name or IP address to resolve. /// /// An System.Net.IPHostEntry instance that contains address information about /// the host specified in hostNameOrAddress. /// public IPHostEntry GetHostEntry(string hostNameOrAddress) { IPAddress iPAddress; if (IPAddress.TryParse(hostNameOrAddress, out iPAddress)) return GetHostEntry(iPAddress); else return MakeEntry(hostNameOrAddress); } private delegate IPHostEntry GetHostEntryViaIPDelegate(IPAddress ip); private delegate IPHostEntry GetHostEntryDelegate(string hostNameOrAddress); /// /// Asynchronously resolves a host name or IP address to an System.Net.IPHostEntry instance. /// /// The host name or IP address to resolve. /// /// An System.AsyncCallback delegate that references the method to invoke when /// the operation is complete. /// /// /// A user-defined object that contains information about the operation. This /// object is passed to the requestCallback delegate when the operation is complete. /// /// An System.IAsyncResult instance that references the asynchronous request. public IAsyncResult BeginGetHostEntry(string hostNameOrAddress, AsyncCallback requestCallback, object stateObject) { GetHostEntryDelegate g = new GetHostEntryDelegate(GetHostEntry); return g.BeginInvoke(hostNameOrAddress, requestCallback, stateObject); } /// /// Asynchronously resolves an IP address to an System.Net.IPHostEntry instance. /// /// The IP address to resolve. /// /// An System.AsyncCallback delegate that references the method to invoke when /// the operation is complete. /// /// /// A user-defined object that contains information about the operation. This /// object is passed to the requestCallback delegate when the operation is complete. /// /// An System.IAsyncResult instance that references the asynchronous request. public IAsyncResult BeginGetHostEntry(IPAddress ip, AsyncCallback requestCallback, object stateObject) { GetHostEntryViaIPDelegate g = new GetHostEntryViaIPDelegate(GetHostEntry); return g.BeginInvoke(ip, requestCallback, stateObject); } /// /// Ends an asynchronous request for DNS information. /// /// /// An System.IAsyncResult instance returned by a call to an /// Overload:Heijden.Dns.Resolver.BeginGetHostEntry method. /// /// /// An System.Net.IPHostEntry instance that contains address information about /// the host. /// public IPHostEntry EndGetHostEntry(IAsyncResult AsyncResult) { AsyncResult aResult = (AsyncResult)AsyncResult; if (aResult.AsyncDelegate is GetHostEntryDelegate) { GetHostEntryDelegate g = (GetHostEntryDelegate)aResult.AsyncDelegate; return g.EndInvoke(AsyncResult); } if (aResult.AsyncDelegate is GetHostEntryViaIPDelegate) { GetHostEntryViaIPDelegate g = (GetHostEntryViaIPDelegate)aResult.AsyncDelegate; return g.EndInvoke(AsyncResult); } return null; } private enum RRRecordStatus { UNKNOWN, NAME, TTL, CLASS, TYPE, VALUE } public void LoadRootFile(string strPath) { StreamReader sr = new StreamReader(strPath); while (!sr.EndOfStream) { string strLine = sr.ReadLine(); if (strLine == null) break; int intI = strLine.IndexOf(';'); if (intI >= 0) strLine = strLine.Substring(0, intI); strLine = strLine.Trim(); if (strLine.Length == 0) continue; RRRecordStatus status = RRRecordStatus.NAME; string Name=""; string Ttl=""; string Class=""; string Type=""; string Value=""; string strW = ""; for (intI = 0; intI < strLine.Length; intI++) { char C = strLine[intI]; if (C <= ' ' && strW!="") { switch (status) { case RRRecordStatus.NAME: Name = strW; status = RRRecordStatus.TTL; break; case RRRecordStatus.TTL: Ttl = strW; status = RRRecordStatus.CLASS; break; case RRRecordStatus.CLASS: Class = strW; status = RRRecordStatus.TYPE; break; case RRRecordStatus.TYPE: Type = strW; status = RRRecordStatus.VALUE; break; case RRRecordStatus.VALUE: Value = strW; status = RRRecordStatus.UNKNOWN; break; default: break; } strW = ""; } if (C > ' ') strW += C; } } sr.Close(); } } // class }