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