Look up SRV records without requiring JNDI
TS3DNS resolved SRV records through JNDI's DNS provider, which Android does not have. The JNDI code moves into its own class that is only loaded where JNDI exists; elsewhere a server is reached by its address, as when it publishes no SRV record. TS3DNS.setSrvLookup lets a platform supply its own resolver. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
package com.github.manevolent.ts3j.protocol;
|
||||||
|
|
||||||
|
import javax.naming.NamingException;
|
||||||
|
import javax.naming.directory.Attribute;
|
||||||
|
import javax.naming.directory.Attributes;
|
||||||
|
import javax.naming.directory.InitialDirContext;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SRV lookups through JNDI's DNS provider, which desktop JVMs ship. Kept apart from
|
||||||
|
* {@link TS3DNS} so runtimes without JNDI (Android) never load it.
|
||||||
|
*/
|
||||||
|
final class JndiSrvLookup implements TS3DNS.SrvLookup {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> lookup(String name) throws IOException {
|
||||||
|
Attribute attribute;
|
||||||
|
try {
|
||||||
|
Attributes attributes = new InitialDirContext().getAttributes("dns:/" + name, new String[] { "SRV" });
|
||||||
|
attribute = attributes.get("SRV");
|
||||||
|
} catch (NamingException e) {
|
||||||
|
throw new UnknownHostException("Host not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> records = new ArrayList<>();
|
||||||
|
if (attribute == null) return records;
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < attribute.size(); i++)
|
||||||
|
records.add((String) attribute.get(i));
|
||||||
|
} catch (NamingException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
package com.github.manevolent.ts3j.protocol;
|
package com.github.manevolent.ts3j.protocol;
|
||||||
|
|
||||||
import javax.naming.NamingException;
|
|
||||||
import javax.naming.directory.Attribute;
|
|
||||||
import javax.naming.directory.Attributes;
|
|
||||||
import javax.naming.directory.InitialDirContext;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@@ -18,8 +14,28 @@ public class TS3DNS {
|
|||||||
private static final String DNS_PREFIX_TCP = "_tsdns._tcp.";
|
private static final String DNS_PREFIX_TCP = "_tsdns._tcp.";
|
||||||
private static final String DNS_PREFIX_UDP = "_ts3._udp.";
|
private static final String DNS_PREFIX_UDP = "_ts3._udp.";
|
||||||
|
|
||||||
|
/** Looks up DNS SRV records; see {@link #setSrvLookup}. */
|
||||||
|
public interface SrvLookup {
|
||||||
/**
|
/**
|
||||||
* SRV record, as returned by JNDI: "priority weight port target."
|
* @return each record as "priority weight port target", empty if there are none
|
||||||
|
* @throws UnknownHostException if the name does not exist
|
||||||
|
*/
|
||||||
|
List<String> lookup(String name) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Null until first use, when JNDI's DNS provider becomes the default. */
|
||||||
|
private static volatile SrvLookup srvLookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces how SRV records are looked up. The default goes through JNDI, which not every
|
||||||
|
* runtime has (Android doesn't); without it, servers are reached by address only.
|
||||||
|
*/
|
||||||
|
public static void setSrvLookup(SrvLookup lookup) {
|
||||||
|
srvLookup = lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SRV record, as {@link SrvLookup} returns it: "priority weight port target."
|
||||||
*/
|
*/
|
||||||
private static final class SrvRecord {
|
private static final class SrvRecord {
|
||||||
private final int priority, weight, port;
|
private final int priority, weight, port;
|
||||||
@@ -40,25 +56,12 @@ public class TS3DNS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static List<InetSocketAddress> lookupSRV(String domain) throws IOException {
|
private static List<InetSocketAddress> lookupSRV(String domain) throws IOException {
|
||||||
Attribute attribute;
|
List<String> found = srvLookup().lookup(domain);
|
||||||
|
if (found.isEmpty()) throw new UnknownHostException("Host not found");
|
||||||
|
|
||||||
try {
|
List<SrvRecord> records = new ArrayList<>(found.size());
|
||||||
Attributes attributes = new InitialDirContext().getAttributes("dns:/" + domain, new String[] { "SRV" });
|
for (String record : found)
|
||||||
attribute = attributes.get("SRV");
|
records.add(new SrvRecord(record));
|
||||||
} catch (NamingException e) {
|
|
||||||
throw new UnknownHostException("Host not found: " + domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attribute == null || attribute.size() <= 0) throw new UnknownHostException("Host not found");
|
|
||||||
|
|
||||||
List<SrvRecord> records = new ArrayList<>(attribute.size());
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (int i = 0; i < attribute.size(); i++)
|
|
||||||
records.add(new SrvRecord((String) attribute.get(i)));
|
|
||||||
} catch (NamingException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loose sorting
|
// Loose sorting
|
||||||
records.sort(Comparator.comparingInt((SrvRecord x) -> x.priority).thenComparingInt(x -> x.weight));
|
records.sort(Comparator.comparingInt((SrvRecord x) -> x.priority).thenComparingInt(x -> x.weight));
|
||||||
@@ -70,6 +73,22 @@ public class TS3DNS {
|
|||||||
return addresses;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SrvLookup srvLookup() {
|
||||||
|
SrvLookup lookup = srvLookup;
|
||||||
|
if (lookup != null) return lookup;
|
||||||
|
try {
|
||||||
|
Class.forName("javax.naming.directory.InitialDirContext");
|
||||||
|
lookup = new JndiSrvLookup();
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// No JNDI on this runtime: behave as if no server published an SRV record.
|
||||||
|
lookup = name -> {
|
||||||
|
throw new UnknownHostException("No SRV lookup available for " + name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
srvLookup = lookup;
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<InetSocketAddress> lookup(String domain) throws IOException {
|
public static List<InetSocketAddress> lookup(String domain) throws IOException {
|
||||||
return lookupSRV(DNS_PREFIX_UDP + domain);
|
return lookupSRV(DNS_PREFIX_UDP + domain);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user