From 2e80a15bf4253186ecdbf041d45f0cc3d762d359 Mon Sep 17 00:00:00 2001 From: Martin/Geno Date: Sat, 10 Nov 2018 23:56:09 +0100 Subject: [PATCH] Search for best connection To fix Connection for IPv6, use a kind of Round Trip Time to implement something like Happy Eyeballs --- .../siacs/conversations/utils/Resolver.java | 67 +++++++++++++------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/Resolver.java b/src/main/java/eu/siacs/conversations/utils/Resolver.java index 1b8e28374..2a8ad8c3b 100644 --- a/src/main/java/eu/siacs/conversations/utils/Resolver.java +++ b/src/main/java/eu/siacs/conversations/utils/Resolver.java @@ -7,8 +7,9 @@ import android.util.Log; import java.io.IOException; import java.lang.reflect.Field; -import java.net.Inet4Address; import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; @@ -38,7 +39,7 @@ import eu.siacs.conversations.services.XmppConnectionService; public class Resolver { 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; @@ -156,7 +157,7 @@ public class Resolver { } private static List 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 result = resolveWithFallback(dnsName, SRV.class); final List results = new ArrayList<>(); final List threads = new ArrayList<>(); @@ -164,24 +165,19 @@ public class Resolver { if (record.name.length() == 0 && record.priority == 0) { continue; } - threads.add(new Thread(() -> { - final List 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(() -> { final List ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls); synchronized (results) { results.addAll(ipv6s); } })); + threads.add(new Thread(() -> { + final List ipv4s = resolveIp(record, A.class, result.isAuthenticData(), directTls); + synchronized (results) { + results.addAll(ipv4s); + } + + })); } for (Thread thread : threads) { thread.start(); @@ -189,6 +185,16 @@ public class Resolver { for (Thread thread : threads) { try { 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) { return Collections.emptyList(); } @@ -204,6 +210,7 @@ public class Resolver { Result resolverResult = Result.fromRecord(srv, directTls); resolverResult.authenticated = results.isAuthenticData() && authenticated; resolverResult.ip = record.getInetAddress(); + resolverResult.rtt = rttTo(resolverResult.ip,resolverResult.port); list.add(resolverResult); } } catch (Throwable t) { @@ -215,12 +222,12 @@ public class Resolver { private static List resolveNoSrvRecords(DNSName dnsName, boolean withCnames) { List results = new ArrayList<>(); 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()) { 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) { for (CNAME cname : resolveWithFallback(dnsName, CNAME.class, false).getAnswersOrEmptySet()) { results.addAll(resolveNoSrvRecords(cname.name, false)); @@ -254,6 +261,18 @@ public class Resolver { 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() { return SERVICE != null && SERVICE.getBooleanPreference("validate_hostname", R.bool.validate_hostname); } @@ -272,6 +291,7 @@ public class Resolver { private boolean directTls = false; private boolean authenticated = false; private int priority; + private long rtt = -1; static Result fromRecord(SRV srv, boolean directTls) { Result result = new Result(); @@ -287,6 +307,9 @@ public class Resolver { result.port = 5222; result.hostname = hostname; result.ip = ip; + if (ip != null) { + result.rtt = rttTo(result.ip, result.port); + } return result; } @@ -365,6 +388,7 @@ public class Resolver { ", directTls=" + directTls + ", authenticated=" + authenticated + ", priority=" + priority + + ", rtt=" + rtt + '}'; } @@ -375,11 +399,10 @@ public class Resolver { if (ip == null && result.ip == null) { return 0; } else if (ip != null && result.ip != null) { - if (ip instanceof Inet4Address && result.ip instanceof Inet4Address) { - return 0; - } else { - return ip instanceof Inet4Address ? -1 : 1; + if (rtt == -1 || result.rtt == -1) { + Log.e(Config.LOGTAG, Resolver.class.getSimpleName() + ": unable to read round trip time servers on compare between "+toString()+ " and "+result.toString()); } + return rtt < result.rtt ? -1 : 1; } else { return ip != null ? -1 : 1; }