1/*
2 * Copyright (C) 2011 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.internal.net;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.text.TextUtils;
22import android.util.Log;
23
24import java.net.InetAddress;
25import java.nio.charset.Charsets;
26
27/**
28 * Parcel-like entity class for VPN profiles. To keep things simple, all
29 * fields are package private. Methods are provided for serialization, so
30 * storage can be implemented easily. Two rules are set for this class.
31 * First, all fields must be kept non-null. Second, always make a copy
32 * using clone() before modifying.
33 *
34 * @hide
35 */
36public class VpnProfile implements Cloneable, Parcelable {
37    private static final String TAG = "VpnProfile";
38
39    // Match these constants with R.array.vpn_types.
40    public static final int TYPE_PPTP = 0;
41    public static final int TYPE_L2TP_IPSEC_PSK = 1;
42    public static final int TYPE_L2TP_IPSEC_RSA = 2;
43    public static final int TYPE_IPSEC_XAUTH_PSK = 3;
44    public static final int TYPE_IPSEC_XAUTH_RSA = 4;
45    public static final int TYPE_IPSEC_HYBRID_RSA = 5;
46    public static final int TYPE_MAX = 5;
47
48    // Entity fields.
49    public final String key;           // -1
50    public String name = "";           // 0
51    public int type = TYPE_PPTP;       // 1
52    public String server = "";         // 2
53    public String username = "";       // 3
54    public String password = "";       // 4
55    public String dnsServers = "";     // 5
56    public String searchDomains = "";  // 6
57    public String routes = "";         // 7
58    public boolean mppe = true;        // 8
59    public String l2tpSecret = "";     // 9
60    public String ipsecIdentifier = "";// 10
61    public String ipsecSecret = "";    // 11
62    public String ipsecUserCert = "";  // 12
63    public String ipsecCaCert = "";    // 13
64    public String ipsecServerCert = "";// 14
65
66    // Helper fields.
67    public boolean saveLogin = false;
68
69    public VpnProfile(String key) {
70        this.key = key;
71    }
72
73    public VpnProfile(Parcel in) {
74        key = in.readString();
75        name = in.readString();
76        type = in.readInt();
77        server = in.readString();
78        username = in.readString();
79        password = in.readString();
80        dnsServers = in.readString();
81        searchDomains = in.readString();
82        routes = in.readString();
83        mppe = in.readInt() != 0;
84        l2tpSecret = in.readString();
85        ipsecIdentifier = in.readString();
86        ipsecSecret = in.readString();
87        ipsecUserCert = in.readString();
88        ipsecCaCert = in.readString();
89        ipsecServerCert = in.readString();
90        saveLogin = in.readInt() != 0;
91    }
92
93    @Override
94    public void writeToParcel(Parcel out, int flags) {
95        out.writeString(key);
96        out.writeString(name);
97        out.writeInt(type);
98        out.writeString(server);
99        out.writeString(username);
100        out.writeString(password);
101        out.writeString(dnsServers);
102        out.writeString(searchDomains);
103        out.writeString(routes);
104        out.writeInt(mppe ? 1 : 0);
105        out.writeString(l2tpSecret);
106        out.writeString(ipsecIdentifier);
107        out.writeString(ipsecSecret);
108        out.writeString(ipsecUserCert);
109        out.writeString(ipsecCaCert);
110        out.writeString(ipsecServerCert);
111        out.writeInt(saveLogin ? 1 : 0);
112    }
113
114    public static VpnProfile decode(String key, byte[] value) {
115        try {
116            if (key == null) {
117                return null;
118            }
119
120            String[] values = new String(value, Charsets.UTF_8).split("\0", -1);
121            // There can be 14 or 15 values in ICS MR1.
122            if (values.length < 14 || values.length > 15) {
123                return null;
124            }
125
126            VpnProfile profile = new VpnProfile(key);
127            profile.name = values[0];
128            profile.type = Integer.valueOf(values[1]);
129            if (profile.type < 0 || profile.type > TYPE_MAX) {
130                return null;
131            }
132            profile.server = values[2];
133            profile.username = values[3];
134            profile.password = values[4];
135            profile.dnsServers = values[5];
136            profile.searchDomains = values[6];
137            profile.routes = values[7];
138            profile.mppe = Boolean.valueOf(values[8]);
139            profile.l2tpSecret = values[9];
140            profile.ipsecIdentifier = values[10];
141            profile.ipsecSecret = values[11];
142            profile.ipsecUserCert = values[12];
143            profile.ipsecCaCert = values[13];
144            profile.ipsecServerCert = (values.length > 14) ? values[14] : "";
145
146            profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
147            return profile;
148        } catch (Exception e) {
149            // ignore
150        }
151        return null;
152    }
153
154    public byte[] encode() {
155        StringBuilder builder = new StringBuilder(name);
156        builder.append('\0').append(type);
157        builder.append('\0').append(server);
158        builder.append('\0').append(saveLogin ? username : "");
159        builder.append('\0').append(saveLogin ? password : "");
160        builder.append('\0').append(dnsServers);
161        builder.append('\0').append(searchDomains);
162        builder.append('\0').append(routes);
163        builder.append('\0').append(mppe);
164        builder.append('\0').append(l2tpSecret);
165        builder.append('\0').append(ipsecIdentifier);
166        builder.append('\0').append(ipsecSecret);
167        builder.append('\0').append(ipsecUserCert);
168        builder.append('\0').append(ipsecCaCert);
169        builder.append('\0').append(ipsecServerCert);
170        return builder.toString().getBytes(Charsets.UTF_8);
171    }
172
173    /**
174     * Test if profile is valid for lockdown, which requires IPv4 address for
175     * both server and DNS. Server hostnames would require using DNS before
176     * connection.
177     */
178    public boolean isValidLockdownProfile() {
179        try {
180            InetAddress.parseNumericAddress(server);
181
182            for (String dnsServer : dnsServers.split(" +")) {
183                InetAddress.parseNumericAddress(this.dnsServers);
184            }
185            if (TextUtils.isEmpty(dnsServers)) {
186                Log.w(TAG, "DNS required");
187                return false;
188            }
189
190            // Everything checked out above
191            return true;
192
193        } catch (IllegalArgumentException e) {
194            Log.w(TAG, "Invalid address", e);
195            return false;
196        }
197    }
198
199    public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
200        @Override
201        public VpnProfile createFromParcel(Parcel in) {
202            return new VpnProfile(in);
203        }
204
205        @Override
206        public VpnProfile[] newArray(int size) {
207            return new VpnProfile[size];
208        }
209    };
210
211    @Override
212    public int describeContents() {
213        return 0;
214    }
215}
216