1 /* 2 * Copyright (C) 2014 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 com.android.server.net; 18 19 import android.net.IpConfiguration; 20 import android.net.IpConfiguration.IpAssignment; 21 import android.net.IpConfiguration.ProxySettings; 22 import android.net.LinkAddress; 23 import android.net.NetworkUtils; 24 import android.net.ProxyInfo; 25 import android.net.RouteInfo; 26 import android.net.StaticIpConfiguration; 27 import android.net.Uri; 28 import android.util.ArrayMap; 29 import android.util.Log; 30 import android.util.SparseArray; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 34 import java.io.BufferedInputStream; 35 import java.io.DataInputStream; 36 import java.io.DataOutputStream; 37 import java.io.EOFException; 38 import java.io.FileInputStream; 39 import java.io.FileNotFoundException; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.net.Inet4Address; 43 import java.net.InetAddress; 44 45 public class IpConfigStore { 46 private static final String TAG = "IpConfigStore"; 47 private static final boolean DBG = false; 48 49 protected final DelayedDiskWrite mWriter; 50 51 /* IP and proxy configuration keys */ 52 protected static final String ID_KEY = "id"; 53 protected static final String IP_ASSIGNMENT_KEY = "ipAssignment"; 54 protected static final String LINK_ADDRESS_KEY = "linkAddress"; 55 protected static final String GATEWAY_KEY = "gateway"; 56 protected static final String DNS_KEY = "dns"; 57 protected static final String PROXY_SETTINGS_KEY = "proxySettings"; 58 protected static final String PROXY_HOST_KEY = "proxyHost"; 59 protected static final String PROXY_PORT_KEY = "proxyPort"; 60 protected static final String PROXY_PAC_FILE = "proxyPac"; 61 protected static final String EXCLUSION_LIST_KEY = "exclusionList"; 62 protected static final String EOS = "eos"; 63 64 protected static final int IPCONFIG_FILE_VERSION = 3; 65 IpConfigStore(DelayedDiskWrite writer)66 public IpConfigStore(DelayedDiskWrite writer) { 67 mWriter = writer; 68 } 69 IpConfigStore()70 public IpConfigStore() { 71 this(new DelayedDiskWrite()); 72 } 73 writeConfig(DataOutputStream out, String configKey, IpConfiguration config)74 private static boolean writeConfig(DataOutputStream out, String configKey, 75 IpConfiguration config) throws IOException { 76 return writeConfig(out, configKey, config, IPCONFIG_FILE_VERSION); 77 } 78 79 @VisibleForTesting writeConfig(DataOutputStream out, String configKey, IpConfiguration config, int version)80 public static boolean writeConfig(DataOutputStream out, String configKey, 81 IpConfiguration config, int version) throws IOException { 82 boolean written = false; 83 84 try { 85 switch (config.ipAssignment) { 86 case STATIC: 87 out.writeUTF(IP_ASSIGNMENT_KEY); 88 out.writeUTF(config.ipAssignment.toString()); 89 StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration; 90 if (staticIpConfiguration != null) { 91 if (staticIpConfiguration.ipAddress != null) { 92 LinkAddress ipAddress = staticIpConfiguration.ipAddress; 93 out.writeUTF(LINK_ADDRESS_KEY); 94 out.writeUTF(ipAddress.getAddress().getHostAddress()); 95 out.writeInt(ipAddress.getPrefixLength()); 96 } 97 if (staticIpConfiguration.gateway != null) { 98 out.writeUTF(GATEWAY_KEY); 99 out.writeInt(0); // Default route. 100 out.writeInt(1); // Have a gateway. 101 out.writeUTF(staticIpConfiguration.gateway.getHostAddress()); 102 } 103 for (InetAddress inetAddr : staticIpConfiguration.dnsServers) { 104 out.writeUTF(DNS_KEY); 105 out.writeUTF(inetAddr.getHostAddress()); 106 } 107 } 108 written = true; 109 break; 110 case DHCP: 111 out.writeUTF(IP_ASSIGNMENT_KEY); 112 out.writeUTF(config.ipAssignment.toString()); 113 written = true; 114 break; 115 case UNASSIGNED: 116 /* Ignore */ 117 break; 118 default: 119 loge("Ignore invalid ip assignment while writing"); 120 break; 121 } 122 123 switch (config.proxySettings) { 124 case STATIC: 125 ProxyInfo proxyProperties = config.httpProxy; 126 String exclusionList = proxyProperties.getExclusionListAsString(); 127 out.writeUTF(PROXY_SETTINGS_KEY); 128 out.writeUTF(config.proxySettings.toString()); 129 out.writeUTF(PROXY_HOST_KEY); 130 out.writeUTF(proxyProperties.getHost()); 131 out.writeUTF(PROXY_PORT_KEY); 132 out.writeInt(proxyProperties.getPort()); 133 if (exclusionList != null) { 134 out.writeUTF(EXCLUSION_LIST_KEY); 135 out.writeUTF(exclusionList); 136 } 137 written = true; 138 break; 139 case PAC: 140 ProxyInfo proxyPacProperties = config.httpProxy; 141 out.writeUTF(PROXY_SETTINGS_KEY); 142 out.writeUTF(config.proxySettings.toString()); 143 out.writeUTF(PROXY_PAC_FILE); 144 out.writeUTF(proxyPacProperties.getPacFileUrl().toString()); 145 written = true; 146 break; 147 case NONE: 148 out.writeUTF(PROXY_SETTINGS_KEY); 149 out.writeUTF(config.proxySettings.toString()); 150 written = true; 151 break; 152 case UNASSIGNED: 153 /* Ignore */ 154 break; 155 default: 156 loge("Ignore invalid proxy settings while writing"); 157 break; 158 } 159 160 if (written) { 161 out.writeUTF(ID_KEY); 162 if (version < 3) { 163 out.writeInt(Integer.valueOf(configKey)); 164 } else { 165 out.writeUTF(configKey); 166 } 167 } 168 } catch (NullPointerException e) { 169 loge("Failure in writing " + config + e); 170 } 171 out.writeUTF(EOS); 172 173 return written; 174 } 175 176 /** 177 * @Deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead. 178 * New method uses string as network identifier which could be interface name or MAC address or 179 * other token. 180 */ 181 @Deprecated writeIpAndProxyConfigurationsToFile(String filePath, final SparseArray<IpConfiguration> networks)182 public void writeIpAndProxyConfigurationsToFile(String filePath, 183 final SparseArray<IpConfiguration> networks) { 184 mWriter.write(filePath, out -> { 185 out.writeInt(IPCONFIG_FILE_VERSION); 186 for(int i = 0; i < networks.size(); i++) { 187 writeConfig(out, String.valueOf(networks.keyAt(i)), networks.valueAt(i)); 188 } 189 }); 190 } 191 writeIpConfigurations(String filePath, ArrayMap<String, IpConfiguration> networks)192 public void writeIpConfigurations(String filePath, 193 ArrayMap<String, IpConfiguration> networks) { 194 mWriter.write(filePath, out -> { 195 out.writeInt(IPCONFIG_FILE_VERSION); 196 for(int i = 0; i < networks.size(); i++) { 197 writeConfig(out, networks.keyAt(i), networks.valueAt(i)); 198 } 199 }); 200 } 201 readIpConfigurations(String filePath)202 public static ArrayMap<String, IpConfiguration> readIpConfigurations(String filePath) { 203 BufferedInputStream bufferedInputStream; 204 try { 205 bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath)); 206 } catch (FileNotFoundException e) { 207 // Return an empty array here because callers expect an empty array when the file is 208 // not present. 209 loge("Error opening configuration file: " + e); 210 return new ArrayMap<>(0); 211 } 212 return readIpConfigurations(bufferedInputStream); 213 } 214 215 /** @Deprecated use {@link #readIpConfigurations(String)} */ 216 @Deprecated readIpAndProxyConfigurations(String filePath)217 public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) { 218 BufferedInputStream bufferedInputStream; 219 try { 220 bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath)); 221 } catch (FileNotFoundException e) { 222 // Return an empty array here because callers expect an empty array when the file is 223 // not present. 224 loge("Error opening configuration file: " + e); 225 return new SparseArray<>(); 226 } 227 return readIpAndProxyConfigurations(bufferedInputStream); 228 } 229 230 /** @Deprecated use {@link #readIpConfigurations(InputStream)} */ 231 @Deprecated readIpAndProxyConfigurations( InputStream inputStream)232 public static SparseArray<IpConfiguration> readIpAndProxyConfigurations( 233 InputStream inputStream) { 234 ArrayMap<String, IpConfiguration> networks = readIpConfigurations(inputStream); 235 if (networks == null) { 236 return null; 237 } 238 239 SparseArray<IpConfiguration> networksById = new SparseArray<>(); 240 for (int i = 0; i < networks.size(); i++) { 241 int id = Integer.valueOf(networks.keyAt(i)); 242 networksById.put(id, networks.valueAt(i)); 243 } 244 245 return networksById; 246 } 247 248 /** Returns a map of network identity token and {@link IpConfiguration}. */ readIpConfigurations( InputStream inputStream)249 public static ArrayMap<String, IpConfiguration> readIpConfigurations( 250 InputStream inputStream) { 251 ArrayMap<String, IpConfiguration> networks = new ArrayMap<>(); 252 DataInputStream in = null; 253 try { 254 in = new DataInputStream(inputStream); 255 256 int version = in.readInt(); 257 if (version != 3 && version != 2 && version != 1) { 258 loge("Bad version on IP configuration file, ignore read"); 259 return null; 260 } 261 262 while (true) { 263 String uniqueToken = null; 264 // Default is DHCP with no proxy 265 IpAssignment ipAssignment = IpAssignment.DHCP; 266 ProxySettings proxySettings = ProxySettings.NONE; 267 StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration(); 268 String proxyHost = null; 269 String pacFileUrl = null; 270 int proxyPort = -1; 271 String exclusionList = null; 272 String key; 273 274 do { 275 key = in.readUTF(); 276 try { 277 if (key.equals(ID_KEY)) { 278 if (version < 3) { 279 int id = in.readInt(); 280 uniqueToken = String.valueOf(id); 281 } else { 282 uniqueToken = in.readUTF(); 283 } 284 } else if (key.equals(IP_ASSIGNMENT_KEY)) { 285 ipAssignment = IpAssignment.valueOf(in.readUTF()); 286 } else if (key.equals(LINK_ADDRESS_KEY)) { 287 LinkAddress linkAddr = new LinkAddress( 288 NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt()); 289 if (linkAddr.getAddress() instanceof Inet4Address && 290 staticIpConfiguration.ipAddress == null) { 291 staticIpConfiguration.ipAddress = linkAddr; 292 } else { 293 loge("Non-IPv4 or duplicate address: " + linkAddr); 294 } 295 } else if (key.equals(GATEWAY_KEY)) { 296 LinkAddress dest = null; 297 InetAddress gateway = null; 298 if (version == 1) { 299 // only supported default gateways - leave the dest/prefix empty 300 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 301 if (staticIpConfiguration.gateway == null) { 302 staticIpConfiguration.gateway = gateway; 303 } else { 304 loge("Duplicate gateway: " + gateway.getHostAddress()); 305 } 306 } else { 307 if (in.readInt() == 1) { 308 dest = new LinkAddress( 309 NetworkUtils.numericToInetAddress(in.readUTF()), 310 in.readInt()); 311 } 312 if (in.readInt() == 1) { 313 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 314 } 315 RouteInfo route = new RouteInfo(dest, gateway); 316 if (route.isIPv4Default() && 317 staticIpConfiguration.gateway == null) { 318 staticIpConfiguration.gateway = gateway; 319 } else { 320 loge("Non-IPv4 default or duplicate route: " + route); 321 } 322 } 323 } else if (key.equals(DNS_KEY)) { 324 staticIpConfiguration.dnsServers.add( 325 NetworkUtils.numericToInetAddress(in.readUTF())); 326 } else if (key.equals(PROXY_SETTINGS_KEY)) { 327 proxySettings = ProxySettings.valueOf(in.readUTF()); 328 } else if (key.equals(PROXY_HOST_KEY)) { 329 proxyHost = in.readUTF(); 330 } else if (key.equals(PROXY_PORT_KEY)) { 331 proxyPort = in.readInt(); 332 } else if (key.equals(PROXY_PAC_FILE)) { 333 pacFileUrl = in.readUTF(); 334 } else if (key.equals(EXCLUSION_LIST_KEY)) { 335 exclusionList = in.readUTF(); 336 } else if (key.equals(EOS)) { 337 break; 338 } else { 339 loge("Ignore unknown key " + key + "while reading"); 340 } 341 } catch (IllegalArgumentException e) { 342 loge("Ignore invalid address while reading" + e); 343 } 344 } while (true); 345 346 if (uniqueToken != null) { 347 IpConfiguration config = new IpConfiguration(); 348 networks.put(uniqueToken, config); 349 350 switch (ipAssignment) { 351 case STATIC: 352 config.staticIpConfiguration = staticIpConfiguration; 353 config.ipAssignment = ipAssignment; 354 break; 355 case DHCP: 356 config.ipAssignment = ipAssignment; 357 break; 358 case UNASSIGNED: 359 loge("BUG: Found UNASSIGNED IP on file, use DHCP"); 360 config.ipAssignment = IpAssignment.DHCP; 361 break; 362 default: 363 loge("Ignore invalid ip assignment while reading."); 364 config.ipAssignment = IpAssignment.UNASSIGNED; 365 break; 366 } 367 368 switch (proxySettings) { 369 case STATIC: 370 ProxyInfo proxyInfo = 371 new ProxyInfo(proxyHost, proxyPort, exclusionList); 372 config.proxySettings = proxySettings; 373 config.httpProxy = proxyInfo; 374 break; 375 case PAC: 376 ProxyInfo proxyPacProperties = new ProxyInfo(Uri.parse(pacFileUrl)); 377 config.proxySettings = proxySettings; 378 config.httpProxy = proxyPacProperties; 379 break; 380 case NONE: 381 config.proxySettings = proxySettings; 382 break; 383 case UNASSIGNED: 384 loge("BUG: Found UNASSIGNED proxy on file, use NONE"); 385 config.proxySettings = ProxySettings.NONE; 386 break; 387 default: 388 loge("Ignore invalid proxy settings while reading"); 389 config.proxySettings = ProxySettings.UNASSIGNED; 390 break; 391 } 392 } else { 393 if (DBG) log("Missing id while parsing configuration"); 394 } 395 } 396 } catch (EOFException ignore) { 397 } catch (IOException e) { 398 loge("Error parsing configuration: " + e); 399 } finally { 400 if (in != null) { 401 try { 402 in.close(); 403 } catch (Exception e) {} 404 } 405 } 406 407 return networks; 408 } 409 loge(String s)410 protected static void loge(String s) { 411 Log.e(TAG, s); 412 } 413 log(String s)414 protected static void log(String s) { 415 Log.d(TAG, s); 416 } 417 } 418