Skip to content

Commit 1b556c8

Browse files
authored
Merge pull request #65 from wiktor-k/wiktor/add-proto-dumper
Add protocol dumper example
2 parents 73fcf48 + 181d7ce commit 1b556c8

File tree

4 files changed

+137
-13
lines changed

4 files changed

+137
-13
lines changed

examples/pgp-wrapper.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
//!
3434
//! Works perfectly in conjunction with `openpgp-card-agent.rs`!
3535
36-
use std::{cell::RefCell, pin::Pin};
36+
use std::cell::RefCell;
3737

3838
use chrono::DateTime;
3939
use clap::Parser;
@@ -55,11 +55,11 @@ use tokio::runtime::Runtime;
5555
struct WrappedKey {
5656
public_key: PublicKey,
5757
pubkey: KeyData,
58-
client: RefCell<Pin<Box<dyn Session>>>,
58+
client: RefCell<Box<dyn Session>>,
5959
}
6060

6161
impl WrappedKey {
62-
fn new(pubkey: KeyData, client: Pin<Box<dyn Session>>) -> Self {
62+
fn new(pubkey: KeyData, client: Box<dyn Session>) -> Self {
6363
let KeyData::Ed25519(key) = pubkey.clone() else {
6464
panic!("The first key was not ed25519!");
6565
};
@@ -206,11 +206,11 @@ fn main() -> testresult::TestResult {
206206
let (client, identities) = rt.block_on(async move {
207207
#[cfg(unix)]
208208
let mut client =
209-
connect(Binding::FilePath(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?).await?;
209+
connect(Binding::FilePath(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?)?;
210210

211211
#[cfg(windows)]
212212
let mut client =
213-
connect(Binding::NamedPipe(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?).await?;
213+
connect(Binding::NamedPipe(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?)?;
214214

215215
let identities = client.request_identities().await?;
216216

examples/proto-dumper.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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::bind,
14+
agent::Agent,
15+
agent::Session,
16+
async_trait,
17+
client::connect,
18+
error::AgentError,
19+
proto::{Request, Response},
20+
};
21+
use ssh_encoding::Encode;
22+
23+
struct DumpAndForward {
24+
target: Box<dyn Session>,
25+
session: u64,
26+
id: u64,
27+
}
28+
29+
#[async_trait]
30+
impl Session for DumpAndForward {
31+
async fn handle(&mut self, message: Request) -> Result<Response, AgentError> {
32+
use std::io::Write;
33+
34+
self.id += 1;
35+
let req_file = format!("req-{}-{}.bin", self.session, self.id);
36+
log::info!("Writing request {message:?} to {req_file}");
37+
38+
let mut req = std::fs::File::create(req_file)?;
39+
let mut buf = vec![];
40+
message.encode(&mut buf).map_err(AgentError::other)?;
41+
req.write_all(&buf)?;
42+
drop(req);
43+
44+
let response = self.target.handle(message).await?;
45+
46+
let resp_file = format!("resp-{}-{}.bin", self.session, self.id);
47+
log::info!("Writing response {response:?} to {resp_file}");
48+
let mut resp = std::fs::File::create(resp_file)?;
49+
let mut buf = vec![];
50+
response.encode(&mut buf).map_err(AgentError::other)?;
51+
resp.write_all(&buf)?;
52+
drop(resp);
53+
54+
Ok(response)
55+
}
56+
}
57+
58+
struct Forwarder {
59+
target: Binding,
60+
id: u64,
61+
}
62+
63+
#[cfg(unix)]
64+
impl Agent<tokio::net::UnixListener> for Forwarder {
65+
fn new_session(&mut self, _socket: &tokio::net::UnixStream) -> impl Session {
66+
self.create_new_session()
67+
}
68+
}
69+
70+
impl Agent<tokio::net::TcpListener> for Forwarder {
71+
fn new_session(&mut self, _socket: &tokio::net::TcpStream) -> impl Session {
72+
self.create_new_session()
73+
}
74+
}
75+
76+
#[cfg(windows)]
77+
impl Agent<ssh_agent_lib::agent::NamedPipeListener> for Forwarder {
78+
fn new_session(
79+
&mut self,
80+
_socket: &tokio::net::windows::named_pipe::NamedPipeServer,
81+
) -> impl Session {
82+
self.create_new_session()
83+
}
84+
}
85+
86+
impl Forwarder {
87+
fn create_new_session(&mut self) -> impl Session {
88+
self.id += 1;
89+
DumpAndForward {
90+
target: connect(self.target.clone().try_into().unwrap()).unwrap(),
91+
session: self.id,
92+
id: 0,
93+
}
94+
}
95+
}
96+
97+
#[derive(Debug, Parser)]
98+
struct Args {
99+
/// Target SSH agent to which we will proxy all requests.
100+
#[clap(long)]
101+
target: Binding,
102+
103+
/// Source that we will bind to.
104+
#[clap(long, short = 'H')]
105+
host: Binding,
106+
}
107+
108+
#[tokio::main]
109+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
110+
env_logger::init();
111+
112+
let args = Args::parse();
113+
114+
bind(
115+
args.host.try_into()?,
116+
Forwarder {
117+
target: args.target,
118+
id: 0,
119+
},
120+
)
121+
.await?;
122+
123+
Ok(())
124+
}

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)