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