• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.connectivity;
18 
19 import static android.Manifest.permission.BIND_VPN_SERVICE;
20 
21 import android.app.Notification;
22 import android.app.NotificationManager;
23 import android.app.PendingIntent;
24 import android.content.BroadcastReceiver;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.ServiceConnection;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.ResolveInfo;
33 import android.graphics.Bitmap;
34 import android.graphics.Canvas;
35 import android.graphics.drawable.Drawable;
36 import android.net.BaseNetworkStateTracker;
37 import android.net.ConnectivityManager;
38 import android.net.IConnectivityManager;
39 import android.net.INetworkManagementEventObserver;
40 import android.net.LinkProperties;
41 import android.net.LocalSocket;
42 import android.net.LocalSocketAddress;
43 import android.net.NetworkInfo;
44 import android.net.RouteInfo;
45 import android.net.NetworkInfo.DetailedState;
46 import android.os.Binder;
47 import android.os.FileUtils;
48 import android.os.IBinder;
49 import android.os.INetworkManagementService;
50 import android.os.Parcel;
51 import android.os.ParcelFileDescriptor;
52 import android.os.Process;
53 import android.os.RemoteException;
54 import android.os.SystemClock;
55 import android.os.SystemService;
56 import android.os.UserHandle;
57 import android.security.Credentials;
58 import android.security.KeyStore;
59 import android.util.Log;
60 import android.widget.Toast;
61 
62 import com.android.internal.R;
63 import com.android.internal.net.LegacyVpnInfo;
64 import com.android.internal.net.VpnConfig;
65 import com.android.internal.net.VpnProfile;
66 import com.android.internal.util.Preconditions;
67 import com.android.server.ConnectivityService.VpnCallback;
68 import com.android.server.net.BaseNetworkObserver;
69 
70 import java.io.File;
71 import java.io.InputStream;
72 import java.io.OutputStream;
73 import java.net.Inet4Address;
74 import java.net.InetAddress;
75 import java.nio.charset.Charsets;
76 import java.util.Arrays;
77 import java.util.concurrent.atomic.AtomicInteger;
78 
79 import libcore.io.IoUtils;
80 
81 /**
82  * @hide
83  */
84 public class Vpn extends BaseNetworkStateTracker {
85     private static final String TAG = "Vpn";
86     private static final boolean LOGD = true;
87 
88     // TODO: create separate trackers for each unique VPN to support
89     // automated reconnection
90 
91     private final VpnCallback mCallback;
92 
93     private String mPackage = VpnConfig.LEGACY_VPN;
94     private String mInterface;
95     private Connection mConnection;
96     private LegacyVpnRunner mLegacyVpnRunner;
97     private PendingIntent mStatusIntent;
98     private volatile boolean mEnableNotif = true;
99     private volatile boolean mEnableTeardown = true;
100     private final IConnectivityManager mConnService;
101 
Vpn(Context context, VpnCallback callback, INetworkManagementService netService, IConnectivityManager connService)102     public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
103             IConnectivityManager connService) {
104         // TODO: create dedicated TYPE_VPN network type
105         super(ConnectivityManager.TYPE_DUMMY);
106         mContext = context;
107         mCallback = callback;
108         mConnService = connService;
109 
110         try {
111             netService.registerObserver(mObserver);
112         } catch (RemoteException e) {
113             Log.wtf(TAG, "Problem registering observer", e);
114         }
115     }
116 
117     /**
118      * Set if this object is responsible for showing its own notifications. When
119      * {@code false}, notifications are handled externally by someone else.
120      */
setEnableNotifications(boolean enableNotif)121     public void setEnableNotifications(boolean enableNotif) {
122         mEnableNotif = enableNotif;
123     }
124 
125     /**
126      * Set if this object is responsible for watching for {@link NetworkInfo}
127      * teardown. When {@code false}, teardown is handled externally by someone
128      * else.
129      */
setEnableTeardown(boolean enableTeardown)130     public void setEnableTeardown(boolean enableTeardown) {
131         mEnableTeardown = enableTeardown;
132     }
133 
134     @Override
startMonitoringInternal()135     protected void startMonitoringInternal() {
136         // Ignored; events are sent through callbacks for now
137     }
138 
139     @Override
teardown()140     public boolean teardown() {
141         // TODO: finish migration to unique tracker for each VPN
142         throw new UnsupportedOperationException();
143     }
144 
145     @Override
reconnect()146     public boolean reconnect() {
147         // TODO: finish migration to unique tracker for each VPN
148         throw new UnsupportedOperationException();
149     }
150 
151     @Override
getTcpBufferSizesPropName()152     public String getTcpBufferSizesPropName() {
153         return PROP_TCP_BUFFER_UNKNOWN;
154     }
155 
156     /**
157      * Update current state, dispaching event to listeners.
158      */
updateState(DetailedState detailedState, String reason)159     private void updateState(DetailedState detailedState, String reason) {
160         if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
161         mNetworkInfo.setDetailedState(detailedState, reason, null);
162         mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
163     }
164 
165     /**
166      * Prepare for a VPN application. This method is designed to solve
167      * race conditions. It first compares the current prepared package
168      * with {@code oldPackage}. If they are the same, the prepared
169      * package is revoked and replaced with {@code newPackage}. If
170      * {@code oldPackage} is {@code null}, the comparison is omitted.
171      * If {@code newPackage} is the same package or {@code null}, the
172      * revocation is omitted. This method returns {@code true} if the
173      * operation is succeeded.
174      *
175      * Legacy VPN is handled specially since it is not a real package.
176      * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
177      * it can be revoked by itself.
178      *
179      * @param oldPackage The package name of the old VPN application.
180      * @param newPackage The package name of the new VPN application.
181      * @return true if the operation is succeeded.
182      */
prepare(String oldPackage, String newPackage)183     public synchronized boolean prepare(String oldPackage, String newPackage) {
184         // Return false if the package does not match.
185         if (oldPackage != null && !oldPackage.equals(mPackage)) {
186             return false;
187         }
188 
189         // Return true if we do not need to revoke.
190         if (newPackage == null ||
191                 (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
192             return true;
193         }
194 
195         // Check if the caller is authorized.
196         enforceControlPermission();
197 
198         // Reset the interface and hide the notification.
199         if (mInterface != null) {
200             jniReset(mInterface);
201             final long token = Binder.clearCallingIdentity();
202             try {
203                 mCallback.restore();
204                 hideNotification();
205             } finally {
206                 Binder.restoreCallingIdentity(token);
207             }
208             mInterface = null;
209         }
210 
211         // Revoke the connection or stop LegacyVpnRunner.
212         if (mConnection != null) {
213             try {
214                 mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
215                         Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
216             } catch (Exception e) {
217                 // ignore
218             }
219             mContext.unbindService(mConnection);
220             mConnection = null;
221         } else if (mLegacyVpnRunner != null) {
222             mLegacyVpnRunner.exit();
223             mLegacyVpnRunner = null;
224         }
225 
226         Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
227         mPackage = newPackage;
228         updateState(DetailedState.IDLE, "prepare");
229         return true;
230     }
231 
232     /**
233      * Protect a socket from routing changes by binding it to the given
234      * interface. The socket is NOT closed by this method.
235      *
236      * @param socket The socket to be bound.
237      * @param interfaze The name of the interface.
238      */
protect(ParcelFileDescriptor socket, String interfaze)239     public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
240         PackageManager pm = mContext.getPackageManager();
241         ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
242         if (Binder.getCallingUid() != app.uid) {
243             throw new SecurityException("Unauthorized Caller");
244         }
245         jniProtect(socket.getFd(), interfaze);
246     }
247 
248     /**
249      * Establish a VPN network and return the file descriptor of the VPN
250      * interface. This methods returns {@code null} if the application is
251      * revoked or not prepared.
252      *
253      * @param config The parameters to configure the network.
254      * @return The file descriptor of the VPN interface.
255      */
establish(VpnConfig config)256     public synchronized ParcelFileDescriptor establish(VpnConfig config) {
257         // Check if the caller is already prepared.
258         PackageManager pm = mContext.getPackageManager();
259         ApplicationInfo app = null;
260         try {
261             app = pm.getApplicationInfo(mPackage, 0);
262         } catch (Exception e) {
263             return null;
264         }
265         if (Binder.getCallingUid() != app.uid) {
266             return null;
267         }
268 
269         // Check if the service is properly declared.
270         Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
271         intent.setClassName(mPackage, config.user);
272         ResolveInfo info = pm.resolveService(intent, 0);
273         if (info == null) {
274             throw new SecurityException("Cannot find " + config.user);
275         }
276         if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
277             throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
278         }
279 
280         // Load the label.
281         String label = app.loadLabel(pm).toString();
282 
283         // Load the icon and convert it into a bitmap.
284         Drawable icon = app.loadIcon(pm);
285         Bitmap bitmap = null;
286         if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
287             int width = mContext.getResources().getDimensionPixelSize(
288                     android.R.dimen.notification_large_icon_width);
289             int height = mContext.getResources().getDimensionPixelSize(
290                     android.R.dimen.notification_large_icon_height);
291             icon.setBounds(0, 0, width, height);
292             bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
293             Canvas c = new Canvas(bitmap);
294             icon.draw(c);
295             c.setBitmap(null);
296         }
297 
298         // Configure the interface. Abort if any of these steps fails.
299         ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
300         try {
301             updateState(DetailedState.CONNECTING, "establish");
302             String interfaze = jniGetName(tun.getFd());
303             if (jniSetAddresses(interfaze, config.addresses) < 1) {
304                 throw new IllegalArgumentException("At least one address must be specified");
305             }
306             if (config.routes != null) {
307                 jniSetRoutes(interfaze, config.routes);
308             }
309             Connection connection = new Connection();
310             if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
311                 throw new IllegalStateException("Cannot bind " + config.user);
312             }
313             if (mConnection != null) {
314                 mContext.unbindService(mConnection);
315             }
316             if (mInterface != null && !mInterface.equals(interfaze)) {
317                 jniReset(mInterface);
318             }
319             mConnection = connection;
320             mInterface = interfaze;
321         } catch (RuntimeException e) {
322             updateState(DetailedState.FAILED, "establish");
323             IoUtils.closeQuietly(tun);
324             throw e;
325         }
326         Log.i(TAG, "Established by " + config.user + " on " + mInterface);
327 
328         // Fill more values.
329         config.user = mPackage;
330         config.interfaze = mInterface;
331 
332         // Override DNS servers and show the notification.
333         final long token = Binder.clearCallingIdentity();
334         try {
335             mCallback.override(config.dnsServers, config.searchDomains);
336             showNotification(config, label, bitmap);
337         } finally {
338             Binder.restoreCallingIdentity(token);
339         }
340         // TODO: ensure that contract class eventually marks as connected
341         updateState(DetailedState.AUTHENTICATING, "establish");
342         return tun;
343     }
344 
345     @Deprecated
interfaceStatusChanged(String iface, boolean up)346     public synchronized void interfaceStatusChanged(String iface, boolean up) {
347         try {
348             mObserver.interfaceStatusChanged(iface, up);
349         } catch (RemoteException e) {
350             // ignored; target is local
351         }
352     }
353 
354     private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
355         @Override
356         public void interfaceStatusChanged(String interfaze, boolean up) {
357             synchronized (Vpn.this) {
358                 if (!up && mLegacyVpnRunner != null) {
359                     mLegacyVpnRunner.check(interfaze);
360                 }
361             }
362         }
363 
364         @Override
365         public void interfaceRemoved(String interfaze) {
366             synchronized (Vpn.this) {
367                 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
368                     final long token = Binder.clearCallingIdentity();
369                     try {
370                         mCallback.restore();
371                         hideNotification();
372                     } finally {
373                         Binder.restoreCallingIdentity(token);
374                     }
375                     mInterface = null;
376                     if (mConnection != null) {
377                         mContext.unbindService(mConnection);
378                         mConnection = null;
379                         updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
380                     } else if (mLegacyVpnRunner != null) {
381                         mLegacyVpnRunner.exit();
382                         mLegacyVpnRunner = null;
383                     }
384                 }
385             }
386         }
387     };
388 
enforceControlPermission()389     private void enforceControlPermission() {
390         // System user is allowed to control VPN.
391         if (Binder.getCallingUid() == Process.SYSTEM_UID) {
392             return;
393         }
394 
395         try {
396             // System dialogs are also allowed to control VPN.
397             PackageManager pm = mContext.getPackageManager();
398             ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
399             if (Binder.getCallingUid() == app.uid) {
400                 return;
401             }
402         } catch (Exception e) {
403             // ignore
404         }
405 
406         throw new SecurityException("Unauthorized Caller");
407     }
408 
409     private class Connection implements ServiceConnection {
410         private IBinder mService;
411 
412         @Override
onServiceConnected(ComponentName name, IBinder service)413         public void onServiceConnected(ComponentName name, IBinder service) {
414             mService = service;
415         }
416 
417         @Override
onServiceDisconnected(ComponentName name)418         public void onServiceDisconnected(ComponentName name) {
419             mService = null;
420         }
421     }
422 
showNotification(VpnConfig config, String label, Bitmap icon)423     private void showNotification(VpnConfig config, String label, Bitmap icon) {
424         if (!mEnableNotif) return;
425         mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
426 
427         NotificationManager nm = (NotificationManager)
428                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
429 
430         if (nm != null) {
431             String title = (label == null) ? mContext.getString(R.string.vpn_title) :
432                     mContext.getString(R.string.vpn_title_long, label);
433             String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
434                     mContext.getString(R.string.vpn_text_long, config.session);
435             config.startTime = SystemClock.elapsedRealtime();
436 
437             Notification notification = new Notification.Builder(mContext)
438                     .setSmallIcon(R.drawable.vpn_connected)
439                     .setLargeIcon(icon)
440                     .setContentTitle(title)
441                     .setContentText(text)
442                     .setContentIntent(mStatusIntent)
443                     .setDefaults(0)
444                     .setOngoing(true)
445                     .build();
446             nm.notifyAsUser(null, R.drawable.vpn_connected, notification, UserHandle.ALL);
447         }
448     }
449 
hideNotification()450     private void hideNotification() {
451         if (!mEnableNotif) return;
452         mStatusIntent = null;
453 
454         NotificationManager nm = (NotificationManager)
455                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
456 
457         if (nm != null) {
458             nm.cancelAsUser(null, R.drawable.vpn_connected, UserHandle.ALL);
459         }
460     }
461 
jniCreate(int mtu)462     private native int jniCreate(int mtu);
jniGetName(int tun)463     private native String jniGetName(int tun);
jniSetAddresses(String interfaze, String addresses)464     private native int jniSetAddresses(String interfaze, String addresses);
jniSetRoutes(String interfaze, String routes)465     private native int jniSetRoutes(String interfaze, String routes);
jniReset(String interfaze)466     private native void jniReset(String interfaze);
jniCheck(String interfaze)467     private native int jniCheck(String interfaze);
jniProtect(int socket, String interfaze)468     private native void jniProtect(int socket, String interfaze);
469 
findLegacyVpnGateway(LinkProperties prop)470     private static String findLegacyVpnGateway(LinkProperties prop) {
471         for (RouteInfo route : prop.getRoutes()) {
472             // Currently legacy VPN only works on IPv4.
473             if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
474                 return route.getGateway().getHostAddress();
475             }
476         }
477 
478         throw new IllegalStateException("Unable to find suitable gateway");
479     }
480 
481     /**
482      * Start legacy VPN, controlling native daemons as needed. Creates a
483      * secondary thread to perform connection work, returning quickly.
484      */
startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress)485     public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
486         enforceControlPermission();
487         if (!keyStore.isUnlocked()) {
488             throw new IllegalStateException("KeyStore isn't unlocked");
489         }
490 
491         final String iface = egress.getInterfaceName();
492         final String gateway = findLegacyVpnGateway(egress);
493 
494         // Load certificates.
495         String privateKey = "";
496         String userCert = "";
497         String caCert = "";
498         String serverCert = "";
499         if (!profile.ipsecUserCert.isEmpty()) {
500             privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
501             byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
502             userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
503         }
504         if (!profile.ipsecCaCert.isEmpty()) {
505             byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
506             caCert = (value == null) ? null : new String(value, Charsets.UTF_8);
507         }
508         if (!profile.ipsecServerCert.isEmpty()) {
509             byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
510             serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
511         }
512         if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
513             throw new IllegalStateException("Cannot load credentials");
514         }
515 
516         // Prepare arguments for racoon.
517         String[] racoon = null;
518         switch (profile.type) {
519             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
520                 racoon = new String[] {
521                     iface, profile.server, "udppsk", profile.ipsecIdentifier,
522                     profile.ipsecSecret, "1701",
523                 };
524                 break;
525             case VpnProfile.TYPE_L2TP_IPSEC_RSA:
526                 racoon = new String[] {
527                     iface, profile.server, "udprsa", privateKey, userCert,
528                     caCert, serverCert, "1701",
529                 };
530                 break;
531             case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
532                 racoon = new String[] {
533                     iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
534                     profile.ipsecSecret, profile.username, profile.password, "", gateway,
535                 };
536                 break;
537             case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
538                 racoon = new String[] {
539                     iface, profile.server, "xauthrsa", privateKey, userCert,
540                     caCert, serverCert, profile.username, profile.password, "", gateway,
541                 };
542                 break;
543             case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
544                 racoon = new String[] {
545                     iface, profile.server, "hybridrsa",
546                     caCert, serverCert, profile.username, profile.password, "", gateway,
547                 };
548                 break;
549         }
550 
551         // Prepare arguments for mtpd.
552         String[] mtpd = null;
553         switch (profile.type) {
554             case VpnProfile.TYPE_PPTP:
555                 mtpd = new String[] {
556                     iface, "pptp", profile.server, "1723",
557                     "name", profile.username, "password", profile.password,
558                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
559                     "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
560                     (profile.mppe ? "+mppe" : "nomppe"),
561                 };
562                 break;
563             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
564             case VpnProfile.TYPE_L2TP_IPSEC_RSA:
565                 mtpd = new String[] {
566                     iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
567                     "name", profile.username, "password", profile.password,
568                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
569                     "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
570                 };
571                 break;
572         }
573 
574         VpnConfig config = new VpnConfig();
575         config.legacy = true;
576         config.user = profile.key;
577         config.interfaze = iface;
578         config.session = profile.name;
579         config.routes = profile.routes;
580         if (!profile.dnsServers.isEmpty()) {
581             config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
582         }
583         if (!profile.searchDomains.isEmpty()) {
584             config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
585         }
586         startLegacyVpn(config, racoon, mtpd);
587     }
588 
startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd)589     private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
590         stopLegacyVpn();
591 
592         // Prepare for the new request. This also checks the caller.
593         prepare(null, VpnConfig.LEGACY_VPN);
594         updateState(DetailedState.CONNECTING, "startLegacyVpn");
595 
596         // Start a new LegacyVpnRunner and we are done!
597         mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
598         mLegacyVpnRunner.start();
599     }
600 
stopLegacyVpn()601     public synchronized void stopLegacyVpn() {
602         if (mLegacyVpnRunner != null) {
603             mLegacyVpnRunner.exit();
604             mLegacyVpnRunner = null;
605 
606             synchronized (LegacyVpnRunner.TAG) {
607                 // wait for old thread to completely finish before spinning up
608                 // new instance, otherwise state updates can be out of order.
609             }
610         }
611     }
612 
613     /**
614      * Return the information of the current ongoing legacy VPN.
615      */
getLegacyVpnInfo()616     public synchronized LegacyVpnInfo getLegacyVpnInfo() {
617         // Check if the caller is authorized.
618         enforceControlPermission();
619         if (mLegacyVpnRunner == null) return null;
620 
621         final LegacyVpnInfo info = new LegacyVpnInfo();
622         info.key = mLegacyVpnRunner.mConfig.user;
623         info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
624         if (mNetworkInfo.isConnected()) {
625             info.intent = mStatusIntent;
626         }
627         return info;
628     }
629 
getLegacyVpnConfig()630     public VpnConfig getLegacyVpnConfig() {
631         if (mLegacyVpnRunner != null) {
632             return mLegacyVpnRunner.mConfig;
633         } else {
634             return null;
635         }
636     }
637 
638     /**
639      * Bringing up a VPN connection takes time, and that is all this thread
640      * does. Here we have plenty of time. The only thing we need to take
641      * care of is responding to interruptions as soon as possible. Otherwise
642      * requests will be piled up. This can be done in a Handler as a state
643      * machine, but it is much easier to read in the current form.
644      */
645     private class LegacyVpnRunner extends Thread {
646         private static final String TAG = "LegacyVpnRunner";
647 
648         private final VpnConfig mConfig;
649         private final String[] mDaemons;
650         private final String[][] mArguments;
651         private final LocalSocket[] mSockets;
652         private final String mOuterInterface;
653         private final AtomicInteger mOuterConnection =
654                 new AtomicInteger(ConnectivityManager.TYPE_NONE);
655 
656         private long mTimer = -1;
657 
658         /**
659          * Watch for the outer connection (passing in the constructor) going away.
660          */
661         private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
662             @Override
663             public void onReceive(Context context, Intent intent) {
664                 if (!mEnableTeardown) return;
665 
666                 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
667                     if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
668                             ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
669                         NetworkInfo info = (NetworkInfo)intent.getExtra(
670                                 ConnectivityManager.EXTRA_NETWORK_INFO);
671                         if (info != null && !info.isConnectedOrConnecting()) {
672                             try {
673                                 mObserver.interfaceStatusChanged(mOuterInterface, false);
674                             } catch (RemoteException e) {}
675                         }
676                     }
677                 }
678             }
679         };
680 
LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd)681         public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
682             super(TAG);
683             mConfig = config;
684             mDaemons = new String[] {"racoon", "mtpd"};
685             // TODO: clear arguments from memory once launched
686             mArguments = new String[][] {racoon, mtpd};
687             mSockets = new LocalSocket[mDaemons.length];
688 
689             // This is the interface which VPN is running on,
690             // mConfig.interfaze will change to point to OUR
691             // internal interface soon. TODO - add inner/outer to mconfig
692             // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
693             // we will leave the VPN up.  We should check that it's still there/connected after
694             // registering
695             mOuterInterface = mConfig.interfaze;
696 
697             try {
698                 mOuterConnection.set(
699                         mConnService.findConnectionTypeForIface(mOuterInterface));
700             } catch (Exception e) {
701                 mOuterConnection.set(ConnectivityManager.TYPE_NONE);
702             }
703 
704             IntentFilter filter = new IntentFilter();
705             filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
706             mContext.registerReceiver(mBroadcastReceiver, filter);
707         }
708 
check(String interfaze)709         public void check(String interfaze) {
710             if (interfaze.equals(mOuterInterface)) {
711                 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
712                 exit();
713             }
714         }
715 
exit()716         public void exit() {
717             // We assume that everything is reset after stopping the daemons.
718             interrupt();
719             for (LocalSocket socket : mSockets) {
720                 IoUtils.closeQuietly(socket);
721             }
722             updateState(DetailedState.DISCONNECTED, "exit");
723             try {
724                 mContext.unregisterReceiver(mBroadcastReceiver);
725             } catch (IllegalArgumentException e) {}
726         }
727 
728         @Override
run()729         public void run() {
730             // Wait for the previous thread since it has been interrupted.
731             Log.v(TAG, "Waiting");
732             synchronized (TAG) {
733                 Log.v(TAG, "Executing");
734                 execute();
735                 monitorDaemons();
736             }
737         }
738 
checkpoint(boolean yield)739         private void checkpoint(boolean yield) throws InterruptedException {
740             long now = SystemClock.elapsedRealtime();
741             if (mTimer == -1) {
742                 mTimer = now;
743                 Thread.sleep(1);
744             } else if (now - mTimer <= 60000) {
745                 Thread.sleep(yield ? 200 : 1);
746             } else {
747                 updateState(DetailedState.FAILED, "checkpoint");
748                 throw new IllegalStateException("Time is up");
749             }
750         }
751 
execute()752         private void execute() {
753             // Catch all exceptions so we can clean up few things.
754             boolean initFinished = false;
755             try {
756                 // Initialize the timer.
757                 checkpoint(false);
758 
759                 // Wait for the daemons to stop.
760                 for (String daemon : mDaemons) {
761                     while (!SystemService.isStopped(daemon)) {
762                         checkpoint(true);
763                     }
764                 }
765 
766                 // Clear the previous state.
767                 File state = new File("/data/misc/vpn/state");
768                 state.delete();
769                 if (state.exists()) {
770                     throw new IllegalStateException("Cannot delete the state");
771                 }
772                 new File("/data/misc/vpn/abort").delete();
773                 initFinished = true;
774 
775                 // Check if we need to restart any of the daemons.
776                 boolean restart = false;
777                 for (String[] arguments : mArguments) {
778                     restart = restart || (arguments != null);
779                 }
780                 if (!restart) {
781                     updateState(DetailedState.DISCONNECTED, "execute");
782                     return;
783                 }
784                 updateState(DetailedState.CONNECTING, "execute");
785 
786                 // Start the daemon with arguments.
787                 for (int i = 0; i < mDaemons.length; ++i) {
788                     String[] arguments = mArguments[i];
789                     if (arguments == null) {
790                         continue;
791                     }
792 
793                     // Start the daemon.
794                     String daemon = mDaemons[i];
795                     SystemService.start(daemon);
796 
797                     // Wait for the daemon to start.
798                     while (!SystemService.isRunning(daemon)) {
799                         checkpoint(true);
800                     }
801 
802                     // Create the control socket.
803                     mSockets[i] = new LocalSocket();
804                     LocalSocketAddress address = new LocalSocketAddress(
805                             daemon, LocalSocketAddress.Namespace.RESERVED);
806 
807                     // Wait for the socket to connect.
808                     while (true) {
809                         try {
810                             mSockets[i].connect(address);
811                             break;
812                         } catch (Exception e) {
813                             // ignore
814                         }
815                         checkpoint(true);
816                     }
817                     mSockets[i].setSoTimeout(500);
818 
819                     // Send over the arguments.
820                     OutputStream out = mSockets[i].getOutputStream();
821                     for (String argument : arguments) {
822                         byte[] bytes = argument.getBytes(Charsets.UTF_8);
823                         if (bytes.length >= 0xFFFF) {
824                             throw new IllegalArgumentException("Argument is too large");
825                         }
826                         out.write(bytes.length >> 8);
827                         out.write(bytes.length);
828                         out.write(bytes);
829                         checkpoint(false);
830                     }
831                     out.write(0xFF);
832                     out.write(0xFF);
833                     out.flush();
834 
835                     // Wait for End-of-File.
836                     InputStream in = mSockets[i].getInputStream();
837                     while (true) {
838                         try {
839                             if (in.read() == -1) {
840                                 break;
841                             }
842                         } catch (Exception e) {
843                             // ignore
844                         }
845                         checkpoint(true);
846                     }
847                 }
848 
849                 // Wait for the daemons to create the new state.
850                 while (!state.exists()) {
851                     // Check if a running daemon is dead.
852                     for (int i = 0; i < mDaemons.length; ++i) {
853                         String daemon = mDaemons[i];
854                         if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
855                             throw new IllegalStateException(daemon + " is dead");
856                         }
857                     }
858                     checkpoint(true);
859                 }
860 
861                 // Now we are connected. Read and parse the new state.
862                 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
863                 if (parameters.length != 6) {
864                     throw new IllegalStateException("Cannot parse the state");
865                 }
866 
867                 // Set the interface and the addresses in the config.
868                 mConfig.interfaze = parameters[0].trim();
869                 mConfig.addresses = parameters[1].trim();
870 
871                 // Set the routes if they are not set in the config.
872                 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
873                     mConfig.routes = parameters[2].trim();
874                 }
875 
876                 // Set the DNS servers if they are not set in the config.
877                 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
878                     String dnsServers = parameters[3].trim();
879                     if (!dnsServers.isEmpty()) {
880                         mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
881                     }
882                 }
883 
884                 // Set the search domains if they are not set in the config.
885                 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
886                     String searchDomains = parameters[4].trim();
887                     if (!searchDomains.isEmpty()) {
888                         mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
889                     }
890                 }
891 
892                 // Set the routes.
893                 jniSetRoutes(mConfig.interfaze, mConfig.routes);
894 
895                 // Here is the last step and it must be done synchronously.
896                 synchronized (Vpn.this) {
897                     // Check if the thread is interrupted while we are waiting.
898                     checkpoint(false);
899 
900                     // Check if the interface is gone while we are waiting.
901                     if (jniCheck(mConfig.interfaze) == 0) {
902                         throw new IllegalStateException(mConfig.interfaze + " is gone");
903                     }
904 
905                     // Now INetworkManagementEventObserver is watching our back.
906                     mInterface = mConfig.interfaze;
907                     mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
908                     showNotification(mConfig, null, null);
909 
910                     Log.i(TAG, "Connected!");
911                     updateState(DetailedState.CONNECTED, "execute");
912                 }
913             } catch (Exception e) {
914                 Log.i(TAG, "Aborting", e);
915                 exit();
916             } finally {
917                 // Kill the daemons if they fail to stop.
918                 if (!initFinished) {
919                     for (String daemon : mDaemons) {
920                         SystemService.stop(daemon);
921                     }
922                 }
923 
924                 // Do not leave an unstable state.
925                 if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
926                     updateState(DetailedState.FAILED, "execute");
927                 }
928             }
929         }
930 
931         /**
932          * Monitor the daemons we started, moving to disconnected state if the
933          * underlying services fail.
934          */
monitorDaemons()935         private void monitorDaemons() {
936             if (!mNetworkInfo.isConnected()) {
937                 return;
938             }
939 
940             try {
941                 while (true) {
942                     Thread.sleep(2000);
943                     for (int i = 0; i < mDaemons.length; i++) {
944                         if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
945                             return;
946                         }
947                     }
948                 }
949             } catch (InterruptedException e) {
950                 Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
951             } finally {
952                 for (String daemon : mDaemons) {
953                     SystemService.stop(daemon);
954                 }
955 
956                 updateState(DetailedState.DISCONNECTED, "babysit");
957             }
958         }
959     }
960 }
961