[tor-commits] [orbot/master] many small changes to support new binary loading, startup and more

n8fr8 at torproject.org n8fr8 at torproject.org
Tue Mar 26 19:29:46 UTC 2019


commit 56917567cd21a734a35f3bee0e56ba23793b6887
Author: n8fr8 <nathan at guardianproject.info>
Date:   Tue Mar 26 03:03:48 2019 -0400

    many small changes to support new binary loading, startup and more
    - improved handling of port conflicts
    - fixed loading of tor binary on arm64-v8a
    - changed how we execute shell commands
---
 app/build.gradle                                   |   6 +-
 app/src/main/AndroidManifest.xml                   |   4 +-
 jsocksAndroid/src/main/AndroidManifest.xml         |   8 +-
 orbotservice/build.gradle                          |  19 +-
 .../org/torproject/android/service/TorService.java | 470 +++++++++++----------
 .../android/service/TorServiceConstants.java       |   4 +-
 .../android/service/util/CustomNativeLoader.java   | 132 ++++++
 .../android/service/util/CustomShell.java          | 101 +++++
 .../service/util/CustomTorResourceInstaller.java   | 244 +++++++++++
 .../android/service/util/NativeLoader.java         |  35 +-
 .../service/util/OtherResourceInstaller.java       |   6 +-
 .../android/service/util/PortForwarder.java        |  80 ++++
 .../android/service/vpn/OrbotVpnManager.java       |  29 +-
 orbotservice/src/main/jni/Application.mk           |   2 +-
 14 files changed, 859 insertions(+), 281 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index 6b59a43d..4994f51e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -34,8 +34,8 @@ android {
             minSdkVersion 16
             applicationId 'org.torproject.android'
             targetSdkVersion 28
-            versionCode 16060001
-            versionName '16.0.6-BETA-1-tor-0.3.5.8'
+            versionCode 16060002
+            versionName '16.0.6-BETA-2-tor-0.3.5.8'
             archivesBaseName = "Orbot-$versionName"
         }
     }
@@ -55,7 +55,7 @@ android {
             reset()
 
             // Specifies a list of ABIs that Gradle should create APKs for.
-            include "x86", "armeabi", "armeabi-v7a"
+            include "x86", "armeabi", "armeabi-v7a", "x86_64", "arm64-v8a"
 
             // Specifies that we do not want to also generate a universal APK that includes all ABIs.
             universalApk true
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ea90afe7..8e3b6014 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,7 +2,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="org.torproject.android"
-    android:installLocation="auto">
+    android:installLocation="internalOnly">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@@ -30,7 +30,7 @@
         >
         <activity
             android:name=".OrbotMainActivity"
-            android:excludeFromRecents="true"
+            android:excludeFromRecents="false"
             android:launchMode="singleTop">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/jsocksAndroid/src/main/AndroidManifest.xml b/jsocksAndroid/src/main/AndroidManifest.xml
index 7351fd23..fb20a650 100644
--- a/jsocksAndroid/src/main/AndroidManifest.xml
+++ b/jsocksAndroid/src/main/AndroidManifest.xml
@@ -1,11 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.jsocksandroid"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-    <uses-sdk
-        android:minSdkVersion="8"
-        android:targetSdkVersion="21" />
+    package="com.example.jsocksandroid" >
 
 
 </manifest>
diff --git a/orbotservice/build.gradle b/orbotservice/build.gradle
index c9f0e8a4..df861342 100644
--- a/orbotservice/build.gradle
+++ b/orbotservice/build.gradle
@@ -1,8 +1,8 @@
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 27
-    buildToolsVersion '27.0.3'
+    compileSdkVersion 28
+    buildToolsVersion '28.0.3'
 
     sourceSets {
         main {
@@ -12,9 +12,10 @@ android {
 
     defaultConfig {
         minSdkVersion 16
-        targetSdkVersion 27
-        versionCode 1
-        versionName "1.0"
+        targetSdkVersion 28
+        versionCode 16060001
+        versionName '16.0.6-BETA-1-tor-0.3.5.8-orbotservice'
+        archivesBaseName = "OrbotService-$versionName"
 
     }
     buildTypes {
@@ -27,9 +28,11 @@ android {
 
 dependencies {
     implementation project(':jsocksAndroid')
-    implementation 'org.torproject:tor-android-binary:0.3.5.8-rc'
-    implementation 'com.android.support:appcompat-v7:27.1.1'
-    implementation 'com.jrummyapps:android-shell:1.0.1'
+    implementation 'org.torproject:tor-android-binary:0.3.5.8-rc-v2'
+    implementation 'com.android.support:appcompat-v7:28.0.0'
+    implementation 'com.jaredrummler:android-shell:1.0.0'
     implementation fileTree(dir: 'libs', include: ['.so'])
     testImplementation 'junit:junit:4.12'
+
+    implementation 'com.offbynull.portmapper:portmapper:2.0.5'
 }
diff --git a/orbotservice/src/main/java/org/torproject/android/service/TorService.java b/orbotservice/src/main/java/org/torproject/android/service/TorService.java
index f87c0a10..7f2b37cc 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/TorService.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/TorService.java
@@ -39,12 +39,14 @@ import android.support.v4.content.LocalBroadcastManager;
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.jrummyapps.android.shell.CommandResult;
-import com.jrummyapps.android.shell.Shell;
+import com.jaredrummler.android.shell.CommandResult;
+import com.jaredrummler.android.shell.Shell;
 
 import org.torproject.android.binary.TorResourceInstaller;
 import org.torproject.android.control.ConfigEntry;
 import org.torproject.android.control.TorControlConnection;
+import org.torproject.android.service.util.CustomShell;
+import org.torproject.android.service.util.CustomTorResourceInstaller;
 import org.torproject.android.service.util.DummyActivity;
 import org.torproject.android.service.util.OtherResourceInstaller;
 import org.torproject.android.service.util.Prefs;
@@ -59,6 +61,7 @@ import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -87,12 +90,13 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
     private final static int CONTROL_SOCKET_TIMEOUT = 0;
         
     private TorControlConnection conn = null;
-    private Socket torConnSocket = null;
     private int mLastProcessId = -1;
 
-    private int mPortHTTP = HTTP_PROXY_PORT_DEFAULT;
-    private int mPortSOCKS = SOCKS_PROXY_PORT_DEFAULT;
-    
+    public static int mPortSOCKS = SOCKS_PROXY_PORT_DEFAULT;
+    public static int mPortHTTP = HTTP_PROXY_PORT_DEFAULT;
+    public static int mPortDns = TOR_DNS_PORT_DEFAULT;
+    public static int mPortTrans = TOR_TRANSPROXY_PORT_DEFAULT;
+
     private static final int NOTIFY_ID = 1;
     private static final int ERROR_NOTIFY_ID = 3;
     private static final int HS_NOTIFY_ID = 4;
@@ -340,7 +344,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
      */
     public int onStartCommand(Intent intent, int flags, int startId) {
 
-        showToolbarNotification(getString(R.string.status_starting_up),NOTIFY_ID,R.drawable.ic_stat_tor);
+        showToolbarNotification("",NOTIFY_ID,R.drawable.ic_stat_tor);
 
         if (intent != null)
             exec (new IncomingIntentRouter(intent));
@@ -509,12 +513,18 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
 
         try
         {
-            appBinHome = getDir(TorServiceConstants.DIRECTORY_TOR_BINARY, Application.MODE_PRIVATE);
-            appCacheHome = getDir(TorServiceConstants.DIRECTORY_TOR_DATA,Application.MODE_PRIVATE);
+            appBinHome = getFilesDir();//getDir(TorServiceConstants.DIRECTORY_TOR_BINARY, Application.MODE_PRIVATE);
+            if (!appBinHome.exists())
+                appBinHome.mkdirs();
+
+            appCacheHome = getCacheDir();// getDir(TorServiceConstants.DIRECTORY_TOR_DATA,Application.MODE_PRIVATE);
+            if (!appCacheHome.exists())
+                appCacheHome.mkdirs();
 
             fileTor= new File(appBinHome, TorServiceConstants.TOR_ASSET_KEY);
             fileObfsclient = new File(appBinHome, TorServiceConstants.OBFSCLIENT_ASSET_KEY);
             fileTorRc = new File(appBinHome, TorServiceConstants.TORRC_ASSET_KEY);
+            fileControlPort = new File(getFilesDir(), "control.txt");
 
             mHSBasePath = new File(
                     getFilesDir().getAbsolutePath(),
@@ -542,15 +552,14 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
             if (Build.VERSION.SDK_INT >= 26)
                 createNotificationChannel();
 
+            torUpgradeAndConfig();
+
             new Thread(new Runnable ()
             {
                 public void run ()
                 {
                     try
                     {
-                        
-                        torUpgradeAndConfig();
-                    
                         findExistingTorDaemon();
                     }
                     catch (Exception e)
@@ -578,90 +587,93 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
         return mCurrentStatus;
     }
 
-    private void torUpgradeAndConfig() throws IOException, TimeoutException {
+    private boolean torUpgradeAndConfig() throws IOException, TimeoutException {
         if (isTorUpgradeAndConfigComplete)
-            return;
+            return true;
 
         SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
         String version = prefs.getString(PREF_BINARY_TOR_VERSION_INSTALLED,null);
 
         logNotice("checking binary version: " + version);
-        
-        TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome);
 
+        CustomTorResourceInstaller installer = new CustomTorResourceInstaller(this, appBinHome);
         logNotice("upgrading binaries to latest version: " + BINARY_TOR_VERSION);
 
         fileTor = installer.installResources();
+        fileTor = new File(appBinHome,"tor");
+        if (fileTor != null && fileTor.canExecute()) {
+            prefs.edit().putString(PREF_BINARY_TOR_VERSION_INSTALLED, BINARY_TOR_VERSION).apply();
 
-        if (fileTor != null && fileTor.canExecute())
-            prefs.edit().putString(PREF_BINARY_TOR_VERSION_INSTALLED,BINARY_TOR_VERSION).apply();
+            fileTorRc = new File(appBinHome,"torrc");//installer.getTorrcFile();
+            if (!fileTorRc.exists())
+                return false;
 
-        fileTorRc = installer.getTorrcFile();
+            OtherResourceInstaller oInstaller = new OtherResourceInstaller(this, appBinHome);
+            oInstaller.installResources();
 
-        OtherResourceInstaller oInstaller = new OtherResourceInstaller(this, appBinHome);
-        oInstaller.installResources();
+            isTorUpgradeAndConfigComplete = true;
+
+            return true;
+        }
 
-        updateTorConfigFile ();
-        isTorUpgradeAndConfigComplete = true;
+
+        return false;
     }
 
-    private boolean updateTorConfigFile () throws IOException, TimeoutException
+    private File updateTorrcCustomFile () throws IOException, TimeoutException
     {
         SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
 
-        TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome);
-        
         StringBuffer extraLines = new StringBuffer();
-        
-        String TORRC_CONTROLPORT_FILE_KEY = "ControlPortWriteToFile";
-        fileControlPort = new File(appBinHome, "control.txt");
-        extraLines.append(TORRC_CONTROLPORT_FILE_KEY).append(' ').append(fileControlPort.getCanonicalPath()).append('\n');
 
-//         extraLines.append("RunAsDaemon 1").append('\n');
- //        extraLines.append("AvoidDiskWrites 1").append('\n');
+        extraLines.append("\n");
+        extraLines.append("ControlPortWriteToFile").append(' ').append(fileControlPort.getCanonicalPath()).append('\n');
+
+ //       extraLines.append("RunAsDaemon 1").append('\n');
+ //       extraLines.append("AvoidDiskWrites 1").append('\n');
         
-         String socksPortPref = prefs.getString(OrbotConstants.PREF_SOCKS,
-                 String.valueOf(TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT));
+         String socksPortPref = prefs.getString(OrbotConstants.PREF_SOCKS,    String.valueOf(TorServiceConstants.SOCKS_PROXY_PORT_DEFAULT));
+
          if (socksPortPref.indexOf(':')!=-1)
              socksPortPref = socksPortPref.split(":")[1];
          
-        if (!socksPortPref.equalsIgnoreCase("auto"))
-        {
-	        boolean isPortUsed = TorServiceUtils.isPortOpen("127.0.0.1",Integer.parseInt(socksPortPref),500);
-	        
-	        if (isPortUsed) //the specified port is not available, so let Tor find one instead
-	        	socksPortPref = "auto";
-        }
+        socksPortPref = checkPortOrAuto(socksPortPref);
 
-        String isolate = " ";
+        String isolate = "";
         if(prefs.getBoolean(OrbotConstants.PREF_ISOLATE_DEST, false))
         {
-            isolate += "IsolateDestAddr ";
+            isolate += " IsolateDestAddr ";
         }
 
-        String ipv6Pref = " IPv6Traffic ";
+        String ipv6Pref = "";
+
         if(prefs.getBoolean(OrbotConstants.PREF_PREFER_IPV6, true))
         {
-            ipv6Pref += "PreferIPv6 ";
+            ipv6Pref += " IPv6Traffic PreferIPv6 ";
         }
+
         if(prefs.getBoolean(OrbotConstants.PREF_DISABLE_IPV4, false))
         {
-            ipv6Pref += "NoIPv4Traffic ";
+            ipv6Pref += " IPv6Traffic NoIPv4Traffic ";
         }
         
         extraLines.append("SOCKSPort ").append(socksPortPref).append(isolate).append(ipv6Pref).append('\n');
         extraLines.append("SafeSocks 0").append('\n');
         extraLines.append("TestSocks 0").append('\n');
+
     	if (Prefs.openProxyOnAllInterfaces())
     		extraLines.append("SocksListenAddress 0.0.0.0").append('\n');
 
-        extraLines.append("HTTPTunnelPort ").append(mPortHTTP).append(isolate).append(ipv6Pref).append('\n');
 
 
+        String httpPortPref = HTTP_PROXY_PORT_DEFAULT + "";
+        extraLines.append("HTTPTunnelPort ").append(checkPortOrAuto(httpPortPref)).append('\n');
+
         if(prefs.getBoolean(OrbotConstants.PREF_CONNECTION_PADDING, false))
         {
             extraLines.append("ConnectionPadding 1").append('\n');
         }
+
         if(prefs.getBoolean(OrbotConstants.PREF_REDUCED_CONNECTION_PADDING, true))
         {
             extraLines.append("ReducedConnectionPadding 1").append('\n');
@@ -670,9 +682,8 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
         String transPort = prefs.getString("pref_transport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+"");
         String dnsPort = prefs.getString("pref_dnsport", TorServiceConstants.TOR_DNS_PORT_DEFAULT+"");
             
-        extraLines.append("TransPort ").append(transPort).append('\n');
-    	extraLines.append("DNSPort ").append(dnsPort).append('\n');
-    	
+        extraLines.append("TransPort ").append(checkPortOrAuto(transPort)).append('\n');
+    	extraLines.append("DNSPort ").append(checkPortOrAuto(dnsPort)).append('\n');
 
         extraLines.append("VirtualAddrNetwork 10.192.0.0/10").append('\n');
         extraLines.append("AutomapHostsOnResolve 1").append('\n');
@@ -681,13 +692,16 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
                 
         if (Prefs.useDebugLogging())
         {
-        	extraLines.append("Log debug syslog").append('\n');    
+        	extraLines.append("Log debug syslog").append('\n');
         	extraLines.append("Log info syslog").append('\n');
         	extraLines.append("SafeLogging 0").append('\n');   
 
         }
-        
-        processSettingsImpl(extraLines);
+
+        extraLines = processSettingsImpl(extraLines);
+
+        if (extraLines == null)
+            return null;
         
         String torrcCustom = new String(prefs.getString("pref_custom_torrc", "").getBytes("US-ASCII"));
         extraLines.append(torrcCustom).append('\n');
@@ -697,14 +711,38 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
         debug("torrc.custom=" + extraLines.toString());
         
         File fileTorRcCustom = new File(fileTorRc.getAbsolutePath() + ".custom");
-        boolean success = installer.updateTorConfigCustom(fileTorRcCustom, extraLines.toString());    
+        boolean success = updateTorConfigCustom(fileTorRcCustom, extraLines.toString());
         
-        if (success)
+        if (success && fileTorRcCustom.exists())
         {
             logNotice ("success.");
+            return fileTorRcCustom;
         }
-        
-        return success;
+        else
+            return null;
+
+    }
+
+    private String checkPortOrAuto (String port)
+    {
+        if (!port.equalsIgnoreCase("auto"))
+        {
+            boolean isPortUsed = TorServiceUtils.isPortOpen("127.0.0.1",Integer.parseInt(port),500);
+
+            if (isPortUsed) //the specified port is not available, so let Tor find one instead
+                port = "auto";
+        }
+
+        return port;
+    }
+
+    public boolean updateTorConfigCustom(File fileTorRcCustom, String extraLines) throws IOException, FileNotFoundException, TimeoutException {
+        FileWriter fos = new FileWriter(fileTorRcCustom, false);
+        PrintWriter ps = new PrintWriter(fos);
+        ps.print(extraLines);
+        ps.flush();
+        ps.close();
+        return true;
     }
 
     /**
@@ -742,27 +780,28 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
     /**
      * The entire process for starting tor and related services is run from this method.
      */
-    private synchronized void startTor() {
+    private void startTor() {
 
         String torProcId = null;
 
         try { if (conn != null) torProcId = conn.getInfo("process/pid"); }
         catch (Exception e){}
 
-        // STATUS_STARTING is set in onCreate()
-        if (mCurrentStatus == STATUS_STOPPING) {
-            // these states should probably be handled better
-            sendCallbackLogMessage("Ignoring start request, currently " + mCurrentStatus);
-            return;
-        } else if (mCurrentStatus == STATUS_ON && (torProcId != null)) {
-        
-            sendCallbackLogMessage("Ignoring start request, already started.");
-            setTorNetworkEnabled (true);
+        try {
 
-            return;
-        }        
+            // STATUS_STARTING is set in onCreate()
+            if (mCurrentStatus == STATUS_STOPPING) {
+                // these states should probably be handled better
+                sendCallbackLogMessage("Ignoring start request, currently " + mCurrentStatus);
+                return;
+            } else if (mCurrentStatus == STATUS_ON && (torProcId != null)) {
+
+                sendCallbackLogMessage("Ignoring start request, already started.");
+                setTorNetworkEnabled (true);
+
+                return;
+            }
 
-        try {
         	
 	        // make sure there are no stray daemons running
 //	        killAllDaemons();
@@ -786,89 +825,93 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
 
 	        boolean success = runTorShellCmd();
 
+            if (success)
+            {
      //       if (mPortHTTP != -1)
        //         runPolipoShellCmd();
 
-            // Tor is running, update new .onion names at db
-            ContentResolver mCR = getApplicationContext().getContentResolver();
-            Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, null, null, null);
-            if(hidden_services != null) {
-                try {
-                    while (hidden_services.moveToNext()) {
-                        String HSDomain = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.DOMAIN));
-                        Integer HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.PORT));
-                        Integer HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE));
-                        String HSAuthCookieValue = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE_VALUE));
-
-                        // Update only new domains or restored from backup with auth cookie
-                        if((HSDomain == null || HSDomain.length() < 1) || (HSAuthCookie == 1 && (HSAuthCookieValue == null || HSAuthCookieValue.length() < 1))) {
-                            String hsDirPath = new File(mHSBasePath.getAbsolutePath(),"hs" + HSLocalPort).getCanonicalPath();
-                            File file = new File(hsDirPath, "hostname");
-
-                            if (file.exists())
-                            {
-                                ContentValues fields = new ContentValues();
-
-                                try {
-                                    String onionHostname = Utils.readString(new FileInputStream(file)).trim();
-                                    if(HSAuthCookie == 1) {
-                                        String[] aux = onionHostname.split(" ");
-                                        onionHostname = aux[0];
-                                        fields.put(HiddenService.AUTH_COOKIE_VALUE, aux[1]);
+                // Tor is running, update new .onion names at db
+                ContentResolver mCR = getApplicationContext().getContentResolver();
+                Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, null, null, null);
+                if(hidden_services != null) {
+                    try {
+                        while (hidden_services.moveToNext()) {
+                            String HSDomain = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.DOMAIN));
+                            Integer HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.PORT));
+                            Integer HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE));
+                            String HSAuthCookieValue = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE_VALUE));
+
+                            // Update only new domains or restored from backup with auth cookie
+                            if ((HSDomain == null || HSDomain.length() < 1) || (HSAuthCookie == 1 && (HSAuthCookieValue == null || HSAuthCookieValue.length() < 1))) {
+                                String hsDirPath = new File(mHSBasePath.getAbsolutePath(), "hs" + HSLocalPort).getCanonicalPath();
+                                File file = new File(hsDirPath, "hostname");
+
+                                if (file.exists()) {
+                                    ContentValues fields = new ContentValues();
+
+                                    try {
+                                        String onionHostname = Utils.readString(new FileInputStream(file)).trim();
+                                        if (HSAuthCookie == 1) {
+                                            String[] aux = onionHostname.split(" ");
+                                            onionHostname = aux[0];
+                                            fields.put(HiddenService.AUTH_COOKIE_VALUE, aux[1]);
+                                        }
+                                        fields.put(HiddenService.DOMAIN, onionHostname);
+                                        mCR.update(HS_CONTENT_URI, fields, "port=" + HSLocalPort, null);
+                                    } catch (FileNotFoundException e) {
+                                        logException("unable to read onion hostname file", e);
+                                        showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
                                     }
-                                    fields.put(HiddenService.DOMAIN, onionHostname);
-                                    mCR.update(HS_CONTENT_URI, fields, "port=" + HSLocalPort , null);
-                                } catch (FileNotFoundException e) {
-                                    logException("unable to read onion hostname file",e);
+                                } else {
                                     showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
-                                }
-                            }
-                            else
-                            {
-                                showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
 
+                                }
                             }
                         }
+
+                    } catch (NumberFormatException e) {
+                        Log.e(OrbotConstants.TAG, "error parsing hsport", e);
+                    } catch (Exception e) {
+                        Log.e(OrbotConstants.TAG, "error starting share server", e);
                     }
 
-                } catch (NumberFormatException e) {
-                    Log.e(OrbotConstants.TAG,"error parsing hsport",e);
-                } catch (Exception e) {
-                    Log.e(OrbotConstants.TAG,"error starting share server",e);
+                    hidden_services.close();
                 }
-
-                hidden_services.close();
             }
 
         } catch (Exception e) {
             logException("Unable to start Tor: " + e.toString(), e);
+            stopTor();
             showToolbarNotification(
                     getString(R.string.unable_to_start_tor) + ": " + e.getMessage(),
                     ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
-            //stopTor();
+
         }
     }
 
-
-
-    private synchronized boolean runTorShellCmd() throws Exception
+    private boolean runTorShellCmd() throws Exception
     {
         boolean result = true;
 
-        String torrcPath = new File(appBinHome, TORRC_ASSET_KEY).getCanonicalPath();
+        File fileTorrcCustom = updateTorrcCustomFile();
+
+        //make sure Tor exists and we can execute it
+        if ((!fileTor.exists()) || (!fileTor.canExecute()))
+            return false;
+
+        if ((!fileTorRc.exists()) || (!fileTorRc.canRead()) )
+            return false;
+
+        if ((!fileTorrcCustom.exists()) || (!fileTorrcCustom.canRead()) )
+            return false;
 
-        updateTorConfigFile();
-        
         sendCallbackLogMessage(getString(R.string.status_starting_up));
-        
+
         String torCmdString = fileTor.getCanonicalPath()
                 + " DataDirectory " + appCacheHome.getCanonicalPath()
-                + " --defaults-torrc " + torrcPath
-                + " -f " + torrcPath + ".custom";
+                + " --defaults-torrc " + fileTorRc.getCanonicalPath()
+                + " -f " + fileTorrcCustom.getCanonicalPath();
     
-        debug(torCmdString);
-
-
         int exitCode = -1;
 
         try {
@@ -880,39 +923,37 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
             return false;
         }
 
+        if (exitCode == 0) {
+            logNotice("Tor configuration VERIFIED.");
+            try {
+                exitCode = exec(torCmdString, false);
+            } catch (Exception e) {
+                logNotice("Tor was unable to start: " + e.getMessage());
+                result = false;
 
-        try {
-            exitCode = exec(torCmdString, true);
-        }
-        catch (Exception e)
-        {
-            logNotice("Tor was unable to start: " + e.getMessage());
-            return false;
-        }
+                throw new Exception("Tor was unable to start: " + e.getMessage());
 
-        if (exitCode != 0)
-        {
-            logNotice("Tor did not start. Exit:" + exitCode);
-            return false;
-        }
-        
-        //now try to connect
-        mLastProcessId = initControlConnection (100,false);
+            }
 
-        if (mLastProcessId == -1)
-        {
-            logNotice(getString(R.string.couldn_t_start_tor_process_) + "; exit=" + exitCode);
-            sendCallbackLogMessage(getString(R.string.couldn_t_start_tor_process_));
-        
-            throw new Exception ("Unable to start Tor");
-        }
-        else
-        {
-        
-            logNotice("Tor started; process id=" + mLastProcessId);
-           
+            if (exitCode != 0) {
+                logNotice("Tor did not start. Exit:" + exitCode);
+                return false;
+            }
+
+            //now try to connect
+            mLastProcessId = initControlConnection(10, false);
+
+            if (mLastProcessId == -1) {
+                logNotice(getString(R.string.couldn_t_start_tor_process_) + "; exit=" + exitCode);
+                result = false;
+                throw new Exception(getString(R.string.couldn_t_start_tor_process_) + "; exit=" + exitCode);
+            } else {
+
+                logNotice("Tor started; process id=" + mLastProcessId);
+                result = true;
+            }
         }
-        
+
         return result;
     }
 
@@ -924,74 +965,29 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
 
     private int exec (String cmd, boolean wait) throws Exception
     {
-        CommandResult shellResult = Shell.run(cmd);
-        debug("CMD: " + cmd + "; SUCCESS=" + shellResult.isSuccessful());
-
-        if (!shellResult.isSuccessful()) {
-            throw new Exception("Error: " + shellResult.exitCode + " ERR=" + shellResult.getStderr() + " OUT=" + shellResult.getStdout());
-        }
-
-        /**
-        SimpleCommand command = new SimpleCommand(cmd);
-        mShell.add(command);
-        if (wait)
-            command.waitForFinish();
-        return command.getExitCode();
-         **/
-        return shellResult.exitCode;
-    }
-
-    /**
-    private void updatePolipoConfig () throws FileNotFoundException, IOException
-    {
-        
-
-        File file = new File(appBinHome, POLIPOCONFIG_ASSET_KEY);
-        
-        Properties props = new Properties();
-        
-        props.load(new FileReader(file));
-        
-        props.put("socksParentProxy", "\"localhost:" + mPortSOCKS + "\"");
-        props.put("proxyPort",mPortHTTP+"");
-        
-        props.store(new FileWriter(file), "updated");
-        
-    }**/
-    
-
-    /**
-    private void runPolipoShellCmd () throws Exception
-    {
-        
-        logNotice( "Starting polipo process");
+         CommandResult result = CustomShell.run("sh",wait, null, cmd);
+         debug("executing: " + cmd);
+         debug("stdout: " + result.getStdout());
+         debug("stderr: " + result.getStderr());
 
-        updatePolipoConfig();
+         return result.exitCode;
 
-        String polipoConfigPath = new File(appBinHome, POLIPOCONFIG_ASSET_KEY).getCanonicalPath();
-        String cmd = (filePolipo.getCanonicalPath() + " -c " + polipoConfigPath);
 
-        CommandResult shellResult = Shell.run(cmd);
-
-        sendCallbackLogMessage(getString(R.string.privoxy_is_running_on_port_) + mPortHTTP);
-            
-        logNotice("Polipo is running");
-            
-    }**/
+    }
 
     protected TorControlConnection getControlConnection ()
     {
         return conn;
     }
     
-    private int initControlConnection (int maxTries, boolean isReconnect) throws Exception, RuntimeException
+    private int initControlConnection (int maxTries, boolean isReconnect) throws Exception
     {
             int controlPort = -1;
             int attempt = 0;
 
             logNotice( "Waiting for control port...");
             
-            while (conn == null && attempt++ < maxTries)
+            while (conn == null && attempt++ < maxTries && (mCurrentStatus != STATUS_OFF))
             {
                 try
                 {
@@ -1001,8 +997,8 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
                     if (controlPort != -1)
                     {
                         logNotice( "Connecting to control port: " + controlPort);
-                        
-                        torConnSocket = new Socket(IP_LOCALHOST, controlPort);
+
+                        Socket torConnSocket = new Socket(IP_LOCALHOST, controlPort);
                         torConnSocket.setSoTimeout(CONTROL_SOCKET_TIMEOUT);
                         
                         conn = new TorControlConnection(torConnSocket);
@@ -1022,7 +1018,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
                 
                 try {
                 //    logNotice("waiting...");
-                    Thread.sleep(1000); }
+                    Thread.sleep(2000); }
                 catch (Exception e){}
             }
             
@@ -1054,6 +1050,27 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
                          confSocks = st.nextToken().split(":")[1];
                          confSocks = confSocks.substring(0,confSocks.length()-1);
                          mPortSOCKS = Integer.parseInt(confSocks);
+
+                        String confHttp = conn.getInfo("net/listeners/httptunnel");
+                        st = new StringTokenizer(confHttp," ");
+
+                        confHttp = st.nextToken().split(":")[1];
+                        confHttp = confHttp.substring(0,confHttp.length()-1);
+                        mPortHTTP = Integer.parseInt(confHttp);
+
+                        String confDns = conn.getInfo("net/listeners/dns");
+                        st = new StringTokenizer(confDns," ");
+
+                        confDns = st.nextToken().split(":")[1];
+                        confDns = confDns.substring(0,confDns.length()-1);
+                        mPortDns = Integer.parseInt(confDns);
+
+                        String confTrans = conn.getInfo("net/listeners/trans");
+                        st = new StringTokenizer(confTrans," ");
+
+                        confTrans = st.nextToken().split(":")[1];
+                        confTrans = confDns.substring(0,confTrans.length()-1);
+                        mPortTrans = Integer.parseInt(confTrans);
                                                   
                         return Integer.parseInt(torProcId);
                         
@@ -1066,8 +1083,8 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
                     }
                 }
                 
-            
-            return -1;
+            throw new Exception("Tor control port could not be found");
+
 
     }
     
@@ -1233,13 +1250,16 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
             
             return false;
         }
-        
-        public void setTorNetworkEnabled (final boolean isEnabled)
+
+        private String lastNetworkValue = "0";
+
+        public void setTorNetworkEnabled (final boolean isEnabled) throws IOException
         {
 
+            final String newValue =isEnabled ? "0" : "1";
         	
         	//it is possible to not have a connection yet, and someone might try to newnym
-            if (conn != null)
+            if (conn != null && (!lastNetworkValue.equals(newValue)))
             {
                 new Thread ()
                 {
@@ -1247,8 +1267,8 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
                     {
                         try { 
                             
-                            conn.setConf("DisableNetwork", isEnabled ? "0" : "1");
-                        	
+                            conn.setConf("DisableNetwork", newValue);
+                            lastNetworkValue = newValue;
                         }
                         catch (Exception ioe){
                             debug("error requesting newnym: " + ioe.getLocalizedMessage());
@@ -1431,9 +1451,13 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
         	
             if (doNetworKSleep && mCurrentStatus != STATUS_OFF)
             {
-	            setTorNetworkEnabled (mConnectivity);
-	            
-	            if (!mConnectivity)
+                try {
+                    setTorNetworkEnabled (mConnectivity);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+
+                if (!mConnectivity)
                 {
                     logNotice(context.getString(R.string.no_network_connectivity_putting_tor_to_sleep_));
                     showToolbarNotification(getString(R.string.no_internet_connection_tor),NOTIFY_ID,R.drawable.ic_stat_tor_off);
@@ -1451,7 +1475,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
         }
     };
 
-    private boolean processSettingsImpl (StringBuffer extraLines) throws IOException
+    private StringBuffer processSettingsImpl (StringBuffer extraLines) throws IOException
     {
         logNotice(getString(R.string.updating_settings_in_tor_service));
         
@@ -1612,7 +1636,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
         {
            showToolbarNotification (getString(R.string.your_reachableaddresses_settings_caused_an_exception_),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr);
 
-           return false;
+           return null;
         }
 
         try
@@ -1636,7 +1660,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
              showToolbarNotification (getString(R.string.your_relay_settings_caused_an_exception_),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr);
 
           
-            return false;
+            return null;
         }
 
         ContentResolver mCR = getApplicationContext().getContentResolver();
@@ -1685,7 +1709,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
             client_cookies.close();
 		}
 
-        return true;
+        return extraLines;
     }
     
     public static String flattenToAscii(String string) {
diff --git a/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java b/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java
index 4b62a6a8..b557de80 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java
@@ -10,8 +10,8 @@ public interface TorServiceConstants {
 	String TOR_APP_USERNAME = "org.torproject.android";
 	String BROWSER_APP_USERNAME = "info.guardianproject.orfox";
 	
-	String DIRECTORY_TOR_BINARY = "bin";
-	String DIRECTORY_TOR_DATA = "data";
+	//String DIRECTORY_TOR_BINARY = "bin";
+	//String DIRECTORY_TOR_DATA = "data";
 	
 	//name of the tor C binary
 	String TOR_ASSET_KEY = "tor";	
diff --git a/orbotservice/src/main/java/org/torproject/android/service/util/CustomNativeLoader.java b/orbotservice/src/main/java/org/torproject/android/service/util/CustomNativeLoader.java
new file mode 100644
index 00000000..6e75a98e
--- /dev/null
+++ b/orbotservice/src/main/java/org/torproject/android/service/util/CustomNativeLoader.java
@@ -0,0 +1,132 @@
+package org.torproject.android.service.util;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+public class CustomNativeLoader {
+
+    private final static String LIB_NAME = "tor";
+    private final static String LIB_SO_NAME = "tor.so";
+
+    private final static String TAG = "TorNativeLoader";
+
+    private static boolean loadFromZip(Context context, File destLocalFile, String arch) {
+
+
+        ZipFile zipFile = null;
+        InputStream stream = null;
+
+        try {
+            zipFile = new ZipFile(context.getApplicationInfo().sourceDir);
+            ZipEntry entry = zipFile.getEntry("lib/" + arch + "/" + LIB_SO_NAME);
+            if (entry == null) {
+                throw new Exception("Unable to find file in apk:" + "lib/" + arch + "/" + LIB_NAME);
+            }
+
+            //how we wrap this in another stream because the native .so is zipped itself
+            stream = zipFile.getInputStream(entry);
+
+            OutputStream out = new FileOutputStream(destLocalFile);
+            byte[] buf = new byte[4096];
+            int len;
+            while ((len = stream.read(buf)) > 0) {
+                Thread.yield();
+                out.write(buf, 0, len);
+            }
+            out.close();
+
+            if (Build.VERSION.SDK_INT >= 9) {
+                destLocalFile.setReadable(true, false);
+                destLocalFile.setExecutable(true, false);
+                destLocalFile.setWritable(true);
+            }
+
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, e.getMessage());
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (Exception e) {
+                    Log.e(TAG, e.getMessage());
+                }
+            }
+            if (zipFile != null) {
+                try {
+                    zipFile.close();
+                } catch (Exception e) {
+                    Log.e(TAG, e.getMessage());
+                }
+            }
+        }
+        return false;
+    }
+
+    public static synchronized File initNativeLibs(Context context, File destLocalFile) {
+
+        try {
+            String folder = Build.CPU_ABI;
+
+            /**
+             try {
+
+             if (Build.CPU_ABI.equalsIgnoreCase("arm64-v8a")) {
+             folder = "arm64-v8a";
+             }
+             else if (Build.CPU_ABI.equalsIgnoreCase("arm64")) {
+             folder = "arm64";
+             }
+             else if (Build.CPU_ABI.equalsIgnoreCase("x86_64")) {
+             folder = "x86_64";
+             }
+             else if (Build.CPU_ABI.equalsIgnoreCase("armeabi-v7a")) {
+             folder = "armeabi-v7a";
+             }
+             else if (Build.CPU_ABI.equalsIgnoreCase("armeabi")) {
+             folder = "armeabi";
+             } else if (Build.CPU_ABI.equalsIgnoreCase("x86")) {
+             folder = "x86";
+             } else if (Build.CPU_ABI.equalsIgnoreCase("mips")) {
+             folder = "mips";
+             } else {
+             folder = "armeabi";
+             //FileLog.e("tmessages", "Unsupported arch: " + Build.CPU_ABI);
+             }
+
+             } catch (Exception e) {
+             //  FileLog.e("tmessages", e);
+             Log.e(TAG, e.getMessage(),e);
+             folder = "armeabi";
+             }**/
+
+
+            String javaArch = System.getProperty("os.arch");
+            if (javaArch != null && javaArch.contains("686")) {
+                folder = "x86";
+            }
+
+
+            if (loadFromZip(context, destLocalFile, folder)) {
+                return destLocalFile;
+            }
+
+        } catch (Throwable e) {
+            Log.e(TAG, e.getMessage(),e);
+        }
+
+
+        return null;
+    }
+}
+
diff --git a/orbotservice/src/main/java/org/torproject/android/service/util/CustomShell.java b/orbotservice/src/main/java/org/torproject/android/service/util/CustomShell.java
new file mode 100644
index 00000000..8bd5fe6f
--- /dev/null
+++ b/orbotservice/src/main/java/org/torproject/android/service/util/CustomShell.java
@@ -0,0 +1,101 @@
+package org.torproject.android.service.util;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+
+import com.jaredrummler.android.shell.CommandResult;
+import com.jaredrummler.android.shell.Shell;
+import com.jaredrummler.android.shell.ShellExitCode;
+import com.jaredrummler.android.shell.StreamGobbler;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class CustomShell extends Shell {
+
+
+    @WorkerThread
+    public static CommandResult run(@NonNull String shell, boolean waitFor, @Nullable String[] env, @NonNull String command) {
+        List<String> stdout = Collections.synchronizedList(new ArrayList<String>());
+        List<String> stderr = Collections.synchronizedList(new ArrayList<String>());
+        int exitCode = -1;
+
+        try {
+
+            // setup our process, retrieve stdin stream, and stdout/stderr gobblers
+            //Process process = runWithEnv(command, env);
+            ProcessBuilder builder = new ProcessBuilder();
+            builder.command("/system/bin/sh", "-c", command);
+            Process process = builder.start();
+
+         //   DataOutputStream stdin = new DataOutputStream(process.getOutputStream());
+            StreamGobbler stdoutGobbler = null;
+            StreamGobbler stderrGobbler = null;
+
+            if (waitFor) {
+                stdoutGobbler = new StreamGobbler(process.getInputStream(), stdout);
+                stderrGobbler = new StreamGobbler(process.getErrorStream(), stderr);
+
+                // start gobbling and write our commands to the shell
+                stdoutGobbler.start();
+                stderrGobbler.start();
+            }
+
+            /**
+
+            try {
+                for (String write : commands) {
+                    stdin.write((write + " &\n").getBytes("UTF-8"));
+                    stdin.flush();
+                }
+
+                if (waitFor)
+                    stdin.write("exit\n".getBytes("UTF-8"));
+
+                stdin.flush();
+            } catch (IOException e) {
+                //noinspection StatementWithEmptyBody
+                if (e.getMessage().contains("EPIPE")) {
+                    // method most horrid to catch broken pipe, in which case we do nothing. the command is not a shell, the
+                    // shell closed stdin, the script already contained the exit command, etc. these cases we want the output
+                    // instead of returning null
+                } else {
+                    // other issues we don't know how to handle, leads to returning null
+                    throw e;
+                }
+            }**/
+
+            // wait for our process to finish, while we gobble away in the background
+            if (waitFor)
+                exitCode = process.waitFor();
+            else
+                exitCode = 0;
+
+            // make sure our threads are done gobbling, our streams are closed, and the process is destroyed - while the
+            // latter two shouldn't be needed in theory, and may even produce warnings, in "normal" Java they are required
+            // for guaranteed cleanup of resources, so lets be safe and do this on Android as well
+           /**
+            try {
+                stdin.close();
+            } catch (IOException e) {
+                // might be closed already
+            }**/
+
+           if (waitFor) {
+               stdoutGobbler.join();
+               stderrGobbler.join();
+           }
+
+        } catch (InterruptedException e) {
+            exitCode = ShellExitCode.WATCHDOG_EXIT;
+        } catch (IOException e) {
+            exitCode = ShellExitCode.SHELL_WRONG_UID;
+        }
+
+        return new CommandResult(stdout, stderr, exitCode);
+    }
+}
diff --git a/orbotservice/src/main/java/org/torproject/android/service/util/CustomTorResourceInstaller.java b/orbotservice/src/main/java/org/torproject/android/service/util/CustomTorResourceInstaller.java
new file mode 100644
index 00000000..043987e2
--- /dev/null
+++ b/orbotservice/src/main/java/org/torproject/android/service/util/CustomTorResourceInstaller.java
@@ -0,0 +1,244 @@
+package org.torproject.android.service.util;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.util.Log;
+
+import org.torproject.android.binary.NativeLoader;
+import org.torproject.android.binary.TorServiceConstants;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.concurrent.TimeoutException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+
+public class CustomTorResourceInstaller implements TorServiceConstants {
+
+
+    File installFolder;
+    Context context;
+
+    File fileTorrc;
+    File fileTor;
+
+    public CustomTorResourceInstaller (Context context, File installFolder)
+    {
+        this.installFolder = installFolder;
+        this.context = context;
+    }
+
+    public File getTorrcFile ()
+    {
+        return fileTorrc;
+    }
+
+    public File getTorFile ()
+    {
+        return fileTor;
+    }
+
+    /**
+     private void deleteDirectory(File file) {
+     if( file.exists() ) {
+     if (file.isDirectory()) {
+     File[] files = file.listFiles();
+     for(int i=0; i<files.length; i++) {
+     if(files[i].isDirectory()) {
+     deleteDirectory(files[i]);
+     }
+     else {
+     files[i].delete();
+     }
+     }
+     }
+
+     file.delete();
+     }
+     }**/
+
+    //
+    /*
+     * Extract the Tor resources from the APK file using ZIP
+     *
+     * @File path to the Tor executable
+     */
+    public File installResources () throws IOException, TimeoutException
+    {
+
+        fileTor = new File(installFolder, TOR_ASSET_KEY);
+
+        if (!installFolder.exists())
+            installFolder.mkdirs();
+
+        installGeoIP();
+        fileTorrc = assetToFile(COMMON_ASSET_KEY + TORRC_ASSET_KEY, TORRC_ASSET_KEY, false, false);
+
+        File fileNativeDir = new File(getNativeLibraryDir(context));
+        fileTor = new File(fileNativeDir,TOR_ASSET_KEY + ".so");
+
+        if (fileTor.exists())
+        {
+            if (fileTor.canExecute())
+                return fileTor;
+            else
+            {
+                setExecutable(fileTor);
+
+                if (fileTor.canExecute())
+                    return fileTor;
+            }
+        }
+
+        if (fileTor.exists()) {
+            InputStream is = new FileInputStream(fileTor);
+            streamToFile(is, fileTor, false, true);
+            setExecutable(fileTor);
+
+            if (fileTor.exists() && fileTor.canExecute())
+                return fileTor;
+        }
+
+        //let's try another approach
+        fileTor = new File(installFolder, TOR_ASSET_KEY);
+        //fileTor = NativeLoader.initNativeLibs(context,fileTor);
+        CustomNativeLoader.initNativeLibs(context,fileTor);
+
+        setExecutable(fileTor);
+
+        if (fileTor != null && fileTor.exists() && fileTor.canExecute())
+            return fileTor;
+
+        return null;
+    }
+
+
+    // Return Full path to the directory where native JNI libraries are stored.
+    private static String getNativeLibraryDir(Context context) {
+        ApplicationInfo appInfo = context.getApplicationInfo();
+        return appInfo.nativeLibraryDir;
+    }
+
+
+    public boolean updateTorConfigCustom (File fileTorRcCustom, String extraLines) throws IOException, FileNotFoundException, TimeoutException
+    {
+        if (fileTorRcCustom.exists())
+        {
+            fileTorRcCustom.delete();
+            Log.d("torResources","deleting existing torrc.custom");
+        }
+        else
+            fileTorRcCustom.createNewFile();
+
+        FileOutputStream fos = new FileOutputStream(fileTorRcCustom, false);
+        PrintStream ps = new PrintStream(fos);
+        ps.print(extraLines);
+        ps.close();
+
+        return true;
+    }
+
+    /*
+     * Extract the Tor binary from the APK file using ZIP
+     */
+
+    private boolean installGeoIP () throws IOException
+    {
+
+        assetToFile(COMMON_ASSET_KEY + GEOIP_ASSET_KEY, GEOIP_ASSET_KEY, false, false);
+
+        assetToFile(COMMON_ASSET_KEY + GEOIP6_ASSET_KEY, GEOIP6_ASSET_KEY, false, false);
+
+        return true;
+    }
+
+    /*
+     * Reads file from assetPath/assetKey writes it to the install folder
+     */
+    private File assetToFile(String assetPath, String assetKey, boolean isZipped, boolean isExecutable) throws IOException {
+        InputStream is = context.getAssets().open(assetPath);
+        File outFile = new File(installFolder, assetKey);
+        streamToFile(is, outFile, false, isZipped);
+        if (isExecutable) {
+            setExecutable(outFile);
+        }
+        return outFile;
+    }
+
+
+    /*
+     * Write the inputstream contents to the file
+     */
+    private static boolean streamToFile(InputStream stm, File outFile, boolean append, boolean zip) throws IOException
+
+    {
+        byte[] buffer = new byte[FILE_WRITE_BUFFER_SIZE];
+
+        int bytecount;
+
+        OutputStream stmOut = new FileOutputStream(outFile.getAbsolutePath(), append);
+        ZipInputStream zis = null;
+
+        if (zip)
+        {
+            zis = new ZipInputStream(stm);
+            ZipEntry ze = zis.getNextEntry();
+            stm = zis;
+
+        }
+
+        while ((bytecount = stm.read(buffer)) > 0)
+        {
+
+            stmOut.write(buffer, 0, bytecount);
+
+        }
+
+        stmOut.close();
+        stm.close();
+
+        if (zis != null)
+            zis.close();
+
+
+        return true;
+
+    }
+
+
+
+    private void setExecutable(File fileBin) {
+        fileBin.setReadable(true);
+        fileBin.setExecutable(true);
+        fileBin.setWritable(false);
+        fileBin.setWritable(true, true);
+    }
+
+    private static File[] listf(String directoryName) {
+
+        // .............list file
+        File directory = new File(directoryName);
+
+        // get all the files from a directory
+        File[] fList = directory.listFiles();
+
+        if (fList != null)
+            for (File file : fList) {
+                if (file.isFile()) {
+                    Log.d(TAG,file.getAbsolutePath());
+                } else if (file.isDirectory()) {
+                    listf(file.getAbsolutePath());
+                }
+            }
+
+        return fList;
+    }
+}
+
diff --git a/orbotservice/src/main/java/org/torproject/android/service/util/NativeLoader.java b/orbotservice/src/main/java/org/torproject/android/service/util/NativeLoader.java
index 4ad10bb1..8621add2 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/util/NativeLoader.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/util/NativeLoader.java
@@ -9,6 +9,7 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Enumeration;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
@@ -24,8 +25,20 @@ public class NativeLoader {
         InputStream stream = null;
         try {
             zipFile = new ZipFile(context.getApplicationInfo().sourceDir);
+
+            /**
+            Enumeration<? extends ZipEntry> entries = zipFile.entries();
+            while (entries.hasMoreElements())
+            {
+                ZipEntry entry = entries.nextElement();
+                Log.d("Zip","entry: " + entry.getName());
+            }
+            **/
+
             ZipEntry entry = zipFile.getEntry("lib/" + folder + "/" + libName + ".so");
             if (entry == null) {
+                entry = zipFile.getEntry("lib/" + folder + "/" + libName);
+                if (entry == null)
                 throw new Exception("Unable to find file in apk:" + "lib/" + folder + "/" + libName);
             }
             stream = zipFile.getInputStream(entry);
@@ -68,27 +81,7 @@ public class NativeLoader {
     public static synchronized boolean initNativeLibs(Context context, String binaryName, File destLocalFile) {
 
         try {
-            String folder = null;
-
-            try {
-
-                if (Build.CPU_ABI.equalsIgnoreCase("armeabi-v7a")) {
-                    folder = "armeabi-v7a";
-                } else if (Build.CPU_ABI.startsWith("armeabi")) {
-                    folder = "armeabi";
-                } else if (Build.CPU_ABI.equalsIgnoreCase("x86")) {
-                    folder = "x86";
-                } else if (Build.CPU_ABI.equalsIgnoreCase("mips")) {
-                    folder = "mips";
-                } else {
-                    folder = "armeabi";
-                }
-            } catch (Exception e) {
-                //  FileLog.e("tmessages", e);
-                Log.e(TAG, e.getMessage());
-                folder = "armeabi";
-            }
-
+            String folder = Build.CPU_ABI;
 
             String javaArch = System.getProperty("os.arch");
             if (javaArch != null && javaArch.contains("686")) {
diff --git a/orbotservice/src/main/java/org/torproject/android/service/util/OtherResourceInstaller.java b/orbotservice/src/main/java/org/torproject/android/service/util/OtherResourceInstaller.java
index 309f7e58..241f6c7e 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/util/OtherResourceInstaller.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/util/OtherResourceInstaller.java
@@ -70,12 +70,12 @@ public class OtherResourceInstaller implements TorServiceConstants {
         if (!installFolder.exists())
             installFolder.mkdirs();
 
-        outFile = new File(installFolder, OBFSCLIENT_ASSET_KEY);
-        NativeLoader.initNativeLibs(context,OBFSCLIENT_ASSET_KEY,outFile);
-
         outFile = new File(installFolder, PDNSD_ASSET_KEY);
         NativeLoader.initNativeLibs(context,PDNSD_ASSET_KEY,outFile);
 
+//        outFile = new File(installFolder, OBFSCLIENT_ASSET_KEY);
+ //       NativeLoader.initNativeLibs(context,OBFSCLIENT_ASSET_KEY,outFile);
+
         return true;
     }
 
diff --git a/orbotservice/src/main/java/org/torproject/android/service/util/PortForwarder.java b/orbotservice/src/main/java/org/torproject/android/service/util/PortForwarder.java
new file mode 100644
index 00000000..d487fb43
--- /dev/null
+++ b/orbotservice/src/main/java/org/torproject/android/service/util/PortForwarder.java
@@ -0,0 +1,80 @@
+package org.torproject.android.service.util;
+
+import android.util.Log;
+
+import com.offbynull.portmapper.PortMapperFactory;
+import com.offbynull.portmapper.gateway.Bus;
+import com.offbynull.portmapper.gateway.Gateway;
+import com.offbynull.portmapper.gateways.network.NetworkGateway;
+import com.offbynull.portmapper.gateways.network.internalmessages.KillNetworkRequest;
+import com.offbynull.portmapper.gateways.process.ProcessGateway;
+import com.offbynull.portmapper.gateways.process.internalmessages.KillProcessRequest;
+import com.offbynull.portmapper.mapper.MappedPort;
+import com.offbynull.portmapper.mapper.PortMapper;
+import com.offbynull.portmapper.mapper.PortType;
+
+import java.util.List;
+
+public class PortForwarder {
+
+    private boolean shutdown = false;
+    private Thread mThread = null;
+
+    public void shutdown ()
+    {
+        shutdown = true;
+    }
+
+    public void forward (final int internalPort, final int externalPort, final long lifetime) throws InterruptedException {
+
+        mThread = new Thread ()
+        {
+            public void run ()
+            {
+                try {
+                    forwardSync(internalPort, externalPort, lifetime);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        };
+
+        mThread.start();
+    }
+
+
+    public void forwardSync (int internalPort, int externalPort, long lifetime) throws InterruptedException {
+        // Start gateways
+        Gateway network = NetworkGateway.create();
+        Gateway process = ProcessGateway.create();
+        Bus networkBus = network.getBus();
+        Bus processBus = process.getBus();
+
+// Discover port forwarding devices and take the first one found
+        List<PortMapper> mappers = PortMapperFactory.discover(networkBus, processBus);
+        PortMapper mapper = mappers.get(0);
+
+// Map internal port 12345 to some external port (55555 preferred)
+//
+// IMPORTANT NOTE: Many devices prevent you from mapping ports that are <= 1024
+// (both internal and external ports). Be mindful of this when choosing which
+// ports you want to map.
+        MappedPort mappedPort = mapper.mapPort(PortType.TCP, internalPort, externalPort, lifetime);
+        Log.d(getClass().getName(),"Port mapping added: " + mappedPort);
+
+// Refresh mapping half-way through the lifetime of the mapping (for example,
+// if the mapping is available for 40 seconds, refresh it every 20 seconds)
+        while(!shutdown) {
+            mappedPort = mapper.refreshPort(mappedPort, mappedPort.getLifetime() / 2L);
+            Log.d(getClass().getName(),"Port mapping refreshed: " + mappedPort);
+            Thread.sleep(mappedPort.getLifetime() * 1000L);
+        }
+
+// Unmap port 12345
+        mapper.unmapPort(mappedPort);
+
+// Stop gateways
+        networkBus.send(new KillNetworkRequest());
+        processBus.send(new KillProcessRequest()); // can kill this after discovery
+    }
+}
diff --git a/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java b/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java
index e5419f3c..5ca4505c 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java
@@ -37,6 +37,7 @@ import com.runjva.sourceforge.jsocks.protocol.ProxyServer;
 import com.runjva.sourceforge.jsocks.server.ServerAuthenticatorNone;
 
 import org.torproject.android.service.R;
+import org.torproject.android.service.TorService;
 import org.torproject.android.service.TorServiceConstants;
 import org.torproject.android.service.util.TorServiceUtils;
 
@@ -73,12 +74,15 @@ public class OrbotVpnManager implements Handler.Callback {
     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 final static String DEFAULT_ACTUAL_DNS_HOST = "127.0.0.1";
+    //private final static int DEFAULT_ACTUAL_DNS_PORT = TorServiceConstants.TOR_DNS_PORT_DEFAULT;
+
 
 
 	File filePdnsd = null;
 
+	private final static int PDNSD_PORT = 8091;
+
 	private boolean isRestart = false;
     
     private VpnService mService;
@@ -88,7 +92,7 @@ public class OrbotVpnManager implements Handler.Callback {
     {
     	mService = service;
 
-		File fileBinHome = mService.getDir(TorServiceConstants.DIRECTORY_TOR_BINARY, Application.MODE_PRIVATE);
+		File fileBinHome = service.getFilesDir();//mService.getDir(TorServiceConstants.DIRECTORY_TOR_BINARY, Application.MODE_PRIVATE);
 		filePdnsd = new File(fileBinHome,TorServiceConstants.PDNSD_ASSET_KEY);
 
 		Tun2Socks.init();
@@ -276,6 +280,8 @@ public class OrbotVpnManager implements Handler.Callback {
         	isRestart = true;
         	Tun2Socks.Stop();
         }
+
+        final int localDns = TorService.mPortDns;
         
     	mThreadVPN = new Thread ()
     	{
@@ -290,10 +296,7 @@ public class OrbotVpnManager implements Handler.Callback {
 	    				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";
 
@@ -306,8 +309,8 @@ public class OrbotVpnManager implements Handler.Callback {
 		    		final String localSocks = localhost + ':'
 		    		        + String.valueOf(mTorSocks);
 		    		
-		    		final String localDNS = virtualGateway + ':' + "8091";//String.valueOf(TorServiceConstants.TOR_DNS_PORT_DEFAULT);
-					//final String localDNS = virtualGateway + ":" + DEFAULT_ACTUAL_DNS_PORT;
+		    		final String localDNS = virtualGateway + ':' + PDNSD_PORT;
+
 		    		final boolean localDnsTransparentProxy = true;
 		        	
 			        builder.setMtu(VPN_MTU);
@@ -345,8 +348,12 @@ public class OrbotVpnManager implements Handler.Callback {
 		        	Tun2Socks.Start(mInterface, VPN_MTU, virtualIP, virtualNetMask, localSocks , localDNS , localDnsTransparentProxy);
 		        	
 		        	isRestart = false;
-		        	
-		        }
+
+					//start PDNSD daemon pointing to actual DNS
+					startDNS("127.0.0.1",localDns);
+
+
+				}
 		        catch (Exception e)
 		        {
 		        	Log.d(TAG,"tun2Socks has stopped",e);
diff --git a/orbotservice/src/main/jni/Application.mk b/orbotservice/src/main/jni/Application.mk
index d35e1e9c..58e4e08f 100644
--- a/orbotservice/src/main/jni/Application.mk
+++ b/orbotservice/src/main/jni/Application.mk
@@ -1,3 +1,3 @@
-APP_ABI      			:= armeabi armeabi-v7a x86
+APP_ABI      			:= armeabi armeabi-v7a x86 arm64-v8a x86_64
 APP_PLATFORM 			:= android-16
 APP_STL      			:= c++_static





More information about the tor-commits mailing list