diff options
author | Arne Schwabe <arne@rfc2549.org> | 2018-03-19 21:51:47 +0100 |
---|---|---|
committer | Arne Schwabe <arne@rfc2549.org> | 2018-03-19 22:17:09 +0100 |
commit | b6180c14ff9cf182563d1ea1b5ab5eddc563d691 (patch) | |
tree | d3d8253747b9e313b0d5634c58fdc3882177500e | |
parent | ae3cc8f568e4d6c6306a609bb8c6a88c15cc334d (diff) |
Implement integration of Orbot
-rw-r--r-- | main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java | 134 | ||||
-rw-r--r-- | main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java | 191 | ||||
-rw-r--r-- | main/src/main/res/layout/server_card.xml | 1 | ||||
-rwxr-xr-x | main/src/main/res/values/strings.xml | 1 |
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> |