com.mongodb.internal.dns.DnsResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mongodb-driver-core Show documentation
Show all versions of mongodb-driver-core Show documentation
The Java operations layer for the MongoDB Java Driver. Third parties can ' +
'wrap this layer to provide custom higher-level APIs
/*
* Copyright 2008-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mongodb.internal.dns;
import com.mongodb.MongoClientException;
import com.mongodb.MongoConfigurationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import static java.lang.String.format;
import static java.util.Arrays.asList;
/**
* Utility class for resolving SRV and TXT records.
*
* This class should not be considered a part of the public API.
*/
public final class DnsResolver {
// The format of SRV record is
// priority weight port target.
// e.g.
// 0 5 5060 example.com.
// The priority and weight are ignored, and we just concatenate the host (after removing the ending '.') and port with a
// ':' in between, as expected by ServerAddress
// It's required that the srvHost has at least three parts (e.g. foo.bar.baz) and that all of the resolved hosts have a parent
// domain equal to the domain of the srvHost.
public static List resolveHostFromSrvRecords(final String srvHost) {
if (srvHost.split("\\.").length < 3) {
throw new IllegalArgumentException(format("An SRV host name '%s' was provided that does not contain at least three parts. "
+ "It must contain a hostname, domain name and a top level domain.", srvHost));
}
String srvHostDomain = srvHost.substring(srvHost.indexOf('.') + 1);
List srvHostDomainParts = asList(srvHostDomain.split("\\."));
List hosts = new ArrayList();
InitialDirContext dirContext = createDnsDirContext();
try {
Attributes attributes = dirContext.getAttributes("_mongodb._tcp." + srvHost, new String[]{"SRV"});
Attribute attribute = attributes.get("SRV");
if (attribute == null) {
throw new MongoClientException("No SRV record available for host " + srvHost);
}
NamingEnumeration> srvRecordEnumeration = attribute.getAll();
while (srvRecordEnumeration.hasMore()) {
String srvRecord = (String) srvRecordEnumeration.next();
String[] split = srvRecord.split(" ");
String resolvedHost = split[3].endsWith(".") ? split[3].substring(0, split[3].length() - 1) : split[3];
String resolvedHostDomain = resolvedHost.substring(resolvedHost.indexOf('.') + 1);
if (!sameParentDomain(srvHostDomainParts, resolvedHostDomain)) {
throw new MongoConfigurationException(
format("The SRV host name '%s'resolved to a host '%s 'that is not in a sub-domain of the SRV host.",
srvHost, resolvedHost));
}
hosts.add(resolvedHost + ":" + split[2]);
}
} catch (NamingException e) {
throw new MongoConfigurationException("Unable to look up SRV record for host " + srvHost, e);
} finally {
try {
dirContext.close();
} catch (NamingException e) {
// ignore
}
}
return hosts;
}
private static boolean sameParentDomain(final List srvHostDomainParts, final String resolvedHostDomain) {
List resolvedHostDomainParts = asList(resolvedHostDomain.split("\\."));
if (srvHostDomainParts.size() > resolvedHostDomainParts.size()) {
return false;
}
return resolvedHostDomainParts.subList(resolvedHostDomainParts.size() - srvHostDomainParts.size(), resolvedHostDomainParts.size())
.equals(srvHostDomainParts);
}
// A TXT record is just a string
// We require each to be one or more query parameters for a MongoDB connection string.
// Here we concatenate TXT records together with a '&' separator as required by connection strings
public static String resolveAdditionalQueryParametersFromTxtRecords(final String host) {
String additionalQueryParameters = "";
InitialDirContext dirContext = createDnsDirContext();
try {
Attributes attributes = dirContext.getAttributes(host, new String[]{"TXT"});
Attribute attribute = attributes.get("TXT");
if (attribute != null) {
NamingEnumeration> txtRecordEnumeration = attribute.getAll();
if (txtRecordEnumeration.hasMore()) {
// Remove all space characters, as the DNS resolver for TXT records inserts a space character
// between each character-string in a single TXT record. That whitespace is spurious in
// this context and must be removed
additionalQueryParameters = ((String) txtRecordEnumeration.next()).replaceAll("\\s", "");
if (txtRecordEnumeration.hasMore()) {
throw new MongoConfigurationException(format("Multiple TXT records found for host '%s'. Only one is permitted",
host));
}
}
}
} catch (NamingException e) {
throw new MongoConfigurationException("Unable to look up TXT record for host " + host, e);
} finally {
try {
dirContext.close();
} catch (NamingException e) {
// ignore
}
}
return additionalQueryParameters;
}
// It's unfortunate that we take a runtime dependency on com.sun.jndi.dns.DnsContextFactory.
// This is not guaranteed to work on all JVMs but in practice is expected to work on most.
private static InitialDirContext createDnsDirContext() {
Hashtable envProps = new Hashtable();
envProps.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
try {
return new InitialDirContext(envProps);
} catch (NamingException e) {
throw new MongoClientException("Unable to create JNDI context for resolving SRV records. "
+ "The 'com.sun.jndi.dns.DnsContextFactory' class is not available in this JRE", e);
}
}
private DnsResolver() {}
}