aboutsummaryrefslogtreecommitdiff
path: root/src/protocol/packet.rs
blob: 468f38a88809cce6d1eefb1c413e3ff81c4318e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use crate::protocol::commands::Command;
use crate::protocol::common::hex::decode_hex;
use crate::target::Target;

/// Packet parse error.
#[derive(Debug)]
pub enum PacketParseError {
    ChecksumMismatched { checksum: u8, calculated: u8 },
    EmptyBuf,
    MissingChecksum,
    MalformedChecksum,
    MalformedCommand,
    UnexpectedHeader(u8),
}

/// Top-Level GDB packet
pub enum Packet<'a> {
    Ack,
    Nack,
    Interrupt,
    Command(Command<'a>),
}

/// Wrapper around a byte buffer containing a GDB packet, while also tracking
/// the range of the buffer containing the packet's "body".
///
/// A newly constructed `PacketBuf` will have a body that spans the entire data
/// portion of the packet (i.e: `b"$data#checksum"`), but this range can be
/// further restricted as part of packet parsing.
///
/// Notably, `PacketBuf` will _always_ maintain a mutable reference back to the
/// _entire_ underlying packet buffer. This makes it possible to re-use any
/// unused buffer space as "scratch" space. One notable example of this use-case
/// is the 'm' packet, which recycles unused packet buffer space as a buffer for
/// the target's `read_memory` method.
pub struct PacketBuf<'a> {
    buf: &'a mut [u8],
    body_range: core::ops::Range<usize>,
}

impl<'a> PacketBuf<'a> {
    /// Validate the contents of the raw packet buffer, checking for checksum
    /// consistency and structural correctness.
    pub fn new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
        if pkt_buf.is_empty() {
            return Err(PacketParseError::EmptyBuf);
        }

        // split buffer into body and checksum components
        let mut parts = pkt_buf[1..].split(|b| *b == b'#');

        let body = parts.next().unwrap(); // spit iter always returns at least one element
        let checksum = parts
            .next()
            .ok_or(PacketParseError::MissingChecksum)?
            .get(..2)
            .ok_or(PacketParseError::MalformedChecksum)?;

        // validate the checksum
        let checksum = decode_hex(checksum).map_err(|_| PacketParseError::MalformedChecksum)?;
        let calculated = body.iter().fold(0u8, |a, x| a.wrapping_add(*x));
        if calculated != checksum {
            return Err(PacketParseError::ChecksumMismatched {
                checksum,
                calculated,
            });
        }

        let body_range = 1..(body.len() + 1); // compensate for the leading '$'

        Ok(PacketBuf {
            buf: pkt_buf,
            body_range,
        })
    }

    /// (used for tests) Create a packet buffer from a raw body buffer, skipping
    /// the header/checksum trimming stage.
    #[cfg(test)]
    pub fn new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
        let len = body.len();
        Ok(PacketBuf {
            buf: body,
            body_range: 0..len,
        })
    }

    /// Strip the specified prefix from the packet buffer, returning `true` if
    /// there was a prefix match.
    pub fn strip_prefix(&mut self, prefix: &[u8]) -> bool {
        let body = {
            // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range`
            // always stays within the bounds of the provided buffer.
            #[cfg(not(feature = "paranoid_unsafe"))]
            unsafe {
                self.buf.get_unchecked_mut(self.body_range.clone())
            }

            #[cfg(feature = "paranoid_unsafe")]
            &mut self.buf[self.body_range.clone()]
        };

        if body.starts_with(prefix) {
            // SAFETY: if the current buffer range `starts_with` the specified prefix, then
            // it is safe to bump `body_range.start` by the prefix length.
            self.body_range = (self.body_range.start + prefix.len())..self.body_range.end;
            true
        } else {
            false
        }
    }

    /// Return a mutable reference to slice of the packet buffer corresponding
    /// to the current body.
    pub fn into_body(self) -> &'a mut [u8] {
        // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range`
        // always stays within the bounds of the provided buffer.
        #[cfg(not(feature = "paranoid_unsafe"))]
        unsafe {
            self.buf.get_unchecked_mut(self.body_range)
        }

        #[cfg(feature = "paranoid_unsafe")]
        &mut self.buf[self.body_range]
    }

    /// Return a mutable reference to the _entire_ underlying packet buffer, and
    /// the current body's range.
    pub fn into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>) {
        (self.buf, self.body_range)
    }

    /// Returns the length of the _entire_ underlying packet buffer - not just
    /// the length of the current range.
    ///
    /// This method is used when handing the `qSupported` packet in order to
    /// obtain the maximum packet size the stub supports.
    pub fn full_len(&self) -> usize {
        self.buf.len()
    }
}

impl<'a> Packet<'a> {
    pub fn from_buf(
        target: &mut impl Target,
        buf: &'a mut [u8],
    ) -> Result<Packet<'a>, PacketParseError> {
        // cannot have empty packet
        if buf.is_empty() {
            return Err(PacketParseError::EmptyBuf);
        }

        match buf[0] {
            b'$' => Ok(Packet::Command(
                Command::from_packet(target, PacketBuf::new(buf)?)
                    .ok_or(PacketParseError::MalformedCommand)?,
            )),
            b'+' => Ok(Packet::Ack),
            b'-' => Ok(Packet::Nack),
            0x03 => Ok(Packet::Interrupt),
            _ => Err(PacketParseError::UnexpectedHeader(buf[0])),
        }
    }
}