diff options
5 files changed, 148 insertions, 112 deletions
diff --git a/src/main/java/de/pixart/messenger/entities/Account.java b/src/main/java/de/pixart/messenger/entities/Account.java index 46d036419..6fd4c1c05 100644 --- a/src/main/java/de/pixart/messenger/entities/Account.java +++ b/src/main/java/de/pixart/messenger/entities/Account.java @@ -139,8 +139,7 @@ public class Account extends AbstractEntity { STREAM_ERROR, POLICY_VIOLATION, PAYMENT_REQUIRED, - MISSING_INTERNET_PERMISSION(false), - NETWORK_IS_UNREACHABLE(false); + MISSING_INTERNET_PERMISSION(false); private final boolean isError; private final boolean attemptReconnect; @@ -218,8 +217,6 @@ public class Account extends AbstractEntity { return R.string.payment_required; case MISSING_INTERNET_PERMISSION: return R.string.missing_internet_permission; - case NETWORK_IS_UNREACHABLE: - return R.string.network_is_unreachable; default: return R.string.account_status_unknown; } diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 6b957a603..b6d23668c 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -4216,7 +4216,7 @@ public class XmppConnectionService extends Service { ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response); if (presence.getVer().equals(discoveryResult.getVer())) { databaseBackend.insertDiscoveryResult(discoveryResult); - injectServiceDiscorveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult); + injectServiceDiscoveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult); } else { Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer()); } @@ -4227,7 +4227,7 @@ public class XmppConnectionService extends Service { } } - private void injectServiceDiscorveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) { + private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) { for (Contact contact : roster.getContacts()) { for (Presence presence : contact.getPresences().getPresences().values()) { if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) { diff --git a/src/main/java/de/pixart/messenger/utils/Resolver.java b/src/main/java/de/pixart/messenger/utils/Resolver.java index 8f8138cb3..4bae6fae7 100644 --- a/src/main/java/de/pixart/messenger/utils/Resolver.java +++ b/src/main/java/de/pixart/messenger/utils/Resolver.java @@ -6,14 +6,15 @@ import android.support.annotation.NonNull; import android.util.Log; import java.io.IOException; +import java.lang.reflect.Field; import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; +import de.measite.minidns.AbstractDNSClient; import de.measite.minidns.DNSClient; import de.measite.minidns.DNSName; import de.measite.minidns.Question; @@ -23,13 +24,13 @@ import de.measite.minidns.dnsserverlookup.AndroidUsingExec; import de.measite.minidns.hla.DnssecResolverApi; import de.measite.minidns.hla.ResolverApi; import de.measite.minidns.hla.ResolverResult; +import de.measite.minidns.iterative.ReliableDNSClient; import de.measite.minidns.record.A; import de.measite.minidns.record.AAAA; import de.measite.minidns.record.CNAME; import de.measite.minidns.record.Data; import de.measite.minidns.record.InternetAddressRR; import de.measite.minidns.record.SRV; -import de.measite.minidns.util.MultipleIoException; import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.services.XmppConnectionService; @@ -39,8 +40,6 @@ public class Resolver { private static final String DIRECT_TLS_SERVICE = "_xmpps-client"; private static final String STARTTLS_SERICE = "_xmpp-client"; - private static final String NETWORK_IS_UNREACHABLE = "Network is unreachable"; - private static XmppConnectionService SERVICE = null; @@ -49,74 +48,122 @@ public class Resolver { 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); + } } - public static List<Result> resolve(String domain) throws NetworkIsUnreachableException { - List<Result> results = new ArrayList<>(); - HashSet<String> messages = new HashSet<>(); - try { - results.addAll(resolveSrv(domain, true)); - } catch (MultipleIoException e) { - messages.addAll(extractMessages(e)); - } catch (Throwable throwable) { - Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving SRV record (direct TLS)", throwable); - } + private static void disableHardcodedDnsServers(ReliableDNSClient reliableDNSClient) { try { - results.addAll(resolveSrv(domain, false)); - } catch (MultipleIoException e) { - messages.addAll(extractMessages(e)); - } catch (Throwable throwable) { - Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving SRV record (STARTTLS)", throwable); - } - if (results.size() == 0) { - if (messages.size() == 1 && messages.contains(NETWORK_IS_UNREACHABLE)) { - throw new NetworkIsUnreachableException(); - } - results.addAll(resolveNoSrvRecords(DNSName.from(domain), true)); + final Field dnsClientField = ReliableDNSClient.class.getDeclaredField("dnsClient"); + dnsClientField.setAccessible(true); + final DNSClient dnsClient = (DNSClient) dnsClientField.get(reliableDNSClient); + dnsClient.getDataSource().setTimeout(3000); + final Field useHardcodedDnsServers = DNSClient.class.getDeclaredField("useHardcodedDnsServers"); + useHardcodedDnsServers.setAccessible(true); + useHardcodedDnsServers.setBoolean(dnsClient, false); + } catch (NoSuchFieldException e) { + Log.e(Config.LOGTAG, "Unable to disable hardcoded DNS servers", e); + } catch (IllegalAccessException e) { + Log.e(Config.LOGTAG, "Unable to disable hardcoded DNS servers", e); } - Collections.sort(results); - Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": " + results.toString()); - return results; } - private static HashSet<String> extractMessages(MultipleIoException e) { - HashSet<String> messages = new HashSet<>(); - for (Exception inner : e.getExceptions()) { - if (inner instanceof MultipleIoException) { - messages.addAll(extractMessages((MultipleIoException) inner)); + public static List<Result> resolve(String domain) { + final List<Result> results = new ArrayList<>(); + final List<Result> fallbackResults = new ArrayList<>(); + Thread[] threads = new Thread[3]; + threads[0] = new Thread(() -> { + try { + final List<Result> list = resolveSrv(domain, true); + synchronized (results) { + results.addAll(list); + } + } catch (Throwable throwable) { + Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving SRV record (direct TLS)", throwable); + } + }); + threads[1] = new Thread(() -> { + try { + final List<Result> list = resolveSrv(domain, false); + synchronized (results) { + results.addAll(list); + } + } catch (Throwable throwable) { + Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving SRV record (STARTTLS)", throwable); + } + }); + threads[2] = new Thread(() -> { + List<Result> list = resolveNoSrvRecords(DNSName.from(domain), true); + synchronized (fallbackResults) { + fallbackResults.addAll(list); + } + }); + for (Thread thread : threads) { + thread.start(); + } + try { + threads[0].join(); + threads[1].join(); + if (results.size() > 0) { + threads[2].interrupt(); + Collections.sort(results); + Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": " + results.toString()); + return results; } else { - messages.add(inner.getMessage()); + threads[2].join(); + Collections.sort(fallbackResults); + Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": " + fallbackResults.toString()); + return fallbackResults; } + } catch (InterruptedException e) { + return results; } - return messages; } private static List<Result> resolveSrv(String domain, final boolean directTls) throws IOException { - if (Thread.currentThread().isInterrupted()) { - return Collections.emptyList(); - } DNSName dnsName = DNSName.from((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERICE) + "._tcp." + domain); ResolverResult<SRV> result = resolveWithFallback(dnsName, SRV.class); - List<Result> results = new ArrayList<>(); + final List<Result> results = new ArrayList<>(); + final List<Thread> threads = new ArrayList<>(); for (SRV record : result.getAnswersOrEmptySet()) { if (record.name.length() == 0 && record.priority == 0) { continue; } - final boolean addedIPv4 = results.addAll(resolveIp(record, A.class, result.isAuthenticData(), directTls)); - results.addAll(resolveIp(record, AAAA.class, result.isAuthenticData(), directTls)); - if (!addedIPv4 && !Thread.currentThread().isInterrupted()) { - Result resolverResult = Result.fromRecord(record, directTls); - resolverResult.authenticated = resolverResult.isAuthenticated(); - results.add(resolverResult); + 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(() -> { + final List<Result> ipv6s = resolveIp(record, AAAA.class, result.isAuthenticData(), directTls); + synchronized (results) { + results.addAll(ipv6s); + } + })); + } + for (Thread thread : threads) { + thread.start(); + } + for (Thread thread : threads) { + try { + thread.join(); + } catch (InterruptedException e) { + return Collections.emptyList(); } } return results; } private static <D extends InternetAddressRR> List<Result> resolveIp(SRV srv, Class<D> type, boolean authenticated, boolean directTls) { - if (Thread.currentThread().isInterrupted()) { - return Collections.emptyList(); - } List<Result> list = new ArrayList<>(); try { ResolverResult<D> results = resolveWithFallback(srv.name, type, authenticated); @@ -179,6 +226,56 @@ public class Resolver { } public static class Result implements Comparable<Result> { + public static final String DOMAIN = "domain"; + 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 = 5222; + private boolean directTls = false; + private boolean authenticated = false; + private int priority; + + static Result fromRecord(SRV srv, boolean directTls) { + Result result = new Result(); + result.port = srv.port; + result.hostname = srv.name; + result.directTls = directTls; + result.priority = srv.priority; + return result; + } + + static Result createDefault(DNSName hostname, InetAddress ip) { + Result result = new Result(); + result.port = 5222; + 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; + } + result.hostname = DNSName.from(cursor.getString(cursor.getColumnIndex(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; @@ -205,22 +302,6 @@ public class Resolver { return result; } - public static final String DOMAIN = "domain"; - - 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 = 5222; - private boolean directTls = false; - private boolean authenticated = false; - private int priority; - public InetAddress getIp() { return ip; } @@ -276,42 +357,6 @@ public class Resolver { } } - public static Result fromRecord(SRV srv, boolean directTls) { - Result result = new Result(); - result.port = srv.port; - result.hostname = srv.name; - result.directTls = directTls; - result.priority = srv.priority; - return result; - } - - public static Result createDefault(DNSName hostname, InetAddress ip) { - Result result = new Result(); - result.port = 5222; - result.hostname = hostname; - result.ip = ip; - return result; - } - - public 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; - } - result.hostname = DNSName.from(cursor.getString(cursor.getColumnIndex(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; - } - public ContentValues toContentValues() { final ContentValues contentValues = new ContentValues(); contentValues.put(IP, ip == null ? null : ip.getAddress()); @@ -324,7 +369,4 @@ public class Resolver { } } - public static class NetworkIsUnreachableException extends Exception { - - } }
\ 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 1def41694..cbfdbc365 100644 --- a/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java +++ b/src/main/java/de/pixart/messenger/xmpp/XmppConnection.java @@ -442,8 +442,6 @@ public class XmppConnection implements Runnable { this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION); } catch (final StateChangingException e) { this.changeStatus(e.state); - } catch (final Resolver.NetworkIsUnreachableException e) { - this.changeStatus(Account.State.NETWORK_IS_UNREACHABLE); } catch (final UnknownHostException | ConnectException e) { this.changeStatus(Account.State.SERVER_NOT_FOUND); } catch (final SocksSocketFactory.SocksProxyNotFoundException e) { diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 43794ee03..26a8a7112 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -699,7 +699,6 @@ <string name="yesterday">Yesterday</string> <string name="pref_validate_hostname">Validate hostname with DNSSEC</string> <string name="pref_validate_hostname_summary">Server certificates that contain the validated hostname are considered verified</string> - <string name="network_is_unreachable">Network is unreachable</string> <string name="certificate_does_not_contain_jid">Certificate does not contain a Jabber ID</string> <string name="error_no_keys_to_trust_presence">There are no usable keys available for this contact.\nMake sure you have mutual presence subscription.</string> <string name="mark_as_read">Mark as read</string> |