0
0
mirror of https://github.com/schwabe/ics-openvpn.git synced 2024-09-20 03:52:27 +02:00

Implement integration of Orbot

This commit is contained in:
Arne Schwabe 2018-03-19 21:51:47 +01:00
parent ae3cc8f568
commit b6180c14ff
4 changed files with 259 additions and 72 deletions

View File

@ -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,21 +46,11 @@ 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;
public OpenVpnManagementThread(VpnProfile profile, OpenVPNService openVpnService) {
mProfile = profile;
mOpenVPNService = openVpnService;
mResumeHandler = new Handler(openVpnService.getMainLooper());
}
private Runnable mResumeHoldRunnable = new Runnable() {
@Override
public void run() {
@ -70,6 +60,29 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
}
};
public OpenVpnManagementThread(VpnProfile profile, OpenVPNService openVpnService) {
mProfile = profile;
mOpenVPNService = openVpnService;
mResumeHandler = new Handler(openVpnService.getMainLooper());
}
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
int tries = 8;
@ -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!");
mResumeHandler.postDelayed(orbotStatusTimeOutRunnable, 20 * 1000);
orbotHelper.addStatusCallback(mOpenVPNService, statusCallback);
orbotHelper.sendOrbotStartAndStatusBroadcast();
} else {
sendProxyCMD(proxyType, proxyname, proxyport);
}
}
@Override
public void onStarting() {
VpnStatus.logDebug("Orbot onStarting");
}
private OrbotHelper.StatusCallback statusCallback = new OrbotHelper.StatusCallback() {
@Override
public void onStopping() {
VpnStatus.logDebug("Orbot onStopping");
}
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 onDisabled() {
VpnStatus.logDebug("Orbot onDisabled");
}
@Override
public void onStatusTimeout() {
VpnStatus.logDebug("Orbot onStatusTimeout");
}
@Override
public void onNotYetInstalled() {
VpnStatus.logDebug("Orbot not yet installed");
}
});
orbotHelper.init();
if(!OrbotHelper.requestStartTor(mOpenVPNService))
VpnStatus.logError("Request starting Orbot failed.");
*/
proxyname = "127.0.0.1";
proxyport = "8118";
proxyType = Connection.ProxyType.HTTP;
@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)

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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>