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.Log; 28 import android.util.SparseArray; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.server.net.DelayedDiskWrite; 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.InetAddress; 42 import java.net.Inet4Address; 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 = 2; 64 65 public IpConfigStore(DelayedDiskWrite writer) { 66 mWriter = writer; 67 } 68 69 public IpConfigStore() { 70 this(new DelayedDiskWrite()); 71 } 72 73 @VisibleForTesting 74 public static boolean writeConfig(DataOutputStream out, int configKey, 75 IpConfiguration config) throws IOException { 76 boolean written = false; 77 78 try { 79 switch (config.ipAssignment) { 80 case STATIC: 81 out.writeUTF(IP_ASSIGNMENT_KEY); 82 out.writeUTF(config.ipAssignment.toString()); 83 StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration; 84 if (staticIpConfiguration != null) { 85 if (staticIpConfiguration.ipAddress != null) { 86 LinkAddress ipAddress = staticIpConfiguration.ipAddress; 87 out.writeUTF(LINK_ADDRESS_KEY); 88 out.writeUTF(ipAddress.getAddress().getHostAddress()); 89 out.writeInt(ipAddress.getPrefixLength()); 90 } 91 if (staticIpConfiguration.gateway != null) { 92 out.writeUTF(GATEWAY_KEY); 93 out.writeInt(0); // Default route. 94 out.writeInt(1); // Have a gateway. 95 out.writeUTF(staticIpConfiguration.gateway.getHostAddress()); 96 } 97 for (InetAddress inetAddr : staticIpConfiguration.dnsServers) { 98 out.writeUTF(DNS_KEY); 99 out.writeUTF(inetAddr.getHostAddress()); 100 } 101 } 102 written = true; 103 break; 104 case DHCP: 105 out.writeUTF(IP_ASSIGNMENT_KEY); 106 out.writeUTF(config.ipAssignment.toString()); 107 written = true; 108 break; 109 case UNASSIGNED: 110 /* Ignore */ 111 break; 112 default: 113 loge("Ignore invalid ip assignment while writing"); 114 break; 115 } 116 117 switch (config.proxySettings) { 118 case STATIC: 119 ProxyInfo proxyProperties = config.httpProxy; 120 String exclusionList = proxyProperties.getExclusionListAsString(); 121 out.writeUTF(PROXY_SETTINGS_KEY); 122 out.writeUTF(config.proxySettings.toString()); 123 out.writeUTF(PROXY_HOST_KEY); 124 out.writeUTF(proxyProperties.getHost()); 125 out.writeUTF(PROXY_PORT_KEY); 126 out.writeInt(proxyProperties.getPort()); 127 if (exclusionList != null) { 128 out.writeUTF(EXCLUSION_LIST_KEY); 129 out.writeUTF(exclusionList); 130 } 131 written = true; 132 break; 133 case PAC: 134 ProxyInfo proxyPacProperties = config.httpProxy; 135 out.writeUTF(PROXY_SETTINGS_KEY); 136 out.writeUTF(config.proxySettings.toString()); 137 out.writeUTF(PROXY_PAC_FILE); 138 out.writeUTF(proxyPacProperties.getPacFileUrl().toString()); 139 written = true; 140 break; 141 case NONE: 142 out.writeUTF(PROXY_SETTINGS_KEY); 143 out.writeUTF(config.proxySettings.toString()); 144 written = true; 145 break; 146 case UNASSIGNED: 147 /* Ignore */ 148 break; 149 default: 150 loge("Ignore invalid proxy settings while writing"); 151 break; 152 } 153 154 if (written) { 155 out.writeUTF(ID_KEY); 156 out.writeInt(configKey); 157 } 158 } catch (NullPointerException e) { 159 loge("Failure in writing " + config + e); 160 } 161 out.writeUTF(EOS); 162 163 return written; 164 } 165 166 public void writeIpAndProxyConfigurations(String filePath, 167 final SparseArray<IpConfiguration> networks) { 168 mWriter.write(filePath, new DelayedDiskWrite.Writer() { 169 public void onWriteCalled(DataOutputStream out) throws IOException{ 170 out.writeInt(IPCONFIG_FILE_VERSION); 171 for(int i = 0; i < networks.size(); i++) { 172 writeConfig(out, networks.keyAt(i), networks.valueAt(i)); 173 } 174 } 175 }); 176 } 177 178 public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) { 179 BufferedInputStream bufferedInputStream; 180 try { 181 bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath)); 182 } catch (FileNotFoundException e) { 183 // Return an empty array here because callers expect an empty array when the file is 184 // not present. 185 loge("Error opening configuration file: " + e); 186 return new SparseArray<>(); 187 } 188 return readIpAndProxyConfigurations(bufferedInputStream); 189 } 190 191 public static SparseArray<IpConfiguration> readIpAndProxyConfigurations( 192 InputStream inputStream) { 193 SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); 194 DataInputStream in = null; 195 try { 196 in = new DataInputStream(inputStream); 197 198 int version = in.readInt(); 199 if (version != 2 && version != 1) { 200 loge("Bad version on IP configuration file, ignore read"); 201 return null; 202 } 203 204 while (true) { 205 int id = -1; 206 // Default is DHCP with no proxy 207 IpAssignment ipAssignment = IpAssignment.DHCP; 208 ProxySettings proxySettings = ProxySettings.NONE; 209 StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration(); 210 String proxyHost = null; 211 String pacFileUrl = null; 212 int proxyPort = -1; 213 String exclusionList = null; 214 String key; 215 216 do { 217 key = in.readUTF(); 218 try { 219 if (key.equals(ID_KEY)) { 220 id = in.readInt(); 221 } else if (key.equals(IP_ASSIGNMENT_KEY)) { 222 ipAssignment = IpAssignment.valueOf(in.readUTF()); 223 } else if (key.equals(LINK_ADDRESS_KEY)) { 224 LinkAddress linkAddr = new LinkAddress( 225 NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt()); 226 if (linkAddr.getAddress() instanceof Inet4Address && 227 staticIpConfiguration.ipAddress == null) { 228 staticIpConfiguration.ipAddress = linkAddr; 229 } else { 230 loge("Non-IPv4 or duplicate address: " + linkAddr); 231 } 232 } else if (key.equals(GATEWAY_KEY)) { 233 LinkAddress dest = null; 234 InetAddress gateway = null; 235 if (version == 1) { 236 // only supported default gateways - leave the dest/prefix empty 237 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 238 if (staticIpConfiguration.gateway == null) { 239 staticIpConfiguration.gateway = gateway; 240 } else { 241 loge("Duplicate gateway: " + gateway.getHostAddress()); 242 } 243 } else { 244 if (in.readInt() == 1) { 245 dest = new LinkAddress( 246 NetworkUtils.numericToInetAddress(in.readUTF()), 247 in.readInt()); 248 } 249 if (in.readInt() == 1) { 250 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 251 } 252 RouteInfo route = new RouteInfo(dest, gateway); 253 if (route.isIPv4Default() && 254 staticIpConfiguration.gateway == null) { 255 staticIpConfiguration.gateway = gateway; 256 } else { 257 loge("Non-IPv4 default or duplicate route: " + route); 258 } 259 } 260 } else if (key.equals(DNS_KEY)) { 261 staticIpConfiguration.dnsServers.add( 262 NetworkUtils.numericToInetAddress(in.readUTF())); 263 } else if (key.equals(PROXY_SETTINGS_KEY)) { 264 proxySettings = ProxySettings.valueOf(in.readUTF()); 265 } else if (key.equals(PROXY_HOST_KEY)) { 266 proxyHost = in.readUTF(); 267 } else if (key.equals(PROXY_PORT_KEY)) { 268 proxyPort = in.readInt(); 269 } else if (key.equals(PROXY_PAC_FILE)) { 270 pacFileUrl = in.readUTF(); 271 } else if (key.equals(EXCLUSION_LIST_KEY)) { 272 exclusionList = in.readUTF(); 273 } else if (key.equals(EOS)) { 274 break; 275 } else { 276 loge("Ignore unknown key " + key + "while reading"); 277 } 278 } catch (IllegalArgumentException e) { 279 loge("Ignore invalid address while reading" + e); 280 } 281 } while (true); 282 283 if (id != -1) { 284 IpConfiguration config = new IpConfiguration(); 285 networks.put(id, config); 286 287 switch (ipAssignment) { 288 case STATIC: 289 config.staticIpConfiguration = staticIpConfiguration; 290 config.ipAssignment = ipAssignment; 291 break; 292 case DHCP: 293 config.ipAssignment = ipAssignment; 294 break; 295 case UNASSIGNED: 296 loge("BUG: Found UNASSIGNED IP on file, use DHCP"); 297 config.ipAssignment = IpAssignment.DHCP; 298 break; 299 default: 300 loge("Ignore invalid ip assignment while reading."); 301 config.ipAssignment = IpAssignment.UNASSIGNED; 302 break; 303 } 304 305 switch (proxySettings) { 306 case STATIC: 307 ProxyInfo proxyInfo = 308 new ProxyInfo(proxyHost, proxyPort, exclusionList); 309 config.proxySettings = proxySettings; 310 config.httpProxy = proxyInfo; 311 break; 312 case PAC: 313 ProxyInfo proxyPacProperties = new ProxyInfo(pacFileUrl); 314 config.proxySettings = proxySettings; 315 config.httpProxy = proxyPacProperties; 316 break; 317 case NONE: 318 config.proxySettings = proxySettings; 319 break; 320 case UNASSIGNED: 321 loge("BUG: Found UNASSIGNED proxy on file, use NONE"); 322 config.proxySettings = ProxySettings.NONE; 323 break; 324 default: 325 loge("Ignore invalid proxy settings while reading"); 326 config.proxySettings = ProxySettings.UNASSIGNED; 327 break; 328 } 329 } else { 330 if (DBG) log("Missing id while parsing configuration"); 331 } 332 } 333 } catch (EOFException ignore) { 334 } catch (IOException e) { 335 loge("Error parsing configuration: " + e); 336 } finally { 337 if (in != null) { 338 try { 339 in.close(); 340 } catch (Exception e) {} 341 } 342 } 343 344 return networks; 345 } 346 347 protected static void loge(String s) { 348 Log.e(TAG, s); 349 } 350 351 protected static void log(String s) { 352 Log.d(TAG, s); 353 } 354 } 355