From 8098837389e04a0fdb5a5c5b1553a19ffcc675fc Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Tue, 11 Jun 2024 20:35:52 +0200 Subject: [PATCH 01/14] add error log --- ios/discover.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ios/discover.go b/ios/discover.go index de21a710..3ee640bf 100644 --- a/ios/discover.go +++ b/ios/discover.go @@ -3,6 +3,7 @@ package ios import ( "context" "fmt" + "log/slog" "net" "github.com/grandcat/zeroconf" @@ -55,16 +56,18 @@ func checkEntry(ctx context.Context, device DeviceEntry, interfaceName string, e continue } for _, ip6 := range entry.AddrIPv6 { - tryHandshake(ip6, interfaceName, device.Properties.SerialNumber, result) + tryHandshake(ip6, entry.Port, interfaceName, device.Properties.SerialNumber, result) } } } } -func tryHandshake(ip6 net.IP, interfaceName, udid string, result chan<- string) { +func tryHandshake(ip6 net.IP, port int, interfaceName, udid string, result chan<- string) { addr := fmt.Sprintf("%s%%%s", ip6.String(), interfaceName) - s, err := NewWithAddr(addr) + s, err := NewWithAddrPort(addr, port) + if err != nil { + slog.Error("failed to connect to remote service discovery", "error", err, "address", addr) return } defer s.Close() From 3de32cb947b876157eee6022d26fa8e209dfe484 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Tue, 11 Jun 2024 20:37:55 +0200 Subject: [PATCH 02/14] add pairing plist and cdc binary --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b6dea61c..56728b12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +selfIdentity.plist +cmd/cdc-ncm/cdc-ncm node_modules *.mobileprovision *.p12 From f2a8fa109178b53abf1b96cf5292c6888b820e5f Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Tue, 11 Jun 2024 22:53:12 +0200 Subject: [PATCH 03/14] add lockdown tunnel support --- .gitignore | 1 + go.work.sum | 35 +++++++++++++ ios/tunnel/tunnel.go | 18 ++++--- ios/tunnel/tunnel_api.go | 3 +- ios/tunnel/tunnel_lockdown.go | 97 +++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 go.work.sum create mode 100644 ios/tunnel/tunnel_lockdown.go diff --git a/.gitignore b/.gitignore index 56728b12..e864ca4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +__debug_bin* selfIdentity.plist cmd/cdc-ncm/cdc-ncm node_modules diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 00000000..cd97ee8e --- /dev/null +++ b/go.work.sum @@ -0,0 +1,35 @@ +github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= +software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= diff --git a/ios/tunnel/tunnel.go b/ios/tunnel/tunnel.go index 2b737dcb..2dbbddd5 100644 --- a/ios/tunnel/tunnel.go +++ b/ios/tunnel/tunnel.go @@ -21,6 +21,7 @@ import ( "github.com/quic-go/quic-go" "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" "github.com/songgao/water" ) @@ -43,6 +44,7 @@ func (t Tunnel) Close() error { // ManualPairAndConnectToTunnel tries to verify an existing pairing, and if this fails it triggers a new manual pairing process. // After a successful pairing a tunnel for this device gets started and the tunnel information is returned func ManualPairAndConnectToTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager) (Tunnel, error) { + log.Info("ManualPairAndConnectToTunnel: starting manual pairing and tunnel connection, dont forget to stop remoted first with 'sudo pkill -SIGSTOP remoted' and run this with sudo.") addr, err := ios.FindDeviceInterfaceAddress(ctx, device) if err != nil { return Tunnel{}, fmt.Errorf("ManualPairAndConnectToTunnel: failed to find device ethernet interface: %w", err) @@ -117,7 +119,13 @@ func connectToTunnel(ctx context.Context, info tunnelListener, addr string, devi return Tunnel{}, err } - tunnelInfo, err := exchangeCoreTunnelParameters(conn) + stream, err := conn.OpenStream() + if err != nil { + return Tunnel{}, err + } + + tunnelInfo, err := exchangeCoreTunnelParameters(stream) + stream.Close() if err != nil { return Tunnel{}, fmt.Errorf("could not exchange tunnel parameters. %w", err) } @@ -277,13 +285,7 @@ func forwardDataToInterface(ctx context.Context, conn quic.Connection, w io.Writ } } -func exchangeCoreTunnelParameters(conn quic.Connection) (tunnelParameters, error) { - stream, err := conn.OpenStream() - if err != nil { - return tunnelParameters{}, err - } - defer stream.Close() - +func exchangeCoreTunnelParameters(stream io.ReadWriteCloser) (tunnelParameters, error) { rq, err := json.Marshal(map[string]interface{}{ "type": "clientHandshakeRequest", "mtu": 1280, diff --git a/ios/tunnel/tunnel_api.go b/ios/tunnel/tunnel_api.go index 0a5f8ba0..c3c718f1 100644 --- a/ios/tunnel/tunnel_api.go +++ b/ios/tunnel/tunnel_api.go @@ -222,7 +222,8 @@ type manualPairingTunnelStart struct { } func (m manualPairingTunnelStart) StartTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager) (Tunnel, error) { - return ManualPairAndConnectToTunnel(ctx, device, p) + return ConnectTunnelLockdown(device) + //return ManualPairAndConnectToTunnel(ctx, device, p) } type deviceList struct { diff --git a/ios/tunnel/tunnel_lockdown.go b/ios/tunnel/tunnel_lockdown.go new file mode 100644 index 00000000..7c9155df --- /dev/null +++ b/ios/tunnel/tunnel_lockdown.go @@ -0,0 +1,97 @@ +package tunnel + +import ( + "context" + "fmt" + "io" + + "github.com/danielpaulus/go-ios/ios" + "github.com/sirupsen/logrus" +) + +const coreDeviceProxy = "com.apple.internal.devicecompute.CoreDeviceProxy" + +func ConnectTunnelLockdown(device ios.DeviceEntry) (Tunnel, error) { + conn, err := ios.ConnectToService(device, coreDeviceProxy) + if err != nil { + return Tunnel{}, err + } + return connectToTunnelLockdown(context.TODO(), tunnelListener{}, "", device, conn) +} + +func connectToTunnelLockdown(ctx context.Context, info tunnelListener, addr string, device ios.DeviceEntry, connToDevice io.ReadWriteCloser) (Tunnel, error) { + logrus.WithField("address", addr).WithField("port", info.TunnelPort).Info("connect to tunnel endpoint on device") + + tunnelInfo, err := exchangeCoreTunnelParameters(connToDevice) + if err != nil { + return Tunnel{}, fmt.Errorf("could not exchange tunnel parameters. %w", err) + } + + utunIface, err := setupTunnelInterface(err, tunnelInfo) + if err != nil { + return Tunnel{}, fmt.Errorf("could not setup tunnel interface. %w", err) + } + + // we want a copy of the parent ctx here, but it shouldn't time out/be cancelled at the same time. + // doing it like this allows us to have a context with a timeout for the tunnel creation, but the tunnel itself + tunnelCtx, _ := context.WithCancel(context.WithoutCancel(ctx)) + + go func() { + err := forwardTCPToInterface(tunnelCtx, connToDevice, utunIface) + if err != nil { + logrus.WithError(err).Error("failed to forward data to tunnel interface") + } + }() + + go func() { + err := forwardTCPToDevice(tunnelCtx, tunnelInfo.ClientParameters.Mtu, utunIface, connToDevice) + if err != nil { + logrus.WithError(err).Error("failed to forward data to the device") + } + }() + + return Tunnel{ + Address: tunnelInfo.ServerAddress, + RsdPort: int(tunnelInfo.ServerRSDPort), + Udid: device.Properties.SerialNumber, + closer: nil, + }, nil +} + +func forwardTCPToDevice(ctx context.Context, mtu uint64, r io.Reader, conn io.Writer) error { + packet := make([]byte, mtu) + for { + select { + case <-ctx.Done(): + return nil + default: + n, err := r.Read(packet) + if err != nil { + return fmt.Errorf("could not read packet. %w", err) + } + _, err = conn.Write(packet[:n]) + if err != nil { + return fmt.Errorf("could not write packet. %w", err) + } + } + } +} + +func forwardTCPToInterface(ctx context.Context, conn io.Reader, w io.Writer) error { + for { + select { + case <-ctx.Done(): + return nil + default: + b := make([]byte, 20000) + n, err := conn.Read(b) + if err != nil { + return fmt.Errorf("failed to read datagram. %w", err) + } + _, err = w.Write(b[:n]) + if err != nil { + return fmt.Errorf("failed to forward data. %w", err) + } + } + } +} From 287439e85d5191756b77a063f5843ae5116a9ee5 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Tue, 11 Jun 2024 23:12:04 +0200 Subject: [PATCH 04/14] add utun commands for windows --- ios/tunnel/tunnel.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ios/tunnel/tunnel.go b/ios/tunnel/tunnel.go index 2dbbddd5..02df622e 100644 --- a/ios/tunnel/tunnel.go +++ b/ios/tunnel/tunnel.go @@ -174,7 +174,17 @@ func setupTunnelInterface(err error, tunnelInfo tunnelParameters) (*water.Interf } const prefixLength = 64 // TODO: this could be calculated from the netmask provided by the device - setIpAddr := exec.Command("ifconfig", ifce.Name(), "inet6", "add", fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)) + var command []string + + switch runtime.GOOS { + case "windows": + command = []string{"netsh", "interface", "ipv6", "add", "address", ifce.Name(), fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)} + log.Info("windows cmd") + log.Info(command) + default: + command = []string{"ifconfig", ifce.Name(), "inet6", "add", fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)} + } + setIpAddr := exec.Command(command[0], command[1:]...) err = runCmd(setIpAddr) if err != nil { return nil, fmt.Errorf("setupTunnelInterface: failed to set IP address for interface: %w", err) From 0992cba911a61c540a5bb39297af4f35a5675484 Mon Sep 17 00:00:00 2001 From: "daniel@windowslaptop.de" Date: Wed, 12 Jun 2024 19:17:59 +0200 Subject: [PATCH 05/14] add some tweaks --- README.md | 2 ++ ios/tunnel/tunnel.go | 6 +++--- ios/tunnel/tunnel_lockdown.go | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a023fdb5..2ef680d9 100644 --- a/README.md +++ b/README.md @@ -125,3 +125,5 @@ ios listen [options] Keeps a persi ios diskspace [options] Prints disk space info. ``` +For Windows install: https://build.openvpn.net/downloads/releases +use wireguard-go \ No newline at end of file diff --git a/ios/tunnel/tunnel.go b/ios/tunnel/tunnel.go index 02df622e..0e894708 100644 --- a/ios/tunnel/tunnel.go +++ b/ios/tunnel/tunnel.go @@ -130,7 +130,7 @@ func connectToTunnel(ctx context.Context, info tunnelListener, addr string, devi return Tunnel{}, fmt.Errorf("could not exchange tunnel parameters. %w", err) } - utunIface, err := setupTunnelInterface(err, tunnelInfo) + utunIface, err := setupTunnelInterface(tunnelInfo) if err != nil { return Tunnel{}, fmt.Errorf("could not setup tunnel interface. %w", err) } @@ -165,12 +165,12 @@ func connectToTunnel(ctx context.Context, info tunnelListener, addr string, devi }, nil } -func setupTunnelInterface(err error, tunnelInfo tunnelParameters) (*water.Interface, error) { +func setupTunnelInterface(tunnelInfo tunnelParameters) (*water.Interface, error) { ifce, err := water.New(water.Config{ DeviceType: water.TUN, }) if err != nil { - logrus.Fatal(err) + return nil, fmt.Errorf("setupTunnelInterface: failed creating TUN device %w", err) } const prefixLength = 64 // TODO: this could be calculated from the netmask provided by the device diff --git a/ios/tunnel/tunnel_lockdown.go b/ios/tunnel/tunnel_lockdown.go index 7c9155df..6bb595b9 100644 --- a/ios/tunnel/tunnel_lockdown.go +++ b/ios/tunnel/tunnel_lockdown.go @@ -27,7 +27,7 @@ func connectToTunnelLockdown(ctx context.Context, info tunnelListener, addr stri return Tunnel{}, fmt.Errorf("could not exchange tunnel parameters. %w", err) } - utunIface, err := setupTunnelInterface(err, tunnelInfo) + utunIface, err := setupTunnelInterface(tunnelInfo) if err != nil { return Tunnel{}, fmt.Errorf("could not setup tunnel interface. %w", err) } From 4f6ede4b590e54bb179970ec7e59c98e2e1ae1d7 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Wed, 12 Jun 2024 19:34:05 +0200 Subject: [PATCH 06/14] try wg pro --- go.mod | 20 ++++++++++++-------- go.sum | 30 ++++++++++++++++++++++++++++++ go.work.sum | 26 ++++++++++++++++---------- ios/tunnel/wireguard.go | 27 +++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 ios/tunnel/wireguard.go diff --git a/go.mod b/go.mod index 6ec88410..21eef9bc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.21 require ( github.com/Masterminds/semver v1.5.0 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 - go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 github.com/google/gopacket v1.1.19 github.com/google/uuid v1.1.2 github.com/grandcat/zeroconf v1.0.0 @@ -16,11 +15,12 @@ require ( github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/stretchr/testify v1.6.1 github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 - golang.org/x/crypto v0.15.0 - software.sslmate.com/src/go-pkcs12 v0.2.0 + go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 + golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db - golang.org/x/net v0.18.0 + golang.org/x/net v0.26.0 howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 + software.sslmate.com/src/go-pkcs12 v0.2.0 ) require ( @@ -36,9 +36,13 @@ require ( github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/stretchr/objx v0.1.0 // indirect go.uber.org/mock v0.3.0 // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.15.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect + golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 + golang.zx2c4.com/wireguard/windows v0.5.3 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a2f763ce..34ff551e 100644 --- a/go.sum +++ b/go.sum @@ -75,42 +75,71 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 h1:aeN+ghOV0b2VCmKKO3gqnDQ8mLbpABZgRR2FVYx4ouI= github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9/go.mod h1:roo6cZ/uqpwKMuvPG0YmzI5+AmUiMWfjCBZpGXqbTxE= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 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-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= +golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= +golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= +golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -122,3 +151,4 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 h1:AQkaJpH+/FmqRjmXZPELom5zIERYZfwTjnHpfoVMQEc= howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= +software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= diff --git a/go.work.sum b/go.work.sum index cd97ee8e..24598935 100644 --- a/go.work.sum +++ b/go.work.sum @@ -3,9 +3,19 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8V github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= +github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -18,18 +28,14 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= -go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= -software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= diff --git a/ios/tunnel/wireguard.go b/ios/tunnel/wireguard.go new file mode 100644 index 00000000..c06002de --- /dev/null +++ b/ios/tunnel/wireguard.go @@ -0,0 +1,27 @@ +package tunnel + +import ( + "fmt" + + "golang.zx2c4.com/wireguard/tun" + "golang.zx2c4.com/wireguard/windows/tunnel" +) + +func Tmain() { + // Create a TUN device + tunDevice, err := tun.CreateTUN("tun0", 1500) + if err != nil { + fmt.Println("Error creating TUN device:", err) + return + } + + // Create a new WireGuard device + device, err := tunnel.CreateTUNWithRequestedGUID("wg0", 1500, nil) + if err != nil { + fmt.Println("Error creating WireGuard device:", err) + return + } + + // Use the TUN device and WireGuard device as needed... + fmt.Println("TUN device and WireGuard device created successfully") +} From 5dc52bbe5bd3d0f381c9c759fb95d61055d92b7a Mon Sep 17 00:00:00 2001 From: "daniel@windowslaptop.de" Date: Wed, 12 Jun 2024 21:27:32 +0200 Subject: [PATCH 07/14] go mod tidy, basic wireguard implementation --- go.mod | 5 ++- go.sum | 33 +++++++----------- ios/tunnel/tunnel.go | 16 +++------ ios/tunnel/wireguard.go | 76 +++++++++++++++++++++++++++++++++++------ main.go | 1 + 5 files changed, 85 insertions(+), 46 deletions(-) diff --git a/go.mod b/go.mod index 21eef9bc..ad4f623d 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db golang.org/x/net v0.26.0 + golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 software.sslmate.com/src/go-pkcs12 v0.2.0 ) @@ -37,12 +38,10 @@ require ( github.com/stretchr/objx v0.1.0 // indirect go.uber.org/mock v0.3.0 // indirect golang.org/x/mod v0.17.0 // indirect + golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect - golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 - golang.zx2c4.com/wireguard/windows v0.5.3 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 34ff551e..4408c032 100644 --- a/go.sum +++ b/go.sum @@ -13,16 +13,17 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -75,36 +76,31 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 h1:aeN+ghOV0b2VCmKKO3gqnDQ8mLbpABZgRR2FVYx4ouI= github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9/go.mod h1:roo6cZ/uqpwKMuvPG0YmzI5+AmUiMWfjCBZpGXqbTxE= +go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 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-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -113,22 +109,18 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -136,10 +128,6 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= -golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= -golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -149,6 +137,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 h1:AQkaJpH+/FmqRjmXZPELom5zIERYZfwTjnHpfoVMQEc= howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= +software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= diff --git a/ios/tunnel/tunnel.go b/ios/tunnel/tunnel.go index 0e894708..7746320b 100644 --- a/ios/tunnel/tunnel.go +++ b/ios/tunnel/tunnel.go @@ -165,7 +165,10 @@ func connectToTunnel(ctx context.Context, info tunnelListener, addr string, devi }, nil } -func setupTunnelInterface(tunnelInfo tunnelParameters) (*water.Interface, error) { +func setupTunnelInterface(tunnelInfo tunnelParameters) (io.ReadWriteCloser, error) { + if runtime.GOOS == "windows" { + return setupWindowsTUN(tunnelInfo) + } ifce, err := water.New(water.Config{ DeviceType: water.TUN, }) @@ -174,17 +177,8 @@ func setupTunnelInterface(tunnelInfo tunnelParameters) (*water.Interface, error) } const prefixLength = 64 // TODO: this could be calculated from the netmask provided by the device - var command []string - switch runtime.GOOS { - case "windows": - command = []string{"netsh", "interface", "ipv6", "add", "address", ifce.Name(), fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)} - log.Info("windows cmd") - log.Info(command) - default: - command = []string{"ifconfig", ifce.Name(), "inet6", "add", fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)} - } - setIpAddr := exec.Command(command[0], command[1:]...) + setIpAddr := exec.Command("ifconfig", ifce.Name(), "inet6", "add", fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)) err = runCmd(setIpAddr) if err != nil { return nil, fmt.Errorf("setupTunnelInterface: failed to set IP address for interface: %w", err) diff --git a/ios/tunnel/wireguard.go b/ios/tunnel/wireguard.go index c06002de..3fe7c74e 100644 --- a/ios/tunnel/wireguard.go +++ b/ios/tunnel/wireguard.go @@ -2,26 +2,80 @@ package tunnel import ( "fmt" + "io" + "os/exec" + log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/tun" - "golang.zx2c4.com/wireguard/windows/tunnel" ) -func Tmain() { - // Create a TUN device - tunDevice, err := tun.CreateTUN("tun0", 1500) +type tunWrapper struct { + device tun.Device + readPackets [][]byte + sizes []int +} + +func (t *tunWrapper) Close() error { + return t.device.Close() +} + +func (t *tunWrapper) Write(p []byte) (int, error) { + bufs := [][]byte{p} // Create a slice of one byte slice + written, err := t.device.Write(bufs, 0) // Use offset 0 + if written > 0 { + return len(p), err // Assume the entire slice was written + } + return 0, err +} + +func (t *tunWrapper) Read(p []byte) (int, error) { + if len(t.readPackets) == 0 { + bufs := make([][]byte, 100) + for i := range bufs { + mtu, _ := t.device.MTU() + + bufs[i] = make([]byte, mtu) + } + sizes := make([]int, 100) + n, err := t.device.Read(bufs, sizes, 0) + if n == 0 { + return 0, err + } + if n > 1 { + t.readPackets = bufs[1:] + t.sizes = sizes[1:] + } + copy(p, bufs[0][:sizes[0]]) + return sizes[0], err + } + size := t.sizes[0] + copy(p, t.readPackets[0][:size]) + t.readPackets = t.readPackets[1:] + t.sizes = t.sizes[1:] + return size, nil +} + +func setupWindowsTUN(tunnelInfo tunnelParameters) (io.ReadWriteCloser, error) { + name := "tun0" + + tunDevice, err := tun.CreateTUN(name, int(tunnelInfo.ClientParameters.Mtu)) if err != nil { fmt.Println("Error creating TUN device:", err) - return + return &tunWrapper{}, err } + tunname, err := tunDevice.Name() - // Create a new WireGuard device - device, err := tunnel.CreateTUNWithRequestedGUID("wg0", 1500, nil) if err != nil { - fmt.Println("Error creating WireGuard device:", err) - return + return nil, fmt.Errorf("setupTunnelInterface: failed to get interface name: %w", err) + } + const prefixLength = 64 + setIpAddr := exec.Command("netsh", "interface", "ipv6", "add", "address", tunname, fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)) + err = runCmd(setIpAddr) + if err != nil { + return nil, fmt.Errorf("setupTunnelInterface: failed to set IP address for interface: %w", err) } + log.Info("windows cmd") + log.Info(setIpAddr.String()) - // Use the TUN device and WireGuard device as needed... - fmt.Println("TUN device and WireGuard device created successfully") + return &tunWrapper{device: tunDevice, readPackets: [][]byte{}, sizes: []int{}}, nil } diff --git a/main.go b/main.go index 79102b41..d2858883 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ const version = "local-build" // Main Exports main for testing func Main() { + usage := fmt.Sprintf(`go-ios %s Usage: From 865b95a19abe5953651fcb8129a385835ffabb02 Mon Sep 17 00:00:00 2001 From: "daniel@windowslaptop.de" Date: Wed, 12 Jun 2024 23:37:18 +0200 Subject: [PATCH 08/14] fix perf issue by dropping ipv4 packets --- ios/tunnel/tunnel_lockdown.go | 23 ++++++++---- ios/tunnel/wireguard.go | 69 ++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/ios/tunnel/tunnel_lockdown.go b/ios/tunnel/tunnel_lockdown.go index 6bb595b9..73a4702b 100644 --- a/ios/tunnel/tunnel_lockdown.go +++ b/ios/tunnel/tunnel_lockdown.go @@ -44,7 +44,7 @@ func connectToTunnelLockdown(ctx context.Context, info tunnelListener, addr stri }() go func() { - err := forwardTCPToDevice(tunnelCtx, tunnelInfo.ClientParameters.Mtu, utunIface, connToDevice) + err := forwardTUNToDevice(tunnelCtx, tunnelInfo.ClientParameters.Mtu, utunIface, connToDevice) if err != nil { logrus.WithError(err).Error("failed to forward data to the device") } @@ -58,40 +58,47 @@ func connectToTunnelLockdown(ctx context.Context, info tunnelListener, addr stri }, nil } -func forwardTCPToDevice(ctx context.Context, mtu uint64, r io.Reader, conn io.Writer) error { +func forwardTUNToDevice(ctx context.Context, mtu uint64, tun io.Reader, deviceConn io.Writer) error { packet := make([]byte, mtu) for { + select { case <-ctx.Done(): return nil default: - n, err := r.Read(packet) + + n, err := tun.Read(packet) + if err != nil { return fmt.Errorf("could not read packet. %w", err) } - _, err = conn.Write(packet[:n]) + + _, err = deviceConn.Write(packet[:n]) if err != nil { return fmt.Errorf("could not write packet. %w", err) } } + } } -func forwardTCPToInterface(ctx context.Context, conn io.Reader, w io.Writer) error { +func forwardTCPToInterface(ctx context.Context, deviceConn io.Reader, tun io.Writer) error { + b := make([]byte, 20000) for { + select { case <-ctx.Done(): return nil default: - b := make([]byte, 20000) - n, err := conn.Read(b) + n, err := deviceConn.Read(b) if err != nil { return fmt.Errorf("failed to read datagram. %w", err) } - _, err = w.Write(b[:n]) + _, err = tun.Write(b[:n]) if err != nil { return fmt.Errorf("failed to forward data. %w", err) } } + } } diff --git a/ios/tunnel/wireguard.go b/ios/tunnel/wireguard.go index 3fe7c74e..77c3e34c 100644 --- a/ios/tunnel/wireguard.go +++ b/ios/tunnel/wireguard.go @@ -6,13 +6,34 @@ import ( "os/exec" log "github.com/sirupsen/logrus" - "golang.zx2c4.com/wireguard/tun" ) type tunWrapper struct { - device tun.Device - readPackets [][]byte - sizes []int + device Device + + buffer [][]byte +} + +func initTUNwrapper(device Device) *tunWrapper { + t := &tunWrapper{} + + t.device = device + if device.BatchSize() != 1 { + panic("batch size not 1") + } + + mtu, _ := t.device.MTU() + log.Infof("batch size: %d mtu:%d", device.BatchSize(), mtu) + + t.buffer = make([][]byte, 1) + t.buffer[0] = make([]byte, mtu) + go func() { + for { + e := <-device.Events() + log.Infof("event: %v", e) + } + }() + return t } func (t *tunWrapper) Close() error { @@ -20,6 +41,7 @@ func (t *tunWrapper) Close() error { } func (t *tunWrapper) Write(p []byte) (int, error) { + bufs := [][]byte{p} // Create a slice of one byte slice written, err := t.device.Write(bufs, 0) // Use offset 0 if written > 0 { @@ -29,36 +51,25 @@ func (t *tunWrapper) Write(p []byte) (int, error) { } func (t *tunWrapper) Read(p []byte) (int, error) { - if len(t.readPackets) == 0 { - bufs := make([][]byte, 100) - for i := range bufs { - mtu, _ := t.device.MTU() - bufs[i] = make([]byte, mtu) - } - sizes := make([]int, 100) - n, err := t.device.Read(bufs, sizes, 0) - if n == 0 { - return 0, err - } - if n > 1 { - t.readPackets = bufs[1:] - t.sizes = sizes[1:] - } - copy(p, bufs[0][:sizes[0]]) - return sizes[0], err + sizes := make([]int, 1) + _, err := t.device.Read(t.buffer, sizes, 0) + + if err != nil { + return 0, err } - size := t.sizes[0] - copy(p, t.readPackets[0][:size]) - t.readPackets = t.readPackets[1:] - t.sizes = t.sizes[1:] - return size, nil + + buf := t.buffer[0] + size := sizes[0] + copy(p, buf[:size]) + return size, err + } func setupWindowsTUN(tunnelInfo tunnelParameters) (io.ReadWriteCloser, error) { name := "tun0" - tunDevice, err := tun.CreateTUN(name, int(tunnelInfo.ClientParameters.Mtu)) + tunDevice, err := CreateTUN(name, int(tunnelInfo.ClientParameters.Mtu)) if err != nil { fmt.Println("Error creating TUN device:", err) return &tunWrapper{}, err @@ -69,7 +80,7 @@ func setupWindowsTUN(tunnelInfo tunnelParameters) (io.ReadWriteCloser, error) { return nil, fmt.Errorf("setupTunnelInterface: failed to get interface name: %w", err) } const prefixLength = 64 - setIpAddr := exec.Command("netsh", "interface", "ipv6", "add", "address", tunname, fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)) + setIpAddr := exec.Command("netsh", "interface", "ipv6", "set", "address", tunname, fmt.Sprintf("%s/%d", tunnelInfo.ClientParameters.Address, prefixLength)) err = runCmd(setIpAddr) if err != nil { return nil, fmt.Errorf("setupTunnelInterface: failed to set IP address for interface: %w", err) @@ -77,5 +88,5 @@ func setupWindowsTUN(tunnelInfo tunnelParameters) (io.ReadWriteCloser, error) { log.Info("windows cmd") log.Info(setIpAddr.String()) - return &tunWrapper{device: tunDevice, readPackets: [][]byte{}, sizes: []int{}}, nil + return initTUNwrapper(tunDevice), nil } From 696467633ffb130323f0923ebe386181055c0ee9 Mon Sep 17 00:00:00 2001 From: "daniel@windowslaptop.de" Date: Wed, 12 Jun 2024 23:38:26 +0200 Subject: [PATCH 09/14] use wintun directly --- go.mod | 5 +- go.sum | 8 -- ios/tunnel/wg.go | 293 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 295 insertions(+), 11 deletions(-) create mode 100644 ios/tunnel/wg.go diff --git a/go.mod b/go.mod index ad4f623d..4affcde8 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,8 @@ require ( golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db golang.org/x/net v0.26.0 - golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 + golang.org/x/sys v0.21.0 + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 software.sslmate.com/src/go-pkcs12 v0.2.0 ) @@ -39,9 +40,7 @@ require ( go.uber.org/mock v0.3.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4408c032..1ddbeafe 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,6 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -116,8 +114,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -126,8 +122,6 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= -golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -137,8 +131,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= -gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 h1:AQkaJpH+/FmqRjmXZPELom5zIERYZfwTjnHpfoVMQEc= howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= diff --git a/ios/tunnel/wg.go b/ios/tunnel/wg.go new file mode 100644 index 00000000..a9618557 --- /dev/null +++ b/ios/tunnel/wg.go @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. + */ + +package tunnel + +import ( + "errors" + "fmt" + "os" + "sync" + "sync/atomic" + "time" + _ "unsafe" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wintun" +) + +type Event int + +const ( + EventUp = 1 << iota + EventDown + EventMTUUpdate +) + +type Device interface { + // File returns the file descriptor of the device. + File() *os.File + + // Read one or more packets from the Device (without any additional headers). + // On a successful read it returns the number of packets read, and sets + // packet lengths within the sizes slice. len(sizes) must be >= len(bufs). + // A nonzero offset can be used to instruct the Device on where to begin + // reading into each element of the bufs slice. + Read(bufs [][]byte, sizes []int, offset int) (n int, err error) + + // Write one or more packets to the device (without any additional headers). + // On a successful write it returns the number of packets written. A nonzero + // offset can be used to instruct the Device on where to begin writing from + // each packet contained within the bufs slice. + Write(bufs [][]byte, offset int) (int, error) + + // MTU returns the MTU of the Device. + MTU() (int, error) + + // Name returns the current name of the Device. + Name() (string, error) + + // Events returns a channel of type Event, which is fed Device events. + Events() <-chan Event + + // Close stops the Device and closes the Event channel. + Close() error + + // BatchSize returns the preferred/max number of packets that can be read or + // written in a single read/write call. BatchSize must not change over the + // lifetime of a Device. + BatchSize() int +} + +const ( + rateMeasurementGranularity = uint64((time.Second / 2) / time.Nanosecond) + spinloopRateThreshold = 800000000 / 8 // 800mbps + spinloopDuration = uint64(time.Millisecond / 80 / time.Nanosecond) // ~1gbit/s +) + +type rateJuggler struct { + current atomic.Uint64 + nextByteCount atomic.Uint64 + nextStartTime atomic.Int64 + changing atomic.Bool +} + +type NativeTun struct { + wt *wintun.Adapter + name string + handle windows.Handle + rate rateJuggler + session wintun.Session + readWait windows.Handle + events chan Event + running sync.WaitGroup + closeOnce sync.Once + close atomic.Bool + forcedMTU int + outSizes []int +} + +var ( + WintunTunnelType = "WireGuard" + WintunStaticRequestedGUID *windows.GUID +) + +//go:linkname procyield runtime.procyield +func procyield(cycles uint32) + +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +// CreateTUN creates a Wintun interface with the given name. Should a Wintun +// interface with the same name exist, it is reused. +func CreateTUN(ifname string, mtu int) (Device, error) { + return CreateTUNWithRequestedGUID(ifname, WintunStaticRequestedGUID, mtu) +} + +// CreateTUNWithRequestedGUID creates a Wintun interface with the given name and +// a requested GUID. Should a Wintun interface with the same name exist, it is reused. +func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int) (Device, error) { + wt, err := wintun.CreateAdapter(ifname, WintunTunnelType, requestedGUID) + if err != nil { + return nil, fmt.Errorf("Error creating interface: %w", err) + } + + forcedMTU := 1420 + if mtu > 0 { + forcedMTU = mtu + } + + tun := &NativeTun{ + wt: wt, + name: ifname, + handle: windows.InvalidHandle, + events: make(chan Event, 10), + forcedMTU: forcedMTU, + } + + tun.session, err = wt.StartSession(0x800000) // Ring capacity, 8 MiB + if err != nil { + tun.wt.Close() + close(tun.events) + return nil, fmt.Errorf("Error starting session: %w", err) + } + tun.readWait = tun.session.ReadWaitEvent() + return tun, nil +} + +func (tun *NativeTun) Name() (string, error) { + return tun.name, nil +} + +func (tun *NativeTun) File() *os.File { + return nil +} + +func (tun *NativeTun) Events() <-chan Event { + return tun.events +} + +func (tun *NativeTun) Close() error { + var err error + tun.closeOnce.Do(func() { + tun.close.Store(true) + windows.SetEvent(tun.readWait) + tun.running.Wait() + tun.session.End() + if tun.wt != nil { + tun.wt.Close() + } + close(tun.events) + }) + return err +} + +func (tun *NativeTun) MTU() (int, error) { + return tun.forcedMTU, nil +} + +// TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes. +func (tun *NativeTun) ForceMTU(mtu int) { + if tun.close.Load() { + return + } + update := tun.forcedMTU != mtu + tun.forcedMTU = mtu + if update { + tun.events <- EventMTUUpdate + } +} + +func (tun *NativeTun) BatchSize() int { + // TODO: implement batching with wintun + return 1 +} + +// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking. + +func (tun *NativeTun) Read(bufs [][]byte, sizes []int, offset int) (int, error) { + tun.running.Add(1) + defer tun.running.Done() +retry: + if tun.close.Load() { + return 0, os.ErrClosed + } + start := nanotime() + shouldSpin := tun.rate.current.Load() >= spinloopRateThreshold && uint64(start-tun.rate.nextStartTime.Load()) <= rateMeasurementGranularity*2 + for { + if tun.close.Load() { + return 0, os.ErrClosed + } + + packet, err := tun.session.ReceivePacket() + + switch err { + case nil: + + n := copy(bufs[0][offset:], packet) + sizes[0] = n + tun.session.ReleaseReceivePacket(packet) + tun.rate.update(uint64(n)) + if packet[0]>>4 != 6 { + continue + } + return 1, nil + case windows.ERROR_NO_MORE_ITEMS: + + if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration { + windows.WaitForSingleObject(tun.readWait, windows.INFINITE) + + goto retry + } + procyield(1) + + continue + case windows.ERROR_HANDLE_EOF: + return 0, os.ErrClosed + case windows.ERROR_INVALID_DATA: + return 0, errors.New("Send ring corrupt") + } + return 0, fmt.Errorf("Read failed: %w", err) + } +} + +func (tun *NativeTun) Write(bufs [][]byte, offset int) (int, error) { + tun.running.Add(1) + defer tun.running.Done() + if tun.close.Load() { + return 0, os.ErrClosed + } + + for i, buf := range bufs { + packetSize := len(buf) - offset + tun.rate.update(uint64(packetSize)) + + packet, err := tun.session.AllocateSendPacket(packetSize) + switch err { + case nil: + // TODO: Explore options to eliminate this copy. + copy(packet, buf[offset:]) + tun.session.SendPacket(packet) + continue + case windows.ERROR_HANDLE_EOF: + return i, os.ErrClosed + case windows.ERROR_BUFFER_OVERFLOW: + continue // Dropping when ring is full. + default: + return i, fmt.Errorf("Write failed: %w", err) + } + } + return len(bufs), nil +} + +// LUID returns Windows interface instance ID. +func (tun *NativeTun) LUID() uint64 { + tun.running.Add(1) + defer tun.running.Done() + if tun.close.Load() { + return 0 + } + return tun.wt.LUID() +} + +// RunningVersion returns the running version of the Wintun driver. +func (tun *NativeTun) RunningVersion() (version uint32, err error) { + return wintun.RunningVersion() +} + +func (rate *rateJuggler) update(packetLen uint64) { + now := nanotime() + total := rate.nextByteCount.Add(packetLen) + period := uint64(now - rate.nextStartTime.Load()) + if period >= rateMeasurementGranularity { + if !rate.changing.CompareAndSwap(false, true) { + return + } + rate.nextStartTime.Store(now) + rate.current.Store(total * uint64(time.Second/time.Nanosecond) / period) + rate.nextByteCount.Store(0) + rate.changing.Store(false) + } +} From f75fdbbb6ca38c180fa59eecd8f6fea6c7061e1f Mon Sep 17 00:00:00 2001 From: "daniel@windowslaptop.de" Date: Wed, 12 Jun 2024 23:40:09 +0200 Subject: [PATCH 10/14] use wintun directly --- ios/tunnel/wg.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/tunnel/wg.go b/ios/tunnel/wg.go index a9618557..557b7b63 100644 --- a/ios/tunnel/wg.go +++ b/ios/tunnel/wg.go @@ -2,7 +2,8 @@ * * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. */ - +// taken from package wireguard-go/tun/tun_windows.go to minimize useless dependencies and transparently drop +// ipv4 packets package tunnel import ( From d9e0b1b76fe84d45fd0127e270dec88b1ba54ee3 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Thu, 13 Jun 2024 21:49:18 +0200 Subject: [PATCH 11/14] rename files, make sure build works on linux and mac --- ios/tunnel/{wg.go => tun_windows.go} | 2 ++ ios/tunnel/tun_windows_shim.go | 9 +++++++++ ios/tunnel/{wireguard.go => tun_windows_wrapper.go} | 2 ++ 3 files changed, 13 insertions(+) rename ios/tunnel/{wg.go => tun_windows.go} (99%) create mode 100644 ios/tunnel/tun_windows_shim.go rename ios/tunnel/{wireguard.go => tun_windows_wrapper.go} (98%) diff --git a/ios/tunnel/wg.go b/ios/tunnel/tun_windows.go similarity index 99% rename from ios/tunnel/wg.go rename to ios/tunnel/tun_windows.go index 557b7b63..03326547 100644 --- a/ios/tunnel/wg.go +++ b/ios/tunnel/tun_windows.go @@ -1,3 +1,5 @@ +//go:build windows + /* SPDX-License-Identifier: MIT * * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. diff --git a/ios/tunnel/tun_windows_shim.go b/ios/tunnel/tun_windows_shim.go new file mode 100644 index 00000000..a22f71d2 --- /dev/null +++ b/ios/tunnel/tun_windows_shim.go @@ -0,0 +1,9 @@ +//go:build !windows + +package tunnel + +import "io" + +func setupWindowsTUN(tunnelInfo tunnelParameters) (io.ReadWriteCloser, error) { + panic("this should never be called, it only exists so the build system can compile the code without errors") +} diff --git a/ios/tunnel/wireguard.go b/ios/tunnel/tun_windows_wrapper.go similarity index 98% rename from ios/tunnel/wireguard.go rename to ios/tunnel/tun_windows_wrapper.go index 77c3e34c..3383f181 100644 --- a/ios/tunnel/wireguard.go +++ b/ios/tunnel/tun_windows_wrapper.go @@ -1,3 +1,5 @@ +//go:build windows + package tunnel import ( From 3dc300e3bf6173e461b54eba6a9bc2b076594f09 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Thu, 13 Jun 2024 23:26:48 +0200 Subject: [PATCH 12/14] add locking to tunnels, add close func, use lockdown tunnel for ios17.4+ --- ios/discover.go | 1 + ios/tunnel/tunnel.go | 30 ++++++++-------------- ios/tunnel/tunnel_api.go | 47 +++++++++++++++++++++++++++-------- ios/tunnel/tunnel_lockdown.go | 15 +++++++---- ios/utils.go | 21 ++++++++++++++++ main.go | 14 +++++++++-- 6 files changed, 90 insertions(+), 38 deletions(-) diff --git a/ios/discover.go b/ios/discover.go index 3ee640bf..02ded159 100644 --- a/ios/discover.go +++ b/ios/discover.go @@ -55,6 +55,7 @@ func checkEntry(ctx context.Context, device DeviceEntry, interfaceName string, e if entry == nil { continue } + print(entry.ServiceInstanceName()) for _, ip6 := range entry.AddrIPv6 { tryHandshake(ip6, entry.Port, interfaceName, device.Properties.SerialNumber, result) } diff --git a/ios/tunnel/tunnel.go b/ios/tunnel/tunnel.go index 7746320b..c4f6a611 100644 --- a/ios/tunnel/tunnel.go +++ b/ios/tunnel/tunnel.go @@ -33,12 +33,12 @@ type Tunnel struct { RsdPort int `json:"rsdPort"` // Udid is the id of the device for this tunnel Udid string `json:"udid"` - closer io.Closer + closer func() error } // Close closes the connection to the device and removes the virtual network interface from the host func (t Tunnel) Close() error { - return t.closer.Close() + return t.closer() } // ManualPairAndConnectToTunnel tries to verify an existing pairing, and if this fails it triggers a new manual pairing process. @@ -153,15 +153,18 @@ func connectToTunnel(ctx context.Context, info tunnelListener, addr string, devi } }() + closeFunc := func() error { + cancel() + quicErr := conn.CloseWithError(0, "") + utunErr := utunIface.Close() + return errors.Join(quicErr, utunErr) + } + return Tunnel{ Address: tunnelInfo.ServerAddress, RsdPort: int(tunnelInfo.ServerRSDPort), Udid: device.Properties.SerialNumber, - closer: tunnelCloser{ - quicConn: conn, - utunCloser: utunIface, - ctxCancel: cancel, - }, + closer: closeFunc, }, nil } @@ -330,16 +333,3 @@ func exchangeCoreTunnelParameters(stream io.ReadWriteCloser) (tunnelParameters, } return parameters, nil } - -type tunnelCloser struct { - quicConn quic.Connection - utunCloser io.Closer - ctxCancel context.CancelFunc -} - -func (t tunnelCloser) Close() error { - t.ctxCancel() - quicErr := t.quicConn.CloseWithError(0, "") - utunErr := t.utunCloser.Close() - return errors.Join(quicErr, utunErr) -} diff --git a/ios/tunnel/tunnel_api.go b/ios/tunnel/tunnel_api.go index c3c718f1..b9423378 100644 --- a/ios/tunnel/tunnel_api.go +++ b/ios/tunnel/tunnel_api.go @@ -7,8 +7,10 @@ import ( "io" "net/http" "strings" + "sync" "time" + "github.com/Masterminds/semver" "github.com/danielpaulus/go-ios/ios" log "github.com/sirupsen/logrus" "golang.org/x/exp/maps" @@ -121,10 +123,10 @@ func ListRunningTunnels(tunnelInfoPort int) ([]Tunnel, error) { // TunnelManager starts tunnels for devices when needed (if no tunnel is running yet) and stores the information // how those tunnels are reachable (address and remote service discovery port) type TunnelManager struct { - ts tunnelStarter - dl deviceLister - pm PairRecordManager - + ts tunnelStarter + dl deviceLister + pm PairRecordManager + mux sync.Mutex tunnels map[string]Tunnel startTunnelTimeout time.Duration } @@ -143,15 +145,22 @@ func NewTunnelManager(pm PairRecordManager) *TunnelManager { // UpdateTunnels checks for connected devices and starts a new tunnel if needed // On device disconnects the tunnel resources get cleaned up func (m *TunnelManager) UpdateTunnels(ctx context.Context) error { + + m.mux.Lock() + localTunnels := map[string]Tunnel{} + maps.Copy(localTunnels, m.tunnels) + m.mux.Unlock() + devices, err := m.dl.ListDevices() if err != nil { return fmt.Errorf("UpdateTunnels: failed to get list of devices: %w", err) } for _, d := range devices.DeviceList { udid := d.Properties.SerialNumber - if _, exists := m.tunnels[udid]; exists { + if _, exists := localTunnels[udid]; exists { continue } + t, err := m.startTunnel(ctx, d) if err != nil { log.WithField("udid", udid). @@ -159,9 +168,12 @@ func (m *TunnelManager) UpdateTunnels(ctx context.Context) error { Warn("failed to start tunnel") continue } + m.mux.Lock() + localTunnels[udid] = t m.tunnels[udid] = t + m.mux.Unlock() } - for udid, tun := range m.tunnels { + for udid, tun := range localTunnels { idx := slices.ContainsFunc(devices.DeviceList, func(entry ios.DeviceEntry) bool { return entry.Properties.SerialNumber == udid }) @@ -173,6 +185,8 @@ func (m *TunnelManager) UpdateTunnels(ctx context.Context) error { } func (m *TunnelManager) stopTunnel(t Tunnel) error { + m.mux.Lock() + defer m.mux.Unlock() log.WithField("udid", t.Udid).Info("stopping tunnel") delete(m.tunnels, t.Udid) @@ -183,7 +197,11 @@ func (m *TunnelManager) startTunnel(ctx context.Context, device ios.DeviceEntry) log.WithField("udid", device.Properties.SerialNumber).Info("start tunnel") startTunnelCtx, cancel := context.WithTimeout(ctx, m.startTunnelTimeout) defer cancel() - t, err := m.ts.StartTunnel(startTunnelCtx, device, m.pm) + version, err := ios.GetProductVersion(device) + if err != nil { + return Tunnel{}, fmt.Errorf("startTunnel: failed to get device version: %w", err) + } + t, err := m.ts.StartTunnel(startTunnelCtx, device, m.pm, version) if err != nil { return Tunnel{}, err } @@ -192,6 +210,8 @@ func (m *TunnelManager) startTunnel(ctx context.Context, device ios.DeviceEntry) // ListTunnels provides all currently running device tunnels func (m *TunnelManager) ListTunnels() ([]Tunnel, error) { + m.mux.Lock() + defer m.mux.Unlock() return maps.Values(m.tunnels), nil } @@ -211,7 +231,7 @@ func (m *TunnelManager) FindTunnel(udid string) (Tunnel, error) { } type tunnelStarter interface { - StartTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager) (Tunnel, error) + StartTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager, version *semver.Version) (Tunnel, error) } type deviceLister interface { @@ -221,9 +241,14 @@ type deviceLister interface { type manualPairingTunnelStart struct { } -func (m manualPairingTunnelStart) StartTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager) (Tunnel, error) { - return ConnectTunnelLockdown(device) - //return ManualPairAndConnectToTunnel(ctx, device, p) +func (m manualPairingTunnelStart) StartTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager, version *semver.Version) (Tunnel, error) { + if version.Major() >= 17 && version.Minor() >= 4 { + return ConnectTunnelLockdown(device) + } + if version.Major() >= 17 { + return ManualPairAndConnectToTunnel(ctx, device, p) + } + return Tunnel{}, fmt.Errorf("manualPairingTunnelStart: unsupported iOS version %s", version.String()) } type deviceList struct { diff --git a/ios/tunnel/tunnel_lockdown.go b/ios/tunnel/tunnel_lockdown.go index 73a4702b..75d5bf75 100644 --- a/ios/tunnel/tunnel_lockdown.go +++ b/ios/tunnel/tunnel_lockdown.go @@ -2,6 +2,7 @@ package tunnel import ( "context" + "errors" "fmt" "io" @@ -16,11 +17,11 @@ func ConnectTunnelLockdown(device ios.DeviceEntry) (Tunnel, error) { if err != nil { return Tunnel{}, err } - return connectToTunnelLockdown(context.TODO(), tunnelListener{}, "", device, conn) + return connectToTunnelLockdown(context.TODO(), device, conn) } -func connectToTunnelLockdown(ctx context.Context, info tunnelListener, addr string, device ios.DeviceEntry, connToDevice io.ReadWriteCloser) (Tunnel, error) { - logrus.WithField("address", addr).WithField("port", info.TunnelPort).Info("connect to tunnel endpoint on device") +func connectToTunnelLockdown(ctx context.Context, device ios.DeviceEntry, connToDevice io.ReadWriteCloser) (Tunnel, error) { + logrus.Info("connect to lockdown tunnel endpoint on device") tunnelInfo, err := exchangeCoreTunnelParameters(connToDevice) if err != nil { @@ -34,7 +35,7 @@ func connectToTunnelLockdown(ctx context.Context, info tunnelListener, addr stri // we want a copy of the parent ctx here, but it shouldn't time out/be cancelled at the same time. // doing it like this allows us to have a context with a timeout for the tunnel creation, but the tunnel itself - tunnelCtx, _ := context.WithCancel(context.WithoutCancel(ctx)) + tunnelCtx, cancel := context.WithCancel(context.WithoutCancel(ctx)) go func() { err := forwardTCPToInterface(tunnelCtx, connToDevice, utunIface) @@ -50,11 +51,15 @@ func connectToTunnelLockdown(ctx context.Context, info tunnelListener, addr stri } }() + closeFunc := func() error { + cancel() + return errors.Join(utunIface.Close(), connToDevice.Close()) + } return Tunnel{ Address: tunnelInfo.ServerAddress, RsdPort: int(tunnelInfo.ServerRSDPort), Udid: device.Properties.SerialNumber, - closer: nil, + closer: closeFunc, }, nil } diff --git a/ios/utils.go b/ios/utils.go index d329392e..b8cdb1c2 100755 --- a/ios/utils.go +++ b/ios/utils.go @@ -4,7 +4,9 @@ import ( "encoding/binary" "errors" "fmt" + "log/slog" "os" + "runtime" "strings" "github.com/Masterminds/semver" @@ -13,6 +15,25 @@ import ( plist "howett.net/plist" ) +// CheckRoot checks if the current user is root or has elevated privileges on Windows. +func CheckRoot() error { + // On Windows, check if the process has elevated privileges + if runtime.GOOS == "windows" { + slog.Info("This program needs elevated privileges. Make sure you are in an administrator shell.") + _, err := os.Open("\\\\.\\PHYSICALDRIVE0") + if err != nil { + return fmt.Errorf("this program needs elevated privileges. Run as administrator.") + } + } else { + // Non-Windows platforms, check if the user is root + u := os.Geteuid() + if u != 0 { + return fmt.Errorf("this program needs root privileges. Run with sudo.") + } + } + return nil +} + // ToPlist converts a given struct to a Plist using the // github.com/DHowett/go-plist library. Make sure your struct is exported. // It returns a string containing the plist. diff --git a/main.go b/main.go index d2858883..b2382972 100644 --- a/main.go +++ b/main.go @@ -240,7 +240,8 @@ The commands work as following: ios diskspace [options] Prints disk space info. ios batterycheck [options] Prints battery info. ios tunnel start [options] [--pair-record-path=] Creates a tunnel connection to the device. If the device was not paired with the host yet, device pairing will also be executed. - > On systems with System Integrity Protection enabled the argument '--pair-record-path' is required as we can not access the default path for the pair record + > On systems with System Integrity Protection enabled the argument '--pair-record-path=default' can be used to point to /var/db/lockdown/RemotePairing/user_501. + > If nothing is specified, the current dir is used for the pair record. > This command needs to be executed with admin privileges. > (On MacOS the process 'remoted' must be paused before starting a tunnel is possible 'sudo pkill -SIGSTOP remoted', and 'sudo pkill -SIGCONT remoted' to resume) ios tunnel ls List currently started tunnels @@ -1008,10 +1009,19 @@ The commands work as following: if tunnelCommand { startCommand, _ := arguments.Bool("start") + + if startCommand { + err := ios.CheckRoot() + exitIfError("Run this with 'sudo' or in as admin on windows", err) + } + listCommand, _ := arguments.Bool("ls") if startCommand { pairRecordsPath, _ := arguments.String("--pair-record-path") if len(pairRecordsPath) == 0 { + pairRecordsPath = "." + } + if strings.ToLower(pairRecordsPath) == "default" { pairRecordsPath = "/var/db/lockdown/RemotePairing/user_501" } startTunnel(context.TODO(), pairRecordsPath, tunnelInfoPort) @@ -2016,7 +2026,7 @@ func startTunnel(ctx context.Context, recordsPath string, tunnelInfoPort int) { exitIfError("failed to start tunnel server", err) } }() - + log.Info("Tunnel server started") <-ctx.Done() } From 4c3986b4e5f8cd21dc14f66d25a67cd1062f5a00 Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Thu, 13 Jun 2024 23:29:22 +0200 Subject: [PATCH 13/14] add log statement --- ios/utils.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/ios/utils.go b/ios/utils.go index b8cdb1c2..846e3921 100755 --- a/ios/utils.go +++ b/ios/utils.go @@ -4,7 +4,6 @@ import ( "encoding/binary" "errors" "fmt" - "log/slog" "os" "runtime" "strings" @@ -19,7 +18,6 @@ import ( func CheckRoot() error { // On Windows, check if the process has elevated privileges if runtime.GOOS == "windows" { - slog.Info("This program needs elevated privileges. Make sure you are in an administrator shell.") _, err := os.Open("\\\\.\\PHYSICALDRIVE0") if err != nil { return fmt.Errorf("this program needs elevated privileges. Run as administrator.") From 7c80be4bb8ff17d1140a70ef0442733eef95b0ff Mon Sep 17 00:00:00 2001 From: Daniel Paulus Date: Mon, 24 Jun 2024 20:50:20 +0200 Subject: [PATCH 14/14] wip, support for remotepairingd --- go.work.sum | 6 +- ios/connect.go | 49 ++++++ ios/discover.go | 109 +++++++++++-- ios/discover_test.go | 28 ++++ ios/tunnel/codec.go | 37 +++-- ios/tunnel/remotepairing.go | 315 ++++++++++++++++++++++++++++++++++++ ios/tunnel/tunnel.go | 4 +- ios/tunnel/tunnel_api.go | 3 + ios/tunnel/untrusted.go | 134 ++++++++++----- 9 files changed, 612 insertions(+), 73 deletions(-) create mode 100644 ios/discover_test.go create mode 100644 ios/tunnel/remotepairing.go diff --git a/go.work.sum b/go.work.sum index 24598935..df049953 100644 --- a/go.work.sum +++ b/go.work.sum @@ -4,8 +4,6 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -19,6 +17,7 @@ github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCL github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= @@ -27,9 +26,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= @@ -38,4 +35,3 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= -software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= diff --git a/ios/connect.go b/ios/connect.go index 1c6abab8..9e64927c 100755 --- a/ios/connect.go +++ b/ios/connect.go @@ -2,6 +2,7 @@ package ios import ( "fmt" + "io" "net" "time" @@ -333,3 +334,51 @@ func initializeXpcConnection(h *http.HttpConnection) error { return nil } + +func initializeXpcConnection2(c io.ReadWriteCloser) error { + csWriter := c + ssWriter := c + err := xpc.EncodeMessage(csWriter, xpc.Message{ + Flags: xpc.AlwaysSetFlag, + Body: map[string]interface{}{}, + Id: 0, + }) + if err != nil { + return fmt.Errorf("initializeXpcConnection: failed to encode message: %w", err) + } + + _, err = xpc.DecodeMessage(csWriter) // TODO : figure out if need to act on this frame + if err != nil { + return fmt.Errorf("initializeXpcConnection: failed to decode message: %w", err) + } + + err = xpc.EncodeMessage(ssWriter, xpc.Message{ + Flags: xpc.InitHandshakeFlag | xpc.AlwaysSetFlag, + Body: nil, + Id: 0, + }) + if err != nil { + return fmt.Errorf("initializeXpcConnection: failed to encode message 2: %w", err) + } + + _, err = xpc.DecodeMessage(ssWriter) // TODO : figure out if need to act on this frame + if err != nil { + return fmt.Errorf("initializeXpcConnection: failed to decode message 2: %w", err) + } + + err = xpc.EncodeMessage(csWriter, xpc.Message{ + Flags: 0x201, // alwaysSetFlag | 0x200 + Body: nil, + Id: 0, + }) + if err != nil { + return fmt.Errorf("initializeXpcConnection: failed to encode message 3: %w", err) + } + + _, err = xpc.DecodeMessage(csWriter) // TODO : figure out if need to act on this frame + if err != nil { + return fmt.Errorf("initializeXpcConnection: failed to decode message 3: %w", err) + } + + return nil +} diff --git a/ios/discover.go b/ios/discover.go index 02ded159..8ccbe324 100644 --- a/ios/discover.go +++ b/ios/discover.go @@ -3,13 +3,26 @@ package ios import ( "context" "fmt" - "log/slog" "net" + "sync" + "time" "github.com/grandcat/zeroconf" log "github.com/sirupsen/logrus" ) +type SDResponse struct { + HandshakeResponse RsdHandshakeResponse + InterfaceName string + ServiceEntry *zeroconf.ServiceEntry + Ipv6 net.IP + Err error +} + +func (r SDResponse) Addr() string { + return fmt.Sprintf("%s%%%s", r.Ipv6.String(), r.InterfaceName) +} + // FindDeviceInterfaceAddress tries to find the address of the device by browsing through all network interfaces. // It uses mDNS to discover the "_remoted._tcp" service on the local. domain. Then tries to connect to the RemoteServiceDiscovery // and checks if the udid of the device matches the udid of the device we are looking for. @@ -19,7 +32,7 @@ func FindDeviceInterfaceAddress(ctx context.Context, device DeviceEntry) (string return "", fmt.Errorf("FindDeviceInterfaceAddress: failed to get network interfaces: %w", err) } - result := make(chan string) + result := make(chan SDResponse) defer close(result) for _, iface := range ifaces { @@ -32,7 +45,9 @@ func FindDeviceInterfaceAddress(ctx context.Context, device DeviceEntry) (string } entries := make(chan *zeroconf.ServiceEntry) resolver.Browse(ctx, "_remoted._tcp", "local.", entries) - go checkEntry(ctx, device, iface.Name, entries, result) + wg := &sync.WaitGroup{} + wg.Add(1) + go checkEntry(ctx, iface.Name, entries, result, wg) } @@ -41,42 +56,108 @@ func FindDeviceInterfaceAddress(ctx context.Context, device DeviceEntry) (string return "", ctx.Err() case r := <-result: log.WithField("udid", device.Properties.SerialNumber).WithField("address", r).Debug("found device address") - return r, nil + addr := fmt.Sprintf("%s%%%s", r.Ipv6.String(), r.InterfaceName) + return addr, nil + } +} + +const RemoteDLocal = "_remoted._tcp" + +func FindDevicesForService(ctx context.Context, service string) ([]SDResponse, error) { + ifaces, err := net.Interfaces() + if err != nil { + return nil, fmt.Errorf("FindDeviceInterfaceAddress: failed to get network interfaces: %w", err) + } + + result := make(chan SDResponse) + defer close(result) + wg := sync.WaitGroup{} + for _, iface := range ifaces { + resolver, err := zeroconf.NewResolver(zeroconf.SelectIfaces([]net.Interface{iface}), zeroconf.SelectIPTraffic(zeroconf.IPv6)) + if err != nil { + log.WithField("interface", iface.Name). + WithField("err", err). + Debug("failed to initialize resolver") + continue + } + entries := make(chan *zeroconf.ServiceEntry) + resolver.Browse(ctx, service, "local.", entries) + wg.Add(1) + go checkEntry(ctx, iface.Name, entries, result, &wg) + } + results := []SDResponse{} + go func() { + for { + select { + case <-ctx.Done(): + return + case r, ok := <-result: + if !ok { + return + } + results = append(results, r) + //log.WithField("udid", device.Properties.SerialNumber).WithField("address", r).Debug("found device address") + + } + } + }() + + wg.Wait() + return results, nil } // checkEntry connects to all remote service discoveries and tests which one belongs to this device' udid. -func checkEntry(ctx context.Context, device DeviceEntry, interfaceName string, entries chan *zeroconf.ServiceEntry, result chan<- string) { +func checkEntry(ctx context.Context, interfaceName string, entries chan *zeroconf.ServiceEntry, result chan<- SDResponse, wg *sync.WaitGroup) { + defer wg.Done() for { select { + case <-time.After(time.Second): + return case <-ctx.Done(): return case entry := <-entries: if entry == nil { continue } - print(entry.ServiceInstanceName()) for _, ip6 := range entry.AddrIPv6 { - tryHandshake(ip6, entry.Port, interfaceName, device.Properties.SerialNumber, result) + tryHandshake(ip6, entry, interfaceName, result, wg) } } } + } -func tryHandshake(ip6 net.IP, port int, interfaceName, udid string, result chan<- string) { - addr := fmt.Sprintf("%s%%%s", ip6.String(), interfaceName) - s, err := NewWithAddrPort(addr, port) +var NewTCP func(string, int) (RsdService, error) +func tryHandshake(ip6 net.IP, svcEntry *zeroconf.ServiceEntry, interfaceName string, result chan<- SDResponse, wg *sync.WaitGroup) { + wg.Add(1) + defer wg.Done() + resp := SDResponse{ + Err: nil, + InterfaceName: interfaceName, + Ipv6: ip6, + ServiceEntry: svcEntry, + } + addr := fmt.Sprintf("%s%%%s", ip6.String(), interfaceName) + port := svcEntry.Port + ff := NewTCP + if ff == nil { + ff = NewWithAddrPort + } + s, err := ff(addr, port) if err != nil { - slog.Error("failed to connect to remote service discovery", "error", err, "address", addr) + resp.Err = err + result <- resp return } defer s.Close() h, err := s.Handshake() if err != nil { + resp.Err = err + result <- resp return } - if udid == h.Udid { - result <- addr - } + resp.HandshakeResponse = h + result <- resp } diff --git a/ios/discover_test.go b/ios/discover_test.go new file mode 100644 index 00000000..9d5ee389 --- /dev/null +++ b/ios/discover_test.go @@ -0,0 +1,28 @@ +package ios_test + +import ( + "context" + "log" + "testing" + + "github.com/danielpaulus/go-ios/ios" + "github.com/danielpaulus/go-ios/ios/tunnel" +) + +func TestDiscover(t *testing.T) { + + const amod = "_apple-mobdev2._tcp" + const r = "_remotepairing._tcp" + const rmp = "_remotepairing-manual-pairing._tcp.local." + var c = func(a string, p int) (ios.RsdService, error) { + return tunnel.NewTCP(a, p) + } + ios.NewTCP = c + rsp, err := ios.FindDevicesForService(context.Background(), r) + if err != nil { + t.Fatal(err) + } + //d := rsp[0] + + log.Fatalf("%v", rsp) +} diff --git a/ios/tunnel/codec.go b/ios/tunnel/codec.go index c821983a..7e43d3b4 100644 --- a/ios/tunnel/codec.go +++ b/ios/tunnel/codec.go @@ -2,6 +2,7 @@ package tunnel import ( "crypto/cipher" + "encoding/base64" "encoding/binary" "encoding/json" "fmt" @@ -40,6 +41,12 @@ func (p *pairingData) Decode(e map[string]interface{}) error { if data, ok := pd["data"].([]byte); ok { p.data = data } + if data, ok := pd["data"].(string); ok { + p.data, err = base64.StdEncoding.DecodeString(data) + if err != nil { + return fmt.Errorf("Decode: failed to decode base64 data: %w", err) + } + } if kind, ok := pd["kind"].(string); ok { p.kind = kind } @@ -184,16 +191,16 @@ func (c *controlChannelReadWriter) read() (map[string]interface{}, error) { // the host. This message pair uses the same nonce before that counter is increased for the next message from the host // to the device type cipherStream struct { - controlChannel *controlChannelReadWriter + controlChannel pairingService clientCipher cipher.AEAD serverCipher cipher.AEAD nonce []byte sequence uint64 } -func newCipherStream(controlChannel *controlChannelReadWriter, clientCipher, serverCipher cipher.AEAD) *cipherStream { +func newCipherStream(p pairingService, clientCipher, serverCipher cipher.AEAD) *cipherStream { return &cipherStream{ - controlChannel: controlChannel, + controlChannel: p, clientCipher: clientCipher, serverCipher: serverCipher, nonce: make([]byte, clientCipher.NonceSize()), @@ -209,7 +216,7 @@ func (c *cipherStream) write(p map[string]interface{}) error { } encrypted := c.clientCipher.Seal(nil, c.nonce, marshalled, nil) c.sequence += 1 - return c.controlChannel.write(map[string]interface{}{ + return c.controlChannel.writeEncrypted(map[string]interface{}{ "streamEncrypted": map[string]interface{}{ "_0": encrypted, }, @@ -217,18 +224,30 @@ func (c *cipherStream) write(p map[string]interface{}) error { } func (c *cipherStream) read(p *map[string]interface{}) error { - m, err := c.controlChannel.read() + m, err := c.controlChannel.readEncrypted() if err != nil { return err } if streamEncr, err := getChildMap(m, "streamEncrypted"); err == nil { - if cip, ok := streamEncr["_0"].([]byte); ok { - plain, err := c.serverCipher.Open(nil, c.nonce, cip, nil) + var cip []byte + var ok bool + if cip, ok = streamEncr["_0"].([]byte); !ok { + cips, ok := streamEncr["_0"].(string) + if !ok { + return fmt.Errorf("read: failed to get encrypted payload") + } + cip, err = base64.StdEncoding.DecodeString(cips) if err != nil { - return err + return fmt.Errorf("read: failed to decode base64 data: %w", err) } - return json.Unmarshal(plain, p) } + + plain, err := c.serverCipher.Open(nil, c.nonce, cip, nil) + if err != nil { + return err + } + return json.Unmarshal(plain, p) + } return fmt.Errorf("not implemented") } diff --git a/ios/tunnel/remotepairing.go b/ios/tunnel/remotepairing.go new file mode 100644 index 00000000..0d7c3a04 --- /dev/null +++ b/ios/tunnel/remotepairing.go @@ -0,0 +1,315 @@ +package tunnel + +import ( + "context" + "crypto/sha512" + "crypto/tls" + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "io" + "log/slog" + "net" + "strings" + "sync" + "time" + + "github.com/danielpaulus/go-ios/ios" + "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/hkdf" +) + +type RemoteTunnelService struct { + pairRecords PairRecordManager + connSequence *ConnSequence + cipher *cipherStream +} + +func (r *RemoteTunnelService) readEncrypted() (map[string]interface{}, error) { + m, err := receiveRemotePair(r.connSequence.Conn) + if err != nil { + return nil, fmt.Errorf("readEncrypted: failed to read message: %w", err) + } + return getChildMap(m, "message") +} + +func (r *RemoteTunnelService) writeEncrypted(data map[string]interface{}) error { + return sendRemotePair(data, r.connSequence) +} + +func (r *RemoteTunnelService) readResponse() (map[string]interface{}, error) { + return receiveResponse(r.connSequence.Conn) +} + +func (r *RemoteTunnelService) getCipher() *cipherStream { + return r.cipher +} + +func (r *RemoteTunnelService) writeEvent(event eventCodec) error { + return writeEvent(event, r.connSequence) +} + +func (r *RemoteTunnelService) readEvent(event eventCodec) error { + return readEvent(event, r.connSequence) +} + +func (r *RemoteTunnelService) setupCiphers(sessionKey []byte) error { + clientKey := make([]byte, 32) + _, err := hkdf.New(sha512.New, sessionKey, nil, []byte("ClientEncrypt-main")).Read(clientKey) + if err != nil { + return err + } + serverKey := make([]byte, 32) + _, err = hkdf.New(sha512.New, sessionKey, nil, []byte("ServerEncrypt-main")).Read(serverKey) + if err != nil { + return err + } + server, err := chacha20poly1305.New(serverKey) + if err != nil { + return err + } + client, err := chacha20poly1305.New(clientKey) + if err != nil { + return err + } + + r.cipher = newCipherStream(r, client, server) + + return nil +} + +func (t *RemoteTunnelService) writeRequest(req map[string]interface{}) error { + return writeRequest(req, t.connSequence) +} + +func (r *RemoteTunnelService) getPairRecords() PairRecordManager { + return r.pairRecords +} + +type ConnSequence struct { + Conn io.ReadWriteCloser + Sequence uint64 + mux sync.Mutex +} + +func (c *ConnSequence) GetAndIncrement() uint64 { + c.mux.Lock() + defer c.mux.Unlock() + c.Sequence++ + return c.Sequence +} + +var remotePairingMagic = []byte("RPPairing") + +func Verify(conn *ConnSequence) error { + data := map[string]interface{}{ + "handshake": map[string]interface{}{ + "_0": map[string]interface{}{ + "hostOptions": map[string]interface{}{ + "attemptPairVerify": true, + }, + "wireProtocolVersion": int64(19), + }, + }, + } + err := writeRequest(data, conn) + if err != nil { + return fmt.Errorf("could not send remote pairing handshake: %w", err) + } + //data, err = receiveRemotePair(conn.Conn) + //data, err = extractResponse(data) + data, _ = receiveResponse(conn.Conn) + print(data) + return nil +} + +func receiveResponse(conn io.Reader) (map[string]interface{}, error) { + data, err := receiveRemotePair(conn) + if err != nil { + return nil, fmt.Errorf("could not receive remote pairing response: %w", err) + } + return extractResponse(data) +} + +func extractResponse(data map[string]interface{}) (map[string]interface{}, error) { + message, err := getChildMap(data, "message") + if err != nil { + return nil, fmt.Errorf("could not extract message: %w", err) + } + message, err = getChildMap(message, "plain") + if err != nil { + return nil, fmt.Errorf("could not extract message: %w", err) + } + message, err = getChildMap(message, "_0") + if err != nil { + return nil, fmt.Errorf("could not extract message: %w", err) + } + + return message, nil +} + +func writeEvent(e eventCodec, conn *ConnSequence) error { + encoded := map[string]interface{}{ + "plain": map[string]interface{}{ + "_0": map[string]interface{}{ + "event": map[string]interface{}{ + "_0": e.Encode(), + }, + }, + }, + } + return sendRemotePair(encoded, conn) +} + +func readEvent(e eventCodec, conn *ConnSequence) error { + m, err := receiveRemotePair(conn.Conn) + if err != nil { + return fmt.Errorf("readEvent: failed to read message: %w", err) + } + event, err := getChildMap(m, "message", "plain", "_0", "event", "_0") + if err != nil { + return fmt.Errorf("readEvent: failed to get event payload: %w", err) + } + return e.Decode(event) +} + +func writeRequest(req map[string]interface{}, conn *ConnSequence) error { + err := sendRemotePair(map[string]interface{}{ + "plain": map[string]interface{}{ + "_0": map[string]interface{}{ + "request": map[string]interface{}{ + "_0": req, + }, + }, + }, + }, conn) + if err != nil { + return fmt.Errorf("writeRequest: failed to write message: %w", err) + } + return nil +} + +func sendRemotePair(data map[string]interface{}, conn *ConnSequence) error { + d := map[string]interface{}{ + "message": data, + "originatedBy": "host", + "sequenceNumber": conn.GetAndIncrement(), + } + b, err := json.Marshal(d) + print(string(b) + "\n") + if err != nil { + return fmt.Errorf("could not marshal remote pairing data: %w", err) + } + + _, err = conn.Conn.Write(remotePairingMagic) + l := make([]byte, 2) + binary.BigEndian.PutUint16(l, uint16(len(b))) + _, err2 := conn.Conn.Write(l) + _, err3 := conn.Conn.Write(b) + return errors.Join(err, err2, err3) +} + +func receiveRemotePair(conn io.Reader) (map[string]interface{}, error) { + magic := make([]byte, 9) + _, err := conn.Read(magic) + if err != nil { + return nil, fmt.Errorf("could not read remote pairing magic: %w", err) + } + if string(magic) != string(remotePairingMagic) { + return nil, fmt.Errorf("invalid remote pairing magic: %s", magic) + } + + var length uint16 + err = binary.Read(conn, binary.BigEndian, &length) + if err != nil { + return nil, fmt.Errorf("could not read remote pairing length: %w", err) + } + + data := make([]byte, length) + _, err = conn.Read(data) + if err != nil { + return nil, fmt.Errorf("could not read remote pairing data: %w", err) + } + + var decoded map[string]interface{} + err = json.Unmarshal(data, &decoded) + if err != nil { + return nil, fmt.Errorf("could not unmarshal remote pairing data: %w", err) + } + + return decoded, nil +} + +func ConnectRemotePairingTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager) (Tunnel, error) { + const amod = "_apple-mobdev2._tcp" + const r = "_remotepairing._tcp" + const rmp = "_remotepairing-manual-pairing._tcp.local." + var c = func(a string, port int) (ios.RsdService, error) { + return NewTCP(a, port, p) + } + ios.NewTCP = c + _, err := ios.FindDevicesForService(context.Background(), r) + if err != nil { + return Tunnel{}, err + } + time.Sleep(15 * time.Second) + + if err != nil { + return Tunnel{}, fmt.Errorf("ManualPairAndConnectToTunnel: failed to create tunnel listener: %w", err) + } + + return Tunnel{}, nil +} + +func NewTCP(addr1 string, port1 int, p PairRecordManager) (ios.RsdService, error) { + addr, err := net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", addr1, port1)) + if err != nil { + return ios.RsdService{}, fmt.Errorf("ConnectToHttp2WithAddr: failed to resolve address: %w", err) + } + + /* + ctx = SSLPSKContext(ssl.PROTOCOL_TLSv1_2) + ctx.psk = self.encryption_key + ctx.set_ciphers('PSK') + */ + conn, err := net.DialTCP("tcp", nil, addr) + if err != nil { + return ios.RsdService{}, fmt.Errorf("ConnectToHttp2WithAddr: failed to dial: %w", err) + } + + err = conn.SetKeepAlive(true) + if err != nil { + return ios.RsdService{}, fmt.Errorf("ConnectToHttp2WithAddr: failed to set keepalive: %w", err) + } + err = conn.SetKeepAlivePeriod(1 * time.Second) + if err != nil { + return ios.RsdService{}, fmt.Errorf("ConnectToHttp2WithAddr: failed to set keepalive period: %w", err) + } + c := ConnSequence{ + Conn: conn, + mux: sync.Mutex{}, + } + //Verify(&c) + remoteTunnelService := RemoteTunnelService{ + pairRecords: p, + connSequence: &c, + } + err = ManualPair(&remoteTunnelService) + if err != nil { + slog.Error("failed to verify pair", "err", err) + return ios.RsdService{}, fmt.Errorf("ConnectToHttp2WithAddr: failed to verify pair: %w", err) + } + tunnelInfo, err := createTunnelListener(&remoteTunnelService, "tcp") + conf, err := createTlsConfig(tunnelInfo) + if err != nil { + return ios.RsdService{}, err + } + addr1 = strings.Replace(addr1, "%en12", "%en0", -1) + cs, err := net.Dial("tcp", fmt.Sprintf("[%s]:%d", addr1, tunnelInfo.TunnelPort)) + tlsconn, err := tls.Dial("tcp", fmt.Sprintf("[%s]:%d", addr1, tunnelInfo.TunnelPort), conf) + tun, err := connectToTunnelLockdown(context.Background(), ios.DeviceEntry{Properties: ios.DeviceProperties{SerialNumber: "d"}}, tlsconn) + + slog.Info("sdf", tun, cs) + return ios.RsdService{}, nil +} diff --git a/ios/tunnel/tunnel.go b/ios/tunnel/tunnel.go index c4f6a611..5bac8697 100644 --- a/ios/tunnel/tunnel.go +++ b/ios/tunnel/tunnel.go @@ -65,11 +65,11 @@ func ManualPairAndConnectToTunnel(ctx context.Context, device ios.DeviceEntry, p } ts := newTunnelServiceWithXpc(xpcConn, h, p) - err = ts.ManualPair() + err = ManualPair(ts) if err != nil { return Tunnel{}, fmt.Errorf("ManualPairAndConnectToTunnel: failed to pair device: %w", err) } - tunnelInfo, err := ts.createTunnelListener() + tunnelInfo, err := createTunnelListener(ts, "quic") if err != nil { return Tunnel{}, fmt.Errorf("ManualPairAndConnectToTunnel: failed to create tunnel listener: %w", err) } diff --git a/ios/tunnel/tunnel_api.go b/ios/tunnel/tunnel_api.go index b9423378..e119b85d 100644 --- a/ios/tunnel/tunnel_api.go +++ b/ios/tunnel/tunnel_api.go @@ -242,6 +242,9 @@ type manualPairingTunnelStart struct { } func (m manualPairingTunnelStart) StartTunnel(ctx context.Context, device ios.DeviceEntry, p PairRecordManager, version *semver.Version) (Tunnel, error) { + //return ManualPairAndConnectToTunnel(ctx, device, p) + return ConnectRemotePairingTunnel(ctx, device, p) + if version.Major() >= 17 && version.Minor() >= 4 { return ConnectTunnelLockdown(device) } diff --git a/ios/tunnel/untrusted.go b/ios/tunnel/untrusted.go index c6aada0e..2e9db6bb 100644 --- a/ios/tunnel/untrusted.go +++ b/ios/tunnel/untrusted.go @@ -34,6 +34,19 @@ func newTunnelServiceWithXpc(xpcConn *xpc.Connection, c io.Closer, pairRecords P } } +// need to find a nicer name, my interface to decouple the implementation from the ncm xpc tunnel and the remote tunnel +type pairingService interface { + getPairRecords() PairRecordManager + setupCiphers(sharedSecret []byte) error + writeEvent(event eventCodec) error + readEvent(event eventCodec) error + getCipher() *cipherStream + writeRequest(req map[string]interface{}) error + readResponse() (map[string]interface{}, error) + writeEncrypted(msg map[string]interface{}) error + readEncrypted() (map[string]interface{}, error) +} + type tunnelService struct { xpcConn *xpc.Connection c io.Closer @@ -48,12 +61,43 @@ func (t *tunnelService) Close() error { return t.c.Close() } +func (t *tunnelService) readEncrypted() (map[string]interface{}, error) { + return t.controlChannel.read() +} + +func (t *tunnelService) writeEncrypted(msg map[string]interface{}) error { + return t.controlChannel.write(msg) +} + +func (t *tunnelService) getCipher() *cipherStream { + return t.cipher +} + +func (t *tunnelService) writeEvent(event eventCodec) error { + return t.controlChannel.writeEvent(event) +} +func (t *tunnelService) readEvent(event eventCodec) error { + return t.controlChannel.readEvent(event) +} + +func (t *tunnelService) writeRequest(req map[string]interface{}) error { + return t.controlChannel.writeRequest(req) +} + +func (t *tunnelService) readResponse() (map[string]interface{}, error) { + return t.controlChannel.read() +} + +func (t *tunnelService) getPairRecords() PairRecordManager { + return t.pairRecords +} + // ManualPair triggers a device pairing that requires the user to press the 'Trust' button on the device that appears // when this operation is triggered // If there is already an active pairing with the credentials stored in PairRecordManager this call does not trigger // anything on the device and returns with an error -func (t *tunnelService) ManualPair() error { - err := t.controlChannel.writeRequest(map[string]interface{}{ +func ManualPair(t pairingService) error { + err := t.writeRequest(map[string]interface{}{ "handshake": map[string]interface{}{ "_0": map[string]interface{}{ "hostOptions": map[string]interface{}{ @@ -68,28 +112,29 @@ func (t *tunnelService) ManualPair() error { return fmt.Errorf("ManualPair: failed to send 'attemptPairVerify' request: %w", err) } // ignore the response for now - _, err = t.controlChannel.read() + r, err := t.readResponse() if err != nil { return fmt.Errorf("ManualPair: failed to read 'attemptPairVerify' response: %w", err) } + print(r) - err = t.verifyPair() + err = verifyPair(t) if err == nil { return nil } log.WithError(err).Info("pair verify failed") - err = t.setupManualPairing() + err = setupManualPairing(t) if err != nil { return fmt.Errorf("ManualPair: failed to initiate manual pairing: %w", err) } - sessionKey, err := t.setupSessionKey() + sessionKey, err := setupSessionKey(t) if err != nil { return fmt.Errorf("ManualPair: failed to setup SRP session key: %w", err) } - err = t.exchangeDeviceInfo(sessionKey) + err = exchangeDeviceInfo(t, sessionKey) if err != nil { return fmt.Errorf("ManualPair: failed to exchange device info: %w", err) } @@ -99,7 +144,7 @@ func (t *tunnelService) ManualPair() error { return fmt.Errorf("ManualPair: failed to setup session ciphers: %w", err) } - _, err = t.createUnlockKey() + _, err = createUnlockKey(t) if err != nil { return fmt.Errorf("ManualPair: failed to create unlock key: %w", err) } @@ -107,7 +152,7 @@ func (t *tunnelService) ManualPair() error { return nil } -func (t *tunnelService) createTunnelListener() (tunnelListener, error) { +func createTunnelListener(t pairingService, protocol string) (tunnelListener, error) { log.Info("create tunnel listener") privateKey, err := rsa.GenerateKey(rand.Reader, 2048) @@ -119,12 +164,12 @@ func (t *tunnelService) createTunnelListener() (tunnelListener, error) { return tunnelListener{}, err } - err = t.cipher.write(map[string]interface{}{ + err = t.getCipher().write(map[string]interface{}{ "request": map[string]interface{}{ "_0": map[string]interface{}{ "createListener": map[string]interface{}{ "key": der, - "transportProtocolType": "quic", + "transportProtocolType": protocol, }, }, }, @@ -134,7 +179,7 @@ func (t *tunnelService) createTunnelListener() (tunnelListener, error) { } var listenerRes map[string]interface{} - err = t.cipher.read(&listenerRes) + err = t.getCipher().read(&listenerRes) if err != nil { return tunnelListener{}, err } @@ -180,12 +225,12 @@ func (t *tunnelService) setupCiphers(sessionKey []byte) error { return err } - t.cipher = newCipherStream(t.controlChannel, client, server) + t.cipher = newCipherStream(t, client, server) return nil } -func (t *tunnelService) setupManualPairing() error { +func setupManualPairing(t pairingService) error { buf := newTlvBuffer() buf.writeByte(typeMethod, 0x00) buf.writeByte(typeState, pairStateStartRequest) @@ -196,20 +241,21 @@ func (t *tunnelService) setupManualPairing() error { startNewSession: true, } - err := t.controlChannel.writeEvent(&event) + err := t.writeEvent(&event) if err != nil { return err } - _, err = t.controlChannel.read() + r, err := t.readResponse() if err != nil { return err } + print(r) return err } -func (t *tunnelService) readDeviceKey() (publicKey []byte, salt []byte, err error) { +func readDeviceKey(t pairingService) (publicKey []byte, salt []byte, err error) { var pairingData pairingData - err = t.controlChannel.readEvent(&pairingData) + err = t.readEvent(&pairingData) if err != nil { return } @@ -224,8 +270,8 @@ func (t *tunnelService) readDeviceKey() (publicKey []byte, salt []byte, err erro return } -func (t *tunnelService) createUnlockKey() ([]byte, error) { - err := t.cipher.write(map[string]interface{}{ +func createUnlockKey(t pairingService) ([]byte, error) { + err := t.getCipher().write(map[string]interface{}{ "request": map[string]interface{}{ "_0": map[string]interface{}{ "createRemoteUnlockKey": map[string]interface{}{}, @@ -237,7 +283,7 @@ func (t *tunnelService) createUnlockKey() ([]byte, error) { } var res map[string]interface{} - err = t.cipher.read(&res) + err = t.getCipher().read(&res) if err != nil { return nil, err } @@ -245,7 +291,7 @@ func (t *tunnelService) createUnlockKey() ([]byte, error) { return nil, err } -func (t *tunnelService) verifyPair() error { +func verifyPair(t pairingService) error { key, _ := ecdh.X25519().GenerateKey(rand.Reader) tlv := newTlvBuffer() tlv.writeByte(typeState, pairStateStartRequest) @@ -257,12 +303,14 @@ func (t *tunnelService) verifyPair() error { startNewSession: true, } - selfId := t.pairRecords.selfId - - err := t.controlChannel.writeEvent(&event) + selfId := t.getPairRecords().selfId + err := t.writeEvent(&event) + if err != nil { + return err + } var devP pairingData - err = t.controlChannel.readEvent(&devP) + err = t.readEvent(&devP) if err != nil { return err } @@ -273,7 +321,7 @@ func (t *tunnelService) verifyPair() error { } if devicePublicKeyBytes == nil { - _ = t.controlChannel.writeEvent(pairVerifyFailed{}) + _ = t.writeEvent(&pairVerifyFailed{}) return fmt.Errorf("verifyPair: did not get public key from device. Can not verify pairing") } devicePublicKey, err := ecdh.X25519().NewPublicKey(devicePublicKeyBytes) @@ -323,13 +371,13 @@ func (t *tunnelService) verifyPair() error { startNewSession: false, } - err = t.controlChannel.writeEvent(&pd) + err = t.writeEvent(&pd) if err != nil { return err } var responseEvent pairingData - err = t.controlChannel.readEvent(&responseEvent) + err = t.readEvent(&responseEvent) if err != nil { return err } @@ -340,7 +388,7 @@ func (t *tunnelService) verifyPair() error { } if len(errRes) > 0 { log.Debug("send pair verify failed event") - err := t.controlChannel.writeEvent(pairVerifyFailed{}) + err := t.writeEvent(pairVerifyFailed{}) if err != nil { return err } @@ -371,8 +419,8 @@ type tunnelParameters struct { } } -func (t *tunnelService) setupSessionKey() ([]byte, error) { - devicePublicKey, deviceSalt, err := t.readDeviceKey() +func setupSessionKey(t pairingService) ([]byte, error) { + devicePublicKey, deviceSalt, err := readDeviceKey(t) if err != nil { return nil, fmt.Errorf("setupSessionKey: failed to read device public key and salt value: %w", err) } @@ -387,7 +435,7 @@ func (t *tunnelService) setupSessionKey() ([]byte, error) { proofTlv.writeData(typePublicKey, srp.ClientPublic) proofTlv.writeData(typeProof, srp.ClientProof) - err = t.controlChannel.writeEvent(&pairingData{ + err = t.writeEvent(&pairingData{ data: proofTlv.bytes(), kind: "setupManualPairing", }) @@ -396,7 +444,7 @@ func (t *tunnelService) setupSessionKey() ([]byte, error) { } var proofPairingData pairingData - err = t.controlChannel.readEvent(&proofPairingData) + err = t.readEvent(&proofPairingData) if err != nil { return nil, fmt.Errorf("setupSessionKey: failed to read device SRP proof: %w", err) } @@ -412,21 +460,21 @@ func (t *tunnelService) setupSessionKey() ([]byte, error) { return srp.SessionKey, nil } -func (t *tunnelService) exchangeDeviceInfo(sessionKey []byte) error { +func exchangeDeviceInfo(t pairingService, sessionKey []byte) error { hkdfPairSetup := hkdf.New(sha512.New, sessionKey, []byte("Pair-Setup-Controller-Sign-Salt"), []byte("Pair-Setup-Controller-Sign-Info")) buf := bytes.NewBuffer(nil) // Write on bytes.Buffer never returns an error _, _ = io.CopyN(buf, hkdfPairSetup, 32) - _, _ = buf.WriteString(t.pairRecords.selfId.Identifier) - _, _ = buf.Write(t.pairRecords.selfId.publicKey()) + _, _ = buf.WriteString(t.getPairRecords().selfId.Identifier) + _, _ = buf.Write(t.getPairRecords().selfId.publicKey()) - signature := ed25519.Sign(t.pairRecords.selfId.privateKey(), buf.Bytes()) + signature := ed25519.Sign(t.getPairRecords().selfId.privateKey(), buf.Bytes()) // this represents the device info of this host that is stored on the device on a successful pairing. // The only relevant field is 'accountID' as it's used earlier in the pairing process already. // Everything else can be random data and is not needed later in any communication. deviceInfo, err := opack.Encode(map[string]interface{}{ - "accountID": t.pairRecords.selfId.Identifier, + "accountID": t.getPairRecords().selfId.Identifier, "altIRK": []byte{0x5e, 0xca, 0x81, 0x91, 0x92, 0x02, 0x82, 0x00, 0x11, 0x22, 0x33, 0x44, 0xbb, 0xf2, 0x4a, 0xc8}, "btAddr": "FF:DD:99:66:BB:AA", "mac": []byte{0xff, 0x44, 0x88, 0x66, 0x33, 0x99}, @@ -437,8 +485,8 @@ func (t *tunnelService) exchangeDeviceInfo(sessionKey []byte) error { deviceInfoTlv := newTlvBuffer() deviceInfoTlv.writeData(typeSignature, signature) - deviceInfoTlv.writeData(typePublicKey, t.pairRecords.selfId.publicKey()) - deviceInfoTlv.writeData(typeIdentifier, []byte(t.pairRecords.selfId.Identifier)) + deviceInfoTlv.writeData(typePublicKey, t.getPairRecords().selfId.publicKey()) + deviceInfoTlv.writeData(typeIdentifier, []byte(t.getPairRecords().selfId.Identifier)) deviceInfoTlv.writeData(typeInfo, deviceInfo) sessionKeyBuf := bytes.NewBuffer(nil) @@ -461,7 +509,7 @@ func (t *tunnelService) exchangeDeviceInfo(sessionKey []byte) error { encryptedTlv.writeByte(typeState, 0x05) encryptedTlv.writeData(typeEncryptedData, x) - err = t.controlChannel.writeEvent(&pairingData{ + err = t.writeEvent(&pairingData{ data: encryptedTlv.bytes(), kind: "setupManualPairing", sendingHost: "SL-1876", @@ -471,7 +519,7 @@ func (t *tunnelService) exchangeDeviceInfo(sessionKey []byte) error { } var encRes pairingData - err = t.controlChannel.readEvent(&encRes) + err = t.readEvent(&encRes) if err != nil { return err }