added tethering support

This commit is contained in:
Nathan Freitas 2011-06-02 16:21:50 -04:00
parent 422ba4bbcd
commit ddb4793b0a
4 changed files with 713 additions and 637 deletions

View File

@ -1,9 +1,8 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */ /* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - https://guardianproject.info */
/* See LICENSE for licensing information */ /* See LICENSE for licensing information */
package org.torproject.android; package org.torproject.android;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -47,35 +46,48 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
{ {
/* Useful UI bits */ /* Useful UI bits */
// so this is probably pretty obvious, here, but also an area
// which we might see quite a bit of change+complexity was the main screen
// UI gets new features
private TextView lblStatus = null; //the main text display widget private TextView lblStatus = null; //the main text display widget
private ImageView imgStatus = null; //the main touchable image for activating Orbot private ImageView imgStatus = null; //the main touchable image for activating Orbot
private ProgressDialog progressDialog; private ProgressDialog progressDialog; //the spinning progress dialog that shows up now and then
private MenuItem mItemOnOff = null; private MenuItem mItemOnOff = null; //the menu item which we toggle based on Orbot state
/* Some tracking bits */ /* Some tracking bits */
private int torStatus = STATUS_READY; //latest status reported from the tor service private int torStatus = STATUS_READY; //latest status reported from the tor service
// this is a value we get passed back from the TorService
/* Tor Service interaction */ /* Tor Service interaction */
/* The primary interface we will be calling on the service. */ /* The primary interface we will be calling on the service. */
ITorService mService = null; ITorService mService = null; //interface to remote TorService
private boolean autoStartOnBind = false; private boolean autoStartOnBind = false; //controls whether service starts when class binds to it
SharedPreferences prefs; SharedPreferences prefs; //what the user really wants!
/** Called when the activity is first created. */ /**
* When the Orbot activity is created, we call startService
* to ensure the Tor remote service is running. However, it may
* already be running, and this should not create more than one instnace
*/
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
//if Tor binary is not running, then start the service up //if Tor binary is not running, then start the service up
//might want to look at whether we need to call this every time
//or whether binding to the service is enough
startService(new Intent(INTENT_TOR_SERVICE)); startService(new Intent(INTENT_TOR_SERVICE));
//something to play with on the UI branch
setTheme(android.R.style.Theme_Black_NoTitleBar); setTheme(android.R.style.Theme_Black_NoTitleBar);
prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs = PreferenceManager.getDefaultSharedPreferences(this);
//same here - layout_main has been cleaned up since 1.0.5.2 a bit (removed table as you recmnd)
//but ther eis more to be done
setContentView(R.layout.layout_main); setContentView(R.layout.layout_main);
//obvious? -yep got everything so far
lblStatus = (TextView)findViewById(R.id.lblStatus); lblStatus = (TextView)findViewById(R.id.lblStatus);
lblStatus.setOnLongClickListener(this); lblStatus.setOnLongClickListener(this);
imgStatus = (ImageView)findViewById(R.id.imgStatus); imgStatus = (ImageView)findViewById(R.id.imgStatus);
@ -201,14 +213,25 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
return true; return true;
} }
/**
* This is our attempt to REALLY exit Orbot, and stop the background service
* However, Android doesn't like people "quitting" apps, and/or our code may not
* be quite right b/c no matter what we do, it seems like the TorService still exists
**/
private void doExit () private void doExit ()
{ {
try { try {
//one of the confusing things about all of this code is the multiple
//places where things like "stopTor" are called, both in the Activity and the Service
//not something to tackle in your first iteration, but i thin we can talk about fixing
//terminology but also making sure there are clear distinctions in control
stopTor(); stopTor();
//perhaps this should be referenced as INTENT_TOR_SERVICE as in startService
stopService(new Intent(ITorService.class.getName())); stopService(new Intent(ITorService.class.getName()));
//clears all notifications from the status bar
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancelAll(); mNotificationManager.cancelAll();
@ -228,6 +251,7 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
/* /*
public boolean onKeyDown(int keyCode, KeyEvent event){ public boolean onKeyDown(int keyCode, KeyEvent event){
//yeah this should probably go away now :) - or not
if(keyCode==KeyEvent.KEYCODE_BACK){ if(keyCode==KeyEvent.KEYCODE_BACK){
if(currentView != R.layout.layout_main){ if(currentView != R.layout.layout_main){
@ -256,6 +280,13 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
aDialog.dismiss(); aDialog.dismiss();
} }
/**
* i think we need to suport this onSave/Restore code more b/c i think
* when someone rotates the screen, and the state is lost during setup
* etc it causes problems. this might be the place to solve that in the wizard - hmm this prob coz android restarts the activity when the screen is rotated. this will prob be fixed(?) when
we redesign the wizard into a view not just a dialogbox
cool
**/
public void onSaveInstanceState(Bundle savedInstanceState) { public void onSaveInstanceState(Bundle savedInstanceState) {
// Save UI state changes to the savedInstanceState. // Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is // This bundle will be passed to onCreate if the process is
@ -269,8 +300,16 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
// Restore UI state from the savedInstanceState. // Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate. // This bundle has also been passed to onCreate.
//we do nothing here
} }
/**
* confirm with the user that they want to open a browser to connect to https://check.torproject.org
and then launch the URL.
this may be where the TorCheck API code/UI is added, though always offering the web-based confirm
should be an option, since users know it
**/
private void doTorCheck () private void doTorCheck ()
{ {
@ -301,6 +340,14 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
/**
* this adds a port to the list of hidden service ports
* we might want to add remove/disable port too
* this is used by external apps that launch an intent
* to request a hidden service on a specific port
* currently, we haven't promoted this intent API or capability
* that much, but we hope to
**/
private void enableHiddenServicePort (int hsPort) private void enableHiddenServicePort (int hsPort)
{ {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
@ -332,10 +379,21 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
//this is where we make sure we have a handle to ITorService
bindService(); bindService();
//this is a hack which basically pings the ITorService to update our status for the UI
// - the dialogbox/progressbar ?
// right, this was for when the label displayed the status, and not the progress, so it may
// not make as much sense now; there is a bunch of loose ends like this that should be
// cleaned up with the transition to the progressdialog - ok
updateStatus(""); updateStatus("");
//this checks if we were launched via an Intent call from another app or activity
//- how does this matter? if Orbot has been launched via an Intent or not ?
//we want to know if this is a launch by the user from the home screen, or via back, or some
// standard interaction, or if it is another app launching Orbot for a programmatic/API request
// this is how we can add more functionality into ORlib, for instance via Intent launching - hmm ok
if (getIntent() == null) if (getIntent() == null)
return; return;
@ -344,9 +402,11 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
if (action == null) if (action == null)
return; return;
//this relates to the previously discussed hidden port capability
if (action.equals("org.torproject.android.REQUEST_HS_PORT")) if (action.equals("org.torproject.android.REQUEST_HS_PORT"))
{ {
//tell the user an app is trying to open a hidden port and ask for permission
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
@ -379,7 +439,7 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
else if (action.equals("org.torproject.android.START_TOR")) else if (action.equals("org.torproject.android.START_TOR")) //this is the intent used to start Tor from another app, again meant for ORlib functionality
{ {
autoStartOnBind = true; autoStartOnBind = true;
@ -389,8 +449,8 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
else else
{ {
//hmm not sure when this is ever reached honestly ;P
//setTitle(getString(R.string.app_name) + ' ' + getString(R.string.app_version)); //but it looks like a general UI reset
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancelAll(); mNotificationManager.cancelAll();
@ -472,6 +532,9 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
//if we get a response from an activity we launched (like from line 527 where we launch the Settings/Prefs screen)
//and the resultCode matches our arbitrary 1010 value, AND Tor is running
//then update the preferences in an async background task
if (requestCode == 1 && resultCode == 1010 && mService != null) if (requestCode == 1 && resultCode == 1010 && mService != null)
{ {
new ProcessSettingsAsyncTask().execute(mService); new ProcessSettingsAsyncTask().execute(mService);
@ -480,6 +543,10 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
AlertDialog aDialog = null; AlertDialog aDialog = null;
//general alert dialog for mostly Tor warning messages
//sometimes this can go haywire or crazy with too many error
//messages from Tor, and the user cannot stop or exit Orbot
//so need to ensure repeated error messages are not spamming this method
private void showAlert(String title, String msg, boolean button) private void showAlert(String title, String msg, boolean button)
{ {
try try
@ -511,15 +578,17 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
/* /*
* Set the state of the running/not running graphic and label * Set the state of the running/not running graphic and label
* this all needs to be looked at w/ the shift to progressDialog
*/ */
public void updateStatus (String torServiceMsg) public void updateStatus (String torServiceMsg)
{ {
try try
{ {
//if the serivce is bound, query it for the curren status value (int)
if (mService != null) if (mService != null)
torStatus = mService.getStatus(); torStatus = mService.getStatus();
//now update the layout_main UI based on the status
if (imgStatus != null) if (imgStatus != null)
{ {
@ -605,16 +674,25 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
// guess what? this start's Tor! actually no it just requests via the local ITorService to the remote TorService instance
// to start Tor
private void startTor () throws RemoteException private void startTor () throws RemoteException
{ {
// here we bind AGAIN - at some point i think we had to bind multiple times just in case
// but i would love to clarify, clean this up
bindService(); bindService();
// this is a bit of a strange/old/borrowed code/design i used to change the service state
// not sure it really makes sense when what we want to say is just "startTor"
mService.setProfile(TorServiceConstants.PROFILE_ON); //this means turn on mService.setProfile(TorServiceConstants.PROFILE_ON); //this means turn on
//here we update the UI which is a bit sloppy and mixed up code wise
//might be best to just call updateStatus() instead of directly manipulating UI in this method - yep makes sense
imgStatus.setImageResource(R.drawable.torstarting); imgStatus.setImageResource(R.drawable.torstarting);
lblStatus.setText(getString(R.string.status_starting_up)); lblStatus.setText(getString(R.string.status_starting_up));
//we send a message here to the progressDialog i believe, but we can clarify that shortly
Message msg = mHandler.obtainMessage(TorServiceConstants.ENABLE_TOR_MSG); Message msg = mHandler.obtainMessage(TorServiceConstants.ENABLE_TOR_MSG);
mHandler.sendMessage(msg); mHandler.sendMessage(msg);
@ -622,11 +700,15 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
//now we stop Tor! amazing!
private void stopTor () throws RemoteException private void stopTor () throws RemoteException
{ {
//if the service is bound, then turn it off, using the same "PROFILE_" technique
if (mService != null) if (mService != null)
{ {
mService.setProfile(TorServiceConstants.PROFILE_OFF); mService.setProfile(TorServiceConstants.PROFILE_OFF);
//again this is related to the progress dialog or some other threaded UI object
Message msg = mHandler.obtainMessage(TorServiceConstants.DISABLE_TOR_MSG); Message msg = mHandler.obtainMessage(TorServiceConstants.DISABLE_TOR_MSG);
mHandler.sendMessage(msg); mHandler.sendMessage(msg);
} }
@ -675,6 +757,10 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
/** /**
* This implementation is used to receive callbacks from the remote * This implementation is used to receive callbacks from the remote
* service. * service.
*
* If we have this setup probably, we shouldn't have to poll or query status
* to the service, as it should send it as it changes or when we bind/unbind to it
* from this activity
*/ */
private ITorServiceCallback mCallback = new ITorServiceCallback.Stub() { private ITorServiceCallback mCallback = new ITorServiceCallback.Stub() {
/** /**
@ -684,14 +770,17 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
* NOT be running in our main thread like most other things -- so, * NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there. * to update the UI, we need to use a Handler to hop over there.
*/ */
//receive a new string vaule end-user displayable message from the ITorService
public void statusChanged(String value) { public void statusChanged(String value) {
//pass it off to the progressDialog
Message msg = mHandler.obtainMessage(TorServiceConstants.STATUS_MSG); Message msg = mHandler.obtainMessage(TorServiceConstants.STATUS_MSG);
msg.getData().putString(HANDLER_TOR_MSG, value); msg.getData().putString(HANDLER_TOR_MSG, value);
mHandler.sendMessage(msg); mHandler.sendMessage(msg);
} }
@Override @Override //this was when we displayed the log in the main Activity; can prob take this out now
public void logMessage(String value) throws RemoteException { public void logMessage(String value) throws RemoteException {
Message msg = mHandler.obtainMessage(TorServiceConstants.LOG_MSG); Message msg = mHandler.obtainMessage(TorServiceConstants.LOG_MSG);
@ -702,6 +791,8 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
}; };
// this is what takes messages or values from the callback threads or other non-mainUI threads
//and passes them back into the main UI thread for display to the user
private Handler mHandler = new Handler() { private Handler mHandler = new Handler() {
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
@ -741,6 +832,10 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
/** /**
* Class for interacting with the main interface of the service. * Class for interacting with the main interface of the service.
*/ */
// this is the connection that gets called back when a successfull bind occurs
// we should use this to activity monitor unbind so that we don't have to call
// bindService() a million times
private ServiceConnection mConnection = new ServiceConnection() { private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, public void onServiceConnected(ComponentName className,
IBinder service) { IBinder service) {
@ -756,6 +851,7 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
try { try {
mService.registerCallback(mCallback); mService.registerCallback(mCallback);
//again with the update status?!? :P
updateStatus(""); updateStatus("");
if (autoStartOnBind) if (autoStartOnBind)
@ -789,10 +885,15 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
}; };
//should move this up with all the other class variables
boolean mIsBound = false; boolean mIsBound = false;
//this is where we bind!
private void bindService () private void bindService ()
{ {
//since its auto create, we prob don't ever need to call startService
//also we should again be consistent with using either iTorService.class.getName()
//or the variable constant
bindService(new Intent(ITorService.class.getName()), bindService(new Intent(ITorService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE); mConnection, Context.BIND_AUTO_CREATE);
@ -800,6 +901,7 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
//unbind removes the callback, and unbinds the service
private void unbindService () private void unbindService ()
{ {
if (mIsBound) { if (mIsBound) {
@ -815,6 +917,9 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
} }
} }
//maybe needs this?
mService = null;
// Detach our existing connection. // Detach our existing connection.
unbindService(mConnection); unbindService(mConnection);
mIsBound = false; mIsBound = false;

View File

@ -1,37 +0,0 @@
package org.torproject.android.boot;
import org.torproject.android.service.ITorService;
import org.torproject.android.service.TorService;
import org.torproject.android.service.TorServiceConstants;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
public class OnbootBroadcastReceiver extends BroadcastReceiver implements TorServiceConstants {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "received on boot notification");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean startOnBoot = prefs.getBoolean("pref_start_boot",true);
Log.d(TAG, "startOnBoot:" + startOnBoot);
if (startOnBoot)
{
Intent serviceIntent = new Intent(context,TorService.class);
serviceIntent.setAction("onboot");
context.startService(serviceIntent);
}
//bindService(new Intent(ITorService.class.getName()),
// mConnection, Context.BIND_AUTO_CREATE);
}
}

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */ /* Copyright (c) 2009-2011, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */ /* See LICENSE for licensing information */
package org.torproject.android.service; package org.torproject.android.service;
@ -545,6 +545,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
boolean enableTransparentProxy = prefs.getBoolean("pref_transparent", false); boolean enableTransparentProxy = prefs.getBoolean("pref_transparent", false);
boolean transProxyAll = prefs.getBoolean("pref_transparent_all", false); boolean transProxyAll = prefs.getBoolean("pref_transparent_all", false);
boolean transProxyPortFallback = prefs.getBoolean("pref_transparent_port_fallback", false); boolean transProxyPortFallback = prefs.getBoolean("pref_transparent_port_fallback", false);
boolean transProxyTethering = prefs.getBoolean("pref_transparent_tethering", false);
TorService.logMessage ("Transparent Proxying: " + enableTransparentProxy); TorService.logMessage ("Transparent Proxying: " + enableTransparentProxy);
@ -581,6 +582,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
showAlert("Status", "Setting up app-based transparent proxying..."); showAlert("Status", "Setting up app-based transparent proxying...");
code = TorTransProxy.setTransparentProxyingByApp(this,AppManager.getApps(this)); code = TorTransProxy.setTransparentProxyingByApp(this,AppManager.getApps(this));
} }
} }
TorService.logMessage ("TorTransProxy resp code: " + code); TorService.logMessage ("TorTransProxy resp code: " + code);
@ -588,12 +590,23 @@ public class TorService extends Service implements TorServiceConstants, Runnable
if (code == 0) if (code == 0)
{ {
showAlert("Status", "Transparent proxying ENABLED"); showAlert("Status", "Transparent proxying ENABLED");
if (transProxyTethering)
{
showAlert("Status", "TransProxy enabled for Tethering!");
TorTransProxy.enableTetheringRules(this);
}
} }
else else
{ {
showAlert("Status", "WARNING: error starting transparent proxying!"); showAlert("Status", "WARNING: error starting transparent proxying!");
} }
return true; return true;
} }

View File

@ -313,14 +313,12 @@ public class TorTransProxy implements TorServiceConstants {
return code; return code;
} }
public static int enableWifiHotspotRules (Context context) throws Exception public static int enableTetheringRules (Context context) throws Exception
{ {
boolean runRoot = true; boolean runRoot = true;
boolean waitFor = true; boolean waitFor = true;
//redirectDNSResolvConf(); //not working yet
String ipTablesPath = new File(context.getDir("bin", 0),"iptables").getAbsolutePath(); String ipTablesPath = new File(context.getDir("bin", 0),"iptables").getAbsolutePath();
StringBuilder script = new StringBuilder(); StringBuilder script = new StringBuilder();
@ -328,25 +326,24 @@ public class TorTransProxy implements TorServiceConstants {
StringBuilder res = new StringBuilder(); StringBuilder res = new StringBuilder();
int code = -1; int code = -1;
String[] hwinterfaces = {"usb0","wl0.1"};
for (int i = 0; i < hwinterfaces.length; i++)
{
script.append(ipTablesPath); script.append(ipTablesPath);
script.append(" -I FORWARD"); script.append(" -t nat -A PREROUTING -i ");
script.append(" -m state --state ESTABLISHED,RELATED -j ACCEPT"); script.append(hwinterfaces[i]);
script.append(" -p udp --dport 53 -j REDIRECT --to-ports ");
script.append(TOR_DNS_PORT);
script.append(" || exit\n"); script.append(" || exit\n");
script.append(ipTablesPath); script.append(ipTablesPath);
script.append(" -I FORWARD"); script.append(" -t nat -A PREROUTING -i ");
script.append(" -j ACCEPT"); script.append(hwinterfaces[i]);
script.append(" || exit\n"); script.append(" -p tcp -j REDIRECT --to-ports ");
script.append(TOR_TRANSPROXY_PORT);
/*
script.append(ipTablesPath);
script.append(" -P FORWARD DROP");
script.append(" || exit\n");
*/
script.append(ipTablesPath);
script.append(" -t nat -I POSTROUTING -j MASQUERADE");
script.append(" || exit\n"); script.append(" || exit\n");
}
String[] cmdAdd = {script.toString()}; String[] cmdAdd = {script.toString()};
@ -374,8 +371,6 @@ public class TorTransProxy implements TorServiceConstants {
purgeIptables(context); purgeIptables(context);
enableWifiHotspotRules(context);
int torUid = context.getApplicationInfo().uid; int torUid = context.getApplicationInfo().uid;
// Set up port redirection // Set up port redirection