modified control port usage, updated startup UI screen, implemented all-app torification and more

svn:r22677
This commit is contained in:
Nathan Freitas 2010-07-23 11:10:00 +00:00
parent 7d7a33b983
commit 183a58f39f
17 changed files with 548 additions and 364 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.torproject.android"
android:versionName="0.2.2.13-orbot-alpha-0.0.8" android:versionCode="2">
android:versionName="0.2.2.13-orbot-alpha-0.0.8" android:versionCode="8">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

View File

@ -10,5 +10,5 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-8
target=android-3
apk-configurations=

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -4,11 +4,15 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/itemcheck"></CheckBox>
<ImageView android:id="@+id/itemicon" android:layout_width="48px" android:layout_height="48px"></ImageView>
<TextView android:layout_height="wrap_content" android:id="@+id/itemtext" android:text="uid:packages" android:layout_width="wrap_content"
android:textSize="18sp"></TextView>
<TextView android:layout_height="wrap_content" android:id="@+id/itemtext" android:text="uid:packages"
android:layout_width="220px" android:textSize="18sp"></TextView>
<CheckBox android:layout_width="48px" android:layout_height="48px" android:id="@+id/itemcheck"></CheckBox>
</LinearLayout>

View File

@ -1,17 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/bgtitanium">
<RelativeLayout android:id="@+id/layoutHeaderMain"
android:layout_width="fill_parent"
android:layout_height="30dp"
android:layout_gravity="center_horizontal"
android:background="#A0909090">
<ImageView android:id="@+id/radioModeImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0px"
android:layout_marginRight="3px"
android:gravity="right"
android:layout_toRightOf="@+id/radioModeLabel"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:src="@drawable/tor25"
/>
<TextView android:id="@+id/radioModeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/powered_by"
android:layout_marginTop="9px"
android:layout_marginRight="0px"
android:gravity="right"
android:layout_toLeftOf="@+id/radioModeImage"
android:textColor="#333333" />
</RelativeLayout>
<ScrollView android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent" android:id="@+id/logScrollView"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:id="@+id/messageLog"
android:layout_height="fill_parent"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_x="2px"
android:layout_y="2px"
android:textSize="12px"
android:background="#A0222222"
/>
</ScrollView>
</LinearLayout>

View File

@ -15,7 +15,7 @@
<string name="status_shutting_down">Orbot is shutting down</string>
<string name="tor_process_connecting">Starting Tor...</string>
<string name="tor_process_connecting_step2">authenticating control connection...</string>
<string name="tor_process_connecting_step2">Setting up control...</string>
<string name="tor_process_connecting_step3">complete.</string>
<string name="tor_process_connecting_step4">waiting.</string>
@ -37,7 +37,7 @@
<string name="help_text_1">Orbot requires different configuration depending on the Android operating system version it is used on.
Please visit https://www.torproject.org/docs/android.html for the latest information.</string>
<string name="help_text_2">For non-rooted Android 1.x devices (G1, MyTouch3G, Hero): Please use the "ProxySurf" browser available in the Android Market, and set
<string name="help_text_2">For non-rooted Android 1.x devices: Please use the "ProxySurf" browser available in the Android Market, and set
the HTTP Proxy to 127.0.0.1 and port 8118, for HTTP traffic only (HTTP/S will not work).</string>
<string name="help_text_3">For Instant Messaging, try "Beem" in the market, and set the SOCKS5 proxy to 127.0.0.1 / port 9050.</string>
@ -52,4 +52,16 @@ and all DNS requests. This includes the built-in Browser, Gmail, YouTube and Map
<string name="menu_exit">Exit</string>
<string name="powered_by">powered by the Tor Project</string>
<string name="press_to_start">- press to start -</string>
<string name="pref_trans_proxy_group">Transparent Proxying (Requires Root)</string>
<string name="pref_trans_proxy_title">Transparent Proxying</string>
<string name="pref_trans_proxy_summary">Automatic Torifying of Apps</string>
<string name="pref_transparent_all_title">Tor Everything</string>
<string name="pref_transparent_all_summary">Send traffic for all apps through Tor</string>
<string name="status_install_success">Tor binaries successfully installed!</string>
<string name="status_install_fail">The Tor binary files were unable to be installed. Please check the log and notify tor-assistants@torproject.org</string>
<string name="title_error">Application Error</string>
</resources>

View File

@ -1,20 +1,20 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Transparent Proxying (Requires Root)">
<PreferenceCategory android:title="@string/pref_trans_proxy_group">
<CheckBoxPreference
android:defaultValue="false"
android:key="pref_transparent"
android:title="Transparent Proxying"
android:summary="Auto-Tor Proxying"
android:title="@string/pref_trans_proxy_title"
android:summary="@string/pref_trans_proxy_summary"
android:enabled="true"/>
<CheckBoxPreference
android:defaultValue="false"
android:key="pref_transparent_all"
android:title="Auto-Proxy All Apps"
android:summary="Route All Traffic Through Tor"
android:enabled="true"/>
android:summary="@string/pref_transparent_all_summary"
android:enabled="true"
android:title="@string/pref_transparent_all_title"/>
<Preference
android:defaultValue=""

View File

@ -3,15 +3,11 @@
package org.torproject.android;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
import org.torproject.android.service.ITorService;
import org.torproject.android.service.ITorServiceCallback;
import org.torproject.android.service.TorServiceUtils;
import org.torproject.android.service.TorTransProxy;
import org.torproject.android.service.TorServiceConstants;
import android.app.Activity;
import android.app.AlertDialog;
@ -36,28 +32,21 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;
public class Orbot extends Activity implements OnClickListener, TorConstants, OnCheckedChangeListener
public class Orbot extends Activity implements OnClickListener, TorConstants
{
/* Useful UI bits */
private TextView txtMessageLog = null; //the full screen log view of Tor control messages
private TextView lblStatus = null; //the main text display widget
private ImageView imgStatus = null; //the main touchable image for activating Orbot
private ProgressDialog progressDialog;
private ListView listApps;
private boolean showingSettings = false;
// private ProgressDialog progressDialog;
private MenuItem mItemOnOff = null;
/* Some tracking bits */
@ -96,7 +85,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
mItem.setIcon(R.drawable.ic_menu_register);
mItem = menu.add(0, 7, Menu.NONE, getString(R.string.menu_verify));
mItem.setIcon(R.drawable.ic_menu_goto);
mItem.setIcon(R.drawable.ic_menu_check);
mItem = menu.add(0,6, Menu.NONE, getString(R.string.menu_log));
mItem.setIcon(R.drawable.ic_menu_reports);
@ -163,10 +152,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
{
showHelp();
}
else if (item.getItemId() == 5)
{
showApps();
}
else if (item.getItemId() == 7)
{
//launch check.torproject.org
@ -257,22 +242,22 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
protected void onResume() {
super.onResume();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancelAll();
updateStatus (""); //update the status, which checks the service status
if (showingSettings)
if (mService != null)
{
showingSettings = false;
processSettings();
try {
processSettings();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/* (non-Javadoc)
@ -285,8 +270,10 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
startService(new Intent(INTENT_TOR_SERVICE));
bindService ();
updateStatus ("");
//updateStatus ("");
hasRoot = TorTransProxy.hasRootAccess();
}
@ -298,8 +285,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
protected void onStop() {
super.onStop();
TorServiceUtils.saveAppSettings(this);
unbindService();
}
@ -312,23 +297,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
{
bindService(); //connect the UI activity to the remote service
if (currentView == R.layout.layout_apps)
{
if (hasRoot)
{
TorServiceUtils.saveAppSettings(this);
if (enableTransparentProxy)
{
TorTransProxy.purgeNatIptables();
TorTransProxy.setDNSProxying();
TorTransProxy.setTransparentProxying(this, TorServiceUtils.getApps(this));
}
}
}
currentView = R.layout.layout_main;
setContentView(currentView);
@ -352,61 +320,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
}
private void loadApps ()
{
final TorifiedApp[] apps = TorServiceUtils.getApps(this);
Arrays.sort(apps, new Comparator<TorifiedApp>() {
public int compare(TorifiedApp o1, TorifiedApp o2) {
if (o1.isTorified() == o2.isTorified()) return o1.getName().compareTo(o2.getName());
if (o1.isTorified()) return -1;
return 1;
}
});
final LayoutInflater inflater = getLayoutInflater();
final ListAdapter adapter = new ArrayAdapter<TorifiedApp>(this,R.layout.layout_apps_item,R.id.itemtext,apps) {
public View getView(int position, View convertView, ViewGroup parent) {
ListEntry entry;
if (convertView == null) {
// Inflate a new view
convertView = inflater.inflate(R.layout.layout_apps_item, parent, false);
entry = new ListEntry();
entry.box = (CheckBox) convertView.findViewById(R.id.itemcheck);
entry.text = (TextView) convertView.findViewById(R.id.itemtext);
convertView.setTag(entry);
entry.box.setOnCheckedChangeListener(Orbot.this);
} else {
// Convert an existing view
entry = (ListEntry) convertView.getTag();
}
final TorifiedApp app = apps[position];
entry.text.setText(app.getName());
final CheckBox box = entry.box;
box.setTag(app);
box.setChecked(app.isTorified());
return convertView;
}
};
this.listApps.setAdapter(adapter);
}
/**
* Called an application is check/unchecked
*/
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
final TorifiedApp app = (TorifiedApp) buttonView.getTag();
if (app != null) {
app.setTorified(isChecked);
}
TorServiceUtils.saveAppSettings(this);
}
private static class ListEntry {
private CheckBox box;
@ -424,7 +337,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
versionName.setText(R.string.app_version);
new AlertDialog.Builder(this)
.setTitle(getString(R.string.menu_info))
.setTitle(getString(R.string.button_about))
.setView(view)
.setNeutralButton(getString(R.string.button_help), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
@ -451,6 +364,25 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
StringBuilder msg = new StringBuilder();
msg.append(getString(R.string.help_text_1));
msg.append("\n\n");
if (hasRoot)
{
msg.append("Your device is ROOTED. Please enable the 'Transparent Proxying' setting to select which apps to send through Tor.");
}
else
{
msg.append("Your device is NOT rooted.\n");
msg.append(getString(R.string.help_text_5));
msg.append("\n\n");
msg.append(getString(R.string.not_anonymous_yet));
}
/*
msg.append(getString(R.string.help_text_2));
msg.append("\n\n");
msg.append(getString(R.string.help_text_3));
@ -459,6 +391,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
msg.append("\n\n");
msg.append(getString(R.string.help_text_5));
msg.append("\n\n");
*/
new AlertDialog.Builder(this)
@ -478,16 +411,14 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
.show();
}
private void showApps ()
private void showHelpWizard ()
{
currentView = R.layout.layout_apps;
setContentView(currentView);
listApps = (ListView)findViewById(R.id.applistview);
loadApps();
//sshowAlert("Configure",getString(R.string.not_anonymous_yet));
}
/*
* Show the message log UI
*/
@ -510,7 +441,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
private void showSettings ()
{
showingSettings = true;
startActivity(new Intent(this, SettingsPreferences.class));
@ -520,7 +451,8 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
* Read in the Preferences and write then to the .torrc file
*/
private void processSettings ()
/*
private void processSettingsOld ()
{
StringBuffer torrcText = new StringBuffer();
@ -566,6 +498,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
return;
}
torrcText.append("UseBridges 1");
torrcText.append('\n');
@ -645,6 +578,111 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
Utils.saveTextFile(TorServiceConstants.TORRC_INSTALL_PATH, torrcText.toString());
}
*/
private void processSettings () throws RemoteException
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean useBridges = prefs.getBoolean(PREF_BRIDGES_ENABLED, false);
boolean autoUpdateBridges = prefs.getBoolean(PREF_BRIDGES_UPDATED, false);
boolean becomeRelay = prefs.getBoolean(PREF_OR, false);
boolean ReachableAddresses = prefs.getBoolean(PREF_REACHABLE_ADDRESSES,false);
boolean enableTransparentProxy = prefs.getBoolean(PREF_TRANSPARENT, false);
String bridgeList = prefs.getString(PREF_BRIDGES_LIST,"");
if (useBridges)
{
if (bridgeList == null || bridgeList.length() == 0)
{
showAlert("Bridge Error","In order to use the bridge feature, you must enter at least one bridge IP address." +
"Send an email to bridges@torproject.org with the line \"get bridges\" by itself in the body of the mail from a gmail account.");
return;
}
mService.updateConfiguration("UseBridges", "1", false);
if (autoUpdateBridges)
{
mService.updateConfiguration("UpdateBridgesFromAuthority", "1", false);
}
else
{
mService.updateConfiguration("UpdateBridgesFromAuthority", "0", false);
}
String bridgeDelim = "\n";
if (bridgeList.indexOf(",") != -1)
{
bridgeDelim = ",";
}
StringTokenizer st = new StringTokenizer(bridgeList,bridgeDelim);
while (st.hasMoreTokens())
{
mService.updateConfiguration("bridge", st.nextToken(), false);
}
}
else
{
mService.updateConfiguration("UseBridges", "0", false);
}
try
{
if (ReachableAddresses)
{
String ReachableAddressesPorts =
prefs.getString(PREF_REACHABLE_ADDRESSES_PORTS, "*:80,*:443");
mService.updateConfiguration("ReachableAddresses", ReachableAddressesPorts, false);
}
}
catch (Exception e)
{
showAlert("Config Error","Your ReachableAddresses settings caused an exception!");
}
try
{
if (becomeRelay && (!useBridges) && (!ReachableAddresses))
{
int ORPort = Integer.parseInt(prefs.getString(PREF_OR_PORT, "9001"));
String nickname = prefs.getString(PREF_OR_NICKNAME, "Orbot");
mService.updateConfiguration("ORPort", ORPort + "", false);
mService.updateConfiguration("Nickname", nickname, false);
mService.updateConfiguration("ExitPolicy", "reject *:*", false);
}
}
catch (Exception e)
{
showAlert("Uh-oh!","Your relay settings caused an exception!");
return;
}
mService.saveConfiguration();
}
private void showAlert(String title, String msg)
{
@ -679,9 +717,14 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
if (torStatus == STATUS_ON)
{
imgStatus.setImageResource(R.drawable.toron);
imgStatus.clearAnimation();
lblStatus.setText(getString(R.string.status_activated));
showHelpWizard ();
/*
if (progressDialog != null)
{
@ -689,23 +732,28 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
progressDialog.hide();
progressDialog = null;
if (!enableTransparentProxy)
{
showAlert("Configure",getString(R.string.not_anonymous_yet));
}
}
// if (torServiceMsg != null && torServiceMsg.length()>0)
// Toast.makeText(this, torServiceMsg, Toast.LENGTH_LONG).show();
}*/
}
else if (torStatus == STATUS_CONNECTING)
{
imgStatus.setImageResource(R.drawable.torstarting);
lblStatus.setText(getString(R.string.status_starting_up));
/*
if (imgStatus.getAnimation()==null)
{
imgStatus.setAnimation(AnimationUtils.loadAnimation(this, R.anim.starting));
imgStatus.getAnimation().setRepeatMode(Animation.INFINITE);
imgStatus.getAnimation().setRepeatCount(Animation.INFINITE);
}*/
/*
if (progressDialog == null)
{
progressDialog = new ProgressDialog(this);
@ -719,14 +767,18 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
}
progressDialog.setMessage(torServiceMsg);
*/
lblStatus.setText(torServiceMsg);
int idx = torServiceMsg.indexOf("%");
if (idx != -1)
{
String pComp = torServiceMsg.substring(idx-2,idx).trim();
int ipComp = Integer.parseInt(pComp);
progressDialog.setProgress(ipComp);
// progressDialog.setProgress(ipComp);
}
@ -734,21 +786,16 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
else if (torStatus == STATUS_OFF)
{
imgStatus.setImageResource(R.drawable.torstopping);
imgStatus.clearAnimation();
lblStatus.setText(getString(R.string.status_shutting_down));
//if (torServiceMsg != null && torServiceMsg.length()>0)
//Toast.makeText(this, torServiceMsg, Toast.LENGTH_LONG).show();
}
else
{
//if (torServiceMsg != null && torServiceMsg.length()>0)
//Toast.makeText(this, torServiceMsg, Toast.LENGTH_LONG).show();
/*
if (progressDialog != null)
{
@ -756,7 +803,8 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
progressDialog.hide();
progressDialog = null;
}
*/
imgStatus.clearAnimation();
imgStatus.setImageResource(R.drawable.toroff);
lblStatus.setText(getString(R.string.status_disabled));
@ -837,31 +885,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
}
private void doTorSetup (boolean enabled)
{
if (enabled)
{
processSettings();
if (hasRoot && enableTransparentProxy)
{
TorTransProxy.setDNSProxying();
TorTransProxy.setTransparentProxying(this,TorServiceUtils.getApps(this));
}
}
else
{
if (hasRoot && enableTransparentProxy)
{
TorTransProxy.purgeNatIptables();
//TorRoot.setDNSProxying(false);
//TorRoot.setTransparentProxying(this,false);
}
}
}
/**
* This implementation is used to receive callbacks from the remote
* service.
@ -876,21 +900,30 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
*/
public void statusChanged(String value) {
Message msg = mHandler.obtainMessage(BUMP_MSG);
Message msg = mHandler.obtainMessage(STATUS_MSG);
msg.getData().putString(HANDLER_TOR_MSG, value);
mHandler.sendMessage(msg);
}
@Override
public void logMessage(String value) throws RemoteException {
Message msg = mHandler.obtainMessage(LOG_MSG);
msg.getData().putString(HANDLER_TOR_MSG, value);
mHandler.sendMessage(msg);
}
};
private static final int BUMP_MSG = 1;
private static final int STATUS_MSG = 1;
private static final int ENABLE_TOR_MSG = 2;
private static final int DISABLE_TOR_MSG = 3;
private static final int LOG_MSG = 4;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
case STATUS_MSG:
String torServiceMsg = (String)msg.getData().getString(HANDLER_TOR_MSG);
@ -899,16 +932,24 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
if (torServiceMsg.length() > 0 && torServiceMsg.charAt(0)!='>')
updateStatus(torServiceMsg);
updateStatus(torServiceMsg);
break;
case LOG_MSG:
String torLogMsg = (String)msg.getData().getString(HANDLER_TOR_MSG);
logBuffer.append(torLogMsg);
logBuffer.append('\n');
break;
case ENABLE_TOR_MSG:
doTorSetup(true);
break;
case DISABLE_TOR_MSG:
doTorSetup(false);
break;
default:
@ -958,7 +999,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
boolean mIsBound = false;
boolean hasRoot = false;
boolean enableTransparentProxy = false;
private void bindService ()
{
@ -967,8 +1007,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants, On
mIsBound = true;
hasRoot = TorTransProxy.hasRootAccess();
}

View File

@ -21,12 +21,25 @@ public class SettingsPreferences
private CheckBoxPreference prefcBTransProxyAll = null;
private Preference prefTransProxyApps = null;
protected void onCreate(Bundle savedInstanceState) {
private boolean hasRoot = false;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
hasRoot = TorServiceUtils.hasRoot();
if (!TorServiceUtils.hasRoot())
}
@Override
protected void onResume() {
super.onResume();
if (!hasRoot)
{
getPreferenceScreen().getPreference(0).setEnabled(false);
}

View File

@ -6,6 +6,10 @@ package org.torproject.android;
public interface TorConstants {
public final static String TAG = "Orbot";
public final static String PREFS_KEY = "OrbotPrefs";
public final static String PREFS_KEY_TORIFIED = "PrefTord";
public final static int FILE_WRITE_BUFFER_SIZE = 2048;
@ -23,10 +27,12 @@ public interface TorConstants {
public final static String NEWLINE = "\n";
/*
public final static String TORRC_DEFAULT =
"SocksPort 9050\nSocksListenAddress 127.0.0.1\nSafeSocks 1\nDNSPort 5400\nLog debug syslog\nDataDirectory /data/data/org.torproject.android/cache\n"
+ "ControlPort 9051\nCookieAuthentication 1\nRelayBandwidthRate 20 KBytes\nRelayBandwidthBurst 20 KBytes\nAutomapHostsOnResolve 1\nTransPort 9040\n";
*/
public final static String INTENT_TOR_SERVICE = "org.torproject.android.service.TOR_SERVICE";
public final static String HANDLER_TOR_MSG = "torServiceMsg";

View File

@ -6,19 +6,40 @@ import org.torproject.android.service.ITorServiceCallback;
* an interface for calling on to a remote service
*/
interface ITorService {
/**
* Often you want to allow a service to call back to its clients.
* This shows how to do so, by registering a callback interface with
* the service.
* This allows Tor service to send messages back to the GUI
*/
void registerCallback(ITorServiceCallback cb);
/**
* Remove a previously registered callback interface.
* Remove registered callback interface.
*/
void unregisterCallback(ITorServiceCallback cb);
/**
* Get a simple int status value for the state of Tor
**/
int getStatus();
/**
* The profile value is the start/stop state for Tor
**/
void setProfile(int profile);
/**
* Set configuration
**/
boolean updateConfiguration (String name, String value, boolean saveToDisk);
/**
* Set configuration
**/
boolean saveConfiguration ();
/**
* Get current configuration value from torrc
*/
String getConfiguration (String name);
}

View File

@ -7,7 +7,13 @@ package org.torproject.android.service;
*/
oneway interface ITorServiceCallback {
/**
* Called when the service has a new value for you.
* Called when the service has a something to display to the user
*/
void statusChanged(String value);
/**
* Called when the service has something to add to the log
*/
void logMessage(String value);
}

View File

@ -15,25 +15,28 @@ import net.freehaven.tor.control.ConfigEntry;
import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.TorControlConnection;
import org.torproject.android.AppManager;
import org.torproject.android.Orbot;
import org.torproject.android.R;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.util.Log;
public class TorService extends Service implements TorServiceConstants, Runnable, EventHandler
{
private static int currentStatus = STATUS_READY;
private TorControlConnection conn = null;
@ -44,7 +47,10 @@ public class TorService extends Service implements TorServiceConstants, Runnable
private static final int MAX_START_TRIES = 3;
private ArrayList<String> configBuffer = null;
private boolean hasRoot = false;
/** Called when the activity is first created. */
public void onCreate() {
super.onCreate();
@ -57,6 +63,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
_torInstance = this;
hasRoot = TorServiceUtils.hasRoot();
}
@ -130,19 +137,19 @@ public class TorService extends Service implements TorServiceConstants, Runnable
}
private void showToolbarNotification (String title, String notifyMsg, int icon)
private void showToolbarNotification (String notifyMsg, int icon)
{
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence tickerText = title;
CharSequence tickerText = notifyMsg;
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
Context context = getApplicationContext();
CharSequence contentTitle = title;
CharSequence contentTitle = getString(R.string.app_name);
CharSequence contentText = notifyMsg;
Intent notificationIntent = new Intent(this, Orbot.class);
@ -189,7 +196,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
catch (Exception e)
{
currentStatus = STATUS_OFF;
this.showToolbarNotification("Orbot", "Unable to start Tor", R.drawable.tornotification);
this.showToolbarNotification(getString(R.string.status_disabled), R.drawable.tornotification);
Log.i(TAG,"Unable to start Tor: " + e.getMessage(),e);
}
}
@ -220,9 +227,11 @@ public class TorService extends Service implements TorServiceConstants, Runnable
currentStatus = STATUS_READY;
showToolbarNotification ("Orbot","Tor is disabled",R.drawable.tornotificationoff);
sendCallbackMessage("Tor is disabled");
showToolbarNotification (getString(R.string.status_disabled),R.drawable.tornotificationoff);
sendCallbackMessage(getString(R.string.status_disabled));
setupTransProxy(false);
}
@ -254,7 +263,8 @@ public class TorService extends Service implements TorServiceConstants, Runnable
if (conn != null)
{
try {
Log.i(TAG,"sending SHUTDOWN signal");
logNotice("sending SHUTDOWN signal to Tor process");
// conn.shutdownTor(arg0)
conn.signal("SHUTDOWN");
} catch (Exception e) {
Log.i(TAG,"error shutting down Tor via connection",e);
@ -269,7 +279,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
while (procId != -1)
{
Log.i(TAG,"Found Tor PID=" + procId + " - killing now...");
logNotice("Found Tor PID=" + procId + " - killing now...");
String[] cmd = { SHELL_CMD_KILL + ' ' + procId + "" };
TorServiceUtils.doShellCommand(cmd,log, false, false);
@ -290,22 +300,6 @@ public class TorService extends Service implements TorServiceConstants, Runnable
procId = TorServiceUtils.findProcessId(TorServiceConstants.PRIVOXY_INSTALL_PATH);
}
/*
//removing this for now
if (_webProxy != null)
{
try
{
//shutdown web proxy
_webProxy.stop();
_webProxy = null;
}
catch (Exception e)
{
Log.i(TAG,"error stopping web proxy",e);
}
}*/
}
private void logNotice (String msg)
@ -335,17 +329,17 @@ public class TorService extends Service implements TorServiceConstants, Runnable
if (torBinaryExists && privoxyBinaryExists)
{
logNotice("Tor, Privoxy, IPtables binaries installed!");
logNotice(getString(R.string.status_install_success));
this.showToolbarNotification("Orbot Installed!", "Tor was successfully extracted and installed", R.drawable.tornotification);
showToolbarNotification(getString(R.string.status_install_success), R.drawable.tornotification);
}
else
{
logNotice("Binary install FAILED!");
this.showToolbarNotification("Orbot FAIL!", "The binaries were unable to be installed", R.drawable.tornotification);
logNotice(getString(R.string.status_install_fail));
showAlert(getString(R.string.title_error),getString(R.string.status_install_fail));
return false;
}
@ -354,11 +348,11 @@ public class TorService extends Service implements TorServiceConstants, Runnable
StringBuilder log = new StringBuilder ();
Log.i(TAG,"Setting permission on Tor binary");
logNotice("Setting permission on Tor binary");
String[] cmd1 = {SHELL_CMD_CHMOD + ' ' + CHMOD_EXE_VALUE + ' ' + TOR_BINARY_INSTALL_PATH};
TorServiceUtils.doShellCommand(cmd1, log, false, true);
Log.i(TAG,"Setting permission on Privoxy binary");
logNotice("Setting permission on Privoxy binary");
String[] cmd2 = {SHELL_CMD_CHMOD + ' ' + CHMOD_EXE_VALUE + ' ' + PRIVOXY_INSTALL_PATH};
TorServiceUtils.doShellCommand(cmd2, log, false, true);
@ -367,17 +361,19 @@ public class TorService extends Service implements TorServiceConstants, Runnable
public void initTor () throws Exception
{
// android.os.Debug.waitForDebugger();
currentStatus = STATUS_CONNECTING;
logNotice("Tor is starting up...");
this.sendCallbackMessage("starting...");
logNotice(getString(R.string.status_starting_up));
sendCallbackMessage(getString(R.string.status_starting_up));
killTorProcess ();
checkTorBinaries ();
new Thread()
{
@ -401,6 +397,8 @@ public class TorService extends Service implements TorServiceConstants, Runnable
{
try {
runTorShellCmd();
setupTransProxy(true);
} catch (Exception e) {
Log.i(TAG,"Unable to start Tor: " + e.getMessage(),e);
@ -440,7 +438,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
{
sendCallbackMessage("Couldn't start Tor process...\n" + log.toString());
Thread.sleep(1000);
sendCallbackMessage("Trying to start Tor again...\n" + log.toString());
sendCallbackMessage(getString(R.string.status_starting_up));
Thread.sleep(3000);
attempts++;
}
@ -457,7 +455,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
logNotice("Tor process id=" + procId);
showToolbarNotification("Orbot starting...", "Tor is running", R.drawable.tornotification);
showToolbarNotification(getString(R.string.status_starting_up), R.drawable.tornotification);
initControlConnection ();
}
@ -489,7 +487,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
if (privoxyProcId == -1)
{
this.sendCallbackMessage("Couldn't start Privoxy process... retrying...\n" + log);
logNotice("Couldn't start Privoxy process... retrying...\n" + log);
Thread.sleep(3000);
attempts++;
}
@ -506,18 +504,16 @@ public class TorService extends Service implements TorServiceConstants, Runnable
}
/*
public String generateHashPassword ()
{
/*
PasswordDigest d = PasswordDigest.generateDigest();
byte[] s = d.getSecret(); // pass this to authenticate
String h = d.getHashedPassword(); // pass this to the Tor on startup.
*/
return null;
}
}*/
public void initControlConnection () throws Exception, RuntimeException
{
@ -534,7 +530,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
conn = TorControlConnection.getConnection(s);
// conn.authenticate(new byte[0]); // See section 3.2
sendCallbackMessage(baseMessage + ' ' + getString(R.string.tor_process_connecting_step2));
sendCallbackMessage(getString(R.string.tor_process_connecting_step2));
Log.i(TAG,"SUCCESS connected to control port");
@ -542,13 +538,20 @@ public class TorService extends Service implements TorServiceConstants, Runnable
byte[] cookie = new byte[(int)fileCookie.length()];
new FileInputStream(new File(TOR_CONTROL_AUTH_COOKIE)).read(cookie);
conn.authenticate(cookie);
Log.i(TAG,"SUCCESS authenticated to control port");
sendCallbackMessage(baseMessage + ' ' + getString(R.string.tor_process_connecting_step3));
sendCallbackMessage(getString(R.string.tor_process_connecting_step2) + getString(R.string.tor_process_connecting_step3));
addEventHandler();
if (configBuffer != null)
{
conn.setConf(configBuffer);
//conn.saveConf();
configBuffer = null;
}
break; //don't need to retry
}
catch (Exception ce)
@ -568,29 +571,6 @@ public class TorService extends Service implements TorServiceConstants, Runnable
}
public void modifyConf () throws IOException
{
// Get one configuration variable.
List<ConfigEntry> options = conn.getConf("contact");
options.size();
// Get a set of configuration variables.
// List options = conn.getConf(Arrays.asList(new String[]{
// "contact", "orport", "socksport"}));
// Change a single configuration variable
conn.setConf("BandwidthRate", "1 MB");
// Change several configuration variables
conn.setConf(Arrays.asList(new String[]{
"HiddenServiceDir /home/tor/service1",
"HiddenServicePort 80",
}));
// Reset some variables to their defaults
conn.resetConf(Arrays.asList(new String[]{
"contact", "socksport"
}));
// Flush the configuration to disk.
conn.saveConf();
}
/*
private void getTorStatus () throws IOException
@ -640,7 +620,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
conn.setEventHandler(this);
conn.setEvents(Arrays.asList(new String[]{
"ORCONN", "CIRC", "NOTICE", "ERR"}));
"ORCONN", "CIRC", "NOTICE", "WARN", "ERR"}));
// conn.setEvents(Arrays.asList(new String[]{
// "DEBUG", "INFO", "NOTICE", "WARN", "ERR"}));
@ -701,7 +681,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
if (msg.indexOf(TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)!=-1)
{
currentStatus = STATUS_ON;
showToolbarNotification ("Orbot","Tor is enabled",R.drawable.tornotification);
showToolbarNotification (getString(R.string.status_activated),R.drawable.tornotification);
}
@ -709,6 +689,15 @@ public class TorService extends Service implements TorServiceConstants, Runnable
}
private void showAlert(String title, String msg)
{
new AlertDialog.Builder(this)
.setTitle(title)
.setMessage(msg)
.setPositiveButton(android.R.string.ok, null)
.show();
}
public void newDescriptors(List<String> orList) {
@ -783,6 +772,8 @@ public class TorService extends Service implements TorServiceConstants, Runnable
}
private Intent launchContext = null;
public IBinder onBind(Intent intent) {
// Select the interface to return. If your service only implements
// a single interface, you can just return it here without checking
@ -807,6 +798,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
* The IRemoteInterface is defined through IDL
*/
private final ITorService.Stub mBinder = new ITorService.Stub() {
public void registerCallback(ITorServiceCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
@ -820,10 +812,103 @@ public class TorService extends Service implements TorServiceConstants, Runnable
public void setProfile (int profile)
{
setTorProfile(profile);
sendCallbackMessage("");
}
public String getConfiguration (String name)
{
try
{
if (conn != null)
{
StringBuffer result = new StringBuffer();
List<ConfigEntry> listCe = conn.getConf(name);
Iterator<ConfigEntry> itCe = listCe.iterator();
ConfigEntry ce = null;
while (itCe.hasNext())
{
ce = itCe.next();
result.append(ce.key);
result.append(' ');
result.append(ce.value);
result.append('\n');
}
return result.toString();
}
}
catch (IOException ioe)
{
Log.e(TAG, "Unable to update Tor configuration", ioe);
logNotice("Unable to update Tor configuration: " + ioe.getMessage());
}
return null;
}
/**
* Set configuration
**/
public boolean updateConfiguration (String name, String value, boolean saveToDisk)
{
try
{
if (conn != null)
{
conn.setConf(name, value);
if (saveToDisk)
{
// Flush the configuration to disk.
//conn.saveConf(); //NF 22/07/10 this is crashing right now
}
return true;
}
else
{
if (configBuffer == null)
configBuffer = new ArrayList<String>();
configBuffer.add(name + ' ' + value);
}
}
catch (IOException ioe)
{
Log.e(TAG, "Unable to update Tor configuration", ioe);
logNotice("Unable to update Tor configuration: " + ioe.getMessage());
}
return false;
}
public boolean saveConfiguration ()
{
try
{
if (conn != null)
{
// Flush the configuration to disk.
//this is doing bad things right now NF 22/07/10
//conn.saveConf();
return true;
}
}
catch (Exception ioe)
{
Log.e(TAG, "Unable to update Tor configuration", ioe);
logNotice("Unable to update Tor configuration: " + ioe.getMessage());
}
return false;
}
};
private ArrayList<String> callbackBuffer = new ArrayList<String>();
@ -864,4 +949,40 @@ public class TorService extends Service implements TorServiceConstants, Runnable
mCallbacks.finishBroadcast();
}
private void setupTransProxy (boolean enabled)
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean enableTransparentProxy = prefs.getBoolean("pref_transparent", false);
boolean transProxyAll = prefs.getBoolean("pref_transparent_all", false);
logNotice ("Transparent Proxying: " + enableTransparentProxy);
if (enabled)
{
if (hasRoot && enableTransparentProxy)
{
TorTransProxy.setDNSProxying();
TorTransProxy.setTransparentProxyingByApp(this,AppManager.getApps(this),transProxyAll);
}
else
{
TorTransProxy.purgeNatIptables();
}
}
else
{
if (hasRoot)
{
TorTransProxy.purgeNatIptables();
}
}
}
}

View File

@ -6,10 +6,12 @@ public interface TorServiceConstants {
public final static String TAG = "TOR_SERVICE";
public final static String TOR_APP_USERNAME = "org.torproject.android";
//home directory of Android application
public final static String TOR_HOME = "/data/data/org.torproject.android/";
public final static String TOR_HOME = "/data/data/" + TOR_APP_USERNAME + "/";
public final static String TOR_HOME_DATA_DIR = TOR_HOME + "cache/";
public final static String TOR_HOME_DATA_DIR = TOR_HOME + "data/";
//name of the tor C binary
public final static String TOR_BINARY_ASSET_KEY = "tor";

View File

@ -21,94 +21,10 @@ import android.util.Log;
public class TorServiceUtils implements TorServiceConstants {
private static TorifiedApp[] apps = null;
private final static String PREFS_KEY = "OrbotPrefs";
private final static String PREFS_KEY_TORIFIED = "PrefTord";
public static void saveAppSettings (Context context)
{
if (apps == null)
return;
final SharedPreferences prefs = context.getSharedPreferences(PREFS_KEY, 0);
StringBuilder tordApps = new StringBuilder();
for (int i = 0; i < apps.length; i++)
{
if (apps[i].isTorified())
{
tordApps.append(apps[i].getUsername());
tordApps.append("|");
}
}
Editor edit = prefs.edit();
edit.putString(PREFS_KEY_TORIFIED, tordApps.toString());
edit.commit();
}
public static TorifiedApp[] getApps (Context context)
{
if (apps != null)
return apps;
final SharedPreferences prefs = context.getSharedPreferences(PREFS_KEY, 0);
String tordAppString = prefs.getString(PREFS_KEY_TORIFIED, "");
String[] tordApps;
StringTokenizer st = new StringTokenizer(tordAppString,"|");
tordApps = new String[st.countTokens()];
int tordIdx = 0;
while (st.hasMoreTokens())
{
tordApps[tordIdx++] = st.nextToken();
}
Arrays.sort(tordApps);
//else load the apps up
PackageManager pMgr = context.getPackageManager();
List<ApplicationInfo> lAppInfo = pMgr.getInstalledApplications(0);
Iterator<ApplicationInfo> itAppInfo = lAppInfo.iterator();
apps = new TorifiedApp[lAppInfo.size()];
ApplicationInfo aInfo = null;
int appIdx = 0;
while (itAppInfo.hasNext())
{
aInfo = itAppInfo.next();
apps[appIdx] = new TorifiedApp();
apps[appIdx].setEnabled(aInfo.enabled);
apps[appIdx].setUid(aInfo.uid);
apps[appIdx].setUsername(pMgr.getNameForUid(apps[appIdx].getUid()));
apps[appIdx].setProcname(aInfo.processName);
apps[appIdx].setName(pMgr.getApplicationLabel(aInfo).toString());
// check if this application is allowed
if (Arrays.binarySearch(tordApps, apps[appIdx].getUsername()) >= 0) {
apps[appIdx].setTorified(true);
}
else
{
apps[appIdx].setTorified(false);
}
appIdx++;
}
return apps;
}
public static int findProcessId(String command)
{

View File

@ -93,7 +93,7 @@ public class TorTransProxy {
}
}
public static boolean setTransparentProxying(Context context, TorifiedApp[] apps) {
public static boolean setTransparentProxyingByApp(Context context, TorifiedApp[] apps, boolean forceAll) {
String command = null;
@ -101,14 +101,24 @@ public class TorTransProxy {
final StringBuilder script = new StringBuilder();
StringBuilder res = new StringBuilder();
int code = -1;
try {
int code;
for (int i = 0; i < apps.length; i++)
{
if (apps[i].isTorified())
if (forceAll || apps[i].isTorified())
{
if (apps[i].getUsername().equals(TorServiceConstants.TOR_APP_USERNAME))
{
Log.i(TAG,"detected Orbot app - will not transproxy");
continue;
}
Log.i(TAG,"enabling transproxy for app: " + apps[i].getUsername() + "(" + apps[i].getUid() + ")");
//TCP
@ -129,17 +139,16 @@ public class TorTransProxy {
}
}
StringBuilder res = new StringBuilder();
String[] cmd = {script.toString()};
code = TorServiceUtils.doShellCommand(cmd, res, true, true);
String msg = res.toString();
Log.e(TAG, msg);
String msg = res.toString();
Log.e(TAG, msg);
} catch (Exception e) {
Log.w(TAG, "error refreshing iptables: " + e);
Log.w(TAG, "error refreshing iptables: err=" + code + "; resp=" + res.toString(), e);
}
return false;
}