[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [orbot/master] moved API to IptablesManager; made version display dynamic
commit f86897f00c8fcd00e6f51e27ed7564e929333181
Author: Nathan Freitas <nathan@xxxxxxxxxxx>
Date: Mon Jul 25 10:51:34 2011 -0400
moved API to IptablesManager; made version display dynamic
---
src/org/torproject/android/Orbot.java | 29 +-
src/org/torproject/android/service/Api.java | 1058 --------------------
.../android/service/IptablesManager.java | 1058 ++++++++++++++++++++
src/org/torproject/android/service/TorService.java | 8 +-
4 files changed, 1079 insertions(+), 1074 deletions(-)
diff --git a/src/org/torproject/android/Orbot.java b/src/org/torproject/android/Orbot.java
index 7374822..0f162af 100644
--- a/src/org/torproject/android/Orbot.java
+++ b/src/org/torproject/android/Orbot.java
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -136,16 +137,24 @@ public class Orbot extends Activity implements OnLongClickListener, TorConstants
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.button_about))
- .setView(view)
- .show();
+ LayoutInflater li = LayoutInflater.from(this);
+ View view = li.inflate(R.layout.layout_about, null);
+
+ String version = "";
+
+ try {
+ version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
+ } catch (NameNotFoundException e) {
+ version = "Version Not Found";
+ }
+
+ TextView versionName = (TextView)view.findViewById(R.id.versionName);
+ versionName.setText(version);
+
+ new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.button_about))
+ .setView(view)
+ .show();
}
/* When a menu item is selected launch the appropriate view or activity
diff --git a/src/org/torproject/android/service/Api.java b/src/org/torproject/android/service/Api.java
deleted file mode 100644
index fdd357b..0000000
--- a/src/org/torproject/android/service/Api.java
+++ /dev/null
@@ -1,1058 +0,0 @@
-/**
- * Contains shared programming interfaces.
- * All iptables "communication" is handled by this class.
- *
- * Copyright (C) 2009-2010 Rodrigo Zechin Rosauro
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Rodrigo Zechin Rosauro
- * @version 1.0
- */
-
-package org.torproject.android.service;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.StringReader;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.StringTokenizer;
-
-import org.torproject.android.R;
-
-import android.Manifest;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.util.Log;
-import android.widget.Toast;
-
-/**
- * Contains shared programming interfaces.
- * All iptables "communication" is handled by this class.
- */
-public final class Api {
- /** application version string */
- public static final String VERSION = "1.5.1-dev";
- /** special application UID used to indicate "any application" */
- public static final int SPECIAL_UID_ANY = -10;
- /** special application UID used to indicate the Linux Kernel */
- public static final int SPECIAL_UID_KERNEL = -11;
- /** root script filename */
- private static final String SCRIPT_FILE = "droidwall.sh";
-
- // Preferences
- public static final String PREFS_NAME = "DroidWallPrefs";
- public static final String PREF_3G_UIDS = "AllowedUids3G";
- public static final String PREF_WIFI_UIDS = "AllowedUidsWifi";
- public static final String PREF_PASSWORD = "Password";
- public static final String PREF_MODE = "BlockMode";
- public static final String PREF_ENABLED = "Enabled";
- public static final String PREF_LOGENABLED = "LogEnabled";
- // Modes
- public static final String MODE_WHITELIST = "whitelist";
- public static final String MODE_BLACKLIST = "blacklist";
- // Messages
- public static final String STATUS_CHANGED_MSG = "com.googlecode.droidwall.intent.action.STATUS_CHANGED";
- public static final String TOGGLE_REQUEST_MSG = "com.googlecode.droidwall.intent.action.TOGGLE_REQUEST";
- public static final String STATUS_EXTRA = "com.googlecode.droidwall.intent.extra.STATUS";
-
- // Cached applications
- public static DroidApp applications[] = null;
- // Do we have root access?
- private static boolean hasroot = false;
- // Flag indicating if this is an ARMv6 device (-1: unknown, 0: no, 1: yes)
- private static int isARMv6 = -1;
-
- /**
- * Display a simple alert box
- * @param ctx context
- * @param msg message
- */
- public static void alert(Context ctx, CharSequence msg) {
- if (ctx != null) {
- new AlertDialog.Builder(ctx)
- .setNeutralButton(android.R.string.ok, null)
- .setMessage(msg)
- .show();
- }
- }
- /**
- * Check if this is an ARMv6 device
- * @return true if this is ARMv6
- */
- private static boolean isARMv6() {
- if (isARMv6 == -1) {
- BufferedReader r = null;
- try {
- isARMv6 = 0;
- r = new BufferedReader(new FileReader("/proc/cpuinfo"));
- for (String line = r.readLine(); line != null; line = r.readLine()) {
- if (line.startsWith("Processor") && line.contains("ARMv6")) {
- isARMv6 = 1;
- break;
- } else if (line.startsWith("CPU architecture") && (line.contains("6TE") || line.contains("5TE"))) {
- isARMv6 = 1;
- break;
- }
- }
- } catch (Exception ex) {
- } finally {
- if (r != null) try {r.close();} catch (Exception ex) {}
- }
- }
- return (isARMv6 == 1);
- }
- /**
- * Create the generic shell script header used to determine which iptables binary to use.
- * @param ctx context
- * @return script header
- */
- private static String scriptHeader(Context ctx) {
- final String dir = ctx.getDir("bin",0).getAbsolutePath();
- final String myiptables = dir + (isARMv6() ? "/iptables_g1" : "/iptables_n1");
- return "" +
- "IPTABLES=iptables\n" +
- "BUSYBOX=busybox\n" +
- "GREP=grep\n" +
- "ECHO=echo\n" +
- "# Try to find busybox\n" +
- "if " + dir + "/busybox_g1 --help >/dev/null 2>/dev/null ; then\n" +
- " BUSYBOX="+dir+"/busybox_g1\n" +
- " GREP=\"$BUSYBOX grep\"\n" +
- " ECHO=\"$BUSYBOX echo\"\n" +
- "elif busybox --help >/dev/null 2>/dev/null ; then\n" +
- " BUSYBOX=busybox\n" +
- "elif /system/xbin/busybox --help >/dev/null 2>/dev/null ; then\n" +
- " BUSYBOX=/system/xbin/busybox\n" +
- "elif /system/bin/busybox --help >/dev/null 2>/dev/null ; then\n" +
- " BUSYBOX=/system/bin/busybox\n" +
- "fi\n" +
- "# Try to find grep\n" +
- "if ! $ECHO 1 | $GREP -q 1 >/dev/null 2>/dev/null ; then\n" +
- " if $ECHO 1 | $BUSYBOX grep -q 1 >/dev/null 2>/dev/null ; then\n" +
- " GREP=\"$BUSYBOX grep\"\n" +
- " fi\n" +
- " # Grep is absolutely required\n" +
- " if ! $ECHO 1 | $GREP -q 1 >/dev/null 2>/dev/null ; then\n" +
- " $ECHO The grep command is required. DroidWall will not work.\n" +
- " exit 1\n" +
- " fi\n" +
- "fi\n" +
- "# Try to find iptables\n" +
- "if " + myiptables + " --version >/dev/null 2>/dev/null ; then\n" +
- " IPTABLES="+myiptables+"\n" +
- "fi\n" +
- "";
- }
- /**
- * Copies a raw resource file, given its ID to the given location
- * @param ctx context
- * @param resid resource id
- * @param file destination file
- * @param mode file permissions (E.g.: "755")
- * @throws IOException on error
- * @throws InterruptedException when interrupted
- */
- private static void copyRawFile(Context ctx, int resid, File file, String mode) throws IOException, InterruptedException
- {
- final String abspath = file.getAbsolutePath();
- // Write the iptables binary
- final FileOutputStream out = new FileOutputStream(file);
- final InputStream is = ctx.getResources().openRawResource(resid);
- byte buf[] = new byte[1024];
- int len;
- while ((len = is.read(buf)) > 0) {
- out.write(buf, 0, len);
- }
- out.close();
- is.close();
- // Change the permissions
- Runtime.getRuntime().exec("chmod "+mode+" "+abspath).waitFor();
- }
- /**
- * Purge and re-add all rules (internal implementation).
- * @param ctx application context (mandatory)
- * @param uidsWifi list of selected UIDs for WIFI to allow or disallow (depending on the working mode)
- * @param uids3g list of selected UIDs for 2G/3G to allow or disallow (depending on the working mode)
- * @param showErrors indicates if errors should be alerted
- */
- private static boolean applyIptablesRulesImpl(Context ctx, List<Integer> uidsWifi, List<Integer> uids3g, boolean showErrors) {
- if (ctx == null) {
- return false;
- }
- assertBinaries(ctx, showErrors);
- final String ITFS_WIFI[] = {"tiwlan+", "wlan+", "eth+"};
- final String ITFS_3G[] = {"rmnet+","pdp+","ppp+","uwbr+","wimax+","vsnet+"};
- final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
- final boolean whitelist = prefs.getString(PREF_MODE, MODE_WHITELIST).equals(MODE_WHITELIST);
- final boolean blacklist = !whitelist;
- final boolean logenabled = ctx.getSharedPreferences(PREFS_NAME, 0).getBoolean(PREF_LOGENABLED, false);
-
- final StringBuilder script = new StringBuilder();
- try {
- int code;
- script.append(scriptHeader(ctx));
- script.append("" +
- "$IPTABLES --version || exit 1\n" +
- "# Create the droidwall chains if necessary\n" +
- "$IPTABLES -L droidwall >/dev/null 2>/dev/null || $IPTABLES --new droidwall || exit 2\n" +
- "$IPTABLES -L droidwall-3g >/dev/null 2>/dev/null || $IPTABLES --new droidwall-3g || exit 3\n" +
- "$IPTABLES -L droidwall-wifi >/dev/null 2>/dev/null || $IPTABLES --new droidwall-wifi || exit 4\n" +
- "$IPTABLES -L droidwall-reject >/dev/null 2>/dev/null || $IPTABLES --new droidwall-reject || exit 5\n" +
- "# Add droidwall chain to OUTPUT chain if necessary\n" +
- "$IPTABLES -L OUTPUT | $GREP -q droidwall || $IPTABLES -A OUTPUT -j droidwall || exit 6\n" +
- "# Flush existing rules\n" +
- "$IPTABLES -F droidwall || exit 7\n" +
- "$IPTABLES -F droidwall-3g || exit 8\n" +
- "$IPTABLES -F droidwall-wifi || exit 9\n" +
- "$IPTABLES -F droidwall-reject || exit 10\n" +
- "");
- // Check if logging is enabled
- if (logenabled) {
- script.append("" +
- "# Create the log and reject rules (ignore errors on the LOG target just in case it is not available)\n" +
- "$IPTABLES -A droidwall-reject -j LOG --log-prefix \"[DROIDWALL] \" --log-uid\n" +
- "$IPTABLES -A droidwall-reject -j REJECT || exit 11\n" +
- "");
- } else {
- script.append("" +
- "# Create the reject rule (log disabled)\n" +
- "$IPTABLES -A droidwall-reject -j REJECT || exit 11\n" +
- "");
- }
- if (whitelist && logenabled) {
- script.append("# Allow DNS lookups on white-list for a better logging (ignore errors)\n");
- script.append("$IPTABLES -A droidwall -p udp --dport 53 -j RETURN\n");
- }
- script.append("# Main rules (per interface)\n");
- for (final String itf : ITFS_3G) {
- script.append("$IPTABLES -A droidwall -o ").append(itf).append(" -j droidwall-3g || exit\n");
- }
- for (final String itf : ITFS_WIFI) {
- script.append("$IPTABLES -A droidwall -o ").append(itf).append(" -j droidwall-wifi || exit\n");
- }
-
- script.append("# Filtering rules\n");
- final String targetRule = (whitelist ? "RETURN" : "droidwall-reject");
- final boolean any_3g = uids3g.indexOf(SPECIAL_UID_ANY) >= 0;
- final boolean any_wifi = uidsWifi.indexOf(SPECIAL_UID_ANY) >= 0;
- if (whitelist && !any_wifi) {
- // When "white listing" wifi, we need to ensure that the dhcp and wifi users are allowed
- int uid = android.os.Process.getUidForName("dhcp");
- if (uid != -1) {
- script.append("# dhcp user\n");
- script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner ").append(uid).append(" -j RETURN || exit\n");
- }
- uid = android.os.Process.getUidForName("wifi");
- if (uid != -1) {
- script.append("# wifi user\n");
- script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner ").append(uid).append(" -j RETURN || exit\n");
- }
- }
- if (any_3g) {
- if (blacklist) {
- /* block any application on this interface */
- script.append("$IPTABLES -A droidwall-3g -j ").append(targetRule).append(" || exit\n");
- }
- } else {
- /* release/block individual applications on this interface */
- for (final Integer uid : uids3g) {
- if (uid >= 0) script.append("$IPTABLES -A droidwall-3g -m owner --uid-owner ").append(uid).append(" -j ").append(targetRule).append(" || exit\n");
- }
- }
- if (any_wifi) {
- if (blacklist) {
- /* block any application on this interface */
- script.append("$IPTABLES -A droidwall-wifi -j ").append(targetRule).append(" || exit\n");
- }
- } else {
- /* release/block individual applications on this interface */
- for (final Integer uid : uidsWifi) {
- if (uid >= 0) script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner ").append(uid).append(" -j ").append(targetRule).append(" || exit\n");
- }
- }
- if (whitelist) {
- if (!any_3g) {
- if (uids3g.indexOf(SPECIAL_UID_KERNEL) >= 0) {
- script.append("# hack to allow kernel packets on white-list\n");
- script.append("$IPTABLES -A droidwall-3g -m owner --uid-owner 0:999999999 -j droidwall-reject || exit\n");
- } else {
- script.append("$IPTABLES -A droidwall-3g -j droidwall-reject || exit\n");
- }
- }
- if (!any_wifi) {
- if (uidsWifi.indexOf(SPECIAL_UID_KERNEL) >= 0) {
- script.append("# hack to allow kernel packets on white-list\n");
- script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner 0:999999999 -j droidwall-reject || exit\n");
- } else {
- script.append("$IPTABLES -A droidwall-wifi -j droidwall-reject || exit\n");
- }
- }
- } else {
- if (uids3g.indexOf(SPECIAL_UID_KERNEL) >= 0) {
- script.append("# hack to BLOCK kernel packets on black-list\n");
- script.append("$IPTABLES -A droidwall-3g -m owner --uid-owner 0:999999999 -j RETURN || exit\n");
- script.append("$IPTABLES -A droidwall-3g -j droidwall-reject || exit\n");
- }
- if (uidsWifi.indexOf(SPECIAL_UID_KERNEL) >= 0) {
- script.append("# hack to BLOCK kernel packets on black-list\n");
- script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner 0:999999999 -j RETURN || exit\n");
- script.append("$IPTABLES -A droidwall-wifi -j droidwall-reject || exit\n");
- }
- }
- final StringBuilder res = new StringBuilder();
- code = runScriptAsRoot(ctx, script.toString(), res);
- if (showErrors && code != 0) {
- String msg = res.toString();
- Log.e("DroidWall", msg);
- // Remove unnecessary help message from output
- if (msg.indexOf("\nTry `iptables -h' or 'iptables --help' for more information.") != -1) {
- msg = msg.replace("\nTry `iptables -h' or 'iptables --help' for more information.", "");
- }
- alert(ctx, "Error applying iptables rules. Exit code: " + code + "\n\n" + msg.trim());
- } else {
- return true;
- }
- } catch (Exception e) {
- if (showErrors) alert(ctx, "error refreshing iptables: " + e);
- }
- return false;
- }
- /**
- * Purge and re-add all saved rules (not in-memory ones).
- * This is much faster than just calling "applyIptablesRules", since it don't need to read installed applications.
- * @param ctx application context (mandatory)
- * @param showErrors indicates if errors should be alerted
- */
- public static boolean applySavedIptablesRules(Context ctx, boolean showErrors) {
- if (ctx == null) {
- return false;
- }
- final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
- final String savedUids_wifi = prefs.getString(PREF_WIFI_UIDS, "");
- final String savedUids_3g = prefs.getString(PREF_3G_UIDS, "");
- final List<Integer> uids_wifi = new LinkedList<Integer>();
- if (savedUids_wifi.length() > 0) {
- // Check which applications are allowed on wifi
- final StringTokenizer tok = new StringTokenizer(savedUids_wifi, "|");
- while (tok.hasMoreTokens()) {
- final String uid = tok.nextToken();
- if (!uid.equals("")) {
- try {
- uids_wifi.add(Integer.parseInt(uid));
- } catch (Exception ex) {
- }
- }
- }
- }
- final List<Integer> uids_3g = new LinkedList<Integer>();
- if (savedUids_3g.length() > 0) {
- // Check which applications are allowed on 2G/3G
- final StringTokenizer tok = new StringTokenizer(savedUids_3g, "|");
- while (tok.hasMoreTokens()) {
- final String uid = tok.nextToken();
- if (!uid.equals("")) {
- try {
- uids_3g.add(Integer.parseInt(uid));
- } catch (Exception ex) {
- }
- }
- }
- }
- return applyIptablesRulesImpl(ctx, uids_wifi, uids_3g, showErrors);
- }
-
- /**
- * Purge and re-add all rules.
- * @param ctx application context (mandatory)
- * @param showErrors indicates if errors should be alerted
- */
- public static boolean applyIptablesRules(Context ctx, boolean showErrors) {
- if (ctx == null) {
- return false;
- }
- saveRules(ctx);
- return applySavedIptablesRules(ctx, showErrors);
- }
-
- /**
- * Save current rules using the preferences storage.
- * @param ctx application context (mandatory)
- */
- public static void saveRules(Context ctx) {
- final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
- final DroidApp[] apps = getApps(ctx);
- // Builds a pipe-separated list of names
- final StringBuilder newuids_wifi = new StringBuilder();
- final StringBuilder newuids_3g = new StringBuilder();
- for (int i=0; i<apps.length; i++) {
- if (apps[i].selected_wifi) {
- if (newuids_wifi.length() != 0) newuids_wifi.append('|');
- newuids_wifi.append(apps[i].uid);
- }
- if (apps[i].selected_3g) {
- if (newuids_3g.length() != 0) newuids_3g.append('|');
- newuids_3g.append(apps[i].uid);
- }
- }
- // save the new list of UIDs
- final Editor edit = prefs.edit();
- edit.putString(PREF_WIFI_UIDS, newuids_wifi.toString());
- edit.putString(PREF_3G_UIDS, newuids_3g.toString());
- edit.commit();
- }
-
- /**
- * Purge all iptables rules.
- * @param ctx mandatory context
- * @param showErrors indicates if errors should be alerted
- * @return true if the rules were purged
- */
- public static boolean purgeIptables(Context ctx, boolean showErrors) {
- StringBuilder res = new StringBuilder();
- try {
- assertBinaries(ctx, showErrors);
- int code = runScriptAsRoot(ctx, scriptHeader(ctx) +
- "$IPTABLES -F droidwall\n" +
- "$IPTABLES -F droidwall-reject\n" +
- "$IPTABLES -F droidwall-3g\n" +
- "$IPTABLES -F droidwall-wifi\n", res);
- if (code == -1) {
- if (showErrors) alert(ctx, "error purging iptables. exit code: " + code + "\n" + res);
- return false;
- }
- return true;
- } catch (Exception e) {
- if (showErrors) alert(ctx, "error purging iptables: " + e);
- return false;
- }
- }
-
- /**
- * Display iptables rules output
- * @param ctx application context
- */
- public static void showIptablesRules(Context ctx) {
- try {
- final StringBuilder res = new StringBuilder();
- runScriptAsRoot(ctx, scriptHeader(ctx) +
- "$ECHO $IPTABLES\n" +
- "$IPTABLES -L -v\n", res);
- alert(ctx, res);
- } catch (Exception e) {
- alert(ctx, "error: " + e);
- }
- }
-
- /**
- * Display logs
- * @param ctx application context
- * @return true if the clogs were cleared
- */
- public static boolean clearLog(Context ctx) {
- try {
- final StringBuilder res = new StringBuilder();
- int code = runScriptAsRoot(ctx, "dmesg -c >/dev/null || exit\n", res);
- if (code != 0) {
- alert(ctx, res);
- return false;
- }
- return true;
- } catch (Exception e) {
- alert(ctx, "error: " + e);
- }
- return false;
- }
- /**
- * Display logs
- * @param ctx application context
- */
- public static void showLog(Context ctx) {
- try {
- StringBuilder res = new StringBuilder();
- int code = runScriptAsRoot(ctx, scriptHeader(ctx) +
- "dmesg | $GREP DROIDWALL\n", res);
- if (code != 0) {
- if (res.length() == 0) {
- res.append("Log is empty");
- }
- alert(ctx, res);
- return;
- }
- final BufferedReader r = new BufferedReader(new StringReader(res.toString()));
- final Integer unknownUID = -99;
- res = new StringBuilder();
- String line;
- int start, end;
- Integer appid;
- final HashMap<Integer, LogInfo> map = new HashMap<Integer, LogInfo>();
- LogInfo loginfo = null;
- while ((line = r.readLine()) != null) {
- if (line.indexOf("[DROIDWALL]") == -1) continue;
- appid = unknownUID;
- if (((start=line.indexOf("UID=")) != -1) && ((end=line.indexOf(" ", start)) != -1)) {
- appid = Integer.parseInt(line.substring(start+4, end));
- }
- loginfo = map.get(appid);
- if (loginfo == null) {
- loginfo = new LogInfo();
- map.put(appid, loginfo);
- }
- loginfo.totalBlocked += 1;
- if (((start=line.indexOf("DST=")) != -1) && ((end=line.indexOf(" ", start)) != -1)) {
- String dst = line.substring(start+4, end);
- if (loginfo.dstBlocked.containsKey(dst)) {
- loginfo.dstBlocked.put(dst, loginfo.dstBlocked.get(dst) + 1);
- } else {
- loginfo.dstBlocked.put(dst, 1);
- }
- }
- }
- final DroidApp[] apps = getApps(ctx);
- for (Integer id : map.keySet()) {
- res.append("App ID ");
- if (id != unknownUID) {
- res.append(id);
- for (DroidApp app : apps) {
- if (app.uid == id) {
- res.append(" (").append(app.names[0]);
- if (app.names.length > 1) {
- res.append(", ...)");
- } else {
- res.append(")");
- }
- break;
- }
- }
- } else {
- res.append("(kernel)");
- }
- loginfo = map.get(id);
- res.append(" - Blocked ").append(loginfo.totalBlocked).append(" packets");
- if (loginfo.dstBlocked.size() > 0) {
- res.append(" (");
- boolean first = true;
- for (String dst : loginfo.dstBlocked.keySet()) {
- if (!first) {
- res.append(", ");
- }
- res.append(loginfo.dstBlocked.get(dst)).append(" packets for ").append(dst);
- first = false;
- }
- res.append(")");
- }
- res.append("\n\n");
- }
- if (res.length() == 0) {
- res.append("Log is empty");
- }
- alert(ctx, res);
- } catch (Exception e) {
- alert(ctx, "error: " + e);
- }
- }
-
- /**
- * @param ctx application context (mandatory)
- * @return a list of applications
- */
- public static DroidApp[] getApps(Context ctx) {
- if (applications != null) {
- // return cached instance
- return applications;
- }
- final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
- // allowed application names separated by pipe '|' (persisted)
- final String savedUids_wifi = prefs.getString(PREF_WIFI_UIDS, "");
- final String savedUids_3g = prefs.getString(PREF_3G_UIDS, "");
- int selected_wifi[] = new int[0];
- int selected_3g[] = new int[0];
- if (savedUids_wifi.length() > 0) {
- // Check which applications are allowed
- final StringTokenizer tok = new StringTokenizer(savedUids_wifi, "|");
- selected_wifi = new int[tok.countTokens()];
- for (int i=0; i<selected_wifi.length; i++) {
- final String uid = tok.nextToken();
- if (!uid.equals("")) {
- try {
- selected_wifi[i] = Integer.parseInt(uid);
- } catch (Exception ex) {
- selected_wifi[i] = -1;
- }
- }
- }
- // Sort the array to allow using "Arrays.binarySearch" later
- Arrays.sort(selected_wifi);
- }
- if (savedUids_3g.length() > 0) {
- // Check which applications are allowed
- final StringTokenizer tok = new StringTokenizer(savedUids_3g, "|");
- selected_3g = new int[tok.countTokens()];
- for (int i=0; i<selected_3g.length; i++) {
- final String uid = tok.nextToken();
- if (!uid.equals("")) {
- try {
- selected_3g[i] = Integer.parseInt(uid);
- } catch (Exception ex) {
- selected_3g[i] = -1;
- }
- }
- }
- // Sort the array to allow using "Arrays.binarySearch" later
- Arrays.sort(selected_3g);
- }
- try {
- final PackageManager pkgmanager = ctx.getPackageManager();
- final List<ApplicationInfo> installed = pkgmanager.getInstalledApplications(0);
- final HashMap<Integer, DroidApp> map = new HashMap<Integer, DroidApp>();
- final Editor edit = prefs.edit();
- boolean changed = false;
- String name = null;
- String cachekey = null;
- DroidApp app = null;
- for (final ApplicationInfo apinfo : installed) {
- app = map.get(apinfo.uid);
- // filter applications which are not allowed to access the Internet
- if (app == null && PackageManager.PERMISSION_GRANTED != pkgmanager.checkPermission(Manifest.permission.INTERNET, apinfo.packageName)) {
- continue;
- }
- // try to get the application label from our cache - getApplicationLabel() is horribly slow!!!!
- cachekey = "cache.label."+apinfo.packageName;
- name = prefs.getString(cachekey, "");
- if (name.length() == 0) {
- // get label and put on cache
- name = pkgmanager.getApplicationLabel(apinfo).toString();
- edit.putString(cachekey, name);
- changed = true;
- }
- if (app == null) {
- app = new DroidApp();
- app.uid = apinfo.uid;
- app.names = new String[] { name };
- map.put(apinfo.uid, app);
- } else {
- final String newnames[] = new String[app.names.length + 1];
- System.arraycopy(app.names, 0, newnames, 0, app.names.length);
- newnames[app.names.length] = name;
- app.names = newnames;
- }
- // check if this application is selected
- if (!app.selected_wifi && Arrays.binarySearch(selected_wifi, app.uid) >= 0) {
- app.selected_wifi = true;
- }
- if (!app.selected_3g && Arrays.binarySearch(selected_3g, app.uid) >= 0) {
- app.selected_3g = true;
- }
- }
- if (changed) {
- edit.commit();
- }
- /* add special applications to the list */
- final DroidApp special[] = {
- new DroidApp(SPECIAL_UID_ANY,"(Any application) - Same as selecting all applications", false, false),
- new DroidApp(SPECIAL_UID_KERNEL,"(Kernel) - Linux kernel", false, false),
- new DroidApp(android.os.Process.getUidForName("root"), "(root) - Applications running as root", false, false),
- new DroidApp(android.os.Process.getUidForName("media"), "Media server", false, false),
- new DroidApp(android.os.Process.getUidForName("vpn"), "VPN networking", false, false),
- new DroidApp(android.os.Process.getUidForName("shell"), "Linux shell", false, false),
- };
- for (int i=0; i<special.length; i++) {
- app = special[i];
- if (app.uid != -1 && !map.containsKey(app.uid)) {
- // check if this application is allowed
- if (Arrays.binarySearch(selected_wifi, app.uid) >= 0) {
- app.selected_wifi = true;
- }
- if (Arrays.binarySearch(selected_3g, app.uid) >= 0) {
- app.selected_3g = true;
- }
- map.put(app.uid, app);
- }
- }
- applications = new DroidApp[map.size()];
- int index = 0;
- for (DroidApp application : map.values()) applications[index++] = application;
- return applications;
- } catch (Exception e) {
- alert(ctx, "error: " + e);
- }
- return null;
- }
- /**
- * Check if we have root access
- * @param ctx mandatory context
- * @param showErrors indicates if errors should be alerted
- * @return boolean true if we have root
- */
- public static boolean hasRootAccess(Context ctx, boolean showErrors) {
- if (hasroot) return true;
- final StringBuilder res = new StringBuilder();
- try {
- // Run an empty script just to check root access
- if (runScriptAsRoot(ctx, "exit 0", res) == 0) {
- hasroot = true;
- return true;
- }
- } catch (Exception e) {
- }
- if (showErrors) {
- alert(ctx, "Could not acquire root access.\n" +
- "You need a rooted phone to run DroidWall.\n\n" +
- "If this phone is already rooted, please make sure DroidWall has enough permissions to execute the \"su\" command.\n" +
- "Error message: " + res.toString());
- }
- return false;
- }
- /**
- * Runs a script, wither as root or as a regular user (multiple commands separated by "\n").
- * @param ctx mandatory context
- * @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 runScript(Context ctx, String script, StringBuilder res, long timeout, boolean asroot) {
- final File file = new File(ctx.getDir("bin",0), SCRIPT_FILE);
- final ScriptRunner runner = new ScriptRunner(file, script, res, asroot);
- runner.start();
- try {
- if (timeout > 0) {
- runner.join(timeout);
- } else {
- runner.join();
- }
- if (runner.isAlive()) {
- // Timed-out
- runner.interrupt();
- runner.join(150);
- runner.destroy();
- runner.join(50);
- }
- } catch (InterruptedException ex) {}
- return runner.exitcode;
- }
- /**
- * Runs a script as root (multiple commands separated by "\n").
- * @param ctx mandatory context
- * @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(Context ctx, String script, StringBuilder res, long timeout) {
- return runScript(ctx, script, res, timeout, true);
- }
- /**
- * Runs a script as root (multiple commands separated by "\n") with a default timeout of 20 seconds.
- * @param ctx mandatory context
- * @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(Context ctx, String script, StringBuilder res) throws IOException {
- return runScriptAsRoot(ctx, script, res, 40000);
- }
- /**
- * Runs a script as a regular user (multiple commands separated by "\n") with a default timeout of 20 seconds.
- * @param ctx mandatory context
- * @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 runScript(Context ctx, String script, StringBuilder res) throws IOException {
- return runScript(ctx, script, res, 40000, false);
- }
- /**
- * Asserts that the binary files are installed in the cache directory.
- * @param ctx context
- * @param showErrors indicates if errors should be alerted
- * @return false if the binary files could not be installed
- */
- public static boolean assertBinaries(Context ctx, boolean showErrors) {
- boolean changed = false;
- try {
- // Check iptables_g1
- File file = new File(ctx.getDir("bin",0), "iptables");
-
- if ((!file.exists()) && isARMv6()) {
- copyRawFile(ctx, R.raw.iptables_g1, file, "755");
- changed = true;
- }
-
- // Check iptables_n1
- file = new File(ctx.getDir("bin",0), "iptables");
- if ((!file.exists()) && (!isARMv6())) {
- copyRawFile(ctx, R.raw.iptables_n1, file, "755");
- changed = true;
- }
-
- // Check busybox
- /*
- file = new File(ctx.getDir("bin",0), "busybox_g1");
- if (!file.exists()) {
- copyRawFile(ctx, R.raw.busybox_g1, file, "755");
- changed = true;
- }
- */
-
- if (changed) {
- Toast.makeText(ctx, R.string.status_install_success, Toast.LENGTH_LONG).show();
- }
- } catch (Exception e) {
- if (showErrors) alert(ctx, "Error installing binary files: " + e);
- return false;
- }
- return true;
- }
- /**
- * Check if the firewall is enabled
- * @param ctx mandatory context
- * @return boolean
- */
- public static boolean isEnabled(Context ctx) {
- if (ctx == null) return false;
- return ctx.getSharedPreferences(PREFS_NAME, 0).getBoolean(PREF_ENABLED, false);
- }
-
- /**
- * Defines if the firewall is enabled and broadcasts the new status
- * @param ctx mandatory context
- * @param enabled enabled flag
- */
- public static void setEnabled(Context ctx, boolean enabled) {
- if (ctx == null) return;
- final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
- if (prefs.getBoolean(PREF_ENABLED, false) == enabled) {
- return;
- }
- final Editor edit = prefs.edit();
- edit.putBoolean(PREF_ENABLED, enabled);
- if (!edit.commit()) {
- alert(ctx, "Error writing to preferences");
- return;
- }
- /* notify */
- final Intent message = new Intent(Api.STATUS_CHANGED_MSG);
- message.putExtra(Api.STATUS_EXTRA, enabled);
- ctx.sendBroadcast(message);
- }
- /**
- * Called when an application in removed (un-installed) from the system.
- * This will look for that application in the selected list and update the persisted values if necessary
- * @param ctx mandatory app context
- * @param uid UID of the application that has been removed
- */
- public static void applicationRemoved(Context ctx, int uid) {
- final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
- final Editor editor = prefs.edit();
- // allowed application names separated by pipe '|' (persisted)
- final String savedUids_wifi = prefs.getString(PREF_WIFI_UIDS, "");
- final String savedUids_3g = prefs.getString(PREF_3G_UIDS, "");
- final String uid_str = uid + "";
- boolean changed = false;
- // look for the removed application in the "wi-fi" list
- if (savedUids_wifi.length() > 0) {
- final StringBuilder newuids = new StringBuilder();
- final StringTokenizer tok = new StringTokenizer(savedUids_wifi, "|");
- while (tok.hasMoreTokens()) {
- final String token = tok.nextToken();
- if (uid_str.equals(token)) {
- Log.d("DroidWall", "Removing UID " + token + " from the wi-fi list (package removed)!");
- changed = true;
- } else {
- if (newuids.length() > 0) newuids.append('|');
- newuids.append(token);
- }
- }
- if (changed) {
- editor.putString(PREF_WIFI_UIDS, newuids.toString());
- }
- }
- // look for the removed application in the "3g" list
- if (savedUids_3g.length() > 0) {
- final StringBuilder newuids = new StringBuilder();
- final StringTokenizer tok = new StringTokenizer(savedUids_3g, "|");
- while (tok.hasMoreTokens()) {
- final String token = tok.nextToken();
- if (uid_str.equals(token)) {
- Log.d("DroidWall", "Removing UID " + token + " from the 3G list (package removed)!");
- changed = true;
- } else {
- if (newuids.length() > 0) newuids.append('|');
- newuids.append(token);
- }
- }
- if (changed) {
- editor.putString(PREF_3G_UIDS, newuids.toString());
- }
- }
- // if anything has changed, save the new prefs...
- if (changed) {
- editor.commit();
- if (isEnabled(ctx)) {
- // .. and also re-apply the rules if the firewall is enabled
- applySavedIptablesRules(ctx, false);
- }
- }
- }
-
- /**
- * Small structure to hold an application info
- */
- public static final class DroidApp {
- /** linux user id */
- int uid;
- /** application names belonging to this user id */
- String names[];
- /** indicates if this application is selected for wifi */
- boolean selected_wifi;
- /** indicates if this application is selected for 3g */
- boolean selected_3g;
- /** toString cache */
- String tostr;
-
- public DroidApp() {
- }
- public DroidApp(int uid, String name, boolean selected_wifi, boolean selected_3g) {
- this.uid = uid;
- this.names = new String[] {name};
- this.selected_wifi = selected_wifi;
- this.selected_3g = selected_3g;
- }
- /**
- * Screen representation of this application
- */
- @Override
- public String toString() {
- if (tostr == null) {
- final StringBuilder s = new StringBuilder();
- if (uid > 0) s.append(uid + ": ");
- for (int i=0; i<names.length; i++) {
- if (i != 0) s.append(", ");
- s.append(names[i]);
- }
- s.append("\n");
- tostr = s.toString();
- }
- return tostr;
- }
- }
- /**
- * Small internal structure used to hold log information
- */
- private static final class LogInfo {
- private int totalBlocked; // Total number of packets blocked
- private HashMap<String, Integer> dstBlocked; // Number of packets blocked per destination IP address
- private LogInfo() {
- this.dstBlocked = new HashMap<String, Integer>();
- }
- }
- /**
- * Internal thread used to execute scripts (as root or not).
- */
- private static final class ScriptRunner extends Thread {
- private final File file;
- private final String script;
- private final StringBuilder res;
- private final boolean asroot;
- public int exitcode = -1;
- private Process exec;
-
- /**
- * Creates a new script runner.
- * @param file temporary script file
- * @param script script to run
- * @param res response output
- * @param asroot if true, executes the script as root
- */
- public ScriptRunner(File file, String script, StringBuilder res, boolean asroot) {
- this.file = file;
- this.script = script;
- this.res = res;
- this.asroot = asroot;
- }
- @Override
- public void run() {
- try {
- file.createNewFile();
- final String abspath = file.getAbsolutePath();
- // make sure we have execution permission on the script file
- Runtime.getRuntime().exec("chmod 777 "+abspath).waitFor();
- // Write the script to be executed
- final OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file));
- if (new File("/system/bin/sh").exists()) {
- out.write("#!/system/bin/sh\n");
- }
- out.write(script);
- if (!script.endsWith("\n")) out.write("\n");
- out.write("exit\n");
- out.flush();
- out.close();
- if (this.asroot) {
- // Create the "su" request to run the script
- exec = Runtime.getRuntime().exec("su -c "+abspath);
- } else {
- // Create the "sh" request to run the script
- exec = Runtime.getRuntime().exec("sh "+abspath);
- }
- InputStreamReader r = new InputStreamReader(exec.getInputStream());
- final char buf[] = new char[1024];
- int read = 0;
- // Consume the "stdout"
- 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;
- }
- }
-}
diff --git a/src/org/torproject/android/service/IptablesManager.java b/src/org/torproject/android/service/IptablesManager.java
new file mode 100644
index 0000000..2781705
--- /dev/null
+++ b/src/org/torproject/android/service/IptablesManager.java
@@ -0,0 +1,1058 @@
+/**
+ * Contains shared programming interfaces.
+ * All iptables "communication" is handled by this class.
+ *
+ * Copyright (C) 2009-2010 Rodrigo Zechin Rosauro
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Rodrigo Zechin Rosauro
+ * @version 1.0
+ */
+
+package org.torproject.android.service;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.torproject.android.R;
+
+import android.Manifest;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Contains shared programming interfaces.
+ * All iptables "communication" is handled by this class.
+ */
+public final class IptablesManager {
+ /** application version string */
+ public static final String VERSION = "1.5.1-dev";
+ /** special application UID used to indicate "any application" */
+ public static final int SPECIAL_UID_ANY = -10;
+ /** special application UID used to indicate the Linux Kernel */
+ public static final int SPECIAL_UID_KERNEL = -11;
+ /** root script filename */
+ private static final String SCRIPT_FILE = "droidwall.sh";
+
+ // Preferences
+ public static final String PREFS_NAME = "DroidWallPrefs";
+ public static final String PREF_3G_UIDS = "AllowedUids3G";
+ public static final String PREF_WIFI_UIDS = "AllowedUidsWifi";
+ public static final String PREF_PASSWORD = "Password";
+ public static final String PREF_MODE = "BlockMode";
+ public static final String PREF_ENABLED = "Enabled";
+ public static final String PREF_LOGENABLED = "LogEnabled";
+ // Modes
+ public static final String MODE_WHITELIST = "whitelist";
+ public static final String MODE_BLACKLIST = "blacklist";
+ // Messages
+ public static final String STATUS_CHANGED_MSG = "com.googlecode.droidwall.intent.action.STATUS_CHANGED";
+ public static final String TOGGLE_REQUEST_MSG = "com.googlecode.droidwall.intent.action.TOGGLE_REQUEST";
+ public static final String STATUS_EXTRA = "com.googlecode.droidwall.intent.extra.STATUS";
+
+ // Cached applications
+ public static DroidApp applications[] = null;
+ // Do we have root access?
+ private static boolean hasroot = false;
+ // Flag indicating if this is an ARMv6 device (-1: unknown, 0: no, 1: yes)
+ private static int isARMv6 = -1;
+
+ /**
+ * Display a simple alert box
+ * @param ctx context
+ * @param msg message
+ */
+ public static void alert(Context ctx, CharSequence msg) {
+ if (ctx != null) {
+ new AlertDialog.Builder(ctx)
+ .setNeutralButton(android.R.string.ok, null)
+ .setMessage(msg)
+ .show();
+ }
+ }
+ /**
+ * Check if this is an ARMv6 device
+ * @return true if this is ARMv6
+ */
+ private static boolean isARMv6() {
+ if (isARMv6 == -1) {
+ BufferedReader r = null;
+ try {
+ isARMv6 = 0;
+ r = new BufferedReader(new FileReader("/proc/cpuinfo"));
+ for (String line = r.readLine(); line != null; line = r.readLine()) {
+ if (line.startsWith("Processor") && line.contains("ARMv6")) {
+ isARMv6 = 1;
+ break;
+ } else if (line.startsWith("CPU architecture") && (line.contains("6TE") || line.contains("5TE"))) {
+ isARMv6 = 1;
+ break;
+ }
+ }
+ } catch (Exception ex) {
+ } finally {
+ if (r != null) try {r.close();} catch (Exception ex) {}
+ }
+ }
+ return (isARMv6 == 1);
+ }
+ /**
+ * Create the generic shell script header used to determine which iptables binary to use.
+ * @param ctx context
+ * @return script header
+ */
+ private static String scriptHeader(Context ctx) {
+ final String dir = ctx.getDir("bin",0).getAbsolutePath();
+ final String myiptables = dir + (isARMv6() ? "/iptables_g1" : "/iptables_n1");
+ return "" +
+ "IPTABLES=iptables\n" +
+ "BUSYBOX=busybox\n" +
+ "GREP=grep\n" +
+ "ECHO=echo\n" +
+ "# Try to find busybox\n" +
+ "if " + dir + "/busybox_g1 --help >/dev/null 2>/dev/null ; then\n" +
+ " BUSYBOX="+dir+"/busybox_g1\n" +
+ " GREP=\"$BUSYBOX grep\"\n" +
+ " ECHO=\"$BUSYBOX echo\"\n" +
+ "elif busybox --help >/dev/null 2>/dev/null ; then\n" +
+ " BUSYBOX=busybox\n" +
+ "elif /system/xbin/busybox --help >/dev/null 2>/dev/null ; then\n" +
+ " BUSYBOX=/system/xbin/busybox\n" +
+ "elif /system/bin/busybox --help >/dev/null 2>/dev/null ; then\n" +
+ " BUSYBOX=/system/bin/busybox\n" +
+ "fi\n" +
+ "# Try to find grep\n" +
+ "if ! $ECHO 1 | $GREP -q 1 >/dev/null 2>/dev/null ; then\n" +
+ " if $ECHO 1 | $BUSYBOX grep -q 1 >/dev/null 2>/dev/null ; then\n" +
+ " GREP=\"$BUSYBOX grep\"\n" +
+ " fi\n" +
+ " # Grep is absolutely required\n" +
+ " if ! $ECHO 1 | $GREP -q 1 >/dev/null 2>/dev/null ; then\n" +
+ " $ECHO The grep command is required. DroidWall will not work.\n" +
+ " exit 1\n" +
+ " fi\n" +
+ "fi\n" +
+ "# Try to find iptables\n" +
+ "if " + myiptables + " --version >/dev/null 2>/dev/null ; then\n" +
+ " IPTABLES="+myiptables+"\n" +
+ "fi\n" +
+ "";
+ }
+ /**
+ * Copies a raw resource file, given its ID to the given location
+ * @param ctx context
+ * @param resid resource id
+ * @param file destination file
+ * @param mode file permissions (E.g.: "755")
+ * @throws IOException on error
+ * @throws InterruptedException when interrupted
+ */
+ private static void copyRawFile(Context ctx, int resid, File file, String mode) throws IOException, InterruptedException
+ {
+ final String abspath = file.getAbsolutePath();
+ // Write the iptables binary
+ final FileOutputStream out = new FileOutputStream(file);
+ final InputStream is = ctx.getResources().openRawResource(resid);
+ byte buf[] = new byte[1024];
+ int len;
+ while ((len = is.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ out.close();
+ is.close();
+ // Change the permissions
+ Runtime.getRuntime().exec("chmod "+mode+" "+abspath).waitFor();
+ }
+ /**
+ * Purge and re-add all rules (internal implementation).
+ * @param ctx application context (mandatory)
+ * @param uidsWifi list of selected UIDs for WIFI to allow or disallow (depending on the working mode)
+ * @param uids3g list of selected UIDs for 2G/3G to allow or disallow (depending on the working mode)
+ * @param showErrors indicates if errors should be alerted
+ */
+ private static boolean applyIptablesRulesImpl(Context ctx, List<Integer> uidsWifi, List<Integer> uids3g, boolean showErrors) {
+ if (ctx == null) {
+ return false;
+ }
+ assertBinaries(ctx, showErrors);
+ final String ITFS_WIFI[] = {"tiwlan+", "wlan+", "eth+"};
+ final String ITFS_3G[] = {"rmnet+","pdp+","ppp+","uwbr+","wimax+","vsnet+"};
+ final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
+ final boolean whitelist = prefs.getString(PREF_MODE, MODE_WHITELIST).equals(MODE_WHITELIST);
+ final boolean blacklist = !whitelist;
+ final boolean logenabled = ctx.getSharedPreferences(PREFS_NAME, 0).getBoolean(PREF_LOGENABLED, false);
+
+ final StringBuilder script = new StringBuilder();
+ try {
+ int code;
+ script.append(scriptHeader(ctx));
+ script.append("" +
+ "$IPTABLES --version || exit 1\n" +
+ "# Create the droidwall chains if necessary\n" +
+ "$IPTABLES -L droidwall >/dev/null 2>/dev/null || $IPTABLES --new droidwall || exit 2\n" +
+ "$IPTABLES -L droidwall-3g >/dev/null 2>/dev/null || $IPTABLES --new droidwall-3g || exit 3\n" +
+ "$IPTABLES -L droidwall-wifi >/dev/null 2>/dev/null || $IPTABLES --new droidwall-wifi || exit 4\n" +
+ "$IPTABLES -L droidwall-reject >/dev/null 2>/dev/null || $IPTABLES --new droidwall-reject || exit 5\n" +
+ "# Add droidwall chain to OUTPUT chain if necessary\n" +
+ "$IPTABLES -L OUTPUT | $GREP -q droidwall || $IPTABLES -A OUTPUT -j droidwall || exit 6\n" +
+ "# Flush existing rules\n" +
+ "$IPTABLES -F droidwall || exit 7\n" +
+ "$IPTABLES -F droidwall-3g || exit 8\n" +
+ "$IPTABLES -F droidwall-wifi || exit 9\n" +
+ "$IPTABLES -F droidwall-reject || exit 10\n" +
+ "");
+ // Check if logging is enabled
+ if (logenabled) {
+ script.append("" +
+ "# Create the log and reject rules (ignore errors on the LOG target just in case it is not available)\n" +
+ "$IPTABLES -A droidwall-reject -j LOG --log-prefix \"[DROIDWALL] \" --log-uid\n" +
+ "$IPTABLES -A droidwall-reject -j REJECT || exit 11\n" +
+ "");
+ } else {
+ script.append("" +
+ "# Create the reject rule (log disabled)\n" +
+ "$IPTABLES -A droidwall-reject -j REJECT || exit 11\n" +
+ "");
+ }
+ if (whitelist && logenabled) {
+ script.append("# Allow DNS lookups on white-list for a better logging (ignore errors)\n");
+ script.append("$IPTABLES -A droidwall -p udp --dport 53 -j RETURN\n");
+ }
+ script.append("# Main rules (per interface)\n");
+ for (final String itf : ITFS_3G) {
+ script.append("$IPTABLES -A droidwall -o ").append(itf).append(" -j droidwall-3g || exit\n");
+ }
+ for (final String itf : ITFS_WIFI) {
+ script.append("$IPTABLES -A droidwall -o ").append(itf).append(" -j droidwall-wifi || exit\n");
+ }
+
+ script.append("# Filtering rules\n");
+ final String targetRule = (whitelist ? "RETURN" : "droidwall-reject");
+ final boolean any_3g = uids3g.indexOf(SPECIAL_UID_ANY) >= 0;
+ final boolean any_wifi = uidsWifi.indexOf(SPECIAL_UID_ANY) >= 0;
+ if (whitelist && !any_wifi) {
+ // When "white listing" wifi, we need to ensure that the dhcp and wifi users are allowed
+ int uid = android.os.Process.getUidForName("dhcp");
+ if (uid != -1) {
+ script.append("# dhcp user\n");
+ script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner ").append(uid).append(" -j RETURN || exit\n");
+ }
+ uid = android.os.Process.getUidForName("wifi");
+ if (uid != -1) {
+ script.append("# wifi user\n");
+ script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner ").append(uid).append(" -j RETURN || exit\n");
+ }
+ }
+ if (any_3g) {
+ if (blacklist) {
+ /* block any application on this interface */
+ script.append("$IPTABLES -A droidwall-3g -j ").append(targetRule).append(" || exit\n");
+ }
+ } else {
+ /* release/block individual applications on this interface */
+ for (final Integer uid : uids3g) {
+ if (uid >= 0) script.append("$IPTABLES -A droidwall-3g -m owner --uid-owner ").append(uid).append(" -j ").append(targetRule).append(" || exit\n");
+ }
+ }
+ if (any_wifi) {
+ if (blacklist) {
+ /* block any application on this interface */
+ script.append("$IPTABLES -A droidwall-wifi -j ").append(targetRule).append(" || exit\n");
+ }
+ } else {
+ /* release/block individual applications on this interface */
+ for (final Integer uid : uidsWifi) {
+ if (uid >= 0) script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner ").append(uid).append(" -j ").append(targetRule).append(" || exit\n");
+ }
+ }
+ if (whitelist) {
+ if (!any_3g) {
+ if (uids3g.indexOf(SPECIAL_UID_KERNEL) >= 0) {
+ script.append("# hack to allow kernel packets on white-list\n");
+ script.append("$IPTABLES -A droidwall-3g -m owner --uid-owner 0:999999999 -j droidwall-reject || exit\n");
+ } else {
+ script.append("$IPTABLES -A droidwall-3g -j droidwall-reject || exit\n");
+ }
+ }
+ if (!any_wifi) {
+ if (uidsWifi.indexOf(SPECIAL_UID_KERNEL) >= 0) {
+ script.append("# hack to allow kernel packets on white-list\n");
+ script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner 0:999999999 -j droidwall-reject || exit\n");
+ } else {
+ script.append("$IPTABLES -A droidwall-wifi -j droidwall-reject || exit\n");
+ }
+ }
+ } else {
+ if (uids3g.indexOf(SPECIAL_UID_KERNEL) >= 0) {
+ script.append("# hack to BLOCK kernel packets on black-list\n");
+ script.append("$IPTABLES -A droidwall-3g -m owner --uid-owner 0:999999999 -j RETURN || exit\n");
+ script.append("$IPTABLES -A droidwall-3g -j droidwall-reject || exit\n");
+ }
+ if (uidsWifi.indexOf(SPECIAL_UID_KERNEL) >= 0) {
+ script.append("# hack to BLOCK kernel packets on black-list\n");
+ script.append("$IPTABLES -A droidwall-wifi -m owner --uid-owner 0:999999999 -j RETURN || exit\n");
+ script.append("$IPTABLES -A droidwall-wifi -j droidwall-reject || exit\n");
+ }
+ }
+ final StringBuilder res = new StringBuilder();
+ code = runScriptAsRoot(ctx, script.toString(), res);
+ if (showErrors && code != 0) {
+ String msg = res.toString();
+ Log.e("DroidWall", msg);
+ // Remove unnecessary help message from output
+ if (msg.indexOf("\nTry `iptables -h' or 'iptables --help' for more information.") != -1) {
+ msg = msg.replace("\nTry `iptables -h' or 'iptables --help' for more information.", "");
+ }
+ alert(ctx, "Error applying iptables rules. Exit code: " + code + "\n\n" + msg.trim());
+ } else {
+ return true;
+ }
+ } catch (Exception e) {
+ if (showErrors) alert(ctx, "error refreshing iptables: " + e);
+ }
+ return false;
+ }
+ /**
+ * Purge and re-add all saved rules (not in-memory ones).
+ * This is much faster than just calling "applyIptablesRules", since it don't need to read installed applications.
+ * @param ctx application context (mandatory)
+ * @param showErrors indicates if errors should be alerted
+ */
+ public static boolean applySavedIptablesRules(Context ctx, boolean showErrors) {
+ if (ctx == null) {
+ return false;
+ }
+ final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
+ final String savedUids_wifi = prefs.getString(PREF_WIFI_UIDS, "");
+ final String savedUids_3g = prefs.getString(PREF_3G_UIDS, "");
+ final List<Integer> uids_wifi = new LinkedList<Integer>();
+ if (savedUids_wifi.length() > 0) {
+ // Check which applications are allowed on wifi
+ final StringTokenizer tok = new StringTokenizer(savedUids_wifi, "|");
+ while (tok.hasMoreTokens()) {
+ final String uid = tok.nextToken();
+ if (!uid.equals("")) {
+ try {
+ uids_wifi.add(Integer.parseInt(uid));
+ } catch (Exception ex) {
+ }
+ }
+ }
+ }
+ final List<Integer> uids_3g = new LinkedList<Integer>();
+ if (savedUids_3g.length() > 0) {
+ // Check which applications are allowed on 2G/3G
+ final StringTokenizer tok = new StringTokenizer(savedUids_3g, "|");
+ while (tok.hasMoreTokens()) {
+ final String uid = tok.nextToken();
+ if (!uid.equals("")) {
+ try {
+ uids_3g.add(Integer.parseInt(uid));
+ } catch (Exception ex) {
+ }
+ }
+ }
+ }
+ return applyIptablesRulesImpl(ctx, uids_wifi, uids_3g, showErrors);
+ }
+
+ /**
+ * Purge and re-add all rules.
+ * @param ctx application context (mandatory)
+ * @param showErrors indicates if errors should be alerted
+ */
+ public static boolean applyIptablesRules(Context ctx, boolean showErrors) {
+ if (ctx == null) {
+ return false;
+ }
+ saveRules(ctx);
+ return applySavedIptablesRules(ctx, showErrors);
+ }
+
+ /**
+ * Save current rules using the preferences storage.
+ * @param ctx application context (mandatory)
+ */
+ public static void saveRules(Context ctx) {
+ final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
+ final DroidApp[] apps = getApps(ctx);
+ // Builds a pipe-separated list of names
+ final StringBuilder newuids_wifi = new StringBuilder();
+ final StringBuilder newuids_3g = new StringBuilder();
+ for (int i=0; i<apps.length; i++) {
+ if (apps[i].selected_wifi) {
+ if (newuids_wifi.length() != 0) newuids_wifi.append('|');
+ newuids_wifi.append(apps[i].uid);
+ }
+ if (apps[i].selected_3g) {
+ if (newuids_3g.length() != 0) newuids_3g.append('|');
+ newuids_3g.append(apps[i].uid);
+ }
+ }
+ // save the new list of UIDs
+ final Editor edit = prefs.edit();
+ edit.putString(PREF_WIFI_UIDS, newuids_wifi.toString());
+ edit.putString(PREF_3G_UIDS, newuids_3g.toString());
+ edit.commit();
+ }
+
+ /**
+ * Purge all iptables rules.
+ * @param ctx mandatory context
+ * @param showErrors indicates if errors should be alerted
+ * @return true if the rules were purged
+ */
+ public static boolean purgeIptables(Context ctx, boolean showErrors) {
+ StringBuilder res = new StringBuilder();
+ try {
+ assertBinaries(ctx, showErrors);
+ int code = runScriptAsRoot(ctx, scriptHeader(ctx) +
+ "$IPTABLES -F droidwall\n" +
+ "$IPTABLES -F droidwall-reject\n" +
+ "$IPTABLES -F droidwall-3g\n" +
+ "$IPTABLES -F droidwall-wifi\n", res);
+ if (code == -1) {
+ if (showErrors) alert(ctx, "error purging iptables. exit code: " + code + "\n" + res);
+ return false;
+ }
+ return true;
+ } catch (Exception e) {
+ if (showErrors) alert(ctx, "error purging iptables: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Display iptables rules output
+ * @param ctx application context
+ */
+ public static void showIptablesRules(Context ctx) {
+ try {
+ final StringBuilder res = new StringBuilder();
+ runScriptAsRoot(ctx, scriptHeader(ctx) +
+ "$ECHO $IPTABLES\n" +
+ "$IPTABLES -L -v\n", res);
+ alert(ctx, res);
+ } catch (Exception e) {
+ alert(ctx, "error: " + e);
+ }
+ }
+
+ /**
+ * Display logs
+ * @param ctx application context
+ * @return true if the clogs were cleared
+ */
+ public static boolean clearLog(Context ctx) {
+ try {
+ final StringBuilder res = new StringBuilder();
+ int code = runScriptAsRoot(ctx, "dmesg -c >/dev/null || exit\n", res);
+ if (code != 0) {
+ alert(ctx, res);
+ return false;
+ }
+ return true;
+ } catch (Exception e) {
+ alert(ctx, "error: " + e);
+ }
+ return false;
+ }
+ /**
+ * Display logs
+ * @param ctx application context
+ */
+ public static void showLog(Context ctx) {
+ try {
+ StringBuilder res = new StringBuilder();
+ int code = runScriptAsRoot(ctx, scriptHeader(ctx) +
+ "dmesg | $GREP DROIDWALL\n", res);
+ if (code != 0) {
+ if (res.length() == 0) {
+ res.append("Log is empty");
+ }
+ alert(ctx, res);
+ return;
+ }
+ final BufferedReader r = new BufferedReader(new StringReader(res.toString()));
+ final Integer unknownUID = -99;
+ res = new StringBuilder();
+ String line;
+ int start, end;
+ Integer appid;
+ final HashMap<Integer, LogInfo> map = new HashMap<Integer, LogInfo>();
+ LogInfo loginfo = null;
+ while ((line = r.readLine()) != null) {
+ if (line.indexOf("[DROIDWALL]") == -1) continue;
+ appid = unknownUID;
+ if (((start=line.indexOf("UID=")) != -1) && ((end=line.indexOf(" ", start)) != -1)) {
+ appid = Integer.parseInt(line.substring(start+4, end));
+ }
+ loginfo = map.get(appid);
+ if (loginfo == null) {
+ loginfo = new LogInfo();
+ map.put(appid, loginfo);
+ }
+ loginfo.totalBlocked += 1;
+ if (((start=line.indexOf("DST=")) != -1) && ((end=line.indexOf(" ", start)) != -1)) {
+ String dst = line.substring(start+4, end);
+ if (loginfo.dstBlocked.containsKey(dst)) {
+ loginfo.dstBlocked.put(dst, loginfo.dstBlocked.get(dst) + 1);
+ } else {
+ loginfo.dstBlocked.put(dst, 1);
+ }
+ }
+ }
+ final DroidApp[] apps = getApps(ctx);
+ for (Integer id : map.keySet()) {
+ res.append("App ID ");
+ if (id != unknownUID) {
+ res.append(id);
+ for (DroidApp app : apps) {
+ if (app.uid == id) {
+ res.append(" (").append(app.names[0]);
+ if (app.names.length > 1) {
+ res.append(", ...)");
+ } else {
+ res.append(")");
+ }
+ break;
+ }
+ }
+ } else {
+ res.append("(kernel)");
+ }
+ loginfo = map.get(id);
+ res.append(" - Blocked ").append(loginfo.totalBlocked).append(" packets");
+ if (loginfo.dstBlocked.size() > 0) {
+ res.append(" (");
+ boolean first = true;
+ for (String dst : loginfo.dstBlocked.keySet()) {
+ if (!first) {
+ res.append(", ");
+ }
+ res.append(loginfo.dstBlocked.get(dst)).append(" packets for ").append(dst);
+ first = false;
+ }
+ res.append(")");
+ }
+ res.append("\n\n");
+ }
+ if (res.length() == 0) {
+ res.append("Log is empty");
+ }
+ alert(ctx, res);
+ } catch (Exception e) {
+ alert(ctx, "error: " + e);
+ }
+ }
+
+ /**
+ * @param ctx application context (mandatory)
+ * @return a list of applications
+ */
+ public static DroidApp[] getApps(Context ctx) {
+ if (applications != null) {
+ // return cached instance
+ return applications;
+ }
+ final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
+ // allowed application names separated by pipe '|' (persisted)
+ final String savedUids_wifi = prefs.getString(PREF_WIFI_UIDS, "");
+ final String savedUids_3g = prefs.getString(PREF_3G_UIDS, "");
+ int selected_wifi[] = new int[0];
+ int selected_3g[] = new int[0];
+ if (savedUids_wifi.length() > 0) {
+ // Check which applications are allowed
+ final StringTokenizer tok = new StringTokenizer(savedUids_wifi, "|");
+ selected_wifi = new int[tok.countTokens()];
+ for (int i=0; i<selected_wifi.length; i++) {
+ final String uid = tok.nextToken();
+ if (!uid.equals("")) {
+ try {
+ selected_wifi[i] = Integer.parseInt(uid);
+ } catch (Exception ex) {
+ selected_wifi[i] = -1;
+ }
+ }
+ }
+ // Sort the array to allow using "Arrays.binarySearch" later
+ Arrays.sort(selected_wifi);
+ }
+ if (savedUids_3g.length() > 0) {
+ // Check which applications are allowed
+ final StringTokenizer tok = new StringTokenizer(savedUids_3g, "|");
+ selected_3g = new int[tok.countTokens()];
+ for (int i=0; i<selected_3g.length; i++) {
+ final String uid = tok.nextToken();
+ if (!uid.equals("")) {
+ try {
+ selected_3g[i] = Integer.parseInt(uid);
+ } catch (Exception ex) {
+ selected_3g[i] = -1;
+ }
+ }
+ }
+ // Sort the array to allow using "Arrays.binarySearch" later
+ Arrays.sort(selected_3g);
+ }
+ try {
+ final PackageManager pkgmanager = ctx.getPackageManager();
+ final List<ApplicationInfo> installed = pkgmanager.getInstalledApplications(0);
+ final HashMap<Integer, DroidApp> map = new HashMap<Integer, DroidApp>();
+ final Editor edit = prefs.edit();
+ boolean changed = false;
+ String name = null;
+ String cachekey = null;
+ DroidApp app = null;
+ for (final ApplicationInfo apinfo : installed) {
+ app = map.get(apinfo.uid);
+ // filter applications which are not allowed to access the Internet
+ if (app == null && PackageManager.PERMISSION_GRANTED != pkgmanager.checkPermission(Manifest.permission.INTERNET, apinfo.packageName)) {
+ continue;
+ }
+ // try to get the application label from our cache - getApplicationLabel() is horribly slow!!!!
+ cachekey = "cache.label."+apinfo.packageName;
+ name = prefs.getString(cachekey, "");
+ if (name.length() == 0) {
+ // get label and put on cache
+ name = pkgmanager.getApplicationLabel(apinfo).toString();
+ edit.putString(cachekey, name);
+ changed = true;
+ }
+ if (app == null) {
+ app = new DroidApp();
+ app.uid = apinfo.uid;
+ app.names = new String[] { name };
+ map.put(apinfo.uid, app);
+ } else {
+ final String newnames[] = new String[app.names.length + 1];
+ System.arraycopy(app.names, 0, newnames, 0, app.names.length);
+ newnames[app.names.length] = name;
+ app.names = newnames;
+ }
+ // check if this application is selected
+ if (!app.selected_wifi && Arrays.binarySearch(selected_wifi, app.uid) >= 0) {
+ app.selected_wifi = true;
+ }
+ if (!app.selected_3g && Arrays.binarySearch(selected_3g, app.uid) >= 0) {
+ app.selected_3g = true;
+ }
+ }
+ if (changed) {
+ edit.commit();
+ }
+ /* add special applications to the list */
+ final DroidApp special[] = {
+ new DroidApp(SPECIAL_UID_ANY,"(Any application) - Same as selecting all applications", false, false),
+ new DroidApp(SPECIAL_UID_KERNEL,"(Kernel) - Linux kernel", false, false),
+ new DroidApp(android.os.Process.getUidForName("root"), "(root) - Applications running as root", false, false),
+ new DroidApp(android.os.Process.getUidForName("media"), "Media server", false, false),
+ new DroidApp(android.os.Process.getUidForName("vpn"), "VPN networking", false, false),
+ new DroidApp(android.os.Process.getUidForName("shell"), "Linux shell", false, false),
+ };
+ for (int i=0; i<special.length; i++) {
+ app = special[i];
+ if (app.uid != -1 && !map.containsKey(app.uid)) {
+ // check if this application is allowed
+ if (Arrays.binarySearch(selected_wifi, app.uid) >= 0) {
+ app.selected_wifi = true;
+ }
+ if (Arrays.binarySearch(selected_3g, app.uid) >= 0) {
+ app.selected_3g = true;
+ }
+ map.put(app.uid, app);
+ }
+ }
+ applications = new DroidApp[map.size()];
+ int index = 0;
+ for (DroidApp application : map.values()) applications[index++] = application;
+ return applications;
+ } catch (Exception e) {
+ alert(ctx, "error: " + e);
+ }
+ return null;
+ }
+ /**
+ * Check if we have root access
+ * @param ctx mandatory context
+ * @param showErrors indicates if errors should be alerted
+ * @return boolean true if we have root
+ */
+ public static boolean hasRootAccess(Context ctx, boolean showErrors) {
+ if (hasroot) return true;
+ final StringBuilder res = new StringBuilder();
+ try {
+ // Run an empty script just to check root access
+ if (runScriptAsRoot(ctx, "exit 0", res) == 0) {
+ hasroot = true;
+ return true;
+ }
+ } catch (Exception e) {
+ }
+ if (showErrors) {
+ alert(ctx, "Could not acquire root access.\n" +
+ "You need a rooted phone to run DroidWall.\n\n" +
+ "If this phone is already rooted, please make sure DroidWall has enough permissions to execute the \"su\" command.\n" +
+ "Error message: " + res.toString());
+ }
+ return false;
+ }
+ /**
+ * Runs a script, wither as root or as a regular user (multiple commands separated by "\n").
+ * @param ctx mandatory context
+ * @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 runScript(Context ctx, String script, StringBuilder res, long timeout, boolean asroot) {
+ final File file = new File(ctx.getDir("bin",0), SCRIPT_FILE);
+ final ScriptRunner runner = new ScriptRunner(file, script, res, asroot);
+ runner.start();
+ try {
+ if (timeout > 0) {
+ runner.join(timeout);
+ } else {
+ runner.join();
+ }
+ if (runner.isAlive()) {
+ // Timed-out
+ runner.interrupt();
+ runner.join(150);
+ runner.destroy();
+ runner.join(50);
+ }
+ } catch (InterruptedException ex) {}
+ return runner.exitcode;
+ }
+ /**
+ * Runs a script as root (multiple commands separated by "\n").
+ * @param ctx mandatory context
+ * @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(Context ctx, String script, StringBuilder res, long timeout) {
+ return runScript(ctx, script, res, timeout, true);
+ }
+ /**
+ * Runs a script as root (multiple commands separated by "\n") with a default timeout of 20 seconds.
+ * @param ctx mandatory context
+ * @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(Context ctx, String script, StringBuilder res) throws IOException {
+ return runScriptAsRoot(ctx, script, res, 40000);
+ }
+ /**
+ * Runs a script as a regular user (multiple commands separated by "\n") with a default timeout of 20 seconds.
+ * @param ctx mandatory context
+ * @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 runScript(Context ctx, String script, StringBuilder res) throws IOException {
+ return runScript(ctx, script, res, 40000, false);
+ }
+ /**
+ * Asserts that the binary files are installed in the cache directory.
+ * @param ctx context
+ * @param showErrors indicates if errors should be alerted
+ * @return false if the binary files could not be installed
+ */
+ public static boolean assertBinaries(Context ctx, boolean showErrors) {
+ boolean changed = false;
+ try {
+ // Check iptables_g1
+ File file = new File(ctx.getDir("bin",0), "iptables");
+
+ if ((!file.exists()) && isARMv6()) {
+ copyRawFile(ctx, R.raw.iptables_g1, file, "755");
+ changed = true;
+ }
+
+ // Check iptables_n1
+ file = new File(ctx.getDir("bin",0), "iptables");
+ if ((!file.exists()) && (!isARMv6())) {
+ copyRawFile(ctx, R.raw.iptables_n1, file, "755");
+ changed = true;
+ }
+
+ // Check busybox
+ /*
+ file = new File(ctx.getDir("bin",0), "busybox_g1");
+ if (!file.exists()) {
+ copyRawFile(ctx, R.raw.busybox_g1, file, "755");
+ changed = true;
+ }
+ */
+
+ if (changed) {
+ Toast.makeText(ctx, R.string.status_install_success, Toast.LENGTH_LONG).show();
+ }
+ } catch (Exception e) {
+ if (showErrors) alert(ctx, "Error installing binary files: " + e);
+ return false;
+ }
+ return true;
+ }
+ /**
+ * Check if the firewall is enabled
+ * @param ctx mandatory context
+ * @return boolean
+ */
+ public static boolean isEnabled(Context ctx) {
+ if (ctx == null) return false;
+ return ctx.getSharedPreferences(PREFS_NAME, 0).getBoolean(PREF_ENABLED, false);
+ }
+
+ /**
+ * Defines if the firewall is enabled and broadcasts the new status
+ * @param ctx mandatory context
+ * @param enabled enabled flag
+ */
+ public static void setEnabled(Context ctx, boolean enabled) {
+ if (ctx == null) return;
+ final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
+ if (prefs.getBoolean(PREF_ENABLED, false) == enabled) {
+ return;
+ }
+ final Editor edit = prefs.edit();
+ edit.putBoolean(PREF_ENABLED, enabled);
+ if (!edit.commit()) {
+ alert(ctx, "Error writing to preferences");
+ return;
+ }
+ /* notify */
+ final Intent message = new Intent(IptablesManager.STATUS_CHANGED_MSG);
+ message.putExtra(IptablesManager.STATUS_EXTRA, enabled);
+ ctx.sendBroadcast(message);
+ }
+ /**
+ * Called when an application in removed (un-installed) from the system.
+ * This will look for that application in the selected list and update the persisted values if necessary
+ * @param ctx mandatory app context
+ * @param uid UID of the application that has been removed
+ */
+ public static void applicationRemoved(Context ctx, int uid) {
+ final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
+ final Editor editor = prefs.edit();
+ // allowed application names separated by pipe '|' (persisted)
+ final String savedUids_wifi = prefs.getString(PREF_WIFI_UIDS, "");
+ final String savedUids_3g = prefs.getString(PREF_3G_UIDS, "");
+ final String uid_str = uid + "";
+ boolean changed = false;
+ // look for the removed application in the "wi-fi" list
+ if (savedUids_wifi.length() > 0) {
+ final StringBuilder newuids = new StringBuilder();
+ final StringTokenizer tok = new StringTokenizer(savedUids_wifi, "|");
+ while (tok.hasMoreTokens()) {
+ final String token = tok.nextToken();
+ if (uid_str.equals(token)) {
+ Log.d("DroidWall", "Removing UID " + token + " from the wi-fi list (package removed)!");
+ changed = true;
+ } else {
+ if (newuids.length() > 0) newuids.append('|');
+ newuids.append(token);
+ }
+ }
+ if (changed) {
+ editor.putString(PREF_WIFI_UIDS, newuids.toString());
+ }
+ }
+ // look for the removed application in the "3g" list
+ if (savedUids_3g.length() > 0) {
+ final StringBuilder newuids = new StringBuilder();
+ final StringTokenizer tok = new StringTokenizer(savedUids_3g, "|");
+ while (tok.hasMoreTokens()) {
+ final String token = tok.nextToken();
+ if (uid_str.equals(token)) {
+ Log.d("DroidWall", "Removing UID " + token + " from the 3G list (package removed)!");
+ changed = true;
+ } else {
+ if (newuids.length() > 0) newuids.append('|');
+ newuids.append(token);
+ }
+ }
+ if (changed) {
+ editor.putString(PREF_3G_UIDS, newuids.toString());
+ }
+ }
+ // if anything has changed, save the new prefs...
+ if (changed) {
+ editor.commit();
+ if (isEnabled(ctx)) {
+ // .. and also re-apply the rules if the firewall is enabled
+ applySavedIptablesRules(ctx, false);
+ }
+ }
+ }
+
+ /**
+ * Small structure to hold an application info
+ */
+ public static final class DroidApp {
+ /** linux user id */
+ int uid;
+ /** application names belonging to this user id */
+ String names[];
+ /** indicates if this application is selected for wifi */
+ boolean selected_wifi;
+ /** indicates if this application is selected for 3g */
+ boolean selected_3g;
+ /** toString cache */
+ String tostr;
+
+ public DroidApp() {
+ }
+ public DroidApp(int uid, String name, boolean selected_wifi, boolean selected_3g) {
+ this.uid = uid;
+ this.names = new String[] {name};
+ this.selected_wifi = selected_wifi;
+ this.selected_3g = selected_3g;
+ }
+ /**
+ * Screen representation of this application
+ */
+ @Override
+ public String toString() {
+ if (tostr == null) {
+ final StringBuilder s = new StringBuilder();
+ if (uid > 0) s.append(uid + ": ");
+ for (int i=0; i<names.length; i++) {
+ if (i != 0) s.append(", ");
+ s.append(names[i]);
+ }
+ s.append("\n");
+ tostr = s.toString();
+ }
+ return tostr;
+ }
+ }
+ /**
+ * Small internal structure used to hold log information
+ */
+ private static final class LogInfo {
+ private int totalBlocked; // Total number of packets blocked
+ private HashMap<String, Integer> dstBlocked; // Number of packets blocked per destination IP address
+ private LogInfo() {
+ this.dstBlocked = new HashMap<String, Integer>();
+ }
+ }
+ /**
+ * Internal thread used to execute scripts (as root or not).
+ */
+ private static final class ScriptRunner extends Thread {
+ private final File file;
+ private final String script;
+ private final StringBuilder res;
+ private final boolean asroot;
+ public int exitcode = -1;
+ private Process exec;
+
+ /**
+ * Creates a new script runner.
+ * @param file temporary script file
+ * @param script script to run
+ * @param res response output
+ * @param asroot if true, executes the script as root
+ */
+ public ScriptRunner(File file, String script, StringBuilder res, boolean asroot) {
+ this.file = file;
+ this.script = script;
+ this.res = res;
+ this.asroot = asroot;
+ }
+ @Override
+ public void run() {
+ try {
+ file.createNewFile();
+ final String abspath = file.getAbsolutePath();
+ // make sure we have execution permission on the script file
+ Runtime.getRuntime().exec("chmod 777 "+abspath).waitFor();
+ // Write the script to be executed
+ final OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file));
+ if (new File("/system/bin/sh").exists()) {
+ out.write("#!/system/bin/sh\n");
+ }
+ out.write(script);
+ if (!script.endsWith("\n")) out.write("\n");
+ out.write("exit\n");
+ out.flush();
+ out.close();
+ if (this.asroot) {
+ // Create the "su" request to run the script
+ exec = Runtime.getRuntime().exec("su -c "+abspath);
+ } else {
+ // Create the "sh" request to run the script
+ exec = Runtime.getRuntime().exec("sh "+abspath);
+ }
+ InputStreamReader r = new InputStreamReader(exec.getInputStream());
+ final char buf[] = new char[1024];
+ int read = 0;
+ // Consume the "stdout"
+ 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;
+ }
+ }
+}
diff --git a/src/org/torproject/android/service/TorService.java b/src/org/torproject/android/service/TorService.java
index 1b11492..57c43b0 100644
--- a/src/org/torproject/android/service/TorService.java
+++ b/src/org/torproject/android/service/TorService.java
@@ -416,16 +416,12 @@ public class TorService extends Service implements TorServiceConstants, Runnable
private boolean checkTorBinaries () throws Exception
{
- //android.os.Debug.waitForDebugger();
-
//check and install iptables
- Api.assertBinaries(this, true);
+ IptablesManager.assertBinaries(this, true);
- File fileInstall = getDir("",0);
- String subBinPath = "bin/";
+ File fileInstall = getDir("bin/",0);
appHome = fileInstall.getAbsolutePath();
- appBinHome = appHome + subBinPath;
appDataHome = getCacheDir().getAbsolutePath() + '/';
logNotice( "appHome=" + appHome);
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits