package org.jgroups.protocols.dns;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import org.jgroups.Address;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.stack.IpAddress;
class DefaultDNSResolver implements DNSResolver {
private static final Pattern SRV_REGEXP = Pattern.compile("\\d+ \\d+ \\d+ ([\\w+.-]+)\\.");
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
final String dnsContextFactory;
final String dnsAddress;
final DirContext dnsContext;
/**
* @deprecated Implemented for testing.
*/
@Deprecated
DefaultDNSResolver(DirContext direcoryContextForTests) {
this.dnsContext = direcoryContextForTests;
dnsAddress = null;
dnsContextFactory = null;
}
public DefaultDNSResolver(String dnsContextFactory, String dnsAddress) {
this.dnsContextFactory = dnsContextFactory;
this.dnsAddress = dnsAddress;
dnsContext = getDnsContext();
}
private final DirContext getDnsContext() {
try {
log.trace("Initializing DNS Context with factory: %s and url: %s" + dnsContextFactory, dnsAddress);
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial", dnsContextFactory);
env.put("java.naming.provider.url", "dns://" + dnsAddress);
return new InitialDirContext(env);
} catch (NamingException e) {
throw new IllegalStateException("Wrong DNS Context", e);
}
}
@Override
public List<Address> resolveIps(String dnsQuery, DNSRecordType recordType) {
log.trace("Resolving DNS Query: %s of a type: %s", dnsQuery, recordType.toString());
switch (recordType) {
case A:
return resolveAEntries(dnsQuery);
case SRV:
return resolveSRVEntries(dnsQuery);
default:
throw new IllegalStateException("Not implemented");
}
}
private List<Address> resolveSRVEntries(String dnsQuery) {
List<Address> addresses = new ArrayList<>();
try {
// We are parsing this kind of structure:
// {srv=SRV: 10 100 8888 9089f34a.jgroups-dns-ping.myproject.svc.cluster.local.}
// The frst attribute is the type of record. We are not interested in this. Next are addresses.
Attributes attributes = dnsContext.getAttributes(dnsQuery, new String[]{DNSRecordType.SRV.toString()});
if(attributes != null && attributes.getAll().hasMoreElements()) {
NamingEnumeration<?> namingEnumeration = attributes.get(DNSRecordType.SRV.toString()).getAll();
while (namingEnumeration.hasMoreElements()) {
try {
String srvEntry = namingEnumeration.nextElement().toString();
Matcher matcher = SRV_REGEXP.matcher(srvEntry);
if(matcher.find()) {
String srcDNSRecord = matcher.group(1);
// The implementation here is not optimal but it's easy to read. SRV discovery will be performed
// extremely rare only when a fine grained discovery using ports is needed.
addresses.addAll(resolveAEntries(srcDNSRecord));
}
} catch (Exception e) {
log.trace("Non critical DNS resolution error", e);
continue;
}
}
}
} catch (NamingException e) {
log.trace("No DNS records for query: " + dnsQuery);
}
return addresses;
}
private List<Address> resolveAEntries(String dnsQuery) {
List<Address> addresses = new ArrayList<>();
try {
// We are parsing this kind of structure:
// {a=A: 172.17.0.2, 172.17.0.7}
// The frst attribute is the type of record. We are not interested in this. Next are addresses.
Attributes attributes = dnsContext.getAttributes(dnsQuery, new String[]{DNSRecordType.A.toString()});
if(attributes != null && attributes.getAll().hasMoreElements()) {
NamingEnumeration<?> namingEnumeration = attributes.get(DNSRecordType.A.toString()).getAll();
while (namingEnumeration.hasMoreElements()) {
try {
addresses.add(new IpAddress(namingEnumeration.nextElement().toString()));
} catch (Exception e) {
log.trace("Non critical DNS resolution error", e);
continue;
}
}
}
} catch (NamingException e) {
log.trace("No DNS records for query: " + dnsQuery);
}
return addresses;
}
}