tor-android/src/org/torproject/android/TorService.java

617 lines
14 KiB
Java

/* Copyright (c) 2009, Nathan Freitas, The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.NullEventHandler;
import net.freehaven.tor.control.TorControlConnection;
import net.sourceforge.jsocks.socks.Proxy;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class TorService extends Service implements TorConstants
{
private static TorControlPanel ACTIVITY = null;
private final static String TAG = "TorService";
private static HttpProxy webProxy = null;
private static int currentStatus = STATUS_OFF;
private TorControlConnection conn = null;
private Timer timer = new Timer ();
private final static int UPDATE_INTERVAL = 60000;
/** Called when the activity is first created. */
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"TorService: onCreate");
timer.scheduleAtFixedRate(
new TimerTask() {
public void run() {
//do nothing
// Log.i(TAG,"TorService: task is running");
}
},
0,
UPDATE_INTERVAL);
int procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH);
if (procId != -1)
{
Log.i(TAG,"Found existing Tor process");
try {
currentStatus = STATUS_STARTING_UP;
initControlConnection();
getTorStatus();
if (webProxy != null)
{
if (webProxy.isRunning())
{
//do nothing
Log.i(TAG, "Web Proxy is already running");
}
else
{
//do nothing
Log.i(TAG, "killing Web Proxy");
webProxy.closeSocket();
setupWebProxy(true);
}
}
else //do something
{
setupWebProxy(true);
}
currentStatus = STATUS_ON;
} catch (RuntimeException e) {
Log.i(TAG,"Unable to connect to existing Tor instance,",e);
currentStatus = STATUS_OFF;
this.stopTor();
} catch (Exception e) {
Log.i(TAG,"Unable to connect to existing Tor instance,",e);
currentStatus = STATUS_OFF;
this.stopTor();
}
}
}
/* (non-Javadoc)
* @see android.app.Service#onLowMemory()
*/
@Override
public void onLowMemory() {
// TODO Auto-generated method stub
super.onLowMemory();
}
/* (non-Javadoc)
* @see android.app.Service#onUnbind(android.content.Intent)
*/
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
return super.onUnbind(intent);
}
public static int getStatus ()
{
return currentStatus;
}
public static void setStatus (int newStatus)
{
currentStatus = newStatus;
}
/* (non-Javadoc)
* @see android.app.Service#onRebind(android.content.Intent)
*/
@Override
public void onRebind(Intent intent) {
// TODO Auto-generated method stub
super.onRebind(intent);
Log.i(TAG,"on rebind");
}
/* (non-Javadoc)
* @see android.app.Service#onStart(android.content.Intent, int)
*/
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.i(TAG,"onStart called");
initTor();
setupWebProxy (true);
}
public void onDestroy ()
{
super.onDestroy();
Log.i(TAG,"onDestroy called");
if (timer != null) timer.cancel();
stopTor();
}
private void stopTor ()
{
currentStatus = STATUS_SHUTTING_DOWN;
setupWebProxy(false);
killTorProcess ();
currentStatus = STATUS_OFF;
}
public static void setActivity(TorControlPanel activity) {
ACTIVITY = activity;
}
private void setupWebProxy (boolean enabled)
{
if (enabled)
{
if (webProxy != null)
{
webProxy.closeSocket();
webProxy = null;
}
Log.i(TAG,"Starting up Web Proxy on port: " + PORT_HTTP);
//httpd s
webProxy = new HttpProxy(PORT_HTTP);
webProxy.setDoSocks(true);
webProxy.start();
//socks
try
{
Proxy.setDefaultProxy(IP_LOCALHOST,PORT_SOCKS);
}
catch (Exception e)
{
Log.w(TAG,e.getMessage());
}
Log.i(TAG,"Web Proxy enabled...");
//Settings.System.putString(getContentResolver(), Settings.System.HTTP_PROXY, proxySetting);//enable proxy
// Settings.Secure.putString(getContentResolver(), Settings.Secure.HTTP_PROXY, proxySetting);//enable proxy
}
else
{
//Log.i(TAG,"Turning off Socks/Tor routing on Web Proxy");
if (webProxy != null)
{
//logNotice("Tor is disabled - browsing is not anonymous!");
//webProxy.setDoSocks(false);
webProxy.closeSocket();
webProxy = null;
Log.i(TAG,"WebProxy ServerSocket closed");
}
}
}
public void reloadConfig ()
{
try
{
if (conn == null)
{
initControlConnection ();
}
if (conn != null)
{
conn.signal("RELOAD");
}
}
catch (Exception e)
{
Log.i(TAG,"Unable to reload configuration",e);
}
}
private void killTorProcess ()
{
if (conn != null)
{
try {
Log.i(TAG,"sending SHUTDOWN signal");
conn.signal("SHUTDOWN");
} catch (IOException e) {
// TODO Auto-generated catch block
Log.i(TAG,"error shutting down Tor via connection",e);
}
conn = null;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
int procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH);
while (procId != -1)
{
Log.i(TAG,"Found Tor PID=" + procId + " - killing now...");
doCommand(SHELL_CMD_KILL, procId + "");
procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH);
}
}
private static void logNotice (String msg)
{
Log.i(TAG, msg);
}
private void checkBinary ()
{
boolean binaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
if (!binaryExists)
{
killTorProcess ();
TorBinaryInstaller installer = new TorBinaryInstaller();
installer.start(true);
binaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
if (binaryExists)
{
logNotice("Tor binary installed!");
}
else
{
logNotice("Tor binary install FAILED!");
return;
}
}
Log.i(TAG,"Setting permission on Tor binary");
doCommand(SHELL_CMD_CHMOD, CHMOD_EXE_VALUE + ' ' + TOR_BINARY_INSTALL_PATH);
}
public void initTor ()
{
try {
currentStatus = STATUS_STARTING_UP;
killTorProcess ();
checkBinary ();
doCommand(SHELL_CMD_RM,TOR_LOG_PATH);
Log.i(TAG,"Starting tor process");
doCommand(TOR_BINARY_INSTALL_PATH, TOR_COMMAND_LINE_ARGS);
int procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH);
if (procId == -1)
{
doCommand(TOR_BINARY_INSTALL_PATH, TOR_COMMAND_LINE_ARGS);
procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH);
}
Log.i(TAG,"Tor process id=" + procId);
currentStatus = STATUS_STARTING_UP;
logNotice("Tor is starting up...");
Thread.sleep(500);
initControlConnection ();
} catch (Exception e) {
Log.w(TAG,"unable to start Tor Process",e);
e.printStackTrace();
}
}
private static void logStream (InputStream is)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
try {
while ((line = reader.readLine()) != null)
{
Log.i(TAG, line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
public static int findProcessId(String command)
{
int procId = -1;
Runtime r = Runtime.getRuntime();
Process procPs = null;
try {
procPs = r.exec(SHELL_CMD_PS);
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
String line = null;
while ((line = reader.readLine())!=null)
{
if (line.indexOf(command)!=-1)
{
StringTokenizer st = new StringTokenizer(line," ");
st.nextToken(); //proc owner
procId = Integer.parseInt(st.nextToken().trim());
break;
}
}
} catch (Exception e) {
Log.e(TAG, "error: " + e.getMessage(), e);
}
return procId;
}
public static Process doCommand(String command, String arg1)
{
Runtime r = Runtime.getRuntime();
Process child = null;
try {
if(child != null) {
child.destroy();
child = null;
}
child = r.exec(command + ' ' + arg1);
} catch (Exception e) {
Log.e(TAG, "error: " + e.getMessage());
}
return child;
}
public static 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
{
for (int i = 0; i < 50; i++)
{
try
{
Log.i(TAG,"Connecting to control port: " + TOR_CONTROL_PORT);
Socket s = new Socket(IP_LOCALHOST, TOR_CONTROL_PORT);
conn = TorControlConnection.getConnection(s);
// conn.authenticate(new byte[0]); // See section 3.2
Log.i(TAG,"SUCCESS connected to control port");
//
File fileCookie = new File(TOR_CONTROL_AUTH_COOKIE);
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");
addEventHandler();
break; //don't need to retry
}
catch (ConnectException ce)
{
Log.i(TAG,"Attempt " + i + ": Error connecting to control port; retrying...");
Thread.sleep(1000);
}
}
}
public void modifyConf () throws IOException
{
// Get one configuration variable.
List options = conn.getConf("contact");
// 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
{
try
{
if (conn != null)
{
// get a single value.
// get several values
if (currentStatus == STATUS_STARTING_UP)
{
//Map vals = conn.getInfo(Arrays.asList(new String[]{
// "status/bootstrap-phase", "status","version"}));
String bsPhase = conn.getInfo("status/bootstrap-phase");
// Log.i(TAG, "bootstrap-phase: " + bsPhase);
if (bsPhase.indexOf("PROGRESS=100")!=-1)
{
currentStatus = STATUS_ON;
}
}
else
{
// String status = conn.getInfo("status/circuit-established");
// Log.i(TAG, "status/circuit-established=" + status);
}
}
else
{
currentStatus = STATUS_OFF;
}
}
catch (Exception e)
{
Log.i(TAG, "Unable to get Tor status from control port");
}
}
public void addEventHandler () throws IOException
{
// We extend NullEventHandler so that we don't need to provide empty
// implementations for all the events we don't care about.
// ...
Log.i(TAG,"adding control port event handler");
conn.setEventHandler(ACTIVITY);
conn.setEvents(Arrays.asList(new String[]{
"ORCONN", "CIRC", "NOTICE", "ERR"}));
// conn.setEvents(Arrays.asList(new String[]{
// "DEBUG", "INFO", "NOTICE", "WARN", "ERR"}));
Log.i(TAG,"SUCCESS added control port event handler");
}
}