Resolver.java

  1package eu.siacs.conversations.utils;
  2
  3import android.content.Context;
  4import android.support.annotation.NonNull;
  5import android.util.Log;
  6
  7import java.io.IOException;
  8import java.net.InetAddress;
  9import java.util.ArrayList;
 10import java.util.Collections;
 11import java.util.List;
 12
 13import de.measite.minidns.DNSClient;
 14import de.measite.minidns.DNSName;
 15import de.measite.minidns.Question;
 16import de.measite.minidns.Record;
 17import de.measite.minidns.dnssec.DNSSECValidationFailedException;
 18import de.measite.minidns.hla.DnssecResolverApi;
 19import de.measite.minidns.hla.ResolverApi;
 20import de.measite.minidns.hla.ResolverResult;
 21import de.measite.minidns.record.A;
 22import de.measite.minidns.record.AAAA;
 23import de.measite.minidns.record.Data;
 24import de.measite.minidns.record.InternetAddressRR;
 25import de.measite.minidns.record.SRV;
 26import eu.siacs.conversations.Config;
 27
 28public class Resolver {
 29
 30    private static final String DIRECT_TLS_SERVICE = "_xmpps-client";
 31    private static final String STARTTLS_SERICE = "_xmpp-client";
 32
 33
 34
 35
 36    public static void registerLookupMechanism(Context context) {
 37        DNSClient.addDnsServerLookupMechanism(new AndroidUsingLinkProperties(context));
 38    }
 39
 40    public static List<Result> resolve(String domain) {
 41        List<Result> results = new ArrayList<>();
 42        try {
 43            results.addAll(resolveSrv(domain,true));
 44        } catch (Throwable t) {
 45            Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": "+t.getMessage());
 46        }
 47        try {
 48            results.addAll(resolveSrv(domain,false));
 49        } catch (Throwable t) {
 50            Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": "+t.getMessage());
 51        }
 52        if (results.size() == 0) {
 53            results.add(Result.createDefault(domain));
 54        }
 55        Collections.sort(results);
 56        return results;
 57    }
 58
 59    private static List<Result> resolveSrv(String domain, final boolean directTls) throws IOException {
 60        Question question = new Question((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERICE)+"._tcp."+domain,Record.TYPE.SRV);
 61        ResolverResult<Data> result;
 62        try {
 63            result = DnssecResolverApi.INSTANCE.resolve(question);
 64        } catch (DNSSECValidationFailedException e) {
 65            Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": error resolving SRV record with DNSSEC. Trying DNS instead "+e.getMessage());
 66            result = ResolverApi.INSTANCE.resolve(question);
 67        }
 68        List<Result> results = new ArrayList<>();
 69        for(Data record : result.getAnswersOrEmptySet()) {
 70            if (record instanceof SRV) {
 71                SRV srvRecord = (SRV) record;
 72                boolean added = results.addAll(resolveIp(srvRecord,A.class,result.isAuthenticData(),directTls));
 73                added |= results.addAll(resolveIp(srvRecord,AAAA.class,result.isAuthenticData(),directTls));
 74                if (!added) {
 75                    Result resolverResult = Result.fromRecord(srvRecord, directTls);
 76                    resolverResult.authenticated = resolverResult.isAuthenticated();
 77                    results.add(resolverResult);
 78                }
 79            }
 80        }
 81        return results;
 82    }
 83
 84    private static <D extends InternetAddressRR> List<Result> resolveIp(SRV srv, Class<D> type, boolean authenticated, boolean directTls) {
 85        List<Result> list = new ArrayList<>();
 86        try {
 87            ResolverResult<D> results;
 88            try {
 89                results = DnssecResolverApi.INSTANCE.resolve(srv.name, type);
 90            } catch (DNSSECValidationFailedException e) {
 91                Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": error resolving "+type.getSimpleName()+" with DNSSEC. Trying DNS instead "+e.getMessage());
 92                results = ResolverApi.INSTANCE.resolve(srv.name,type);
 93            }
 94            for (D record : results.getAnswersOrEmptySet()) {
 95                Result resolverResult = Result.fromRecord(srv, directTls);
 96                resolverResult.authenticated = results.isAuthenticData() && authenticated;
 97                resolverResult.ip = record.getInetAddress();
 98                list.add(resolverResult);
 99            }
100        } catch (Throwable t) {
101            Log.d(Config.LOGTAG,Resolver.class.getSimpleName()+": error resolving "+type.getSimpleName()+" "+t.getMessage());
102        }
103        return list;
104    }
105
106    public static class Result implements Comparable<Result> {
107        private InetAddress ip;
108        private DNSName hostname;
109        private int port = 5222;
110        private boolean directTls = false;
111        private boolean authenticated =false;
112        private int priority;
113
114        public InetAddress getIp() {
115            return ip;
116        }
117
118        public int getPort() {
119            return port;
120        }
121
122        public DNSName getHostname() {
123            return hostname;
124        }
125
126        public boolean isDirectTls() {
127            return directTls;
128        }
129
130        public boolean isAuthenticated() {
131            return authenticated;
132        }
133
134        @Override
135        public String toString() {
136            return "Result{" +
137                    "ip='" + ip + '\'' +
138                    ", hostame='" + hostname.toString() + '\'' +
139                    ", port=" + port +
140                    ", directTls=" + directTls +
141                    ", authenticated=" + authenticated +
142                    ", priority=" + priority +
143                    '}';
144        }
145
146        @Override
147        public int compareTo(@NonNull Result result) {
148            if (result.priority == priority) {
149                if (directTls == result.directTls) {
150                    return 0;
151                } else {
152                    return directTls ? 1 : -1;
153                }
154            } else {
155                return priority - result.priority;
156            }
157        }
158
159        public static Result fromRecord(SRV srv, boolean directTls) {
160            Result result = new Result();
161            result.port = srv.port;
162            result.hostname = srv.name;
163            result.directTls = directTls;
164            result.priority = srv.priority;
165            return result;
166        }
167
168        public static Result createDefault(String domain) {
169            Result result = new Result();
170            result.port = 5222;
171            result.hostname = DNSName.from(domain);
172            return result;
173        }
174    }
175
176}