summaryrefslogtreecommitdiffhomepage
path: root/tunnel/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tunnel/tools')
-rw-r--r--tunnel/tools/libwg-go/.gitignore3
-rw-r--r--tunnel/tools/libwg-go/Makefile35
-rw-r--r--tunnel/tools/libwg-go/api-android.go69
-rw-r--r--tunnel/tools/libwg-go/dhcp.go193
-rw-r--r--tunnel/tools/libwg-go/go.mod23
-rw-r--r--tunnel/tools/libwg-go/go.sum173
-rw-r--r--tunnel/tools/libwg-go/http-proxy.go736
-rw-r--r--tunnel/tools/libwg-go/jni.c32
-rw-r--r--tunnel/tools/libwg-go/service.go342
9 files changed, 1583 insertions, 23 deletions
diff --git a/tunnel/tools/libwg-go/.gitignore b/tunnel/tools/libwg-go/.gitignore
index d1638636..d69ee823 100644
--- a/tunnel/tools/libwg-go/.gitignore
+++ b/tunnel/tools/libwg-go/.gitignore
@@ -1 +1,2 @@
-build/ \ No newline at end of file
+build/
+/gen/
diff --git a/tunnel/tools/libwg-go/Makefile b/tunnel/tools/libwg-go/Makefile
index f1936e1d..ed718258 100644
--- a/tunnel/tools/libwg-go/Makefile
+++ b/tunnel/tools/libwg-go/Makefile
@@ -19,13 +19,21 @@ export CGO_LDFLAGS := $(CLANG_FLAGS) $(patsubst -Wl$(comma)--build-id=%,-Wl$(com
export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
export GOOS := android
export CGO_ENABLED := 1
+export GOPATH ?= $(HOME)/go
-GO_VERSION := 1.20.3
+GO_VERSION := 1.21.3
GO_PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m))
GO_TARBALL := go$(GO_VERSION).$(GO_PLATFORM).tar.gz
-GO_HASH_darwin-amd64 := c1e1161d6d859deb576e6cfabeb40e3d042ceb1c6f444f617c3c9d76269c3565
-GO_HASH_darwin-arm64 := 86b0ed0f2b2df50fa8036eea875d1cf2d76cefdacf247c44639a1464b7e36b95
-GO_HASH_linux-amd64 := 979694c2c25c735755bf26f4f45e19e64e4811d661dd07b8c010f7a8e18adfca
+GO_HASH_darwin-amd64 := 27014fc69e301d7588a169ca239b3cc609f0aa1abf38528bf0d20d3b259211eb
+GO_HASH_darwin-arm64 := 65302a7a9f7a4834932b3a7a14cb8be51beddda757b567a2f9e0cbd0d7b5a6ab
+GO_HASH_linux-amd64 := 1241381b2843fae5a9707eec1f8fb2ef94d827990582c7c7c32f5bdfbfd420c8
+
+PROTOC_VERSION := 3.24.4
+PROTOC_GEN_GO := $(GOPATH)/bin/protoc-gen-go
+PROTOC := $(GRADLE_USER_HOME)/caches/protoc-bin-$(PROTOC_VERSION)/protoc
+PROTODIR = $(CURDIR)/../../src/main/proto
+PROTO_INCLUDEDIR = $(CURDIR)/../../build/extracted-include-protos/debug
+PBDIR = $(GOPATH)/pkg/mod
default: $(DESTDIR)/libwg-go.so
@@ -45,8 +53,25 @@ $(BUILDDIR)/go-$(GO_VERSION)/.prepared: $(GRADLE_USER_HOME)/caches/golang/$(GO_T
patch -p1 -f -N -r- -d "$(dir $@)" < goruntime-boottime-over-monotonic.diff && \
touch "$@"'
+$(PROTOC_GEN_GO): export GOARCH :=
+$(PROTOC_GEN_GO): export GOOS :=
+$(PROTOC_GEN_GO): export PATH := $(BUILDDIR)/go-$(GO_VERSION)/bin/:$(PATH)
+$(PROTOC_GEN_GO): $(BUILDDIR)/go-$(GO_VERSION)/.prepared Makefile
+ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
+ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
+
+gen/%.pb.go: export PATH := $(GOPATH)/bin:$(PATH)
+gen/%.pb.go: $(PROTODIR)/%.proto $(BUILDDIR)/go-$(GO_VERSION)/.prepared $(PROTOC_GEN_GO)
+ test -d gen || mkdir gen
+ $(PROTOC) -I $(PROTODIR) -I $(PROTO_INCLUDEDIR) --go_out=paths=source_relative:./gen $<
+
+gen/%_grpc.pb.go: export PATH := $(GOPATH)/bin:$(PATH)
+gen/%_grpc.pb.go: $(PROTODIR)/%.proto $(BUILDDIR)/go-$(GO_VERSION)/.prepared $(PROTOC_GEN_GO)
+ test -d gen || mkdir gen
+ $(PROTOC) -I $(PROTODIR) -I $(PROTO_INCLUDEDIR) --go-grpc_out=./gen --go-grpc_opt=paths=source_relative $<
+
$(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(GO_VERSION)/bin/:$(PATH)
-$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(GO_VERSION)/.prepared go.mod
+$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(GO_VERSION)/.prepared go.mod api-android.go dhcp.go http-proxy.go service.go gen/libwg.pb.go gen/libwg_grpc.pb.go jni.c
go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard -buildid=" -v -trimpath -buildvcs=false -o "$@" -buildmode c-shared
.DELETE_ON_ERROR:
diff --git a/tunnel/tools/libwg-go/api-android.go b/tunnel/tools/libwg-go/api-android.go
index d47c5d76..5d9e8b96 100644
--- a/tunnel/tools/libwg-go/api-android.go
+++ b/tunnel/tools/libwg-go/api-android.go
@@ -48,10 +48,17 @@ func (l AndroidLogger) Printf(format string, args ...interface{}) {
type TunnelHandle struct {
device *device.Device
uapi net.Listener
+ logger *device.Logger
+ tun *tun.NativeTun
}
var tunnelHandles map[int32]TunnelHandle
+func GetTunnel(handle int32) (tunnelHandle TunnelHandle, ok bool) {
+ tunnelHandle, ok = tunnelHandles[handle]
+ return
+}
+
func init() {
tunnelHandles = make(map[int32]TunnelHandle)
signals := make(chan os.Signal)
@@ -142,10 +149,29 @@ func wgTurnOn(interfaceName string, tunFd int32, settings string) int32 {
device.Close()
return -1
}
- tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi}
+ tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi, tun: tun}
return i
}
+//export wgSetFd
+func wgSetFd(tunnelHandle int32, tunFd int32) {
+ tag := cstring(fmt.Sprintf("WireGuard/GoBackend/%x", tunnelHandle))
+ logger := &device.Logger{
+ Verbosef: AndroidLogger{level: C.ANDROID_LOG_DEBUG, tag: tag}.Printf,
+ Errorf: AndroidLogger{level: C.ANDROID_LOG_ERROR, tag: tag}.Printf,
+ }
+
+ handle, ok := tunnelHandles[tunnelHandle]
+ if !ok {
+ unix.Close(int(tunFd))
+ logger.Errorf("Tunnel not found")
+ return
+ }
+
+ handle.tun.SetFd(int(tunFd))
+ logger.Verbosef("wgSetFd: %v", tunFd)
+}
+
//export wgTurnOff
func wgTurnOff(tunnelHandle int32) {
handle, ok := tunnelHandles[tunnelHandle]
@@ -208,20 +234,53 @@ func wgGetConfig(tunnelHandle int32) *C.char {
//export wgVersion
func wgVersion() *C.char {
+ return C.CString(Version())
+}
+
+func Version() string {
info, ok := debug.ReadBuildInfo()
if !ok {
- return C.CString("unknown")
+ return "unknown"
}
for _, dep := range info.Deps {
if dep.Path == "golang.zx2c4.com/wireguard" {
parts := strings.Split(dep.Version, "-")
if len(parts) == 3 && len(parts[2]) == 12 {
- return C.CString(parts[2][:7])
+ return parts[2][:7]
}
- return C.CString(dep.Version)
+ return dep.Version
}
}
- return C.CString("unknown")
+ return "unknown"
+}
+
+//export wgStartGrpc
+func wgStartGrpc(sock_path string) C.int{
+ tag := cstring("WireGuard/GoBackend/gRPC")
+ logger := &device.Logger{
+ Verbosef: AndroidLogger{level: C.ANDROID_LOG_DEBUG, tag: tag}.Printf,
+ Errorf: AndroidLogger{level: C.ANDROID_LOG_ERROR, tag: tag}.Printf,
+ }
+
+ res, errmsg := StartGrpc(sock_path, logger)
+ if res < 0 {
+ logger.Verbosef("wgStartGrpc: %v (%v)", errmsg, res)
+ }
+
+ return C.int(res)
+}
+
+//export wgSetConfig
+func wgSetConfig(tunnelHandle int32, settings string) int32 {
+ handle, ok := tunnelHandles[tunnelHandle]
+ if !ok {
+ return -1
+ }
+ err := handle.device.IpcSet(settings)
+ if err != nil {
+ return -1
+ }
+ return 0
}
func main() {}
diff --git a/tunnel/tools/libwg-go/dhcp.go b/tunnel/tools/libwg-go/dhcp.go
new file mode 100644
index 00000000..4af5b0cb
--- /dev/null
+++ b/tunnel/tools/libwg-go/dhcp.go
@@ -0,0 +1,193 @@
+package main
+
+import (
+ "context"
+ "net"
+ "net/netip"
+ "time"
+
+ "github.com/insomniacslk/dhcp/dhcpv4"
+ "github.com/insomniacslk/dhcp/dhcpv6"
+ "github.com/insomniacslk/dhcp/dhcpv6/nclient6"
+ "github.com/insomniacslk/dhcp/iana"
+
+ gen "golang.zx2c4.com/wireguard/android/gen"
+ "google.golang.org/protobuf/types/known/durationpb"
+)
+
+const (
+ ENABLE_PD = true
+)
+
+type dhcp struct {
+ fqdn string
+ hwAddr net.HardwareAddr
+ conn *net.UDPConn
+ serverAddr net.UDPAddr
+ client *nclient6.Client
+ linkAddr net.IP
+ peerAddr net.IP
+}
+
+func newClientIDOpt(duid dhcpv6.DUID) dhcpv4.Option {
+ iaid := []byte{0, 0, 0, 3}
+ ident := []byte{255} // Type IAID+DUID
+ ident = append(ident, iaid...) // IAID
+ ident = append(ident, duid.ToBytes()...) // DUID
+ return dhcpv4.OptClientIdentifier(ident)
+}
+
+func getDuid(hwAddr net.HardwareAddr) dhcpv6.DUID {
+ duid := &dhcpv6.DUIDLLT{
+ HWType: iana.HWTypeEthernet,
+ Time: uint32(time.Now().Unix()),
+ LinkLayerAddr: hwAddr,
+ }
+
+ return duid
+}
+
+func (d *dhcp) encapSendAndRead(ctx context.Context, msg *dhcpv6.Message, match nclient6.Matcher) (*dhcpv6.Message, error) {
+ packet, err := dhcpv6.EncapsulateRelay(msg, dhcpv6.MessageTypeRelayForward, d.linkAddr, d.peerAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ relay, err := d.client.SendAndReadRelay(ctx, &d.serverAddr, packet, match)
+ if err != nil {
+ return nil, err
+ }
+
+ inner, err := relay.GetInnerMessage()
+ if err != nil {
+ return nil, err
+ }
+
+ return inner, nil
+}
+
+// isRelayMessageType returns a matcher that checks for the message type.
+func isRelayMessageType(t dhcpv6.MessageType, tt ...dhcpv6.MessageType) nclient6.Matcher {
+ return func(p dhcpv6.DHCPv6) bool {
+ inner, err := p.GetInnerMessage()
+ if err != nil {
+ return false
+ }
+ if inner.Type() == t {
+ return true
+ }
+ for _, mt := range tt {
+ if inner.Type() == mt {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+// func New() *dhcp {
+// }
+
+func RunDhcp(ctx context.Context, laddr, raddr netip.Addr) ([]*gen.Lease, error) {
+ d := &dhcp{}
+
+ d.linkAddr = net.ParseIP("fe80::101")
+ d.peerAddr = net.ParseIP("::1")
+
+ // TODO generate hostname and hwAddr from public key
+ hostName := "foobar"
+ d.fqdn = hostName + ".m7n.se"
+ d.hwAddr = []byte{41, 42, 43, 44, 45, 46}
+
+
+ src := net.UDPAddr{IP: laddr.AsSlice(),
+ Port: 0, // Use non-restrict UDP source port
+ }
+
+
+ d.serverAddr = net.UDPAddr{IP: raddr.AsSlice(),
+ Port: 547,
+ }
+
+ err := d.Start(&src)
+ if err != nil {
+ return nil, err
+ }
+
+ defer d.Close()
+
+ reply, err := d.ObtainLease(ctx) // Use reply
+ if err != nil {
+ return nil, err
+ }
+
+ return getAddressesFromReply(reply), nil
+}
+
+func getAddressesFromReply(reply *dhcpv6.Message) []*gen.Lease{
+ var leases []*gen.Lease = make([]*gen.Lease, 0, 4)
+
+ if opt := reply.GetOneOption(dhcpv6.OptionIANA); opt != nil {
+ iana := opt.(*dhcpv6.OptIANA)
+ ianaOpts := iana.Options.Get(dhcpv6.OptionIAAddr)
+
+ for _, opt := range ianaOpts {
+ addrOpt := opt.(*dhcpv6.OptIAAddress)
+ lease := &gen.Lease{
+ Address: &gen.InetAddress{
+ Address: addrOpt.IPv6Addr,
+ },
+ PreferredLifetime: durationpb.New(addrOpt.PreferredLifetime),
+ ValidLifetime: durationpb.New(addrOpt.ValidLifetime),
+ }
+ leases = append(leases, lease)
+ }
+ }
+
+ return leases
+}
+
+func (d *dhcp) Start(localAddr *net.UDPAddr) error {
+ conn, err := net.ListenUDP("udp6", localAddr)
+ if err != nil {
+ return err
+ }
+
+ d.client, err = nclient6.NewWithConn(conn, d.hwAddr, nclient6.WithDebugLogger())
+ return err
+}
+
+func (d *dhcp) Close() error {
+ err := d.client.Close()
+
+ d.client = nil
+ return err
+}
+
+func (d *dhcp) ObtainLease(ctx context.Context) (*dhcpv6.Message, error){
+ duidOpt := dhcpv6.WithClientID(getDuid(d.hwAddr))
+ fqdnOpt := dhcpv6.WithFQDN(0x1, d.fqdn)
+
+ solicit, err := dhcpv6.NewSolicit(d.hwAddr, duidOpt, fqdnOpt, dhcpv6.WithRapidCommit)
+ if err != nil {
+ return nil, err
+ }
+
+ msg, err := d.encapSendAndRead(ctx, solicit, isRelayMessageType(dhcpv6.MessageTypeReply, dhcpv6.MessageTypeAdvertise))
+ if err != nil {
+ return nil, err
+ }
+
+ if msg.Type() == dhcpv6.MessageTypeReply {
+ // We got RapidCommitted.
+ return msg, nil
+ }
+
+ // We didn't get RapidCommitted. Request regular lease.
+ req, err := dhcpv6.NewRequestFromAdvertise(msg, fqdnOpt)
+ if err != nil {
+ return nil, err
+ }
+
+ return d.encapSendAndRead(ctx, req, nil)
+}
diff --git a/tunnel/tools/libwg-go/go.mod b/tunnel/tools/libwg-go/go.mod
index 9318ebcf..86a09293 100644
--- a/tunnel/tools/libwg-go/go.mod
+++ b/tunnel/tools/libwg-go/go.mod
@@ -3,12 +3,27 @@ module golang.zx2c4.com/wireguard/android
go 1.20
require (
- golang.org/x/sys v0.6.0
- golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
+ github.com/elazarl/goproxy v0.0.0-20231017160920-1fe6677f404d
+ github.com/insomniacslk/dhcp v0.0.0-00010101000000-000000000000
+ golang.org/x/net v0.17.0
+ golang.org/x/sys v0.13.0
+ golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb
+ google.golang.org/grpc v1.39.0
+ google.golang.org/protobuf v1.27.1
+ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20210326210528-650f7c854440
)
require (
- golang.org/x/crypto v0.7.0 // indirect
- golang.org/x/net v0.8.0 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
+ github.com/josharian/native v1.1.0 // indirect
+ github.com/pierrec/lz4/v4 v4.1.14 // indirect
+ github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
+ golang.org/x/crypto v0.14.0 // indirect
+ golang.org/x/text v0.13.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
+ google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
)
+
+replace github.com/insomniacslk/dhcp => /src/insomniacslk-dhcp
+
+replace golang.zx2c4.com/wireguard => /src/wireguard-go
diff --git a/tunnel/tools/libwg-go/go.sum b/tunnel/tools/libwg-go/go.sum
index 3b49b497..70c365ff 100644
--- a/tunnel/tools/libwg-go/go.sum
+++ b/tunnel/tools/libwg-go/go.sum
@@ -1,10 +1,167 @@
-golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/elazarl/goproxy v0.0.0-20231017160920-1fe6677f404d h1:ikfPAvbWWkCagwNfwfWsIaRyKHDF1I6T4lh7PewKV9A=
+github.com/elazarl/goproxy v0.0.0-20231017160920-1fe6677f404d/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
+github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
+github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
+github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
+github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
-golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU=
-golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f h1:YORWxaStkWBnWgELOHTmDrqNlFXuVGEbhwbB5iK94bQ=
+google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/tunnel/tools/libwg-go/http-proxy.go b/tunnel/tools/libwg-go/http-proxy.go
new file mode 100644
index 00000000..11ab0716
--- /dev/null
+++ b/tunnel/tools/libwg-go/http-proxy.go
@@ -0,0 +1,736 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/netip"
+ "net/url"
+ "strings"
+ "sync"
+
+ "github.com/elazarl/goproxy"
+
+ "golang.zx2c4.com/wireguard/device"
+
+ net_proxy "golang.org/x/net/proxy"
+
+ "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 {
+ listener net.Listener
+ tlsListener net.Listener
+ logger *device.Logger
+ addrPort netip.AddrPort
+ tlsAddrPort netip.AddrPort
+ ctx *duktape.Context
+ defaultProxy *goproxy.ProxyHttpServer
+ uidRequest chan UidRequest
+ handlers []*HttpHandler
+}
+
+func NewHttpProxy(uidRequest chan UidRequest, logger *device.Logger) *HttpProxy {
+ logger.Verbosef("NewHttpProxy")
+ return &HttpProxy{
+ listener: nil,
+ logger: logger,
+ uidRequest: uidRequest,
+ handlers: make([]*HttpHandler, 0, 2),
+ }
+}
+
+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
+}
+
+type Logger struct {
+ logger *device.Logger
+}
+
+func (l *Logger) Printf(format string, v ...interface{}) {
+ l.logger.Verbosef(format, v...)
+}
+
+func (p *HttpProxy) newGoProxy(cat, proxyUrl string) (*goproxy.ProxyHttpServer, error) {
+ proxy := goproxy.NewProxyHttpServer()
+ proxy.Logger = &Logger{logger: p.logger}
+ proxy.Verbose = true
+ if cat == "SOCKS5" {
+ socksDialer, err := net_proxy.SOCKS5("tcp", proxyUrl, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ proxy.Tr.Dial = socksDialer.Dial
+ }
+ proxy.NonproxyHandler = http.HandlerFunc(func (w http.ResponseWriter, req *http.Request) {
+ if req.Host == "" {
+ fmt.Fprintln(w, "Cannot handle requests without Host header, e.g., HTTP 1.0")
+ return
+ }
+ req.URL.Scheme = "http"
+ req.URL.Host = req.Host
+ proxy.ServeHTTP(w, req)
+ })
+
+ if cat == "PROXY" {
+ proxy.Tr.Proxy = func(req *http.Request) (*url.URL, error) {
+ return url.Parse(proxyUrl)
+ }
+ proxy.ConnectDial = proxy.NewConnectDialToProxy(proxyUrl)
+ }
+
+ return proxy, nil
+}
+
+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 newPacBodyCtx(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)
+ logger.Verbosef("ASCII_PAC_UTILS result is: %v stack:%v", ctx.GetType(-1), ctx.GetTop())
+ ctx.Pop()
+
+ 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) newPacFileCtx(pacFileUrl *url.URL) (*duktape.Context, error) {
+ var pacFileBody = ""
+ if pacFileUrl == nil {
+ return nil, nil
+ }
+
+ resp, err := http.Get(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)
+ return newPacBodyCtx(pacFileBody, p.logger), nil
+ }
+ }
+ }
+
+ return nil, err
+}
+
+func (p *HttpProxy) SetPacFileUrl(pacFileUrl *url.URL) error {
+ ctx, err := p.newPacFileCtx(pacFileUrl)
+ if err != nil {
+ return err
+ }
+
+ for _, handler := range(p.handlers) {
+ handler.setPacCtx(ctx)
+ }
+
+ if p.ctx != nil {
+ p.ctx.Destroy()
+ }
+ p.ctx = ctx
+ p.logger.Verbosef("SetNotMetered(false) from SetPacFileUrl")
+ p.SetNotMetered(false)
+ return nil
+}
+
+func (p *HttpProxy) SetPacFileContent(pacFile string) error {
+ ctx := newPacBodyCtx(pacFile, p.logger)
+
+ for _, handler := range(p.handlers) {
+ handler.setPacCtx(ctx)
+ }
+
+ if p.ctx != nil {
+ p.ctx.Destroy()
+ }
+ p.ctx = ctx
+ p.logger.Verbosef("SetNotMetered(false) from SetPacFileContent")
+ p.SetNotMetered(false)
+ return nil
+}
+
+func (p *HttpProxy) SetNotMetered(notMetered bool) error {
+ p.logger.Verbosef("SetNotMetered: %v", notMetered)
+
+ if p.ctx == nil {
+ return fmt.Errorf("No ductape context")
+ }
+
+ p.ctx.PushGlobalObject()
+ p.ctx.PushBoolean(notMetered)
+ p.ctx.PutPropString(-2, "notMetered")
+ p.ctx.Pop()
+
+ return nil
+}
+
+func (p *HttpProxy) Start() (listen_port uint16, err error) {
+ p.logger.Verbosef("HttpProxy.Start()")
+ listen_port = 0
+
+
+ proxyMap := make(map[string]*goproxy.ProxyHttpServer)
+
+ p.defaultProxy, err = p.newGoProxy("", "")
+ if err != nil {
+ return
+ }
+ proxyMap[""] = p.defaultProxy
+
+ listen_port, handler, err := p.startRegularProxy(proxyMap)
+ if err != nil {
+ return
+ }
+ p.handlers = append(p.handlers, handler)
+
+ return
+}
+
+func (p *HttpProxy) startRegularProxy(proxyMap map[string]*goproxy.ProxyHttpServer) (listen_port uint16, handler *HttpHandler, err error) {
+ p.listener, err = net.Listen("tcp", "[::]:")
+ if err != nil {
+ return
+ }
+
+ p.addrPort, err = netip.ParseAddrPort(p.listener.Addr().String())
+ if err != nil {
+ return
+ }
+
+ listen_port = p.addrPort.Port()
+
+ handler = NewHttpHandler(p, p.defaultProxy, proxyMap, p.logger)
+
+ go http.Serve(NewListener(p.listener, handler, p.logger), handler)
+ return
+}
+
+func (p *HttpProxy) Stop() {
+ if p.listener != nil {
+ p.logger.Verbosef("Close: %v", p.listener)
+ p.listener.Close()
+// p.listener = nil
+ }
+ if p.tlsListener != nil {
+ p.logger.Verbosef("Close: %v", p.tlsListener)
+ p.tlsListener.Close()
+// p.tlsListener = nil
+ }
+ if p.ctx != nil {
+ p.ctx.DestroyHeap()
+ p.ctx = nil
+ }
+}
+
+type HttpHandler struct {
+ p *HttpProxy
+ defaultProxy *goproxy.ProxyHttpServer
+ logger *device.Logger
+ remoteAddrPkgMapMutex sync.RWMutex
+ remoteAddrPkgMap map[string]*goproxy.ProxyHttpServer
+ uidRequest chan UidRequest
+ ctx *duktape.Context
+ proxyMap map[string]*goproxy.ProxyHttpServer // FIXME include type in map key
+}
+
+func NewHttpHandler(p *HttpProxy, defaultProxy *goproxy.ProxyHttpServer, proxyMap map[string]*goproxy.ProxyHttpServer, logger *device.Logger) *HttpHandler{
+ h := &HttpHandler{
+ p: p,
+ defaultProxy: defaultProxy,
+ logger: logger,
+ remoteAddrPkgMapMutex: sync.RWMutex{},
+ remoteAddrPkgMap: make(map[string]*goproxy.ProxyHttpServer),
+ uidRequest: p.uidRequest,
+ proxyMap: proxyMap,
+ }
+
+ return h
+}
+
+// TODO fix multi-threading
+func (h *HttpHandler) setPacCtx(ctx *duktape.Context) {
+ h.ctx = ctx
+}
+
+func (h *HttpHandler) addConnToProxyMap(c net.Conn) error {
+ h.logger.Verbosef("Accept: %v -> %v", c.RemoteAddr().String(), c.LocalAddr().String())
+ local, err := netip.ParseAddrPort(c.RemoteAddr().String())
+ if err != nil {
+ return fmt.Errorf("Bad local address (%v): %v", c.RemoteAddr(), err)
+ }
+ // Remove IPv6 zone, NOOP on IPv4
+ local = netip.AddrPortFrom(local.Addr().WithZone(""), local.Port())
+
+ remote, err := netip.ParseAddrPort(c.LocalAddr().String())
+ if err != nil {
+ return fmt.Errorf("Bad remote address (%v): %v", c.LocalAddr(), err)
+ }
+
+ h.logger.Verbosef("uidRequest: %v -> %v", local, remote)
+ addrPortPair := AddrPortPair{local: local, remote: remote}
+ retCh := make(chan string)
+ h.uidRequest <- UidRequest{Data: addrPortPair, RetCh: retCh}
+
+ select {
+ case pkg := <-retCh:
+ h.logger.Verbosef("uidResponse: '%v'", pkg)
+
+ // TODO add NotMetered as
+ proxy, ok := h.proxyMap[pkg]
+ if !ok {
+ proxy, err = h.findProxyForPkg(pkg)
+ if err != nil {
+ return err
+ }
+ }
+
+ if proxy != nil {
+ h.remoteAddrPkgMapMutex.Lock()
+ h.remoteAddrPkgMap[c.RemoteAddr().String()] = proxy
+ h.remoteAddrPkgMapMutex.Unlock()
+ }
+ }
+
+ return nil
+}
+
+func (h *HttpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+ h.remoteAddrPkgMapMutex.Lock()
+ proxy, ok := h.remoteAddrPkgMap[req.RemoteAddr]
+ if ok && proxy != nil {
+ delete(h.remoteAddrPkgMap, req.RemoteAddr)
+ h.remoteAddrPkgMapMutex.Unlock()
+
+ proxyStr := "nil"
+ proxyUrl, err := proxy.Tr.Proxy(req)
+ if err == nil && proxyUrl != nil {
+ proxyStr = proxyUrl.String()
+ }
+ h.logger.Verbosef("ServeHTTP remote:%s proxy:%v", req.RemoteAddr, proxyStr)
+
+ proxy.ServeHTTP(rw, req)
+ } else {
+ h.remoteAddrPkgMapMutex.Unlock()
+ h.defaultProxy.ServeHTTP(rw, req)
+ }
+}
+
+func (h *HttpHandler) findProxyForPkg(pkg string) (*goproxy.ProxyHttpServer, error) {
+ if h.ctx == nil {
+ return nil, fmt.Errorf("Null context")
+ }
+
+ find := func() (cat string, res string, err error) {
+ h.logger.Verbosef("Call FindProxyForPkg %v %v", pkg)
+ res, err = FindProxyForPkg(h.ctx, pkg, h.logger)
+ h.logger.Verbosef("FindProxyForPkg res %v %v", res, err)
+ if err != nil {
+ h.logger.Verbosef("FindProxyForPkg result is: %v stack:%v", res, h.ctx.GetTop())
+ return "", "", err
+ } else if res == "" {
+ return "DIRECT", "", nil
+ } else {
+ values := strings.Split(strings.Trim(res, " "), ";")
+ for _, v := range values {
+ value := strings.Trim(v, " ")
+ parts := strings.SplitN(value, " ", 2)
+ if parts[0] == "PROXY" || parts[0] == "HTTP" {
+ return "PROXY", "http://" + parts[1], nil
+ } else if parts[0] == "HTTPS" {
+ return "PROXY", "https://" + parts[1], nil
+ } else if parts[0] == "DIRECT" {
+ return parts[0], "", nil
+ } else if parts[0] == "SOCKS" || parts[0] == "SOCKS5" {
+ return "SOCKS5", parts[1], nil
+ }
+ }
+ }
+ return "", "", fmt.Errorf("No result")
+ }
+ cat, res, err := find()
+ if err != nil {
+ return nil, err
+ }
+
+ var proxy *goproxy.ProxyHttpServer
+ if cat == "DIRECT" {
+ proxy = h.defaultProxy
+ } else {
+ proxy, err = h.p.newGoProxy(cat, res)
+ if err != nil {
+ return nil, err
+ }
+ }
+ h.proxyMap[pkg] = proxy
+
+ return proxy, nil
+}
+
+type AddrPortPair struct {
+ local netip.AddrPort
+ remote netip.AddrPort
+}
+
+// Listener
+type Listener struct {
+ l net.Listener
+ handler *HttpHandler
+ logger *device.Logger
+}
+
+func NewListener(listener net.Listener, handler *HttpHandler, logger *device.Logger) *Listener{
+ l := &Listener{
+ l: listener,
+ handler: handler,
+ logger: logger,
+ }
+
+ return l
+}
+
+func (l *Listener) Accept() (net.Conn, error) {
+ c, err := l.l.Accept()
+ if err != nil {
+ l.logger.Verbosef("Accept failed: %v", err)
+ return c, err
+ }
+
+ if err := l.handler.addConnToProxyMap(c); err != nil {
+ err = fmt.Errorf("Reject connection (%v): %v", c, err)
+ c.Close()
+ return nil, err
+ }
+
+ return c, nil
+}
+
+func (l *Listener) Close() error {
+ return l.l.Close()
+}
+
+func (l *Listener) Addr() net.Addr {
+ return l.l.Addr()
+}
diff --git a/tunnel/tools/libwg-go/jni.c b/tunnel/tools/libwg-go/jni.c
index 7ad94d35..443848e8 100644
--- a/tunnel/tools/libwg-go/jni.c
+++ b/tunnel/tools/libwg-go/jni.c
@@ -14,6 +14,9 @@ extern int wgGetSocketV4(int handle);
extern int wgGetSocketV6(int handle);
extern char *wgGetConfig(int handle);
extern char *wgVersion();
+extern int wgSetConfig(int handle, struct go_string settings);
+extern int wgStartGrpc();
+extern int wgSetFd(int handle, int tun_fd);
JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings)
{
@@ -69,3 +72,32 @@ JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgVersion
free(version);
return ret;
}
+
+JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgSetConfig(JNIEnv *env, jclass c, jint handle, jstring settings)
+{
+ const char *settings_str = (*env)->GetStringUTFChars(env, settings, 0);
+ size_t settings_len = (*env)->GetStringUTFLength(env, settings);
+ int ret = wgSetConfig(handle, (struct go_string){
+ .str = settings_str,
+ .n = settings_len
+ });
+ (*env)->ReleaseStringUTFChars(env, settings, settings_str);
+ return ret;
+}
+
+JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgStartGrpc(JNIEnv *env, jclass c, jstring sockname)
+{
+ const char *sockname_str = (*env)->GetStringUTFChars(env, sockname, 0);
+ size_t sockname_len = (*env)->GetStringUTFLength(env, sockname);
+ jint res = wgStartGrpc((struct go_string){
+ .str = sockname_str,
+ .n = sockname_len
+ });
+ (*env)->ReleaseStringUTFChars(env, sockname, sockname_str);
+ return res;
+}
+
+JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgSetFd(JNIEnv *env, jclass c, jint handle, jint tun_fd)
+{
+ wgSetFd(handle, tun_fd);
+}
diff --git a/tunnel/tools/libwg-go/service.go b/tunnel/tools/libwg-go/service.go
new file mode 100644
index 00000000..41834fe0
--- /dev/null
+++ b/tunnel/tools/libwg-go/service.go
@@ -0,0 +1,342 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net"
+ "net/netip"
+ "net/url"
+ "os"
+
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ gen "golang.zx2c4.com/wireguard/android/gen"
+ "golang.zx2c4.com/wireguard/device"
+)
+
+const (
+ IPPROTO_TCP = 6
+)
+
+type UidRequest struct {
+ Data AddrPortPair
+ RetCh chan string
+}
+
+type LibwgServiceImpl struct {
+ gen.UnimplementedLibwgServer
+ logger *device.Logger
+ httpProxy *HttpProxy
+ uidRequest chan UidRequest
+ stopReverse chan bool
+}
+
+var service *LibwgServiceImpl
+var server *grpc.Server
+
+func NewLibwgService(logger *device.Logger) gen.LibwgServer {
+ return &LibwgServiceImpl{
+ logger: logger,
+ uidRequest: make(chan UidRequest),
+ stopReverse: make(chan bool),
+ }
+}
+
+func StartGrpc(sock_path string, logger *device.Logger) (int, string) {
+ if server != nil {
+ return -1, "Already started"
+ }
+
+ if _, err := os.Stat(sock_path); err == nil {
+ if err := os.RemoveAll(sock_path); err != nil {
+ return -1, fmt.Sprintf("Cleanup failed: %v %v", sock_path, err)
+ }
+ }
+
+ listener, err := net.Listen("unix", sock_path)
+ if err != nil {
+ return -1, fmt.Sprintf("Listen failed: %v %v", sock_path, err)
+ }
+
+ server = grpc.NewServer()
+ service = NewLibwgService(logger).(*LibwgServiceImpl)
+
+ gen.RegisterLibwgServer(server, service)
+
+ go func() {
+ server.Serve(listener)
+ }()
+
+ logger.Verbosef("gRPC started")
+
+ return 0, ""
+}
+
+func (e *LibwgServiceImpl) Version(ctx context.Context, req *gen.VersionRequest) (*gen.VersionResponse, error) {
+
+ r := &gen.VersionResponse{
+ Version: Version(),
+ }
+
+ return r, nil
+}
+
+func (e *LibwgServiceImpl) StopGrpc(ctx context.Context, req *gen.StopGrpcRequest) (*gen.StopGrpcResponse, error) {
+ if server != nil {
+ server.Stop()
+ server = nil
+ service = nil
+ }
+
+ r := &gen.StopGrpcResponse{
+ }
+
+ return r, nil
+}
+
+func buildStartHttpProxyError(message string) (*gen.StartHttpProxyResponse, error) {
+ r := &gen.StartHttpProxyResponse{
+ Error: &gen.Error{
+ Message: message,
+ },
+ }
+ return r, nil
+}
+
+func (e *LibwgServiceImpl) StartHttpProxy(ctx context.Context, req *gen.StartHttpProxyRequest) (*gen.StartHttpProxyResponse, error) {
+ var listenPort uint16
+ if e.httpProxy == nil {
+ e.httpProxy = NewHttpProxy(e.uidRequest, e.logger)
+ var err error
+ listenPort, err = e.httpProxy.Start()
+
+ if err != nil {
+ e.httpProxy = nil
+ return buildStartHttpProxyError(fmt.Sprintf("Http proxy start failed: %v", err))
+ }
+ } else {
+ listenPort = e.httpProxy.GetAddrPort().Port()
+ }
+
+ content := req.GetPacFileContent()
+ if content != "" {
+ err := e.httpProxy.SetPacFileContent(content)
+ if err != nil {
+ return buildStartHttpProxyError(fmt.Sprintf("Bad pacFileContent: %v (%s)", content))
+ }
+ } else {
+ pacFileUrl, err := url.Parse(req.GetPacFileUrl())
+ if err != nil {
+ return buildStartHttpProxyError(fmt.Sprintf("Bad pacFileUrl: %v (%s)", err, req.GetPacFileUrl()))
+ }
+ err = e.httpProxy.SetPacFileUrl(pacFileUrl)
+ if err != nil {
+ return buildStartHttpProxyError(fmt.Sprintf("Bad pacFileUrl: %v (%s)", err, pacFileUrl))
+ }
+ }
+
+ r := &gen.StartHttpProxyResponse{
+ ListenPort: uint32(listenPort),
+ }
+ return r, nil
+}
+
+func (e *LibwgServiceImpl) StopHttpProxy(ctx context.Context, req *gen.StopHttpProxyRequest) (*gen.StopHttpProxyResponse, error) {
+ if e.httpProxy == nil {
+ r := &gen.StopHttpProxyResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("Http proxy not running"),
+ },
+ }
+ return r, nil
+ }
+
+ e.httpProxy.Stop()
+ e.httpProxy = nil
+ e.stopReverse <- true
+ r := &gen.StopHttpProxyResponse{}
+ return r, nil
+}
+
+func (e *LibwgServiceImpl) Reverse(stream gen.Libwg_ReverseServer) error {
+ e.logger.Verbosef("Reverse enter loop")
+ for e.httpProxy != nil {
+ var err error
+
+ // err := contextError(stream.Context())
+ err = stream.Context().Err()
+ if err != nil {
+ e.logger.Verbosef("Reverse: context: %v", err)
+ return err
+ }
+
+ select {
+ case <-e.stopReverse:
+ e.logger.Verbosef("Reverse: stop")
+ break
+ case uidReq := <-e.uidRequest:
+ addrPortPair := uidReq.Data
+ local := addrPortPair.local
+ remote := addrPortPair.remote
+ r := &gen.ReverseResponse{
+ Request: &gen.ReverseResponse_Uid{
+ Uid: &gen.GetConnectionOwnerUidRequest{
+ Protocol: IPPROTO_TCP,
+ Local: &gen.InetSocketAddress{
+ Address: &gen.InetAddress{
+ Address: local.Addr().AsSlice(),
+ },
+ Port: uint32(local.Port()),
+ },
+ Remote: &gen.InetSocketAddress{
+ Address: &gen.InetAddress{
+ Address: remote.Addr().AsSlice(),
+ },
+ Port: uint32(remote.Port()),
+ },
+ },
+ },
+ }
+
+ stream.Send(r)
+
+ req, err := stream.Recv()
+ if err == io.EOF {
+ e.logger.Verbosef("no more data")
+ uidReq.RetCh <- ""
+ break
+ }
+ if err != nil {
+ err = status.Errorf(codes.Unknown, "cannot receive stream request: %v", err)
+ e.logger.Verbosef("Reverse: %v", err)
+ uidReq.RetCh <- ""
+ return err
+ }
+
+ e.logger.Verbosef("Reverse: received, wait: %v", req)
+ uidReq.RetCh <- req.GetUid().GetPackage()
+ }
+ }
+
+
+ e.logger.Verbosef("Reverse returns")
+ return nil
+}
+
+func (e *LibwgServiceImpl) IpcSet(ctx context.Context, req *gen.IpcSetRequest) (*gen.IpcSetResponse, error) {
+ tunnel, ok := GetTunnel(req.GetTunnel().GetHandle())
+ if !ok {
+ r := &gen.IpcSetResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("Invalid tunnel"),
+ },
+ }
+ return r, nil
+ }
+
+ err := tunnel.device.IpcSet(req.GetConfig())
+ if err != nil {
+ r := &gen.IpcSetResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("IpcSet failed: %v", err),
+ },
+ }
+ return r, nil
+ }
+
+ r := &gen.IpcSetResponse{
+ }
+
+ return r, nil
+}
+
+func (e *LibwgServiceImpl) Dhcp(ctx context.Context, req *gen.DhcpRequest) (*gen.DhcpResponse, error) {
+ var relayAddr netip.Addr
+ var sourceAddr netip.Addr
+
+ source := req.GetSource()
+ if source != nil {
+ sourceAddr, _ = netip.AddrFromSlice(source.GetAddress())
+ }
+
+ if !sourceAddr.IsValid() || !sourceAddr.Is6() {
+ r := &gen.DhcpResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("DHCPv6 source address missing"),
+ },
+ }
+ return r, nil
+ }
+
+ relay := req.GetRelay()
+ if relay != nil {
+ relayAddr, _ = netip.AddrFromSlice(relay.GetAddress())
+ } else {
+ // Construct relay address from source prefix
+ relayRaw := source.GetAddress()[:8]
+ relayRaw = append(relayRaw, 0)
+ relayRaw = append(relayRaw, 0)
+ relayRaw = append(relayRaw, 0)
+ relayRaw = append(relayRaw, 0)
+ relayRaw = append(relayRaw, 0)
+ relayRaw = append(relayRaw, 0)
+ relayRaw = append(relayRaw, 0)
+ relayRaw = append(relayRaw, 1)
+ relayAddr, _ = netip.AddrFromSlice(relayRaw)
+ }
+
+ if !relayAddr.IsValid() || !relayAddr.Is6() {
+ r := &gen.DhcpResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("DHCPv6 relay address calculation failed"),
+ },
+ }
+ return r, nil
+ }
+
+ e.logger.Verbosef("RunDhcp %v %v", sourceAddr, relayAddr)
+
+ leases, err := RunDhcp(ctx, sourceAddr, relayAddr)
+ if err != nil {
+ r := &gen.DhcpResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("RunDhcp failed: %v", err),
+ },
+ }
+ return r, nil
+ }
+
+ r := &gen.DhcpResponse{
+ Leases: leases,
+ }
+ return r, nil
+}
+
+func (e *LibwgServiceImpl) CapabilitiesChanged(ctx context.Context, req *gen.CapabilitiesChangedRequest) (*gen.CapabilitiesChangedResponse, error) {
+ if e.httpProxy == nil {
+ r := &gen.CapabilitiesChangedResponse{
+ Error: &gen.Error{
+ Message: fmt.Sprintf("Http Proxy not running"),
+ },
+ }
+ return r, nil
+ }
+
+ notMetered := false
+
+ for _, cap := range req.Capabilities {
+ if cap == gen.CapabilitiesChangedRequest_NOT_METERED {
+ notMetered = true
+ }
+ }
+
+ e.logger.Verbosef("Set not metered %v", notMetered)
+ e.httpProxy.SetNotMetered(notMetered)
+
+ r := &gen.CapabilitiesChangedResponse{
+ }
+ return r, nil
+}