From 22dc081ed1f7045b79de385875d551e1af58011c Mon Sep 17 00:00:00 2001 From: genofire Date: Wed, 5 Feb 2020 20:25:36 +0100 Subject: Networkstack: easy happy eyeball (#411) * Networkstack: easy happy eyeball * Networkstack: drop own implementation of DNS-Server selection Co-authored-by: Christian Schneppe --- .../messenger/persistance/DatabaseBackend.java | 55 +----- .../utils/AndroidUsingExecLowPriority.java | 92 --------- .../utils/AndroidUsingLinkProperties.java | 90 --------- .../java/de/pixart/messenger/utils/Resolver.java | 213 ++++++++++++--------- .../de/pixart/messenger/xmpp/XmppConnection.java | 100 ++++------ 5 files changed, 158 insertions(+), 392 deletions(-) delete mode 100644 src/main/java/de/pixart/messenger/utils/AndroidUsingExecLowPriority.java delete mode 100644 src/main/java/de/pixart/messenger/utils/AndroidUsingLinkProperties.java diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java index 9738bb8b3..36d9c945b 100644 --- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java @@ -53,7 +53,6 @@ import de.pixart.messenger.services.ShortcutService; import de.pixart.messenger.utils.CryptoHelper; import de.pixart.messenger.utils.CursorUtils; import de.pixart.messenger.utils.FtsUtils; -import de.pixart.messenger.utils.Resolver; import de.pixart.messenger.xmpp.InvalidJid; import de.pixart.messenger.xmpp.mam.MamReference; import rocks.xmpp.addr.Jid; @@ -149,17 +148,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { + ") ON CONFLICT IGNORE" + ");"; - private static String RESOLVER_RESULTS_TABLENAME = "resolver_results"; - private static String CREATE_RESOLVER_RESULTS_TABLE = "create table " + RESOLVER_RESULTS_TABLENAME + "(" - + Resolver.Result.DOMAIN + " TEXT," - + Resolver.Result.HOSTNAME + " TEXT," - + Resolver.Result.IP + " BLOB," - + Resolver.Result.PRIORITY + " NUMBER," - + Resolver.Result.DIRECT_TLS + " NUMBER," - + Resolver.Result.AUTHENTICATED + " NUMBER," - + Resolver.Result.PORT + " NUMBER," - + "UNIQUE(" + Resolver.Result.DOMAIN + ") ON CONFLICT REPLACE" - + ");"; private static String CREATE_MESSAGE_TIME_INDEX = "create INDEX message_time_index ON " + Message.TABLENAME + "(" + Message.TIME_SENT + ")"; private static String CREATE_MESSAGE_CONVERSATION_INDEX = "create INDEX message_conversation_index ON " + Message.TABLENAME + "(" + Message.CONVERSATION + ")"; private static String CREATE_MESSAGE_DELETED_INDEX = "create index message_deleted_index ON " + Message.TABLENAME + "(" + Message.DELETED + ")"; @@ -258,7 +246,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT); db.execSQL(CREATE_IDENTITIES_STATEMENT); db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT); - db.execSQL(CREATE_RESOLVER_RESULTS_TABLE); db.execSQL(CREATE_MESSAGE_INDEX_TABLE); db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER); db.execSQL(CREATE_MESSAGE_UPDATE_TRIGGER); @@ -528,10 +515,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.MARKABLE + " NUMBER DEFAULT 0"); } - if (oldVersion < 40 && newVersion >= 40) { - db.execSQL(CREATE_RESOLVER_RESULTS_TABLE); - } - if (oldVersion < 42 && newVersion >= 42) { db.execSQL(CREATE_MESSAGE_INDEX_TABLE); db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER); @@ -561,14 +544,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { } } - if (oldVersion < 48 && newVersion >= 48) { - try { - db.execSQL(CREATE_RESOLVER_RESULTS_TABLE); - } catch (Exception e) { - //ignore - } - } - if (oldVersion < 49 && newVersion >= 49) { db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.BODY_LANGUAGE); } @@ -581,6 +556,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { final long diff = SystemClock.elapsedRealtime() - start; Log.d(Config.LOGTAG, "deleted old edit information in " + diff + "ms"); } + + db.execSQL("DROP TABLE IF EXISTS resolver_results"); } private boolean isColumnExisting(SQLiteDatabase db, String TableName, String ColumnName) { @@ -717,34 +694,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { return result; } - public void saveResolverResult(String domain, Resolver.Result result) { - SQLiteDatabase db = this.getWritableDatabase(); - ContentValues contentValues = result.toContentValues(); - contentValues.put(Resolver.Result.DOMAIN, domain); - db.insert(RESOLVER_RESULTS_TABLENAME, null, contentValues); - } - - public synchronized Resolver.Result findResolverResult(String domain) { - SQLiteDatabase db = this.getReadableDatabase(); - String where = Resolver.Result.DOMAIN + "=?"; - String[] whereArgs = {domain}; - final Cursor cursor = db.query(RESOLVER_RESULTS_TABLENAME, null, where, whereArgs, null, null, null); - Resolver.Result result = null; - if (cursor != null) { - try { - if (cursor.moveToFirst()) { - result = Resolver.Result.fromCursor(cursor); - } - } catch (Exception e) { - Log.d(Config.LOGTAG, "unable to find cached resolver result in database " + e.getMessage()); - return null; - } finally { - cursor.close(); - } - } - return result; - } - public void insertPresenceTemplate(PresenceTemplate template) { SQLiteDatabase db = this.getWritableDatabase(); String whereToDelete = PresenceTemplate.MESSAGE + "=?"; diff --git a/src/main/java/de/pixart/messenger/utils/AndroidUsingExecLowPriority.java b/src/main/java/de/pixart/messenger/utils/AndroidUsingExecLowPriority.java deleted file mode 100644 index 6097fcd03..000000000 --- a/src/main/java/de/pixart/messenger/utils/AndroidUsingExecLowPriority.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2015-2016 the original author or authors - * - * This software is licensed under the Apache License, Version 2.0, - * the GNU Lesser General Public License version 2 or later ("LGPL") - * and the WTFPL. - * You may choose either license to govern your use of this software only - * upon the condition that you accept all of the terms of either - * the Apache License 2.0, the LGPL 2.1+ or the WTFPL. - */ - -package de.pixart.messenger.utils; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.LineNumberReader; -import java.net.InetAddress; -import java.util.HashSet; -import java.util.logging.Level; - -import de.measite.minidns.dnsserverlookup.AbstractDNSServerLookupMechanism; -import de.measite.minidns.dnsserverlookup.AndroidUsingReflection; -import de.measite.minidns.dnsserverlookup.DNSServerLookupMechanism; -import de.measite.minidns.util.PlatformDetection; - -/** - * Try to retrieve the list of DNS server by executing getprop. - */ -public class AndroidUsingExecLowPriority extends AbstractDNSServerLookupMechanism { - - public static final DNSServerLookupMechanism INSTANCE = new AndroidUsingExecLowPriority(); - public static final int PRIORITY = AndroidUsingReflection.PRIORITY + 1; - - private AndroidUsingExecLowPriority() { - super(AndroidUsingExecLowPriority.class.getSimpleName(), PRIORITY); - } - - @Override - public String[] getDnsServerAddresses() { - try { - Process process = Runtime.getRuntime().exec("getprop"); - InputStream inputStream = process.getInputStream(); - LineNumberReader lnr = new LineNumberReader( - new InputStreamReader(inputStream)); - String line; - HashSet server = new HashSet<>(6); - while ((line = lnr.readLine()) != null) { - int split = line.indexOf("]: ["); - if (split == -1) { - continue; - } - String property = line.substring(1, split); - String value = line.substring(split + 4, line.length() - 1); - - if (value.isEmpty()) { - continue; - } - - if (property.endsWith(".dns") || property.endsWith(".dns1") || - property.endsWith(".dns2") || property.endsWith(".dns3") || - property.endsWith(".dns4")) { - - // normalize the address - - InetAddress ip = InetAddress.getByName(value); - - if (ip == null) continue; - - value = ip.getHostAddress(); - - if (value == null) continue; - if (value.length() == 0) continue; - - server.add(value); - } - } - if (server.size() > 0) { - return server.toArray(new String[server.size()]); - } - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Exception in findDNSByExec", e); - } - return null; - } - - @Override - public boolean isAvailable() { - return PlatformDetection.isAndroid(); - } - -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/AndroidUsingLinkProperties.java b/src/main/java/de/pixart/messenger/utils/AndroidUsingLinkProperties.java deleted file mode 100644 index f2f33dc1b..000000000 --- a/src/main/java/de/pixart/messenger/utils/AndroidUsingLinkProperties.java +++ /dev/null @@ -1,90 +0,0 @@ -package de.pixart.messenger.utils; - -import android.annotation.TargetApi; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkInfo; -import android.net.RouteInfo; -import android.os.Build; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; - -import de.measite.minidns.dnsserverlookup.AbstractDNSServerLookupMechanism; -import de.measite.minidns.dnsserverlookup.AndroidUsingExec; - -public class AndroidUsingLinkProperties extends AbstractDNSServerLookupMechanism { - - private final Context context; - - AndroidUsingLinkProperties(Context context) { - super(AndroidUsingLinkProperties.class.getSimpleName(), AndroidUsingExec.PRIORITY - 1); - this.context = context; - } - - @Override - public boolean isAvailable() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; - } - - @Override - @TargetApi(21) - public String[] getDnsServerAddresses() { - final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - final Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks(); - if (networks == null) { - return new String[0]; - } - final Network activeNetwork = getActiveNetwork(connectivityManager); - final List servers = new ArrayList<>(); - int vpnOffset = 0; - for (Network network : networks) { - LinkProperties linkProperties = connectivityManager.getLinkProperties(network); - if (linkProperties == null) { - continue; - } - final NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network); - final boolean isActiveNetwork = network.equals(activeNetwork); - final boolean isVpn = networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_VPN; - if (isActiveNetwork && isVpn) { - final List tmp = getIPv4First(linkProperties.getDnsServers()); - servers.addAll(0, tmp); - vpnOffset += tmp.size(); - } else if (hasDefaultRoute(linkProperties) || isActiveNetwork || activeNetwork == null || isVpn) { - servers.addAll(vpnOffset, getIPv4First(linkProperties.getDnsServers())); - } - } - return servers.toArray(new String[0]); - } - - @TargetApi(23) - private static Network getActiveNetwork(ConnectivityManager cm) { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? cm.getActiveNetwork() : null; - } - - private static List getIPv4First(List in) { - List out = new ArrayList<>(); - for (InetAddress address : in) { - if (address instanceof Inet4Address) { - out.add(0, address.getHostAddress()); - } else { - out.add(address.getHostAddress()); - } - } - return out; - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private static boolean hasDefaultRoute(LinkProperties linkProperties) { - for (RouteInfo route : linkProperties.getRoutes()) { - if (route.isDefaultRoute()) { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/Resolver.java b/src/main/java/de/pixart/messenger/utils/Resolver.java index 3fe7e2286..0221b08b7 100644 --- a/src/main/java/de/pixart/messenger/utils/Resolver.java +++ b/src/main/java/de/pixart/messenger/utils/Resolver.java @@ -8,11 +8,17 @@ import androidx.annotation.NonNull; 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; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.List; import de.measite.minidns.AbstractDNSClient; @@ -21,7 +27,6 @@ import de.measite.minidns.DNSName; import de.measite.minidns.Question; import de.measite.minidns.Record; import de.measite.minidns.dnssec.DNSSECResultNotAuthenticException; -import de.measite.minidns.dnsserverlookup.AndroidUsingExec; import de.measite.minidns.hla.DnssecResolverApi; import de.measite.minidns.hla.ResolverApi; import de.measite.minidns.hla.ResolverResult; @@ -34,22 +39,21 @@ import de.measite.minidns.record.InternetAddressRR; import de.measite.minidns.record.SRV; import de.pixart.messenger.Config; import de.pixart.messenger.R; +import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.services.XmppConnectionService; public class Resolver { public static final int DEFAULT_PORT_XMPP = 5222; + 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; public static void init(XmppConnectionService service) { Resolver.SERVICE = service; - DNSClient.removeDNSServerLookupMechanism(AndroidUsingExec.INSTANCE); - DNSClient.addDnsServerLookupMechanism(AndroidUsingExecLowPriority.INSTANCE); - DNSClient.addDnsServerLookupMechanism(new AndroidUsingLinkProperties(service)); final AbstractDNSClient client = ResolverApi.INSTANCE.getClient(); if (client instanceof ReliableDNSClient) { disableHardcodedDnsServers((ReliableDNSClient) client); @@ -72,23 +76,25 @@ public class Resolver { } } - public static List fromHardCoded(String hostname, int port) { - Result result = new Result(); - result.hostname = DNSName.from(hostname); - result.port = port; - result.directTls = useDirectTls(port); - result.authenticated = true; - return Collections.singletonList(result); + public static Result fromHardCoded(String hostname, int port) { + final Result ipResult = fromIpAddress(hostname, port); + if (ipResult != null) { + ipResult.connect(); + return ipResult; + } + return happyEyeball(resolveNoSrvRecords(DNSName.from(hostname), port, true)); } + public static boolean useDirectTls(final int port) { return port == 443 || port == 5223; } - public static List resolve(String domain) { - final List ipResults = fromIpAddress(domain); - if (ipResults.size() > 0) { - return ipResults; + public static Result resolve(String domain) { + final Result ipResult = fromIpAddress(domain, DEFAULT_PORT_XMPP); + if (ipResult != null) { + ipResult.connect(); + return ipResult; } final List results = new ArrayList<>(); final List fallbackResults = new ArrayList<>(); @@ -114,7 +120,7 @@ public class Resolver { } }); threads[2] = new Thread(() -> { - List list = resolveNoSrvRecords(DNSName.from(domain), true); + List list = resolveNoSrvRecords(DNSName.from(domain), DEFAULT_PORT_XMPP, true); synchronized (fallbackResults) { fallbackResults.addAll(list); } @@ -130,40 +136,40 @@ public class Resolver { synchronized (results) { Collections.sort(results); Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": " + results.toString()); - return new ArrayList<>(results); + return happyEyeball(results); } } else { threads[2].join(); synchronized (fallbackResults) { Collections.sort(fallbackResults); Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": " + fallbackResults.toString()); - return new ArrayList<>(fallbackResults); + return happyEyeball(fallbackResults); } } } catch (InterruptedException e) { for (Thread thread : threads) { thread.interrupt(); } - return Collections.emptyList(); + return null; } } - private static List fromIpAddress(String domain) { + private static Result fromIpAddress(String domain, int port) { if (!IP.matches(domain)) { - return Collections.emptyList(); + return null; } try { Result result = new Result(); result.ip = InetAddress.getByName(domain); - result.port = DEFAULT_PORT_XMPP; - return Collections.singletonList(result); + result.port = port; + return result; } catch (UnknownHostException e) { - return Collections.emptyList(); + return null; } } 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<>(); @@ -172,21 +178,15 @@ public class Resolver { 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); - } + final List ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls); synchronized (results) { - results.addAll(ipv4s); + results.addAll(ipv6s); } - })); threads.add(new Thread(() -> { - final List ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls); + final List ipv4s = resolveIp(record, A.class, result.isAuthenticData(), directTls); synchronized (results) { - results.addAll(ipv6s); + results.addAll(ipv4s); } })); } @@ -219,24 +219,23 @@ public class Resolver { return list; } - private static List resolveNoSrvRecords(DNSName dnsName, boolean withCnames) { + private static List resolveNoSrvRecords(DNSName dnsName, int port, 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())); + results.add(Result.createDefault(dnsName, aaaa.getInetAddress(), port)); + } + for (A a : resolveWithFallback(dnsName, A.class, false).getAnswersOrEmptySet()) { + results.add(Result.createDefault(dnsName, a.getInetAddress(), port)); } if (results.size() == 0 && withCnames) { for (CNAME cname : resolveWithFallback(dnsName, CNAME.class, false).getAnswersOrEmptySet()) { - results.addAll(resolveNoSrvRecords(cname.name, false)); + results.addAll(resolveNoSrvRecords(cname.name, port, false)); } } } catch (Throwable throwable) { Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + "error resolving fallback records", throwable); } - results.add(Result.createDefault(dnsName)); return results; } @@ -261,24 +260,64 @@ public class Resolver { return ResolverApi.INSTANCE.resolve(question); } + private static Result happyEyeball(List r) { + if (r.size() == 0) return null; + + Result result; + if (r.size() == 1) { + result = r.get(0); + result.connect(); + return result; + } + + ExecutorService executor = (ExecutorService) Executors.newFixedThreadPool(4); + + try { + result = executor.invokeAny(r); + executor.shutdown(); + Thread disconnector = new Thread(() -> { + while (true) { + try { + if (executor.awaitTermination(5, TimeUnit.SECONDS)) break; + Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": happy eyeball wait for cleanup ..."); + } catch (InterruptedException e) {} + } + Log.i(Config.LOGTAG, Resolver.class.getSimpleName() + ": happy eyeball cleanup"); + for (Result re : r) { + if(!re.equals(result)) re.disconnect(); + } + }); + disconnector.start(); + Log.i(Config.LOGTAG, Resolver.class.getSimpleName() + ": happy eyeball used: " + result.toString()); + return result; + } catch (InterruptedException e) { + Log.e(Config.LOGTAG, Resolver.class.getSimpleName() + ": happy eyeball failed: ", e); + return null; + } catch (ExecutionException e) { + Log.e(Config.LOGTAG, Resolver.class.getSimpleName() + ": happy eyeball failed: ", e); + return null; + } + } + private static boolean validateHostname() { return SERVICE != null && SERVICE.getBooleanPreference("validate_hostname", R.bool.validate_hostname); } - public static class Result implements Comparable { - public static final String DOMAIN = "domain"; + public static class Result implements Comparable, Callable { public static final String IP = "ip"; public static final String HOSTNAME = "hostname"; public static final String PORT = "port"; public static final String PRIORITY = "priority"; public static final String DIRECT_TLS = "directTls"; public static final String AUTHENTICATED = "authenticated"; + private InetAddress ip; private DNSName hostname; private int port = DEFAULT_PORT_XMPP; private boolean directTls = false; private boolean authenticated = false; private int priority; + private Socket socket; static Result fromRecord(SRV srv, boolean directTls) { Result result = new Result(); @@ -288,35 +327,15 @@ public class Resolver { result.priority = srv.priority; return result; } - - static Result createDefault(DNSName hostname, InetAddress ip) { + + static Result createDefault(DNSName hostname, InetAddress ip, int port) { Result result = new Result(); - result.port = DEFAULT_PORT_XMPP; + result.port = port; result.hostname = hostname; result.ip = ip; return result; } - static Result createDefault(DNSName hostname) { - return createDefault(hostname, null); - } - - public static Result fromCursor(Cursor cursor) { - final Result result = new Result(); - try { - result.ip = InetAddress.getByAddress(cursor.getBlob(cursor.getColumnIndex(IP))); - } catch (UnknownHostException e) { - result.ip = null; - } - final String hostname = cursor.getString(cursor.getColumnIndex(HOSTNAME)); - result.hostname = hostname == null ? null : DNSName.from(hostname); - result.port = cursor.getInt(cursor.getColumnIndex(PORT)); - result.priority = cursor.getInt(cursor.getColumnIndex(PRIORITY)); - result.authenticated = cursor.getInt(cursor.getColumnIndex(AUTHENTICATED)) > 0; - result.directTls = cursor.getInt(cursor.getColumnIndex(DIRECT_TLS)) > 0; - return result; - } - @Override public boolean equals(Object o) { if (this == o) return true; @@ -343,14 +362,6 @@ public class Resolver { return result; } - public InetAddress getIp() { - return ip; - } - - public int getPort() { - return port; - } - public DNSName getHostname() { return hostname; } @@ -363,6 +374,10 @@ public class Resolver { return authenticated; } + public Socket getSocket() { + return socket; + } + @Override public String toString() { return "Result{" + @@ -375,21 +390,35 @@ public class Resolver { '}'; } + public void connect() { + if (this.socket != null) { + this.disconnect(); + } + final InetSocketAddress addr = new InetSocketAddress(this.ip, this.port); + this.socket = new Socket(); + try { + long time = System.currentTimeMillis(); + this.socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); + time = System.currentTimeMillis() - time; + Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result connect: " + toString() + " after: " + time + " ms"); + } catch (IOException e) { + this.disconnect(); + } + } + + public void disconnect() { + if (this.socket != null ) { + FileBackend.close(this.socket); + this.socket = null; + Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": Result disconnect: " + toString()); + } + } + @Override public int compareTo(@NonNull Result result) { if (result.priority == priority) { if (directTls == result.directTls) { - 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; - } - } else { - return ip != null ? -1 : 1; - } + return 0; } else { return directTls ? -1 : 1; } @@ -397,6 +426,11 @@ public class Resolver { return priority - result.priority; } } + @Override + public Result call() throws Exception { + this.connect(); + return this.socket.isConnected() ? this : null; + } public ContentValues toContentValues() { final ContentValues contentValues = new ContentValues(); @@ -409,4 +443,5 @@ public class Resolver { return contentValues; } } -} \ No newline at end of file + +} diff --git a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java index b8cddc170..be9fd5a3a 100644 --- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java +++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java @@ -317,86 +317,50 @@ public class XmppConnection implements Runnable { } } else { final String domain = account.getJid().getDomain(); - final List results; + final Resolver.Result result; final boolean hardcoded = extended && !account.getHostname().isEmpty(); if (hardcoded) { - results = Resolver.fromHardCoded(account.getHostname(), account.getPort()); + result = Resolver.fromHardCoded(account.getHostname(), account.getPort()); } else { - results = Resolver.resolve(domain); + result = Resolver.resolve(domain); + } + if (result == null) { + throw new UnknownHostException(); } if (Thread.currentThread().isInterrupted()) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": Thread was interrupted"); return; } - if (results.size() == 0) { - Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": Resolver results were empty"); - return; - } - final Resolver.Result storedBackupResult; - if (hardcoded) { - storedBackupResult = null; - } else { - storedBackupResult = mXmppConnectionService.databaseBackend.findResolverResult(domain); - if (storedBackupResult != null && !results.contains(storedBackupResult)) { - results.add(storedBackupResult); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": loaded backup resolver result from db: " + storedBackupResult); - } - } - for (Iterator iterator = results.iterator(); iterator.hasNext(); ) { - final Resolver.Result result = iterator.next(); - if (Thread.currentThread().isInterrupted()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": Thread was interrupted"); - return; - } - try { - // if tls is true, encryption is implied and must not be started - features.encryptionEnabled = result.isDirectTls(); - verifiedHostname = result.isAuthenticated() ? result.getHostname().toString() : null; - Log.d(Config.LOGTAG, "verified hostname " + verifiedHostname); - final InetSocketAddress addr; - if (result.getIp() != null) { - addr = new InetSocketAddress(result.getIp(), result.getPort()); - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() - + ": using values from resolver " + (result.getHostname() == null ? "" : result.getHostname().toString() - + "/") + result.getIp().getHostAddress() + ":" + result.getPort() + " tls: " + features.encryptionEnabled); - } else { - addr = new InetSocketAddress(IDN.toASCII(result.getHostname().toString()), result.getPort()); - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() - + ": using values from resolver " - + result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled); - } + try { + // if tls is true, encryption is implied and must not be started + features.encryptionEnabled = result.isDirectTls(); + verifiedHostname = result.isAuthenticated() ? result.getHostname().toString() : null; + Log.d(Config.LOGTAG,"verified hostname " + verifiedHostname); + Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + + ": using values from resolver " + result.toString()); - localSocket = new Socket(); - localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000); + localSocket = result.getSocket(); - if (features.encryptionEnabled) { - localSocket = upgradeSocketToTls(localSocket); - } + if (features.encryptionEnabled) { + localSocket = upgradeSocketToTls(localSocket); + } - localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000); - if (startXmpp(localSocket)) { - localSocket.setSoTimeout(0); //reset to 0; once the connection is established we don’t want this - if (!hardcoded && !result.equals(storedBackupResult)) { - mXmppConnectionService.databaseBackend.saveResolverResult(domain, result); - } - break; // successfully connected to server that speaks xmpp - } else { - FileBackend.close(localSocket); - throw new StateChangingException(Account.State.STREAM_OPENING_ERROR); - } - } catch (final StateChangingException e) { - if (!iterator.hasNext()) { - throw e; - } - } catch (InterruptedException e) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": thread was interrupted before beginning stream"); - return; - } catch (final Throwable e) { - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": " + e.getMessage() + "(" + e.getClass().getName() + ")"); - if (!iterator.hasNext()) { - throw new UnknownHostException(); - } + localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000); + if (startXmpp(localSocket)) { + localSocket.setSoTimeout(0); //reset to 0; once the connection is established we don’t want this + // successfully connected to server that speaks xmpp + } else { + FileBackend.close(localSocket); + throw new StateChangingException(Account.State.STREAM_OPENING_ERROR); } + } catch (final StateChangingException e) { + throw e; + } catch (InterruptedException e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": thread was interrupted before beginning stream"); + return; + } catch (final Throwable e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": " + e.getMessage() + "(" + e.getClass().getName() + ")"); + throw new UnknownHostException(); } } processStream(); -- cgit v1.2.3