• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.shared;
18 
19 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
20 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
21 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
22 import static android.net.shared.ParcelableUtil.fromParcelableArray;
23 import static android.net.shared.ParcelableUtil.toParcelableArray;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.net.PrivateDnsConfigParcel;
28 import android.text.TextUtils;
29 
30 import java.net.InetAddress;
31 import java.util.Arrays;
32 
33 /** @hide */
34 public class PrivateDnsConfig {
35     // These fields store the private DNS configuration from setting.
36     public final int mode;
37     @NonNull
38     public final String hostname;
39 
40     // Stores the DoT server IP addresses resolved from A/AAAA lookups.
41     @NonNull
42     public final InetAddress[] ips;
43 
44     // Whether DDR discovery is enabled.
45     // If DDR is enabled, then empty dohName / dohIps indicate that DoH is disabled.
46     // If DDR is disabled, then empty dohName / dohIps indicate that DNS resolver should attempt to
47     // enable DoH based on using its hardcoded list of known providers.
48     public final boolean ddrEnabled;
49 
50     // These fields store the DoH information discovered from SVCB lookups.
51     @NonNull
52     public final String dohName;
53     @NonNull
54     public final InetAddress[] dohIps;
55     @NonNull
56     public final String dohPath;
57     public final int dohPort;
58 
59     /**
60      * A constructor for off mode private DNS configuration.
61      * TODO(b/261404136): Consider simplifying the constructors. One possible way is to
62      * use constants to represent private DNS modes:
63      *   public static PrivateDnsConfig OFF = new PrivateDnsConfig(false);
64      *   public static PrivateDnsConfig OPPORTUNISTIC = new PrivateDnsConfig(true);
65      *   public static PrivateDnsConfig STRICT = new PrivateDnsConfig(String hostname);
66      */
PrivateDnsConfig()67     public PrivateDnsConfig() {
68         this(false);
69     }
70 
71     /**
72      * A constructor for off/opportunistic mode private DNS configuration depending on `useTls`.
73      */
PrivateDnsConfig(boolean useTls)74     public PrivateDnsConfig(boolean useTls) {
75         this(useTls ? PRIVATE_DNS_MODE_OPPORTUNISTIC : PRIVATE_DNS_MODE_OFF, null /* hostname */,
76                 null /* ips */, false /* ddrEnabled */, null /* dohName */, null /* dohIps */,
77                 null /* dohPath */, -1 /* dohPort */);
78     }
79 
80     /**
81      * A constructor for off/strict mode private DNS configuration depending on `hostname`.
82      * If `hostname` is empty or null, this constructor creates a PrivateDnsConfig for off mode;
83      * otherwise, it creates a PrivateDnsConfig for strict mode.
84      */
PrivateDnsConfig(@ullable String hostname, @Nullable InetAddress[] ips)85     public PrivateDnsConfig(@Nullable String hostname, @Nullable InetAddress[] ips) {
86         this(TextUtils.isEmpty(hostname) ? PRIVATE_DNS_MODE_OFF :
87                 PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, hostname, ips, false /* ddrEnabled */,
88                 null /* dohName */, null /* dohIps */, null /* dohPath */, -1 /* dohPort */);
89     }
90 
91     /**
92      * A constructor for all kinds of private DNS configuration with given DoH information.
93      * It treats both null values and empty strings as equivalent. Similarly, treats null values
94      * and empty arrays as equivalent.
95      */
PrivateDnsConfig(int mode, @Nullable String hostname, @Nullable InetAddress[] ips, boolean ddrEnabled, @Nullable String dohName, @Nullable InetAddress[] dohIps, @Nullable String dohPath, int dohPort)96     public PrivateDnsConfig(int mode, @Nullable String hostname, @Nullable InetAddress[] ips,
97             boolean ddrEnabled,  @Nullable String dohName, @Nullable InetAddress[] dohIps,
98             @Nullable String dohPath, int dohPort) {
99         this.mode = mode;
100         this.hostname = (hostname != null) ? hostname : "";
101         this.ips = (ips != null) ? ips.clone() : new InetAddress[0];
102         this.ddrEnabled = ddrEnabled;
103         this.dohName = (dohName != null) ? dohName : "";
104         this.dohIps = (dohIps != null) ? dohIps.clone() : new InetAddress[0];
105         this.dohPath = (dohPath != null) ? dohPath : "";
106         this.dohPort = dohPort;
107     }
108 
PrivateDnsConfig(PrivateDnsConfig cfg)109     public PrivateDnsConfig(PrivateDnsConfig cfg) {
110         mode = cfg.mode;
111         hostname = cfg.hostname;
112         ips = cfg.ips;
113         ddrEnabled = cfg.ddrEnabled;
114         dohName = cfg.dohName;
115         dohIps = cfg.dohIps;
116         dohPath = cfg.dohPath;
117         dohPort = cfg.dohPort;
118     }
119 
120     /**
121      * Indicates whether this is a strict mode private DNS configuration.
122      */
inStrictMode()123     public boolean inStrictMode() {
124         return mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
125     }
126 
127     /**
128      * Indicates whether this is an opportunistic mode private DNS configuration.
129      */
inOpportunisticMode()130     public boolean inOpportunisticMode() {
131         return mode == PRIVATE_DNS_MODE_OPPORTUNISTIC;
132     }
133 
134     /**
135      * Returns whether the fields related to private DNS settings are the same.
136      */
areSettingsSameAs(PrivateDnsConfig other)137     public boolean areSettingsSameAs(PrivateDnsConfig other) {
138         return mode == other.mode && TextUtils.equals(hostname, other.hostname);
139     }
140 
141     @Override
toString()142     public String toString() {
143         return PrivateDnsConfig.class.getSimpleName()
144                 + "{" + modeAsString(mode) + ":" + hostname + "/" + Arrays.toString(ips)
145                 + ", dohName=" + dohName
146                 + ", dohIps=" + Arrays.toString(dohIps)
147                 + ", dohPath=" + dohPath
148                 + ", dohPort=" + dohPort
149                 + "}";
150     }
151 
152     @NonNull
modeAsString(int mode)153     private static String modeAsString(int mode) {
154         switch (mode) {
155             case PRIVATE_DNS_MODE_OFF: return "off";
156             case PRIVATE_DNS_MODE_OPPORTUNISTIC: return "opportunistic";
157             case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME: return "strict";
158             default: return "unknown";
159         }
160     }
161 
162     /**
163      * Create a stable AIDL-compatible parcel from the current instance.
164      */
toParcel()165     public PrivateDnsConfigParcel toParcel() {
166         final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel();
167         parcel.hostname = hostname;
168         parcel.ips = toParcelableArray(
169                 Arrays.asList(ips), IpConfigurationParcelableUtil::parcelAddress, String.class);
170         parcel.privateDnsMode = mode;
171         parcel.dohName = dohName;
172         parcel.dohIps = toParcelableArray(
173                 Arrays.asList(dohIps), IpConfigurationParcelableUtil::parcelAddress, String.class);
174         parcel.dohPath = dohPath;
175         parcel.dohPort = dohPort;
176         parcel.ddrEnabled = ddrEnabled;
177         return parcel;
178     }
179 
180     /**
181      * Build a configuration from a stable AIDL-compatible parcel.
182      */
fromParcel(PrivateDnsConfigParcel parcel)183     public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) {
184         InetAddress[] ips = new InetAddress[parcel.ips.length];
185         ips = fromParcelableArray(parcel.ips, IpConfigurationParcelableUtil::unparcelAddress)
186                 .toArray(ips);
187 
188         // For compatibility. If the sender (Tethering module) is using an old version (< 19) of
189         // NetworkStack AIDL that `privateDnsMode` field is not present, `privateDnsMode` will be
190         // assigned from the default value -1. Let `privateDnsMode` assigned based on the hostname.
191         // In this case, there is a harmless bug that the receiver (NetworkStack module) can't
192         // convert the parcel to a PrivateDnsConfig that indicates opportunistic mode.
193         // The bug is harmless because 1) the bug exists for years without any problems and
194         // 2) NetworkMonitor cares PrivateDnsConfig that indicates strict/off mode only.
195         // If the sender is using new version (>=19) while the receiver is using an old version,
196         // the above mentioned harmless bug will persist. Except for that harmless bug, there
197         // should be no other issues. New version's toParcel() doesn't change how the pre-existing
198         // fields `hostname` and `ips` are assigned.
199         if (parcel.privateDnsMode == -1) {
200             return new PrivateDnsConfig(parcel.hostname, ips);
201         }
202 
203         InetAddress[] dohIps = new InetAddress[parcel.dohIps.length];
204         dohIps = fromParcelableArray(parcel.dohIps,
205                 IpConfigurationParcelableUtil::unparcelAddress).toArray(dohIps);
206         return new PrivateDnsConfig(parcel.privateDnsMode, parcel.hostname, ips,
207                 parcel.ddrEnabled, parcel.dohName, dohIps, parcel.dohPath, parcel.dohPort);
208     }
209 }
210