Files
ipsw/pkg/usb/client.go

190 lines
3.6 KiB
Go

package usb
import (
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"net"
"github.com/blacktop/go-plist"
)
type Client struct {
tlsConn *tls.Conn
conn net.Conn
udid string
deviceID int
pairRecord *PairRecord
}
func NewClient(udid string, port int) (*Client, error) {
conn, err := NewConn()
if err != nil {
return nil, err
}
devices, err := conn.ListDevices()
if err != nil {
return nil, fmt.Errorf("failed to list devices: %v", err)
}
deviceID := -1
for _, device := range devices {
if device.SerialNumber == udid {
deviceID = device.DeviceID
break
}
}
if deviceID < 0 {
return nil, fmt.Errorf("unable to find device with udid: %v", udid)
}
pairRecord, err := conn.ReadPairRecord(udid)
if err != nil {
return nil, err
}
if err := conn.Dial(deviceID, port); err != nil {
return nil, fmt.Errorf("failed to dial device %d on port %d: %v", deviceID, port, err)
}
return &Client{
conn: conn,
pairRecord: pairRecord,
udid: udid,
deviceID: deviceID,
}, nil
}
func (c *Client) EnableSSL() error {
cert, err := tls.X509KeyPair(c.pairRecord.HostCertificate, c.pairRecord.HostPrivateKey)
if err != nil {
return fmt.Errorf("failed to load host certificate from pair record: %v", err)
}
c.tlsConn = tls.Client(c.conn, &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.NoClientCert,
InsecureSkipVerify: true,
})
if err := c.tlsConn.Handshake(); err != nil {
return fmt.Errorf("failed to perform tls handshake: %v", err)
}
return nil
}
func (c *Client) DisableSSL() {
c.tlsConn = nil
}
func (c *Client) Request(req, resp any) error {
if err := c.Send(req); err != nil {
return err
}
return c.Recv(resp)
}
func (c *Client) Send(req any) error {
data, err := plist.Marshal(req, plist.XMLFormat)
if err != nil {
return err
}
if err := binary.Write(c.Conn(), binary.BigEndian, uint32(len(data))); err != nil {
return err
}
return binary.Write(c.Conn(), binary.BigEndian, data)
}
func (c Client) Recv(resp any) error {
data, err := c.RecvBytes()
if err != nil {
return err
}
if _, err := plist.Unmarshal(data, resp); err != nil {
return err
}
return nil
}
func (c *Client) RecvBytes() ([]byte, error) {
size := uint32(0)
if err := binary.Read(c.Conn(), binary.BigEndian, &size); err != nil {
return nil, err
}
data := make([]byte, size)
if _, err := io.ReadFull(c.Conn(), data); err != nil {
return nil, err
}
return data, nil
}
func (c *Client) RecvByte() (byte, error) {
var b byte
if err := binary.Read(c.Conn(), binary.BigEndian, &b); err != nil {
return byte(0), err
}
return b, nil
}
func (c *Client) UDID() string {
return c.udid
}
func (c *Client) DeviceID() int {
return c.deviceID
}
func (c *Client) Conn() net.Conn {
if c.tlsConn != nil {
return c.tlsConn
}
return c.conn
}
func (c *Client) PairRecord() *PairRecord {
return c.pairRecord
}
func (c Client) Close() error {
return c.Conn().Close()
}
func (c *Client) DeviceLinkHandshake() ([]any, error) {
var versionExchange []any
if err := c.Recv(&versionExchange); err != nil {
return nil, err
}
reply := []any{"DLMessageVersionExchange", "DLVersionsOk", versionExchange[1]}
if err := c.Send(reply); err != nil {
return nil, err
}
var ready []any
if err := c.Recv(&ready); err != nil {
return nil, err
}
return ready, nil
}
func (c *Client) DeviceLinkSend(msg any) error {
return c.Send([]any{"DLMessageProcessMessage", msg})
}
func (c *Client) DeviceLinkRecv() (any, error) {
var dlMsg []any
if err := c.Recv(&dlMsg); err != nil {
return nil, err
}
return dlMsg[1], nil
}