2017-06-21 21:28:01 +00:00
package eu.siacs.conversations.utils ;
import android.content.Context ;
import android.support.annotation.NonNull ;
import android.util.Log ;
import java.io.IOException ;
2017-07-09 17:05:23 +00:00
import java.net.Inet4Address ;
2017-06-21 21:28:01 +00:00
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 ;
2017-06-25 20:46:56 +00:00
import de.measite.minidns.dnssec.DNSSECResultNotAuthenticException ;
2017-06-21 21:28:01 +00:00
import de.measite.minidns.hla.DnssecResolverApi ;
2017-06-25 20:46:56 +00:00
import de.measite.minidns.hla.ResolverApi ;
2017-06-21 21:28:01 +00:00
import de.measite.minidns.hla.ResolverResult ;
import de.measite.minidns.record.A ;
import de.measite.minidns.record.AAAA ;
2017-06-25 20:46:56 +00:00
import de.measite.minidns.record.Data ;
2017-06-21 21:28:01 +00:00
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 ) ) ;
2017-06-25 20:46:56 +00:00
} catch ( IOException e ) {
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : " + e . getMessage ( ) ) ;
2017-06-21 21:28:01 +00:00
}
try {
results . addAll ( resolveSrv ( domain , false ) ) ;
2017-06-25 20:46:56 +00:00
} catch ( IOException e ) {
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : " + e . getMessage ( ) ) ;
2017-06-21 21:28:01 +00:00
}
if ( results . size ( ) = = 0 ) {
results . add ( Result . createDefault ( domain ) ) ;
}
Collections . sort ( results ) ;
2017-06-25 20:46:56 +00:00
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : " + results . toString ( ) ) ;
2017-06-21 21:28:01 +00:00
return results ;
}
private static List < Result > resolveSrv ( String domain , final boolean directTls ) throws IOException {
2017-06-25 20:46:56 +00:00
if ( Thread . interrupted ( ) ) {
return Collections . emptyList ( ) ;
}
2017-06-25 17:30:03 +00:00
DNSName dnsName = DNSName . from ( ( directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERICE ) + " ._tcp. " + domain ) ;
2017-06-25 20:46:56 +00:00
ResolverResult < SRV > result = resolveWithFallback ( dnsName , SRV . class ) ;
2017-06-21 21:28:01 +00:00
List < Result > results = new ArrayList < > ( ) ;
2017-06-25 17:30:03 +00:00
for ( SRV record : result . getAnswersOrEmptySet ( ) ) {
2017-06-25 20:46:56 +00:00
final boolean addedIPv4 = results . addAll ( resolveIp ( record , A . class , result . isAuthenticData ( ) , directTls ) ) ;
results . addAll ( resolveIp ( record , AAAA . class , result . isAuthenticData ( ) , directTls ) ) ;
if ( ! addedIPv4 & & ! Thread . interrupted ( ) ) {
2017-06-25 17:30:03 +00:00
Result resolverResult = Result . fromRecord ( record , directTls ) ;
resolverResult . authenticated = resolverResult . isAuthenticated ( ) ;
results . add ( resolverResult ) ;
2017-06-21 21:28:01 +00:00
}
}
return results ;
}
private static < D extends InternetAddressRR > List < Result > resolveIp ( SRV srv , Class < D > type , boolean authenticated , boolean directTls ) {
2017-06-25 20:46:56 +00:00
if ( Thread . interrupted ( ) ) {
return Collections . emptyList ( ) ;
}
2017-06-21 21:28:01 +00:00
List < Result > list = new ArrayList < > ( ) ;
try {
2017-06-25 20:46:56 +00:00
ResolverResult < D > results = resolveWithFallback ( srv . name , type ) ;
2017-06-21 21:28:01 +00:00
for ( D record : results . getAnswersOrEmptySet ( ) ) {
Result resolverResult = Result . fromRecord ( srv , directTls ) ;
resolverResult . authenticated = results . isAuthenticData ( ) & & authenticated ;
resolverResult . ip = record . getInetAddress ( ) ;
list . add ( resolverResult ) ;
}
2017-06-25 16:35:40 +00:00
} catch ( Throwable t ) {
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : error resolving " + type . getSimpleName ( ) + " " + t . getMessage ( ) ) ;
2017-06-21 21:28:01 +00:00
}
return list ;
}
2017-06-25 20:46:56 +00:00
private static < D extends Data > ResolverResult < D > resolveWithFallback ( DNSName dnsName , Class < D > type ) throws IOException {
try {
2017-07-09 16:03:26 +00:00
final ResolverResult < D > r = DnssecResolverApi . INSTANCE . resolveDnssecReliable ( dnsName , type ) ;
if ( r . wasSuccessful ( ) ) {
if ( r . getAnswers ( ) . isEmpty ( ) & & type . equals ( SRV . class ) ) {
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : resolving SRV records of " + dnsName . toString ( ) + " with DNSSEC yielded empty result " ) ;
}
return r ;
}
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : error resolving " + type . getSimpleName ( ) + " with DNSSEC. Trying DNS instead. " , r . getResolutionUnsuccessfulException ( ) ) ;
2017-06-26 12:03:38 +00:00
} catch ( DNSSECResultNotAuthenticException e ) {
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : error resolving " + type . getSimpleName ( ) + " with DNSSEC. Trying DNS instead. " , e ) ;
} catch ( IOException e ) {
throw e ;
} catch ( Throwable throwable ) {
Log . d ( Config . LOGTAG , Resolver . class . getSimpleName ( ) + " : error resolving " + type . getSimpleName ( ) + " with DNSSEC. Trying DNS instead. " , throwable ) ;
2017-06-25 20:46:56 +00:00
}
2017-07-09 16:03:26 +00:00
return ResolverApi . INSTANCE . resolve ( dnsName , type ) ;
2017-06-25 20:46:56 +00:00
}
2017-06-21 21:28:01 +00:00
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{ " +
2017-06-25 17:30:03 +00:00
" ip=' " + ( ip = = null ? null : ip . getHostAddress ( ) ) + '\'' +
2017-06-21 21:28:01 +00:00
" , 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 ) {
2017-06-25 20:46:56 +00:00
if ( ip = = null & & result . ip = = null ) {
return 0 ;
2017-07-09 17:05:23 +00:00
} else if ( ip ! = null & & result . ip ! = null ) {
if ( ip instanceof Inet4Address & & result . ip instanceof Inet4Address ) {
return 0 ;
} else {
return ip instanceof Inet4Address ? - 1 : 1 ;
}
2017-06-25 20:46:56 +00:00
} else {
return ip ! = null ? - 1 : 1 ;
}
2017-06-21 21:28:01 +00:00
} else {
2017-06-25 20:46:56 +00:00
return directTls ? - 1 : 1 ;
2017-06-21 21:28:01 +00:00
}
} 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 ;
}
}
}