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

Side by Side Diff: src/org/adblockplus/android/configurators/IptablesProxyConfigurator.java

Issue 5624117646589952: Issue 1771 - Update RootTools (Closed)
Patch Set: Updated to v3.4 as v3.3 wasn't the latest version Created Nov. 6, 2014, 4:06 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « libs/RootTools-3.4.jar ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2014 Eyeo GmbH 3 * Copyright (C) 2006-2014 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 package org.adblockplus.android.configurators; 18 package org.adblockplus.android.configurators;
19 19
20 import java.io.File; 20 import java.io.File;
21 import java.io.FileNotFoundException; 21 import java.io.FileNotFoundException;
22 import java.io.IOException;
22 import java.net.InetAddress; 23 import java.net.InetAddress;
24 import java.util.ArrayList;
23 import java.util.List; 25 import java.util.List;
26 import java.util.concurrent.Semaphore;
27 import java.util.concurrent.TimeoutException;
24 28
25 import org.adblockplus.android.Utils; 29 import org.adblockplus.android.Utils;
26 30
27 import com.stericson.RootTools.RootTools;
28
29 import android.content.Context; 31 import android.content.Context;
30 import android.util.Log; 32 import android.util.Log;
31 33
34 import com.stericson.RootTools.RootTools;
35 import com.stericson.RootTools.exceptions.RootDeniedException;
36 import com.stericson.RootTools.execution.Command;
37 import com.stericson.RootTools.execution.Shell;
38
32 /** 39 /**
33 * Proxy registration using RootTools and iptables. 40 * Proxy registration using RootTools and iptables.
34 */ 41 */
35 public class IptablesProxyConfigurator implements ProxyConfigurator 42 public class IptablesProxyConfigurator implements ProxyConfigurator
36 { 43 {
37 private static final String TAG = Utils.getTag(IptablesProxyConfigurator.class ); 44 private static final String TAG = Utils.getTag(IptablesProxyConfigurator.class );
38 private static final int DEFAULT_TIMEOUT = 3000; 45 private static final int DEFAULT_TIMEOUT = 3000;
39 private static final String IPTABLES_RETURN = " -t nat -m owner --uid-owner {{ UID}} -A OUTPUT -p tcp -j RETURN\n"; 46 private static final String IPTABLES_RETURN = " -t nat -m owner --uid-owner {{ UID}} -A OUTPUT -p tcp -j RETURN\n";
40 private static final String IPTABLES_ADD_HTTP = " -t nat -A OUTPUT -p tcp --dp ort 80 -j REDIRECT --to {{PORT}}\n"; 47 private static final String IPTABLES_ADD_HTTP = " -t nat -A OUTPUT -p tcp --dp ort 80 -j REDIRECT --to {{PORT}}\n";
41 48
42 private final Context context; 49 private final Context context;
43 private String iptables; 50 private String iptables;
44 private boolean isRegistered = false; 51 private boolean isRegistered = false;
45 52
46 public IptablesProxyConfigurator(final Context context) 53 public IptablesProxyConfigurator(final Context context)
47 { 54 {
48 this.context = context; 55 this.context = context;
49 } 56 }
50 57
58 private static List<String> sendShell(final String command, final int timeout) throws IOException, TimeoutException,
Felix Dahlke 2014/11/19 10:38:39 I think "sendShell" is not a great name, I can see
René Jeschke 2014/11/24 13:07:57 Right, makes sense, will change this.
59 RootDeniedException
60 {
61 final CommandOutput cmd = new CommandOutput(0, DEFAULT_TIMEOUT, command);
62
63 Shell.runRootCommand(cmd);
64
65 cmd.waitForCompletion();
66
67 return cmd.output;
68 }
69
51 @Override 70 @Override
52 public boolean initialize() 71 public boolean initialize()
53 { 72 {
54 try 73 try
55 { 74 {
75 // If we don't set `handlerEnabled` to `false`, RootTools uses Handlers
76 // which get executed on the UI thread which in fact renders it useless
77 // for our purpose (as it either finished too late or blocks forever).
78 RootTools.handlerEnabled = false;
79
56 if (!RootTools.isAccessGiven()) 80 if (!RootTools.isAccessGiven())
57 { 81 {
58 throw new IllegalStateException("No root access"); 82 throw new IllegalStateException("No root access");
59 } 83 }
60 84
61 final File ipt = this.context.getFileStreamPath("iptables"); 85 final File ipt = this.context.getFileStreamPath("iptables");
62 86
63 if (!ipt.exists()) 87 if (!ipt.exists())
64 { 88 {
65 throw new FileNotFoundException("No iptables executable"); 89 throw new FileNotFoundException("No iptables executable");
66 } 90 }
67 91
68 final String path = ipt.getAbsolutePath(); 92 final String path = ipt.getAbsolutePath();
69 93
70 RootTools.sendShell("chmod 700 " + path, DEFAULT_TIMEOUT); 94 sendShell("chmod 700 " + path, DEFAULT_TIMEOUT);
71 95
72 boolean compatible = false; 96 boolean compatible = false;
73 boolean version = false; 97 boolean version = false;
74 98
75 final String command = path + " --version\n" + path + " -L -t nat -n\n"; 99 final String command = path + " --version\n" + path + " -L -t nat -n\n";
76 100
77 final List<String> result = RootTools.sendShell(command, DEFAULT_TIMEOUT); 101 final List<String> result = sendShell(command, DEFAULT_TIMEOUT);
78 102
79 for (final String line : result) 103 for (final String line : result)
80 { 104 {
81 if (line.contains("OUTPUT")) 105 if (line.contains("OUTPUT"))
82 { 106 {
83 compatible = true; 107 compatible = true;
84 } 108 }
85 if (line.contains("v1.4.")) 109 if (line.contains("v1.4."))
86 { 110 {
87 version = true; 111 version = true;
(...skipping 22 matching lines...) Expand all
110 { 134 {
111 final int uid = this.context.getPackageManager().getPackageInfo(this.conte xt.getPackageName(), 0).applicationInfo.uid; 135 final int uid = this.context.getPackageManager().getPackageInfo(this.conte xt.getPackageName(), 0).applicationInfo.uid;
112 136
113 final StringBuilder cmd = new StringBuilder(); 137 final StringBuilder cmd = new StringBuilder();
114 cmd.append(this.iptables); 138 cmd.append(this.iptables);
115 cmd.append(IPTABLES_RETURN.replace("{{UID}}", String.valueOf(uid))); 139 cmd.append(IPTABLES_RETURN.replace("{{UID}}", String.valueOf(uid)));
116 cmd.append('\n'); 140 cmd.append('\n');
117 cmd.append(this.iptables); 141 cmd.append(this.iptables);
118 cmd.append(IPTABLES_ADD_HTTP.replace("{{PORT}}", String.valueOf(port))); 142 cmd.append(IPTABLES_ADD_HTTP.replace("{{PORT}}", String.valueOf(port)));
119 143
120 RootTools.sendShell(cmd.toString(), DEFAULT_TIMEOUT); 144 sendShell(cmd.toString(), DEFAULT_TIMEOUT);
121 145
122 this.isRegistered = true; 146 this.isRegistered = true;
123 147
124 return true; 148 return true;
125 } 149 }
126 catch (final Exception e) 150 catch (final Exception e)
127 { 151 {
128 // I leave this logging message for now, passing 'init' and failing 'regis ter' definitely is a failure 152 // I leave this logging message for now, passing 'init' and failing 'regis ter' definitely is a failure
129 Log.e(TAG, "Couldn't register proxy using iptables.", e); 153 Log.e(TAG, "Couldn't register proxy using iptables.", e);
130 return false; 154 return false;
131 } 155 }
132 } 156 }
133 157
134 @Override 158 @Override
135 public void unregisterProxy() 159 public void unregisterProxy()
136 { 160 {
137 try 161 try
138 { 162 {
139 RootTools.sendShell(this.iptables + " -t nat -F OUTPUT", DEFAULT_TIMEOUT); 163 sendShell(this.iptables + " -t nat -F OUTPUT", DEFAULT_TIMEOUT);
140 } 164 }
141 catch (final Exception e) 165 catch (final Exception e)
142 { 166 {
143 Log.w(TAG, "Failed to unregister proxy using iptables.", e); 167 Log.w(TAG, "Failed to unregister proxy using iptables.", e);
144 } 168 }
145 finally 169 finally
146 { 170 {
147 this.isRegistered = false; 171 this.isRegistered = false;
148 } 172 }
149 } 173 }
(...skipping 15 matching lines...) Expand all
165 189
166 final File ipt = context.getFileStreamPath("iptables"); 190 final File ipt = context.getFileStreamPath("iptables");
167 191
168 if (!ipt.exists()) 192 if (!ipt.exists())
169 { 193 {
170 throw new FileNotFoundException("No iptables executable"); 194 throw new FileNotFoundException("No iptables executable");
171 } 195 }
172 196
173 final String path = ipt.getAbsolutePath(); 197 final String path = ipt.getAbsolutePath();
174 198
175 RootTools.sendShell("chmod 700 " + path, DEFAULT_TIMEOUT); 199 sendShell("chmod 700 " + path, DEFAULT_TIMEOUT);
176 200
177 boolean compatible = false; 201 boolean compatible = false;
178 boolean version = false; 202 boolean version = false;
179 203
180 String command = path + " --version\n" + path + " -L -t nat -n\n"; 204 String command = path + " --version\n" + path + " -L -t nat -n\n";
181 205
182 final List<String> result = RootTools.sendShell(command, DEFAULT_TIMEOUT); 206 final List<String> result = sendShell(command, DEFAULT_TIMEOUT);
183 207
184 for (final String line : result) 208 for (final String line : result)
185 { 209 {
186 if (line.contains("OUTPUT")) 210 if (line.contains("OUTPUT"))
187 { 211 {
188 compatible = true; 212 compatible = true;
189 } 213 }
190 if (line.contains("v1.4.")) 214 if (line.contains("v1.4."))
191 { 215 {
192 version = true; 216 version = true;
193 } 217 }
194 } 218 }
195 219
196 if (!(compatible && version)) 220 if (!(compatible && version))
197 { 221 {
198 throw new IllegalStateException("Incompatible iptables excutable"); 222 throw new IllegalStateException("Incompatible iptables excutable");
199 } 223 }
200 224
201 command = path + " -L -t nat -n\n"; 225 command = path + " -L -t nat -n\n";
202 226
203 return RootTools.sendShell(command, DEFAULT_TIMEOUT); 227 return sendShell(command, DEFAULT_TIMEOUT);
204 } 228 }
205 catch (final Throwable t) 229 catch (final Throwable t)
206 { 230 {
207 return null; 231 return null;
208 } 232 }
209 } 233 }
210 234
211 @Override 235 @Override
212 public ProxyRegistrationType getType() 236 public ProxyRegistrationType getType()
213 { 237 {
(...skipping 10 matching lines...) Expand all
224 public boolean isSticky() 248 public boolean isSticky()
225 { 249 {
226 return false; 250 return false;
227 } 251 }
228 252
229 @Override 253 @Override
230 public String toString() 254 public String toString()
231 { 255 {
232 return "[ProxyConfigurator: " + this.getType() + "]"; 256 return "[ProxyConfigurator: " + this.getType() + "]";
233 } 257 }
258
259 private final static class CommandOutput extends Command
Felix Dahlke 2014/11/19 10:38:39 Shouldn't this rather be "OutputCommand" (or anyth
René Jeschke 2014/11/24 13:07:57 Right, will also change this.
260 {
261 private final Semaphore completionNotify = new Semaphore(1);
Felix Dahlke 2014/11/19 10:38:39 This confused me a little bit, being a verb (bit u
René Jeschke 2014/11/24 13:07:57 "running" as name for a flag would mean sense, but
René Jeschke 2015/01/23 13:37:36 Ok, I changed this to running as I don't have a be
Felix Dahlke 2015/01/27 08:45:36 True, but I figure for a semaphore with one permit
262
263 public List<String> output = new ArrayList<String>();
264
265 public CommandOutput(final int id, final int timeout, final String command)
266 {
267 super(id, timeout, command);
268
269 this.completionNotify.acquireUninterruptibly();
270 }
271
272 @Override
273 public void commandOutput(int id, String line)
274 {
275 this.output.add(line);
276 }
277
278 @Override
279 public void commandCompleted(int id, int exitCode)
280 {
281 this.completionNotify.release();
282 }
283
284 @Override
285 public void commandTerminated(int id, String reason)
286 {
287 this.completionNotify.release();
288 }
289
290 public void waitForCompletion()
291 {
292 this.completionNotify.acquireUninterruptibly();
293 }
294 }
234 } 295 }
OLDNEW
« no previous file with comments | « libs/RootTools-3.4.jar ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld