aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgenofire <geno+dev@fireorbit.de>2020-02-05 20:25:36 +0100
committerGitHub <noreply@github.com>2020-02-05 20:25:36 +0100
commit22dc081ed1f7045b79de385875d551e1af58011c (patch)
treec15bf4c9f1ae17c807c9b9ed68a45ca360e23c36
parent1e5ebaa19c427adb68f3efa52eb5a9b521064132 (diff)
Networkstack: easy happy eyeball (#411)
* Networkstack: easy happy eyeball * Networkstack: drop own implementation of DNS-Server selection Co-authored-by: Christian Schneppe <kriztan@users.noreply.github.com>
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java55
-rw-r--r--src/main/java/de/pixart/messenger/utils/AndroidUsingExecLowPriority.java92
-rw-r--r--src/main/java/de/pixart/messenger/utils/AndroidUsingLinkProperties.java90
-rw-r--r--src/main/java/de/pixart/messenger/utils/Resolver.java213
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/XmppConnection.java100
5 files changed, 158 insertions, 392 deletions
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<String> 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<String> 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<String> 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<String> getIPv4First(List<InetAddress> in) {
- List<String> 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<Result> 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<Result> resolve(String domain) {
- final List<Result> 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<Result> results = new ArrayList<>();
final List<Result> fallbackResults = new ArrayList<>();
@@ -114,7 +120,7 @@ public class Resolver {
}
});
threads[2] = new Thread(() -> {
- List<Result> list = resolveNoSrvRecords(DNSName.from(domain), true);
+ List<Result> 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<Result> 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<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);
final List<Result> results = new ArrayList<>();
final List<Thread> threads = new ArrayList<>();
@@ -172,21 +178,15 @@ public class Resolver {
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);
- }
+ final List<Result> ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls);
synchronized (results) {
- results.addAll(ipv4s);
+ results.addAll(ipv6s);
}
-
}));
threads.add(new Thread(() -> {
- final List<Result> ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls);
+ final List<Result> 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<Result> resolveNoSrvRecords(DNSName dnsName, boolean withCnames) {
+ private static List<Result> resolveNoSrvRecords(DNSName dnsName, int port, boolean withCnames) {
List<Result> 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<Result> 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<Result> {
- public static final String DOMAIN = "domain";
+ public static class Result implements Comparable<Result>, Callable<Result> {
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<Resolver.Result> 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<Resolver.Result> 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();