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 // These fields store the DoH information discovered from SVCB lookups. 45 @NonNull 46 public final String dohName; 47 @NonNull 48 public final InetAddress[] dohIps; 49 @NonNull 50 public final String dohPath; 51 public final int dohPort; 52 53 /** 54 * A constructor for off mode private DNS configuration. 55 * TODO(b/261404136): Consider simplifying the constructors. One possible way is to 56 * use constants to represent private DNS modes: 57 * public static PrivateDnsConfig OFF = new PrivateDnsConfig(false); 58 * public static PrivateDnsConfig OPPORTUNISTIC = new PrivateDnsConfig(true); 59 * public static PrivateDnsConfig STRICT = new PrivateDnsConfig(String hostname); 60 */ PrivateDnsConfig()61 public PrivateDnsConfig() { 62 this(false); 63 } 64 65 /** 66 * A constructor for off/opportunistic mode private DNS configuration depending on `useTls`. 67 */ PrivateDnsConfig(boolean useTls)68 public PrivateDnsConfig(boolean useTls) { 69 this(useTls ? PRIVATE_DNS_MODE_OPPORTUNISTIC : PRIVATE_DNS_MODE_OFF, null /* hostname */, 70 null /* ips */, null /* dohName */, null /* dohIps */, null /* dohPath */, 71 -1 /* dohPort */); 72 } 73 74 /** 75 * A constructor for off/strict mode private DNS configuration depending on `hostname`. 76 * If `hostname` is empty or null, this constructor creates a PrivateDnsConfig for off mode; 77 * otherwise, it creates a PrivateDnsConfig for strict mode. 78 */ PrivateDnsConfig(@ullable String hostname, @Nullable InetAddress[] ips)79 public PrivateDnsConfig(@Nullable String hostname, @Nullable InetAddress[] ips) { 80 this(TextUtils.isEmpty(hostname) ? PRIVATE_DNS_MODE_OFF : 81 PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, hostname, ips, null /* dohName */, 82 null /* dohIps */, null /* dohPath */, -1 /* dohPort */); 83 } 84 85 /** 86 * A constructor for all kinds of private DNS configuration with given DoH information. 87 * It treats both null values and empty strings as equivalent. Similarly, treats null values 88 * and empty arrays as equivalent. 89 */ PrivateDnsConfig(int mode, @Nullable String hostname, @Nullable InetAddress[] ips, @Nullable String dohName, @Nullable InetAddress[] dohIps, @Nullable String dohPath, int dohPort)90 public PrivateDnsConfig(int mode, @Nullable String hostname, @Nullable InetAddress[] ips, 91 @Nullable String dohName, @Nullable InetAddress[] dohIps, @Nullable String dohPath, 92 int dohPort) { 93 this.mode = mode; 94 this.hostname = (hostname != null) ? hostname : ""; 95 this.ips = (ips != null) ? ips.clone() : new InetAddress[0]; 96 this.dohName = (dohName != null) ? dohName : ""; 97 this.dohIps = (dohIps != null) ? dohIps.clone() : new InetAddress[0]; 98 this.dohPath = (dohPath != null) ? dohPath : ""; 99 this.dohPort = dohPort; 100 } 101 PrivateDnsConfig(PrivateDnsConfig cfg)102 public PrivateDnsConfig(PrivateDnsConfig cfg) { 103 mode = cfg.mode; 104 hostname = cfg.hostname; 105 ips = cfg.ips; 106 dohName = cfg.dohName; 107 dohIps = cfg.dohIps; 108 dohPath = cfg.dohPath; 109 dohPort = cfg.dohPort; 110 } 111 112 /** 113 * Indicates whether this is a strict mode private DNS configuration. 114 */ inStrictMode()115 public boolean inStrictMode() { 116 return mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; 117 } 118 119 /** 120 * Indicates whether this is an opportunistic mode private DNS configuration. 121 */ inOpportunisticMode()122 public boolean inOpportunisticMode() { 123 return mode == PRIVATE_DNS_MODE_OPPORTUNISTIC; 124 } 125 126 @Override toString()127 public String toString() { 128 return PrivateDnsConfig.class.getSimpleName() 129 + "{" + modeAsString(mode) + ":" + hostname + "/" + Arrays.toString(ips) 130 + ", dohName=" + dohName 131 + ", dohIps=" + Arrays.toString(dohIps) 132 + ", dohPath=" + dohPath 133 + ", dohPort=" + dohPort 134 + "}"; 135 } 136 137 @NonNull modeAsString(int mode)138 private static String modeAsString(int mode) { 139 switch (mode) { 140 case PRIVATE_DNS_MODE_OFF: return "off"; 141 case PRIVATE_DNS_MODE_OPPORTUNISTIC: return "opportunistic"; 142 case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME: return "strict"; 143 default: return "unknown"; 144 } 145 } 146 147 /** 148 * Create a stable AIDL-compatible parcel from the current instance. 149 */ toParcel()150 public PrivateDnsConfigParcel toParcel() { 151 final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel(); 152 parcel.hostname = hostname; 153 parcel.ips = toParcelableArray( 154 Arrays.asList(ips), IpConfigurationParcelableUtil::parcelAddress, String.class); 155 parcel.privateDnsMode = mode; 156 parcel.dohName = dohName; 157 parcel.dohIps = toParcelableArray( 158 Arrays.asList(dohIps), IpConfigurationParcelableUtil::parcelAddress, String.class); 159 parcel.dohPath = dohPath; 160 parcel.dohPort = dohPort; 161 return parcel; 162 } 163 164 /** 165 * Build a configuration from a stable AIDL-compatible parcel. 166 */ fromParcel(PrivateDnsConfigParcel parcel)167 public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) { 168 InetAddress[] ips = new InetAddress[parcel.ips.length]; 169 ips = fromParcelableArray(parcel.ips, IpConfigurationParcelableUtil::unparcelAddress) 170 .toArray(ips); 171 172 // For compatibility. If the sender (Tethering module) is using an old version (< 19) of 173 // NetworkStack AIDL that `privateDnsMode` field is not present, `privateDnsMode` will be 174 // assigned from the default value -1. Let `privateDnsMode` assigned based on the hostname. 175 // In this case, there is a harmless bug that the receiver (NetworkStack module) can't 176 // convert the parcel to a PrivateDnsConfig that indicates opportunistic mode. 177 // The bug is harmless because 1) the bug exists for years without any problems and 178 // 2) NetworkMonitor cares PrivateDnsConfig that indicates strict/off mode only. 179 // If the sender is using new version (>=19) while the receiver is using an old version, 180 // the above mentioned harmless bug will persist. Except for that harmless bug, there 181 // should be no other issues. New version's toParcel() doesn't change how the pre-existing 182 // fields `hostname` and `ips` are assigned. 183 if (parcel.privateDnsMode == -1) { 184 return new PrivateDnsConfig(parcel.hostname, ips); 185 } 186 187 InetAddress[] dohIps = new InetAddress[parcel.dohIps.length]; 188 dohIps = fromParcelableArray(parcel.dohIps, 189 IpConfigurationParcelableUtil::unparcelAddress).toArray(dohIps); 190 return new PrivateDnsConfig(parcel.privateDnsMode, parcel.hostname, ips, parcel.dohName, 191 dohIps, parcel.dohPath, parcel.dohPort); 192 } 193 } 194