diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-12-22 22:16:49 +0100 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2022-03-18 22:07:58 +0100 |
commit | ff9adf1e6213dada052cec632bde907f0102f938 (patch) | |
tree | c7c52123d314277dae296fc7e3acf51b419426cc | |
parent | c4bd77de299bba9e949b4e8f95ddecfb659165ee (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.java | 23 | ||||
-rw-r--r-- | tunnel/src/main/proto/libwg.proto | 1 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/go.mod | 1 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/go.sum | 2 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/http-proxy.go | 460 | ||||
-rw-r--r-- | tunnel/tools/libwg-go/service.go | 13 |
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 { |