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