Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: src/org/adblockplus/android/configurators/IptablesProxyConfigurator.java

Issue 4705284891082752: Proxy configurators (Closed)
Patch Set: Last batch of review issues Created Aug. 24, 2014, 11:52 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/org/adblockplus/android/configurators/IptablesProxyConfigurator.java
diff --git a/src/org/adblockplus/android/configurators/IptablesProxyConfigurator.java b/src/org/adblockplus/android/configurators/IptablesProxyConfigurator.java
new file mode 100644
index 0000000000000000000000000000000000000000..0017d5aa5089caf618c35ce77c8a5b5bfc9ee370
--- /dev/null
+++ b/src/org/adblockplus/android/configurators/IptablesProxyConfigurator.java
@@ -0,0 +1,234 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus 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 Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.adblockplus.android.configurators;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.InetAddress;
+import java.util.List;
+
+import org.adblockplus.android.Utils;
+
+import com.stericson.RootTools.RootTools;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Proxy registration using RootTools and iptables.
+ */
+public class IptablesProxyConfigurator implements ProxyConfigurator
+{
+ private static final String TAG = Utils.getTag(IptablesProxyConfigurator.class);
+ private static final int DEFAULT_TIMEOUT = 3000;
+ private static final String IPTABLES_RETURN = " -t nat -m owner --uid-owner {{UID}} -A OUTPUT -p tcp -j RETURN\n";
+ private static final String IPTABLES_ADD_HTTP = " -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to {{PORT}}\n";
+
+ private final Context context;
+ private String iptables;
+ private boolean isRegistered = false;
+
+ public IptablesProxyConfigurator(final Context context)
+ {
+ this.context = context;
+ }
+
+ @Override
+ public boolean initialize()
+ {
+ try
+ {
+ if (!RootTools.isAccessGiven())
+ {
+ throw new IllegalStateException("No root access");
+ }
+
+ final File ipt = this.context.getFileStreamPath("iptables");
+
+ if (!ipt.exists())
+ {
+ throw new FileNotFoundException("No iptables executable");
+ }
+
+ final String path = ipt.getAbsolutePath();
+
+ RootTools.sendShell("chmod 700 " + path, DEFAULT_TIMEOUT);
+
+ boolean compatible = false;
+ boolean version = false;
+
+ final String command = path + " --version\n" + path + " -L -t nat -n\n";
+
+ final List<String> result = RootTools.sendShell(command, DEFAULT_TIMEOUT);
+
+ for (final String line : result)
+ {
+ if (line.contains("OUTPUT"))
+ {
+ compatible = true;
+ }
+ if (line.contains("v1.4."))
+ {
+ version = true;
+ }
+ }
+
+ if (!(compatible && version))
+ {
+ throw new IllegalStateException("Incompatible iptables excutable");
+ }
+
+ this.iptables = path;
+
+ return true;
+ }
+ catch (final Exception e)
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean registerProxy(final InetAddress address, final int port)
+ {
+ try
+ {
+ final int uid = this.context.getPackageManager().getPackageInfo(this.context.getPackageName(), 0).applicationInfo.uid;
+
+ final StringBuilder cmd = new StringBuilder();
+ cmd.append(this.iptables);
+ cmd.append(IPTABLES_RETURN.replace("{{UID}}", String.valueOf(uid)));
+ cmd.append('\n');
+ cmd.append(this.iptables);
+ cmd.append(IPTABLES_ADD_HTTP.replace("{{PORT}}", String.valueOf(port)));
+
+ RootTools.sendShell(cmd.toString(), DEFAULT_TIMEOUT);
+
+ this.isRegistered = true;
+
+ return true;
+ }
+ catch (final Exception e)
+ {
+ // I leave this logging message for now, passing 'init' and failing 'register' definitely is a failure
+ Log.e(TAG, "Couldn't register proxy using iptables.", e);
+ return false;
+ }
+ }
+
+ @Override
+ public void unregisterProxy()
+ {
+ try
+ {
+ RootTools.sendShell(this.iptables + " -t nat -F OUTPUT", DEFAULT_TIMEOUT);
+ }
+ catch (final Exception e)
+ {
+ Log.w(TAG, "Failed to unregister proxy using iptables.", e);
+ }
+ finally
+ {
+ this.isRegistered = false;
+ }
+ }
+
+ @Override
+ public void shutdown()
+ {
+ // Nothing to do here
+ }
+
+ public static List<String> getIptablesOutput(final Context context)
+ {
+ try
+ {
+ if (!RootTools.isAccessGiven())
+ {
+ throw new IllegalStateException("No root access");
+ }
+
+ final File ipt = context.getFileStreamPath("iptables");
+
+ if (!ipt.exists())
+ {
+ throw new FileNotFoundException("No iptables executable");
+ }
+
+ final String path = ipt.getAbsolutePath();
+
+ RootTools.sendShell("chmod 700 " + path, DEFAULT_TIMEOUT);
+
+ boolean compatible = false;
+ boolean version = false;
+
+ String command = path + " --version\n" + path + " -L -t nat -n\n";
+
+ final List<String> result = RootTools.sendShell(command, DEFAULT_TIMEOUT);
+
+ for (final String line : result)
+ {
+ if (line.contains("OUTPUT"))
+ {
+ compatible = true;
+ }
+ if (line.contains("v1.4."))
+ {
+ version = true;
+ }
+ }
+
+ if (!(compatible && version))
+ {
+ throw new IllegalStateException("Incompatible iptables excutable");
+ }
+
+ command = path + " -L -t nat -n\n";
+
+ return RootTools.sendShell(command, DEFAULT_TIMEOUT);
+ }
+ catch (final Throwable t)
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public ProxyRegistrationType getType()
+ {
+ return ProxyRegistrationType.IPTABLES;
+ }
+
+ @Override
+ public boolean isRegistered()
+ {
+ return this.isRegistered;
+ }
+
+ @Override
+ public boolean isSticky()
+ {
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[ProxyConfigurator: " + this.getType() + "]";
+ }
+}

Powered by Google App Engine
This is Rietveld