upgrade minidns. refactored dns utils
This commit is contained in:
		
							parent
							
								
									5d9d725446
								
							
						
					
					
						commit
						2e380ed792
					
				| @ -33,7 +33,7 @@ dependencies { | |||||||
|     compile 'org.gnu.inet:libidn:1.15' |     compile 'org.gnu.inet:libidn:1.15' | ||||||
|     compile 'com.google.zxing:core:3.2.1' |     compile 'com.google.zxing:core:3.2.1' | ||||||
|     compile 'com.google.zxing:android-integration: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 'de.timroes.android:EnhancedListView:0.3.4' | ||||||
|     compile 'me.leolin:ShortcutBadger:1.1.12@aar' |     compile 'me.leolin:ShortcutBadger:1.1.12@aar' | ||||||
|     compile 'com.kyleduo.switchbutton:library:1.2.8' |     compile 'com.kyleduo.switchbutton:library:1.2.8' | ||||||
|  | |||||||
| @ -114,6 +114,7 @@ import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; | |||||||
| import eu.siacs.conversations.utils.PRNGFixes; | import eu.siacs.conversations.utils.PRNGFixes; | ||||||
| import eu.siacs.conversations.utils.PhoneHelper; | import eu.siacs.conversations.utils.PhoneHelper; | ||||||
| import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor; | import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor; | ||||||
|  | import eu.siacs.conversations.utils.Resolver; | ||||||
| import eu.siacs.conversations.utils.SerialSingleThreadExecutor; | import eu.siacs.conversations.utils.SerialSingleThreadExecutor; | ||||||
| import eu.siacs.conversations.xml.Namespace; | import eu.siacs.conversations.xml.Namespace; | ||||||
| import eu.siacs.conversations.utils.XmppUri; | import eu.siacs.conversations.utils.XmppUri; | ||||||
| @ -983,6 +984,7 @@ public class XmppConnectionService extends Service { | |||||||
| 	public void onCreate() { | 	public void onCreate() { | ||||||
| 		ExceptionHelper.init(getApplicationContext()); | 		ExceptionHelper.init(getApplicationContext()); | ||||||
| 		PRNGFixes.apply(); | 		PRNGFixes.apply(); | ||||||
|  | 		Resolver.registerLookupMechanism(this); | ||||||
| 		this.mRandom = new SecureRandom(); | 		this.mRandom = new SecureRandom(); | ||||||
| 		updateMemorizingTrustmanager(); | 		updateMemorizingTrustmanager(); | ||||||
| 		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); | 		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); | ||||||
|  | |||||||
| @ -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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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()); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										22
									
								
								src/main/java/eu/siacs/conversations/utils/IP.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/main/java/eu/siacs/conversations/utils/IP.java
									
									
									
									
									
										Normal file
									
								
							| @ -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()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										163
									
								
								src/main/java/eu/siacs/conversations/utils/Resolver.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/main/java/eu/siacs/conversations/utils/Resolver.java
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -2,8 +2,6 @@ package eu.siacs.conversations.xmpp; | |||||||
| 
 | 
 | ||||||
| import android.graphics.Bitmap; | import android.graphics.Bitmap; | ||||||
| import android.graphics.BitmapFactory; | import android.graphics.BitmapFactory; | ||||||
| import android.os.Bundle; |  | ||||||
| import android.os.Parcelable; |  | ||||||
| import android.os.PowerManager; | import android.os.PowerManager; | ||||||
| import android.os.PowerManager.WakeLock; | import android.os.PowerManager.WakeLock; | ||||||
| import android.os.SystemClock; | import android.os.SystemClock; | ||||||
| @ -20,7 +18,6 @@ import java.io.IOException; | |||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.math.BigInteger; | import java.math.BigInteger; | ||||||
| import java.net.ConnectException; | import java.net.ConnectException; | ||||||
| import java.net.IDN; |  | ||||||
| import java.net.InetAddress; | import java.net.InetAddress; | ||||||
| import java.net.InetSocketAddress; | import java.net.InetSocketAddress; | ||||||
| import java.net.Socket; | import java.net.Socket; | ||||||
| @ -67,8 +64,9 @@ import eu.siacs.conversations.entities.ServiceDiscoveryResult; | |||||||
| import eu.siacs.conversations.generator.IqGenerator; | import eu.siacs.conversations.generator.IqGenerator; | ||||||
| import eu.siacs.conversations.services.NotificationService; | import eu.siacs.conversations.services.NotificationService; | ||||||
| import eu.siacs.conversations.services.XmppConnectionService; | 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.Patterns; | ||||||
|  | import eu.siacs.conversations.utils.Resolver; | ||||||
| import eu.siacs.conversations.utils.SSLSocketHelper; | import eu.siacs.conversations.utils.SSLSocketHelper; | ||||||
| import eu.siacs.conversations.utils.SocksSocketFactory; | import eu.siacs.conversations.utils.SocksSocketFactory; | ||||||
| import eu.siacs.conversations.xml.Element; | import eu.siacs.conversations.xml.Element; | ||||||
| @ -329,7 +327,7 @@ public class XmppConnection implements Runnable { | |||||||
| 				} catch (Exception e) { | 				} catch (Exception e) { | ||||||
| 					throw new IOException(e.getMessage()); | 					throw new IOException(e.getMessage()); | ||||||
| 				} | 				} | ||||||
| 			} else if (DNSHelper.isIp(account.getServer().toString())) { | 			} else if (IP.matches(account.getServer().toString())) { | ||||||
| 				localSocket = new Socket(); | 				localSocket = new Socket(); | ||||||
| 				try { | 				try { | ||||||
| 					localSocket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); | 					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()); | 					throw new IOException(e.getMessage()); | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService); | 				List<Resolver.Result> results = Resolver.resolve(account.getJid().getDomainpart()); | ||||||
| 				final ArrayList<Parcelable> values = result.getParcelableArrayList("values"); | 				Log.d(Config.LOGTAG,"results: "+results); | ||||||
| 				for (Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext(); ) { | 				for (Iterator<Resolver.Result> iterator = results.iterator(); iterator.hasNext(); ) { | ||||||
|  | 					final Resolver.Result result = iterator.next(); | ||||||
| 					if (Thread.currentThread().isInterrupted()) { | 					if (Thread.currentThread().isInterrupted()) { | ||||||
| 						Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Thread was interrupted"); | 						Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Thread was interrupted"); | ||||||
| 						return; | 						return; | ||||||
| 					} | 					} | ||||||
| 					final Bundle namePort = (Bundle) iterator.next(); |  | ||||||
| 					try { | 					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 | 						// if tls is true, encryption is implied and must not be started | ||||||
| 						features.encryptionEnabled = namePort.getBoolean("tls"); | 						features.encryptionEnabled = result.isDirectTls(); | ||||||
| 						final InetSocketAddress addr; | 						final InetSocketAddress addr; | ||||||
| 						if (srvIpServer != null) { | 						if (result.getIp() != null) { | ||||||
| 							addr = new InetSocketAddress(srvIpServer, srvRecordPort); | 							addr = new InetSocketAddress(result.getIp(), result.getPort()); | ||||||
| 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() | 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() | ||||||
| 									+ ": using values from dns " + srvRecordServer | 									+ ": using values from dns " + result.getHostname().toString() | ||||||
| 									+ "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled); | 									+ "[" + result.getIp().toString() + "]:" + result.getPort() + " tls: " + features.encryptionEnabled); | ||||||
| 						} else { | 						} else { | ||||||
| 							addr = new InetSocketAddress(srvRecordServer, srvRecordPort); | 							addr = new InetSocketAddress(result.getHostname().toString(), result.getPort()); | ||||||
| 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() | 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() | ||||||
| 									+ ": using values from dns " | 									+ ": using values from dns " | ||||||
| 									+ srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled); | 									+ result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled); | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
| 						if (!features.encryptionEnabled) { | 						if (!features.encryptionEnabled) { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch