microsoft.exchange.webservices.data.autodiscover.AutodiscoverDnsClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ews-java-api Show documentation
Show all versions of ews-java-api Show documentation
Exchange Web Services (EWS) Java API
/*
* The MIT License
* Copyright (c) 2012 Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package microsoft.exchange.webservices.data.autodiscover;
import microsoft.exchange.webservices.data.core.EwsUtilities;
import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
import microsoft.exchange.webservices.data.core.exception.dns.DnsException;
import microsoft.exchange.webservices.data.dns.DnsClient;
import microsoft.exchange.webservices.data.dns.DnsSrvRecord;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Class that reads AutoDiscover configuration information from DNS.
*/
class AutodiscoverDnsClient {
/**
* SRV DNS prefix to lookup.
*/
private static final String AutoDiscoverSrvPrefix = "_autodiscover._tcp.";
/**
* We are only interested in records that use SSL.
*/
private static final int SslPort = 443;
/**
* Random selector in the case of ties.
*/
private static Random RandomTieBreakerSelector = new Random();
/**
* AutodiscoverService using this DNS reader.
*/
private AutodiscoverService service;
/**
* Initializes a new instance of the class.
*
* @param service the service
*/
protected AutodiscoverDnsClient(AutodiscoverService service) {
this.service = service;
}
/**
* Extracts a valid autodiscover hostname, if any, from a dns srv response.
*
* @param dnsNameTarget The hostname response returned by DNS
* @return Autodiscover hostname (will be null if dnsNameTarget is invalid).
*/
protected static String extractHostnameFromDnsSrv(String dnsNameTarget) {
if (dnsNameTarget == null || dnsNameTarget.isEmpty()) {
return null;
} else {
if (dnsNameTarget.endsWith(".")) {
dnsNameTarget = dnsNameTarget.substring(0, dnsNameTarget.length()-1);
}
return dnsNameTarget;
}
}
/**
* Finds the Autodiscover host from DNS SRV records.
*
* @param domain the domain
* @return Autodiscover hostname (will be null if lookup failed).
* @throws IOException signals that an I/O exception has occurred.
*/
protected String findAutodiscoverHostFromSrv(String domain)
throws XMLStreamException, IOException {
String domainToMatch = AutoDiscoverSrvPrefix + domain;
DnsSrvRecord dnsSrvRecord = this
.findBestMatchingSrvRecord(domainToMatch);
if (dnsSrvRecord != null) {
String hostName = extractHostnameFromDnsSrv(dnsSrvRecord.getNameTarget());
if (hostName != null) {
this.service.traceMessage(TraceFlags.AutodiscoverConfiguration, String
.format("DNS query for SRV record for domain %s found %s", domain, hostName));
return hostName;
}
}
this.service.traceMessage(TraceFlags.AutodiscoverConfiguration,
"No appropriate SRV record was found.");
return null;
}
/**
* Finds the best matching SRV record.
*
* @param domain the domain
* @return DnsSrvRecord (will be null if lookup failed)
* @throws XMLStreamException the XML stream exception
* @throws IOException signals that an I/O exception has occurred.
*/
private DnsSrvRecord findBestMatchingSrvRecord(String domain)
throws XMLStreamException, IOException {
List dnsSrvRecordList;
try {
// Make DnsQuery call to get collection of SRV records.
dnsSrvRecordList = DnsClient.dnsQuery(DnsSrvRecord.class, domain, this.service.getDnsServerAddress());
} catch (DnsException ex) {
String dnsExcMessage = String.format("DnsQuery returned error '%s'.", ex.getMessage());
this.service
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
dnsExcMessage);
return null;
} catch (SecurityException ex) {
// In restricted environments, we may not be allowed to call
// un-managed code.
this.service.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format(
"DnsQuery cannot be called. Security error: %s.",
ex.getMessage()));
return null;
}
this.service.traceMessage(TraceFlags.AutodiscoverConfiguration, String
.format("%d SRV records were returned.", dnsSrvRecordList
.size()));
// 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 = Integer.MIN_VALUE;
int weight = Integer.MAX_VALUE;
boolean recordFound = false;
for (DnsSrvRecord dnsSrvRecord : dnsSrvRecordList) {
if (dnsSrvRecord.getPort() == SslPort) {
priority = dnsSrvRecord.getPriority();
weight = dnsSrvRecord.getWeight();
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;
}
List bestDnsSrvRecordList = new ArrayList();
for (DnsSrvRecord dnsSrvRecord : dnsSrvRecordList) {
if (dnsSrvRecord.getPort() == SslPort &&
dnsSrvRecord.getPriority() == priority &&
dnsSrvRecord.getWeight() == weight) {
bestDnsSrvRecordList.add(dnsSrvRecord);
}
}
// The list must contain at least one matching record since we found one
// earlier.
EwsUtilities.ewsAssert(dnsSrvRecordList.size() > 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.size() > 1) ?
RandomTieBreakerSelector
.nextInt(bestDnsSrvRecordList.size()) :
0;
DnsSrvRecord bestDnsSrvRecord = bestDnsSrvRecordList.get(recordIndex);
String traceMessage = String.format("Returning SRV record %d " +
"of %d records. " +
"Target: %s, Priority: %d, Weight: %d",
recordIndex, dnsSrvRecordList.size(),
bestDnsSrvRecord.getNameTarget(),
bestDnsSrvRecord.getPriority(),
bestDnsSrvRecord.getWeight());
this.service.traceMessage(TraceFlags.
AutodiscoverConfiguration, traceMessage);
return bestDnsSrvRecord;
}
}