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