Skip to content

Commit

Permalink
Xcuitestrunner over tunnel (iOS 17) (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
diegoperini authored Dec 14, 2023
1 parent 1a22f71 commit 3a1ee2f
Show file tree
Hide file tree
Showing 12 changed files with 398 additions and 479 deletions.
348 changes: 17 additions & 331 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ios/accessibility/accessibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const serviceName string = "com.apple.accessibility.axAuditDaemon.remoteserver"

// New creates and connects to the given device, a new ControlInterface instance
func New(device ios.DeviceEntry) (ControlInterface, error) {
conn, err := dtx.NewConnection(device, serviceName)
conn, err := dtx.NewUsbmuxdConnection(device, serviceName)
if err != nil {
return ControlInterface{}, err
}
Expand Down
73 changes: 44 additions & 29 deletions ios/appservice/appservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package appservice
import (
"bytes"
"fmt"
"strings"

"github.com/danielpaulus/go-ios/ios"
"github.com/danielpaulus/go-ios/ios/xpc"
"github.com/google/uuid"
Expand All @@ -14,7 +16,7 @@ type Connection struct {
}

func New(deviceEntry ios.DeviceEntry) (*Connection, error) {
xpcConn, err := ios.ConnectToServiceTunnelIface(deviceEntry, "com.apple.coredevice.appservice")
xpcConn, err := ios.ConnectToXpcServiceTunnelIface(deviceEntry, "com.apple.coredevice.appservice")
if err != nil {
return nil, err
}
Expand All @@ -26,8 +28,8 @@ type AppLaunch struct {
Pid int64
}

func (c *Connection) LaunchApp(deviceId string, bundleId string, args []interface{}, env map[string]interface{}) (AppLaunch, error) {
msg := buildAppLaunchPayload(deviceId, bundleId, args, env)
func (c *Connection) LaunchApp(deviceId string, bundleId string, args []interface{}, env map[string]interface{}, opt map[string]interface{}) (AppLaunch, error) {
msg := buildAppLaunchPayload(deviceId, bundleId, args, env, opt)
err := c.conn.Send(msg, xpc.HeartbeatRequestFlag)
m, err := c.conn.ReceiveOnServerClientStream()
if err != nil {
Expand All @@ -44,15 +46,37 @@ func (c *Connection) Close() error {
return c.conn.Close()
}

func buildAppLaunchPayload(deviceId string, bundleId string, args []interface{}, env map[string]interface{}) map[string]interface{} {
u := uuid.New()
func buildAppLaunchPayload(deviceId string, bundleId string, args []interface{}, env map[string]interface{}, opt map[string]interface{}) map[string]interface{} {
platformSpecificOptions := bytes.NewBuffer(nil)
plistEncoder := plist.NewBinaryEncoder(platformSpecificOptions)
err := plistEncoder.Encode(map[string]interface{}{})
err := plistEncoder.Encode(opt)
if err != nil {
panic(err)
}

return buildCoreDevicePayload(deviceId, "com.apple.coredevice.feature.launchapplication", map[string]interface{}{
"applicationSpecifier": map[string]interface{}{
"bundleIdentifier": map[string]interface{}{
"_0": bundleId,
},
},
"options": map[string]interface{}{
"arguments": args,
"environmentVariables": env,
"platformSpecificOptions": platformSpecificOptions.Bytes(),
"standardIOUsesPseudoterminals": true,
"startStopped": false,
"terminateExisting": true,
"user": map[string]interface{}{
"active": true,
},
"workingDirectory": nil,
},
"standardIOIdentifiers": map[string]interface{}{},
})
}

func buildCoreDevicePayload(deviceId string, feature string, input map[string]interface{}) map[string]interface{} {
return map[string]interface{}{
"CoreDevice.CoreDeviceDDIProtocolVersion": int64(0),
"CoreDevice.action": map[string]interface{}{},
Expand All @@ -61,29 +85,10 @@ func buildAppLaunchPayload(deviceId string, bundleId string, args []interface{},
"originalComponentsCount": int64(2),
"stringValue": "348.1",
},
"CoreDevice.deviceIdentifier": deviceId,
"CoreDevice.featureIdentifier": "com.apple.coredevice.feature.launchapplication",
"CoreDevice.input": map[string]interface{}{
"applicationSpecifier": map[string]interface{}{
"bundleIdentifier": map[string]interface{}{
"_0": bundleId,
},
},
"options": map[string]interface{}{
"arguments": args,
"environmentVariables": env,
"platformSpecificOptions": platformSpecificOptions.Bytes(),
"standardIOUsesPseudoterminals": true,
"startStopped": false,
"terminateExisting": false,
"user": map[string]interface{}{
"active": true,
},
"workingDirectory": nil,
},
"standardIOIdentifiers": map[string]interface{}{},
},
"CoreDevice.invocationIdentifier": u.String(),
"CoreDevice.deviceIdentifier": deviceId,
"CoreDevice.featureIdentifier": feature,
"CoreDevice.input": input,
"CoreDevice.invocationIdentifier": strings.ToUpper(uuid.New().String()),
}
}

Expand All @@ -97,3 +102,13 @@ func pidFromResponse(response map[string]interface{}) (int64, error) {
}
return 0, fmt.Errorf("could not get pid from response")
}

func (c *Connection) ListProcesses(deviceId string) (map[string]interface{}, error) {
msg := buildCoreDevicePayload(deviceId, "com.apple.coredevice.feature.listprocesses", map[string]interface{}{})
err := c.conn.Send(msg, xpc.HeartbeatRequestFlag)
if err != nil {
return nil, err
}

return c.conn.ReceiveOnServerClientStream()
}
40 changes: 38 additions & 2 deletions ios/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package ios

import (
"fmt"
"github.com/danielpaulus/go-ios/ios/http"
"net"
"time"

"github.com/danielpaulus/go-ios/ios/http"

"github.com/danielpaulus/go-ios/ios/xpc"
)

Expand Down Expand Up @@ -103,7 +104,7 @@ func ConnectToService(device DeviceEntry, serviceName string) (DeviceConnectionI
return muxConn.ReleaseDeviceConnection(), nil
}

func ConnectToServiceTunnelIface(device DeviceEntry, serviceName string) (*xpc.Connection, error) {
func ConnectToXpcServiceTunnelIface(device DeviceEntry, serviceName string) (*xpc.Connection, error) {
port := device.Rsd.GetPort(serviceName)

h, err := ConnectToHttp2(device, port)
Expand All @@ -113,6 +114,17 @@ func ConnectToServiceTunnelIface(device DeviceEntry, serviceName string) (*xpc.C
return CreateXpcConnection(h)
}

func ConnectToServiceTunnelIface(device DeviceEntry, serviceName string) (DeviceConnectionInterface, error) {
port := device.Rsd.GetPort(serviceName)

conn, err := connectToTunnel(device, port)
if err != nil {
return nil, err
}

return NewDeviceConnectionWithConn(conn), nil
}

func ConnectToHttp2(device DeviceEntry, port int) (*http.HttpConnection, error) {
addr, err := net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", device.Address, port))
if err != nil {
Expand All @@ -136,6 +148,30 @@ func ConnectToHttp2(device DeviceEntry, port int) (*http.HttpConnection, error)
return http.NewHttpConnection(conn)
}

func connectToTunnel(device DeviceEntry, port int) (*net.TCPConn, error) {
addr, err := net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", device.Address, port))
if err != nil {
return nil, err
}

conn, err := net.DialTCP("tcp", nil, addr)

if err != nil {
return nil, err
}

err = conn.SetKeepAlive(true)
if err != nil {
return nil, err
}
err = conn.SetKeepAlivePeriod(1 * time.Second)
if err != nil {
return nil, err
}

return conn, nil
}

func ConnectToHttp2WithAddr(a string, port int) (*http.HttpConnection, error) {
addr, err := net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", a, port))
if err != nil {
Expand Down
19 changes: 17 additions & 2 deletions ios/dtx_codec/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,27 @@ func notifyOfPublishedCapabilities(msg Message) {
log.Debug("capabs received")
}

// NewConnection connects and starts reading from a Dtx based service on the device
func NewConnection(device ios.DeviceEntry, serviceName string) (*Connection, error) {
// NewUsbmuxdConnection connects and starts reading from a Dtx based service on the device
func NewUsbmuxdConnection(device ios.DeviceEntry, serviceName string) (*Connection, error) {
conn, err := ios.ConnectToService(device, serviceName)
if err != nil {
return nil, err
}

return newDtxConnection(conn)
}

// NewTunnelConnection connects and starts reading from a Dtx based service on the device, using tunnel interface instead of usbmuxd
func NewTunnelConnection(device ios.DeviceEntry, serviceName string) (*Connection, error) {
conn, err := ios.ConnectToServiceTunnelIface(device, serviceName)
if err != nil {
return nil, err
}

return newDtxConnection(conn)
}

func newDtxConnection(conn ios.DeviceConnectionInterface) (*Connection, error) {
requestChannelMessages := make(chan Message, 5)

// The global channel has channelCode 0, so we need to start with channelCodeCounter==1
Expand Down
4 changes: 2 additions & 2 deletions ios/instruments/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ func (p loggingDispatcher) Dispatch(m dtx.Message) {
}

func connectInstruments(device ios.DeviceEntry) (*dtx.Connection, error) {
dtxConn, err := dtx.NewConnection(device, serviceName)
dtxConn, err := dtx.NewUsbmuxdConnection(device, serviceName)
if err != nil {
log.Debugf("Failed connecting to %s, trying %s", serviceName, serviceNameiOS14)
dtxConn, err = dtx.NewConnection(device, serviceNameiOS14)
dtxConn, err = dtx.NewUsbmuxdConnection(device, serviceNameiOS14)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion ios/nskeyedarchiver/archiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func archiveObject(object interface{}) (interface{}, error) {
archiverSkeleton := createSkeleton(true)
objects := make([]interface{}, 1)
objects[0] = null
objects, _ = archive(object, objects)
objects, pid := archive(object, objects)
archiverSkeleton[topKey] = map[string]interface{}{"root": pid}

archiverSkeleton[objectsKey] = objects
return archiverSkeleton, nil
Expand Down
Loading

0 comments on commit 3a1ee2f

Please sign in to comment.