/// <summary> /// Finds the Autodiscover host from DNS SRV records. /// </summary> /// <remarks> /// If the domain to lookup is "contoso.com", Autodiscover will use DnsQuery on SRV records /// for "_autodiscover._tcp.contoso.com". If the query is successful it will return a target /// domain (e.g. "mail.contoso.com") which will be tried as an Autodiscover endpoint. /// </remarks> /// <param name="domain">The domain.</param> /// <returns>Autodiscover hostname (will be null if lookup failed).</returns> internal string FindAutodiscoverHostFromSrv(string domain) { string domainToMatch = AutoDiscoverSrvPrefix + domain; DnsSrvRecord dnsSrvRecord = this.FindBestMatchingSrvRecord(domainToMatch); if ((dnsSrvRecord == null) || string.IsNullOrEmpty(dnsSrvRecord.NameTarget)) { this.service.TraceMessage( TraceFlags.AutodiscoverConfiguration, "No appropriate SRV record was found."); return(null); } else { this.service.TraceMessage( TraceFlags.AutodiscoverConfiguration, string.Format("DNS query for SRV record for domain {0} found {1}", domain, dnsSrvRecord.NameTarget)); return(dnsSrvRecord.NameTarget); } }
/// <summary> /// Finds the best matching SRV record. /// </summary> /// <param name="domain">The domain.</param> /// <returns>DnsSrvRecord(will be null if lookup failed).</returns> private DnsSrvRecord FindBestMatchingSrvRecord(string domain) { List <DnsSrvRecord> dnsSrvRecordList; try { // Make DnsQuery call to get collection of SRV records. dnsSrvRecordList = DnsClient.DnsQuery <DnsSrvRecord>(domain, this.service.DnsServerAddress); } catch (DnsException ex) { string dnsExcMessage = string.Format( "DnsQuery returned error error '{0}' error code 0x{1:X8}.", ex.Message, ex.NativeErrorCode); this.service.TraceMessage(TraceFlags.AutodiscoverConfiguration, dnsExcMessage); return(null); } catch (SecurityException ex) { // In restricted environments, we may not be allowed to call unmanaged code. this.service.TraceMessage( TraceFlags.AutodiscoverConfiguration, string.Format("DnsQuery cannot be called. Security error: {0}.", ex.Message)); return(null); } this.service.TraceMessage( TraceFlags.AutodiscoverConfiguration, string.Format("{0} SRV records were returned.", dnsSrvRecordList.Count)); // If multiple records were returned, they will be returned sorted by priority // (and weight) order. Need to find the index of the first record that supports SSL. int priority = int.MinValue; int weight = int.MinValue; bool recordFound = false; foreach (DnsSrvRecord dnsSrvRecord in dnsSrvRecordList) { if (dnsSrvRecord.Port == SslPort) { priority = dnsSrvRecord.Priority; weight = dnsSrvRecord.Weight; recordFound = true; break; } } // Records were returned but nothing matched our criteria. if (!recordFound) { this.service.TraceMessage( TraceFlags.AutodiscoverConfiguration, "No appropriate SRV records were found."); return(null); } // Collect all records with the same (highest) priority. // (Aren't lambda expressions cool? ;-) List <DnsSrvRecord> bestDnsSrvRecordList = dnsSrvRecordList.FindAll( record => (record.Port == SslPort) && (record.Priority == priority) && (record.Weight == weight)); // The list must contain at least one matching record since we found one earlier. EwsUtilities.Assert( dnsSrvRecordList.Count > 0, "AutodiscoverDnsClient.FindBestMatchingSrvRecord", "At least one DNS SRV record must match the criteria."); // If we have multiple records with the same priority and weight, randomly pick one. int recordIndex = (bestDnsSrvRecordList.Count > 1) ? randomTieBreakerSelector.Next(bestDnsSrvRecordList.Count) : 0; DnsSrvRecord bestDnsSrvRecord = bestDnsSrvRecordList[recordIndex]; string traceMessage = string.Format( "Returning SRV record {0} of {1} records. Target: {2}, Priority: {3}, Weight: {4}", recordIndex, dnsSrvRecordList.Count, bestDnsSrvRecord.NameTarget, bestDnsSrvRecord.Priority, bestDnsSrvRecord.Weight); this.service.TraceMessage(TraceFlags.AutodiscoverConfiguration, traceMessage); return(bestDnsSrvRecord); }