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.server.net.DelayedDiskWrite;
31
32import java.io.BufferedInputStream;
33import java.io.DataInputStream;
34import java.io.DataOutputStream;
35import java.io.EOFException;
36import java.io.FileInputStream;
37import java.io.IOException;
38import java.net.InetAddress;
39import java.net.Inet4Address;
40
41public class IpConfigStore {
42    private static final String TAG = "IpConfigStore";
43    private static final boolean DBG = false;
44
45    protected final DelayedDiskWrite mWriter;
46
47    /* IP and proxy configuration keys */
48    protected static final String ID_KEY = "id";
49    protected static final String IP_ASSIGNMENT_KEY = "ipAssignment";
50    protected static final String LINK_ADDRESS_KEY = "linkAddress";
51    protected static final String GATEWAY_KEY = "gateway";
52    protected static final String DNS_KEY = "dns";
53    protected static final String PROXY_SETTINGS_KEY = "proxySettings";
54    protected static final String PROXY_HOST_KEY = "proxyHost";
55    protected static final String PROXY_PORT_KEY = "proxyPort";
56    protected static final String PROXY_PAC_FILE = "proxyPac";
57    protected static final String EXCLUSION_LIST_KEY = "exclusionList";
58    protected static final String EOS = "eos";
59
60    protected static final int IPCONFIG_FILE_VERSION = 2;
61
62    public IpConfigStore(DelayedDiskWrite writer) {
63        mWriter = writer;
64    }
65
66    public IpConfigStore() {
67        this(new DelayedDiskWrite());
68    }
69
70    private boolean writeConfig(DataOutputStream out, int configKey,
71                                IpConfiguration config) throws IOException {
72        boolean written = false;
73
74        try {
75            switch (config.ipAssignment) {
76                case STATIC:
77                    out.writeUTF(IP_ASSIGNMENT_KEY);
78                    out.writeUTF(config.ipAssignment.toString());
79                    StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration;
80                    if (staticIpConfiguration != null) {
81                        if (staticIpConfiguration.ipAddress != null) {
82                            LinkAddress ipAddress = staticIpConfiguration.ipAddress;
83                            out.writeUTF(LINK_ADDRESS_KEY);
84                            out.writeUTF(ipAddress.getAddress().getHostAddress());
85                            out.writeInt(ipAddress.getPrefixLength());
86                        }
87                        if (staticIpConfiguration.gateway != null) {
88                            out.writeUTF(GATEWAY_KEY);
89                            out.writeInt(0);  // Default route.
90                            out.writeInt(1);  // Have a gateway.
91                            out.writeUTF(staticIpConfiguration.gateway.getHostAddress());
92                        }
93                        for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {
94                            out.writeUTF(DNS_KEY);
95                            out.writeUTF(inetAddr.getHostAddress());
96                        }
97                    }
98                    written = true;
99                    break;
100                case DHCP:
101                    out.writeUTF(IP_ASSIGNMENT_KEY);
102                    out.writeUTF(config.ipAssignment.toString());
103                    written = true;
104                    break;
105                case UNASSIGNED:
106                /* Ignore */
107                    break;
108                default:
109                    loge("Ignore invalid ip assignment while writing");
110                    break;
111            }
112
113            switch (config.proxySettings) {
114                case STATIC:
115                    ProxyInfo proxyProperties = config.httpProxy;
116                    String exclusionList = proxyProperties.getExclusionListAsString();
117                    out.writeUTF(PROXY_SETTINGS_KEY);
118                    out.writeUTF(config.proxySettings.toString());
119                    out.writeUTF(PROXY_HOST_KEY);
120                    out.writeUTF(proxyProperties.getHost());
121                    out.writeUTF(PROXY_PORT_KEY);
122                    out.writeInt(proxyProperties.getPort());
123                    if (exclusionList != null) {
124                        out.writeUTF(EXCLUSION_LIST_KEY);
125                        out.writeUTF(exclusionList);
126                    }
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