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