summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2021-12-22 22:16:49 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2022-03-18 22:07:58 +0100
commitff9adf1e6213dada052cec632bde907f0102f938 (patch)
treec7c52123d314277dae296fc7e3acf51b419426cc
parentc4bd77de299bba9e949b4e8f95ddecfb659165ee (diff)
WIP use go-duktape to select proxy from uid
Call FindProxyForPkg in Proxy auto-config script to select proxy from uid name.
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java23
-rw-r--r--tunnel/src/main/proto/libwg.proto1
-rw-r--r--tunnel/tools/libwg-go/go.mod1
-rw-r--r--tunnel/tools/libwg-go/go.sum2
-rw-r--r--tunnel/tools/libwg-go/http-proxy.go460
-rw-r--r--tunnel/tools/libwg-go/service.go13
6 files changed, 475 insertions, 25 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
index 11c9086e..a5f7e98b 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
@@ -7,9 +7,11 @@ package com.wireguard.android.backend;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.LocalSocketAddress;
import android.net.ProxyInfo;
+import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.system.OsConstants;
@@ -41,6 +43,7 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.net.URL;
import java.util.Collections;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -282,7 +285,7 @@ public final class GoBackend implements Backend {
}
- private int startHttpProxy() {
+ private int startHttpProxy(Optional<ProxyInfo> httpProxy) {
LibwgGrpc.LibwgStub asyncStub = LibwgGrpc.newStub(channel);
LibwgGrpc.LibwgBlockingStub stub = LibwgGrpc.newBlockingStub(channel);
@@ -301,7 +304,14 @@ public final class GoBackend implements Backend {
});
streamer.start();
- StartHttpProxyRequest req = StartHttpProxyRequest.newBuilder().build();
+ StartHttpProxyRequest.Builder reqBuilder = StartHttpProxyRequest.newBuilder();
+ if (httpProxy.isPresent()) {
+ ProxyInfo pi = httpProxy.get();
+ Uri pacFileUrl = pi.getPacFileUrl();
+ if (pacFileUrl != null && pacFileUrl != Uri.EMPTY)
+ reqBuilder.setPacFileUrl(pacFileUrl.toString());
+ }
+ StartHttpProxyRequest req = reqBuilder.build();
StartHttpProxyResponse resp = stub.startHttpProxy(req);
Log.i(TAG, "Start http proxy listen_port:" + resp.getListenPort() + ", error:" + resp.getError().getMessage());
return resp.getListenPort();
@@ -333,8 +343,13 @@ public final class GoBackend implements Backend {
@Override
public void onNext(ReverseResponse resp) {
int uid = connectivityManager.getConnectionOwnerUid(resp.getUid().getProtocol(), toInetSocketAddress(resp.getUid().getLocal()), toInetSocketAddress(resp.getUid().getRemote()));
- String pkg = context.getPackageManager().getNameForUid(uid);
+ PackageManager pm = context.getPackageManager();
+ String pkg = pm.getNameForUid(uid);
+ String[] pkgs = pm.getPackagesForUid(uid);
Log.i(TAG, "reverse onNext uid:" + uid + " package:" + pkg);
+ for (int i=0; i < pkgs.length; i++) {
+ Log.i(TAG, "getPackagesForUid() = " + pkgs[i]);
+ }
ReverseRequest req = ReverseRequest.newBuilder()
.setUid(GetConnectionOwnerUidResponse.newBuilder()
@@ -484,7 +499,7 @@ public final class GoBackend implements Backend {
builder.setBlocking(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- int listenPort = startHttpProxy();
+ int listenPort = startHttpProxy(config.getInterface().getHttpProxy());
ProxyInfo proxy = ProxyInfo.buildDirectProxy("localhost", listenPort);
builder.setHttpProxy(proxy);
}
diff --git a/tunnel/src/main/proto/libwg.proto b/tunnel/src/main/proto/libwg.proto
index c136ae4d..49bb57bd 100644
--- a/tunnel/src/main/proto/libwg.proto
+++ b/tunnel/src/main/proto/libwg.proto
@@ -70,6 +70,7 @@ message VersionResponse {
}
message StartHttpProxyRequest {
+ string pacFileUrl = 1;
}
message StartHttpProxyResponse {
diff --git a/tunnel/tools/libwg-go/go.mod b/tunnel/tools/libwg-go/go.mod
index 3d24d309..00105ab0 100644
--- a/tunnel/tools/libwg-go/go.mod
+++ b/tunnel/tools/libwg-go/go.mod
@@ -18,4 +18,5 @@ require (
golang.org/x/text v0.3.6 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
+ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20210326210528-650f7c854440 // indirect
)
diff --git a/tunnel/tools/libwg-go/go.sum b/tunnel/tools/libwg-go/go.sum
index c68fb496..9039129d 100644
--- a/tunnel/tools/libwg-go/go.sum
+++ b/tunnel/tools/libwg-go/go.sum
@@ -158,6 +158,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/olebedev/go-duktape.v3 v3.0.0-20210326210528-650f7c854440 h1:SxFAMd+8zfpL/Rk4pgdb8leeZDiL3M/gCWCbBvmLkoE=
+gopkg.in/olebedev/go-duktape.v3 v3.0.0-20210326210528-650f7c854440/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/tunnel/tools/libwg-go/http-proxy.go b/tunnel/tools/libwg-go/http-proxy.go
index 4af97314..e9da32ab 100644
--- a/tunnel/tools/libwg-go/http-proxy.go
+++ b/tunnel/tools/libwg-go/http-proxy.go
@@ -1,14 +1,259 @@
package main
import (
+ "fmt"
+ "io"
"net"
"net/http"
"net/url"
+ "strings"
"github.com/elazarl/goproxy"
"golang.zx2c4.com/go118/netip"
"golang.zx2c4.com/wireguard/device"
+
+ "gopkg.in/olebedev/go-duktape.v3"
+)
+
+const (
+ // Imported from Firefox' ProxyAutoConfig.cpp
+ // https://searchfox.org/mozilla-central/source/netwerk/base/ProxyAutoConfig.cpp
+ // This Source Code Form is subject to the terms of the Mozilla Public
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ ASCII_PAC_UTILS = "function dnsDomainIs(host, domain) {\n" +
+ " return (host.length >= domain.length &&\n" +
+ " host.substring(host.length - domain.length) == domain);\n" +
+ "}\n" +
+ "" +
+ "function dnsDomainLevels(host) {\n" +
+ " return host.split('.').length - 1;\n" +
+ "}\n" +
+ "" +
+ "function isValidIpAddress(ipchars) {\n" +
+ " var matches = " +
+ "/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipchars);\n" +
+ " if (matches == null) {\n" +
+ " return false;\n" +
+ " } else if (matches[1] > 255 || matches[2] > 255 || \n" +
+ " matches[3] > 255 || matches[4] > 255) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " return true;\n" +
+ "}\n" +
+ "" +
+ "function convert_addr(ipchars) {\n" +
+ " var bytes = ipchars.split('.');\n" +
+ " var result = ((bytes[0] & 0xff) << 24) |\n" +
+ " ((bytes[1] & 0xff) << 16) |\n" +
+ " ((bytes[2] & 0xff) << 8) |\n" +
+ " (bytes[3] & 0xff);\n" +
+ " return result;\n" +
+ "}\n" +
+ "" +
+ "function isInNet(ipaddr, pattern, maskstr) {\n" +
+ " if (!isValidIpAddress(pattern) || !isValidIpAddress(maskstr)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " if (!isValidIpAddress(ipaddr)) {\n" +
+ " ipaddr = dnsResolve(ipaddr);\n" +
+ " if (ipaddr == null) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " }\n" +
+ " var host = convert_addr(ipaddr);\n" +
+ " var pat = convert_addr(pattern);\n" +
+ " var mask = convert_addr(maskstr);\n" +
+ " return ((host & mask) == (pat & mask));\n" +
+ " \n" +
+ "}\n" +
+ "" +
+ "function isPlainHostName(host) {\n" +
+ " return (host.search('(\\\\.)|:') == -1);\n" +
+ "}\n" +
+ "" +
+ "function isResolvable(host) {\n" +
+ " var ip = dnsResolve(host);\n" +
+ " return (ip != null);\n" +
+ "}\n" +
+ "" +
+ "function localHostOrDomainIs(host, hostdom) {\n" +
+ " return (host == hostdom) ||\n" +
+ " (hostdom.lastIndexOf(host + '.', 0) == 0);\n" +
+ "}\n" +
+ "" +
+ "function shExpMatch(url, pattern) {\n" +
+ " pattern = pattern.replace(/\\./g, '\\\\.');\n" +
+ " pattern = pattern.replace(/\\*/g, '.*');\n" +
+ " pattern = pattern.replace(/\\?/g, '.');\n" +
+ " var newRe = new RegExp('^'+pattern+'$');\n" +
+ " return newRe.test(url);\n" +
+ "}\n" +
+ "" +
+ "var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" +
+ "var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, " +
+ "AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n" +
+ "" +
+ "function weekdayRange() {\n" +
+ " function getDay(weekday) {\n" +
+ " if (weekday in wdays) {\n" +
+ " return wdays[weekday];\n" +
+ " }\n" +
+ " return -1;\n" +
+ " }\n" +
+ " var date = new Date();\n" +
+ " var argc = arguments.length;\n" +
+ " var wday;\n" +
+ " if (argc < 1)\n" +
+ " return false;\n" +
+ " if (arguments[argc - 1] == 'GMT') {\n" +
+ " argc--;\n" +
+ " wday = date.getUTCDay();\n" +
+ " } else {\n" +
+ " wday = date.getDay();\n" +
+ " }\n" +
+ " var wd1 = getDay(arguments[0]);\n" +
+ " var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" +
+ " return (wd1 == -1 || wd2 == -1) ? false\n" +
+ " : (wd1 <= wd2) ? (wd1 <= wday && wday " +
+ "<= wd2)\n" +
+ " : (wd2 >= wday || wday " +
+ ">= wd1);\n" +
+ "}\n" +
+ "" +
+ "function dateRange() {\n" +
+ " function getMonth(name) {\n" +
+ " if (name in months) {\n" +
+ " return months[name];\n" +
+ " }\n" +
+ " return -1;\n" +
+ " }\n" +
+ " var date = new Date();\n" +
+ " var argc = arguments.length;\n" +
+ " if (argc < 1) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " var isGMT = (arguments[argc - 1] == 'GMT');\n" +
+ "\n" +
+ " if (isGMT) {\n" +
+ " argc--;\n" +
+ " }\n" +
+ " // function will work even without explict handling of this case\n" +
+ " if (argc == 1) {\n" +
+ " var tmp = parseInt(arguments[0]);\n" +
+ " if (isNaN(tmp)) {\n" +
+ " return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n" +
+ " getMonth(arguments[0]));\n" +
+ " } else if (tmp < 32) {\n" +
+ " return ((isGMT ? date.getUTCDate() : date.getDate()) == " +
+ "tmp);\n" +
+ " } else { \n" +
+ " return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) " +
+ "==\n" +
+ " tmp);\n" +
+ " }\n" +
+ " }\n" +
+ " var year = date.getFullYear();\n" +
+ " var date1, date2;\n" +
+ " date1 = new Date(year, 0, 1, 0, 0, 0);\n" +
+ " date2 = new Date(year, 11, 31, 23, 59, 59);\n" +
+ " var adjustMonth = false;\n" +
+ " for (var i = 0; i < (argc >> 1); i++) {\n" +
+ " var tmp = parseInt(arguments[i]);\n" +
+ " if (isNaN(tmp)) {\n" +
+ " var mon = getMonth(arguments[i]);\n" +
+ " date1.setMonth(mon);\n" +
+ " } else if (tmp < 32) {\n" +
+ " adjustMonth = (argc <= 2);\n" +
+ " date1.setDate(tmp);\n" +
+ " } else {\n" +
+ " date1.setFullYear(tmp);\n" +
+ " }\n" +
+ " }\n" +
+ " for (var i = (argc >> 1); i < argc; i++) {\n" +
+ " var tmp = parseInt(arguments[i]);\n" +
+ " if (isNaN(tmp)) {\n" +
+ " var mon = getMonth(arguments[i]);\n" +
+ " date2.setMonth(mon);\n" +
+ " } else if (tmp < 32) {\n" +
+ " date2.setDate(tmp);\n" +
+ " } else {\n" +
+ " date2.setFullYear(tmp);\n" +
+ " }\n" +
+ " }\n" +
+ " if (adjustMonth) {\n" +
+ " date1.setMonth(date.getMonth());\n" +
+ " date2.setMonth(date.getMonth());\n" +
+ " }\n" +
+ " if (isGMT) {\n" +
+ " var tmp = date;\n" +
+ " tmp.setFullYear(date.getUTCFullYear());\n" +
+ " tmp.setMonth(date.getUTCMonth());\n" +
+ " tmp.setDate(date.getUTCDate());\n" +
+ " tmp.setHours(date.getUTCHours());\n" +
+ " tmp.setMinutes(date.getUTCMinutes());\n" +
+ " tmp.setSeconds(date.getUTCSeconds());\n" +
+ " date = tmp;\n" +
+ " }\n" +
+ " return (date1 <= date2) ? (date1 <= date) && (date <= date2)\n" +
+ " : (date2 >= date) || (date >= date1);\n" +
+ "}\n" +
+ "" +
+ "function timeRange() {\n" +
+ " var argc = arguments.length;\n" +
+ " var date = new Date();\n" +
+ " var isGMT= false;\n" +
+ "" +
+ " if (argc < 1) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " if (arguments[argc - 1] == 'GMT') {\n" +
+ " isGMT = true;\n" +
+ " argc--;\n" +
+ " }\n" +
+ "\n" +
+ " var hour = isGMT ? date.getUTCHours() : date.getHours();\n" +
+ " var date1, date2;\n" +
+ " date1 = new Date();\n" +
+ " date2 = new Date();\n" +
+ "\n" +
+ " if (argc == 1) {\n" +
+ " return (hour == arguments[0]);\n" +
+ " } else if (argc == 2) {\n" +
+ " return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" +
+ " } else {\n" +
+ " switch (argc) {\n" +
+ " case 6:\n" +
+ " date1.setSeconds(arguments[2]);\n" +
+ " date2.setSeconds(arguments[5]);\n" +
+ " case 4:\n" +
+ " var middle = argc >> 1;\n" +
+ " date1.setHours(arguments[0]);\n" +
+ " date1.setMinutes(arguments[1]);\n" +
+ " date2.setHours(arguments[middle]);\n" +
+ " date2.setMinutes(arguments[middle + 1]);\n" +
+ " if (middle == 2) {\n" +
+ " date2.setSeconds(59);\n" +
+ " }\n" +
+ " break;\n" +
+ " default:\n" +
+ " throw 'timeRange: bad number of arguments'\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " if (isGMT) {\n" +
+ " date.setFullYear(date.getUTCFullYear());\n" +
+ " date.setMonth(date.getUTCMonth());\n" +
+ " date.setDate(date.getUTCDate());\n" +
+ " date.setHours(date.getUTCHours());\n" +
+ " date.setMinutes(date.getUTCMinutes());\n" +
+ " date.setSeconds(date.getUTCSeconds());\n" +
+ " }\n" +
+ " return (date1 <= date2) ? (date1 <= date) && (date <= date2)\n" +
+ " : (date2 >= date) || (date >= date1);\n" +
+ "\n" +
+ "}\n"
)
type HttpProxy struct {
@@ -17,18 +262,25 @@ type HttpProxy struct {
addrPort netip.AddrPort
uidRequest chan netip.AddrPort
uidResponse chan string
+ pacFileUrl *url.URL
+ ctx *duktape.Context
}
-func NewHttpProxy(uidRequest chan netip.AddrPort, uidResponse chan string, logger *device.Logger) *HttpProxy {
+func NewHttpProxy(uidRequest chan netip.AddrPort, uidResponse chan string, logger *device.Logger, pacFileUrl *url.URL) *HttpProxy {
logger.Verbosef("NewHttpProxy")
return &HttpProxy{
listener: nil,
logger: logger,
uidRequest: uidRequest,
uidResponse: uidResponse,
+ pacFileUrl: pacFileUrl,
}
}
+var (
+ ASCII_PAC_UTILS_NAMES = []string{"dnsDomainIs", "dnsDomainLevels", "isValidIpAddress", "convert_addr", "isInNet", "isPlainHostName", "isResolvable", "localHostOrDomainIs", "shExpMatch", "weekdayRange", "dateRange", "timeRange"}
+)
+
func (p *HttpProxy) GetAddrPort() netip.AddrPort {
return p.addrPort
}
@@ -44,10 +296,125 @@ func newGoProxy(proxyUrl string) *goproxy.ProxyHttpServer {
return proxy
}
+func FindProxyForURL(ctx *duktape.Context, url, host string, logger *device.Logger) (res string, err error) {
+ if !ctx.GetGlobalString("FindProxyForURL") {
+ ctx.Pop()
+ return "", fmt.Errorf("FindProxyForURL not found")
+ }
+
+ ctx.PushString(url)
+ ctx.PushString(host)
+
+ res = "DIRECT"
+ logger.Verbosef("Before pcall")
+ r := ctx.Pcall(2)
+ logger.Verbosef("After pcall")
+ if r == 0 {
+ res = ctx.GetString(-1)
+ } else if ctx.IsError(-1) {
+ ctx.GetPropString(-1, "stack")
+ err = fmt.Errorf("Error: %v", ctx.SafeToString(-1))
+ } else {
+ err = fmt.Errorf("Error: %v", ctx.SafeToString(-1))
+ }
+ ctx.Pop()
+ return
+}
+
+
+func FindProxyForPkg(ctx *duktape.Context, pkg string, logger *device.Logger) (res string, err error) {
+ if !ctx.GetGlobalString("FindProxyForPkg") {
+ ctx.Pop()
+ return "", fmt.Errorf("FindProxyForPkg not found")
+ }
+
+ ctx.PushString(pkg)
+
+ res = "DIRECT"
+ logger.Verbosef("Before pcall")
+ r := ctx.Pcall(1)
+ logger.Verbosef("After pcall")
+ if r == 0 {
+ res = ctx.GetString(-1)
+ } else if ctx.IsError(-1) {
+ ctx.GetPropString(-1, "stack")
+ err = fmt.Errorf("Error: %v", ctx.SafeToString(-1))
+ } else {
+ err = fmt.Errorf("Error: %v", ctx.SafeToString(-1))
+ }
+ ctx.Pop()
+ return
+}
+
+func newPacFileCtx(body string, logger *device.Logger) *duktape.Context {
+ ctx := duktape.New()
+
+ ctx.PushGlobalGoFunction("dnsResolve",
+ func(ctx *duktape.Context) int {
+ // Check stack
+ host := ctx.GetString(-1)
+ ctx.Pop()
+ ips, err := net.LookupIP(host)
+ if err != nil {
+ return 0
+ }
+
+ for _, ip := range(ips) {
+ ipStr := string(ip)
+ if strings.Contains(ipStr, ".") {
+ // Found IPv4
+ ctx.PushString(ipStr)
+ return 1
+ }
+ }
+ // No IPv4
+ return 0
+ })
+
+ ctx.PevalString(ASCII_PAC_UTILS)
+ // ctx.PushString("ascii_pac_utils.js")
+ // ctx.CompileStringFilename(0, ASCII_PAC_UTILS)
+ logger.Verbosef("ASCII_PAC_UTILS result is: %v stack:%v", ctx.GetType(-1), ctx.GetTop())
+ ctx.Pop()
+
+ // for _, f := range ASCII_PAC_UTILS_NAMES {
+ // ctx.GetGlobalString("dnsDomainIs")
+ // ctx.PushString("proxy.pac")
+ // if err = ctx.Pcompile(0); err != nil {
+ // logger.Verbosef("Compile failed: %v, %v", err, ctx.SafeToString(-1))
+ // } else {
+ // ctx.PutGlobalString("dnsDomainIs")
+ // }
+
+ ctx.PevalString(string(body))
+ logger.Verbosef("result is: %v stack:%v", ctx.GetType(-1), ctx.GetTop())
+ ctx.Pop()
+ FindProxyForURL(ctx, "http://www.jabra.se/", "www.jabra.se", logger)
+ return ctx
+}
+
+
func (p *HttpProxy) Start() (listen_port uint16, err error) {
p.logger.Verbosef("HttpProxy.Start()")
listen_port = 0
+ var pacFileBody = ""
+ if p.pacFileUrl != nil {
+ resp, err := http.Get(p.pacFileUrl.String())
+ p.logger.Verbosef("pacFile: %v, %v", resp, err)
+
+ if err == nil {
+ defer resp.Body.Close()
+ ct, ok := resp.Header["Content-Type"]
+ if ok && len(ct) == 1 && ct[0] == "application/x-ns-proxy-autoconfig" {
+ body, err := io.ReadAll(resp.Body)
+ if err == nil {
+ pacFileBody = string(body)
+ }
+ }
+ }
+ }
+
proxyMap := make(map[string]*goproxy.ProxyHttpServer)
proxyMap["bbc.iplayer.android"] = newGoProxy("http://10.49.124.111:8888")
proxyMap["no.nrk.tv"] = newGoProxy("http://10.49.124.115:8888")
@@ -67,9 +434,9 @@ func (p *HttpProxy) Start() (listen_port uint16, err error) {
listen_port = p.addrPort.Port()
- handler := NewHttpHandler(proxyMap, defaultProxy, p.logger)
+ handler := NewHttpHandler(defaultProxy, p.logger)
- go http.Serve(NewUidListener(listener, handler, p.uidRequest, p.uidResponse, p.logger), handler)
+ go http.Serve(NewUidListener(listener, handler, p.uidRequest, p.uidResponse, pacFileBody, proxyMap, p.logger), handler)
return
}
@@ -80,36 +447,38 @@ func (p *HttpProxy) Stop() {
p.listener.Close()
p.listener = nil
}
+ if p.ctx != nil {
+ p.ctx.DestroyHeap()
+ p.ctx = nil
+ }
}
type HttpHandler struct {
- proxyMap map[string]*goproxy.ProxyHttpServer
defaultProxy *goproxy.ProxyHttpServer
logger *device.Logger
- remoteAddrPkgMap map[string]string
+ remoteAddrPkgMap map[string]*goproxy.ProxyHttpServer
}
-func NewHttpHandler(proxyMap map[string]*goproxy.ProxyHttpServer, defaultProxy *goproxy.ProxyHttpServer, logger *device.Logger) *HttpHandler{
+func NewHttpHandler(defaultProxy *goproxy.ProxyHttpServer, logger *device.Logger) *HttpHandler{
return &HttpHandler{
- proxyMap: proxyMap,
defaultProxy: defaultProxy,
logger: logger,
- remoteAddrPkgMap: make(map[string]string),
+ remoteAddrPkgMap: make(map[string]*goproxy.ProxyHttpServer),
}
}
func (h *HttpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
- pkg, ok := h.remoteAddrPkgMap[req.RemoteAddr]
- if ok {
- h.logger.Verbosef("ServeHTTP remote:%s package:%s", req.RemoteAddr, pkg)
+ proxy, ok := h.remoteAddrPkgMap[req.RemoteAddr]
+ if ok && proxy != nil {
+ proxyStr := "Error"
+ proxyUrl, err := proxy.Tr.Proxy(nil)
+ if err == nil {
+ proxyStr = proxyUrl.String()
+ }
+ h.logger.Verbosef("ServeHTTP remote:%s proxy:%v", req.RemoteAddr, proxyStr)
delete(h.remoteAddrPkgMap, req.RemoteAddr)
- proxy, ok := h.proxyMap[pkg]
- if ok {
- proxy.ServeHTTP(rw, req)
- } else {
- h.defaultProxy.ServeHTTP(rw, req)
- }
+ proxy.ServeHTTP(rw, req)
} else {
h.defaultProxy.ServeHTTP(rw, req)
}
@@ -122,16 +491,59 @@ type UidListener struct {
logger *device.Logger
uidRequest chan netip.AddrPort
uidResponse chan string
+ ctx *duktape.Context
+ proxyMap map[string]*goproxy.ProxyHttpServer
}
-func NewUidListener(listener net.Listener, handler *HttpHandler, uidRequest chan netip.AddrPort, uidResponse chan string, logger *device.Logger) *UidListener{
- return &UidListener{
+func NewUidListener(listener net.Listener, handler *HttpHandler, uidRequest chan netip.AddrPort, uidResponse chan string, pacFileBody string, proxyMap map[string]*goproxy.ProxyHttpServer, logger *device.Logger) *UidListener{
+ l := &UidListener{
l: listener,
handler: handler,
logger: logger,
uidRequest: uidRequest,
uidResponse: uidResponse,
+ proxyMap: proxyMap,
+ }
+
+ if pacFileBody != "" {
+ l.ctx = newPacFileCtx(pacFileBody, logger)
+ }
+ return l
+}
+
+func (l *UidListener) findProxyForPkg(pkg string) *goproxy.ProxyHttpServer {
+ if l.ctx == nil {
+ return nil
+ }
+
+ find := func() (res string, err error) {
+ l.logger.Verbosef("Call FindProxyForPkg %v %v", pkg)
+ res, err = FindProxyForPkg(l.ctx, pkg, l.logger)
+ l.logger.Verbosef("FindProxyForPkg res %v %v", res, err)
+ if err != nil {
+ l.logger.Verbosef("FindProxyForPkg result is: %v stack:%v", res, l.ctx.GetTop())
+ return "", err
+ } else {
+ values := strings.Split(strings.Trim(res, " "), ";")
+ for _, v := range values {
+ value := strings.Trim(v, " ")
+ parts := strings.SplitN(value, " ", 2)
+ if parts[0] == "PROXY" {
+ return parts[1], nil
+ }
+ }
+ }
+ return "", fmt.Errorf("No result")
}
+ res, err := find()
+ if err != nil {
+ return nil
+ }
+
+ proxy := newGoProxy("http://" + res)
+ l.proxyMap[res] = proxy
+
+ return proxy
}
func (l *UidListener) Accept() (net.Conn, error) {
@@ -150,7 +562,15 @@ func (l *UidListener) Accept() (net.Conn, error) {
select {
case pkg := <-l.uidResponse:
l.logger.Verbosef("uidResponse: %v", pkg)
- l.handler.remoteAddrPkgMap[c.RemoteAddr().String()] = pkg
+
+ proxy, ok := l.proxyMap[pkg]
+ if !ok {
+ proxy = l.findProxyForPkg(pkg)
+ }
+
+ if proxy != nil {
+ l.handler.remoteAddrPkgMap[c.RemoteAddr().String()] = proxy
+ }
}
}
return c, nil
diff --git a/tunnel/tools/libwg-go/service.go b/tunnel/tools/libwg-go/service.go
index c201e45d..06b3c86a 100644
--- a/tunnel/tools/libwg-go/service.go
+++ b/tunnel/tools/libwg-go/service.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net"
+ "net/url"
"os"
"google.golang.org/grpc"
@@ -105,7 +106,17 @@ func (e *LibwgServiceImpl) StartHttpProxy(ctx context.Context, req *gen.StartHtt
return r, nil
}
- e.http_proxy = NewHttpProxy(e.uidRequest, e.uidResponse, e.logger)
+ pacFileUrl, err := url.Parse(req.PacFileUrl)
+ if err != nil {
+ r := &gen.StartHttpProxyResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("Bad pacFileUrl: %s", req.PacFileUrl),
+ },
+ }
+ return r, nil
+ }
+
+ e.http_proxy = NewHttpProxy(e.uidRequest, e.uidResponse, e.logger, pacFileUrl)
listen_port, err := e.http_proxy.Start()
if err != nil {