Skip to main content

android/adb/
wire.rs

1//! Byte formats of the adb server protocol: smart-socket requests and statuses,
2//! shell protocol v2 packets, sync messages and forward and reverse listings.
3
4use std::io::{self, ErrorKind, Read};
5
6/// Longest service name a request can carry, since its length travels as four
7/// hex digits.
8pub const MAX_REQUEST_LEN: usize = 0xffff;
9
10/// Largest `DATA` chunk the sync protocol allows.
11pub const SYNC_MAX_CHUNK: usize = 64 * 1024;
12
13/// Longest remote path adbd's sync service accepts.
14pub const SYNC_MAX_PATH: usize = 1024;
15
16/// Shell protocol v2 packet ids, from adb's `shell_protocol.h`.
17pub mod shell_id {
18    pub const STDIN: u8 = 0;
19    pub const STDOUT: u8 = 1;
20    pub const STDERR: u8 = 2;
21    pub const EXIT: u8 = 3;
22    pub const CLOSE_STDIN: u8 = 4;
23}
24
25/// Regular-file type bits of `st_mode`, which sync `SEND` expects in its mode.
26const S_IFREG: u32 = 0o100000;
27
28/// Reply to a smart-socket request.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum Status {
31    Okay,
32    Fail(String),
33}
34
35fn invalid(message: String) -> io::Error {
36    io::Error::new(ErrorKind::InvalidData, message)
37}
38
39/// A request: the service name's length as four lowercase hex digits, then the
40/// name. `None` when the name is longer than [`MAX_REQUEST_LEN`].
41pub fn encode_request(service: &str) -> Option<Vec<u8>> {
42    (service.len() <= MAX_REQUEST_LEN)
43        .then(|| format!("{:04x}{service}", service.len()).into_bytes())
44}
45
46/// Reads `OKAY`, or `FAIL` and its hex-length message. Anything else is
47/// `InvalidData`.
48pub fn read_status<R: Read>(r: &mut R) -> io::Result<Status> {
49    let mut id = [0u8; 4];
50    r.read_exact(&mut id)?;
51    match &id {
52        b"OKAY" => Ok(Status::Okay),
53        b"FAIL" => {
54            let message = read_hex_block(r)?;
55            Ok(Status::Fail(String::from_utf8_lossy(&message).into_owned()))
56        }
57        other => Err(invalid(format!(
58            "expected OKAY or FAIL, got {:?}",
59            String::from_utf8_lossy(other)
60        ))),
61    }
62}
63
64/// Reads four hex digits and that many bytes.
65pub fn read_hex_block<R: Read>(r: &mut R) -> io::Result<Vec<u8>> {
66    let mut hex = [0u8; 4];
67    r.read_exact(&mut hex)?;
68    let len = std::str::from_utf8(&hex)
69        .ok()
70        .filter(|s| s.bytes().all(|b| b.is_ascii_hexdigit()))
71        .and_then(|s| usize::from_str_radix(s, 16).ok())
72        .ok_or_else(|| {
73            invalid(format!(
74                "bad hex length {:?}",
75                String::from_utf8_lossy(&hex)
76            ))
77        })?;
78    let mut body = vec![0; len];
79    r.read_exact(&mut body)?;
80    Ok(body)
81}
82
83/// One shell protocol v2 packet from the device.
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub enum ShellPacket {
86    Stdout(Vec<u8>),
87    Stderr(Vec<u8>),
88    /// The command's exit status; the last packet of a command.
89    Exit(u8),
90}
91
92/// Output of one command run through shell protocol v2.
93#[derive(Debug, Clone, Default, PartialEq, Eq)]
94pub struct ShellOutput {
95    pub stdout: Vec<u8>,
96    pub stderr: Vec<u8>,
97    /// `None` when the stream ended without an exit packet.
98    pub exit: Option<u8>,
99}
100
101impl ShellOutput {
102    /// Whether the command exited with status 0.
103    pub fn success(&self) -> bool {
104        self.exit == Some(0)
105    }
106
107    /// Standard output as text, with invalid UTF-8 replaced.
108    pub fn stdout_text(&self) -> String {
109        String::from_utf8_lossy(&self.stdout).into_owned()
110    }
111
112    /// Standard error as text, with invalid UTF-8 replaced.
113    pub fn stderr_text(&self) -> String {
114        String::from_utf8_lossy(&self.stderr).into_owned()
115    }
116}
117
118/// A host-to-device shell v2 packet: the id byte, a u32 little-endian length
119/// and the payload.
120pub fn shell_packet(id: u8, data: &[u8]) -> Vec<u8> {
121    let len = u32::try_from(data.len()).expect("a shell packet fits in u32");
122    let mut out = Vec::with_capacity(5 + data.len());
123    out.push(id);
124    out.extend_from_slice(&len.to_le_bytes());
125    out.extend_from_slice(data);
126    out
127}
128
129/// Reads the next packet. `Ok(None)` is a clean end of stream between packets;
130/// a stream that ends inside a packet is `UnexpectedEof`. Packet ids the host
131/// never receives from adbd are skipped.
132pub fn read_shell_packet<R: Read>(r: &mut R) -> io::Result<Option<ShellPacket>> {
133    loop {
134        let mut header = [0u8; 5];
135        let mut got = 0;
136        while got < header.len() {
137            match r.read(&mut header[got..]) {
138                Ok(0) if got == 0 => return Ok(None),
139                Ok(0) => {
140                    return Err(io::Error::new(
141                        ErrorKind::UnexpectedEof,
142                        "stream ended inside a shell v2 header",
143                    ));
144                }
145                Ok(n) => got += n,
146                Err(e) if e.kind() == ErrorKind::Interrupted => {}
147                Err(e) => return Err(e),
148            }
149        }
150        let len = u32::from_le_bytes([header[1], header[2], header[3], header[4]]) as usize;
151        let mut data = vec![0; len];
152        r.read_exact(&mut data)?;
153        match header[0] {
154            shell_id::STDOUT => return Ok(Some(ShellPacket::Stdout(data))),
155            shell_id::STDERR => return Ok(Some(ShellPacket::Stderr(data))),
156            shell_id::EXIT => {
157                let status = data
158                    .first()
159                    .copied()
160                    .ok_or_else(|| invalid("empty shell v2 exit packet".into()))?;
161                return Ok(Some(ShellPacket::Exit(status)));
162            }
163            _ => {}
164        }
165    }
166}
167
168/// Collects packets until the exit packet or the end of the stream.
169pub fn read_shell_output<R: Read>(r: &mut R) -> io::Result<ShellOutput> {
170    let mut out = ShellOutput::default();
171    while let Some(packet) = read_shell_packet(r)? {
172        match packet {
173            ShellPacket::Stdout(data) => out.stdout.extend_from_slice(&data),
174            ShellPacket::Stderr(data) => out.stderr.extend_from_slice(&data),
175            ShellPacket::Exit(status) => {
176                out.exit = Some(status);
177                break;
178            }
179        }
180    }
181    Ok(out)
182}
183
184/// A sync request: the four-byte id, a u32 little-endian length, the argument.
185pub fn sync_request(id: &[u8; 4], arg: &[u8]) -> Vec<u8> {
186    let len = u32::try_from(arg.len()).expect("a sync argument fits in u32");
187    let mut out = Vec::with_capacity(8 + arg.len());
188    out.extend_from_slice(id);
189    out.extend_from_slice(&len.to_le_bytes());
190    out.extend_from_slice(arg);
191    out
192}
193
194/// The argument of `SEND`: the remote path, a comma and the mode in decimal,
195/// with the regular-file type bits added to the permission bits in `mode`.
196pub fn send_arg(remote: &str, mode: u32) -> String {
197    format!("{remote},{}", S_IFREG | (mode & 0o7777))
198}
199
200/// Appends one `DATA` message to `buf`.
201///
202/// # Panics
203///
204/// When `data` is longer than [`SYNC_MAX_CHUNK`].
205pub fn push_data_chunk(buf: &mut Vec<u8>, data: &[u8]) {
206    assert!(
207        data.len() <= SYNC_MAX_CHUNK,
208        "sync DATA chunks are at most 64 KiB, got {} bytes",
209        data.len()
210    );
211    buf.extend_from_slice(b"DATA");
212    buf.extend_from_slice(&(data.len() as u32).to_le_bytes());
213    buf.extend_from_slice(data);
214}
215
216/// Appends the `DONE` message that ends a `SEND`, carrying the file's mtime.
217pub fn push_done(buf: &mut Vec<u8>, mtime: u32) {
218    buf.extend_from_slice(b"DONE");
219    buf.extend_from_slice(&mtime.to_le_bytes());
220}
221
222/// Reads the status that answers `DONE`: `Ok(Ok(()))` for `OKAY`, `Ok(Err)`
223/// with adbd's message for `FAIL`, and `InvalidData` for anything else.
224pub fn read_sync_status<R: Read>(r: &mut R) -> io::Result<Result<(), String>> {
225    let mut msg = [0u8; 8];
226    r.read_exact(&mut msg)?;
227    let len = u32::from_le_bytes([msg[4], msg[5], msg[6], msg[7]]) as usize;
228    match &msg[..4] {
229        b"OKAY" => Ok(Ok(())),
230        b"FAIL" => {
231            let mut text = vec![0; len];
232            r.read_exact(&mut text)?;
233            Ok(Err(String::from_utf8_lossy(&text).into_owned()))
234        }
235        other => Err(invalid(format!(
236            "expected a sync OKAY or FAIL, got {:?}",
237            String::from_utf8_lossy(other)
238        ))),
239    }
240}
241
242/// A forward or reverse rule, named by its two sides.
243#[derive(Debug, Clone, PartialEq, Eq)]
244pub struct Rule {
245    /// The host-side socket spec, such as `tcp:40123`.
246    pub host: String,
247    /// The device-side socket spec, such as `localabstract:mobile-agent`.
248    pub device: String,
249}
250
251/// The three fields of a listing line, or `None` for any other line.
252fn three_fields(line: &str) -> Option<[&str; 3]> {
253    let mut fields = line.split_whitespace();
254    let parsed = [fields.next()?, fields.next()?, fields.next()?];
255    fields.next().is_none().then_some(parsed)
256}
257
258/// Rules of `serial` in a `list-forward` answer, whose lines read
259/// `<serial> <host side> <device side>`.
260pub fn parse_forward_listing(listing: &str, serial: &str) -> Vec<Rule> {
261    listing
262        .lines()
263        .filter_map(three_fields)
264        .filter(|[s, _, _]| *s == serial)
265        .map(|[_, host, device]| Rule {
266            host: host.to_owned(),
267            device: device.to_owned(),
268        })
269        .collect()
270}
271
272/// Rules in adbd's `reverse:list-forward` answer, whose lines read
273/// `<transport> <device side> <host side>`.
274pub fn parse_reverse_listing(listing: &str) -> Vec<Rule> {
275    listing
276        .lines()
277        .filter_map(three_fields)
278        .map(|[_, device, host]| Rule {
279            host: host.to_owned(),
280            device: device.to_owned(),
281        })
282        .collect()
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288    use std::io::{Cursor, ErrorKind};
289
290    #[test]
291    fn requests_carry_a_four_digit_lowercase_hex_length() {
292        assert_eq!(
293            encode_request("host:devices-l").unwrap(),
294            b"000ehost:devices-l"
295        );
296        assert_eq!(
297            encode_request("host:transport:emulator-5554").unwrap(),
298            b"001chost:transport:emulator-5554"
299        );
300        assert_eq!(encode_request("").unwrap(), b"0000");
301    }
302
303    #[test]
304    fn a_request_longer_than_four_hex_digits_allow_is_refused() {
305        let longest = "x".repeat(MAX_REQUEST_LEN);
306        assert_eq!(encode_request(&longest).unwrap()[..4], *b"ffff");
307        assert_eq!(encode_request(&"x".repeat(MAX_REQUEST_LEN + 1)), None);
308    }
309
310    #[test]
311    fn okay_fail_and_garbage_statuses() {
312        assert_eq!(
313            read_status(&mut Cursor::new(b"OKAY".to_vec())).unwrap(),
314            Status::Okay
315        );
316        assert_eq!(
317            read_status(&mut Cursor::new(b"FAIL000edevice offline".to_vec())).unwrap(),
318            Status::Fail("device offline".into())
319        );
320        assert_eq!(
321            read_status(&mut Cursor::new(b"FAIL0006closed".to_vec())).unwrap(),
322            Status::Fail("closed".into())
323        );
324        let garbage = read_status(&mut Cursor::new(b"WHAT".to_vec())).unwrap_err();
325        assert_eq!(garbage.kind(), ErrorKind::InvalidData);
326        let short = read_status(&mut Cursor::new(b"OK".to_vec())).unwrap_err();
327        assert_eq!(short.kind(), ErrorKind::UnexpectedEof);
328    }
329
330    #[test]
331    fn hex_length_blocks() {
332        assert_eq!(
333            read_hex_block(&mut Cursor::new(b"0005hello".to_vec())).unwrap(),
334            b"hello"
335        );
336        assert_eq!(
337            read_hex_block(&mut Cursor::new(b"0000".to_vec())).unwrap(),
338            b""
339        );
340        assert_eq!(
341            read_hex_block(&mut Cursor::new(b"000Ahelloworld".to_vec())).unwrap(),
342            b"helloworld"
343        );
344        let bad = read_hex_block(&mut Cursor::new(b"zzzz".to_vec())).unwrap_err();
345        assert_eq!(bad.kind(), ErrorKind::InvalidData);
346        let short = read_hex_block(&mut Cursor::new(b"0005hel".to_vec())).unwrap_err();
347        assert_eq!(short.kind(), ErrorKind::UnexpectedEof);
348    }
349
350    #[test]
351    fn shell_packets_carry_the_id_and_a_little_endian_length() {
352        assert_eq!(
353            shell_packet(shell_id::STDIN, b"abc"),
354            [0, 3, 0, 0, 0, b'a', b'b', b'c']
355        );
356        assert_eq!(shell_packet(shell_id::CLOSE_STDIN, b""), [4, 0, 0, 0, 0]);
357    }
358
359    #[test]
360    fn shell_v2_separates_streams_and_keeps_the_exit_status() {
361        let bytes = [
362            shell_packet(shell_id::STDOUT, b"ok\n"),
363            shell_packet(shell_id::STDERR, b"warn"),
364            shell_packet(shell_id::STDOUT, b"more"),
365            shell_packet(shell_id::EXIT, &[7]),
366        ]
367        .concat();
368        let out = read_shell_output(&mut Cursor::new(bytes)).unwrap();
369        assert_eq!(out.stdout, b"ok\nmore");
370        assert_eq!(out.stderr, b"warn");
371        assert_eq!(out.exit, Some(7));
372        assert!(!out.success());
373    }
374
375    #[test]
376    fn packets_read_one_at_a_time_end_cleanly() {
377        let bytes = [
378            shell_packet(shell_id::STDOUT, b"a"),
379            shell_packet(shell_id::EXIT, &[0]),
380        ]
381        .concat();
382        let mut r = Cursor::new(bytes);
383        assert_eq!(
384            read_shell_packet(&mut r).unwrap(),
385            Some(ShellPacket::Stdout(b"a".to_vec()))
386        );
387        assert_eq!(
388            read_shell_packet(&mut r).unwrap(),
389            Some(ShellPacket::Exit(0))
390        );
391        assert_eq!(read_shell_packet(&mut r).unwrap(), None);
392    }
393
394    #[test]
395    fn shell_v2_without_an_exit_packet_has_no_status() {
396        let out =
397            read_shell_output(&mut Cursor::new(shell_packet(shell_id::STDOUT, b"ok"))).unwrap();
398        assert_eq!(out.stdout, b"ok");
399        assert_eq!(out.exit, None);
400        assert!(!out.success());
401    }
402
403    #[test]
404    fn a_truncated_shell_v2_packet_is_unexpected_eof() {
405        let header_only = read_shell_output(&mut Cursor::new(vec![1, 10, 0])).unwrap_err();
406        assert_eq!(header_only.kind(), ErrorKind::UnexpectedEof);
407        let short_body =
408            read_shell_output(&mut Cursor::new(vec![1, 10, 0, 0, 0, b'o'])).unwrap_err();
409        assert_eq!(short_body.kind(), ErrorKind::UnexpectedEof);
410    }
411
412    #[test]
413    fn unknown_shell_packet_ids_are_skipped() {
414        let bytes = [
415            shell_packet(5, b"24x80"),
416            shell_packet(shell_id::STDOUT, b"ok"),
417            shell_packet(shell_id::EXIT, &[0]),
418        ]
419        .concat();
420        let out = read_shell_output(&mut Cursor::new(bytes)).unwrap();
421        assert_eq!(out.stdout, b"ok");
422        assert!(out.success());
423    }
424
425    #[test]
426    fn sync_messages_use_little_endian_lengths() {
427        assert_eq!(
428            sync_request(b"SEND", b"/data/local/tmp/x,33188"),
429            [
430                b"SEND".as_slice(),
431                &23u32.to_le_bytes(),
432                b"/data/local/tmp/x,33188"
433            ]
434            .concat()
435        );
436        let mut buf = b"prefix".to_vec();
437        push_data_chunk(&mut buf, b"abc");
438        push_done(&mut buf, 0x0102_0304);
439        assert_eq!(
440            buf,
441            [
442                b"prefix".as_slice(),
443                b"DATA",
444                &3u32.to_le_bytes(),
445                b"abc",
446                b"DONE",
447                &[4, 3, 2, 1]
448            ]
449            .concat()
450        );
451    }
452
453    #[test]
454    fn a_full_chunk_is_allowed() {
455        let mut buf = Vec::new();
456        push_data_chunk(&mut buf, &vec![0; SYNC_MAX_CHUNK]);
457        assert_eq!(buf.len(), 8 + SYNC_MAX_CHUNK);
458        assert_eq!(buf[4..8], (SYNC_MAX_CHUNK as u32).to_le_bytes());
459    }
460
461    #[test]
462    #[should_panic(expected = "64 KiB")]
463    fn a_chunk_over_64_kib_is_refused() {
464        push_data_chunk(&mut Vec::new(), &vec![0; SYNC_MAX_CHUNK + 1]);
465    }
466
467    #[test]
468    fn the_send_mode_marks_a_regular_file() {
469        assert_eq!(
470            send_arg("/data/local/tmp/a.jar", 0o644),
471            "/data/local/tmp/a.jar,33188"
472        );
473        assert_eq!(
474            send_arg("/data/local/tmp/run", 0o755),
475            "/data/local/tmp/run,33261"
476        );
477        // Only permission bits are taken from the caller.
478        assert_eq!(send_arg("/x", 0o170644), "/x,33188");
479    }
480
481    #[test]
482    fn sync_status_okay_and_fail() {
483        let okay = [b"OKAY".as_slice(), &[0; 4]].concat();
484        assert_eq!(read_sync_status(&mut Cursor::new(okay)).unwrap(), Ok(()));
485        let fail = [b"FAIL".as_slice(), &6u32.to_le_bytes(), b"denied"].concat();
486        assert_eq!(
487            read_sync_status(&mut Cursor::new(fail)).unwrap(),
488            Err("denied".to_owned())
489        );
490        let other = [b"DATA".as_slice(), &[0; 4]].concat();
491        let err = read_sync_status(&mut Cursor::new(other)).unwrap_err();
492        assert_eq!(err.kind(), ErrorKind::InvalidData);
493    }
494
495    #[test]
496    fn forward_listing_keeps_this_serials_rules() {
497        let listing = "emulator-5554 tcp:40123 localabstract:mobile-agent\n\
498                       emulator-5556 tcp:40124 localabstract:other\n\
499                       emulator-5554 tcp:8081 tcp:8081\n";
500        assert_eq!(
501            parse_forward_listing(listing, "emulator-5554"),
502            [
503                Rule {
504                    host: "tcp:40123".into(),
505                    device: "localabstract:mobile-agent".into()
506                },
507                Rule {
508                    host: "tcp:8081".into(),
509                    device: "tcp:8081".into()
510                },
511            ]
512        );
513        assert!(parse_forward_listing(listing, "R58M123ABC").is_empty());
514        assert!(parse_forward_listing("", "emulator-5554").is_empty());
515    }
516
517    #[test]
518    fn reverse_listing_reads_the_device_side_first() {
519        let listing =
520            "host-16 localabstract:s6r_1 tcp:5000\nhost-16 tcp:8081 tcp:8081\nmalformed\n";
521        assert_eq!(
522            parse_reverse_listing(listing),
523            [
524                Rule {
525                    host: "tcp:5000".into(),
526                    device: "localabstract:s6r_1".into()
527                },
528                Rule {
529                    host: "tcp:8081".into(),
530                    device: "tcp:8081".into()
531                },
532            ]
533        );
534        assert!(parse_reverse_listing("").is_empty());
535    }
536}