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 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 */ 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 */ 130 public void setEnableTeardown(boolean enableTeardown) { 131 mEnableTeardown = enableTeardown; 132 } 133 134 @Override 135 protected void startMonitoringInternal() { 136 // Ignored; events are sent through callbacks for now 137 } 138 139 @Override 140 public boolean teardown() { 141 // TODO: finish migration to unique tracker for each VPN 142 throw new UnsupportedOperationException(); 143 } 144 145 @Override 146 public boolean reconnect() { 147 // TODO: finish migration to unique tracker for each VPN 148 throw new UnsupportedOperationException(); 149 } 150 151 @Override 152 public String getTcpBufferSizesPropName() { 153 return PROP_TCP_BUFFER_UNKNOWN; 154 } 155 156 /** 157 * Update current state, dispaching event to listeners. 158 */ 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 */ 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 */ 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 */ 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 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 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 413 public void onServiceConnected(ComponentName name, IBinder service) { 414 mService = service; 415 } 416 417 @Override 418 public void onServiceDisconnected(ComponentName name) { 419 mService = null; 420 } 421 } 422 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 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 462 private native int jniCreate(int mtu); 463 private native String jniGetName(int tun); 464 private native int jniSetAddresses(String interfaze, String addresses); 465 private native int jniSetRoutes(String interfaze, String routes); 466 private native void jniReset(String interfaze); 467 private native int jniCheck(String interfaze); 468 private native void jniProtect(int socket, String interfaze); 469 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 */ 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 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 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 */ 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 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 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 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 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 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 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 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 */ 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