Document Dosh Rust SDK examples
This commit is contained in:
@@ -7,6 +7,7 @@ It runs a `dosh-server` on the remote machine and a `dosh` client locally. The
|
||||
first setup can use SSH. After that, Dosh can attach over encrypted UDP with
|
||||
cached credentials, keep terminal sessions alive, reconnect after network
|
||||
changes, and forward TCP ports.
|
||||
It also includes native encrypted file copy over the Dosh stream layer.
|
||||
|
||||
## Support
|
||||
|
||||
@@ -15,8 +16,8 @@ changes, and forward TCP ports.
|
||||
- Windows: client only
|
||||
- UDP port: one configured server port, default `50000`
|
||||
|
||||
Dosh is not an SCP/SFTP client and does not implement X11 forwarding. Windows
|
||||
is client-only.
|
||||
Dosh does not implement SFTP, X11 forwarding, or a Windows server. Windows is
|
||||
client-only.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -44,6 +45,12 @@ irm https://git.palav.dev/Palav/dosh/raw/branch/main/install.ps1 | iex
|
||||
dosh setup HOST # import SSH config and trust the Dosh host key
|
||||
dosh HOST # connect
|
||||
dosh HOST COMMAND # connect and run a command
|
||||
dosh exec HOST COMMAND # run a non-interactive remote command
|
||||
dosh cp SRC DST # copy files; use host:path for a remote side
|
||||
dosh ls host:path # list a remote path
|
||||
dosh cat host:path # print a remote file
|
||||
dosh mkdir host:path # create a remote directory
|
||||
dosh rm [-r] host:path # remove a remote file or directory
|
||||
dosh update # update Dosh
|
||||
dosh status HOST # show remote tmux sessions and Dosh service status
|
||||
dosh restart HOST # restart dosh-server and show service status
|
||||
@@ -56,12 +63,74 @@ Examples:
|
||||
```sh
|
||||
dosh server
|
||||
dosh server tm
|
||||
dosh exec server 'uname -a'
|
||||
dosh cp file.txt server:tmp/file.txt
|
||||
dosh cp -r server:Projects/app ./app
|
||||
dosh cp -r server:Projects/app backup:app
|
||||
dosh ls server:Projects
|
||||
dosh cat server:tmp/file.txt
|
||||
dosh forward server -L 8080:127.0.0.1:80
|
||||
dosh forward server -D 1080
|
||||
dosh forward server -R 2222:127.0.0.1:22
|
||||
```
|
||||
|
||||
Agent forwarding is opt-in with `-A` and must be enabled on the server.
|
||||
File copy is enabled by `allow_file_transfer = true` on the server.
|
||||
|
||||
## Library
|
||||
|
||||
Dosh can also be used as a Rust transport for application protocols that need
|
||||
encrypted streams, roaming, reconnect behavior, keepalives, retransmission,
|
||||
flow control, and in-order delivery.
|
||||
|
||||
Use `dosh::client::DoshClient` and `dosh::server::DoshServer` for the complete
|
||||
native-auth path. Use `dosh::transport::DoshTransport` or `StreamMux` only when
|
||||
you already have your own session/auth layer.
|
||||
|
||||
Client-side SDK:
|
||||
|
||||
```rust
|
||||
let client = dosh::client::DoshClient::load()?;
|
||||
let mut dosh = client
|
||||
.connect("server")
|
||||
.service("myapp")
|
||||
.connect()
|
||||
.await?
|
||||
.into_transport();
|
||||
|
||||
let stream = dosh.open_service("myapp").await?;
|
||||
dosh.send(stream, b"hello").await?;
|
||||
```
|
||||
|
||||
Server-side SDK:
|
||||
|
||||
```rust
|
||||
let config = dosh::server::DoshServerConfig::default()
|
||||
.service("myapp")?;
|
||||
let mut server = dosh::server::DoshServer::bind(config).await?;
|
||||
|
||||
loop {
|
||||
match server.recv().await? {
|
||||
dosh::server::DoshServerEvent::Accepted(client) => {
|
||||
eprintln!("accepted {:?}", client.conn_id);
|
||||
}
|
||||
dosh::server::DoshServerEvent::Session { conn_id, event } => {
|
||||
if let dosh::transport::SessionEvent::Stream(
|
||||
dosh::transport::TransportEvent::Open(open)
|
||||
) = event {
|
||||
server.accept_stream(conn_id, open.stream_id).await?;
|
||||
}
|
||||
}
|
||||
dosh::server::DoshServerEvent::Ignored => {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The server runtime owns UDP reads and demuxes packets by connection id, so
|
||||
multiple clients can share one Dosh port without per-session readers racing.
|
||||
|
||||
Runnable SDK examples live in `examples/sdk_echo_server.rs` and
|
||||
`examples/sdk_echo_client.rs`.
|
||||
|
||||
## Config
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use anyhow::{Result, bail};
|
||||
use dosh::client::DoshClient;
|
||||
use dosh::transport::{SessionEvent, TransportEvent};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let mut args = std::env::args().skip(1);
|
||||
let host = args.next().unwrap_or_else(|| "server".to_string());
|
||||
let message = args.collect::<Vec<_>>().join(" ");
|
||||
let message = if message.is_empty() {
|
||||
"hello from dosh".to_string()
|
||||
} else {
|
||||
message
|
||||
};
|
||||
|
||||
let client = DoshClient::load()?;
|
||||
let mut transport = client
|
||||
.connect(host)
|
||||
.service("echo")
|
||||
.connect()
|
||||
.await?
|
||||
.into_transport();
|
||||
let stream = transport.open_service("echo").await?;
|
||||
|
||||
loop {
|
||||
match transport.recv().await? {
|
||||
SessionEvent::Stream(TransportEvent::OpenOk { stream_id, .. })
|
||||
if stream_id == stream =>
|
||||
{
|
||||
transport.send(stream, message.as_bytes().to_vec()).await?;
|
||||
}
|
||||
SessionEvent::Stream(TransportEvent::Data(data)) if data.stream_id == stream => {
|
||||
for chunk in data.chunks {
|
||||
print!("{}", String::from_utf8_lossy(&chunk));
|
||||
}
|
||||
println!();
|
||||
transport.close(stream).await?;
|
||||
return Ok(());
|
||||
}
|
||||
SessionEvent::Stream(TransportEvent::OpenReject { reason, .. }) => {
|
||||
bail!("server rejected echo stream: {reason}");
|
||||
}
|
||||
SessionEvent::Stream(_)
|
||||
| SessionEvent::Ping
|
||||
| SessionEvent::Pong
|
||||
| SessionEvent::Ignored => {}
|
||||
}
|
||||
transport.maintenance().await?;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use anyhow::Result;
|
||||
use dosh::server::{DoshServer, DoshServerConfig, DoshServerEvent};
|
||||
use dosh::transport::{SessionEvent, TransportEvent};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let config = DoshServerConfig::default().service("echo")?;
|
||||
let mut server = DoshServer::bind(config).await?;
|
||||
eprintln!("listening on {}", server.local_addr()?);
|
||||
|
||||
loop {
|
||||
match server.recv().await? {
|
||||
DoshServerEvent::Accepted(client) => {
|
||||
eprintln!(
|
||||
"accepted user={} session={} conn={:?}",
|
||||
client.user, client.session, client.conn_id
|
||||
);
|
||||
}
|
||||
DoshServerEvent::Session {
|
||||
conn_id,
|
||||
event: SessionEvent::Stream(TransportEvent::Open(open)),
|
||||
} => {
|
||||
server.accept_stream(conn_id, open.stream_id).await?;
|
||||
}
|
||||
DoshServerEvent::Session {
|
||||
conn_id,
|
||||
event: SessionEvent::Stream(TransportEvent::Data(data)),
|
||||
} => {
|
||||
for chunk in data.chunks {
|
||||
server.send(conn_id, data.stream_id, chunk).await?;
|
||||
}
|
||||
}
|
||||
DoshServerEvent::Session { .. } | DoshServerEvent::Ignored => {}
|
||||
}
|
||||
server.maintenance().await?;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user