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 out.writeUTF(EXCLUSION_LIST_KEY); 126 out.writeUTF(exclusionList); 127 written = true; 128 break; 129 case PAC: 130 ProxyInfo proxyPacProperties = config.httpProxy; 131 out.writeUTF(PROXY_SETTINGS_KEY); 132 out.writeUTF(config.proxySettings.toString()); 133 out.writeUTF(PROXY_PAC_FILE); 134 out.writeUTF(proxyPacProperties.getPacFileUrl().toString()); 135 written = true; 136 break; 137 case NONE: 138 out.writeUTF(PROXY_SETTINGS_KEY); 139 out.writeUTF(config.proxySettings.toString()); 140 written = true; 141 break; 142 case UNASSIGNED: 143 /* Ignore */ 144 break; 145 default: 146 loge("Ignore invalid proxy settings while writing"); 147 break; 148 } 149 150 if (written) { 151 out.writeUTF(ID_KEY); 152 out.writeInt(configKey); 153 } 154 } catch (NullPointerException e) { 155 loge("Failure in writing " + config + e); 156 } 157 out.writeUTF(EOS); 158 159 return written; 160 } 161 162 public void writeIpAndProxyConfigurations(String filePath, 163 final SparseArray<IpConfiguration> networks) { 164 mWriter.write(filePath, new DelayedDiskWrite.Writer() { 165 public void onWriteCalled(DataOutputStream out) throws IOException{ 166 out.writeInt(IPCONFIG_FILE_VERSION); 167 for(int i = 0; i < networks.size(); i++) { 168 writeConfig(out, networks.keyAt(i), networks.valueAt(i)); 169 } 170 } 171 }); 172 } 173 174 public SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) { 175 SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); 176 177 DataInputStream in = null; 178 try { 179 in = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath))); 180 181 int version = in.readInt(); 182 if (version != 2 && version != 1) { 183 loge("Bad version on IP configuration file, ignore read"); 184 return null; 185 } 186 187 while (true) { 188 int id = -1; 189 // Default is DHCP with no proxy 190 IpAssignment ipAssignment = IpAssignment.DHCP; 191 ProxySettings proxySettings = ProxySettings.NONE; 192 StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration(); 193 String proxyHost = null; 194 String pacFileUrl = null; 195 int proxyPort = -1; 196 String exclusionList = null; 197 String key; 198 199 do { 200 key = in.readUTF(); 201 try { 202 if (key.equals(ID_KEY)) { 203 id = in.readInt(); 204 } else if (key.equals(IP_ASSIGNMENT_KEY)) { 205 ipAssignment = IpAssignment.valueOf(in.readUTF()); 206 } else if (key.equals(LINK_ADDRESS_KEY)) { 207 LinkAddress linkAddr = new LinkAddress( 208 NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt()); 209 if (linkAddr.getAddress() instanceof Inet4Address && 210 staticIpConfiguration.ipAddress == null) { 211 staticIpConfiguration.ipAddress = linkAddr; 212 } else { 213 loge("Non-IPv4 or duplicate address: " + linkAddr); 214 } 215 } else if (key.equals(GATEWAY_KEY)) { 216 LinkAddress dest = null; 217 InetAddress gateway = null; 218 if (version == 1) { 219 // only supported default gateways - leave the dest/prefix empty 220 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 221 if (staticIpConfiguration.gateway == null) { 222 staticIpConfiguration.gateway = gateway; 223 } else { 224 loge("Duplicate gateway: " + gateway.getHostAddress()); 225 } 226 } else { 227 if (in.readInt() == 1) { 228 dest = new LinkAddress( 229 NetworkUtils.numericToInetAddress(in.readUTF()), 230 in.readInt()); 231 } 232 if (in.readInt() == 1) { 233 gateway = NetworkUtils.numericToInetAddress(in.readUTF()); 234 } 235 RouteInfo route = new RouteInfo(dest, gateway); 236 if (route.isIPv4Default() && 237 staticIpConfiguration.gateway == null) { 238 staticIpConfiguration.gateway = gateway; 239 } else { 240 loge("Non-IPv4 default or duplicate route: " + route); 241 } 242 } 243 } else if (key.equals(DNS_KEY)) { 244 staticIpConfiguration.dnsServers.add( 245 NetworkUtils.numericToInetAddress(in.readUTF())); 246 } else if (key.equals(PROXY_SETTINGS_KEY)) { 247 proxySettings = ProxySettings.valueOf(in.readUTF()); 248 } else if (key.equals(PROXY_HOST_KEY)) { 249 proxyHost = in.readUTF(); 250 } else if (key.equals(PROXY_PORT_KEY)) { 251 proxyPort = in.readInt(); 252 } else if (key.equals(PROXY_PAC_FILE)) { 253 pacFileUrl = in.readUTF(); 254 } else if (key.equals(EXCLUSION_LIST_KEY)) { 255 exclusionList = in.readUTF(); 256 } else if (key.equals(EOS)) { 257 break; 258 } else { 259 loge("Ignore unknown key " + key + "while reading"); 260 } 261 } catch (IllegalArgumentException e) { 262 loge("Ignore invalid address while reading" + e); 263 } 264 } while (true); 265 266 if (id != -1) { 267 IpConfiguration config = new IpConfiguration(); 268 networks.put(id, config); 269 270 switch (ipAssignment) { 271 case STATIC: 272 config.staticIpConfiguration = staticIpConfiguration; 273 config.ipAssignment = ipAssignment; 274 break; 275 case DHCP: 276 config.ipAssignment = ipAssignment; 277 break; 278 case UNASSIGNED: 279 loge("BUG: Found UNASSIGNED IP on file, use DHCP"); 280 config.ipAssignment = IpAssignment.DHCP; 281 break; 282 default: 283 loge("Ignore invalid ip assignment while reading."); 284 config.ipAssignment = IpAssignment.UNASSIGNED; 285 break; 286 } 287 288 switch (proxySettings) { 289 case STATIC: 290 ProxyInfo proxyInfo = 291 new ProxyInfo(proxyHost, proxyPort, exclusionList); 292 config.proxySettings = proxySettings; 293 config.httpProxy = proxyInfo; 294 break; 295 case PAC: 296 ProxyInfo proxyPacProperties = new ProxyInfo(pacFileUrl); 297 config.proxySettings = proxySettings; 298 config.httpProxy = proxyPacProperties; 299 break; 300 case NONE: 301 config.proxySettings = proxySettings; 302 break; 303 case UNASSIGNED: 304 loge("BUG: Found UNASSIGNED proxy on file, use NONE"); 305 config.proxySettings = ProxySettings.NONE; 306 break; 307 default: 308 loge("Ignore invalid proxy settings while reading"); 309 config.proxySettings = ProxySettings.UNASSIGNED; 310 break; 311 } 312 } else { 313 if (DBG) log("Missing id while parsing configuration"); 314 } 315 } 316 } catch (EOFException ignore) { 317 } catch (IOException e) { 318 loge("Error parsing configuration: " + e); 319 } finally { 320 if (in != null) { 321 try { 322 in.close(); 323 } catch (Exception e) {} 324 } 325 } 326 327 return networks; 328 } 329 330 protected void loge(String s) { 331 Log.e(TAG, s); 332 } 333 334 protected void log(String s) { 335 Log.d(TAG, s); 336 } 337 } 338