From f40e31b0376638a7e4471b248cf37c3bc77d677b Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Sat, 28 Aug 2021 01:37:56 +0100 Subject: [PATCH] Read messages off the wire from git clients --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/main.rs | 27 ++++++++++++++++++++++++++- src/git/codec.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/git/mod.rs | 2 ++ 5 files changed, 95 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3ec7e1..656b3c2 100644 --- a/Cargo.lock +++ a/Cargo.lock @@ -189,11 +189,13 @@ "anyhow", "async-trait", "axum", + "bytes", "env_logger", "futures", "thrussh", "thrussh-keys", "tokio", + "tokio-util", "tower", "tower-http", ] diff --git a/Cargo.toml b/Cargo.toml index ebde3e0..76ddaef 100644 --- a/Cargo.toml +++ a/Cargo.toml @@ -17,3 +17,5 @@ thrussh-keys = "0.21" anyhow = "1" env_logger = "0.9" +tokio-util = { version = "0.6", features = ["codec"] } +bytes = "1" diff --git a/src/main.rs b/src/main.rs index 7782588..bb8bf81 100644 --- a/src/main.rs +++ a/src/main.rs @@ -7,6 +7,9 @@ use thrussh::server::{Auth, Session}; use thrussh::*; use thrussh_keys::*; +use bytes::BytesMut; +use crate::git::codec::GitCodec; +use tokio_util::codec::Decoder; #[tokio::main] async fn main() { @@ -26,13 +29,20 @@ struct Server; impl server::Server for Server { - type Handler = Self; - fn new(&mut self, _: Option) -> Self { - self.clone() + type Handler = Handler; + + fn new(&mut self, _: Option) -> Self::Handler { + Handler::default() } +} + +#[derive(Default)] +struct Handler { + codec: GitCodec, + input_bytes: BytesMut, } -impl server::Handler for Server { +impl server::Handler for Handler { type Error = anyhow::Error; type FutureAuth = futures::future::Ready>; type FutureUnit = futures::future::Ready>; @@ -101,9 +111,14 @@ eprintln!("finished auth pubkey"); self.finished_auth(server::Auth::Accept) } + + fn data(mut self, _channel: ChannelId, data: &[u8], session: Session) -> Self::FutureUnit { + self.input_bytes.extend_from_slice(data); + + while let Some(frame) = self.codec.decode(&mut self.input_bytes).unwrap() { + eprintln!("data: {:x?}", frame); + } - fn data(self, _channel: ChannelId, data: &[u8], session: Session) -> Self::FutureUnit { - eprintln!("got data: {:x?}", data); futures::future::ready(Ok((self, session))) } diff --git a/src/git/codec.rs b/src/git/codec.rs new file mode 100644 index 0000000..42e27b6 100644 --- /dev/null +++ a/src/git/codec.rs @@ -1,0 +1,68 @@ +use tokio_util::codec; +use bytes::{Bytes, Buf}; + +#[derive(Default)] +pub struct GitCodec; + +impl codec::Decoder for GitCodec { + type Item = Bytes; + type Error = anyhow::Error; + + fn decode(&mut self, src: &mut bytes::BytesMut) -> Result, Self::Error> { + if src.len() < 4 { + return Ok(None); + } + + let mut length_bytes = [0u8; 4]; + length_bytes.copy_from_slice(&src[..4]); + let length = u16::from_str_radix(std::str::from_utf8(&length_bytes)?, 16)? as usize; + + if length == 0 // flush-pkt + || length == 1 // delim-pkt + || length == 2 // response-end-pkt + { + src.advance(4); + return self.decode(src); + } + + if length > 65520 || length <= 4 { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "protocol abuse").into()); + } + + if src.len() < length { + src.reserve(length - src.len()); + return Ok(None); + } + + let mut bytes = src.split_to(length); + bytes.advance(4); + + if bytes.ends_with(b"\n") { + bytes.truncate(bytes.len() - 1); + } + + Ok(Some(bytes.freeze())) + } +} + +#[cfg(test)] +mod test { + use tokio_util::codec::Decoder; + use bytes::BytesMut; + use std::fmt::Write; + + #[test] + fn decode() { + let mut codec = super::GitCodec; + + let mut bytes = BytesMut::new(); + + bytes.write_str("0015agent=git/2.32.0").unwrap(); + let res = codec.decode(&mut bytes).unwrap(); + assert_eq!(res, None); + + bytes.write_char('\n').unwrap(); + let res = codec.decode(&mut bytes).unwrap(); + assert_eq!(res.as_deref(), Some("agent=git/2.32.0".as_bytes())); + } +} diff --git a/src/git/mod.rs b/src/git/mod.rs index f464cbf..7e366b3 100644 --- a/src/git/mod.rs +++ a/src/git/mod.rs @@ -1,3 +1,5 @@ +pub mod codec; + use thrussh::CryptoVec; pub const END_OF_MESSAGE: &'static [u8] = b"0000"; -- rgit 0.1.3