🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-08-28 1:37:56.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-08-28 1:37:56.0 +01:00:00
commit
f40e31b0376638a7e4471b248cf37c3bc77d677b [patch]
tree
d68026169c562de5e3ace31860dfe7c099237eac
parent
d7eb7d3a1a52d7fdcbb5d23f68352dbe94786ae7
download
f40e31b0376638a7e4471b248cf37c3bc77d677b.tar.gz

Read messages off the wire from git clients



Diff

 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<std::net::SocketAddr>) -> Self {
        self.clone()
    type Handler = Handler;

    fn new(&mut self, _: Option<std::net::SocketAddr>) -> 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<Result<(Self, server::Auth), anyhow::Error>>;
    type FutureUnit = futures::future::Ready<Result<(Self, Session), anyhow::Error>>;
@@ -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<Option<Self::Item>, 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";