Documentation ¶
Index ¶
- Constants
- Variables
- func BuildAckMessage(msg Message) []byte
- func Decompress(data []byte) ([]byte, error)
- func Encode(Identifier int, ConversationIndex int, ChannelCode int, ExpectsReply bool, ...) ([]byte, error)
- func IsIncomplete(err error) bool
- func IsOutOfSync(err error) bool
- func NewIncomplete(message string) error
- func NewOutOfSync(message string) error
- func SendAckIfNeeded(dtxConn *Connection, msg Message)
- type AuxiliaryEncoder
- type AuxiliaryHeader
- type Channel
- func (d *Channel) AddResponseWaiter(identifier int, channel chan Message)
- func (d *Channel) Dispatch(msg Message)
- func (d *Channel) MethodCall(selector string, args ...interface{}) (Message, error)
- func (d *Channel) MethodCallAsync(selector string, args ...interface{}) error
- func (d *Channel) MethodCallWithAuxiliary(selector string, aux PrimitiveDictionary) (Message, error)
- func (d *Channel) ReceiveMethodCall(selector string) Message
- func (d *Channel) RegisterMethodForRemote(selector string)
- func (d *Channel) Send(expectsReply bool, messageType MessageType, payloadBytes []byte, ...) error
- func (d *Channel) SendAndAwaitReply(expectsReply bool, messageType MessageType, payloadBytes []byte, ...) (Message, error)
- type ChannelOption
- type Connection
- func (dtxConn *Connection) AddDefaultChannelReceiver(messageDispatcher Dispatcher) *Channel
- func (dtxConn *Connection) Close() error
- func (dtxConn *Connection) Closed() <-chan struct{}
- func (dtxConn *Connection) Dispatch(msg Message)
- func (dtxConn *Connection) Err() error
- func (dtxConn *Connection) ForChannelRequest(messageDispatcher Dispatcher) *Channel
- func (dtxConn *Connection) GlobalChannel() *Channel
- func (dtxConn *Connection) RequestChannelIdentifier(identifier string, messageDispatcher Dispatcher, opts ...ChannelOption) *Channel
- func (dtxConn *Connection) Send(message []byte) error
- type Dispatcher
- type FragmentDecoder
- type GlobalDispatcher
- type Message
- func (d Message) HasAuxiliary() bool
- func (d Message) HasError() bool
- func (d Message) HasPayload() bool
- func (d Message) IsFirstFragment() bool
- func (d Message) IsFragment() bool
- func (d Message) IsLastFragment() bool
- func (d Message) MessageIsFirstFragmentFor(otherMessage Message) bool
- func (d Message) PayloadLength() uint32
- func (d Message) String() string
- func (d Message) StringDebug() string
- type MessageType
- type MethodWithResponse
- type PayloadHeader
- type PrimitiveDictionary
- func (d *PrimitiveDictionary) AddBytes(value []byte)
- func (d *PrimitiveDictionary) AddInt32(value int)
- func (d *PrimitiveDictionary) AddNsKeyedArchivedObject(object interface{})
- func (d PrimitiveDictionary) GetArguments() []interface{}
- func (d PrimitiveDictionary) String() string
- func (d PrimitiveDictionary) ToBytes() ([]byte, error)
- type PrimitiveKeyValuePair
Constants ¶
const ( // DtxMessageMagic 4byte signature of every Message DtxMessageMagic uint32 = 0x795B3D1F // DtxMessageHeaderLength alwys 32 byte DtxMessageHeaderLength uint32 = 32 // DtxMessagePayloadHeaderLength always 16 bytes DtxMessagePayloadHeaderLength uint32 = 16 // DtxReservedBits are always 0x0 DtxReservedBits uint32 = 0x0 )
Variables ¶
var ErrConnectionClosed = errors.New("Connection closed")
Functions ¶
func BuildAckMessage ¶
BuildAckMessage creates a 32+ 16 byte long message that can be used as a response for a message the had the ExpectsReply flag set.
func Decompress ¶ added in v1.0.90
func Encode ¶
func Encode( Identifier int, ConversationIndex int, ChannelCode int, ExpectsReply bool, MessageType MessageType, payloadBytes []byte, Auxiliary PrimitiveDictionary, ) ([]byte, error)
Encode encodes the given parameters to a DtxMessage that can be sent to the device.
func IsIncomplete ¶
IsIncomplete returns true if the DtxMessage was incomplete
func IsOutOfSync ¶
IsOutOfSync returns true if err is an OutOfSync error
func NewIncomplete ¶
NewIncomplete when the Message was not complete
func NewOutOfSync ¶
NewOutOfSync should be used when the MagicBytes are wrong
func SendAckIfNeeded ¶
func SendAckIfNeeded(dtxConn *Connection, msg Message)
Types ¶
type AuxiliaryEncoder ¶
type AuxiliaryEncoder struct {
// contains filtered or unexported fields
}
func (*AuxiliaryEncoder) AddNsKeyedArchivedObject ¶
func (a *AuxiliaryEncoder) AddNsKeyedArchivedObject(object interface{})
func (*AuxiliaryEncoder) GetBytes ¶
func (a *AuxiliaryEncoder) GetBytes() []byte
type AuxiliaryHeader ¶
type AuxiliaryHeader struct { BufferSize uint32 Unknown uint32 AuxiliarySize uint32 Unknown2 uint32 }
The AuxiliaryHeader can actually be completely ignored. We do not need to care about the buffer size And we already know the AuxiliarySize. The other two ints seem to be always 0 anyway. Could also be that Buffer and Aux Size are Uint64
func (AuxiliaryHeader) String ¶
func (a AuxiliaryHeader) String() string
type Channel ¶
type Channel struct {
// contains filtered or unexported fields
}
func (*Channel) AddResponseWaiter ¶
func (*Channel) MethodCall ¶
MethodCall is the standard DTX style remote method invocation pattern. The ObjectiveC Selector goes as a NSKeyedArchiver.archived NSString into the DTXMessage payload, and the arguments are separately NSKeyArchiver.archived and put into the Auxiliary DTXPrimitiveDictionary. It returns the response message and an error.
func (*Channel) MethodCallAsync ¶
func (*Channel) MethodCallWithAuxiliary ¶ added in v1.0.161
func (d *Channel) MethodCallWithAuxiliary(selector string, aux PrimitiveDictionary) (Message, error)
MethodCallWithAuxiliary is a DTX style remote method invocation pattern. The ObjectiveC Selector goes as a NSKeyedArchiver.archived NSString into the DTXMessage payload, and the primitive arguments put into the Auxiliary DTXPrimitiveDictionary. It returns the response message and an error.
func (*Channel) ReceiveMethodCall ¶
func (*Channel) RegisterMethodForRemote ¶
func (*Channel) Send ¶
func (d *Channel) Send(expectsReply bool, messageType MessageType, payloadBytes []byte, auxiliary PrimitiveDictionary) error
func (*Channel) SendAndAwaitReply ¶
func (d *Channel) SendAndAwaitReply(expectsReply bool, messageType MessageType, payloadBytes []byte, auxiliary PrimitiveDictionary) (Message, error)
type ChannelOption ¶ added in v1.0.24
type ChannelOption func(*Channel)
ChannelOption for configuring settings on dtx.Channels
func WithTimeout ¶ added in v1.0.24
func WithTimeout(seconds uint32) ChannelOption
WithTimeout adds a custom timeout in seconds to the channel. Some longer running synchronous operations need that.
type Connection ¶
type Connection struct { // MessageDispatcher use this prop to catch messages from GlobalDispatcher // and handle it accordingly in a custom dispatcher of the dedicated service // // Set this prop when creating a connection instance // // Refer to end-to-end example of `instruments/instruments_sysmontap.go` MessageDispatcher Dispatcher // contains filtered or unexported fields }
Connection manages channels, including the GlobalChannel, for a DtxConnection and dispatches received messages to the right channel.
func NewTunnelConnection ¶ added in v1.0.122
func NewTunnelConnection(device ios.DeviceEntry, serviceName string) (*Connection, error)
NewTunnelConnection connects and starts reading from a Dtx based service on the device, using tunnel interface instead of usbmuxd
func NewUsbmuxdConnection ¶ added in v1.0.122
func NewUsbmuxdConnection(device ios.DeviceEntry, serviceName string) (*Connection, error)
NewUsbmuxdConnection connects and starts reading from a Dtx based service on the device
func (*Connection) AddDefaultChannelReceiver ¶ added in v1.0.77
func (dtxConn *Connection) AddDefaultChannelReceiver(messageDispatcher Dispatcher) *Channel
AddDefaultChannelReceiver let's you set the Dispatcher for the Channel with code -1 ( or 4294967295 for uint32). I am just calling it the "default" channel now, without actually figuring out what it is for exactly from disassembled code. If someone wants to do that and bring some clarity, please go ahead :-) This channel seems to always be there without explicitly requesting it and sometimes it is used.
func (*Connection) Close ¶
func (dtxConn *Connection) Close() error
Close closes the underlying deviceConnection
func (*Connection) Closed ¶ added in v1.0.122
func (dtxConn *Connection) Closed() <-chan struct{}
Closed is closed when the underlying DTX connection was closed for any reason (either initiated by calling Close() or due to an error)
func (*Connection) Dispatch ¶ added in v1.0.152
func (dtxConn *Connection) Dispatch(msg Message)
Dispatch to a MessageDispatcher of the Connection if set
func (*Connection) Err ¶ added in v1.0.122
func (dtxConn *Connection) Err() error
Err is non-nil when the connection was closed (when Close was called this will be ErrConnectionClosed)
func (*Connection) ForChannelRequest ¶
func (dtxConn *Connection) ForChannelRequest(messageDispatcher Dispatcher) *Channel
func (*Connection) GlobalChannel ¶
func (dtxConn *Connection) GlobalChannel() *Channel
GlobalChannel returns the connections automatically created global channel.
func (*Connection) RequestChannelIdentifier ¶
func (dtxConn *Connection) RequestChannelIdentifier(identifier string, messageDispatcher Dispatcher, opts ...ChannelOption) *Channel
RequestChannelIdentifier requests a channel to be opened on the Connection with the given identifier, an automatically assigned channelCode and a Dispatcher for receiving messages.
func (*Connection) Send ¶
func (dtxConn *Connection) Send(message []byte) error
Send sends the byte slice directly to the device using the underlying DeviceConnectionInterface
type Dispatcher ¶
type Dispatcher interface {
Dispatch(msg Message)
}
Dispatcher is a simple interface containing a Dispatch func to receive dtx.Messages
func NewGlobalDispatcher ¶
func NewGlobalDispatcher(requestChannelMessages chan Message, dtxConnection *Connection) Dispatcher
NewGlobalDispatcher create a Dispatcher for the GlobalChannel
type FragmentDecoder ¶
type FragmentDecoder struct {
// contains filtered or unexported fields
}
FragmentDecoder collects DtxMessage fragments and merges them into a single DtxMessage when they are complete. This makes it a little easier and it works perfectly fine with Xcode. I don't get the point in fragmenting USB messages anyway.. DTX Fragment logic: 1. Fragment is only 32 bytes long, fragment index ==0, fragment length>1 following fragments contain the 32 bytes dtx header, then immediately the fragment data once the last fragment is received (index == length-1), we can: 1. merge all the fragment data 2. prepend the dtx header of the first message 3. set fragment length to 1 and index to 0, and we have a defragmented single message that Xcode will be able to use just the same as the fragmented one :-)
func NewFragmentDecoder ¶
func NewFragmentDecoder(firstFragment Message) *FragmentDecoder
NewFragmentDecoder creates a new decoder with the first fragment
func (*FragmentDecoder) AddFragment ¶
func (f *FragmentDecoder) AddFragment(fragment Message) bool
AddFragment adds fragments if they match the firstFragment this FragmentDecoder was created with. It returns true if the fragment was added and fals if the fragment was not matching this decoder's first fragment.
func (FragmentDecoder) Extract ¶
func (f FragmentDecoder) Extract() []byte
Extract can be used to get an assembled DtxMessage from all the fragments. Never call this befor HasFinished is true.
func (FragmentDecoder) HasFinished ¶
func (f FragmentDecoder) HasFinished() bool
HasFinished can be used to check if all fragments have been added
type GlobalDispatcher ¶
type GlobalDispatcher struct {
// contains filtered or unexported fields
}
GlobalDispatcher the message dispatcher for the automatically created global Channel
func (GlobalDispatcher) Dispatch ¶
func (g GlobalDispatcher) Dispatch(msg Message)
Dispatch prints log messages and errors when they are received and also creates local Channels when requested by the device.
type Message ¶
type Message struct { Fragments uint16 FragmentIndex uint16 MessageLength int Identifier int ConversationIndex int ChannelCode int ExpectsReply bool PayloadHeader PayloadHeader Payload []interface{} AuxiliaryHeader AuxiliaryHeader Auxiliary PrimitiveDictionary RawBytes []byte // contains filtered or unexported fields }
Message contains a decoded DtxMessage aka the most overcomplicated RPC protocol this planet has ever seen :-D
DTXMessages consist of a 32byte header that always starts with the DtxMessageMagic It is followed by the 16 bytes PayloadHeader. If there is an Auxiliary:
Next is the 16 byte AuxiliaryHeader followed by the DtxPrimitiveDictionary containing auxiliary data Directly after the Auxiliary, the payload bytes are following. If there is no Auxiliary: The payload bytes follow directly after the PayloadHeader There is also support for fragmenting DTX messages into multiple messages, see fragmentdecoder.go for details how that works
func DecodeNonBlocking ¶
DecodeNonBlocking should only be used for the debug proxy to on the fly decode DtxMessages. It is used because if the Decoder encounters an error, we can still keep reading and forwarding the raw bytes. This ensures that the debug proxy keeps working and the byte dump can be used to fix the DtxDecoder
func ReadMessage ¶
ReadMessage uses the reader to fully read a Message from it in blocking mode.
func (Message) HasAuxiliary ¶
HasAuxiliary returns PayloadHeader.AuxiliaryLength > 0
func (Message) HasError ¶
HasError returns true when the MessageType in this message's PayloadHeader equals 0x4 and false otherwise.
func (Message) HasPayload ¶
HasPayload returns PayloadLength() > 0, it is true if the Message has payload bytes
func (Message) IsFirstFragment ¶
IsFirstFragment returns true if the message is the first of a series of fragments.IsFirstFragment The first fragment message is only 32 bytes long
func (Message) IsFragment ¶
IsFragment returns true if the Message is a fragment
func (Message) IsLastFragment ¶
IsLastFragment returns true if this message is the last fragment
func (Message) MessageIsFirstFragmentFor ¶
MessageIsFirstFragmentFor indicates whether the message you call this on, is the first part of a fragmented message, and if otherMessage is a subsequent fragment
func (Message) PayloadLength ¶
PayloadLength equals PayloadHeader.TotalPayloadLength - d.PayloadHeader.AuxiliaryLength so it is the Payload without the Auxiliary
func (Message) StringDebug ¶
StringDebug prints the Message and its Payload/Auxiliary
type MessageType ¶ added in v1.0.122
type MessageType uint32
const ( // Ack is the messagetype for a 16 byte long acknowleding DtxMessage. Ack MessageType = 0x0 // Unknown UnknownTypeOne MessageType = 0x1 // Methodinvocation is the messagetype for a remote procedure call style DtxMessage. Methodinvocation MessageType = 0x2 // ResponseWithReturnValueInPayload is the response for a method call that has a return value ResponseWithReturnValueInPayload MessageType = 0x3 // DtxTypeError is the messagetype for a DtxMessage containing an error DtxTypeError MessageType = 0x4 LZ4CompressedMessage MessageType = 0x0707 )
All the known MessageTypes
func (MessageType) String ¶ added in v1.0.122
func (m MessageType) String() string
type MethodWithResponse ¶
type PayloadHeader ¶
type PayloadHeader struct { MessageType MessageType AuxiliaryLength uint32 TotalPayloadLength uint32 Flags uint32 }
PayloadHeader contains the message type and Payload length
func (PayloadHeader) String ¶ added in v1.0.122
func (p PayloadHeader) String() string
type PrimitiveDictionary ¶
type PrimitiveDictionary struct {
// contains filtered or unexported fields
}
PrimitiveDictionary contains a custom dictionary type only used for DTX. In practice however, the keys of all dictionaries are always null and the values are used as a simple array containing the method arguments for the method this message is invoking. (The payload object usually contains method names or returnvalues)
func DecodeAuxiliary ¶ added in v1.0.109
func DecodeAuxiliary(auxBytes []byte) PrimitiveDictionary
func NewPrimitiveDictionary ¶
func NewPrimitiveDictionary() PrimitiveDictionary
func (*PrimitiveDictionary) AddBytes ¶
func (d *PrimitiveDictionary) AddBytes(value []byte)
func (*PrimitiveDictionary) AddInt32 ¶
func (d *PrimitiveDictionary) AddInt32(value int)
func (*PrimitiveDictionary) AddNsKeyedArchivedObject ¶
func (d *PrimitiveDictionary) AddNsKeyedArchivedObject(object interface{})
AddNsKeyedArchivedObject wraps the object in a NSKeyedArchiver envelope before saving it to the dictionary as a []byte. This will panic on error because NSKeyedArchiver has to support everything that is put in here during runtime. If not, it is a non-recoverable bug and needs to be fixed anyway.
func (PrimitiveDictionary) GetArguments ¶
func (d PrimitiveDictionary) GetArguments() []interface{}
func (PrimitiveDictionary) String ¶
func (d PrimitiveDictionary) String() string
func (PrimitiveDictionary) ToBytes ¶
func (d PrimitiveDictionary) ToBytes() ([]byte, error)
ToBytes serializes this PrimitiveDictionary to a byte slice
type PrimitiveKeyValuePair ¶
type PrimitiveKeyValuePair struct {
// contains filtered or unexported fields
}