migrating Orbot codebase from old repo and updating to 0.0.3a

svn:r21593
This commit is contained in:
Nathan Freitas 2010-02-08 20:39:42 +00:00
parent d116e22223
commit 549d4b1247
35 changed files with 2334 additions and 2410 deletions

View File

@ -3,7 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="lib" path="libs/jsocks.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/jtorctrl"/>
<classpathentry kind="lib" path="libs/asocks.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -1,26 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.torproject.android"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
android:versionName="011301.2" android:versionCode="2">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:debuggable="false">
<activity android:name=".TorControlPanel"
<application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="false">
<activity android:name=".Orbot"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<activity android:name=".SettingsPreferences" android:label="@string/app_name">
</activity>
<service android:name=".TorService" android:enabled="true" android:exported="true"/>
<service android:name=".service.TorService" android:process=":remote">
<intent-filter>
<action android:name="org.torproject.android.service.ITorService" />
<action android:name="org.torproject.android.service.TOR_SERVICE" />
</intent-filter>
</service>
</application>
<uses-sdk android:minSdkVersion="3"/>
</manifest>

24
BUILD
View File

@ -7,7 +7,8 @@ Please install the following prerequisites (instructions for each follows):
droid-wrapper: http://github.com/tmurakam/droid-wrapper
libevent source (1.4.12-stable from svn)
Tor source (most recent git master branch)
Privoxy source (http://sourceforge.net/projects/ijbswa/)
Install and prepare the Android OS SDK ( http://source.android.com/download )
on Debian Lenny:
@ -42,14 +43,27 @@ Install droid-wrapper:
sudo make install
zlib and OpenSSL are included with the Android OS SDK. You'll need to build
libevent and finally Tor. We'll create an externals directory for this code:
libevent, Privoxy and finally Tor. We'll create an externals directory for this code:
mkdir -p ~/mydroid/external/{libevent,tor}
mkdir -p ~/mydroid/external/{libevent,tor,privoxy}
We need to set to environment variables for droid-gcc:
export DROID_ROOT=~/mydroid/
export DROID_TARGET=generic
Fetch and build Privoxy:
cd ~/mydroid/external/privoxy
wget http://sourceforge.net/projects/ijbswa/files/Sources/3.0.12%20%28stable%29/privoxy-3.0.12-stable-src.tar.gz/download
tar xzvf privoxy-3.0.12-stable-src.tar.gz
cd privoxy-3.0.12-stable
autoheader
autoconf
#need to disable setpgrp check in configure
export ac_cv_func_setpgrp_void=yes
CC=droid-gcc LD=droid-ld ./configure --host=arm-none-linux-gnueabi
#don't mind the "unrecognized option '-pthred'" error message that you'll see when you run make
make
Fetch and build libevent:
cd ~/mydroid/external/libevent
@ -62,7 +76,6 @@ Fetch and build libevent:
make
Copy over the libevent library:
cp .libs/libevent.a ~/mydroid/out/target/product/generic/obj/lib
Fetch and build Tor:
@ -71,7 +84,7 @@ Fetch and build Tor:
export ZLIBDIR=`cd ~/mydroid/external/zlib && pwd`
cd ~/mydroid/external/tor
git clone https://git.torproject.org/git/tor.git
git clone git://git.torproject.org/git/tor.git
cd tor/
./autogen.sh
CC=droid-gcc LD=droid-ld ./configure --host=arm-none-linux-gnueabi \
@ -105,6 +118,7 @@ Finally, we'll make a proper Android package with ant and the Android App SDK:
export APP_SDK=~/Documents/projects/android/android-sdk-linux_x86-1.5_r3/tools
cd ../Orbot/
cp ~/mydroid/external/privoxy/privoxy-3.0.12-stable/privoxy assets/privoxy
cp ~/mydroid/external/tor/tor/src/or/tor assets/tor
$APP_SDK/android update project --name Orbot --target 1 --path .
ant release

View File

@ -1,3 +1,10 @@
0.0.3a - 2010-02-07
- Integrated iptables support for transparenty proxying of outbound port 80 and DNS
- Privoxy is now used as HTTP Proxy server (cross-compiled to ARM)
- New UI layout and graphics
- Android settings screen for generated torrc file
- Improved performance and error handling
0.0.2a - 2009-11-30
- Update user interace layout and graphics
- Modified service launch, shutdown and handling

174
LICENSE
View File

@ -94,108 +94,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================
src/common/strlcat.c and src/common/strlcpy.c by Todd C. Miller are licensed
under the following license:
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
:src/common/strlcat.c and src/common/strlcpy.c by Todd C. Miller are licensed
under the following license:
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
:src/common/strlcat.c and src/common/strlcpy.c by Todd C. Miller are licensed
under the following license:
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCD FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
:src/common/strlcat.c and src/common/strlcpy.c by Todd C. Miller are licensed
under the following license:
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
:src/common/strlcat.c and src/common/strlcpy.c by Todd C. Miller are licensed
under the following license:
@ -230,79 +128,7 @@ If you got Tor as a static binary with OpenSSL included, then you should know:
"This product includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit (http://www.openssl.org/)"
===============================================================================
"This program uses the IP-to-Country Database provided by
WebHosting.Info (http://www.webhosting.info), available from
http://ip-to-country.webhosting.info."
See the src/config/geoip file in particular.
:src/common/strlcat.c and src/common/strlcpy.c by Todd C. Miller are licensed
under the following license:
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================
If you got Tor as a static binary with OpenSSL included, then you should know:
"This product includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit (http://www.openssl.org/)"
===============================================================================
"This program uses the IP-to-Country Database provided by
WebHosting.Info (http://www.webhosting.info), available from
http://ip-to-country.webhosting.info."
See the src/config/geoip file in particular.
:src/common/strlcat.c and src/common/strlcpy.c by Todd C. Miller are licensed
under the following license:
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================
If you got Tor as a static binary with OpenSSL included, then you should know:
"This product includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit (http://www.openssl.org/)"
===============================================================================
"This program uses the IP-to-Country Database provided by
WebHosting.Info (http://www.webhosting.info), available from

View File

@ -1,48 +0,0 @@
<html>
<head>
<title>help.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
<style>
body
{
background:black;
color:white;
font-family:arial;
}
a, a:link, a:visited
{
color:#00ff00;
}
</style>
</head>
<body>
<h2>ORbot</h2>
<i>Onion Routing Robot - v0.0.2a / November 30, 2009</i><br/>
<hr/>
<h3>How to use ORbot</h3>
Simply touch the Orbot icon on the main screen and wait for it to fully connect to the Tor network. You can use the Menu -> Log option to view
more detailed information about the attempt to connect to the network.
<h3>How to surf anonymously</h3>
Once you have Orbot running and connected to the Tor network, you can utilize the anonymous proxy service in multiple ways:
<ul>
<li>Set your Android APN Proxy (Settings -> Wireless Networks -> Mobile networks -> Access Point Names) to 127.0.0.1/8118</li>
<li>Tell your application to use Web Proxy: 127.0.0.1:8118</li>
<li>Download and use the 'Shadow' browser from the Android Market</li>
</ul>
You can always visit <a href="http://check.torproject.org">http://check.torproject.org</a> to ensure that you are properly connected to the Tor network.
<h3>Bridges and Other Settings</h3>
You can modify the TORRC settings file using the Menu -> Settings option. This is where you can copy and paste in Tor Bridge node addresses
if they are needed in your local area.
<h3>Even More Information!</h3>
If you'd like to learn more about the Tor Project, please visit <a href="http://torproject.org">http://torproject.org</a>.
</body>
</html>

27
assets/privoxy.config Normal file
View File

@ -0,0 +1,27 @@
# Generally, this file goes in /etc/privoxy/config
#
# Tor listens as a SOCKS4a proxy here:
forward-socks4a / 127.0.0.1:9050 .
confdir /data/data/org.torproject.android
logdir /data/data/org.torproject.android
# actionsfile standard # Internal purpose, recommended
#actionsfile default.action # Main actions file
#actionsfile user.action # User customizations
#filterfile default.filter
# Don't log interesting things, only startup messages, warnings and errors
#logfile logfile
#jarfile jarfile
#debug 1
#debug 0 # show each GET/POST/CONNECT request
#debug 4096 # Startup banner and warnings
#debug 8192 # Errors - *we highly recommended enabling this*
#user-manual /usr/share/doc/privoxy/user-manual
listen-address 127.0.0.1:8118
toggle 1
accept-intercepted-requests 1
enable-remote-toggle 0
enable-edit-actions 0
enable-remote-http-toggle 0
buffer-limit 4096

View File

@ -1,169 +1,16 @@
## Configuration file for a typical Tor user
## Last updated 16 July 2009 for Tor 0.2.2.1-alpha.
## (May or may not work for much older or much newer versions of Tor.)
##
## Lines that begin with "## " try to explain what's going on. Lines
## that begin with just "#" are disabled commands: you can enable them
## by removing the "#" symbol.
##
## See 'man tor', or https://www.torproject.org/tor-manual.html,
## for more options you can use in this file.
##
## Tor will look for this file in various places based on your platform:
## https://wiki.torproject.org/noreply/TheOnionRouter/TorFAQ#torrc
## Replace this with "SocksPort 0" if you plan to run Tor only as a
## relay, and not make any local application connections yourself.
SocksPort 9050 # what port to open for local application connections
SocksListenAddress 127.0.0.1 # accept connections only from localhost
SocksListenAddress 127.0.0.1:1080 # listen on this IP:port also
## Entry policies to allow/deny SOCKS requests based on IP address.
## First entry that matches wins. If no SocksPolicy is set, we accept
## all (and only) requests from SocksListenAddress.
#SocksPolicy accept 192.168.0.0/16
#SocksPolicy reject *
## Logs go to stdout at level "notice" unless redirected by something
## else, like one of the below lines. You can have as many Log lines as
## you want.
##
## We advise using "notice" in most cases, since anything more verbose
## may provide sensitive information to an attacker who obtains the logs.
##
## Send all messages of level 'notice' or higher to @LOCALSTATEDIR@/log/tor/notices.log
Log notice file /data/data/org.torproject.android/notices.log
## Send every possible message to @LOCALSTATEDIR@/log/tor/debug.log
#Log debug file /data/data/org.torproject.android/debug.log
## Use the system log instead of Tor's logfiles
#Log notice syslog
## To send all messages to stderr:
#Log debug stderr
## Uncomment this to start the process in the background... or use
## --runasdaemon 1 on the command line. This is ignored on Windows;
## see the FAQ entry if you want Tor to run as an NT service.
#RunAsDaemon 1
## The directory for keeping all the keys/etc. By default, we store
## things in $HOME/.tor on Unix, and in Application Data\tor on Windows.
SocksPort 9050
SocksListenAddress 127.0.0.1
SafeSocks 1
DNSPort 5400
Log notice stdout
Log debug syslog
DataDirectory /data/data/org.torproject.android/data
## The port on which Tor will listen for local connections from Tor
## controller applications, as documented in control-spec.txt.
ControlPort 9051
## If you enable the controlport, be sure to enable one of these
## authentication methods, to prevent attackers from accessing it.
#HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C
CookieAuthentication 1
############### This section is just for location-hidden services ###
## Once you have configured a hidden service, you can look at the
## contents of the file ".../hidden_service/hostname" for the address
## to tell people.
##
## HiddenServicePort x y:z says to redirect requests on port x to the
## address y:z.
#HiddenServiceDir @LOCALSTATEDIR@/lib/tor/hidden_service/
#HiddenServicePort 80 127.0.0.1:80
#HiddenServiceDir @LOCALSTATEDIR@/lib/tor/other_hidden_service/
#HiddenServicePort 80 127.0.0.1:80
#HiddenServicePort 22 127.0.0.1:22
################ This section is just for relays #####################
#
## See https://www.torproject.org/docs/tor-doc-relay for details.
## Required: what port to advertise for incoming Tor connections.
#ORPort 9001
## If you want to listen on a port other than the one advertised
## in ORPort (e.g. to advertise 443 but bind to 9090), uncomment the
## line below too. You'll need to do ipchains or other port forwarding
## yourself to make this work.
#ORListenAddress 0.0.0.0:9090
## A handle for your relay, so people don't have to refer to it by key.
#Nickname ididnteditheconfig
## The IP address or full DNS name for your relay. Leave commented out
## and Tor will guess.
#Address noname.example.com
## Define these to limit how much relayed traffic you will allow. Your
## own traffic is still unthrottled. Note that RelayBandwidthRate must
## be at least 20 KBytes.
#RelayBandwidthRate 100 KBytes # Throttle traffic to 100KB/s (800Kbps)
#RelayBandwidthBurst 200 KBytes # But allow bursts up to 200KB/s (1600Kbps)
## Use these to restrict the maximum traffic per day, week, or month.
## Note that this threshold applies to sent _and_ to received bytes,
## not to their sum: Setting "4 GBytes" may allow up to 8 GBytes
## total before hibernating.
##
## Set a maximum of 4 gigabytes each way per period.
#AccountingMax 4 GBytes
## Each period starts daily at midnight (AccountingMax is per day)
#AccountingStart day 00:00
## Each period starts on the 3rd of the month at 15:00 (AccountingMax
## is per month)
#AccountingStart month 3 15:00
## Contact info to be published in the directory, so we can contact you
## if your relay is misconfigured or something else goes wrong. Google
## indexes this, so spammers might also collect it.
#ContactInfo Random Person <nobody AT example dot com>
## You might also include your PGP or GPG fingerprint if you have one:
#ContactInfo 1234D/FFFFFFFF Random Person <nobody AT example dot com>
## Uncomment this to mirror directory information for others. Please do
## if you have enough bandwidth.
#DirPort 9030 # what port to advertise for directory connections
## If you want to listen on a port other than the one advertised
## in DirPort (e.g. to advertise 80 but bind to 9091), uncomment the line
## below too. You'll need to do ipchains or other port forwarding yourself
## to make this work.
#DirListenAddress 0.0.0.0:9091
## Uncomment to return an arbitrary blob of html on your DirPort. Now you
## can explain what Tor is if anybody wonders why your IP address is
## contacting them. See contrib/tor-exit-notice.html for a sample.
#DirPortFrontPage /etc/tor/exit-notice.html
## Uncomment this if you run more than one Tor relay, and add the identity
## key fingerprint of each Tor relay you control, even if they're on
## different networks. You declare it here so Tor clients can avoid
## using more than one of your relays in a single circuit. See
## https://wiki.torproject.org/noreply/TheOnionRouter/TorFAQ#MultipleServers
#MyFamily $keyid,$keyid,...
## A comma-separated list of exit policies. They're considered first
## to last, and the first match wins. If you want to _replace_
## the default exit policy, end this with either a reject *:* or an
## accept *:*. Otherwise, you're _augmenting_ (prepending to) the
## default exit policy. Leave commented to just use the default, which is
## described in the man page or at
## https://www.torproject.org/documentation.html
##
## Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses
## for issues you might encounter if you use the default exit policy.
##
## If certain IPs and ports are blocked externally, e.g. by your firewall,
## you should update your exit policy to reflect this -- otherwise Tor
## users will be told that those destinations are down.
##
#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more
#ExitPolicy accept *:119 # accept nntp as well as default exit policy
#ExitPolicy reject *:* # no exits allowed
#
## Bridge relays (or "bridges") are Tor relays that aren't listed in the
## main directory. Since there is no complete public list of them, even if an
## ISP is filtering connections to all the known Tor relays, they probably
## won't be able to block all the bridges. Also, websites won't treat you
## differently because they won't know you're running Tor. If you can
## be a real relay, please do; but if not, be a bridge!
#BridgeRelay 1
#ExitPolicy reject *:*
RelayBandwidthRate 20 KBytes
RelayBandwidthBurst 20 KBytes
UseBridges 0
UpdateBridgesFromAuthority 1
bridge 74.82.1.191:19030
bridge 221.31.40.135:4430
bridge 24.110.168.130:443

View File

@ -7,8 +7,8 @@
# "build.properties", and override values to adapt the script to your
# project structure.
apk-configurations=
# Project target.
target=android-4
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-4
apk-configurations=

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,89 @@
<?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">
<ScrollView android:id="@+id/aboutscrollview"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:text="Version: "
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15px"
android:paddingLeft="15px"
android:textStyle="bold"
android:textColor="#ffffff" />
<TextView android:text="- Unkown -"
android:id="@+id/versionName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="15px"
android:layout_gravity="center_vertical"
android:textColor="#ffffff" />
<TextView android:text="Project-Home: "
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15px"
android:paddingLeft="15px"
android:textStyle="bold"
android:textColor="#ffffff" />
<TextView android:text="http://torproject.org/orbot"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:textColorLink="#ffffff"
android:paddingLeft="15px"
android:textSize="12sp"
android:textColor="#ffffff" />
<TextView android:text="License: "
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15px"
android:paddingLeft="15px"
android:textStyle="bold"
android:textColor="#ffffff" />
<TextView android:text="The Tor License"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="15px"
android:textColor="#ffffff" />
<TextView android:text="http://torproject.org"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:textColorLink="#ffffff"
android:paddingLeft="15px"
android:textSize="12sp"
android:textColor="#ffffff" />
<TextView android:text="3rd-Party-Software: "
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15px"
android:paddingLeft="15px"
android:textStyle="bold"
android:textColor="#ffffff" />
<TextView android:text="Tor vX.x: http://www.torproject.org"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:textColorLink="#ffffff"
android:paddingLeft="15px"
android:textSize="12sp"
android:textColor="#ffffff" />
<TextView android:text="LibEvent vX.x: http://www.monkey.org/~provos/libevent/"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:textColorLink="#ffffff"
android:paddingLeft="15px"
android:textSize="12sp"
android:textColor="#ffffff" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@ -0,0 +1,22 @@
<?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">
<ScrollView android:id="@+id/helpscrollview"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:text="@string/help_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15px"
android:paddingLeft="15px"
android:textColor="#ffffff" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@ -1,27 +1,19 @@
<?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">
<ScrollView android:layout_height="380px"
android:layout_width="fill_parent" android:id="@+id/logScrollView">
<TextView android:id="@+id/messageLog"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:layout_weight="1.0"
android:textSize="11px"
/>
</ScrollView>
<Button android:id="@+id/btnLogClear"
android:layout_height="40px"
android:text="Clear Log"
android:layout_margin="0sp"
android:layout_width="fill_parent"/>
</LinearLayout>
<ScrollView android:orientation="vertical"
android:layout_height="480px"
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="wrap_content"
android:layout_width="wrap_content"
android:layout_x="2px"
android:layout_y="2px"
android:textSize="12px"
/>
</ScrollView>

View File

@ -3,13 +3,40 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/bgdarkdroid">
android:background="@drawable/background">
<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="3px"
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="powered by the tor project "
android:layout_marginTop="8px"
android:layout_marginRight="0px"
android:gravity="right"
android:layout_toLeftOf="@+id/radioModeImage"
android:textColor="#cccccc" />
</RelativeLayout>
<ScrollView android:id="@+id/mainview"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableLayout android:id="@+id/mainLayout"
<TableLayout android:id="@+id/mainLayout"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
@ -19,30 +46,28 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<TableRow>
<ImageView
android:id="@+id/imgStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/toroff"/>
android:id="@+id/imgStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/toroff"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/lblStatus"
android:text=" \n "
android:paddingTop="15px"
android:text="- Press to enable - "
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:textStyle="bold"
android:width="240px"
android:height="100px"
android:textColor="#ffffff" />
android:textColor="#ffffff"
/>
</TableRow>
</TableLayout>
</TableRow>
</TableLayout>
</ScrollView>
</ScrollView>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, Google Inc.
**
** 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<WebView
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
</LinearLayout>

View File

@ -1,6 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ORbot</string>
<string name="app_version">0.0.2a</string>
<string name="app_name">Orbot</string>
<string name="app_version">0.0.3a</string>
<string name="internal_web_url">http://orbot/</string>
<string name="default_web_url">http://check.torproject.org</string>
<string name="control_permission_label">start and stop the anonymous data connection</string>
<string name="tor_proxy_service_process">torproxyservice</string>
<string name="status_starting_up">Starting up...</string>
<string name="status_activated">"Orbot is Activated\n&lt;&lt; TOUCH TO DISCONNECT &gt;&gt;</string>
<string name="status_disabled">"Orbot is Disabled\n&lt;&lt; TOUCH TO START &gt;&gt;</string>
<string name="status_shutting_down">Orbot is shutting down</string>
<string name="tor_process_connecting">Starting Tor engine...</string>
<string name="tor_process_connecting_step2">authenticating...</string>
<string name="tor_process_connecting_step3">complete.</string>
<string name="tor_process_connecting_step4">waiting.</string>
<string name="menu_home">Home</string>
<string name="menu_browse">Browse</string>
<string name="menu_settings">Settings</string>
<string name="menu_log">Log</string>
<string name="menu_info">Info</string>
<string name="button_help">Help</string>
<string name="button_close">Close</string>
<string name="button_about">About</string>
<string name="help_text">Orbot requires different configuration depending on the Android operating system version it is used on.
\n\n
For non-rooted Android 1.x devices (G1, MyTouch3G, Hero): Please use the "ProxySurf" browser available in the Android Market, and set
the HTTP Proxy to 127.0.0.1 and port 8118. For Instant Messsaging, try "Beem" in the market, and set the HTTP or SOCKS5 proxy (to port 9050).
\n\n
For Android 2.x devices, you MUST ROOT your device in order for Orbot to work transparently for all web and DNS traffic. Otherwise, the "Beem" app will allow
you to set the SOCKS5 proxy to 127.0.0.1 and port 9050. You should also enable SSL to protect your username and password.
\n\n
If you root your device, whether it is 1.x or 2.x based, Orbot will automatically, transparently proxy all web traffic on port 80
and all DNS requests. This includes the built-in Browser, Gmail, YouTube, Maps and any other application that uses standard port 80
traffic.
\n\n
At this time, Orbot cannot proxy standard HTTPS traffic unless the application supports proxy via HTTP or SOCKS.
</string>
</resources>

View File

@ -1,639 +0,0 @@
package org.torproject.android;
/* <!-- in case someone opens this in a browser... --> <pre> */
/*
* This is a simple multi-threaded Java proxy server
* for HTTP requests (HTTPS doesn't seem to work, because
* the CONNECT requests aren't always handled properly).
* I implemented the class as a thread so you can call it
* from other programs and kill it, if necessary (by using
* the closeSocket() method).
*
* We'll call this the 1.1 version of this class. All I
* changed was to separate the HTTP header elements with
* \r\n instead of just \n, to comply with the official
* HTTP specification.
*
* This can be used either as a direct proxy to other
* servers, or as a forwarding proxy to another proxy
* server. This makes it useful if you want to monitor
* traffic going to and from a proxy server (for example,
* you can run this on your local machine and set the
* fwdServer and fwdPort to a real proxy server, and then
* tell your browser to use "localhost" as the proxy, and
* you can watch the browser traffic going in and out).
*
* One limitation of this implementation is that it doesn't
* close the ProxyThread socket if the client disconnects
* or the server never responds, so you could end up with
* a bunch of loose threads running amuck and waiting for
* connections. As a band-aid, you can set the server socket
* to timeout after a certain amount of time (use the
* setTimeout() method in the ProxyThread class), although
* this can cause false timeouts if a remote server is simply
* slow to respond.
*
* Another thing is that it doesn't limit the number of
* socket threads it will create, so if you use this on a
* really busy machine that processed a bunch of requests,
* you may have problems. You should use thread pools if
* you're going to try something like this in a "real"
* application.
*
* Note that if you're using the "main" method to run this
* by itself and you don't need the debug output, it will
* run a bit faster if you pipe the std output to 'nul'.
*
* You may use this code as you wish, just don't pretend
* that you wrote it yourself, and don't hold me liable for
* anything that it does or doesn't do. If you're feeling
* especially honest, please include a link to nsftools.com
* along with the code. Thanks, and good luck.
*
* Julian Robichaux -- http://www.nsftools.com
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import net.sourceforge.jsocks.socks.Socks5Proxy;
import net.sourceforge.jsocks.socks.SocksSocket;
public class HttpProxy extends Thread
{
public static final int DEFAULT_PORT = 8888;
private ServerSocket server = null;
private int thisPort = DEFAULT_PORT;
private String fwdServer = "";
private int fwdPort = 0;
private int ptTimeout = ProxyThread.DEFAULT_TIMEOUT;
private int debugLevel = 1;
private PrintStream debugOut = System.out;
private boolean keepRunning = true;
private boolean doSocks = false;
private Socks5Proxy sProxy = null;
/**
* @return the doSocks
*/
public boolean isDoSocks() {
return doSocks;
}
/**
* @param doSocks the doSocks to set
*/
public void setDoSocks(boolean doSocks) {
this.doSocks = doSocks;
}
/* here's a main method, in case you want to run this by itself */
public static void main (String args[])
{
int port = 0;
String fwdProxyServer = "";
int fwdProxyPort = 0;
if (args.length == 0)
{
System.err.println("USAGE: java jProxy <port number> [<fwd proxy> <fwd port>]");
System.err.println(" <port number> the port this service listens on");
System.err.println(" <fwd proxy> optional proxy server to forward requests to");
System.err.println(" <fwd port> the port that the optional proxy server is on");
System.err.println("\nHINT: if you don't want to see all the debug information flying by,");
System.err.println("you can pipe the output to a file or to 'nul' using \">\". For example:");
System.err.println(" to send output to the file prox.txt: java jProxy 8080 > prox.txt");
System.err.println(" to make the output go away: java jProxy 8080 > nul");
return;
}
// get the command-line parameters
port = Integer.parseInt(args[0]);
if (args.length > 2)
{
fwdProxyServer = args[1];
fwdProxyPort = Integer.parseInt(args[2]);
}
// create and start the jProxy thread, using a 20 second timeout
// value to keep the threads from piling up too much
System.err.println(" ** Starting jProxy on port " + port + ". Press CTRL-C to end. **\n");
HttpProxy jp = new HttpProxy(port, fwdProxyServer, fwdProxyPort, 20);
jp.setDebug(1, System.out); // or set the debug level to 2 for tons of output
jp.start();
// run forever; if you were calling this class from another
// program and you wanted to stop the jProxy thread at some
// point, you could write a loop that waits for a certain
// condition and then calls jProxy.closeSocket() to kill
// the running jProxy thread
while (true)
{
try { Thread.sleep(3000); } catch (Exception e) {}
}
// if we ever had a condition that stopped the loop above,
// we'd want to do this to kill the running thread
//jp.closeSocket();
//return;
}
/* the proxy server just listens for connections and creates
* a new thread for each connection attempt (the ProxyThread
* class really does all the work)
*/
public HttpProxy (int port)
{
thisPort = port;
try {
sProxy = new Socks5Proxy(TorConstants.IP_LOCALHOST,TorConstants.PORT_SOCKS);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sProxy.resolveAddrLocally(false);
}
public HttpProxy (int port, String proxyServer, int proxyPort)
{
thisPort = port;
fwdServer = proxyServer;
fwdPort = proxyPort;
}
public HttpProxy (int port, String proxyServer, int proxyPort, int timeout)
{
thisPort = port;
fwdServer = proxyServer;
fwdPort = proxyPort;
ptTimeout = timeout;
}
/* allow the user to decide whether or not to send debug
* output to the console or some other PrintStream
*/
public void setDebug (int level, PrintStream out)
{
debugLevel = level;
debugOut = out;
}
/* get the port that we're supposed to be listening on
*/
public int getPort ()
{
return thisPort;
}
/* return whether or not the socket is currently open
*/
public boolean isRunning ()
{
if (server == null)
return false;
else
return true;
}
/* closeSocket will close the open ServerSocket; use this
* to halt a running jProxy thread
*/
public void closeSocket ()
{
try {
keepRunning = false;
// close the open server socket
server.close();
// send it a message to make it stop waiting immediately
// (not really necessary)
/*Socket s = new Socket("localhost", thisPort);
OutputStream os = s.getOutputStream();
os.write((byte)0);
os.close();
s.close();*/
} catch(Exception e) {
if (debugLevel > 0)
debugOut.println(e);
}
server = null;
}
public void run()
{
try {
// create a server socket, and loop forever listening for
// client connections
server = new ServerSocket(thisPort);
while (keepRunning)
{
Socket client = server.accept();
ProxyThread t = new ProxyThread(client, doSocks, sProxy);
//t.setDebug(debugLevel, debugOut);
//t.setTimeout(ptTimeout);
t.start();
}
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("jProxy Thread error: " + e);
}
closeSocket();
}
}
/*
* The ProxyThread will take an HTTP request from the client
* socket and send it to either the server that the client is
* trying to contact, or another proxy server
*/
class ProxyThread extends Thread
{
private Socket pSocket;
private String fwdServer = "";
private int fwdPort = 0;
private int debugLevel = 0;
private PrintStream debugOut = System.out;
// the socketTimeout is used to time out the connection to
// the remote server after a certain period of inactivity;
// the value is in milliseconds -- use zero if you don't want
// a timeout
public static final int DEFAULT_TIMEOUT = 20 * 1000;
private int socketTimeout = DEFAULT_TIMEOUT;
private boolean doSocks = false;
private static Socks5Proxy sProxy = null;
public ProxyThread(Socket s, boolean doSocks, Socks5Proxy sProxy)
{
pSocket = s;
this.sProxy = sProxy;
this.doSocks = doSocks;
}
public ProxyThread(Socket s, String proxy, int port)
{
pSocket = s;
fwdServer = proxy;
fwdPort = port;
}
public void setTimeout (int timeout)
{
// assume that the user will pass the timeout value
// in seconds (because that's just more intuitive)
socketTimeout = timeout * 1000;
}
public void setDebug (int level, PrintStream out)
{
debugLevel = level;
debugOut = out;
}
public void run()
{
try
{
long startTime = System.currentTimeMillis();
// client streams (make sure you're using streams that use
// byte arrays, so things like GIF and JPEG files and file
// downloads will transfer properly)
BufferedInputStream clientIn = new BufferedInputStream(pSocket.getInputStream());
BufferedOutputStream clientOut = new BufferedOutputStream(pSocket.getOutputStream());
// the socket to the remote server
Socket server = null;
// other variables
byte[] request = null;
byte[] response = null;
int requestLength = 0;
int responseLength = 0;
int pos = -1;
StringBuffer host = new StringBuffer("");
String hostName = "";
int hostPort = 80;
// get the header info (the web browser won't disconnect after
// it's sent a request, so make sure the waitForDisconnect
// parameter is false)
request = getHTTPData(clientIn, host, false);
requestLength = Array.getLength(request);
// separate the host name from the host port, if necessary
// (like if it's "servername:8000")
hostName = host.toString();
pos = hostName.indexOf(":");
if (pos > 0)
{
try { hostPort = Integer.parseInt(hostName.substring(pos + 1));
} catch (Exception e) { }
hostName = hostName.substring(0, pos);
}
// either forward this request to another proxy server or
// send it straight to the Host
try
{
if (!doSocks)
{
if ((fwdServer.length() > 0) && (fwdPort > 0))
{
server = new Socket(fwdServer, fwdPort);
} else {
server = new Socket(hostName, hostPort);
}
}
else
{
server = new SocksSocket(sProxy,hostName, hostPort);
}
} catch (Exception e) {
// tell the client there was an error
String errMsg = "HTTP/1.0 500\nContent Type: text/plain\n\n" +
"Error connecting to the server:\n" + e + "\n";
clientOut.write(errMsg.getBytes(), 0, errMsg.length());
}
if (server != null)
{
server.setSoTimeout(socketTimeout);
BufferedInputStream serverIn = new BufferedInputStream(server.getInputStream());
BufferedOutputStream serverOut = new BufferedOutputStream(server.getOutputStream());
// send the request out
serverOut.write(request, 0, requestLength);
serverOut.flush();
// and get the response; if we're not at a debug level that
// requires us to return the data in the response, just stream
// it back to the client to save ourselves from having to
// create and destroy an unnecessary byte array. Also, we
// should set the waitForDisconnect parameter to 'true',
// because some servers (like Google) don't always set the
// Content-Length header field, so we have to listen until
// they decide to disconnect (or the connection times out).
if (debugLevel > 1)
{
response = getHTTPData(serverIn, true);
responseLength = Array.getLength(response);
} else {
responseLength = streamHTTPData(serverIn, clientOut, true);
}
serverIn.close();
serverOut.close();
}
// send the response back to the client, if we haven't already
if (debugLevel > 1)
clientOut.write(response, 0, responseLength);
// if the user wants debug info, send them debug info; however,
// keep in mind that because we're using threads, the output won't
// necessarily be synchronous
if (debugLevel > 0)
{
long endTime = System.currentTimeMillis();
debugOut.println("Request from " + pSocket.getInetAddress().getHostAddress() +
" on Port " + pSocket.getLocalPort() +
" to host " + hostName + ":" + hostPort +
"\n (" + requestLength + " bytes sent, " +
responseLength + " bytes returned, " +
Long.toString(endTime - startTime) + " ms elapsed)");
debugOut.flush();
}
if (debugLevel > 1)
{
debugOut.println("REQUEST:\n" + (new String(request)));
debugOut.println("RESPONSE:\n" + (new String(response)));
debugOut.flush();
}
// close all the client streams so we can listen again
clientOut.close();
clientIn.close();
pSocket.close();
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error in ProxyThread: " + e);
//e.printStackTrace();
}
}
private byte[] getHTTPData (InputStream in, boolean waitForDisconnect)
{
// get the HTTP data from an InputStream, and return it as
// a byte array
// the waitForDisconnect parameter tells us what to do in case
// the HTTP header doesn't specify the Content-Length of the
// transmission
StringBuffer foo = new StringBuffer("");
return getHTTPData(in, foo, waitForDisconnect);
}
private byte[] getHTTPData (InputStream in, StringBuffer host, boolean waitForDisconnect)
{
// get the HTTP data from an InputStream, and return it as
// a byte array, and also return the Host entry in the header,
// if it's specified -- note that we have to use a StringBuffer
// for the 'host' variable, because a String won't return any
// information when it's used as a parameter like that
ByteArrayOutputStream bs = new ByteArrayOutputStream();
streamHTTPData(in, bs, host, waitForDisconnect);
return bs.toByteArray();
}
private int streamHTTPData (InputStream in, OutputStream out, boolean waitForDisconnect)
{
StringBuffer foo = new StringBuffer("");
return streamHTTPData(in, out, foo, waitForDisconnect);
}
private int streamHTTPData (InputStream in, OutputStream out,
StringBuffer host, boolean waitForDisconnect)
{
// get the HTTP data from an InputStream, and send it to
// the designated OutputStream
StringBuffer header = new StringBuffer("");
String data = "";
int responseCode = 200;
int contentLength = 0;
int pos = -1;
int byteCount = 0;
try
{
// get the first line of the header, so we know the response code
data = readLine(in);
if (data != null)
{
header.append(data + "\r\n");
pos = data.indexOf(" ");
if ((data.toLowerCase().startsWith("http")) &&
(pos >= 0) && (data.indexOf(" ", pos+1) >= 0))
{
String rcString = data.substring(pos+1, data.indexOf(" ", pos+1));
try
{
responseCode = Integer.parseInt(rcString);
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error parsing response code " + rcString);
}
}
}
// get the rest of the header info
while ((data = readLine(in)) != null)
{
// the header ends at the first blank line
if (data.length() == 0)
break;
header.append(data + "\r\n");
// check for the Host header
pos = data.toLowerCase().indexOf("host:");
if (pos >= 0)
{
host.setLength(0);
host.append(data.substring(pos + 5).trim());
}
// check for the Content-Length header
pos = data.toLowerCase().indexOf("content-length:");
if (pos >= 0)
contentLength = Integer.parseInt(data.substring(pos + 15).trim());
}
// add a blank line to terminate the header info
header.append("\r\n");
// convert the header to a byte array, and write it to our stream
out.write(header.toString().getBytes(), 0, header.length());
// if the header indicated that this was not a 200 response,
// just return what we've got if there is no Content-Length,
// because we may not be getting anything else
if ((responseCode != 200) && (contentLength == 0))
{
out.flush();
return header.length();
}
// get the body, if any; we try to use the Content-Length header to
// determine how much data we're supposed to be getting, because
// sometimes the client/server won't disconnect after sending us
// information...
if (contentLength > 0)
waitForDisconnect = false;
if ((contentLength > 0) || (waitForDisconnect))
{
try {
byte[] buf = new byte[4096];
int bytesIn = 0;
while ( ((byteCount < contentLength) || (waitForDisconnect))
&& ((bytesIn = in.read(buf)) >= 0) )
{
out.write(buf, 0, bytesIn);
byteCount += bytesIn;
}
} catch (Exception e) {
String errMsg = "Error getting HTTP body: " + e;
if (debugLevel > 0)
debugOut.println(errMsg);
//bs.write(errMsg.getBytes(), 0, errMsg.length());
}
}
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error getting HTTP data: " + e);
}
//flush the OutputStream and return
try { out.flush(); } catch (Exception e) {}
return (header.length() + byteCount);
}
private String readLine (InputStream in)
{
// reads a line of text from an InputStream
StringBuffer data = new StringBuffer("");
int c;
try
{
// if we have nothing to read, just return null
in.mark(1);
if (in.read() == -1)
return null;
else
in.reset();
while ((c = in.read()) >= 0)
{
// check for an end-of-line character
if ((c == 0) || (c == 10) || (c == 13))
break;
else
data.append((char)c);
}
// deal with the case where the end-of-line terminator is \r\n
if (c == 13)
{
in.mark(1);
if (in.read() != 10)
in.reset();
}
} catch (Exception e) {
if (debugLevel > 0)
debugOut.println("Error getting header: " + e);
}
// and return what we have
return data.toString();
}
}

View File

@ -0,0 +1,642 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android;
import java.util.StringTokenizer;
import org.torproject.android.service.ITorService;
import org.torproject.android.service.ITorServiceCallback;
import org.torproject.android.service.TorRoot;
import org.torproject.android.service.TorServiceConstants;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
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;
/* Some tracking bits */
private int torStatus = STATUS_REQUIRES_DEMAND; //latest status reported from the tor service
private int currentView = 0; //the currently displayed UI view
private StringBuffer logBuffer = new StringBuffer(); //the output of the service log messages
private String lastUrl = null;
/* Tor Service interaction */
/* The primary interface we will be calling on the service. */
ITorService mService = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_Black);
setTitle(getString(R.string.app_name) + ' ' + getString(R.string.app_version));
showMain();
}
/*
* Create the UI Options Menu (non-Javadoc)
* @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
*/
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem mItem = null;
mItem = menu.add(0, 1, Menu.NONE, getString(R.string.menu_home));
mItem.setIcon(R.drawable.ic_menu_home);
mItem = menu.add(0, 2, Menu.NONE, getString(R.string.menu_browse));
mItem.setIcon(R.drawable.ic_menu_goto);
mItem = menu.add(0, 3, Menu.NONE, getString(R.string.menu_settings));
mItem.setIcon(R.drawable.ic_menu_register);
mItem = menu.add(0, 4, Menu.NONE, getString(R.string.menu_log));
mItem.setIcon(R.drawable.ic_menu_reports);
mItem = menu.add(0, 5, Menu.NONE, getString(R.string.menu_info));
mItem.setIcon(R.drawable.ic_menu_about);
return true;
}
/* When a menu item is selected launch the appropriate view or activity
* (non-Javadoc)
* @see android.app.Activity#onMenuItemSelected(int, android.view.MenuItem)
*/
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
super.onMenuItemSelected(featureId, item);
if (item.getItemId() == 1)
{
this.showMain();
}
else if (item.getItemId() == 3)
{
this.showSettings();
}
else if (item.getItemId() == 4)
{
this.showMessageLog();
}
else if (item.getItemId() == 2)
{
openBrowser(URL_TOR_CHECK);
}
else if (item.getItemId() == 5)
{
showHelp();
}
return true;
}
/* Return to the main view when the back key is pressed
* (non-Javadoc)
* @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
*/
public boolean onKeyDown(int keyCode, KeyEvent event){
if(keyCode==KeyEvent.KEYCODE_BACK){
if(currentView != R.layout.layout_main){
showMain ();
return true;
}
else{
return super.onKeyDown(keyCode, event);
}
}
return super.onKeyDown(keyCode, event);
}
/* (non-Javadoc)
* @see android.app.Activity#onPause()
*/
@Override
protected void onPause() {
super.onPause();
}
/* (non-Javadoc)
* @see android.app.Activity#onResume()
*/
@Override
protected void onResume() {
super.onResume();
updateStatus (""); //update the status, which checks the service status
}
/* (non-Javadoc)
* @see android.app.Activity#onStart()
*/
@Override
protected void onStart() {
super.onStart();
//if Tor binary is not running, then start the service up
startService(new Intent(INTENT_TOR_SERVICE));
bindService ();
updateStatus ("");
}
/* (non-Javadoc)
* @see android.app.Activity#onStop()
*/
@Override
protected void onStop() {
super.onStop();
unbindService();
}
/*
* Show the main form UI
*/
private void showMain ()
{
bindService(); //connect the UI activity to the remote service
currentView = R.layout.layout_main;
setContentView(currentView);
//add touch listeners for the image and the text label
findViewById(R.id.imgStatus).setOnClickListener(this);
findViewById(R.id.lblStatus).setOnClickListener(this);
lblStatus = (TextView)findViewById(R.id.lblStatus);
imgStatus = (ImageView)findViewById(R.id.imgStatus);
updateStatus("");
}
/*
* Launch the system activity for Uri viewing with the provided url
*/
private void openBrowser(String url)
{
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
}
/*
* Show the about view - a popup dialog
*/
private void showAbout ()
{
LayoutInflater li = LayoutInflater.from(this);
View view = li.inflate(R.layout.layout_about, null);
TextView versionName = (TextView)view.findViewById(R.id.versionName);
versionName.setText(R.string.app_version);
new AlertDialog.Builder(this)
.setTitle(getString(R.string.menu_info))
.setView(view)
.setNeutralButton(getString(R.string.button_help), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
showHelp();
}
})
.setNegativeButton(getString(R.string.button_close), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Log.d(TAG, "Close pressed");
}
})
.show();
}
/*
* Show the help view - a popup dialog
*/
private void showHelp ()
{
LayoutInflater li = LayoutInflater.from(this);
View view = li.inflate(R.layout.layout_help, null);
new AlertDialog.Builder(this)
.setTitle(getString(R.string.menu_info))
.setView(view)
.setNeutralButton(getString(R.string.button_about), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
showAbout();
}
})
.setNegativeButton(getString(R.string.button_close), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Log.d(TAG, "Close pressed");
}
})
.show();
}
/*
* Show the message log UI
*/
private void showMessageLog ()
{
currentView = R.layout.layout_log;
setContentView(currentView);
txtMessageLog = (TextView)findViewById(R.id.messageLog);
txtMessageLog.setText(logBuffer.toString());
}
/*
* Load the basic settings application to display torrc
*/
private void showSettings ()
{
startActivity(new Intent(this, SettingsPreferences.class));
}
/*
* Read in the Preferences and write then to the .torrc file
*/
private void processSettings ()
{
StringBuffer torrcText = new StringBuffer();
torrcText.append(TorConstants.TORRC_DEFAULT);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean useBridges = prefs.getBoolean(PREF_BRIDGES_ENABLED, false);
boolean autoUpdateBridges = prefs.getBoolean(PREF_BRIDGES_UPDATED, false);
String bridgeList = prefs.getString(PREF_BRIDGES_LIST,"");
if (useBridges)
{
torrcText.append("UseBridges 1");
torrcText.append('\n');
torrcText.append("UpdateBridgesFromAuthority ");
if (autoUpdateBridges)
torrcText.append("1");
else
torrcText.append("0");
torrcText.append('\n');
String bridgeDelim = "\n";
if (bridgeList.indexOf(",") != -1)
{
bridgeDelim = ",";
}
StringTokenizer st = new StringTokenizer(bridgeList,bridgeDelim);
while (st.hasMoreTokens())
{
torrcText.append("bridge ");
torrcText.append(st.nextToken());
torrcText.append('\n');
}
}
else
{
torrcText.append("UseBridges 0");
}
Utils.saveTextFile(TorServiceConstants.TORRC_INSTALL_PATH, torrcText.toString());
}
/*
* Set the state of the running/not running graphic and label
*/
public void updateStatus (String torServiceMsg)
{
try
{
if (mService != null)
torStatus = mService.getStatus();
if (imgStatus != null)
{
if (torStatus == STATUS_ON)
{
imgStatus.setImageResource(R.drawable.toron);
lblStatus.setText(getString(R.string.status_activated));
if (progressDialog != null)
{
progressDialog.cancel();
progressDialog.hide();
progressDialog = null;
}
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 (progressDialog == null)
{
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(true);
progressDialog.setMessage(getString(R.string.status_starting_up));
progressDialog.show();
progressDialog.setProgress(10);
}
progressDialog.setMessage(torServiceMsg);
int idx = torServiceMsg.indexOf("%");
if (idx != -1)
{
String pComp = torServiceMsg.substring(idx-2,idx).trim();
int ipComp = Integer.parseInt(pComp);
progressDialog.setProgress(ipComp);
}
}
else if (torStatus == STATUS_UNAVAILABLE)
{
imgStatus.setImageResource(R.drawable.torstopping);
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)
{
progressDialog.cancel();
progressDialog.hide();
progressDialog = null;
}
imgStatus.setImageResource(R.drawable.toroff);
lblStatus.setText(getString(R.string.status_disabled));
}
}
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
/*
* (non-Javadoc)
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
public void onClick(View view) {
// the start button
if (view.getId()==R.id.imgStatus || view.getId()==R.id.lblStatus)
{
try
{
if (mService == null)
{
}
else if (mService.getStatus() == STATUS_REQUIRES_DEMAND)
{
processSettings();
mService.setProfile(PROFILE_ON);
if (hasRoot)
{
TorRoot.enableDNSProxying();
TorRoot.enabledWebProxying();
}
}
else
{
mService.setProfile(PROFILE_ONDEMAND);
if (hasRoot)
{
TorRoot.purgeNatIptables();
}
}
}
catch (Exception e)
{
Log.i(TAG,"error onclick",e);
}
}
}
/**
* This implementation is used to receive callbacks from the remote
* service.
*/
private ITorServiceCallback mCallback = new ITorServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values. Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* 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.
*/
public void statusChanged(String value) {
Message msg = mHandler.obtainMessage(BUMP_MSG);
msg.getData().putString(HANDLER_TOR_MSG, value);
mHandler.sendMessage(msg);
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
String torServiceMsg = (String)msg.getData().getString(HANDLER_TOR_MSG);
logBuffer.append(torServiceMsg);
logBuffer.append('\n');
updateStatus(torServiceMsg);
break;
default:
super.handleMessage(msg);
}
}
};
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = ITorService.Stub.asInterface(service);
updateStatus ("");
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
}
};
boolean mIsBound = false;
boolean hasRoot = false;
private void bindService ()
{
bindService(new Intent(ITorService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
hasRoot = TorRoot.hasRootAccess();
}
private void unbindService ()
{
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
}

View File

@ -0,0 +1,16 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class SettingsPreferences
extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}

View File

@ -1,75 +1,37 @@
/* Copyright (c) 2009, Nathan Freitas, The Guardian Project - http://openideals.com/guardian */
/* Copyright (c) 2009, Nathan Freitas, Orbot/The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android;
public interface TorConstants {
//home directory of Android application
public final static String TOR_HOME = "/data/data/org.torproject.android/";
public final static String TAG = "Orbot";
//name of the tor C binary
public final static String TOR_BINARY_ASSET_KEY = "tor";
//path to install the Tor binary too
public final static String TOR_BINARY_INSTALL_PATH = TOR_HOME + TOR_BINARY_ASSET_KEY;
//key of the tor binary in the Zip file
public final static String TOR_BINARY_ZIP_KEY = "assets/" + TOR_BINARY_ASSET_KEY;
//torrc file name
public final static String TORRC_ASSET_KEY = "torrc";
//path to install torrc to within the android app data folder
public final static String TORRC_INSTALL_PATH = TOR_HOME + TORRC_ASSET_KEY;
//key of the torrc file in the Zip file
public final static String TORRC_ZIP_KEY = "assets/" + TORRC_ASSET_KEY;
//where to send the notices log
public final static String TOR_LOG_PATH = TOR_HOME + "notices.log";
//control port cookie path
public final static String TOR_CONTROL_AUTH_COOKIE = TOR_HOME + "data/control_auth_cookie";
//how to launch tor
public final static String TOR_COMMAND_LINE_ARGS = "-f " + TORRC_INSTALL_PATH;
//various console cmds
public final static String SHELL_CMD_CHMOD = "/system/bin/chmod";
public final static String SHELL_CMD_KILL = "/system/bin/kill";
public final static String SHELL_CMD_RM = "/system/bin/rm";
public final static String SHELL_CMD_PS = "ps";
public final static String CHMOD_EXE_VALUE = "777";
//path of the installed APK file
public final static String APK_PATH = "/data/app/org.torproject.android.apk";
public final static int FILE_WRITE_BUFFER_SIZE = 2048;
//path to check Tor against
public final static String URL_TOR_CHECK = "http://check.torproject.org";
public final static int FILE_WRITE_BUFFER_SIZE = 2048;
//HTTP Proxy server port
public final static int PORT_HTTP = 8118; //just like Privoxy!
//Socks port client connects to, server is the Tor binary
public final static int PORT_SOCKS = 9050;
//what is says!
public final static String IP_LOCALHOST = "127.0.0.1";
public final static int TOR_CONTROL_PORT = 9051;
public final static int UPDATE_TIMEOUT = 3000;
public final static String DEFAULT_HOME_PAGE = "file:///android_asset/help.html";// "http://check.torproject.org";
//status to communicate state
public final static int STATUS_OFF = 0;
public final static int STATUS_UNAVAILABLE = -1;
public final static int STATUS_REQUIRES_DEMAND = 0;
public final static int STATUS_ON = 1;
public final static int STATUS_STARTING_UP = 2;
public final static int STATUS_SHUTTING_DOWN = 3;
public final static int STATUS_CONNECTING = 2;
//control port
public final static String TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE = "Bootstrapped 100%";
public final static int PROFILE_OFF = -1;
public final static int PROFILE_ONDEMAND = 0;
public final static int PROFILE_ON = 1;
public final static String NEWLINE = "\n";
public final static String TORRC_DEFAULT =
"SocksPort 9050\nSocksListenAddress 127.0.0.1\nSafeSocks 1\nDNSPort 5400\nLog notice stdout\nLog debug syslog\nDataDirectory /data/data/org.torproject.android/data\n"
+ "ControlPort 9051\nCookieAuthentication 1\nRelayBandwidthRate 20 KBytes\nRelayBandwidthBurst 20 KBytes\n";
public final static String INTENT_TOR_SERVICE = "org.torproject.android.service.TOR_SERVICE";
public final static String HANDLER_TOR_MSG = "torServiceMsg";
public final static String PREF_BRIDGES_ENABLED = "pref_bridges_enabled";
public final static String PREF_BRIDGES_UPDATED = "pref_bridges_enabled";
public final static String PREF_BRIDGES_LIST = "pref_bridges_list";
}

View File

@ -1,587 +0,0 @@
/* 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.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import net.freehaven.tor.control.EventHandler;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class TorControlPanel extends Activity implements OnClickListener, TorConstants, EventHandler
{
private final static String TAG = "Tor";
private static Intent torService = null;
private boolean updateLog = false;
private boolean updateStatus = false;
private TextView lblStatus = null;
private ImageView imgStatus = null;
private String txtStatus = "";
private int torStatus = STATUS_OFF;
private Thread threadStatus = null;
private WebView mWebView;
private int currentView = 0;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_Black);
showMain();
}
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem mItem = menu.add(0, 1, Menu.NONE, "Home");
MenuItem mItem2 = menu.add(0, 2, Menu.NONE, "Settings");
MenuItem mItem3 = menu.add(0, 3, Menu.NONE, "Log");
MenuItem mItem4 = menu.add(0, 4, Menu.NONE, "Help");
mItem.setIcon(R.drawable.ic_menu_home);
mItem2.setIcon(R.drawable.ic_menu_register);
mItem3.setIcon(R.drawable.ic_menu_reports);
mItem4.setIcon(R.drawable.ic_menu_about);
return true;
}
/* (non-Javadoc)
* @see android.app.Activity#onMenuItemSelected(int, android.view.MenuItem)
*/
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
super.onMenuItemSelected(featureId, item);
if (item.getItemId() == 1)
{
this.showMain();
}
else if (item.getItemId() == 2)
{
this.showSettings();
}
else if (item.getItemId() == 3)
{
this.showMessageLog();
}
else if (item.getItemId() == 4)
{
this.showWeb(DEFAULT_HOME_PAGE);
}
return true;
}
public boolean onKeyDown(int keyCode, KeyEvent event){
if(keyCode==KeyEvent.KEYCODE_BACK){
if(currentView != R.layout.layout_main){
showMain ();
return true;
}
else{
return super.onKeyDown(keyCode, event);
}
}
return super.onKeyDown(keyCode, event);
}
/* (non-Javadoc)
* @see android.app.Activity#onPause()
*/
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
TorService.setStatus(torStatus);
}
/* (non-Javadoc)
* @see android.app.Activity#onResume()
*/
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
torStatus = TorService.getStatus();
updateStatus ();
}
/* (non-Javadoc)
* @see android.app.Activity#onStart()
*/
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
torStatus = TorService.getStatus();
updateStatus ();
}
/* (non-Javadoc)
* @see android.app.Activity#onStop()
*/
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
TorService.setStatus(torStatus);
}
/*
* Show the main form UI
*/
private void showMain ()
{
updateLog = false;
updateStatus = true;
currentView = R.layout.layout_main;
setContentView(currentView);
findViewById(R.id.imgStatus).setOnClickListener(this);
lblStatus = (TextView)findViewById(R.id.lblStatus);
imgStatus = (ImageView)findViewById(R.id.imgStatus);
updateStatus();
}
private void showWeb (String url)
{
currentView =R.layout.layout_web;
setContentView(currentView);
mWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
webSettings.setJavaScriptEnabled(true);
mWebView.setWebChromeClient(new MyWebChromeClient());
mWebView.loadUrl(url);
}
/*
* Show the message log UI
*/
private void showMessageLog ()
{
currentView = R.layout.layout_log;
setContentView(currentView);
((Button)findViewById(R.id.btnLogClear)).setOnClickListener(this);
updateStatus = false;
updateLog = true;
Thread thread = new Thread ()
{
public void run ()
{
while (updateLog)
{
try {
Thread.sleep(UPDATE_TIMEOUT);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}
};
thread.start();
}
/*
* Load the Tor log and display it in a text field
*/
private void updateMessageLog ()
{
TextView tvLog = (TextView)findViewById(R.id.messageLog);
if (tvLog != null)
{
String output = loadTextFile(TOR_LOG_PATH);
tvLog.setText(output);
}
}
/*
* Handle to reload Tor debug log every few seconds while viewing it
*/
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
updateMessageLog ();
}
};
/*
* Handle to reload Tor debug log every few seconds while viewing it
*/
private Handler handlerStatus = new Handler() {
@Override
public void handleMessage(Message msg) {
updateStatus();
// Toast.makeText(this,txtStatus, Toast.LENGTH_SHORT).show();
}
};
/*
* Load the basic settings application to display torrc
* TODO: these needs to be improved into an actual form GUI
*/
private void showSettings ()
{
updateStatus = false;
updateLog = false;
currentView = R.layout.layout_settings;
setContentView(currentView);
String output = loadTextFile(TORRC_INSTALL_PATH);
TextView tvSettings = (TextView)findViewById(R.id.textSettings);
((Button)findViewById(R.id.btnSettingsSave)).setOnClickListener(this);
tvSettings.setText(output);
}
/*
* Set the state of the running/not running graphic and label
*/
public void updateStatus ()
{
if (imgStatus != null)
{
if (torStatus == STATUS_ON)
{
imgStatus.setImageResource(R.drawable.toron);
lblStatus.setText("ORbot is running\n- touch the bot to stop -");
updateStatus = false;
}
else if (torStatus == STATUS_STARTING_UP)
{
imgStatus.setImageResource(R.drawable.torstarting);
lblStatus.setText("ORbot reports:\n\"" + txtStatus + "\"");
}
else if (torStatus == STATUS_SHUTTING_DOWN)
{
imgStatus.setImageResource(R.drawable.torstopping);
lblStatus.setText("ORbot is shutting down\nplease wait...");
}
else
{
imgStatus.setImageResource(R.drawable.toroff);
lblStatus.setText("ORbot is not running\n- touch the bot to start -");
updateStatus = false;
}
}
}
/*
* (non-Javadoc)
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
public void onClick(View view) {
// the start button
if (view.getId()==R.id.imgStatus)
{
//if Tor binary is not running, then start the service up
if (TorService.getStatus()==STATUS_OFF)
{
torStatus = STATUS_STARTING_UP;
txtStatus = "Connecting to Tor...";
updateStatus();
startTorService ();
}
else
{
torStatus = STATUS_SHUTTING_DOWN;
updateStatus();
stopService(torService);
torStatus = STATUS_OFF;
updateStatus();
}
}
else if (view.getId()==R.id.btnLogClear)
{
saveTextFile(TOR_LOG_PATH,"");
}
else if (view.getId()==R.id.btnSettingsSave)
{
TextView tvSettings = (TextView)findViewById(R.id.textSettings);
String newSettings = tvSettings.getText().toString();
saveTextFile(TORRC_INSTALL_PATH, newSettings);
}
}
private void startTorService ()
{
if (torService == null)
{
torService = new Intent(this, TorService.class);
//torService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
TorService.setActivity(this);
}
startService(torService);
}
/*
* Load the log file text
*/
public static String loadTextFile (String path)
{
String line = null;
StringBuffer out = new StringBuffer();
try {
BufferedReader reader = new BufferedReader((new FileReader(new File(path))));
while ((line = reader.readLine()) != null)
{
out.append(line);
out.append('\n');
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return out.toString();
}
/*
* Load the log file text
*/
public static boolean saveTextFile (String path, String contents)
{
try {
FileWriter writer = new FileWriter( path, false );
writer.write( contents );
writer.close();
return true;
} catch (IOException e) {
// Log.i(TAG, "error writing file: " + path, e);
e.printStackTrace();
return false;
}
}
@Override
public void bandwidthUsed(long read, long written) {
Log.i(TAG,"BW Used: read=" + read + " written=" + written);
}
@Override
public void circuitStatus(String status, String circID, String path) {
Log.i(TAG,"CircuitStatus=" + status + ": " + circID);
}
@Override
public void message(String severity, String msg) {
// Log.println(priority, tag, msg)("["+severity+"] "+msg);
//Toast.makeText(, text, duration)
// Toast.makeText(ACTIVITY, severity + ": " + msg, Toast.LENGTH_SHORT);
Log.i(TAG, "[Tor Control Port] " + severity + ": " + msg);
if (msg.indexOf(TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)!=-1)
{
torStatus = STATUS_ON;
//setupWebProxy(true);
}
txtStatus = msg;
handlerStatus.sendEmptyMessage(0);
}
@Override
public void newDescriptors(List<String> orList) {
// TODO Auto-generated method stub
}
@Override
public void orConnStatus(String status, String orName) {
Log.i(TAG,"OrConnStatus=" + status + ": " + orName);
}
@Override
public void streamStatus(String status, String streamID, String target) {
Log.i(TAG,"StreamStatus=" + status + ": " + streamID);
}
@Override
public void unrecognized(String type, String msg) {
Log.i(TAG,"unrecognized log=" + type + ": " + msg);
}
/**
* Provides a hook for calling "alert" from javascript. Useful for
* debugging your javascript.
*/
final class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(TAG, message);
result.confirm();
return true;
}
}
}

View File

@ -1,617 +0,0 @@
/* 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");
}
}

View File

@ -0,0 +1,73 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / 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.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Utils {
/*
* Load the log file text
*/
public static String loadTextFile (String path)
{
String line = null;
StringBuffer out = new StringBuffer();
try {
BufferedReader reader = new BufferedReader((new FileReader(new File(path))));
while ((line = reader.readLine()) != null)
{
out.append(line);
out.append('\n');
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return out.toString();
}
/*
* Load the log file text
*/
public static boolean saveTextFile (String path, String contents)
{
try {
FileWriter writer = new FileWriter( path, false );
writer.write( contents );
writer.close();
return true;
} catch (IOException e) {
// Log.i(TAG, "error writing file: " + path, e);
e.printStackTrace();
return false;
}
}
}

View File

@ -1,28 +1,45 @@
/* Copyright (c) 2009, Nathan Freitas, The Guardian Project - http://openideals.com/guardian */
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
/** SOCKS aware echo client*/
package org.torproject.android;
package org.torproject.android.net;
import java.io.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import net.sourceforge.jsocks.socks.*;
import net.sourceforge.jsocks.socks.InetRange;
import net.sourceforge.jsocks.socks.Proxy;
import net.sourceforge.jsocks.socks.SocksException;
import net.sourceforge.jsocks.socks.SocksSocket;
import org.torproject.android.TorConstants;
import android.util.Log;
public class SocksClient implements Runnable {
@SuppressWarnings("unused")
private int port;
@SuppressWarnings("unused")
private InetAddress hostIP;
private Socket ss;
private InputStream in;
private OutputStream out;
private static final int BUF_SIZE = 1024;
@SuppressWarnings("unused")
private static final int BUF_SIZE = 1024;
private static final String IP_LOCALHOST = "127.0.0.1";
public SocksClient(String host,int port)
throws IOException,UnknownHostException,SocksException{
this.port = port;
@ -30,9 +47,9 @@ public class SocksClient implements Runnable {
ss = new SocksSocket(host, port);
out = ss.getOutputStream();
in = ss.getInputStream();
System.out.println("Connected...");
System.out.println("TO: "+host+":"+port);
System.out.println("ViaProxy: "+ss.getLocalAddress().getHostAddress()
Log.i(getClass().getName(),"Connected...");
Log.i(getClass().getName(),"TO: "+host+":"+port);
Log.i(getClass().getName(),"ViaProxy: "+ss.getLocalAddress().getHostAddress()
+":"+ss.getLocalPort());
}
@ -75,11 +92,11 @@ public class SocksClient implements Runnable {
port = Integer.parseInt(args[1]);
proxyPort =(args.length > 3)? Integer.parseInt(args[3])
: TorConstants.PORT_SOCKS;
: 9050;
host = args[0];
proxyHost =(args.length > 2)? args[2]
: TorConstants.IP_LOCALHOST;
: IP_LOCALHOST;
Proxy.setDefaultProxy(proxyHost,proxyPort,"KOUKY001");
//Proxy.setDefaultProxy(proxyHost,proxyPort);

View File

@ -0,0 +1,24 @@
package org.torproject.android.service;
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.
*/
void registerCallback(ITorServiceCallback cb);
/**
* Remove a previously registered callback interface.
*/
void unregisterCallback(ITorServiceCallback cb);
int getStatus();
void setProfile(int profile);
}

View File

@ -0,0 +1,13 @@
package org.torproject.android.service;
/**
* Callback interface used to send
* synchronous notifications back to its clients. Note that this is a
* one-way interface so the server does not block waiting for the client.
*/
oneway interface ITorServiceCallback {
/**
* Called when the service has a new value for you.
*/
void statusChanged(String value);
}

View File

@ -1,7 +1,7 @@
/* Copyright (c) 2009, Nathan Freitas, The Guardian Project - http://openideals.com/guardian */
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android;
package org.torproject.android.service;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@ -14,9 +14,7 @@ import java.util.zip.ZipFile;
import android.util.Log;
public class TorBinaryInstaller implements TorConstants {
private final static String LOG_TAG = "Tor";
public class TorBinaryInstaller implements TorServiceConstants {
public TorBinaryInstaller ()
@ -28,11 +26,13 @@ public class TorBinaryInstaller implements TorConstants {
*/
public void start (boolean force)
{
boolean binaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
boolean torBinaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
Log.i(TAG,"Tor binary exists=" + torBinaryExists);
Log.i(LOG_TAG,"Tor binary exists=" + binaryExists);
boolean privoxyBinaryExists = new File(PRIVOXY_INSTALL_PATH).exists();
Log.i(TAG,"Privoxy binary exists=" + privoxyBinaryExists);
if (!binaryExists || force)
if (!(torBinaryExists && privoxyBinaryExists) || force)
installFromZip ();
}
@ -53,14 +53,21 @@ public class TorBinaryInstaller implements TorConstants {
zipen = zip.getEntry(TORRC_ZIP_KEY);
streamToFile(zip.getInputStream(zipen),TORRC_INSTALL_PATH);
zipen = zip.getEntry(PRIVOXY_ZIP_KEY);
streamToFile(zip.getInputStream(zipen),PRIVOXY_INSTALL_PATH);
zipen = zip.getEntry(PRIVOXYCONFIG_ZIP_KEY);
streamToFile(zip.getInputStream(zipen),PRIVOXYCONFIG_INSTALL_PATH);
zip.close();
Log.i(LOG_TAG,"SUCCESS: unzipped tor binary from apk");
Log.i(TAG,"SUCCESS: unzipped tor, privoxy binaries from apk");
}
catch (IOException ioe)
{
Log.i(LOG_TAG,"FAIL: unable to unzip tor binary from apk",ioe);
Log.i(TAG,"FAIL: unable to unzip binaries from apk",ioe);
}
}
@ -91,7 +98,7 @@ public class TorBinaryInstaller implements TorConstants {
{
Log.i(LOG_TAG,"Error opening output file " + targetFilename,e);
Log.i(TAG,"Error opening output file " + targetFilename,e);
return;
}
@ -118,7 +125,7 @@ public class TorBinaryInstaller implements TorConstants {
{
Log.i(LOG_TAG,"Error writing output file '" + targetFilename + "': " + e.toString());
Log.i(TAG,"Error writing output file '" + targetFilename + "': " + e.toString());
return;
@ -135,12 +142,15 @@ public class TorBinaryInstaller implements TorConstants {
DataOutputStream out = new DataOutputStream(new FileOutputStream(outputFile));
DataInputStream in = new DataInputStream(is);
int b;
int b = -1;
byte[] data = new byte[1024];
while ((b = in.read(data)) != -1) {
out.write(data);
}
if (b == -1); //rejoice
//
out.flush();
out.close();
@ -150,7 +160,7 @@ public class TorBinaryInstaller implements TorConstants {
} catch (IOException ex) {
Log.e(LOG_TAG, "error copying binary", ex);
Log.e(TAG, "error copying binary", ex);
}
}

View File

@ -0,0 +1,228 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android.service;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import android.util.Log;
/**
* Contains shared programming interfaces.
* All iptables "communication" is handled by this class.
*/
public final class TorRoot {
private final static String TAG = "TOR_ROOT";
// Do we have root access?
private static boolean hasroot = false;
private final static String CMD_NAT_FLUSH = "iptables -t nat -F || exit\n";
private final static String CMD_NAT_IPTABLES_80 = "iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to 127.0.0.1:8118 || exit\n";
private final static String CMD_DNS_PROXYING = "iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to 127.0.0.1:5400 || exit\n";
public static boolean enableDNSProxying ()
{
final StringBuilder script = new StringBuilder();
int code;
//Enable UDP Proxying
script.append(CMD_DNS_PROXYING);
StringBuilder res = new StringBuilder();
try
{
code = runScriptAsRoot(script.toString(), res);
if (code != 0)
{
Log.w(TAG, "error apply DNS proxying: " + res.toString());
}
} catch (Exception e) {
Log.w(TAG, "error apply DNS proxying: " + res.toString(), e);
return false;
}
return true;
}
/**
* Purge and re-add all rules (internal implementation).
* @param ctx application context (mandatory)
* @param uids list of selected uids to allow or disallow (depending on the working mode)
* @param showErrors indicates if errors should be alerted
*/
public static boolean enabledWebProxying() {
final StringBuilder script = new StringBuilder();
try {
int code;
script.append(CMD_NAT_IPTABLES_80);
/*
int uid = android.os.Process.getUidForName("dhcp");
if (uid != -1) script.append("iptables -A OUTPUT " + itfFilter + " -m owner --uid-owner " + uid + " -j ACCEPT || exit\n");
uid = android.os.Process.getUidForName("wifi");
if (uid != -1) script.append("iptables -A OUTPUT " + itfFilter + " -m owner --uid-owner " + uid + " -j ACCEPT || exit\n");
*/
StringBuilder res = new StringBuilder();
code = runScriptAsRoot(script.toString(), res);
String msg = res.toString();
Log.e(TAG, msg);
} catch (Exception e) {
Log.w(TAG, "error refreshing iptables: " + e);
}
return false;
}
/**
* Purge all iptables rules.
* @return true if the rules were purged
*/
public static boolean purgeNatIptables() {
StringBuilder res = new StringBuilder();
try {
int code = runScriptAsRoot(CMD_NAT_FLUSH, res);
if (code != 0) {
Log.w(TAG, "error purging iptables. exit code: " + code + "\n" + res);
return false;
}
return true;
} catch (Exception e) {
Log.w(TAG,"error purging iptables: " + e);
return false;
}
}
/**
* Check if we have root access
* @return boolean true if we have root
*/
public static boolean hasRootAccess() {
if (hasroot) return true;
try {
// Run an empty script just to check root access
if (runScriptAsRoot("exit 0", null, 20000) == 0) {
hasroot = true;
return true;
}
} catch (Exception e) {
}
Log.w(TAG, "Could not acquire root access.");
return false;
}
/**
* Runs a script as root (multiple commands separated by "\n").
*
* @param script the script to be executed
* @param res the script output response (stdout + stderr)
* @param timeout timeout in milliseconds (-1 for none)
* @return the script exit code
*/
public static int runScriptAsRoot(String script, StringBuilder res, final long timeout) {
Log.i(TAG,"executing script: " + script);
final ScriptRunner runner = new ScriptRunner(script, res);
runner.start();
try {
if (timeout > 0) {
runner.join(timeout);
} else {
runner.join();
}
if (runner.isAlive()) {
// Timed-out
runner.interrupt();
runner.destroy();
runner.join(50);
}
} catch (InterruptedException ex) {}
return runner.exitcode;
}
/**
* Runs a script as root (multiple commands separated by "\n") with a default timeout of 5 seconds.
*
* @param script the script to be executed
* @param res the script output response (stdout + stderr)
* @param timeout timeout in milliseconds (-1 for none)
* @return the script exit code
* @throws IOException on any error executing the script, or writing it to disk
*/
public static int runScriptAsRoot(String script, StringBuilder res) throws IOException {
return runScriptAsRoot(script, res, 5000);
}
/**
* Internal thread used to execute scripts as root.
*/
private static final class ScriptRunner extends Thread {
private final String script;
private final StringBuilder res;
public int exitcode = -1;
private Process exec;
/**
* Creates a new script runner.
* @param script script to run
* @param res response output
*/
public ScriptRunner(String script, StringBuilder res) {
this.script = script;
this.res = res;
}
@Override
public void run() {
try {
// Create the "su" request to run the command
// note that this will create a shell that we must interact to (using stdin/stdout)
exec = Runtime.getRuntime().exec("su");
final OutputStreamWriter out = new OutputStreamWriter(exec.getOutputStream());
// Write the script to be executed
out.write(script);
// Ensure that the last character is an "enter"
if (!script.endsWith("\n")) out.write("\n");
out.flush();
// Terminate the "su" process
out.write("exit\n");
out.flush();
final char buf[] = new char[1024];
// Consume the "stdout"
InputStreamReader r = new InputStreamReader(exec.getInputStream());
int read=0;
while ((read=r.read(buf)) != -1) {
if (res != null) res.append(buf, 0, read);
}
// Consume the "stderr"
r = new InputStreamReader(exec.getErrorStream());
read=0;
while ((read=r.read(buf)) != -1) {
if (res != null) res.append(buf, 0, read);
}
// get the process exit code
if (exec != null) this.exitcode = exec.waitFor();
} catch (InterruptedException ex) {
if (res != null) res.append("\nOperation timed-out");
} catch (Exception ex) {
if (res != null) res.append("\n" + ex);
} finally {
destroy();
}
}
/**
* Destroy this script runner
*/
public synchronized void destroy() {
if (exec != null) exec.destroy();
exec = null;
}
}
}

View File

@ -0,0 +1,721 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import net.freehaven.tor.control.ConfigEntry;
import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.TorControlConnection;
import org.torproject.android.Orbot;
import org.torproject.android.R;
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.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
public class TorService extends Service implements TorServiceConstants, Runnable, EventHandler
{
private static int currentStatus = STATUS_REQUIRES_DEMAND;
private TorControlConnection conn = null;
private static TorService _torInstance;
private static final int NOTIFY_ID = 1;
/** Called when the activity is first created. */
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"TorService: onCreate");
checkTorBinaries();
findExistingProc ();
_torInstance = this;
}
private boolean findExistingProc ()
{
int procId = TorServiceUtils.findProcessId(TorServiceConstants.TOR_BINARY_INSTALL_PATH);
if (procId != -1)
{
Log.i(TAG,"Found existing Tor process");
sendCallbackMessage ("found existing Tor process...");
try {
currentStatus = STATUS_CONNECTING;
initControlConnection();
currentStatus = STATUS_ON;
return true;
} catch (RuntimeException e) {
Log.i(TAG,"Unable to connect to existing Tor instance,",e);
currentStatus = STATUS_REQUIRES_DEMAND;
this.stopTor();
} catch (Exception e) {
Log.i(TAG,"Unable to connect to existing Tor instance,",e);
currentStatus = STATUS_REQUIRES_DEMAND;
this.stopTor();
}
}
return false;
}
/* (non-Javadoc)
* @see android.app.Service#onLowMemory()
*/
@Override
public void onLowMemory() {
super.onLowMemory();
Log.i(TAG, "Low Memory Called");
}
/* (non-Javadoc)
* @see android.app.Service#onUnbind(android.content.Intent)
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind Called: " + intent.getAction());
return super.onUnbind(intent);
}
public int getTorStatus ()
{
return currentStatus;
}
private void showToolbarNotification (String title, String notifyMsg, int icon)
{
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence tickerText = title;
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
Context context = getApplicationContext();
CharSequence contentTitle = title;
CharSequence contentText = notifyMsg;
Intent notificationIntent = new Intent(this, Orbot.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
mNotificationManager.notify(NOTIFY_ID, notification);
}
/* (non-Javadoc)
* @see android.app.Service#onRebind(android.content.Intent)
*/
@Override
public void onRebind(Intent intent) {
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) {
super.onStart(intent, startId);
/*
if (currentStatus == STATUS_ON && conn != null && webProxy != null)
{
//we are good to go
Log.i(TAG,"onStart: Tor is running");
}
else
{
Log.i(TAG,"onStart: Starting up Tor");
new Thread(this).start();
}
*/
}
public void run ()
{
boolean isRunning = _torInstance.findExistingProc ();
if (!isRunning)
{
try
{
initTor();
}
catch (Exception e)
{
currentStatus = STATUS_REQUIRES_DEMAND;
this.showToolbarNotification("Orbot", "Unable to start Tor", R.drawable.tornotification);
Log.i(TAG,"Unable to start Tor: " + e.getMessage(),e);
}
}
}
public void onDestroy ()
{
super.onDestroy();
// Unregister all callbacks.
mCallbacks.kill();
Log.i(TAG,"onDestroy called");
stopTor();
}
private void stopTor ()
{
currentStatus = STATUS_UNAVAILABLE;
sendCallbackMessage("Web proxy shutdown");
killTorProcess ();
currentStatus = STATUS_REQUIRES_DEMAND;
showToolbarNotification ("Orbot","Anonymous browsing is disabled",R.drawable.tornotificationoff);
sendCallbackMessage("Anonymous browsing is disabled");
}
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 (Exception e) {
Log.i(TAG,"error shutting down Tor via connection",e);
}
conn = null;
}
int procId = TorServiceUtils.findProcessId(TorServiceConstants.TOR_BINARY_INSTALL_PATH);
while (procId != -1)
{
Log.i(TAG,"Found Tor PID=" + procId + " - killing now...");
TorServiceUtils.doCommand(SHELL_CMD_KILL, procId + "");
procId = TorServiceUtils.findProcessId(TorServiceConstants.TOR_BINARY_INSTALL_PATH);
}
procId = TorServiceUtils.findProcessId(TorServiceConstants.PRIVOXY_INSTALL_PATH);
while (procId != -1)
{
Log.i(TAG,"Found Privoxy PID=" + procId + " - killing now...");
TorServiceUtils.doCommand(SHELL_CMD_KILL, procId + "");
procId = TorServiceUtils.findProcessId(TorServiceConstants.PRIVOXY_INSTALL_PATH);
}
}
private void logNotice (String msg)
{
Log.i(TAG, msg);
}
private boolean checkTorBinaries ()
{
boolean torBinaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
boolean privoxyBinaryExists = new File(PRIVOXY_INSTALL_PATH).exists();
if (!(torBinaryExists && privoxyBinaryExists))
{
killTorProcess ();
TorBinaryInstaller installer = new TorBinaryInstaller();
installer.start(true);
torBinaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
privoxyBinaryExists = new File(PRIVOXY_INSTALL_PATH).exists();
if (torBinaryExists && privoxyBinaryExists)
{
logNotice("Tor, Privoxy, IPtables binaries installed!");
this.showToolbarNotification("Orbot Installed!", "The Tor binary was successfully extracted and installed", R.drawable.tornotification);
}
else
{
logNotice("Binary install FAILED!");
this.showToolbarNotification("Orbot FAIL!", "The binaries were unable to be installed", R.drawable.tornotification);
return false;
}
}
Log.i(TAG,"Setting permission on Tor binary");
TorServiceUtils.doCommand(SHELL_CMD_CHMOD, CHMOD_EXE_VALUE + ' ' + TOR_BINARY_INSTALL_PATH);
Log.i(TAG,"Setting permission on Privoxy binary");
TorServiceUtils.doCommand(SHELL_CMD_CHMOD, CHMOD_EXE_VALUE + ' ' + PRIVOXY_INSTALL_PATH);
return true;
}
public void initTor () throws Exception
{
currentStatus = STATUS_CONNECTING;
logNotice("Tor is starting up...");
this.sendCallbackMessage("starting...");
killTorProcess ();
checkTorBinaries ();
Log.i(TAG,"Starting tor process");
TorServiceUtils.doCommand(TOR_BINARY_INSTALL_PATH, TOR_COMMAND_LINE_ARGS);
int procId = TorServiceUtils.findProcessId(TorServiceConstants.TOR_BINARY_INSTALL_PATH);
while (procId == -1)
{
TorServiceUtils.doCommand(TOR_BINARY_INSTALL_PATH, TOR_COMMAND_LINE_ARGS);
procId = TorServiceUtils.findProcessId(TorServiceConstants.TOR_BINARY_INSTALL_PATH);
if (procId == -1)
{
this.sendCallbackMessage("Couldn't start Tor process... retrying...");
Thread.sleep(3000);
}
}
Log.i(TAG,"Tor process id=" + procId);
showToolbarNotification("Orbot starting...", "Orbot is starting up", R.drawable.tornotification);
initControlConnection ();
int privoxyProcId = TorServiceUtils.findProcessId(TorServiceConstants.PRIVOXY_INSTALL_PATH);
while (privoxyProcId == -1)
{
TorServiceUtils.doCommand(PRIVOXY_INSTALL_PATH, PRIVOXY_COMMAND_LINE_ARGS);
privoxyProcId = TorServiceUtils.findProcessId(TorServiceConstants.PRIVOXY_INSTALL_PATH);
if (privoxyProcId == -1)
{
this.sendCallbackMessage("Couldn't start Privoxy process... retrying...");
Thread.sleep(3000);
}
}
Log.i(TAG,"Privoxy process id=" + privoxyProcId);
}
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
{
while (true)
{
try
{
Log.i(TAG,"Connecting to control port: " + TOR_CONTROL_PORT);
String baseMessage = getString(R.string.tor_process_connecting);
sendCallbackMessage(baseMessage);
Socket s = new Socket(IP_LOCALHOST, TOR_CONTROL_PORT);
conn = TorControlConnection.getConnection(s);
// conn.authenticate(new byte[0]); // See section 3.2
sendCallbackMessage(baseMessage + ' ' + getString(R.string.tor_process_connecting_step2));
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");
sendCallbackMessage(baseMessage + ' ' + getString(R.string.tor_process_connecting_step3));
addEventHandler();
break; //don't need to retry
}
catch (Exception ce)
{
conn = null;
Log.i(TAG,"Attempt: Error connecting to control port: " + ce.getLocalizedMessage(),ce);
sendCallbackMessage(getString(R.string.tor_process_connecting_step4));
Thread.sleep(1000);
}
}
}
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
{
try
{
if (conn != null)
{
// get a single value.
// get several values
if (currentStatus == STATUS_CONNECTING)
{
//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);
}
else
{
// String status = conn.getInfo("status/circuit-established");
// Log.i(TAG, "status/circuit-established=" + status);
}
}
}
catch (Exception e)
{
Log.i(TAG, "Unable to get Tor status from control port");
currentStatus = STATUS_UNAVAILABLE;
}
}*/
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(this);
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");
}
/**
* Returns the port number that the HTTP proxy is running on
*/
public int getHTTPPort() throws RemoteException {
return TorServiceConstants.PORT_HTTP;
}
/**
* Returns the port number that the SOCKS proxy is running on
*/
public int getSOCKSPort() throws RemoteException {
return TorServiceConstants.PORT_SOCKS;
}
public int getProfile() throws RemoteException {
//return mProfile;
return PROFILE_ON;
}
public void setTorProfile(int profile) {
if (profile == PROFILE_ON)
{
currentStatus = STATUS_CONNECTING;
sendCallbackMessage ("starting...");
new Thread(_torInstance).start();
}
else
{
currentStatus = STATUS_UNAVAILABLE;
sendCallbackMessage ("shutting down...");
_torInstance.stopTor();
}
}
@Override
public void message(String severity, String msg) {
Log.i(TAG, "[Tor Control Port] " + severity + ": " + msg);
if (msg.indexOf(TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)!=-1)
{
currentStatus = STATUS_ON;
showToolbarNotification ("Orbot","Anonymous browsing is enabled",R.drawable.tornotification);
}
else
{
showToolbarNotification("Orbot", msg, R.drawable.tornotification);
}
sendCallbackMessage (msg);
}
@Override
public void newDescriptors(List<String> orList) {
}
@Override
public void orConnStatus(String status, String orName) {
Log.i(TAG,"OrConnStatus=" + status + ": " + orName);
}
@Override
public void streamStatus(String status, String streamID, String target) {
Log.i(TAG,"StreamStatus=" + status + ": " + streamID);
}
@Override
public void unrecognized(String type, String msg) {
Log.i(TAG,"unrecognized log=" + type + ": " + msg);
}
@Override
public void bandwidthUsed(long read, long written) {
}
@Override
public void circuitStatus(String status, String circID, String path) {
}
@Override
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
// the Intent.
if (ITorService.class.getName().equals(intent.getAction())) {
return mBinder;
}
return null;
}
/**
* This is a list of callbacks that have been registered with the
* service. Note that this is package scoped (instead of private) so
* that it can be accessed more efficiently from inner classes.
*/
final RemoteCallbackList<ITorServiceCallback> mCallbacks
= new RemoteCallbackList<ITorServiceCallback>();
/**
* 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);
}
public void unregisterCallback(ITorServiceCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
public int getStatus () {
return getTorStatus();
}
public void setProfile (int profile)
{
setTorProfile(profile);
}
};
private void sendCallbackMessage (String status)
{
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).statusChanged(status);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
}
}

View File

@ -0,0 +1,87 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android.service;
public interface TorServiceConstants {
public final static String TAG = "TOR_SERVICE";
//home directory of Android application
public final static String TOR_HOME = "/data/data/org.torproject.android/";
//name of the tor C binary
public final static String TOR_BINARY_ASSET_KEY = "tor";
public final static String TOR_BINARY_INSTALL_PATH = TOR_HOME + TOR_BINARY_ASSET_KEY; //path to install the Tor binary too
public final static String TOR_BINARY_ZIP_KEY = "assets/" + TOR_BINARY_ASSET_KEY;//key of the tor binary in the Zip file
//torrc (tor config file)
public final static String TORRC_ASSET_KEY = "torrc";
public final static String TORRC_INSTALL_PATH = TOR_HOME + TORRC_ASSET_KEY; //path to install torrc to within the android app data folder
public final static String TORRC_ZIP_KEY = "assets/" + TORRC_ASSET_KEY; //key of the torrc file in the Zip file
//how to launch tor
public final static String TOR_COMMAND_LINE_ARGS = "-f " + TORRC_INSTALL_PATH;
//privoxy
public final static String PRIVOXY_ASSET_KEY = "privoxy";
public final static String PRIVOXY_INSTALL_PATH = TOR_HOME + PRIVOXY_ASSET_KEY; //path to install privoxy to within the android app data folder
public final static String PRIVOXY_ZIP_KEY = "assets/" + PRIVOXY_ASSET_KEY; //key of the privoxy file in the Zip file
//privoxy.config
public final static String PRIVOXYCONFIG_ASSET_KEY = "privoxy.config";
public final static String PRIVOXYCONFIG_INSTALL_PATH = TOR_HOME + PRIVOXYCONFIG_ASSET_KEY; //path to install privoxy to within the android app data folder
public final static String PRIVOXYCONFIG_ZIP_KEY = "assets/" + PRIVOXYCONFIG_ASSET_KEY; //key of the privoxy file in the Zip file
//how to launch privoxy
public final static String PRIVOXY_COMMAND_LINE_ARGS = ' ' + PRIVOXYCONFIG_INSTALL_PATH;
//where to send the notices log
public final static String TOR_LOG_PATH = TOR_HOME + "notices.log";
//control port cookie path
public final static String TOR_CONTROL_AUTH_COOKIE = TOR_HOME + "data/control_auth_cookie";
//various console cmds
public final static String SHELL_CMD_CHMOD = "chmod";
public final static String SHELL_CMD_KILL = "kill";
public final static String SHELL_CMD_RM = "rm";
public final static String SHELL_CMD_PS = "ps";
public final static String CHMOD_EXE_VALUE = "777";
//path of the installed APK file
public final static String APK_PATH = "/data/app/org.torproject.android.apk";
public final static int FILE_WRITE_BUFFER_SIZE = 2048;
//HTTP Proxy server port
public final static int PORT_HTTP = 8118; //just like Privoxy!
//Socks port client connects to, server is the Tor binary
public final static int PORT_SOCKS = 9050;
//what is says!
public final static String IP_LOCALHOST = "127.0.0.1";
public final static int TOR_CONTROL_PORT = 9051;
public final static int UPDATE_TIMEOUT = 1000;
//path to check Tor against
public final static String URL_TOR_CHECK = "http://check.torproject.org";
//IPTABLES
// public final static String CMD_IPTABLES_PREROUTING = "iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to 127.0.0.1:8118 || exit\n";
//public final static String CMD_IPTABLES_PREROUTING_FLUSH = "iptables -t nat -F || exit\n";
//control port
public final static String TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE = "Bootstrapped 100%";
public final static int STATUS_UNAVAILABLE = -1;
public final static int STATUS_REQUIRES_DEMAND = 0;
public final static int STATUS_ON = 1;
public final static int STATUS_CONNECTING = 2;
public final static int PROFILE_OFF = -1;
public final static int PROFILE_ONDEMAND = 0;
public final static int PROFILE_ON = 1;
}

View File

@ -0,0 +1,153 @@
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
/* See LICENSE for licensing information */
package org.torproject.android.service;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;
import android.util.Log;
public class TorServiceUtils implements TorServiceConstants {
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)
{
Log.i(TAG,"executing command: " + command + ' ' + arg1);
Runtime r = Runtime.getRuntime();
Process proc = null;
StringBuilder log = new StringBuilder();
try {
proc = r.exec(command + ' ' + arg1);
/*
final char buf[] = new char[1024];
// Consume the "stdout"
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
int read=0;
while ((read=reader.read(buf)) != -1) {
if (log != null) log.append(buf, 0, read);
}
// Consume the "stderr"
reader = new InputStreamReader(proc.getErrorStream());
read=0;
while ((read=reader.read(buf)) != -1) {
if (log != null) log.append(buf, 0, read);
}*/
Log.i(TAG,"command process exit value: " + proc.exitValue());
Log.i(TAG, "shell cmd output: " + log.toString());
} catch (Exception e) {
Log.e(TAG, "error: " + e.getMessage());
Log.e(TAG, "shell cmd output: " + log.toString());
}
return proc;
}
public static boolean hasRoot ()
{
String[] cmds = {"exit 0"};
int code = doRootCommand(cmds,null);
return (code == 0);
}
public static int doRootCommand(String[] cmds, StringBuilder log)
{
Log.i(TAG,"executing commands: " + cmds.length);
Runtime runtime = Runtime.getRuntime();
Process proc = null;
int exitCode = -1;
try {
proc = runtime.exec(cmds[0]);
OutputStreamWriter out = new OutputStreamWriter(proc.getOutputStream());
for (int i = 1; i < cmds.length; i++)
{
out.write(cmds[i]);
out.write("\n");
}
out.flush();
out.write("exit\n");
final char buf[] = new char[1024];
// Consume the "stdout"
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
int read=0;
while ((read=reader.read(buf)) != -1) {
if (log != null) log.append(buf, 0, read);
}
// Consume the "stderr"
reader = new InputStreamReader(proc.getErrorStream());
read=0;
while ((read=reader.read(buf)) != -1) {
if (log != null) log.append(buf, 0, read);
}
exitCode = proc.waitFor();
Log.i(TAG,"command process exit value: " + exitCode);
} catch (Exception e) {
Log.e(TAG, "Error executing shell cmd: " + e.getMessage(),e);
}
return exitCode;
}
}