gdb_protocol/
io.rs

1use crate::{
2    packet::{CheckedPacket, Kind},
3    parser::Parser,
4    Error,
5};
6
7use std::{
8    io::{prelude::*, BufReader},
9    mem,
10    net::{TcpListener, TcpStream, ToSocketAddrs},
11};
12
13pub const BUF_SIZE: usize = 8 * 1024;
14
15pub struct GdbServer<R, W>
16where
17    R: BufRead,
18    W: Write,
19{
20    pub reader: R,
21    pub writer: W,
22    parser: Parser,
23}
24
25impl GdbServer<BufReader<TcpStream>, TcpStream> {
26    pub fn listen<A>(addr: A) -> Result<Self, Error>
27    where
28        A: ToSocketAddrs,
29    {
30        let listener = TcpListener::bind(addr)?;
31
32        let (writer, _addr) = listener.accept()?;
33        let reader = BufReader::new(writer.try_clone()?);
34
35        Ok(Self::new(reader, writer))
36    }
37}
38impl<'a> GdbServer<&'a mut &'a [u8], Vec<u8>> {
39    pub fn tester(input: &'a mut &'a [u8]) -> Self {
40        Self::new(input, Vec::new())
41    }
42    pub fn response(&mut self) -> Vec<u8> {
43        mem::replace(&mut self.writer, Vec::new())
44    }
45}
46impl<R, W> GdbServer<R, W>
47where
48    R: BufRead,
49    W: Write,
50{
51    pub fn new(reader: R, writer: W) -> Self {
52        Self {
53            reader,
54            writer,
55            parser: Parser::default(),
56        }
57    }
58
59    pub fn next_packet(&mut self) -> Result<Option<CheckedPacket>, Error> {
60        loop {
61            let buf = self.reader.fill_buf()?;
62            if buf.is_empty() {
63                break Ok(None);
64            }
65
66            // println!("{:?}", std::str::from_utf8(buf));
67            let (read, packet) = self.parser.feed(buf)?;
68            self.reader.consume(read);
69
70            if let Some(packet) = packet {
71                break Ok(match packet.kind {
72                    Kind::Packet => match packet.check() {
73                        Some(checked) => {
74                            self.writer.write_all(&[b'+'])?;
75                            Some(checked)
76                        }
77                        None => {
78                            self.writer.write_all(&[b'-'])?;
79                            continue; // Retry
80                        }
81                    },
82                    // Protocol specifies notifications should not be checked
83                    Kind::Notification => packet.check(),
84                });
85            }
86        }
87    }
88    /// Sends a packet, retrying upon any failed checksum verification
89    /// on the remote.
90    pub fn dispatch(&mut self, packet: &CheckedPacket) -> Result<(), Error> {
91        loop {
92            packet.encode(&mut self.writer)?;
93            self.writer.flush()?;
94
95            // TCP guarantees the order of packets, so theoretically
96            // '+' or '-' will always be sent directly after a packet
97            // is received.
98            let buf = self.reader.fill_buf()?;
99            match buf.first() {
100                Some(b'+') => {
101                    self.reader.consume(1);
102                    break;
103                },
104                Some(b'-') => {
105                    self.reader.consume(1);
106                    if packet.is_valid() {
107                        // Well, ok, not our fault. The packet is
108                        // definitely valid, let's re-try
109                        continue;
110                    } else {
111                        // Oh... so the user actually tried to send a
112                        // packet with an invalid checksum. It's very
113                        // possible that they know what they're doing
114                        // though, perhaps they thought they disabled
115                        // the checksum verification. So let's not
116                        // panic.
117                        return Err(Error::InvalidChecksum);
118                    }
119                },
120                // Never mind... Just... hope for the best?
121                _ => break,
122            }
123        }
124        Ok(())
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use crate::packet::UncheckedPacket;
132
133    #[test]
134    fn it_acknowledges_valid_packets() {
135        let mut input: &[u8] = b"$packet#78";
136        let mut tester = GdbServer::tester(&mut input);
137        assert_eq!(
138            tester.next_packet().unwrap(),
139            Some(CheckedPacket::from_data(Kind::Packet, b"packet".to_vec()))
140        );
141        assert_eq!(tester.response(), b"+");
142    }
143    #[test]
144    fn it_acknowledges_invalid_packets() {
145        let mut input: &[u8] = b"$packet#99";
146        let mut tester = GdbServer::tester(&mut input);
147        assert_eq!(tester.next_packet().unwrap(), None);
148        assert_eq!(tester.response(), b"-");
149    }
150    #[test]
151    fn it_ignores_garbage() {
152        let mut input: &[u8] =
153            b"<garbage here yada yaya> $packet#13 $packet#37 more garbage $GARBA#GE-- $packet#78";
154        let mut tester = GdbServer::tester(&mut input);
155        assert_eq!(
156            tester.next_packet().unwrap(),
157            Some(CheckedPacket::from_data(Kind::Packet, b"packet".to_vec()))
158        );
159        assert_eq!(tester.response(), b"---+");
160    }
161    #[test]
162    fn it_dispatches() {
163        let mut input: &[u8] = b"";
164        let mut tester = GdbServer::tester(&mut input);
165        tester.dispatch(&CheckedPacket::from_data(Kind::Packet, b"hOi!!".to_vec())).unwrap();
166        assert_eq!(tester.response(), b"$hOi!!#62");
167    }
168    #[test]
169    fn it_resends() {
170        let mut input: &[u8] = b"-+";
171        let mut tester = GdbServer::tester(&mut input);
172        tester.dispatch(&CheckedPacket::from_data(Kind::Packet, b"IMBATMAN".to_vec())).unwrap();
173        assert_eq!(tester.response(), b"$IMBATMAN#49$IMBATMAN#49");
174    }
175    #[test]
176    fn it_complains_when_the_user_lies() {
177        let mut input: &[u8] = b"-";
178        let mut tester = GdbServer::tester(&mut input);
179        let result = tester.dispatch(&CheckedPacket::assume_checked(UncheckedPacket {
180            kind: Kind::Packet,
181            data: b"This sentence is false. (dontthinkaboutitdontthinkaboutit)".to_vec(),
182            checksum: *b"FF",
183        }));
184        if let Err(Error::InvalidChecksum) = result {
185        } else {
186            panic!("Expected error InvalidChecksum, got {:?}", result);
187        }
188        // It will still send once, just in case the user has disabled checksum verification
189        assert_eq!(tester.response(), b"$This sentence is false. (dontthinkaboutitdontthinkaboutit)#FF".to_vec());
190    }
191}