summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2018-03-19 21:51:47 +0100
committerArne Schwabe <arne@rfc2549.org>2018-03-19 22:17:09 +0100
commitb6180c14ff9cf182563d1ea1b5ab5eddc563d691 (patch)
treed3d8253747b9e313b0d5634c58fdc3882177500e
parentae3cc8f568e4d6c6306a609bb8c6a88c15cc334d (diff)
Implement integration of Orbot
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java134
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java191
-rw-r--r--main/src/main/res/layout/server_card.xml1
-rwxr-xr-xmain/src/main/res/values/strings.xml1
4 files changed, 257 insertions, 70 deletions
diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
index 5285b42f..cc777c79 100644
--- a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
+++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
@@ -13,7 +13,6 @@ import android.net.LocalSocketAddress;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
-import android.text.TextUtils;
import android.util.Log;
import junit.framework.Assert;
@@ -38,6 +37,7 @@ import de.blinkt.openvpn.VpnProfile;
public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
private static final String TAG = "openvpn";
+ private static final Vector<OpenVpnManagementThread> active = new Vector<>();
private final Handler mResumeHandler;
private LocalSocket mSocket;
private VpnProfile mProfile;
@@ -46,13 +46,19 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
private LocalServerSocket mServerSocket;
private boolean mWaitingForRelease = false;
private long mLastHoldRelease = 0;
-
- private static final Vector<OpenVpnManagementThread> active = new Vector<>();
private LocalSocket mServerSocketLocal;
private pauseReason lastPauseReason = pauseReason.noNetwork;
private PausedStateCallback mPauseCallback;
private boolean mShuttingDown;
+ private Runnable mResumeHoldRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (shouldBeRunning()) {
+ releaseHoldCmd();
+ }
+ }
+ };
public OpenVpnManagementThread(VpnProfile profile, OpenVPNService openVpnService) {
mProfile = profile;
@@ -61,14 +67,21 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
}
- private Runnable mResumeHoldRunnable = new Runnable() {
- @Override
- public void run() {
- if (shouldBeRunning()) {
- releaseHoldCmd();
+ private static boolean stopOpenVPN() {
+ synchronized (active) {
+ boolean sendCMD = false;
+ for (OpenVpnManagementThread mt : active) {
+ sendCMD = mt.managmentCommand("signal SIGINT\n");
+ try {
+ if (mt.mSocket != null)
+ mt.mSocket.close();
+ } catch (IOException e) {
+ // Ignore close error on already closed socket
+ }
}
+ return sendCMD;
}
- };
+ }
public boolean openManagementInterface(@NonNull Context c) {
// Could take a while to open connection
@@ -123,7 +136,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
return false;
}
-
@Override
public void run() {
byte[] buffer = new byte[2048];
@@ -224,7 +236,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
return pendingInput;
}
-
private void processCommand(String command) {
//Log.i(TAG, "Line from managment" + command);
@@ -368,7 +379,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
//managmentCommand("log on all\n");
}
-
public void releaseHold() {
if (mWaitingForRelease)
releaseHoldCmd();
@@ -414,52 +424,59 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
if (proxyType == Connection.ProxyType.ORBOT) {
- // schwabe: TODO WIP and does not really work
- /* OrbotHelper orbotHelper = OrbotHelper.get(mOpenVPNService);
- orbotHelper.addStatusCallback(new StatusCallback() {
- @Override
- public void onEnabled(Intent statusIntent) {
- VpnStatus.logDebug("Orbot onEnabled:" + statusIntent.toString());
- }
+ VpnStatus.updateStateString("WAIT_ORBOT", "Waiting for Orbot to start", R.string.state_waitorbot, ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET);
+ OrbotHelper orbotHelper = OrbotHelper.get(mOpenVPNService);
+ if (!orbotHelper.checkTorReceier(mOpenVPNService))
+ VpnStatus.logError("Orbot does not seem to be installed!");
- @Override
- public void onStarting() {
- VpnStatus.logDebug("Orbot onStarting");
- }
+ mResumeHandler.postDelayed(orbotStatusTimeOutRunnable, 20 * 1000);
+ orbotHelper.addStatusCallback(mOpenVPNService, statusCallback);
- @Override
- public void onStopping() {
- VpnStatus.logDebug("Orbot onStopping");
- }
+ orbotHelper.sendOrbotStartAndStatusBroadcast();
- @Override
- public void onDisabled() {
- VpnStatus.logDebug("Orbot onDisabled");
- }
+ } else {
+ sendProxyCMD(proxyType, proxyname, proxyport);
+ }
+ }
- @Override
- public void onStatusTimeout() {
- VpnStatus.logDebug("Orbot onStatusTimeout");
- }
- @Override
- public void onNotYetInstalled() {
- VpnStatus.logDebug("Orbot notyetinstalled");
- }
- });
- orbotHelper.init();
- if(!OrbotHelper.requestStartTor(mOpenVPNService))
+ private OrbotHelper.StatusCallback statusCallback = new OrbotHelper.StatusCallback() {
- VpnStatus.logError("Request starting Orbot failed.");
- */
- proxyname = "127.0.0.1";
- proxyport = "8118";
- proxyType = Connection.ProxyType.HTTP;
+ @Override
+ public void onStatus(Intent statusIntent) {
+ String extras = "";
+ for (String key : statusIntent.getExtras().keySet())
+ extras += String.format(Locale.ENGLISH, "%s - '%s'", key, statusIntent.getExtras().getString(key, "[]"));
+ VpnStatus.logDebug("Got Orbot status: " + extras);
+
+ }
+
+ @Override
+ public void onNotYetInstalled() {
+ VpnStatus.logDebug("Orbot not yet installed");
+ }
+
+ @Override
+ public void onOrbotReady(Intent intent, String socksHost, int socksPort) {
+ mResumeHandler.removeCallbacks(orbotStatusTimeOutRunnable);
+ sendProxyCMD(Connection.ProxyType.SOCKS5, socksHost, Integer.toString(socksPort));
+ OrbotHelper.get(mOpenVPNService).removeStatusCallback(this);
+ }
+ };
+
+ private Runnable orbotStatusTimeOutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ sendProxyCMD(Connection.ProxyType.SOCKS5, "127.0.0.1", Integer.toString(OrbotHelper.SOCKS_PROXY_PORT_DEFAULT));
+ OrbotHelper.get(mOpenVPNService).removeStatusCallback(statusCallback);
}
+ };
+
+ private void sendProxyCMD(Connection.ProxyType proxyType, String proxyname, String proxyport) {
if (proxyType != Connection.ProxyType.NONE && proxyname != null) {
- VpnStatus.logInfo(R.string.using_proxy, proxyname, proxyport);
+ VpnStatus.logInfo(R.string.using_proxy, proxyname, proxyname);
String proxycmd = String.format(Locale.ENGLISH, "proxy %s %s %s\n",
proxyType == Connection.ProxyType.HTTP ? "HTTP" : "SOCKS",
@@ -468,7 +485,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
} else {
managmentCommand("proxy NONE\n");
}
-
}
private void processState(String argument) {
@@ -481,7 +497,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
VpnStatus.updateStateString(currentstate, args[2]);
}
-
private void processByteCount(String argument) {
// >BYTECOUNT:{BYTES_IN},{BYTES_OUT}
int comma = argument.indexOf(',');
@@ -492,7 +507,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
}
-
private void processNeedCommand(String argument) {
int p1 = argument.indexOf('\'');
int p2 = argument.indexOf('\'', p1 + 1);
@@ -657,28 +671,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
}
-
private void proccessPWFailed(String needed, String args) {
VpnStatus.updateStateString("AUTH_FAILED", needed + args, R.string.state_auth_failed, ConnectionStatus.LEVEL_AUTH_FAILED);
}
-
- private static boolean stopOpenVPN() {
- synchronized (active) {
- boolean sendCMD = false;
- for (OpenVpnManagementThread mt : active) {
- sendCMD = mt.managmentCommand("signal SIGINT\n");
- try {
- if (mt.mSocket != null)
- mt.mSocket.close();
- } catch (IOException e) {
- // Ignore close error on already closed socket
- }
- }
- return sendCMD;
- }
- }
-
@Override
public void networkChange(boolean samenetwork) {
if (mWaitingForRelease)
diff --git a/main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java b/main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java
new file mode 100644
index 00000000..f2d7e0b9
--- /dev/null
+++ b/main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2012-2018 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+/*
+ * Portions Copyright 2014-2016 Hans-Christoph Steiner
+ * Portions Copyright 2012-2016 Nathan Freitas
+ * Portions Copyright (c) 2016 CommonsWare, LLC
+ *
+ * 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 de.blinkt.openvpn.core;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.text.TextUtils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static de.blinkt.openvpn.core.OpenVPNService.ORBOT_PACKAGE_NAME;
+
+public class OrbotHelper {
+ //! Based on the class from NetCipher but stripped down and modified for icsopenvpn
+
+ /**
+ * {@link Intent} send by Orbot with {@code ON/OFF/STARTING/STOPPING} status
+ * included as an {@link #EXTRA_STATUS} {@code String}. Your app should
+ * always receive {@code ACTION_STATUS Intent}s since any other app could
+ * start Orbot. Also, user-triggered starts and stops will also cause
+ * {@code ACTION_STATUS Intent}s to be broadcast.
+ */
+ public final static String ACTION_STATUS = "org.torproject.android.intent.action.STATUS";
+ public final static String STATUS_ON = "ON";
+ public final static String STATUS_STARTING = "STARTING";
+ public final static String STATUS_STOPPING = "STOPPING";
+ public final static String EXTRA_STATUS = "org.torproject.android.intent.extra.STATUS";
+ /**
+ * A request to Orbot to transparently start Tor services
+ */
+ public final static String ACTION_START = "org.torproject.android.intent.action.START";
+ public final static String EXTRA_PACKAGE_NAME = "org.torproject.android.intent.extra.PACKAGE_NAME";
+ public static final int SOCKS_PROXY_PORT_DEFAULT = 9050;
+ private static OrbotHelper mInstance;
+
+ String EXTRA_SOCKS_PROXY_HOST = "org.torproject.android.intent.extra.SOCKS_PROXY_HOST";
+ String EXTRA_SOCKS_PROXY_PORT = "org.torproject.android.intent.extra.SOCKS_PROXY_PORT";
+ private Context mContext;
+ private Set<StatusCallback> statusCallbacks = new HashSet<>();
+ private BroadcastReceiver orbotStatusReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context c, Intent intent) {
+ if (TextUtils.equals(intent.getAction(),
+ OrbotHelper.ACTION_STATUS)) {
+ for (StatusCallback cb : statusCallbacks) {
+ cb.onStatus(intent);
+ }
+
+ String status = intent.getStringExtra(EXTRA_STATUS);
+ if (TextUtils.equals(status, STATUS_ON)) {
+ int socksPort = intent.getIntExtra(EXTRA_SOCKS_PROXY_PORT, SOCKS_PROXY_PORT_DEFAULT);
+ String socksHost = intent.getStringExtra(EXTRA_SOCKS_PROXY_HOST);
+ if (TextUtils.isEmpty(socksHost))
+ socksHost = "127.0.0.1";
+ for (StatusCallback cb : statusCallbacks) {
+ cb.onOrbotReady(intent, socksHost, socksPort);
+ }
+ }
+
+ }
+ }
+ };
+
+ private OrbotHelper() {
+
+ }
+
+ public static OrbotHelper get(OpenVPNService mOpenVPNService) {
+ if (mInstance == null)
+ mInstance = new OrbotHelper();
+ return mInstance;
+ }
+
+ /**
+ * Gets an {@link Intent} for starting Orbot. Orbot will reply with the
+ * current status to the {@code packageName} of the app in the provided
+ * {@link Context} (i.e. {@link Context#getPackageName()}.
+ */
+ public static Intent getOrbotStartIntent(Context context) {
+ Intent intent = new Intent(ACTION_START);
+ intent.setPackage(ORBOT_PACKAGE_NAME);
+ intent.putExtra(EXTRA_PACKAGE_NAME, context.getPackageName());
+ return intent;
+ }
+
+ /**
+ * Adds a StatusCallback to be called when we find out that
+ * Orbot is ready. If Orbot is ready for use, your callback
+ * will be called with onEnabled() immediately, before this
+ * method returns.
+ *
+ * @param cb a callback
+ * @return the singleton, for chaining
+ */
+ public synchronized OrbotHelper addStatusCallback(Context c, StatusCallback cb) {
+ if (statusCallbacks.size() == 0) {
+ c.getApplicationContext().registerReceiver(orbotStatusReceiver,
+ new IntentFilter(OrbotHelper.ACTION_STATUS));
+ mContext = c.getApplicationContext();
+ }
+ if (!checkTorReceier(c))
+ cb.onNotYetInstalled();
+ statusCallbacks.add(cb);
+ return (this);
+ }
+
+ /**
+ * Removes an existing registered StatusCallback.
+ *
+ * @param cb the callback to remove
+ * @return the singleton, for chaining
+ */
+ public synchronized void removeStatusCallback(StatusCallback cb) {
+ statusCallbacks.remove(cb);
+ if (statusCallbacks.size() == 0)
+ mContext.unregisterReceiver(orbotStatusReceiver);
+ }
+
+ public void sendOrbotStartAndStatusBroadcast() {
+ mContext.sendBroadcast(getOrbotStartIntent(mContext));
+ }
+
+ private void startOrbotService(String action) {
+ Intent clearVPNMode = new Intent();
+ clearVPNMode.setComponent(new ComponentName(ORBOT_PACKAGE_NAME, ".service.TorService"));
+ clearVPNMode.setAction(action);
+ mContext.startService(clearVPNMode);
+ }
+
+ boolean checkTorReceier(Context c) {
+ Intent startOrbot = getOrbotStartIntent(c);
+ PackageManager pm = c.getPackageManager();
+ Intent result = null;
+ List<ResolveInfo> receivers =
+ pm.queryBroadcastReceivers(startOrbot, 0);
+
+ return receivers != null && receivers.size() > 0;
+ }
+
+ public interface StatusCallback {
+ /**
+ * Called when Orbot is operational
+ *
+ * @param statusIntent an Intent containing information about
+ * Orbot, including proxy ports
+ */
+ void onStatus(Intent statusIntent);
+
+
+ /**
+ * Called if Orbot is not yet installed. Usually, you handle
+ * this by checking the return value from init() on OrbotInitializer
+ * or calling isInstalled() on OrbotInitializer. However, if
+ * you have need for it, if a callback is registered before
+ * an init() call determines that Orbot is not installed, your
+ * callback will be called with onNotYetInstalled().
+ */
+ void onNotYetInstalled();
+
+ void onOrbotReady(Intent intent, String socksHost, int socksPort);
+ }
+}
diff --git a/main/src/main/res/layout/server_card.xml b/main/src/main/res/layout/server_card.xml
index db802468..40655d1c 100644
--- a/main/src/main/res/layout/server_card.xml
+++ b/main/src/main/res/layout/server_card.xml
@@ -206,7 +206,6 @@
android:layout_height="wrap_content" />
<RadioButton
- android:visibility="invisible"
android:id="@+id/proxy_orbot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml
index 25adb5a1..96c7920e 100755
--- a/main/src/main/res/values/strings.xml
+++ b/main/src/main/res/values/strings.xml
@@ -250,6 +250,7 @@
<string name="state_tcp_connect">Connecting (TCP)</string>
<string name="state_auth_failed">Authentication failed</string>
<string name="state_nonetwork">Waiting for usable network</string>
+ <string name="state_waitorbot">Waiting for Orbot to start</string>
<string name="statusline_bytecount">↓%2$s %1$s - ↑%4$s %3$s</string>
<string name="notifcation_title_notconnect">Not connected</string>
<string name="start_vpn_title">Connecting to VPN %s</string>