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
17package com.android.server.net;
18
19import android.net.IpConfiguration;
20import android.net.IpConfiguration.IpAssignment;
21import android.net.IpConfiguration.ProxySettings;
22import android.net.LinkAddress;
23import android.net.NetworkUtils;
24import android.net.ProxyInfo;
25import android.net.RouteInfo;
26import android.net.StaticIpConfiguration;
27import android.util.Log;
28import android.util.SparseArray;
29
30import com.android.internal.annotations.VisibleForTesting;
31import com.android.server.net.DelayedDiskWrite;
32
33import java.io.BufferedInputStream;
34import java.io.DataInputStream;
35import java.io.DataOutputStream;
36import java.io.EOFException;
37import java.io.FileInputStream;
38import java.io.FileNotFoundException;
39import java.io.IOException;
40import java.io.InputStream;
41import java.net.InetAddress;
42import java.net.Inet4Address;
43
44public 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