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