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