initial commit

This commit is contained in:
Jonas Maier 2024-07-31 12:41:41 +02:00
commit b7c689e1ec
No known key found for this signature in database
GPG Key ID: BAE442A0B8A17D02
5 changed files with 381 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

196
Cargo.lock generated Normal file
View 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
View 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
View 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
View 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:?}");
}
}
}