Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xcuitestrunner over tunnel (iOS 17) #301

Merged
merged 25 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8036cdf
Implementing xcuitestrunner over tunnel
diegoperini Nov 8, 2023
086b2bc
Remvoed dead code
diegoperini Nov 8, 2023
6a8dc5c
Debugging
diegoperini Dec 5, 2023
c1a4151
Merge branch 'ios-17' into dp-xcuitestrunner-ios17
diegoperini Dec 11, 2023
c21b1a1
Implemented xcuitest and reusable connections
diegoperini Dec 11, 2023
8898126
Clean up
diegoperini Dec 11, 2023
bccb2a5
Polish in coments
diegoperini Dec 11, 2023
355fe8a
Polish after review
diegoperini Dec 12, 2023
0fa16ea
Fixing CI
diegoperini Dec 12, 2023
10d0158
Trying go 1.18
diegoperini Dec 12, 2023
abcbace
Trying go 1.19
diegoperini Dec 12, 2023
1fd2109
Trying go 1.20
diegoperini Dec 12, 2023
d030aa5
Trying go 1.21
diegoperini Dec 12, 2023
089d099
Rolled back to go 1.17
diegoperini Dec 12, 2023
de74eb0
Using go 1.20
diegoperini Dec 12, 2023
7406497
Installed pcap on linux CI tests
diegoperini Dec 12, 2023
d17946c
Fixing pcap in CI
diegoperini Dec 12, 2023
9a3162e
Fixed typo in last commit
diegoperini Dec 12, 2023
0d3b0bf
Merge branch 'ios-17' into dp-xcuitestrunner-ios17
diegoperini Dec 13, 2023
4675bd9
Merge branch 'ios-17' into dp-xcuitestrunner-ios17
diegoperini Dec 13, 2023
8af54f9
Merge branch 'ios-17' into dp-xcuitestrunner-ios17
diegoperini Dec 13, 2023
31d9bdd
Merge branch 'ios-17' into dp-xcuitestrunner-ios17
diegoperini Dec 13, 2023
a62f523
Fixed main.go imports
diegoperini Dec 13, 2023
c1f5e98
Revisited some xcuitest info logs and changed those to debug logs
diegoperini Dec 13, 2023
9b0a446
Removed unnecessary CI script step
diegoperini Dec 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everything except this line is a duplicate of NewLockdownConnection. This can be handled in a better way to have less duplication.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DTX connection part is now under its own function.

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