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