/// <summary> /// Processes received UDP packet. /// </summary> /// <param name="e">UDP packet.</param> private void ProcessUdpPacket(UDP_e_PacketReceived e) { try { if (m_IsDisposed) { return; } DnsServerResponse serverResponse = ParseQuery(e.Buffer); DNS_ClientTransaction transaction = null; // Pass response to transaction. if (m_pTransactions.TryGetValue(serverResponse.ID, out transaction)) { if (transaction.State == DNS_ClientTransactionState.Active) { // Cache query. if (m_UseDnsCache && serverResponse.ResponseCode == DNS_RCode.NO_ERROR) { DnsCache.AddToCache(transaction.QName, (int)transaction.QType, serverResponse); } transaction.ProcessResponse(serverResponse); } } // No such transaction or transaction has timed out before answer received. //else{ //} } catch { // We don't care about receiving errors here, skip them. } }
/// <summary> /// Processes DNS server response through this transaction. /// </summary> /// <param name="response">DNS server response.</param> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception> internal void ProcessResponse(DnsServerResponse response) { if (response == null) { throw new ArgumentNullException("response"); } try { lock (m_pLock) { // Late arriving response or retransmitted response, just skip it. if (m_pResponse != null) { return; } m_pResponse = response; } SetState(DNS_ClientTransactionState.Completed); } finally { if (this.State == DNS_ClientTransactionState.Completed) { Dispose(); } } }
/// <summary> /// Cleans up any resource being used. /// </summary> public void Dispose() { lock (m_pLock) { if (this.State == DNS_ClientTransactionState.Disposed) { return; } SetState(DNS_ClientTransactionState.Disposed); if (m_pTimeoutTimer != null) { m_pTimeoutTimer.Dispose(); m_pTimeoutTimer = null; } m_pOwner = null; m_pResponse = null; this.StateChanged = null; this.Timeout = null; } }
/// <summary> /// Queries server with specified query. /// </summary> /// <param name="queryText">Query text. It depends on queryType.</param> /// <param name="queryType">Query type.</param> /// <param name="timeout">Query timeout in milli seconds.</param> /// <returns>Returns DSN server response.</returns> public DnsServerResponse Query(string queryText, DNS_QType queryType, int timeout) { DnsServerResponse retVal = null; ManualResetEvent wait = new ManualResetEvent(false); DNS_ClientTransaction transaction = CreateTransaction(queryType, queryText, timeout); transaction.Timeout += delegate(object s, EventArgs e) { wait.Set(); }; transaction.StateChanged += delegate(object s1, EventArgs <DNS_ClientTransaction> e1) { if (transaction.State == DNS_ClientTransactionState.Completed || transaction.State == DNS_ClientTransactionState.Disposed) { retVal = transaction.Response; wait.Set(); } }; transaction.Start(); // Wait transaction to complete. wait.WaitOne(); return(retVal); }
/// <summary> /// Default constructor. /// </summary> /// <param name="response">DNS server response.</param> /// <param name="expires">Time when cache entry expires.</param> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception> public CacheEntry(DnsServerResponse response,DateTime expires) { if(response == null){ throw new ArgumentNullException("response"); } m_pResponse = response; m_Expires = expires; }
/// <summary> /// Starts DNS transaction processing. /// </summary> /// <exception cref="InvalidOperationException">Is raised when this method is called in invalid transaction state.</exception> public void Start() { if (this.State != DNS_ClientTransactionState.WaitingForStart) { throw new InvalidOperationException("DNS_ClientTransaction.Start may be called only in 'WaitingForStart' transaction state."); } SetState(DNS_ClientTransactionState.Active); // Move processing to thread pool. ThreadPool.QueueUserWorkItem(delegate(object state){ try{ // Use DNS cache if allowed. if (Dns_Client.UseDnsCache) { DnsServerResponse response = m_pOwner.Cache.GetFromCache(m_QName, (int)m_QType); if (response != null) { m_pResponse = response; SetState(DNS_ClientTransactionState.Completed); Dispose(); return; } } byte[] buffer = new byte[1400]; int count = CreateQuery(buffer, m_ID, m_QName, m_QType, 1); // Send parallel query to DNS server(s). foreach (string server in Dns_Client.DnsServers) { if (Net_Utils.IsIPAddress(server)) { IPAddress ip = IPAddress.Parse(server); m_pOwner.Send(ip, buffer, count); } } m_pTimeoutTimer.Start(); } catch { // Check if we have bad unicode qname. try{ System.Globalization.IdnMapping ldn = new System.Globalization.IdnMapping(); ldn.GetAscii(m_QName); } catch { m_pResponse = new DnsServerResponse(true, m_ID, DNS_RCode.NAME_ERROR, new List <DNS_rr>(), new List <DNS_rr>(), new List <DNS_rr>()); } SetState(DNS_ClientTransactionState.Completed); } }); }
/// <summary> /// Default constructor. /// </summary> /// <param name="response">DNS server response.</param> /// <param name="expires">Time when cache entry expires.</param> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception> public CacheEntry(DnsServerResponse response, DateTime expires) { if (response == null) { throw new ArgumentNullException("response"); } m_pResponse = response; m_Expires = expires; }
/// <summary> /// Resolves host name to IP addresses. /// </summary> /// <param name="host">Host name or IP address.</param> /// <returns>Return specified host IP addresses.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>host</b> is null.</exception> public static IPAddress[] Resolve(string host) { if (host == null) { throw new ArgumentNullException("host"); } // If hostName_IP is IP try { return(new IPAddress[] { IPAddress.Parse(host) }); } catch { } // This is probably NetBios name if (host.IndexOf(".") == -1) { return(System.Net.Dns.GetHostEntry(host).AddressList); } else { // hostName_IP must be host name, try to resolve it's IP using (Dns_Client dns = new Dns_Client()) { DnsServerResponse resp = dns.Query(host, DNS_QType.A); if (resp.ResponseCode == DNS_RCode.NO_ERROR) { DNS_rr_A[] records = resp.GetARecords(); IPAddress[] retVal = new IPAddress[records.Length]; for (int i = 0; i < records.Length; i++) { retVal[i] = records[i].IP; } return(retVal); } else { throw new Exception(resp.ResponseCode.ToString()); } } } }
/// <summary> /// Adds dns records to cache. If old entry exists, it is replaced. /// </summary> /// <param name="qname">Query name.</param> /// <param name="qtype">Query type.</param> /// <param name="response">DNS server response.</param> /// <exception cref="ArgumentNullException">Is raised when <b>qname</b> or <b>response</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public void AddToCache(string qname, int qtype, DnsServerResponse response) { if (qname == null) { throw new ArgumentNullException("qname"); } if (qname == string.Empty) { throw new ArgumentException("Argument 'qname' value must be specified.", "qname"); } if (response == null) { throw new ArgumentNullException("response"); } lock (m_pCache) { // Remove old cache entry, if any. if (m_pCache.ContainsKey(qname + qtype)) { m_pCache.Remove(qname + qtype); } if (response.ResponseCode == DNS_RCode.NO_ERROR) { int ttl = m_MaxCacheTtl; // Search smallest DNS record TTL and use it. foreach (DNS_rr rr in response.AllAnswers) { if (rr.TTL < ttl) { ttl = rr.TTL; } } m_pCache.Add(qname + qtype, new CacheEntry(response, DateTime.Now.AddSeconds(ttl))); } else { m_pCache.Add(qname + qtype, new CacheEntry(response, DateTime.Now.AddSeconds(m_MaxNegativeCacheTtl))); } } }
/// <summary> /// Cleans up any resource being used. /// </summary> public void Dispose() { lock(m_pLock){ if(this.State == DNS_ClientTransactionState.Disposed){ return; } SetState(DNS_ClientTransactionState.Disposed); m_pTimeoutTimer.Dispose(); m_pTimeoutTimer = null; m_pOwner = null; m_pResponse = null; this.StateChanged = null; this.Timeout = null; } }
/// <summary> /// Adds dns records to cache. If old entry exists, it is replaced. /// </summary> /// <param name="qname"></param> /// <param name="qtype"></param> /// <param name="answers"></param> public static void AddToCache(string qname, int qtype, DnsServerResponse answers) { if (answers == null) { return; } try{ lock (m_pCache){ // Remove old cache entry, if any. if (m_pCache.Contains(qname + qtype)) { m_pCache.Remove(qname + qtype); } m_pCache.Add(qname + qtype, new DnsCacheEntry(answers, DateTime.Now)); } } catch { } }
/// <summary> /// Gets specified host IP addresses(A and AAAA). /// </summary> /// <param name="host">Host name.</param> /// <returns>Returns specified host IP addresses.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>host</b> is null reference.</exception> public IPAddress[] GetHostAddresses(string host) { if (host == null) { throw new ArgumentNullException("host"); } List <IPAddress> retVal = new List <IPAddress>(); // This is probably NetBios name if (host.IndexOf(".") == -1) { return(System.Net.Dns.GetHostEntry(host).AddressList); } else { DnsServerResponse response = Query(host, DNS_QType.A); if (response.ResponseCode != DNS_RCode.NO_ERROR) { throw new DNS_ClientException(response.ResponseCode); } foreach (DNS_rr_A record in response.GetARecords()) { retVal.Add(record.IP); } response = Query(host, DNS_QType.AAAA); if (response.ResponseCode != DNS_RCode.NO_ERROR) { throw new DNS_ClientException(response.ResponseCode); } foreach (DNS_rr_AAAA record in response.GetAAAARecords()) { retVal.Add(record.IP); } } return(retVal.ToArray()); }
/// <summary> /// Processes DNS server response through this transaction. /// </summary> /// <param name="response">DNS server response.</param> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception> internal void ProcessResponse(DnsServerResponse response) { if (response == null) { throw new ArgumentNullException("response"); } try { lock (m_pLock) { if (this.State != DNS_ClientTransactionState.Active) { return; } m_ResponseCount++; // Late arriving response or retransmitted response, just skip it. if (m_pResponse != null) { return; } // If server refused to complete query and we more active queries to other servers, skip that response. if (response.ResponseCode == DNS_RCode.REFUSED && m_ResponseCount < Dns_Client.DnsServers.Length) { return; } m_pResponse = response; SetState(DNS_ClientTransactionState.Completed); } } finally { if (this.State == DNS_ClientTransactionState.Completed) { Dispose(); } } }
/// <summary> /// Processes DNS server response through this transaction. /// </summary> /// <param name="response">DNS server response.</param> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception> internal void ProcessResponse(DnsServerResponse response) { if(response == null){ throw new ArgumentNullException("response"); } try{ lock(m_pLock){ if(this.State != DNS_ClientTransactionState.Active){ return; } m_ResponseCount++; // Late arriving response or retransmitted response, just skip it. if(m_pResponse != null){ return; } // If server refused to complete query and we more active queries to other servers, skip that response. if(response.ResponseCode == DNS_RCode.REFUSED && m_ResponseCount < Dns_Client.DnsServers.Length){ return; } m_pResponse = response; SetState(DNS_ClientTransactionState.Completed); } } finally{ if(this.State == DNS_ClientTransactionState.Completed){ Dispose(); } } }
/// <summary> /// Starts DNS transaction processing. /// </summary> /// <exception cref="InvalidOperationException">Is raised when this method is called in invalid transaction state.</exception> public void Start() { if(this.State != DNS_ClientTransactionState.WaitingForStart){ throw new InvalidOperationException("DNS_ClientTransaction.Start may be called only in 'WaitingForStart' transaction state."); } SetState(DNS_ClientTransactionState.Active); // Move processing to thread pool. ThreadPool.QueueUserWorkItem(delegate(object state){ try{ // Use DNS cache if allowed. if(Dns_Client.UseDnsCache){ DnsServerResponse response = m_pOwner.Cache.GetFromCache(m_QName,(int)m_QType); if(response != null){ m_pResponse = response; SetState(DNS_ClientTransactionState.Completed); Dispose(); return; } } byte[] buffer = new byte[1400]; int count = CreateQuery(buffer,m_ID,m_QName,m_QType,1); // Send parallel query to DNS server(s). foreach(string server in Dns_Client.DnsServers){ if(Net_Utils.IsIPAddress(server)){ IPAddress ip = IPAddress.Parse(server); m_pOwner.Send(ip,buffer,count); } } m_pTimeoutTimer.Start(); } catch{ Dispose(); } }); }
/// <summary> /// Default constructor. /// </summary> /// <param name="answers">Dns answers.</param> /// <param name="addTime">Entry add time.</param> public DnsCacheEntry(DnsServerResponse answers,DateTime addTime) { m_pResponse = answers; m_Time = addTime; }
/// <summary> /// Adds dns records to cache. If old entry exists, it is replaced. /// </summary> /// <param name="qname">Query name.</param> /// <param name="qtype">Query type.</param> /// <param name="response">DNS server response.</param> /// <exception cref="ArgumentNullException">Is raised when <b>qname</b> or <b>response</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public void AddToCache(string qname,int qtype,DnsServerResponse response) { if(qname == null){ throw new ArgumentNullException("qname"); } if(qname == string.Empty){ throw new ArgumentException("Argument 'qname' value must be specified.","qname"); } if(response == null){ throw new ArgumentNullException("response"); } lock(m_pCache){ // Remove old cache entry, if any. if(m_pCache.ContainsKey(qname + qtype)){ m_pCache.Remove(qname + qtype); } if(response.ResponseCode == DNS_RCode.NO_ERROR){ int ttl = m_MaxCacheTtl; // Search smallest DNS record TTL and use it. foreach(DNS_rr rr in response.AllAnswers){ if(rr.TTL < ttl){ ttl = rr.TTL; } } m_pCache.Add(qname + qtype,new CacheEntry(response,DateTime.Now.AddSeconds(ttl))); } else{ m_pCache.Add(qname + qtype,new CacheEntry(response,DateTime.Now.AddSeconds(m_MaxNegativeCacheTtl))); } } }
/// <summary> /// Gets A and AAAA records from DNS server additional responses section. /// </summary> /// <param name="name">Host name.</param> /// <param name="response">DNS server response.</param> /// <returns>Returns A and AAAA records from DNS server additional responses section.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>name</b> or <b>response</b> is null reference.</exception> private IPAddress[] Get_A_or_AAAA_FromResponse(string name,DnsServerResponse response) { if(name == null){ throw new ArgumentNullException("name"); } if(response == null){ throw new ArgumentNullException("response"); } List<IPAddress> aList = new List<IPAddress>(); List<IPAddress> aaaaList = new List<IPAddress>(); foreach(DNS_rr rr in response.AdditionalAnswers){ if(string.Equals(name,rr.Name,StringComparison.InvariantCultureIgnoreCase)){ if(rr is DNS_rr_A){ aList.Add(((DNS_rr_A)rr).IP); } else if(rr is DNS_rr_AAAA){ aaaaList.Add(((DNS_rr_AAAA)rr).IP); } } } // We list IPv4 first and then IPv6 addresses. aList.AddRange(aaaaList); return aList.ToArray(); }
/// <summary> /// Default constructor. /// </summary> /// <param name="answers">Dns answers.</param> /// <param name="addTime">Entry add time.</param> public DnsCacheEntry(DnsServerResponse answers, DateTime addTime) { m_pResponse = answers; m_Time = addTime; }
/// <summary> /// Adds dns records to cache. If old entry exists, it is replaced. /// </summary> /// <param name="qname"></param> /// <param name="qtype"></param> /// <param name="answers"></param> public static void AddToCache(string qname,int qtype,DnsServerResponse answers) { if(answers == null){ return; } try{ lock(m_pCache){ // Remove old cache entry, if any. if(m_pCache.Contains(qname + qtype)){ m_pCache.Remove(qname + qtype); } m_pCache.Add(qname + qtype,new DnsCacheEntry(answers,DateTime.Now)); } } catch{ } }