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:
parent
ae3cc8f568
commit
b6180c14ff
@ -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)
|
||||
|
191
main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java
Normal file
191
main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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"
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user