Skip to content

Commit 688b291

Browse files
committed
Add protocol dumper example
Signed-off-by: Wiktor Kwapisiewicz <[email protected]>
1 parent 6b369db commit 688b291

File tree

3 files changed

+106
-8
lines changed

3 files changed

+106
-8
lines changed

examples/proto-dumper.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! This example illustrates a couple of features: First, it
2+
//! implements a forwarder, exposing an SSH agent socket and
3+
//! forwarding to a different one. Secondly it shows how to work with
4+
//! low-level handling of messages instead of parsed high-level
5+
//! structures.
6+
//!
7+
//! Run with
8+
//! RUST_LOG=info cargo run --example proto-dumper -- --target unix://$SSH_AUTH_SOCK -H unix:///tmp/test.sock
9+
10+
use clap::Parser;
11+
use service_binding::Binding;
12+
use ssh_agent_lib::{
13+
agent::Agent,
14+
agent::Session,
15+
async_trait,
16+
client::connect,
17+
error::AgentError,
18+
proto::{Request, Response},
19+
};
20+
use ssh_encoding::Encode;
21+
22+
struct DumpAndForward {
23+
target: Box<dyn Session>,
24+
session: u64,
25+
id: u64,
26+
}
27+
28+
#[async_trait]
29+
impl Session for DumpAndForward {
30+
async fn handle(&mut self, message: Request) -> Result<Response, AgentError> {
31+
use std::io::Write;
32+
33+
self.id += 1;
34+
let req_file = format!("req-{}-{}.bin", self.session, self.id);
35+
log::info!("Writing request {message:?} to {req_file}");
36+
37+
let mut req = std::fs::File::create(req_file)?;
38+
let mut buf = vec![];
39+
message.encode(&mut buf).map_err(AgentError::other)?;
40+
req.write_all(&buf)?;
41+
drop(req);
42+
43+
let response = self.target.handle(message).await?;
44+
45+
let resp_file = format!("resp-{}-{}.bin", self.session, self.id);
46+
log::info!("Writing response {response:?} to {resp_file}");
47+
let mut resp = std::fs::File::create(resp_file)?;
48+
let mut buf = vec![];
49+
response.encode(&mut buf).map_err(AgentError::other)?;
50+
resp.write_all(&buf)?;
51+
drop(resp);
52+
53+
Ok(response)
54+
}
55+
}
56+
57+
struct Forwarder {
58+
target: Binding,
59+
id: u64,
60+
}
61+
62+
impl Agent for Forwarder {
63+
fn new_session(&mut self) -> impl Session {
64+
self.id += 1;
65+
DumpAndForward {
66+
target: connect(self.target.clone().try_into().unwrap()).unwrap(),
67+
session: self.id,
68+
id: 0,
69+
}
70+
}
71+
}
72+
73+
#[derive(Debug, Parser)]
74+
struct Args {
75+
/// Target SSH agent to which we will proxy all requests.
76+
#[clap(long)]
77+
target: Binding,
78+
79+
/// Source that we will bind to.
80+
#[clap(long, short = 'H')]
81+
host: Binding,
82+
}
83+
84+
#[tokio::main]
85+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
86+
env_logger::init();
87+
88+
let args = Args::parse();
89+
90+
Forwarder {
91+
target: args.target,
92+
id: 0,
93+
}
94+
.bind(args.host.try_into()?)
95+
.await?;
96+
97+
Ok(())
98+
}

examples/ssh-agent-client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use ssh_agent_lib::client::connect;
55
async fn main() -> Result<(), Box<dyn std::error::Error>> {
66
#[cfg(unix)]
77
let mut client =
8-
connect(Binding::FilePath(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?).await?;
8+
connect(Binding::FilePath(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?)?;
99

1010
#[cfg(windows)]
1111
let mut client =
12-
connect(Binding::NamedPipe(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?).await?;
12+
connect(Binding::NamedPipe(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?)?;
1313

1414
eprintln!(
1515
"Identities that this agent knows of: {:#?}",

src/client.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,18 @@ where
3737
}
3838

3939
/// Wrap a stream into an SSH agent client.
40-
pub async fn connect(
40+
pub fn connect(
4141
stream: service_binding::Stream,
42-
) -> Result<std::pin::Pin<Box<dyn crate::agent::Session>>, Box<dyn std::error::Error>> {
42+
) -> Result<Box<dyn crate::agent::Session>, Box<dyn std::error::Error>> {
4343
match stream {
4444
#[cfg(unix)]
4545
service_binding::Stream::Unix(stream) => {
4646
let stream = tokio::net::UnixStream::from_std(stream)?;
47-
Ok(Box::pin(Client::new(stream)))
47+
Ok(Box::new(Client::new(stream)))
4848
}
4949
service_binding::Stream::Tcp(stream) => {
5050
let stream = tokio::net::TcpStream::from_std(stream)?;
51-
Ok(Box::pin(Client::new(stream)))
51+
Ok(Box::new(Client::new(stream)))
5252
}
5353
#[cfg(windows)]
5454
service_binding::Stream::NamedPipe(pipe) => {
@@ -65,9 +65,9 @@ pub async fn connect(
6565
Err(e) => Err(e)?,
6666
}
6767

68-
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
68+
std::thread::sleep(std::time::Duration::from_millis(50));
6969
};
70-
Ok(Box::pin(Client::new(stream)))
70+
Ok(Box::new(Client::new(stream)))
7171
}
7272
#[cfg(not(windows))]
7373
service_binding::Stream::NamedPipe(_) => Err(ProtoError::IO(std::io::Error::other(

0 commit comments

Comments
 (0)