encoder
Diff
src/main.rs | 212 +++++++++++++++++++++++++++++++++++++++++++-------------------------------------
src/git/codec.rs | 26 +++++++++++++++++++++++---
src/git/mod.rs | 39 ++++++++++++++++++++++-----------------
src/git/packfile.rs | 15 ++++++++-------
4 files changed, 126 insertions(+), 166 deletions(-)
@@ -1,15 +1,16 @@
pub mod git;
use crate::git::PktLine;
use std::sync::{Arc};
use crate::git::codec::GitCodec;
use bytes::BytesMut;
use futures::future::Future;
use git::codec::Encoder;
use std::{fmt::Write, pin::Pin, sync::Arc};
use thrussh::server::{Auth, Session};
use thrussh::*;
use thrussh_keys::*;
use bytes::BytesMut;
use crate::git::codec::GitCodec;
use tokio_util::codec::Decoder;
use tokio_util::codec::{Decoder, Encoder as TokioEncoder};
#[tokio::main]
async fn main() {
@@ -40,12 +41,26 @@
struct Handler {
codec: GitCodec,
input_bytes: BytesMut,
output_bytes: BytesMut,
}
impl Handler {
fn write(&mut self, packet: PktLine<'_>) -> Result<(), anyhow::Error> {
Encoder {}.encode(packet, &mut self.output_bytes)
}
fn flush(&mut self, session: &mut Session, channel: ChannelId) {
session.data(
channel,
CryptoVec::from_slice(self.output_bytes.split().as_ref()),
)
}
}
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>>;
type FutureUnit = Pin<Box<dyn Future<Output = Result<(Self, Session), Self::Error>> + Send>>;
type FutureBool = futures::future::Ready<Result<(Self, Session, bool), anyhow::Error>>;
fn finished_auth(self, auth: Auth) -> Self::FutureAuth {
@@ -60,41 +75,49 @@
fn finished(self, s: Session) -> Self::FutureUnit {
eprintln!("finished");
futures::future::ready(Ok((self, s)))
Box::pin(futures::future::ready(Ok((self, s))))
}
fn shell_request(self, channel: ChannelId, mut session: Session) -> Self::FutureUnit {
session.data(channel, CryptoVec::from_slice(b"Hi there! You've successfully authenticated, but chartered does not provide shell access.\r\n"));
session.close(channel);
futures::future::ready(Ok((self, session)))
fn shell_request(mut self, channel: ChannelId, mut session: Session) -> Self::FutureUnit {
Box::pin(async move {
write!(&mut self.output_bytes, "Hi there! You've successfully authenticated, but chartered does not provide shell access.\r\n")?;
self.flush(&mut session, channel);
session.close(channel);
Ok((self, session))
})
}
fn exec_request(
self,
mut self,
channel: ChannelId,
data: &[u8],
mut session: Session,
) -> Self::FutureUnit {
eprintln!("exec {:x?}", data);
if !data.starts_with(b"git-upload-pack ") {
session.data(
channel,
CryptoVec::from_slice(b"Sorry, I have no clue who you are\r\n"),
);
session.close(channel);
} else {
session.data(channel, PktLine::Data(b"version 2\n").into());
session.data(channel, PktLine::Data(b"agent=chartered/0.1.0\n").into());
session.data(channel, PktLine::Data(b"ls-refs=unborn\n").into());
session.data(channel, PktLine::Data(b"fetch=shallow wait-for-done\n").into());
session.data(channel, PktLine::Data(b"server-option\n").into());
session.data(channel, PktLine::Data(b"object-info\n").into());
session.data(channel, PktLine::Flush.into());
}
futures::future::ready(Ok((self, session)))
let git_upload_pack = data.starts_with(b"git-upload-pack ");
Box::pin(async move {
if git_upload_pack {
self.write(PktLine::Data(b"version 2\n"))?;
self.write(PktLine::Data(b"agent=chartered/0.1.0\n"))?;
self.write(PktLine::Data(b"ls-refs=unborn\n"))?;
self.write(PktLine::Data(b"fetch=shallow wait-for-done\n"))?;
self.write(PktLine::Data(b"server-option\n"))?;
self.write(PktLine::Data(b"object-info\n"))?;
self.write(PktLine::Flush)?;
self.flush(&mut session, channel);
} else {
session.data(
channel,
CryptoVec::from_slice(b"Sorry, I have no clue who you are\r\n"),
);
session.close(channel);
}
Ok((self, session))
})
}
fn subsystem_request(
@@ -104,7 +127,7 @@
session: Session,
) -> Self::FutureUnit {
eprintln!("subsystem req: {}", data);
futures::future::ready(Ok((self, session)))
Box::pin(futures::future::ready(Ok((self, session))))
}
fn auth_publickey(self, _: &str, _: &key::PublicKey) -> Self::FutureAuth {
@@ -115,115 +138,32 @@
fn data(mut self, channel: ChannelId, data: &[u8], mut session: Session) -> Self::FutureUnit {
self.input_bytes.extend_from_slice(data);
let mut ls_refs = false;
Box::pin(async move {
let mut ls_refs = false;
while let Some(frame) = self.codec.decode(&mut self.input_bytes).unwrap() {
eprintln!("data: {:x?}", frame);
while let Some(frame) = self.codec.decode(&mut self.input_bytes)? {
eprintln!("data: {:x?}", frame);
if frame.as_ref() == "command=ls-refs".as_bytes() {
ls_refs = true;
if frame.as_ref() == "command=ls-refs".as_bytes() {
ls_refs = true;
}
}
}
if ls_refs {
session.data(channel, PktLine::Data(b"1a1b25ae7c87a0e87b7a9aa478a6bc4331c6b954 HEAD symref-target:refs/heads/master\n").into());
session.data(channel, PktLine::Flush.into());
}
futures::future::ready(Ok((self, session)))
}
fn extended_data(
self,
_channel: ChannelId,
code: u32,
data: &[u8],
session: Session,
) -> Self::FutureUnit {
eprintln!("got extended data: {:x?} ({})", data, code);
futures::future::ready(Ok((self, session)))
}
fn signal(self, _channel: ChannelId, signal_name: Sig, session: Session) -> Self::FutureUnit {
eprintln!("signal: {:#?}", signal_name);
futures::future::ready(Ok((self, session)))
}
fn window_change_request(
self,
_channel: ChannelId,
_col_width: u32,
_row_height: u32,
_pix_width: u32,
_pix_height: u32,
session: Session,
) -> Self::FutureUnit {
eprintln!("window change req");
futures::future::ready(Ok((self, session)))
}
fn env_request(
self,
_channel: ChannelId,
variable_name: &str,
variable_value: &str,
session: Session,
) -> Self::FutureUnit {
eprintln!("set env {} = {}", variable_name, variable_value);
futures::future::ready(Ok((self, session)))
}
fn x11_request(
self,
_channel: ChannelId,
_single_connection: bool,
_x11_auth_protocol: &str,
_x11_auth_cookie: &str,
_x11_screen_number: u32,
session: Session,
) -> Self::FutureUnit {
eprintln!("x11 req");
futures::future::ready(Ok((self, session)))
}
fn pty_request(
self,
_channel: ChannelId,
_term: &str,
_col_width: u32,
_row_height: u32,
_pix_width: u32,
_pix_height: u32,
_modes: &[(Pty, u32)],
session: Session,
) -> Self::FutureUnit {
eprintln!("pty req");
futures::future::ready(Ok((self, session)))
}
fn channel_open_direct_tcpip(
self,
_channel: ChannelId,
_host_to_connect: &str,
_port_to_connect: u32,
_originator_address: &str,
_originator_port: u32,
session: Session,
) -> Self::FutureUnit {
eprintln!("direct tcpip");
futures::future::ready(Ok((self, session)))
}
if ls_refs {
self.write(PktLine::Data(b"1a1b25ae7c87a0e87b7a9aa478a6bc4331c6b954 HEAD symref-target:refs/heads/master\n"))?;
self.write(PktLine::Flush)?;
self.flush(&mut session, channel);
}
fn channel_eof(self, _channel: ChannelId, session: Session) -> Self::FutureUnit {
eprintln!("eof");
futures::future::ready(Ok((self, session)))
Ok((self, session))
})
}
}
@@ -1,6 +1,21 @@
use bytes::{Buf, Bytes, BytesMut};
use tokio_util::codec;
use bytes::{Bytes, Buf};
use super::PktLine;
pub struct Encoder {
}
impl codec::Encoder<PktLine<'_>> for Encoder {
type Error = anyhow::Error;
fn encode(&mut self, item: PktLine<'_>, dst: &mut BytesMut) -> Result<(), Self::Error> {
item.encode_to(dst)?;
Ok(())
}
}
#[derive(Default)]
pub struct GitCodec;
@@ -19,7 +34,8 @@
if length == 0
|| length == 1
|| length == 2
|| length == 2
{
eprintln!("pkt: {}", length);
src.advance(4);
@@ -27,7 +43,9 @@
}
if length > 65520 || length < 4 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "protocol abuse").into());
return Err(
std::io::Error::new(std::io::ErrorKind::InvalidData, "protocol abuse").into(),
);
}
if src.len() < length {
@@ -48,9 +66,9 @@
#[cfg(test)]
mod test {
use tokio_util::codec::Decoder;
use bytes::BytesMut;
use std::fmt::Write;
use tokio_util::codec::Decoder;
#[test]
fn decode() {
@@ -1,7 +1,8 @@
pub mod codec;
pub mod packfile;
use thrussh::CryptoVec;
use bytes::BytesMut;
use std::fmt::Write;
pub enum PktLine<'a> {
Data(&'a [u8]),
@@ -11,29 +12,26 @@
}
impl PktLine<'_> {
pub fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
pub fn encode_to(&self, buf: &mut BytesMut) -> Result<(), anyhow::Error> {
match self {
Self::Data(data) => {
v.extend_from_slice(format!("{:04x}", data.len() + 4).as_ref());
v.extend_from_slice(data);
},
Self::Flush => v.extend_from_slice(b"0000"),
Self::Delimiter => v.extend_from_slice(b"0001"),
Self::ResponseEnd => v.extend_from_slice(b"0002"),
write!(buf, "{:04x}", data.len() + 4)?;
buf.extend_from_slice(&data);
}
Self::Flush => buf.extend_from_slice(b"0000"),
Self::Delimiter => buf.extend_from_slice(b"0001"),
Self::ResponseEnd => buf.extend_from_slice(b"0002"),
}
v
Ok(())
}
}
impl From<PktLine<'_>> for CryptoVec {
fn from(val: PktLine<'_>) -> Self {
Self::from(val.encode())
}
}
impl<'a> From<&'a str> for PktLine<'a> {
fn from(val: &'a str) -> Self {
@@ -43,9 +41,12 @@
#[cfg(test)]
mod test {
use bytes::BytesMut;
#[test]
fn test_pkt_line() {
let encoded = super::PktLine(b"agent=git/2.32.0\n").encode();
assert_eq!(encoded, b"0015agent=git/2.32.0\n");
let mut buffer = BytesMut::new();
super::PktLine::Data(b"agent=git/2.32.0\n").encode_to(&mut buffer).unwrap();
assert_eq!(buffer.as_ref(), b"0015agent=git/2.32.0\n");
}
}
@@ -5,17 +5,18 @@
pub struct PackFileIndex<const S: usize> {
pub magic: [u8; 4],
pub struct PackFileIndex<const S: usize> {
pub magic: [u8; 4],
pub version: [u8; 4],
pub fanout: [[u8; 4]; 255],
pub size: u16,
pub sha1: [[u8; 20]; S],
pub crc: [[u8; 4]; S],
pub size: u16,
pub sha1: [[u8; 20]; S],
pub crc: [[u8; 4]; S],
pub offset: [[u8; 4]; S],
pub packfile_checksum: [u8; 20],
pub idxfiel_checksum: [u8; 20],
pub idxfiel_checksum: [u8; 20],
}
@@ -23,4 +24,4 @@