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 }