From d842714b5d89716e3875f5faa0c4e5b13a5b5944 Mon Sep 17 00:00:00 2001 From: Amogh Pradeep Date: Fri, 12 Jun 2015 02:07:45 -0400 Subject: [PATCH] Orfox: Centralized proxy applied to AbstractCommunicator and BaseResources. See Bug 1357997 for partial uplift. Also: Bug 28051 - Use our Orbot for proxying our connections Bug 31144 - ESR68 Network Code Review --- .../org/mozilla/gecko/GeckoApplication.java | 12 +++- .../gecko/customtabs/CustomTabsActivity.java | 21 +++--- .../gecko/widget/GeckoActionProvider.java | 10 ++- .../java/org/mozilla/gecko/GeckoAppShell.java | 68 ++++++++++--------- .../org/mozilla/gecko/util/BitmapUtils.java | 13 +--- .../org/mozilla/gecko/util/ProxySelector.java | 25 ++++++- .../mozilla/gecko/sync/net/BaseResource.java | 7 ++ .../service/utils/AbstractCommunicator.java | 11 ++- 8 files changed, 105 insertions(+), 62 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java index dbd57d72ebc8f..99630b0bc2d95 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java @@ -61,6 +61,7 @@ import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.IntentUtils; import org.mozilla.gecko.util.PRNGFixes; +import org.mozilla.gecko.util.ProxySelector; import org.mozilla.gecko.util.ShortcutUtils; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.GeckoRuntime; @@ -71,7 +72,7 @@ import java.io.File; import java.io.InputStream; import java.io.IOException; import java.lang.reflect.Method; -import java.net.URL; +import java.net.URI; import java.util.UUID; public class GeckoApplication extends Application @@ -861,11 +862,16 @@ public class GeckoApplication extends Application byte[] buf = Base64.decode(aSrc.substring(dataStart + 1), Base64.DEFAULT); image = BitmapUtils.decodeByteArray(buf); } else { + URI uri; int byteRead; byte[] buf = new byte[4192]; os = new ByteArrayOutputStream(); - URL url = new URL(aSrc); - is = url.openStream(); + try { + uri = new URI(aSrc); + } catch (Exception e) { + return; + } + is = ProxySelector.openConnectionWithProxy(uri).getInputStream(); // Cannot read from same stream twice. Also, InputStream from // URL does not support reset. So converting to byte array. diff --git a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java index 19514c33ac668..420ef6c2876f3 100644 --- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java +++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java @@ -359,16 +359,19 @@ public class CustomTabsActivity extends AppCompatActivity } private void performPendingIntent(@NonNull PendingIntent pendingIntent) { + // Bug 31144 - Fail and return early, prevent potential proxy-bypass. + return; + // bug 1337771: If intent-creator haven't set data url, call send() directly won't work. - final Intent additional = new Intent(); - if (!TextUtils.isEmpty(mCurrentUrl)) { - additional.setData(Uri.parse(mCurrentUrl)); - } - try { - pendingIntent.send(this, 0, additional); - } catch (PendingIntent.CanceledException e) { - Log.w(LOGTAG, "Performing a canceled pending intent", e); - } + //final Intent additional = new Intent(); + //if (!TextUtils.isEmpty(mCurrentUrl)) { + // additional.setData(Uri.parse(mCurrentUrl)); + //} + //try { + // pendingIntent.send(this, 0, additional); + //} catch (PendingIntent.CanceledException e) { + // Log.w(LOGTAG, "Performing a canceled pending intent", e); + //} } /** diff --git a/mobile/android/base/java/org/mozilla/gecko/widget/GeckoActionProvider.java b/mobile/android/base/java/org/mozilla/gecko/widget/GeckoActionProvider.java index ef3b306f5914f..761029a429aa7 100644 --- a/mobile/android/base/java/org/mozilla/gecko/widget/GeckoActionProvider.java +++ b/mobile/android/base/java/org/mozilla/gecko/widget/GeckoActionProvider.java @@ -23,6 +23,7 @@ import org.mozilla.gecko.overlays.ui.ShareDialog; import org.mozilla.gecko.menu.MenuItemSwitcherLayout; import org.mozilla.gecko.util.IOUtils; import org.mozilla.gecko.util.IntentUtils; +import org.mozilla.gecko.util.ProxySelector; import org.mozilla.gecko.util.ThreadUtils; import android.content.Context; @@ -44,7 +45,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; @@ -344,9 +346,9 @@ public class GeckoActionProvider { InputStream is = null; try { final byte[] buf = new byte[2048]; - final URL url = new URL(src); + final URI uri = new URI(src); final String filename = URLUtil.guessFileName(src, null, type); - is = url.openStream(); + is = ProxySelector.openConnectionWithProxy(uri).getInputStream(); final File imageFile = new File(dir, filename); os = new FileOutputStream(imageFile); @@ -362,6 +364,8 @@ public class GeckoActionProvider { IOUtils.safeStreamClose(is); } } + } catch (URISyntaxException ex) { + // Handle this the same way as IOException } catch (IOException ex) { // If something went wrong, we'll just leave the intent un-changed } finally { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java index 03486a12c22e2..c38e31dc3b0e5 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -1810,39 +1810,41 @@ public class GeckoAppShell { @WrapForJNI private static URLConnection getConnection(final String url) { - try { - String spec; - if (url.startsWith("android://")) { - spec = url.substring(10); - } else { - spec = url.substring(8); - } - - // Check if we are loading a package icon. - try { - if (spec.startsWith("icon/")) { - String[] splits = spec.split("/"); - if (splits.length != 2) { - return null; - } - final String pkg = splits[1]; - final PackageManager pm = getApplicationContext().getPackageManager(); - final Drawable d = pm.getApplicationIcon(pkg); - final Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(d); - return new BitmapConnection(bitmap); - } - } catch (Exception ex) { - Log.e(LOGTAG, "error", ex); - } - - // if the colon got stripped, put it back - int colon = spec.indexOf(':'); - if (colon == -1 || colon > spec.indexOf('/')) { - spec = spec.replaceFirst("/", ":/"); - } - } catch (Exception ex) { - return null; - } + // Bug 31144 - Prevent potential proxy-bypass + + //try { + // String spec; + // if (url.startsWith("android://")) { + // spec = url.substring(10); + // } else { + // spec = url.substring(8); + // } + + // // Check if we are loading a package icon. + // try { + // if (spec.startsWith("icon/")) { + // String[] splits = spec.split("/"); + // if (splits.length != 2) { + // return null; + // } + // final String pkg = splits[1]; + // final PackageManager pm = getApplicationContext().getPackageManager(); + // final Drawable d = pm.getApplicationIcon(pkg); + // final Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(d); + // return new BitmapConnection(bitmap); + // } + // } catch (Exception ex) { + // Log.e(LOGTAG, "error", ex); + // } + + // // if the colon got stripped, put it back + // int colon = spec.indexOf(':'); + // if (colon == -1 || colon > spec.indexOf('/')) { + // spec = spec.replaceFirst("/", ":/"); + // } + //} catch (Exception ex) { + // return null; + //} return null; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BitmapUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BitmapUtils.java index 2a3f08de4ae7f..b5e1a9664b35e 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BitmapUtils.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BitmapUtils.java @@ -103,16 +103,9 @@ public final class BitmapUtils { public static Bitmap decodeUrl(final URL url) { InputStream stream = null; - try { - if ("jar".equals(url.getProtocol())) { - final Context context = GeckoAppShell.getApplicationContext(); - stream = GeckoJarReader.getStream(context, url.toString()); - } else { - stream = url.openStream(); - } - } catch (IOException e) { - Log.w(LOGTAG, "decodeUrl: IOException downloading " + url); - return null; + if ("jar".equals(url.getProtocol())) { + final Context context = GeckoAppShell.getApplicationContext(); + stream = GeckoJarReader.getStream(context, url.toString()); } if (stream == null) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java index 3940d3c842490..9515975f680ad 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java @@ -29,6 +29,10 @@ import java.net.URLConnection; import java.util.List; public class ProxySelector { + private static final String TOR_PROXY_ADDRESS = "127.0.0.1"; + private static final int TOR_SOCKS_PROXY_PORT = 9150; + private static final int TOR_HTTP_PROXY_PORT = 8218; + public static URLConnection openConnectionWithProxy(final URI uri) throws IOException { java.net.ProxySelector ps = java.net.ProxySelector.getDefault(); Proxy proxy = Proxy.NO_PROXY; @@ -39,7 +43,26 @@ public class ProxySelector { } } - return uri.toURL().openConnection(proxy); + /* Ignore the proxy we found from the VM, only use Tor. We can probably + * safely use the logic in this class in the future. */ + return uri.toURL().openConnection(getProxy()); + } + + public static Proxy getProxy() { + // TODO make configurable + return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(TOR_PROXY_ADDRESS, TOR_SOCKS_PROXY_PORT)); + } + + public static String getProxyHostAddress() { + return TOR_PROXY_ADDRESS; + } + + public static int getSocksProxyPort() { + return TOR_SOCKS_PROXY_PORT; + } + + public static int getHttpProxyPort() { + return TOR_HTTP_PROXY_PORT; } public ProxySelector() { diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/BaseResource.java b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/BaseResource.java index e85ccb855bff7..51ebd6ce990f5 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/BaseResource.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/net/BaseResource.java @@ -23,9 +23,11 @@ import org.json.simple.JSONObject; import org.mozilla.gecko.background.common.GlobalConstants; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.sync.ExtendedJSONObject; +import org.mozilla.gecko.util.ProxySelector; import ch.boye.httpclientandroidlib.Header; import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpHost; import ch.boye.httpclientandroidlib.HttpResponse; import ch.boye.httpclientandroidlib.HttpVersion; import ch.boye.httpclientandroidlib.client.AuthCache; @@ -40,6 +42,7 @@ import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase; import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; import ch.boye.httpclientandroidlib.client.protocol.ClientContext; import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames; import ch.boye.httpclientandroidlib.conn.scheme.PlainSocketFactory; import ch.boye.httpclientandroidlib.conn.scheme.Scheme; import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; @@ -215,6 +218,10 @@ public class BaseResource implements Resource { // We could reuse these client instances, except that we mess around // with their parameters… so we'd need a pool of some kind. client = new DefaultHttpClient(getConnectionManager()); + /* TBA: We need a HTTP Proxy here */ + HttpHost defaultProxy = new HttpHost(ProxySelector.getProxyHostAddress(), + ProxySelector.getHttpProxyPort()); + client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, defaultProxy); // TODO: Eventually we should use Apache HttpAsyncClient. It's not out of alpha yet. // Until then, we synchronously make the request, then invoke our delegate's callback. diff --git a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java index 4e30cb7deeb78..9b3ee98f89db7 100644 --- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java +++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java @@ -7,6 +7,7 @@ package org.mozilla.mozstumbler.service.utils; import android.os.Build; import android.util.Log; +import org.mozilla.gecko.util.ProxySelector; import org.mozilla.mozstumbler.service.AppGlobals; import org.mozilla.mozstumbler.service.Prefs; @@ -16,7 +17,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; public abstract class AbstractCommunicator { @@ -72,8 +73,12 @@ public abstract class AbstractCommunicator { if (sMozApiKey == null || prefs != null) { sMozApiKey = prefs.getMozApiKey(); } - URL url = new URL(getUrlString() + "?key=" + sMozApiKey); - mHttpURLConnection = (HttpURLConnection) url.openConnection(); + + /* TBA: This was a URL, but the connection logic would simply + * convert the URL into a URI, and then later convert the URI back + * into a URL. Creating a URL at the beginning is only wasteful. */ + URI uri = new URI(getUrlString() + "?key=" + sMozApiKey); + mHttpURLConnection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(uri); mHttpURLConnection.setRequestMethod("POST"); } catch (MalformedURLException e) { throw new IllegalArgumentException(e); -- GitLab