/// <summary> /// Starts operation processing. /// </summary> /// <param name="dnsClient">DNS client.</param> /// <returns>Returns true if asynchronous operation in progress or false if operation completed synchronously.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>dnsClient</b> is null reference.</exception> internal bool Start(Dns_Client dnsClient) { if(dnsClient == null){ throw new ArgumentNullException("dnsClient"); } SetState(AsyncOP_State.Active); // Argument 'hostNameOrIP' is IP address. if(Net_Utils.IsIPAddress(m_HostNameOrIP)){ m_pIPv4Addresses.Add(IPAddress.Parse(m_HostNameOrIP)); SetState(AsyncOP_State.Completed); } // This is probably NetBios name. if(m_HostNameOrIP.IndexOf(".") == -1){ try{ // This callback is called when BeginGetHostAddresses method has completed. AsyncCallback callback = delegate(IAsyncResult ar){ try{ foreach(IPAddress ip in System.Net.Dns.EndGetHostAddresses(ar)){ if(ip.AddressFamily == AddressFamily.InterNetwork){ m_pIPv4Addresses.Add(ip); } else{ m_pIPv6Addresses.Add(ip); } } } catch(Exception x){ m_pException = x; } SetState(AsyncOP_State.Completed); }; // Start resolving host ip addresses. System.Net.Dns.BeginGetHostAddresses(m_HostNameOrIP,callback,null); } catch(Exception x){ m_pException = x; } } // Query A/AAAA records. else{ #region A records transaction DNS_ClientTransaction transaction_A = dnsClient.CreateTransaction(DNS_QType.A,m_HostNameOrIP,2000); transaction_A.StateChanged += delegate(object s1,EventArgs<DNS_ClientTransaction> e1){ if(e1.Value.State == DNS_ClientTransactionState.Completed){ lock(m_pLock){ if(e1.Value.Response.ResponseCode != DNS_RCode.NO_ERROR){ m_pException = new DNS_ClientException(e1.Value.Response.ResponseCode); } else{ foreach(DNS_rr_A record in e1.Value.Response.GetARecords()){ m_pIPv4Addresses.Add(record.IP); } } m_Counter++; // Both A and AAAA transactions are completed, we are done. if(m_Counter == 2){ SetState(AsyncOP_State.Completed); } } } }; transaction_A.Timeout += delegate(object s1,EventArgs e1){ lock(m_pLock){ m_pException = new IOException("DNS transaction timeout, no response from DNS server."); m_Counter++; // Both A and AAAA transactions are completed, we are done. if(m_Counter == 2){ SetState(AsyncOP_State.Completed); } } }; transaction_A.Start(); #endregion #region AAAA records transaction DNS_ClientTransaction transaction_AAAA = dnsClient.CreateTransaction(DNS_QType.AAAA,m_HostNameOrIP,2000); transaction_AAAA.StateChanged += delegate(object s1,EventArgs<DNS_ClientTransaction> e1){ if(e1.Value.State == DNS_ClientTransactionState.Completed){ lock(m_pLock){ if(e1.Value.Response.ResponseCode != DNS_RCode.NO_ERROR){ m_pException = new DNS_ClientException(e1.Value.Response.ResponseCode); } else{ foreach(DNS_rr_AAAA record in e1.Value.Response.GetAAAARecords()){ m_pIPv6Addresses.Add(record.IP); } } m_Counter++; // Both A and AAAA transactions are completed, we are done. if(m_Counter == 2){ SetState(AsyncOP_State.Completed); } } } }; transaction_AAAA.Timeout += delegate(object s1,EventArgs e1){ lock(m_pLock){ m_pException = new IOException("DNS transaction timeout, no response from DNS server."); m_Counter++; // Both A and AAAA transactions are completed, we are done. if(m_Counter == 2){ SetState(AsyncOP_State.Completed); } } }; transaction_AAAA.Start(); #endregion } // Set flag rise CompletedAsync event flag. The event is raised when async op completes. // If already completed sync, that flag has no effect. lock(m_pLock){ m_RiseCompleted = true; return m_State == AsyncOP_State.Active; } }
/// <summary> /// Starts looking up MX records for specified domain. /// </summary> /// <param name="dnsClient">DNS client.</param> /// <param name="domain">Domain name.</param> /// <param name="domainIsCName">If true domain name is CNAME(alias).</param> /// <exception cref="ArgumentNullException">Is riased when <b>dnsClient</b> or <b>domain</b> is null reference.</exception> private void LookupMX(Dns_Client dnsClient,string domain,bool domainIsCName) { if(dnsClient == null){ throw new ArgumentNullException("dnsClient"); } if(domain == null){ throw new ArgumentNullException("domain"); } // Try to get MX records. DNS_ClientTransaction transaction_MX = dnsClient.CreateTransaction(DNS_QType.MX,domain,2000); transaction_MX.StateChanged += delegate(object s1,EventArgs<DNS_ClientTransaction> e1){ try{ if(e1.Value.State == DNS_ClientTransactionState.Completed){ // No errors. if(e1.Value.Response.ResponseCode == DNS_RCode.NO_ERROR){ List<DNS_rr_MX> mxRecords = new List<DNS_rr_MX>(); foreach(DNS_rr_MX mx in e1.Value.Response.GetMXRecords()){ // Skip invalid MX records. if(string.IsNullOrEmpty(mx.Host)){ } else{ mxRecords.Add(mx); } } // Use MX records. if(mxRecords.Count > 0){ m_pHosts = new HostEntry[mxRecords.Count]; // Create name to index map, so we can map asynchronous A/AAAA lookup results back to MX priority index. Dictionary<string,int> name_to_index_map = new Dictionary<string,int>(); List<string> lookupQueue = new List<string>(); // Process MX records. for(int i=0;i<m_pHosts.Length;i++){ DNS_rr_MX mx = mxRecords[i]; IPAddress[] ips = Get_A_or_AAAA_FromResponse(mx.Host,e1.Value.Response); // No A or AAAA records in addtional answers section for MX, we need todo new query for that. if(ips.Length == 0){ name_to_index_map[mx.Host] = i; lookupQueue.Add(mx.Host); } else{ m_pHosts[i] = new HostEntry(mx.Host,ips,null); } } // We have MX records which A or AAAA records not provided in DNS response, lookup them. if(lookupQueue.Count > 0){ GetHostsAddressesAsyncOP op = new GetHostsAddressesAsyncOP(lookupQueue.ToArray(),true); // This event is raised when lookup completes asynchronously. op.CompletedAsync += delegate(object s2,EventArgs<GetHostsAddressesAsyncOP> e2){ LookupCompleted(op,name_to_index_map); }; // Lookup completed synchronously. if(!dnsClient.GetHostsAddressesAsync(op)){ LookupCompleted(op,name_to_index_map); } } // All MX records resolved. else{ SetState(AsyncOP_State.Completed); } } // Use CNAME as initial domain name. else if(e1.Value.Response.GetCNAMERecords().Length > 0){ if(domainIsCName){ m_pException = new Exception("CNAME to CNAME loop dedected."); SetState(AsyncOP_State.Completed); } else{ LookupMX(dnsClient,e1.Value.Response.GetCNAMERecords()[0].Alias,true); } } // Use domain name as MX. else{ m_pHosts = new HostEntry[1]; // Create name to index map, so we can map asynchronous A/AAAA lookup results back to MX priority index. Dictionary<string,int> name_to_index_map = new Dictionary<string,int>(); name_to_index_map.Add(domain,0); GetHostsAddressesAsyncOP op = new GetHostsAddressesAsyncOP(new string[]{domain}); // This event is raised when lookup completes asynchronously. op.CompletedAsync += delegate(object s2,EventArgs<GetHostsAddressesAsyncOP> e2){ LookupCompleted(op,name_to_index_map); }; // Lookup completed synchronously. if(!dnsClient.GetHostsAddressesAsync(op)){ LookupCompleted(op,name_to_index_map); } } } // DNS server returned error, just return error. else{ m_pException = new DNS_ClientException(e1.Value.Response.ResponseCode); SetState(AsyncOP_State.Completed); } } transaction_MX.Timeout += delegate(object s2,EventArgs e2){ m_pException = new IOException("DNS transaction timeout, no response from DNS server."); SetState(AsyncOP_State.Completed); }; } catch(Exception x){ m_pException = x; SetState(AsyncOP_State.Completed); } }; transaction_MX.Start(); }