glue: Proper support for -http-proxy option.

This adds the required glue code to make -http-proxy option
work, by injecting the AndroidEmu HTTP proxy implementation
into the SLIRP stack at emulation setup time.

+ Remove -http-proxy from the list of QEMU2 options, it is
  not needed because the corresponding initialization can
  be performed directly in android-qemu2-glue/main.cpp
  instead.

Change-Id: Ia972b7882ebf00a379ed9536e93e44551e3b0160
diff --git a/android-qemu2-glue/build/Makefile.qemu2-glue.mk b/android-qemu2-glue/build/Makefile.qemu2-glue.mk
index 649c1a0..613d2bf 100644
--- a/android-qemu2-glue/build/Makefile.qemu2-glue.mk
+++ b/android-qemu2-glue/build/Makefile.qemu2-glue.mk
@@ -24,6 +24,7 @@
     emulation/VmLock.cpp \
     looper-qemu.cpp \
     net-android.cpp \
+    proxy/slirp_proxy.cpp \
     qemu-battery-agent-impl.c \
     qemu-cellular-agent-impl.c \
     qemu-clipboard-agent-impl.cpp \
diff --git a/android-qemu2-glue/main.cpp b/android-qemu2-glue/main.cpp
index 0a18f06..add247d 100644
--- a/android-qemu2-glue/main.cpp
+++ b/android-qemu2-glue/main.cpp
@@ -16,6 +16,7 @@
 #include "android/base/StringFormat.h"
 #include "android/base/system/System.h"
 
+
 #include "android/android.h"
 #include "android/avd/hw-config.h"
 #include "android/cmdline-option.h"
@@ -56,6 +57,7 @@
 
 #include "android/ui-emu-agent.h"
 #include "android-qemu2-glue/emulation/serial_line.h"
+#include "android-qemu2-glue/proxy/slirp_proxy.h"
 #include "android-qemu2-glue/qemu-control-impl.h"
 
 #ifdef TARGET_AARCH64
@@ -551,8 +553,9 @@
     }
 
     if (opts->http_proxy) {
-        args[n++] = "-http-proxy";
-        args[n++] = opts->http_proxy;
+        if (!qemu_android_setup_http_proxy(opts->http_proxy)) {
+            return 1;
+        }
     }
 
     if (!opts->charmap) {
diff --git a/android-qemu2-glue/proxy/slirp_proxy.cpp b/android-qemu2-glue/proxy/slirp_proxy.cpp
new file mode 100644
index 0000000..da53263
--- /dev/null
+++ b/android-qemu2-glue/proxy/slirp_proxy.cpp
@@ -0,0 +1,121 @@
+/* Copyright 2016 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "android-qemu2-glue/proxy/slirp_proxy.h"
+
+#include "android/base/Log.h"
+#include "android/base/memory/LazyInstance.h"
+#include "android/proxy/proxy_common.h"
+#include "android/utils/sockets.h"
+
+#include "slirp/proxy.h"
+
+#ifdef _WIN32
+#include "android/base/sockets/Winsock.h"
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#include <unordered_map>
+#include <utility>
+
+extern "C" const char* op_http_proxy;
+
+namespace {
+
+// NOTE: The SLIRP stack only runs in the main thread, so don't
+//       use a mutex to protect global variables from multiple threads.
+
+struct Globals {
+    // Implement a global map |connect_opaque| -> { |af|, |connect_func| }
+    // to be used in the android_tcp_proxy_event() function to map opaque
+    // handles to the appropriate callback and address family.
+    struct Info {
+        Info() = default;
+        Info(int af_, SlirpProxyConnectFunc* connect_func_) : af(af_), connect_func(connect_func_) {}
+        int af = 0;
+        SlirpProxyConnectFunc* connect_func = nullptr;
+    };
+    using MapType = std::unordered_map<void*, Info>;
+    MapType mMap;
+};
+
+android::base::LazyInstance<Globals> sGlobals = LAZY_INSTANCE_INIT;
+
+}  // namespace
+
+using MapType = Globals::MapType;
+
+static void android_tcp_proxy_event(void* opaque, int fd, ProxyEvent event) {
+    MapType* map = &sGlobals->mMap;
+    auto it = map->find(opaque);
+    DCHECK(it != map->end());
+    if (event != PROXY_EVENT_CONNECTED) {
+        fd = -1;
+    }
+    it->second.connect_func(opaque, fd, it->second.af);
+}
+
+static bool android_proxy_try_connect(const struct sockaddr_storage *addr,
+                                      SlirpProxyConnectFunc *connect_func,
+                                      void *connect_opaque) {
+    SockAddress sockaddr = {};
+    int af = addr->ss_family;
+    switch (af) {
+        case AF_INET: {
+            auto sin = reinterpret_cast<const sockaddr_in *>(addr);
+            sock_address_init_inet(&sockaddr, ntohl(sin->sin_addr.s_addr),
+                                   ntohs(sin->sin_port));
+            break;
+        }
+        case AF_INET6: {
+            auto sin6 = reinterpret_cast<const sockaddr_in6 *>(addr);
+            sock_address_init_in6(&sockaddr, sin6->sin6_addr.s6_addr,
+                                  ntohs(sin6->sin6_port));
+            break;
+        }
+        default:
+            return false;
+    }
+
+    MapType* map = &sGlobals->mMap;
+    (*map)[connect_opaque] = Globals::Info({af, connect_func});
+
+    if (!proxy_manager_add(&sockaddr, SOCKET_STREAM, android_tcp_proxy_event,
+                           connect_opaque)) {
+        return true;
+    }
+
+    // No proxy possible here, to remove from map.
+    map->erase(connect_opaque);
+    return false;
+}
+
+static void android_proxy_remove(void* connect_opaque) {
+    sGlobals->mMap.erase(connect_opaque);
+    proxy_manager_del(connect_opaque);
+}
+
+bool qemu_android_setup_http_proxy(const char* http_proxy) {
+    // Inject TCP proxy implementation into SLIRP stack.
+    // Initialization of the proxy will happen later.
+    static const SlirpProxyOps android_proxy_ops = {
+        .try_connect = android_proxy_try_connect,
+        .remove = android_proxy_remove,
+    };
+
+    op_http_proxy = http_proxy;
+
+    slirp_proxy = &android_proxy_ops;
+
+    return true;
+}
diff --git a/android-qemu2-glue/proxy/slirp_proxy.h b/android-qemu2-glue/proxy/slirp_proxy.h
new file mode 100644
index 0000000..d8994e4
--- /dev/null
+++ b/android-qemu2-glue/proxy/slirp_proxy.h
@@ -0,0 +1,21 @@
+/* Copyright 2016 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#pragma once
+
+#include "android/utils/compiler.h"
+#include <stdbool.h>
+
+ANDROID_BEGIN_HEADER
+
+bool qemu_android_setup_http_proxy(const char* http_proxy);
+
+ANDROID_END_HEADER
diff --git a/android-qemu2-glue/qemu-setup.cpp b/android-qemu2-glue/qemu-setup.cpp
index bdb0af8..34ac0ca 100644
--- a/android-qemu2-glue/qemu-setup.cpp
+++ b/android-qemu2-glue/qemu-setup.cpp
@@ -24,6 +24,7 @@
 #include "android-qemu2-glue/looper-qemu.h"
 #include "android-qemu2-glue/android_qemud.h"
 #include "android-qemu2-glue/net-android.h"
+#include "android-qemu2-glue/proxy/slirp_proxy.h"
 #include "android-qemu2-glue/qemu-control-impl.h"
 
 extern "C" {
@@ -31,6 +32,10 @@
 #include "qemu-common.h"
 #include "qemu/main-loop.h"
 #include "qemu/thread.h"
+
+// TODO: Remove op_http_proxy global variable.
+extern char* op_http_proxy;
+
 }  // extern "C"
 
 using android::VmLock;
@@ -79,6 +84,10 @@
             gQAndroidNetAgent,
     };
 
+    if (!qemu_android_setup_http_proxy(op_http_proxy)) {
+        return false;
+    }
+
     return android_emulation_setup(&consoleAgents);
 }
 
diff --git a/qemu-options.def b/qemu-options.def
index 3822c5b..e8d6f4f 100644
--- a/qemu-options.def
+++ b/qemu-options.def
@@ -876,9 +876,6 @@
 "-android-report-console <socket>"
 " report console port to remote socket\n", QEMU_ARCH_ALL)
 
-DEF("http-proxy", HAS_ARG, QEMU_OPTION_http_proxy,
-"-http-proxy <proxy> make TCP connections through a HTTP/HTTPS proxy\n", QEMU_ARCH_ALL)
-
 DEF("dns-server", HAS_ARG, QEMU_OPTION_dns_server,
 "-dns-server <servers> use this DNS server(s) in the emulated system\n", QEMU_ARCH_ALL)
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 92705ac..d543529 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4025,14 +4025,6 @@
 Report console port to remote socket
 ETEXI
 
-DEF("http-proxy", HAS_ARG, QEMU_OPTION_http_proxy,
-    "-http-proxy <proxy> make TCP connections through a HTTP/HTTPS proxy\n", QEMU_ARCH_ALL)
-STEXI
-@item -http-proxy @var{proxy}
-@findex -http-proxy
-make TCP connections through a HTTP/HTTPS proxy
-ETEXI
-
 DEF("dns-server", HAS_ARG, QEMU_OPTION_dns_server,
     "-dns-server <servers> use this DNS server(s) in the emulated system\n", QEMU_ARCH_ALL)
 STEXI
diff --git a/vl.c b/vl.c
index 1dd8897..c36e607 100755
--- a/vl.c
+++ b/vl.c
@@ -241,7 +241,6 @@
 int graphic_rotate = 0;
 #ifdef CONFIG_ANDROID
 int lcd_density = LCD_DENSITY_MDPI;
-extern char* op_http_proxy;
 extern char* android_op_ports;
 extern int android_op_ports_numbers[2];
 extern char* android_op_report_console;
@@ -4327,9 +4326,6 @@
                 android_op_dns_server = (char*)optarg;
                 break;
 
-            case QEMU_OPTION_http_proxy:
-                op_http_proxy = (char*)optarg;
-                break;
             case QEMU_OPTION_android_hw:
                 android_hw_file = optarg;
                 break;