upgrade minidns. refactored dns utils

Daniel Gultsch created

Change summary

build.gradle                                                               |   2 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java   |   2 
src/main/java/eu/siacs/conversations/utils/AndroidUsingLinkProperties.java |  76 
src/main/java/eu/siacs/conversations/utils/DNSHelper.java                  | 299 
src/main/java/eu/siacs/conversations/utils/IP.java                         |  22 
src/main/java/eu/siacs/conversations/utils/Resolver.java                   | 163 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java              |  39 
7 files changed, 278 insertions(+), 325 deletions(-)

Detailed changes

build.gradle 🔗

@@ -33,7 +33,7 @@ dependencies {
     compile 'org.gnu.inet:libidn:1.15'
     compile 'com.google.zxing:core:3.2.1'
     compile 'com.google.zxing:android-integration:3.2.1'
-    compile 'de.measite.minidns:minidns:0.1.7'
+    compile 'de.measite.minidns:minidns-hla:0.2.1'
     compile 'de.timroes.android:EnhancedListView:0.3.4'
     compile 'me.leolin:ShortcutBadger:1.1.12@aar'
     compile 'com.kyleduo.switchbutton:library:1.2.8'

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -114,6 +114,7 @@ import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
 import eu.siacs.conversations.utils.PRNGFixes;
 import eu.siacs.conversations.utils.PhoneHelper;
 import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
+import eu.siacs.conversations.utils.Resolver;
 import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
 import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.utils.XmppUri;
@@ -983,6 +984,7 @@ public class XmppConnectionService extends Service {
 	public void onCreate() {
 		ExceptionHelper.init(getApplicationContext());
 		PRNGFixes.apply();
+		Resolver.registerLookupMechanism(this);
 		this.mRandom = new SecureRandom();
 		updateMemorizingTrustmanager();
 		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

src/main/java/eu/siacs/conversations/utils/AndroidUsingLinkProperties.java 🔗

@@ -0,0 +1,76 @@
+package eu.siacs.conversations.utils;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+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;
+
+    protected 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() {
+        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
+        if (networks == null) {
+            return new String[0];
+        }
+        List<String> servers = new ArrayList<>();
+        for(Network network : networks) {
+            LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
+            if (linkProperties != null) {
+                if (hasDefaultRoute(linkProperties)) {
+                    servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
+                } else {
+                    servers.addAll(getIPv4First(linkProperties.getDnsServers()));
+                }
+            }
+        }
+        return servers.toArray(new String[servers.size()]);
+    }
+
+    private static List<String> getIPv4First(List<InetAddress> in) {
+        List<String> out = new ArrayList<>();
+        for(InetAddress addr : in) {
+            if (addr instanceof Inet4Address) {
+                out.add(0, addr.toString());
+            } else {
+                out.add(addr.toString());
+            }
+        }
+        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;
+    }
+}

src/main/java/eu/siacs/conversations/utils/DNSHelper.java 🔗

@@ -1,299 +0,0 @@
-package eu.siacs.conversations.utils;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.RouteInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Random;
-import java.util.TreeMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-import de.measite.minidns.Client;
-import de.measite.minidns.DNSMessage;
-import de.measite.minidns.Record;
-import de.measite.minidns.Record.CLASS;
-import de.measite.minidns.Record.TYPE;
-import de.measite.minidns.record.A;
-import de.measite.minidns.record.AAAA;
-import de.measite.minidns.record.Data;
-import de.measite.minidns.record.SRV;
-import de.measite.minidns.util.NameUtil;
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.xmpp.jid.Jid;
-
-public class DNSHelper {
-
-	public static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
-	public static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
-	public static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
-	public static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
-	public static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
-
-	protected static Client client = new Client();
-
-	protected static Context context;
-
-	public static Bundle getSRVRecord(final Jid jid, Context context) throws IOException {
-		DNSHelper.context = context;
-        final String host = jid.getDomainpart();
-		final List<InetAddress> servers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers(context) : getDnsServersPreLollipop();
-		Bundle b = new Bundle();
-		boolean interrupted = false;
-		for(InetAddress server : servers) {
-			if (Thread.currentThread().isInterrupted()) {
-				interrupted = true;
-				break;
-			}
-			b = queryDNS(host, server);
-			if (b.containsKey("values")) {
-				return b;
-			}
-		}
-		if (!b.containsKey("values")) {
-			Log.d(Config.LOGTAG,(interrupted ? "Thread interrupted during DNS query" :"all dns queries failed") + ". provide fallback A record");
-			ArrayList<Parcelable> values = new ArrayList<>();
-			values.add(createNamePortBundle(host, 5222, false));
-			b.putParcelableArrayList("values",values);
-		}
-		return b;
-	}
-
-	@TargetApi(21)
-	private static List<InetAddress> getDnsServers(Context context) {
-		List<InetAddress> servers = new ArrayList<>();
-		ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-		Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
-		if (networks == null) {
-			return getDnsServersPreLollipop();
-		}
-		for(int i = 0; i < networks.length; ++i) {
-			LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
-			if (linkProperties != null) {
-				if (hasDefaultRoute(linkProperties)) {
-					servers.addAll(0, getIPv4First(linkProperties.getDnsServers()));
-				} else {
-					servers.addAll(getIPv4First(linkProperties.getDnsServers()));
-				}
-			}
-		}
-		if (servers.size() > 0) {
-			Log.d(Config.LOGTAG, "used lollipop variant to discover dns servers in " + networks.length + " networks");
-		}
-		return servers.size() > 0 ? servers : getDnsServersPreLollipop();
-	}
-
-	private static List<InetAddress> getIPv4First(List<InetAddress> in) {
-		List<InetAddress> out = new ArrayList<>();
-		for(InetAddress addr : in) {
-			if (addr instanceof Inet4Address) {
-				out.add(0, addr);
-			} else {
-				out.add(addr);
-			}
-		}
-		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;
-	}
-
-	private static List<InetAddress> getDnsServersPreLollipop() {
-		List<InetAddress> servers = new ArrayList<>();
-		String[] dns = client.findDNS();
-		for(int i = 0; i < dns.length; ++i) {
-			try {
-				servers.add(InetAddress.getByName(dns[i]));
-			} catch (UnknownHostException e) {
-				//ignore
-			}
-		}
-		return servers;
-	}
-
-	private static class TlsSrv {
-		private final SRV srv;
-		private final boolean tls;
-
-		public TlsSrv(SRV srv, boolean tls) {
-			this.srv = srv;
-			this.tls = tls;
-		}
-	}
-
-	private static void fillSrvMaps(final String qname, final InetAddress dnsServer, final Map<Integer, List<TlsSrv>> priorities, final Map<String, List<String>> ips4, final Map<String, List<String>> ips6, final boolean tls) throws IOException {
-		final DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
-		for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) {
-			for (Record rr : rrset) {
-				Data d = rr.getPayload();
-				final String name = rr.getName() != null ? rr.getName().toLowerCase(Locale.US) : null;
-				if (d instanceof SRV && NameUtil.idnEquals(qname, name)) {
-					SRV srv = (SRV) d;
-					if (!priorities.containsKey(srv.getPriority())) {
-						priorities.put(srv.getPriority(),new ArrayList<TlsSrv>());
-					}
-					priorities.get(srv.getPriority()).add(new TlsSrv(srv, tls));
-				} else if (d instanceof SRV) {
-					Log.d(Config.LOGTAG,"found unrecognized SRV record with name: "+name);
-				}
-				if (d instanceof A) {
-					A a = (A) d;
-					if (!ips4.containsKey(name)) {
-						ips4.put(name, new ArrayList<String>());
-					}
-					ips4.get(name).add(a.toString());
-				}
-				if (d instanceof AAAA) {
-					AAAA aaaa = (AAAA) d;
-					if (!ips6.containsKey(name)) {
-						ips6.put(name, new ArrayList<String>());
-					}
-					ips6.get(name).add("[" + aaaa.toString() + "]");
-				}
-			}
-		}
-	}
-
-	public static Bundle queryDNS(String host, InetAddress dnsServer) {
-		Bundle bundle = new Bundle();
-		try {
-			client.setTimeout(Config.SOCKET_TIMEOUT * 1000);
-			final String qname = "_xmpp-client._tcp." + host.toLowerCase(Locale.US);
-			final String tlsQname = "_xmpps-client._tcp." + host.toLowerCase(Locale.US);
-			Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
-
-			final Map<Integer, List<TlsSrv>> priorities = new TreeMap<>();
-			final Map<String, List<String>> ips4 = new TreeMap<>();
-			final Map<String, List<String>> ips6 = new TreeMap<>();
-
-			fillSrvMaps(qname, dnsServer, priorities, ips4, ips6, false);
-			fillSrvMaps(tlsQname, dnsServer, priorities, ips4, ips6, true);
-
-			final List<TlsSrv> result = new ArrayList<>();
-			for (final List<TlsSrv> s : priorities.values()) {
-				result.addAll(s);
-			}
-
-			final ArrayList<Bundle> values = new ArrayList<>();
-			if (result.size() == 0) {
-				DNSMessage response;
-				try {
-					response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
-					for (int i = 0; i < response.getAnswers().length; ++i) {
-						values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
-					}
-				} catch (SocketTimeoutException e) {
-					Log.d(Config.LOGTAG,"ignoring timeout exception when querying A record on "+dnsServer.getHostAddress());
-				}
-				try {
-					response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
-					for (int i = 0; i < response.getAnswers().length; ++i) {
-						values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
-					}
-				} catch (SocketTimeoutException e) {
-					Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
-				}
-				values.add(createNamePortBundle(host, 5222, false));
-				bundle.putParcelableArrayList("values", values);
-				return bundle;
-			}
-			for (final TlsSrv tlsSrv : result) {
-				final SRV srv = tlsSrv.srv;
-				final String name = srv.getName() != null ? srv.getName().toLowerCase(Locale.US) : null;
-				if (ips6.containsKey(name)) {
-					values.add(createNamePortBundle(name,srv.getPort(),ips6, tlsSrv.tls));
-				} else {
-					try {
-						DNSMessage response = client.query(name, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
-						for (int i = 0; i < response.getAnswers().length; ++i) {
-							values.add(createNamePortBundle(name, srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
-						}
-					} catch (SocketTimeoutException e) {
-						Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
-					}
-				}
-				if (ips4.containsKey(name)) {
-					values.add(createNamePortBundle(name,srv.getPort(),ips4, tlsSrv.tls));
-				} else {
-					DNSMessage response = client.query(name, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
-					for(int i = 0; i < response.getAnswers().length; ++i) {
-						values.add(createNamePortBundle(name,srv.getPort(),response.getAnswers()[i].getPayload(), tlsSrv.tls));
-					}
-				}
-				values.add(createNamePortBundle(name, srv.getPort(), tlsSrv.tls));
-			}
-			bundle.putParcelableArrayList("values", values);
-		} catch (SocketTimeoutException e) {
-			bundle.putString("error", "timeout");
-		} catch (Exception e) {
-			bundle.putString("error", "unhandled");
-		}
-		return bundle;
-	}
-
-	private static Bundle createNamePortBundle(String name, int port, final boolean tls) {
-		Bundle namePort = new Bundle();
-		namePort.putString("name", name);
-		namePort.putBoolean("tls", tls);
-		namePort.putInt("port", port);
-		return namePort;
-	}
-
-	private static Bundle createNamePortBundle(String name, int port, Map<String, List<String>> ips, final boolean tls) {
-		Bundle namePort = new Bundle();
-		namePort.putString("name", name);
-		namePort.putBoolean("tls", tls);
-		namePort.putInt("port", port);
-		if (ips!=null) {
-			List<String> ip = ips.get(name);
-			Collections.shuffle(ip, new Random());
-			namePort.putString("ip", ip.get(0));
-		}
-		return namePort;
-	}
-
-	private static Bundle createNamePortBundle(String name, int port, Data data, final boolean tls) {
-		Bundle namePort = new Bundle();
-		namePort.putString("name", name);
-		namePort.putBoolean("tls", tls);
-		namePort.putInt("port", port);
-		if (data instanceof A) {
-			namePort.putString("ip", data.toString());
-		} else if (data instanceof AAAA) {
-			namePort.putString("ip","["+data.toString()+"]");
-		}
-		return namePort;
-	}
-
-	public static boolean isIp(final String server) {
-		return server != null && (
-				PATTERN_IPV4.matcher(server).matches()
-				|| PATTERN_IPV6.matcher(server).matches()
-				|| PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
-				|| PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
-				|| PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
-	}
-}

src/main/java/eu/siacs/conversations/utils/IP.java 🔗

@@ -0,0 +1,22 @@
+package eu.siacs.conversations.utils;
+
+import java.util.regex.Pattern;
+
+public class IP {
+
+    private static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+    private static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+    private static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+    private static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
+    private static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
+
+    public static boolean matches(String server) {
+        return server != null && (
+                PATTERN_IPV4.matcher(server).matches()
+                        || PATTERN_IPV6.matcher(server).matches()
+                        || PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
+                        || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
+                        || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
+    }
+
+}

src/main/java/eu/siacs/conversations/utils/Resolver.java 🔗

@@ -0,0 +1,163 @@
+package eu.siacs.conversations.utils;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import de.measite.minidns.DNSClient;
+import de.measite.minidns.DNSName;
+import de.measite.minidns.Question;
+import de.measite.minidns.Record;
+import de.measite.minidns.hla.DnssecResolverApi;
+import de.measite.minidns.hla.ResolverResult;
+import de.measite.minidns.record.A;
+import de.measite.minidns.record.AAAA;
+import de.measite.minidns.record.Data;
+import de.measite.minidns.record.InternetAddressRR;
+import de.measite.minidns.record.SRV;
+import eu.siacs.conversations.Config;
+
+public class Resolver {
+
+    private static final String DIRECT_TLS_SERVICE = "_xmpps-client";
+    private static final String STARTTLS_SERICE = "_xmpp-client";
+
+
+
+
+    public static void registerLookupMechanism(Context context) {
+        DNSClient.addDnsServerLookupMechanism(new AndroidUsingLinkProperties(context));
+    }
+
+    public static List<Result> resolve(String domain) {
+        List<Result> results = new ArrayList<>();
+        try {
+            results.addAll(resolveSrv(domain,true));
+        } catch (IOException e) {
+            //ignore
+        }
+        try {
+            results.addAll(resolveSrv(domain,false));
+        } catch (IOException e) {
+            //ignore
+        }
+        if (results.size() == 0) {
+            results.add(Result.createDefault(domain));
+        }
+        Collections.sort(results);
+        return results;
+    }
+
+    private static List<Result> resolveSrv(String domain, final boolean directTls) throws IOException {
+        Question question = new Question((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERICE)+"._tcp."+domain,Record.TYPE.SRV);
+        ResolverResult<Data> result = DnssecResolverApi.INSTANCE.resolve(question);
+        List<Result> results = new ArrayList<>();
+        for(Data record : result.getAnswersOrEmptySet()) {
+            if (record instanceof SRV) {
+                SRV srvRecord = (SRV) record;
+                boolean added = results.addAll(resolveIp(srvRecord,A.class,result.isAuthenticData(),directTls));
+                added |= results.addAll(resolveIp(srvRecord,AAAA.class,result.isAuthenticData(),directTls));
+                if (!added) {
+                    Result resolverResult = Result.fromRecord(srvRecord, directTls);
+                    resolverResult.authenticated = resolverResult.isAuthenticated();
+                    results.add(resolverResult);
+                }
+            }
+        }
+        return results;
+    }
+
+    private static <D extends InternetAddressRR> List<Result> resolveIp(SRV srv, Class<D> type, boolean authenticated, boolean directTls) {
+        List<Result> list = new ArrayList<>();
+        try {
+            ResolverResult<D> results = DnssecResolverApi.INSTANCE.resolve(srv.name, type);
+            for (D record : results.getAnswersOrEmptySet()) {
+                Result resolverResult = Result.fromRecord(srv, directTls);
+                resolverResult.authenticated = results.isAuthenticData() && authenticated;
+                resolverResult.ip = record.getInetAddress();
+                list.add(resolverResult);
+            }
+        } catch (IOException e) {
+            Log.d(Config.LOGTAG,e.getMessage());
+           //ignore. will add default record later
+        }
+        return list;
+    }
+
+    public static class Result implements Comparable<Result> {
+        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;
+        }
+
+        public int getPort() {
+            return port;
+        }
+
+        public DNSName getHostname() {
+            return hostname;
+        }
+
+        public boolean isDirectTls() {
+            return directTls;
+        }
+
+        public boolean isAuthenticated() {
+            return authenticated;
+        }
+
+        @Override
+        public String toString() {
+            return "Result{" +
+                    "ip='" + ip + '\'' +
+                    ", hostame='" + hostname.toString() + '\'' +
+                    ", port=" + port +
+                    ", directTls=" + directTls +
+                    ", authenticated=" + authenticated +
+                    ", priority=" + priority +
+                    '}';
+        }
+
+        @Override
+        public int compareTo(@NonNull Result result) {
+            if (result.priority == priority) {
+                if (directTls == result.directTls) {
+                    return 0;
+                } else {
+                    return directTls ? 1 : -1;
+                }
+            } else {
+                return priority - result.priority;
+            }
+        }
+
+        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(String domain) {
+            Result result = new Result();
+            result.port = 5222;
+            result.hostname = DNSName.from(domain);
+            return result;
+        }
+    }
+
+}

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -2,8 +2,6 @@ package eu.siacs.conversations.xmpp;
 
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.os.Parcelable;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.SystemClock;
@@ -20,7 +18,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigInteger;
 import java.net.ConnectException;
-import java.net.IDN;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
@@ -67,8 +64,9 @@ import eu.siacs.conversations.entities.ServiceDiscoveryResult;
 import eu.siacs.conversations.generator.IqGenerator;
 import eu.siacs.conversations.services.NotificationService;
 import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.utils.DNSHelper;
+import eu.siacs.conversations.utils.IP;
 import eu.siacs.conversations.utils.Patterns;
+import eu.siacs.conversations.utils.Resolver;
 import eu.siacs.conversations.utils.SSLSocketHelper;
 import eu.siacs.conversations.utils.SocksSocketFactory;
 import eu.siacs.conversations.xml.Element;
@@ -329,7 +327,7 @@ public class XmppConnection implements Runnable {
 				} catch (Exception e) {
 					throw new IOException(e.getMessage());
 				}
-			} else if (DNSHelper.isIp(account.getServer().toString())) {
+			} else if (IP.matches(account.getServer().toString())) {
 				localSocket = new Socket();
 				try {
 					localSocket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000);
@@ -345,37 +343,28 @@ public class XmppConnection implements Runnable {
 					throw new IOException(e.getMessage());
 				}
 			} else {
-				final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
-				final ArrayList<Parcelable> values = result.getParcelableArrayList("values");
-				for (Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext(); ) {
+				List<Resolver.Result> results = Resolver.resolve(account.getJid().getDomainpart());
+				Log.d(Config.LOGTAG,"results: "+results);
+				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().toBareJid() + ": Thread was interrupted");
 						return;
 					}
-					final Bundle namePort = (Bundle) iterator.next();
 					try {
-						String srvRecordServer;
-						try {
-							srvRecordServer = IDN.toASCII(namePort.getString("name"));
-						} catch (final IllegalArgumentException e) {
-							// TODO: Handle me?`
-							srvRecordServer = "";
-						}
-						final int srvRecordPort = namePort.getInt("port");
-						final String srvIpServer = namePort.getString("ip");
 						// if tls is true, encryption is implied and must not be started
-						features.encryptionEnabled = namePort.getBoolean("tls");
+						features.encryptionEnabled = result.isDirectTls();
 						final InetSocketAddress addr;
-						if (srvIpServer != null) {
-							addr = new InetSocketAddress(srvIpServer, srvRecordPort);
+						if (result.getIp() != null) {
+							addr = new InetSocketAddress(result.getIp(), result.getPort());
 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
-									+ ": using values from dns " + srvRecordServer
-									+ "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled);
+									+ ": using values from dns " + result.getHostname().toString()
+									+ "[" + result.getIp().toString() + "]:" + result.getPort() + " tls: " + features.encryptionEnabled);
 						} else {
-							addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
+							addr = new InetSocketAddress(result.getHostname().toString(), result.getPort());
 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
 									+ ": using values from dns "
-									+ srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled);
+									+ result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled);
 						}
 
 						if (!features.encryptionEnabled) {