Skip to content

Commit

Permalink
Add additional types to XPC codec
Browse files Browse the repository at this point in the history
  • Loading branch information
dmissmann committed Oct 26, 2023
1 parent 232e3a6 commit 313cb36
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 8 deletions.
110 changes: 102 additions & 8 deletions ios/xpc/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"github.com/google/uuid"
"io"
"math"
"reflect"
Expand All @@ -21,21 +22,25 @@ type xpcType uint32

// TODO: there are more types available and need to be added still when observed
const (
nullType = xpcType(0x00001000)
boolType = xpcType(0x00002000)
int64Type = xpcType(0x00003000)
uint64Type = xpcType(0x00004000)
dataType = xpcType(0x00008000)
stringType = xpcType(0x00009000)
arrayType = xpcType(0x0000e000)
dictionaryType = xpcType(0x0000f000)
nullType = xpcType(0x00001000)
boolType = xpcType(0x00002000)
int64Type = xpcType(0x00003000)
uint64Type = xpcType(0x00004000)
doubleType = xpcType(0x00005000)
dataType = xpcType(0x00008000)
stringType = xpcType(0x00009000)
uuidType = xpcType(0x0000a000)
arrayType = xpcType(0x0000e000)
dictionaryType = xpcType(0x0000f000)
fileTransferType = xpcType(0x0001a000)
)

const (
alwaysSetFlag = uint32(0x00000001)
dataFlag = uint32(0x00000100)
heartbeatRequestFlag = uint32(0x00010000)
heartbeatReplyFlag = uint32(0x00020000)
fileOpenFlag = uint32(0x00100000)
)

type wrapperHeader struct {
Expand All @@ -49,6 +54,15 @@ type Message struct {
Body map[string]interface{}
}

func (m Message) IsFileOpen() bool {
return m.Flags&fileOpenFlag > 0
}

type FileTransfer struct {
MsgId uint64
TransferSize uint64
}

// DecodeMessage expects a full RemoteXPC message and decodes the message body into a map
func DecodeMessage(r io.Reader) (Message, error) {
var magic uint32
Expand Down Expand Up @@ -174,19 +188,61 @@ func decodeObject(r io.Reader) (interface{}, error) {
return decodeInt64(r)
case uint64Type:
return decodeUint64(r)
case doubleType:
return decodeDouble(r)
case dataType:
return decodeData(r)
case stringType:
return decodeString(r)
case uuidType:
b := make([]byte, 16)
_, err := r.Read(b)
if err != nil {
return nil, err
}
u, err := uuid.FromBytes(b)
if err != nil {
return nil, err
}
return u, nil
case arrayType:
return decodeArray(r)
case dictionaryType:
return decodeDictionary(r)
case fileTransferType:
return decodeFileTransfer(r)
default:
return nil, fmt.Errorf("can't handle unknown type 0x%08x", t)
}
}

func decodeFileTransfer(r io.Reader) (FileTransfer, error) {
header := struct {
MsgId uint64 // always 1
}{}
err := binary.Read(r, binary.LittleEndian, &header)
if err != nil {
return FileTransfer{}, err
}
d, err := decodeObject(r)
if err != nil {
return FileTransfer{}, err
}
if dict, ok := d.(map[string]interface{}); ok {
// the transfer length is always stored in a property 's'
if transferLen, ok := dict["s"].(uint64); ok {
return FileTransfer{
MsgId: header.MsgId,
TransferSize: transferLen,
}, nil
} else {
return FileTransfer{}, fmt.Errorf("expected uint64 for transfer length")
}
} else {
return FileTransfer{}, fmt.Errorf("expected a dictionary but got %T", d)
}
}

func decodeDictionary(r io.Reader) (map[string]interface{}, error) {
var l, numEntries uint32
err := binary.Read(r, binary.LittleEndian, &l)
Expand Down Expand Up @@ -282,6 +338,12 @@ func decodeData(r io.Reader) ([]byte, error) {
return b, nil
}

func decodeDouble(r io.Reader) (interface{}, error) {
var d float64
err := binary.Read(r, binary.LittleEndian, &d)
return d, err
}

func decodeUint64(r io.Reader) (uint64, error) {
var i uint64
err := binary.Read(r, binary.LittleEndian, &i)
Expand Down Expand Up @@ -375,10 +437,18 @@ func encodeObject(w io.Writer, e interface{}) error {
if err := encodeUint64(w, e.(uint64)); err != nil {
return err
}
case float64:
if err := encodeDouble(w, e.(float64)); err != nil {
return err
}
case string:
if err := encodeString(w, e.(string)); err != nil {
return err
}
case uuid.UUID:
if err := encodeUuid(w, e.(uuid.UUID)); err != nil {
return err
}
case map[string]interface{}:
if err := encodeDictionary(w, e.(map[string]interface{})); err != nil {
return err
Expand All @@ -389,6 +459,18 @@ func encodeObject(w io.Writer, e interface{}) error {
return nil
}

func encodeUuid(w io.Writer, u uuid.UUID) error {
out := struct {
t xpcType
u uuid.UUID
}{uuidType, u}
err := binary.Write(w, binary.LittleEndian, out)
if err != nil {
return err
}
return nil
}

func encodeArray(w io.Writer, slice []interface{}) error {
buf := bytes.NewBuffer(nil)
for _, e := range slice {
Expand Down Expand Up @@ -477,6 +559,18 @@ func encodeInt64(w io.Writer, i int64) error {
return nil
}

func encodeDouble(w io.Writer, d float64) error {
out := struct {
t xpcType
d float64
}{doubleType, d}
err := binary.Write(w, binary.LittleEndian, out)
if err != nil {
return err
}
return nil
}

func encodeBool(w io.Writer, b bool) error {
out := struct {
t xpcType
Expand Down
12 changes: 12 additions & 0 deletions ios/xpc/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package xpc
import (
"bytes"
"encoding/base64"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"os"
"path"
Expand Down Expand Up @@ -106,6 +107,7 @@ func TestEncodeDecode(t *testing.T) {
"int64": int64(123),
"uint64": uint64(321),
"data": []byte{0x1},
"double": float64(1.2),
},
},
expectedFlags: alwaysSetFlag | dataFlag,
Expand All @@ -124,6 +126,16 @@ func TestEncodeDecode(t *testing.T) {
},
expectedFlags: alwaysSetFlag | dataFlag,
},
{
name: "encode uuid",
input: map[string]interface{}{
"uuidvalue": func() uuid.UUID {
u, _ := uuid.FromBytes(base64Decode("RYjS2yNAbEG+Y0WWxq5/4w=="))
return u
}(),
},
expectedFlags: alwaysSetFlag | dataFlag,
},
}

for _, tt := range tests {
Expand Down

0 comments on commit 313cb36

Please sign in to comment.