1package de.gultsch.minidns;
2
3import android.content.Context;
4import android.net.ConnectivityManager;
5import android.net.LinkProperties;
6import android.net.Network;
7import android.os.Build;
8
9import com.google.common.base.Strings;
10import com.google.common.collect.ImmutableList;
11
12import de.measite.minidns.AbstractDNSClient;
13import de.measite.minidns.DNSMessage;
14
15import java.io.IOException;
16import java.net.InetAddress;
17import java.util.List;
18
19public class AndroidDNSClient extends AbstractDNSClient {
20 private final Context context;
21 private final NetworkDataSource networkDataSource = new NetworkDataSource();
22 private boolean askForDnssec = false;
23
24 public AndroidDNSClient(final Context context) {
25 super();
26 this.setDataSource(networkDataSource);
27 this.context = context;
28 }
29
30 private static String getPrivateDnsServerName(final LinkProperties linkProperties) {
31 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
32 return linkProperties.getPrivateDnsServerName();
33 } else {
34 return null;
35 }
36 }
37
38 private static boolean isPrivateDnsActive(final LinkProperties linkProperties) {
39 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
40 return linkProperties.isPrivateDnsActive();
41 } else {
42 return false;
43 }
44 }
45
46 @Override
47 protected DNSMessage.Builder newQuestion(final DNSMessage.Builder message) {
48 message.setRecursionDesired(true);
49 message.getEdnsBuilder()
50 .setUdpPayloadSize(networkDataSource.getUdpPayloadSize())
51 .setDnssecOk(askForDnssec);
52 return message;
53 }
54
55 @Override
56 protected DNSMessage query(final DNSMessage.Builder queryBuilder) throws IOException {
57 final DNSMessage question = newQuestion(queryBuilder).build();
58 for (final DNSServer dnsServer : getDNSServers()) {
59 final DNSMessage response = this.networkDataSource.query(question, dnsServer);
60 if (response == null) {
61 continue;
62 }
63 switch (response.responseCode) {
64 case NO_ERROR:
65 case NX_DOMAIN:
66 break;
67 default:
68 continue;
69 }
70
71 return response;
72 }
73 return null;
74 }
75
76 public boolean isAskForDnssec() {
77 return askForDnssec;
78 }
79
80 public void setAskForDnssec(boolean askForDnssec) {
81 this.askForDnssec = askForDnssec;
82 }
83
84 private List<DNSServer> getDNSServers() {
85 final ImmutableList.Builder<DNSServer> dnsServerBuilder = new ImmutableList.Builder<>();
86 final ConnectivityManager connectivityManager =
87 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
88 final Network[] networks = getActiveNetworks(connectivityManager);
89 for (final Network network : networks) {
90 final LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
91 if (linkProperties == null) {
92 continue;
93 }
94 final String privateDnsServerName = getPrivateDnsServerName(linkProperties);
95 if (Strings.isNullOrEmpty(privateDnsServerName)) {
96 final boolean isPrivateDns = isPrivateDnsActive(linkProperties);
97 for (final InetAddress dnsServer : linkProperties.getDnsServers()) {
98 if (isPrivateDns) {
99 dnsServerBuilder.add(new DNSServer(dnsServer, Transport.TLS));
100 } else {
101 dnsServerBuilder.add(new DNSServer(dnsServer));
102 }
103 }
104 } else {
105 dnsServerBuilder.add(new DNSServer(privateDnsServerName, Transport.TLS));
106 }
107 }
108 return dnsServerBuilder.build();
109 }
110
111 private Network[] getActiveNetworks(final ConnectivityManager connectivityManager) {
112 if (connectivityManager == null) {
113 return new Network[0];
114 }
115 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
116 final Network activeNetwork = connectivityManager.getActiveNetwork();
117 if (activeNetwork != null) {
118 return new Network[] {activeNetwork};
119 }
120 }
121 return connectivityManager.getAllNetworks();
122 }
123}