[tor-commits] [orbot/master] Move OrbotVPNService to a Manager and consolidate services

n8fr8 at torproject.org n8fr8 at torproject.org
Wed Mar 9 19:51:59 UTC 2016


commit 9097b79a7e3e76185f09d5c9d510eefa52447d2b
Author: Nathan Freitas <nathan at freitas.net>
Date:   Wed Mar 9 14:43:01 2016 -0500

    Move OrbotVPNService to a Manager and consolidate services
    This allows for the VPN service to be set in the foreground with the TorService
    and reduce the chance to be killed due to lack of memory
---
 AndroidManifest.xml                                |   9 +-
 external/Makefile                                  |   2 +
 res/values/pdnsd.xml                               |   4 +-
 src/org/torproject/android/service/TorService.java |  41 +-
 .../torproject/android/vpn/OrbotVpnManager.java    | 455 +++++++++++++++++++++
 .../torproject/android/vpn/OrbotVpnService.java    | 449 --------------------
 6 files changed, 495 insertions(+), 465 deletions(-)

diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ccf0692..ce30100 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,12 +6,15 @@
         android:installLocation="auto"      
       >
     <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/>
+    <!-- 
  <permission android:name="org.torproject.android.MANAGE_TOR" 
      android:label="@string/permission_manage_tor_label" 
      android:description="@string/permission_manage_tor_description" 
      android:protectionLevel="signature"/>
  
 	<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
+	-->
+	
 	<uses-permission android:name="android.permission.INTERNET"/>
 	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 	<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
@@ -88,7 +91,7 @@
         <service
             android:name=".service.TorService"
             android:enabled="true"
-            android:permission="org.torproject.android.MANAGE_TOR"
+            android:permission="android.permission.BIND_VPN_SERVICE"
             android:stopWithTask="false" >
         </service>
 
@@ -118,13 +121,13 @@
 			</intent-filter>
 		</receiver>
 		
-   	
+   	<!-- 
        <service android:name="org.torproject.android.vpn.OrbotVpnService"
                android:permission="android.permission.BIND_VPN_SERVICE">
            <intent-filter>
                <action android:name="android.net.VpnService"/>
            </intent-filter>
        </service>
-        
+         -->
 </application>
 </manifest> 
diff --git a/external/Makefile b/external/Makefile
index 92e4c0c..211df76 100644
--- a/external/Makefile
+++ b/external/Makefile
@@ -83,6 +83,8 @@ ifeq ($(APP_ABI),armeabi)
 	CFLAGS += $(TARGET_thumb_release_CFLAGS)
 endif
 
+
+
 .PHONY = clean showsetup \
 	assets assets-clean \
 	openssl-static openssl-static-clean \
diff --git a/res/values/pdnsd.xml b/res/values/pdnsd.xml
index 42834d2..522f765 100644
--- a/res/values/pdnsd.xml
+++ b/res/values/pdnsd.xml
@@ -2,7 +2,7 @@
 <resources>
 	<string name="pdnsd_conf" formatted="false">
 global {
-	perm_cache=1024;
+	perm_cache=0;
 	cache_dir="/data/data/org.torproject.android/app_bin";
 	server_port = 8091;
 	server_ip = 0.0.0.0;
@@ -12,7 +12,7 @@ global {
 	timeout=10;
 	daemon=on;
 	pid_file="/data/data/org.torproject.android/app_bin/pdnsd.pid";
-	
+
 }
 
 server {
diff --git a/src/org/torproject/android/service/TorService.java b/src/org/torproject/android/service/TorService.java
index 95192c6..24a90db 100644
--- a/src/org/torproject/android/service/TorService.java
+++ b/src/org/torproject/android/service/TorService.java
@@ -23,6 +23,7 @@ import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.net.VpnService;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -46,7 +47,7 @@ import org.torproject.android.Prefs;
 import org.torproject.android.R;
 import org.torproject.android.settings.AppManager;
 import org.torproject.android.settings.TorifiedApp;
-import org.torproject.android.vpn.OrbotVpnService;
+import org.torproject.android.vpn.OrbotVpnManager;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
@@ -82,7 +83,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeoutException;
 
-public class TorService extends Service implements TorServiceConstants, OrbotConstants, EventHandler
+public class TorService extends VpnService implements TorServiceConstants, OrbotConstants, EventHandler
 {
     
     private String mCurrentStatus = STATUS_OFF;
@@ -121,7 +122,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
     private long lastWritten = -1;
     
     private NotificationManager mNotificationManager = null;
-    private Builder mNotifyBuilder;
+    private Notification.Builder mNotifyBuilder;
     private Notification mNotification;
     private boolean mNotificationShowing = false;
 
@@ -130,6 +131,8 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
     private ExecutorService mExecutor = Executors.newFixedThreadPool(1);
 
     private NumberFormat mNumberFormat = null;
+    
+    private OrbotVpnManager mVpnManager;
 
     public void debug(String msg)
     {
@@ -364,6 +367,15 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
         unregisterReceiver(mNetworkStateReceiver);
         super.onDestroy();
     }
+    
+    @Override
+    public void onRevoke ()
+    {
+    	if (mVpnManager != null)
+    		mVpnManager.onRevoke();
+    	
+    	super.onRevoke();
+    }
 
     private void stopTor() {
         Log.i("TorService", "stopTor");
@@ -744,7 +756,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
 	        if (Prefs.bridgesEnabled())
 	        	if (Prefs.useVpn() && !mIsLollipop)
 	        	{
-	        		customEnv.add("TOR_PT_PROXY=socks5://" + OrbotVpnService.sSocksProxyLocalhost + ":" + OrbotVpnService.sSocksProxyServerPort); 
+	        		customEnv.add("TOR_PT_PROXY=socks5://" + OrbotVpnManager.sSocksProxyLocalhost + ":" + OrbotVpnManager.sSocksProxyServerPort); 
 	        	}
 	        
 	        String baseDirectory = OrbotApp.fileTor.getParent();
@@ -1190,12 +1202,14 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
             
             updateConfiguration("DNSPort",TOR_VPN_DNS_LISTEN_ADDRESS + ":" + TorServiceConstants.TOR_DNS_PORT_DEFAULT,false);
             
-            Intent intent = new Intent(TorService.this, OrbotVpnService.class);
-            intent.setAction("start");
+            if (mVpnManager == null)
+            	mVpnManager = new OrbotVpnManager (this);
             
+            Intent intent = new Intent();
+            intent.setAction("start");            
             intent.putExtra("torSocks", mPortSOCKS);
             
-            startService(intent);
+            mVpnManager.handleIntent(new Builder(),intent);
            
         }
         
@@ -1205,9 +1219,14 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
             Prefs.putUseVpn(false);
             processTransparentProxying();
             
-            Intent intent = new Intent(TorService.this, OrbotVpnService.class);
-            intent.setAction("stop");
-            startService(intent);                                              
+            if (mVpnManager != null)
+            {
+            	Intent intent = new Intent();
+                intent.setAction("stop");
+            	mVpnManager.handleIntent(new Builder(), intent);
+            	mVpnManager = null;
+            }            
+                             
         }
 
     @Override
@@ -1851,7 +1870,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
 	        	if (!mIsLollipop)
 	        	{
 		        	String proxyType = "socks5";
-		        	extraLines.append(proxyType + "Proxy" + ' ' + OrbotVpnService.sSocksProxyLocalhost + ':' + OrbotVpnService.sSocksProxyServerPort).append('\n');
+		        	extraLines.append(proxyType + "Proxy" + ' ' + OrbotVpnManager.sSocksProxyLocalhost + ':' + OrbotVpnManager.sSocksProxyServerPort).append('\n');
 	        	};
 			
 	        }
diff --git a/src/org/torproject/android/vpn/OrbotVpnManager.java b/src/org/torproject/android/vpn/OrbotVpnManager.java
new file mode 100644
index 0000000..bfb564c
--- /dev/null
+++ b/src/org/torproject/android/vpn/OrbotVpnManager.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.torproject.android.vpn;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.concurrent.TimeoutException;
+
+import org.sufficientlysecure.rootcommands.Shell;
+import org.sufficientlysecure.rootcommands.command.SimpleCommand;
+import org.torproject.android.OrbotApp;
+import org.torproject.android.R;
+import org.torproject.android.service.TorService;
+import org.torproject.android.service.TorServiceConstants;
+import org.torproject.android.service.TorServiceUtils;
+import org.torproject.android.settings.AppManager;
+import org.torproject.android.settings.TorifiedApp;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.VpnService;
+import android.net.VpnService.Builder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.runjva.sourceforge.jsocks.protocol.ProxyServer;
+import com.runjva.sourceforge.jsocks.server.ServerAuthenticatorNone;
+
+ at TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+
+public class OrbotVpnManager implements Handler.Callback {
+    private static final String TAG = "OrbotVpnService";
+
+    private PendingIntent mConfigureIntent;
+
+    private Thread mThreadVPN;
+
+    private String mSessionName = "OrbotVPN";
+    private ParcelFileDescriptor mInterface;
+
+    private int mTorSocks = TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT;
+    
+    public static int sSocksProxyServerPort = -1;
+    public static String sSocksProxyLocalhost = null;
+    private ProxyServer mSocksProxyServer;
+   
+    
+    private final static int VPN_MTU = 1500;
+    
+    private final static boolean mIsLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+    
+    //this is the actual DNS server we talk to over UDP or TCP (now using Tor's DNS port)
+    private final static String DEFAULT_ACTUAL_DNS_HOST = "127.0.0.1";
+    private final static int DEFAULT_ACTUAL_DNS_PORT = TorServiceConstants.TOR_DNS_PORT_DEFAULT;
+    
+    private boolean isRestart = false;
+    
+    private TorService mService;
+    
+
+    static{
+    	  System.loadLibrary("tun2socks");
+    	}
+    
+    public OrbotVpnManager (TorService service)
+    {
+    	mService = service;
+    }
+   
+    //public int onStartCommand(Intent intent, int flags, int startId) {
+    public int handleIntent(Builder builder, Intent intent) {
+
+    	if (intent != null)
+    	{
+	    	String action = intent.getAction();
+	    	
+	    	if (action.equals("start"))
+	    	{
+		
+		        // Stop the previous session by interrupting the thread.
+		        if (mThreadVPN == null || (!mThreadVPN.isAlive()))
+		        {
+		        	Log.d(TAG,"starting OrbotVPNService service!");
+		        	
+		        	mTorSocks = intent.getIntExtra("torSocks", TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT);
+			    	
+		        	if (!mIsLollipop)
+		        	{
+	
+		        		startSocksBypass();
+		        	}
+		        	
+		            setupTun2Socks(builder);               
+		        }
+	    	}
+	    	else if (action.equals("stop"))
+	    	{
+	    		Log.d(TAG,"stop OrbotVPNService service!");
+	    		
+	    		stopVPN();    		
+	    		//if (mHandler != null)
+	    			//mHandler.postDelayed(new Runnable () { public void run () { stopSelf(); }}, 1000);
+	    	}
+	    	else if (action.equals("refresh"))
+	    	{
+	    		Log.d(TAG,"refresh OrbotVPNService service!");
+	    		
+	    		if (!mIsLollipop)
+	    		  startSocksBypass();
+	    		
+	    		if (!isRestart)
+	    			setupTun2Socks(builder);
+	    	}
+    	}
+     
+        
+        return Service.START_STICKY;
+    }
+  
+    private void startSocksBypass()
+    {
+       
+    	new Thread ()
+    	{
+    		
+    		public void run ()
+    		{
+
+                //generate the proxy port that the 
+                if (sSocksProxyServerPort == -1)
+                {
+                	try {
+						
+                		sSocksProxyLocalhost = "127.0.0.1";// InetAddress.getLocalHost().getHostAddress();
+	                	sSocksProxyServerPort = (int)((Math.random()*1000)+10000); 
+	                	
+					} catch (Exception e) {
+						Log.e(TAG,"Unable to access localhost",e);
+						throw new RuntimeException("Unable to access localhost: " + e);
+						
+					}
+                	
+                }
+                
+                
+		    	if (mSocksProxyServer != null)
+		    	{
+		    		stopSocksBypass ();
+		    	}
+		    	
+		    	try
+		    	{
+			        mSocksProxyServer = new ProxyServer(new ServerAuthenticatorNone(null, null));
+			        ProxyServer.setVpnService(mService);
+			        mSocksProxyServer.start(sSocksProxyServerPort, 5, InetAddress.getLocalHost());
+			        
+		    	}
+		    	catch (Exception e)
+		    	{
+		    		Log.e(TAG,"error getting host",e);
+		    	}
+    		}
+    	}.start();
+       
+    }
+
+    private synchronized void stopSocksBypass ()
+    {
+
+        if (mSocksProxyServer != null){
+            mSocksProxyServer.stop();
+            mSocksProxyServer = null;
+        }
+        
+        
+    }
+    
+    /**
+    @Override
+	public void onCreate() {
+		super.onCreate();
+		
+		// Set the locale to English (or probably any other language that^M
+        // uses Hindu-Arabic (aka Latin) numerals).^M
+        // We have found that VpnService.Builder does something locale-dependent^M
+        // internally that causes errors when the locale uses its own numerals^M
+        // (i.e., Farsi and Arabic).^M
+		Locale.setDefault(new Locale("en"));
+		
+	}
+
+
+	@Override
+    public void onDestroy() {
+    	stopVPN();
+    }*/
+    
+    private void stopVPN ()
+    {
+    	if (mIsLollipop)
+    		stopSocksBypass ();
+        
+        if (mInterface != null){
+            try
+            {
+            	Log.d(TAG,"closing interface, destroying VPN interface");
+                
+        		mInterface.close();
+        		mInterface = null;
+            	
+            }
+            catch (Exception e)
+            {
+                Log.d(TAG,"error stopping tun2socks",e);
+            }
+            catch (Error e)
+            {
+                Log.d(TAG,"error stopping tun2socks",e);
+            }   
+        }
+        
+        Tun2Socks.Stop();
+        
+        try {
+        	TorServiceUtils.killProcess(OrbotApp.filePdnsd);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        
+        mThreadVPN = null;
+        
+
+    }
+
+    @Override
+    public boolean handleMessage(Message message) {
+        if (message != null) {
+            Toast.makeText(mService, message.what, Toast.LENGTH_SHORT).show();
+        }
+        return true;
+    }
+
+  
+    private synchronized void setupTun2Socks(final Builder builder)  {
+
+    	
+        if (mInterface != null) //stop tun2socks now to give it time to clean up
+        {
+        	isRestart = true;
+        	Tun2Socks.Stop();
+        }
+        
+    	mThreadVPN = new Thread ()
+    	{
+    		
+    		public void run ()
+    		{
+	    		try
+		        {
+	    			
+	    			if (isRestart)
+	    			{
+	    				Log.d(TAG,"is a restart... let's wait for a few seconds");
+			        	Thread.sleep(3000);
+	    			}
+	    			
+	    			//start PDNSD daemon pointing to actual DNS
+	    			startDNS(DEFAULT_ACTUAL_DNS_HOST,DEFAULT_ACTUAL_DNS_PORT);
+	    			
+		    		final String vpnName = "OrbotVPN";
+		    		final String localhost = "127.0.0.1";
+
+		    		final String virtualGateway = "10.10.10.1";
+		    		final String virtualIP = "10.10.10.2";
+		    		final String virtualNetMask = "255.255.255.0";
+		    		final String dummyDNS = "8.8.8.8"; //this is intercepted by the tun2socks library, but we must put in a valid DNS to start
+		    		final String defaultRoute = "0.0.0.0";
+		    		
+		    		final String localSocks = localhost + ':'
+		    		        + String.valueOf(mTorSocks);
+		    		
+		    		final String localDNS = virtualGateway + ':' + "8091";//String.valueOf(TorServiceConstants.TOR_DNS_PORT_DEFAULT);
+		    		final boolean localDnsTransparentProxy = true;
+		        	
+			        builder.setMtu(VPN_MTU);
+			        builder.addAddress(virtualGateway,32);
+			        
+			        builder.setSession(vpnName);		        
+
+			        builder.addDnsServer(dummyDNS);
+			        builder.addRoute(dummyDNS,32);
+				        
+			        //route all traffic through VPN (we might offer country specific exclude lists in the future)
+			        builder.addRoute(defaultRoute,0);	
+			        
+			        //handle ipv6
+			        //builder.addAddress("fdfe:dcba:9876::1", 126);
+					//builder.addRoute("::", 0);
+			        
+			        if (mIsLollipop)			        
+			        	doLollipopAppRouting(builder);			        
+
+			         // Create a new interface using the builder and save the parameters.
+			        ParcelFileDescriptor newInterface = builder.setSession(mSessionName)
+			                .setConfigureIntent(mConfigureIntent)
+			                .establish();
+		
+			        if (mInterface != null)
+			        {
+			        	Log.d(TAG,"Stopping existing VPN interface");
+			        	mInterface.close();
+			        	mInterface = null;
+			        }
+
+		        	mInterface = newInterface;
+			        
+		        	Tun2Socks.Start(mInterface, VPN_MTU, virtualIP, virtualNetMask, localSocks , localDNS , localDnsTransparentProxy);
+		        	
+		        	isRestart = false;
+		        	
+		        }
+		        catch (Exception e)
+		        {
+		        	Log.d(TAG,"tun2Socks has stopped",e);
+		        }
+	    	}
+    		
+    	};
+    	
+    	mThreadVPN.start();
+    	
+    }
+    
+    
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+	private void doLollipopAppRouting (Builder builder) throws NameNotFoundException
+    {    
+    	   
+        ArrayList<TorifiedApp> apps = AppManager.getApps(mService, TorServiceUtils.getSharedPrefs(mService.getApplicationContext()));
+    
+        boolean perAppEnabled = false;
+        
+        for (TorifiedApp app : apps)
+        {
+        	if (app.isTorified() && (!app.getPackageName().equals(mService.getPackageName())))
+        	{
+        		builder.addAllowedApplication(app.getPackageName());
+        		perAppEnabled = true;
+        	}
+        	
+        }
+    
+        if (!perAppEnabled)
+        	builder.addDisallowedApplication(mService.getPackageName());
+    
+    }
+    
+    
+    public void onRevoke() {
+    
+    	Log.w(TAG,"VPNService REVOKED!");
+    	
+    	if (!isRestart)
+    	{
+	    	SharedPreferences prefs = TorServiceUtils.getSharedPrefs(mService.getApplicationContext()); 
+	        prefs.edit().putBoolean("pref_vpn", false).commit();      
+	    	stopVPN();	
+    	}
+    	
+    	isRestart = false;
+    	
+    	//super.onRevoke();
+    
+    }
+    
+    
+    private void startDNS (String dns, int port) throws IOException, TimeoutException
+    {
+    	makePdnsdConf(mService, dns, port,OrbotApp.filePdnsd.getParentFile() );
+    	
+        ArrayList<String> customEnv = new ArrayList<String>();
+    	String baseDirectory = OrbotApp.filePdnsd.getParent();
+        Shell shell = Shell.startShell(customEnv, baseDirectory);
+        
+        String cmdString = OrbotApp.filePdnsd.getCanonicalPath() + 
+        		" -c " + baseDirectory + "/pdnsd.conf";
+    
+        SimpleCommand shellCommand = new SimpleCommand(cmdString);
+        
+        shell.add(shellCommand).waitForFinish();
+    
+        Log.i(TAG,"PDNSD: " + shellCommand.getExitCode() + ": " + shellCommand.getOutput());
+        
+    }
+    
+    public static void makePdnsdConf(Context context, String dns, int port, File fileDir) throws FileNotFoundException {
+        String conf = String.format(context.getString(R.string.pdnsd_conf), dns, port);
+
+        File f = new File(fileDir,"pdnsd.conf");
+
+        if (f.exists()) {
+                f.delete();
+        }
+
+        FileOutputStream fos = new FileOutputStream(f, false);
+    	PrintStream ps = new PrintStream(fos);
+    	ps.print(conf);
+    	ps.close();
+    	
+        //f.withWriter { out -> out.print conf };
+        
+        
+        File cache = new File(fileDir,"pdnsd.cache");
+
+        if (!cache.exists()) {
+                try {
+                        cache.createNewFile();
+                } catch (Exception e) {
+
+                }
+        }
+}
+
+    
+}
diff --git a/src/org/torproject/android/vpn/OrbotVpnService.java b/src/org/torproject/android/vpn/OrbotVpnService.java
deleted file mode 100644
index 2efab1c..0000000
--- a/src/org/torproject/android/vpn/OrbotVpnService.java
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.torproject.android.vpn;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.concurrent.TimeoutException;
-
-import org.sufficientlysecure.rootcommands.Shell;
-import org.sufficientlysecure.rootcommands.command.SimpleCommand;
-import org.torproject.android.OrbotApp;
-import org.torproject.android.R;
-import org.torproject.android.service.TorServiceConstants;
-import org.torproject.android.service.TorServiceUtils;
-import org.torproject.android.settings.AppManager;
-import org.torproject.android.settings.TorifiedApp;
-
-import android.annotation.TargetApi;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.VpnService;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.runjva.sourceforge.jsocks.protocol.ProxyServer;
-import com.runjva.sourceforge.jsocks.server.ServerAuthenticatorNone;
-
- at TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
-
-public class OrbotVpnService extends VpnService implements Handler.Callback {
-    private static final String TAG = "OrbotVpnService";
-
-    private PendingIntent mConfigureIntent;
-
-    private Handler mHandler;
-    private Thread mThreadVPN;
-
-    private String mSessionName = "OrbotVPN";
-    private ParcelFileDescriptor mInterface;
-
-    private int mTorSocks = TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT;
-    
-    public static int sSocksProxyServerPort = -1;
-    public static String sSocksProxyLocalhost = null;
-    private ProxyServer mSocksProxyServer;
-   
-    
-    private final static int VPN_MTU = 1500;
-    
-    private final static boolean mIsLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
-    
-    //this is the actual DNS server we talk to over UDP or TCP (now using Tor's DNS port)
-    private final static String DEFAULT_ACTUAL_DNS_HOST = "127.0.0.1";
-    private final static int DEFAULT_ACTUAL_DNS_PORT = TorServiceConstants.TOR_DNS_PORT_DEFAULT;
-    
-    private boolean isRestart = false;
-    
-
-    static{
-    	  System.loadLibrary("tun2socks");
-    	}
-    
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-
-    	if (intent != null)
-    	{
-	    	String action = intent.getAction();
-	    	
-	    	if (action.equals("start"))
-	    	{
-		
-		        // Stop the previous session by interrupting the thread.
-		        if (mThreadVPN == null || (!mThreadVPN.isAlive()))
-		        {
-		        	Log.d(TAG,"starting OrbotVPNService service!");
-		        	
-		        	mTorSocks = intent.getIntExtra("torSocks", TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT);
-			    	
-			        // The handler is only used to show messages.
-			        if (mHandler == null) {
-			            mHandler = new Handler(this);
-			        }
-		        	
-		        	if (!mIsLollipop)
-		        	{
-	
-		        		startSocksBypass();
-		        	}
-		        	
-		            setupTun2Socks();               
-		        }
-	    	}
-	    	else if (action.equals("stop"))
-	    	{
-	    		Log.d(TAG,"stop OrbotVPNService service!");
-	    		
-	    		stopVPN();    		
-	    		if (mHandler != null)
-	    			mHandler.postDelayed(new Runnable () { public void run () { stopSelf(); }}, 1000);
-	    	}
-	    	else if (action.equals("refresh"))
-	    	{
-	    		Log.d(TAG,"refresh OrbotVPNService service!");
-	    		
-	    		if (!mIsLollipop)
-	    		  startSocksBypass();
-	    		
-	    		if (!isRestart)
-	    			setupTun2Socks();
-	    	}
-    	}
-     
-        
-        return START_STICKY;
-    }
-  
-    private void startSocksBypass()
-    {
-       
-    	new Thread ()
-    	{
-    		
-    		public void run ()
-    		{
-
-                //generate the proxy port that the 
-                if (sSocksProxyServerPort == -1)
-                {
-                	try {
-						
-                		sSocksProxyLocalhost = "127.0.0.1";// InetAddress.getLocalHost().getHostAddress();
-	                	sSocksProxyServerPort = (int)((Math.random()*1000)+10000); 
-	                	
-					} catch (Exception e) {
-						Log.e(TAG,"Unable to access localhost",e);
-						throw new RuntimeException("Unable to access localhost: " + e);
-						
-					}
-                	
-                }
-                
-                
-		    	if (mSocksProxyServer != null)
-		    	{
-		    		stopSocksBypass ();
-		    	}
-		    	
-		    	try
-		    	{
-			        mSocksProxyServer = new ProxyServer(new ServerAuthenticatorNone(null, null));
-			        ProxyServer.setVpnService(OrbotVpnService.this);
-			        mSocksProxyServer.start(sSocksProxyServerPort, 5, InetAddress.getLocalHost());
-			        
-		    	}
-		    	catch (Exception e)
-		    	{
-		    		Log.e(TAG,"error getting host",e);
-		    	}
-    		}
-    	}.start();
-       
-    }
-
-    private synchronized void stopSocksBypass ()
-    {
-
-        if (mSocksProxyServer != null){
-            mSocksProxyServer.stop();
-            mSocksProxyServer = null;
-        }
-        
-        
-    }
-    
-    @Override
-	public void onCreate() {
-		super.onCreate();
-		
-		// Set the locale to English (or probably any other language that^M
-        // uses Hindu-Arabic (aka Latin) numerals).^M
-        // We have found that VpnService.Builder does something locale-dependent^M
-        // internally that causes errors when the locale uses its own numerals^M
-        // (i.e., Farsi and Arabic).^M
-		Locale.setDefault(new Locale("en"));
-		
-	}
-
-
-	@Override
-    public void onDestroy() {
-    	stopVPN();
-    }
-    
-    private void stopVPN ()
-    {
-    	if (mIsLollipop)
-    		stopSocksBypass ();
-        
-        if (mInterface != null){
-            try
-            {
-            	Log.d(TAG,"closing interface, destroying VPN interface");
-                
-        		mInterface.close();
-        		mInterface = null;
-            	
-            }
-            catch (Exception e)
-            {
-                Log.d(TAG,"error stopping tun2socks",e);
-            }
-            catch (Error e)
-            {
-                Log.d(TAG,"error stopping tun2socks",e);
-            }   
-        }
-        
-        Tun2Socks.Stop();
-        
-        try {
-        	TorServiceUtils.killProcess(OrbotApp.filePdnsd);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        
-        mThreadVPN = null;
-        
-
-    }
-
-    @Override
-    public boolean handleMessage(Message message) {
-        if (message != null) {
-            Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show();
-        }
-        return true;
-    }
-
-  
-    private synchronized void setupTun2Socks()  {
-
-    	
-        if (mInterface != null) //stop tun2socks now to give it time to clean up
-        {
-        	isRestart = true;
-        	Tun2Socks.Stop();
-        }
-        
-    	mThreadVPN = new Thread ()
-    	{
-    		
-    		public void run ()
-    		{
-	    		try
-		        {
-	    			
-	    			if (isRestart)
-	    			{
-	    				Log.d(TAG,"is a restart... let's wait for a few seconds");
-			        	Thread.sleep(3000);
-	    			}
-	    			
-	    			//start PDNSD daemon pointing to OpenDNS
-	    			startDNS(DEFAULT_ACTUAL_DNS_HOST,DEFAULT_ACTUAL_DNS_PORT);
-	    			
-		    		final String vpnName = "OrbotVPN";
-		    		final String localhost = "127.0.0.1";
-
-		    		final String virtualGateway = "10.10.10.1";
-		    		final String virtualIP = "10.10.10.2";
-		    		final String virtualNetMask = "255.255.255.0";
-		    		final String dummyDNS = "8.8.8.8"; //this is intercepted by the tun2socks library, but we must put in a valid DNS to start
-		    		final String defaultRoute = "0.0.0.0";
-		    		
-		    		final String localSocks = localhost + ':'
-		    		        + String.valueOf(mTorSocks);
-		    		
-		    		final String localDNS = virtualGateway + ':' + "8091";//String.valueOf(TorServiceConstants.TOR_DNS_PORT_DEFAULT);
-		        	final boolean localDnsTransparentProxy = true;
-		        	
-			        Builder builder = new Builder();
-			        
-			        builder.setMtu(VPN_MTU);
-			        builder.addAddress(virtualGateway,32);
-			        
-			        builder.setSession(vpnName);		        
-
-			        builder.addDnsServer(dummyDNS);
-			        builder.addRoute(dummyDNS,32);
-				        
-			        //route all traffic through VPN (we might offer country specific exclude lists in the future)
-			        builder.addRoute(defaultRoute,0);	
-			        
-			        //handle ipv6
-			        //builder.addAddress("fdfe:dcba:9876::1", 126);
-					//builder.addRoute("::", 0);
-			        
-			        if (mIsLollipop)			        
-			        	doLollipopAppRouting(builder);			        
-
-			         // Create a new interface using the builder and save the parameters.
-			        ParcelFileDescriptor newInterface = builder.setSession(mSessionName)
-			                .setConfigureIntent(mConfigureIntent)
-			                .establish();
-		
-			        if (mInterface != null)
-			        {
-			        	Log.d(TAG,"Stopping existing VPN interface");
-			        	mInterface.close();
-			        	mInterface = null;
-			        }
-
-		        	mInterface = newInterface;
-			        
-		        	Tun2Socks.Start(mInterface, VPN_MTU, virtualIP, virtualNetMask, localSocks , localDNS , localDnsTransparentProxy);
-		        	
-		        	isRestart = false;
-		        	
-		        }
-		        catch (Exception e)
-		        {
-		        	Log.d(TAG,"tun2Socks has stopped",e);
-		        }
-	    	}
-    		
-    	};
-    	
-    	mThreadVPN.start();
-    }
-    
-    
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-	private void doLollipopAppRouting (Builder builder) throws NameNotFoundException
-    {    
-    	   
-        ArrayList<TorifiedApp> apps = AppManager.getApps(this, TorServiceUtils.getSharedPrefs(getApplicationContext()));
-    
-        boolean perAppEnabled = false;
-        
-        for (TorifiedApp app : apps)
-        {
-        	if (app.isTorified() && (!app.getPackageName().equals(getPackageName())))
-        	{
-        		builder.addAllowedApplication(app.getPackageName());
-        		perAppEnabled = true;
-        	}
-        	
-        }
-    
-        if (!perAppEnabled)
-        	builder.addDisallowedApplication(getPackageName());
-    
-    }
-    
-    @Override
-    public void onRevoke() {
-    
-    	Log.w(TAG,"VPNService REVOKED!");
-    	
-    	if (!isRestart)
-    	{
-	    	SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); 
-	        prefs.edit().putBoolean("pref_vpn", false).commit();      
-	    	stopVPN();	
-    	}
-    	
-    	isRestart = false;
-    	
-        super.onRevoke();
-    }
-    
-    private void startDNS (String dns, int port) throws IOException, TimeoutException
-    {
-    	makePdnsdConf(this, dns, port,OrbotApp.filePdnsd.getParentFile() );
-    	
-        ArrayList<String> customEnv = new ArrayList<String>();
-    	String baseDirectory = OrbotApp.filePdnsd.getParent();
-        Shell shell = Shell.startShell(customEnv, baseDirectory);
-        
-        String cmdString = OrbotApp.filePdnsd.getCanonicalPath() + 
-        		" -c " + baseDirectory + "/pdnsd.conf";
-    
-        SimpleCommand shellCommand = new SimpleCommand(cmdString);
-        
-        shell.add(shellCommand).waitForFinish();
-    
-        Log.i(TAG,"PDNSD: " + shellCommand.getExitCode() + ": " + shellCommand.getOutput());
-        
-    }
-    
-    public static void makePdnsdConf(Context context, String dns, int port, File fileDir) throws FileNotFoundException {
-        String conf = String.format(context.getString(R.string.pdnsd_conf), dns, port);
-
-        File f = new File(fileDir,"pdnsd.conf");
-
-        if (f.exists()) {
-                f.delete();
-        }
-
-        FileOutputStream fos = new FileOutputStream(f, false);
-    	PrintStream ps = new PrintStream(fos);
-    	ps.print(conf);
-    	ps.close();
-    	
-        //f.withWriter { out -> out.print conf };
-        
-        
-        File cache = new File(fileDir,"pdnsd.cache");
-
-        if (!cache.exists()) {
-                try {
-                        cache.createNewFile();
-                } catch (Exception e) {
-
-                }
-        }
-}
-
-    
-}





More information about the tor-commits mailing list