initial commit
This commit is contained in:
commit
b7c689e1ec
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
||||
196
Cargo.lock
generated
Normal file
196
Cargo.lock
generated
Normal file
@ -0,0 +1,196 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remote-std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ssh2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "ssh2"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7fe461910559f6d5604c3731d00d2aafc4a83d1665922e280f42f9a168d5455"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libssh2-sys",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "remote-std"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.22"
|
||||
ssh2 = "0.9.4"
|
||||
16
src/fs/mod.rs
Normal file
16
src/fs/mod.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use crate::{Connection, Result};
|
||||
use std::{io::Read, path::Path};
|
||||
|
||||
pub struct File {}
|
||||
|
||||
impl File {
|
||||
pub fn open<P: AsRef<Path>>(con: &Connection, path: P) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for File {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
160
src/lib.rs
Normal file
160
src/lib.rs
Normal file
@ -0,0 +1,160 @@
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
net::TcpStream,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
pub mod fs;
|
||||
|
||||
pub struct Connection {
|
||||
pub(crate) session: ssh2::Session,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
IO(io::Error),
|
||||
Ssh(ssh2::Error),
|
||||
AuthenticationFailure,
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::IO(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssh2::Error> for Error {
|
||||
fn from(value: ssh2::Error) -> Self {
|
||||
Self::Ssh(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub struct ConnectionParams<'a> {
|
||||
user: &'a str,
|
||||
host: &'a str,
|
||||
port: u16,
|
||||
auth: Auth<'a>,
|
||||
}
|
||||
|
||||
pub enum Auth<'a> {
|
||||
SshAgent,
|
||||
Password(&'a str),
|
||||
KeyFile(KeyFile<'a>),
|
||||
KeyMemory(KeyMemory<'a>),
|
||||
}
|
||||
|
||||
pub struct KeyFile<'a> {
|
||||
path: &'a Path,
|
||||
passphrase: Option<&'a str>,
|
||||
}
|
||||
|
||||
pub struct KeyMemory<'a> {
|
||||
/// PEM encoded key
|
||||
key: &'a str,
|
||||
passphrase: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Default for ConnectionParams<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
user: Default::default(),
|
||||
host: Default::default(),
|
||||
port: 22,
|
||||
auth: Auth::SshAgent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub fn connect(params: ConnectionParams<'_>) -> Result<Self> {
|
||||
let tcp = TcpStream::connect(format!("{}:{}", params.host, params.port))?;
|
||||
let mut session = ssh2::Session::new()?;
|
||||
|
||||
session.set_tcp_stream(tcp);
|
||||
session.handshake()?;
|
||||
|
||||
match params.auth {
|
||||
Auth::SshAgent => todo!(),
|
||||
Auth::Password(pass) => session.userauth_password(params.user, pass)?,
|
||||
Auth::KeyFile(key) => {
|
||||
session.userauth_pubkey_file(params.user, None, key.path, key.passphrase)?
|
||||
}
|
||||
Auth::KeyMemory(key) => {
|
||||
session.userauth_pubkey_memory(params.user, None, key.key, key.passphrase)?
|
||||
}
|
||||
}
|
||||
|
||||
if !session.authenticated() {
|
||||
return Err(Error::AuthenticationFailure);
|
||||
}
|
||||
|
||||
Ok(Connection { session })
|
||||
}
|
||||
|
||||
fn read2(&self, path: &Path) -> Result<impl Read> {
|
||||
let (chan, _stat) = self.session.scp_recv(path)?;
|
||||
Ok(FileReader { chan })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read<P: AsRef<Path>>(&self, path: P) -> Result<impl Read> {
|
||||
self.read2(path.as_ref())
|
||||
}
|
||||
|
||||
fn read_to_end_2(&self, path: &Path) -> Result<Vec<u8>> {
|
||||
let mut reader = self.read2(path)?;
|
||||
let mut buf = Vec::new();
|
||||
reader.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_to_end<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>> {
|
||||
self.read_to_end_2(path.as_ref())
|
||||
}
|
||||
|
||||
fn write_to_end_2(&self, path: &Path, content: &[u8]) -> Result<()> {
|
||||
let mut chan = self
|
||||
.session
|
||||
.scp_send(path, 0o644, content.len() as _, None)?;
|
||||
|
||||
chan.write_all(content)?;
|
||||
if let Err(e) = close_channel(&mut chan) {
|
||||
log::error!("Failed to close file channel: {e:?}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_to_end<P: AsRef<Path>, B: AsRef<[u8]>>(&self, path: P, buf: B) -> Result<()> {
|
||||
self.write_to_end_2(path.as_ref(), buf.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
struct FileReader {
|
||||
chan: ssh2::Channel,
|
||||
}
|
||||
|
||||
impl Read for FileReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.chan.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn close_channel(chan: &mut ssh2::Channel) -> Result<()> {
|
||||
chan.send_eof()?;
|
||||
chan.wait_eof()?;
|
||||
chan.close()?;
|
||||
chan.wait_close()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Drop for FileReader {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = close_channel(&mut self.chan) {
|
||||
log::error!("Failed to close file channel: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user