123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- --- a replacement for aproto -------------------------------------------
- When it comes down to it, aproto's primary purpose is to forward
- various streams between the host computer and client device (in either
- direction).
- This replacement further simplifies the concept, reducing the protocol
- to an extremely straightforward model optimized to accomplish the
- forwarding of these streams and removing additional state or
- complexity.
- The host side becomes a simple comms bridge with no "UI", which will
- be used by either commandline or interactive tools to communicate with
- a device or emulator that is connected to the bridge.
- The protocol is designed to be straightforward and well-defined enough
- that if it needs to be reimplemented in another environment (Java
- perhaps), there should not problems ensuring perfect interoperability.
- The protocol discards the layering aproto has and should allow the
- implementation to be much more robust.
- --- protocol overview and basics ---------------------------------------
- The transport layer deals in "messages", which consist of a 24 byte
- header followed (optionally) by a payload. The header consists of 6
- 32 bit words which are sent across the wire in little endian format.
- struct message {
- unsigned command; /* command identifier constant */
- unsigned arg0; /* first argument */
- unsigned arg1; /* second argument */
- unsigned data_length; /* length of payload (0 is allowed) */
- unsigned data_crc32; /* crc32 of data payload */
- unsigned magic; /* command ^ 0xffffffff */
- };
- Receipt of an invalid message header, corrupt message payload, or an
- unrecognized command MUST result in the closing of the remote
- connection. The protocol depends on shared state and any break in the
- message stream will result in state getting out of sync.
- The following sections describe the six defined message types in
- detail. Their format is COMMAND(arg0, arg1, payload) where the payload
- is represented by a quoted string or an empty string if none should be
- sent.
- The identifiers "local-id" and "remote-id" are always relative to the
- *sender* of the message, so for a receiver, the meanings are effectively
- reversed.
- --- CONNECT(version, maxdata, "system-identity-string") ----------------
- The CONNECT message establishes the presence of a remote system.
- The version is used to ensure protocol compatibility and maxdata
- declares the maximum message body size that the remote system
- is willing to accept.
- Currently, version=0x01000000 and maxdata=4096
- Both sides send a CONNECT message when the connection between them is
- established. Until a CONNECT message is received no other messages may
- be sent. Any messages received before a CONNECT message MUST be ignored.
- If a CONNECT message is received with an unknown version or insufficiently
- large maxdata value, the connection with the other side must be closed.
- The system identity string should be "<systemtype>:<serialno>:<banner>"
- where systemtype is "bootloader", "device", or "host", serialno is some
- kind of unique ID (or empty), and banner is a human-readable version
- or identifier string. The banner is used to transmit useful properties.
- --- AUTH(type, 0, "data") ----------------------------------------------
- The AUTH message informs the recipient that authentication is required to
- connect to the sender. If type is TOKEN(1), data is a random token that
- the recipient can sign with a private key. The recipient replies with an
- AUTH packet where type is SIGNATURE(2) and data is the signature. If the
- signature verification succeeds, the sender replies with a CONNECT packet.
- If the signature verification fails, the sender replies with a new AUTH
- packet and a new random token, so that the recipient can retry signing
- with a different private key.
- Once the recipient has tried all its private keys, it can reply with an
- AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If
- possible, an on-screen confirmation may be displayed for the user to
- confirm they want to install the public key on the device.
- --- OPEN(local-id, 0, "destination") -----------------------------------
- The OPEN message informs the recipient that the sender has a stream
- identified by local-id that it wishes to connect to the named
- destination in the message payload. The local-id may not be zero.
- The OPEN message MUST result in either a READY message indicating that
- the connection has been established (and identifying the other end) or
- a CLOSE message, indicating failure. An OPEN message also implies
- a READY message sent at the same time.
- Common destination naming conventions include:
- * "tcp:<host>:<port>" - host may be omitted to indicate localhost
- * "udp:<host>:<port>" - host may be omitted to indicate localhost
- * "local-dgram:<identifier>"
- * "local-stream:<identifier>"
- * "shell" - local shell service
- * "upload" - service for pushing files across (like aproto's /sync)
- * "fs-bridge" - FUSE protocol filesystem bridge
- --- READY(local-id, remote-id, "") -------------------------------------
- The READY message informs the recipient that the sender's stream
- identified by local-id is ready for write messages and that it is
- connected to the recipient's stream identified by remote-id.
- Neither the local-id nor the remote-id may be zero.
- A READY message containing a remote-id which does not map to an open
- stream on the recipient's side is ignored. The stream may have been
- closed while this message was in-flight.
- The local-id is ignored on all but the first READY message (where it
- is used to establish the connection). Nonetheless, the local-id MUST
- not change on later READY messages sent to the same stream.
- --- WRITE(0, remote-id, "data") ----------------------------------------
- The WRITE message sends data to the recipient's stream identified by
- remote-id. The payload MUST be <= maxdata in length.
- A WRITE message containing a remote-id which does not map to an open
- stream on the recipient's side is ignored. The stream may have been
- closed while this message was in-flight.
- A WRITE message may not be sent until a READY message is received.
- Once a WRITE message is sent, an additional WRITE message may not be
- sent until another READY message has been received. Recipients of
- a WRITE message that is in violation of this requirement will CLOSE
- the connection.
- --- CLOSE(local-id, remote-id, "") -------------------------------------
- The CLOSE message informs recipient that the connection between the
- sender's stream (local-id) and the recipient's stream (remote-id) is
- broken. The remote-id MUST not be zero, but the local-id MAY be zero
- if this CLOSE indicates a failed OPEN.
- A CLOSE message containing a remote-id which does not map to an open
- stream on the recipient's side is ignored. The stream may have
- already been closed by the recipient while this message was in-flight.
- The recipient should not respond to a CLOSE message in any way. The
- recipient should cancel pending WRITEs or CLOSEs, but this is not a
- requirement, since they will be ignored.
- --- SYNC(online, sequence, "") -----------------------------------------
- The SYNC message is used by the io pump to make sure that stale
- outbound messages are discarded when the connection to the remote side
- is broken. It is only used internally to the bridge and never valid
- to send across the wire.
- * when the connection to the remote side goes offline, the io pump
- sends a SYNC(0, 0) and starts discarding all messages
- * when the connection to the remote side is established, the io pump
- sends a SYNC(1, token) and continues to discard messages
- * when the io pump receives a matching SYNC(1, token), it once again
- starts accepting messages to forward to the remote side
- --- message command constants ------------------------------------------
- #define A_SYNC 0x434e5953
- #define A_CNXN 0x4e584e43
- #define A_AUTH 0x48545541
- #define A_OPEN 0x4e45504f
- #define A_OKAY 0x59414b4f
- #define A_CLSE 0x45534c43
- #define A_WRTE 0x45545257
- --- implementation details ---------------------------------------------
- The core of the bridge program will use three threads. One thread
- will be a select/epoll loop to handle io between various inbound and
- outbound connections and the connection to the remote side.
- The remote side connection will be implemented as two threads (one for
- reading, one for writing) and a datagram socketpair to provide the
- channel between the main select/epoll thread and the remote connection
- threadpair. The reason for this is that for usb connections, the
- kernel interface on linux and osx does not allow you to do meaningful
- nonblocking IO.
- The endian swapping for the message headers will happen (as needed) in
- the remote connection threadpair and that the rest of the program will
- always treat message header values as native-endian.
- The bridge program will be able to have a number of mini-servers
- compiled in. They will be published under known names (examples
- "shell", "fs-bridge", etc) and upon receiving an OPEN() to such a
- service, the bridge program will create a stream socketpair and spawn
- a thread or subprocess to handle the io.
- --- simplified / embedded implementation -------------------------------
- For limited environments, like the bootloader, it is allowable to
- support a smaller, fixed number of channels using pre-assigned channel
- ID numbers such that only one stream may be connected to a bootloader
- endpoint at any given time. The protocol remains unchanged, but the
- "embedded" version of it is less dynamic.
- The bootloader will support two streams. A "bootloader:debug" stream,
- which may be opened to get debug messages from the bootloader and a
- "bootloader:control", stream which will support the set of basic
- bootloader commands.
- Example command stream dialogues:
- "flash_kernel,2515049,........\n" "okay\n"
- "flash_ramdisk,5038,........\n" "fail,flash write error\n"
- "bogus_command......" <CLOSE>
- --- future expansion ---------------------------------------------------
- I plan on providing either a message or a special control stream so that
- the client device could ask the host computer to setup inbound socket
- translations on the fly on behalf of the client device.
- The initial design does handshaking to provide flow control, with a
- message flow that looks like:
- >OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE
- The far side may choose to issue the READY message as soon as it receives
- a WRITE or it may defer the READY until the write to the local stream
- succeeds. A future version may want to do some level of windowing where
- multiple WRITEs may be sent without requiring individual READY acks.
- ------------------------------------------------------------------------
- --- smartsockets -------------------------------------------------------
- Port 5037 is used for smart sockets which allow a client on the host
- side to request access to a service in the host adb daemon or in the
- remote (device) daemon. The service is requested by ascii name,
- preceeded by a 4 digit hex length. Upon successful connection an
- "OKAY" response is sent, otherwise a "FAIL" message is returned. Once
- connected the client is talking to that (remote or local) service.
- client: <hex4> <service-name>
- server: "OKAY"
- client: <hex4> <service-name>
- server: "FAIL" <hex4> <reason>
|