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