Search for best connection

To fix Connection for IPv6, use a kind of Round Trip Time to implement something like Happy Eyeballs
This commit is contained in:
Martin/Geno 2018-11-10 23:56:09 +01:00
parent 56c8e569af
commit ee0a212bfd
No known key found for this signature in database
GPG Key ID: 9D7D3C6BFF600C6A
1 changed files with 45 additions and 22 deletions

View File

@ -7,8 +7,9 @@ import android.util.Log;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -38,7 +39,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
public class Resolver { public class Resolver {
private static final String DIRECT_TLS_SERVICE = "_xmpps-client"; private static final String DIRECT_TLS_SERVICE = "_xmpps-client";
private static final String STARTTLS_SERICE = "_xmpp-client"; private static final String STARTTLS_SERVICE = "_xmpp-client";
private static XmppConnectionService SERVICE = null; private static XmppConnectionService SERVICE = null;
@ -156,7 +157,7 @@ public class Resolver {
} }
private static List<Result> resolveSrv(String domain, final boolean directTls) throws IOException { private static List<Result> resolveSrv(String domain, final boolean directTls) throws IOException {
DNSName dnsName = DNSName.from((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERICE) + "._tcp." + domain); DNSName dnsName = DNSName.from((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERVICE) + "._tcp." + domain);
ResolverResult<SRV> result = resolveWithFallback(dnsName, SRV.class); ResolverResult<SRV> result = resolveWithFallback(dnsName, SRV.class);
final List<Result> results = new ArrayList<>(); final List<Result> results = new ArrayList<>();
final List<Thread> threads = new ArrayList<>(); final List<Thread> threads = new ArrayList<>();
@ -164,24 +165,19 @@ public class Resolver {
if (record.name.length() == 0 && record.priority == 0) { if (record.name.length() == 0 && record.priority == 0) {
continue; continue;
} }
threads.add(new Thread(() -> {
final List<Result> ipv4s = resolveIp(record, A.class, result.isAuthenticData(), directTls);
if (ipv4s.size() == 0) {
Result resolverResult = Result.fromRecord(record, directTls);
resolverResult.authenticated = resolverResult.isAuthenticated();
ipv4s.add(resolverResult);
}
synchronized (results) {
results.addAll(ipv4s);
}
}));
threads.add(new Thread(() -> { threads.add(new Thread(() -> {
final List<Result> ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls); final List<Result> ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls);
synchronized (results) { synchronized (results) {
results.addAll(ipv6s); results.addAll(ipv6s);
} }
})); }));
threads.add(new Thread(() -> {
final List<Result> ipv4s = resolveIp(record, A.class, result.isAuthenticData(), directTls);
synchronized (results) {
results.addAll(ipv4s);
}
}));
} }
for (Thread thread : threads) { for (Thread thread : threads) {
thread.start(); thread.start();
@ -189,6 +185,16 @@ public class Resolver {
for (Thread thread : threads) { for (Thread thread : threads) {
try { try {
thread.join(); thread.join();
if (results.size() == 0) {
for (SRV record : result.getAnswersOrEmptySet()) {
if (record.name.length() == 0 && record.priority == 0) {
continue;
}
Result resolverResult = Result.fromRecord(record, directTls);
resolverResult.authenticated = resolverResult.isAuthenticated();
results.add(resolverResult);
}
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -204,6 +210,7 @@ public class Resolver {
Result resolverResult = Result.fromRecord(srv, directTls); Result resolverResult = Result.fromRecord(srv, directTls);
resolverResult.authenticated = results.isAuthenticData() && authenticated; resolverResult.authenticated = results.isAuthenticData() && authenticated;
resolverResult.ip = record.getInetAddress(); resolverResult.ip = record.getInetAddress();
resolverResult.rtt = rttTo(resolverResult.ip,resolverResult.port);
list.add(resolverResult); list.add(resolverResult);
} }
} catch (Throwable t) { } catch (Throwable t) {
@ -215,12 +222,12 @@ public class Resolver {
private static List<Result> resolveNoSrvRecords(DNSName dnsName, boolean withCnames) { private static List<Result> resolveNoSrvRecords(DNSName dnsName, boolean withCnames) {
List<Result> results = new ArrayList<>(); List<Result> results = new ArrayList<>();
try { try {
for (A a : resolveWithFallback(dnsName, A.class, false).getAnswersOrEmptySet()) {
results.add(Result.createDefault(dnsName, a.getInetAddress()));
}
for (AAAA aaaa : resolveWithFallback(dnsName, AAAA.class, false).getAnswersOrEmptySet()) { for (AAAA aaaa : resolveWithFallback(dnsName, AAAA.class, false).getAnswersOrEmptySet()) {
results.add(Result.createDefault(dnsName, aaaa.getInetAddress())); results.add(Result.createDefault(dnsName, aaaa.getInetAddress()));
} }
for (A a : resolveWithFallback(dnsName, A.class, false).getAnswersOrEmptySet()) {
results.add(Result.createDefault(dnsName, a.getInetAddress()));
}
if (results.size() == 0 && withCnames) { if (results.size() == 0 && withCnames) {
for (CNAME cname : resolveWithFallback(dnsName, CNAME.class, false).getAnswersOrEmptySet()) { for (CNAME cname : resolveWithFallback(dnsName, CNAME.class, false).getAnswersOrEmptySet()) {
results.addAll(resolveNoSrvRecords(cname.name, false)); results.addAll(resolveNoSrvRecords(cname.name, false));
@ -254,6 +261,18 @@ public class Resolver {
return ResolverApi.INSTANCE.resolve(question); return ResolverApi.INSTANCE.resolve(question);
} }
private static long rttTo(InetAddress ip, int port){
long time = System.currentTimeMillis();
try (Socket s = new Socket()) {
s.connect(new InetSocketAddress(ip, port), 200);
s.close();
return System.currentTimeMillis() - time;
}catch (Exception e){
Log.e(Config.LOGTAG, Resolver.class.getSimpleName() + ": error testing connection to " + (ip == null ? null : ip.getHostAddress()) + ":" + port);
return -1;
}
}
private static boolean validateHostname() { private static boolean validateHostname() {
return SERVICE != null && SERVICE.getBooleanPreference("validate_hostname", R.bool.validate_hostname); return SERVICE != null && SERVICE.getBooleanPreference("validate_hostname", R.bool.validate_hostname);
} }
@ -272,6 +291,7 @@ public class Resolver {
private boolean directTls = false; private boolean directTls = false;
private boolean authenticated = false; private boolean authenticated = false;
private int priority; private int priority;
private long rtt = -1;
static Result fromRecord(SRV srv, boolean directTls) { static Result fromRecord(SRV srv, boolean directTls) {
Result result = new Result(); Result result = new Result();
@ -287,6 +307,9 @@ public class Resolver {
result.port = 5222; result.port = 5222;
result.hostname = hostname; result.hostname = hostname;
result.ip = ip; result.ip = ip;
if (ip != null) {
result.rtt = rttTo(result.ip, result.port);
}
return result; return result;
} }
@ -365,6 +388,7 @@ public class Resolver {
", directTls=" + directTls + ", directTls=" + directTls +
", authenticated=" + authenticated + ", authenticated=" + authenticated +
", priority=" + priority + ", priority=" + priority +
", rtt=" + rtt +
'}'; '}';
} }
@ -375,11 +399,10 @@ public class Resolver {
if (ip == null && result.ip == null) { if (ip == null && result.ip == null) {
return 0; return 0;
} else if (ip != null && result.ip != null) { } else if (ip != null && result.ip != null) {
if (ip instanceof Inet4Address && result.ip instanceof Inet4Address) { if (rtt == -1 || result.rtt == -1) {
return 0; Log.e(Config.LOGTAG, Resolver.class.getSimpleName() + ": unable to read round trip time servers on compare between "+toString()+ " and "+result.toString());
} else {
return ip instanceof Inet4Address ? -1 : 1;
} }
return rtt < result.rtt ? -1 : 1;
} else { } else {
return ip != null ? -1 : 1; return ip != null ? -1 : 1;
} }