Added basic ssh auth in rust without error handling
This commit is contained in:
parent
bb49e9482c
commit
6dee4fbc93
15 changed files with 239 additions and 267 deletions
|
@ -205,7 +205,7 @@ fun generateKotlinFromUdl() {
|
||||||
workingDir = File(project.projectDir, "rs")
|
workingDir = File(project.projectDir, "rs")
|
||||||
commandLine = listOf(
|
commandLine = listOf(
|
||||||
"cargo", "run", "--features=uniffi/cli",
|
"cargo", "run", "--features=uniffi/cli",
|
||||||
"--bin", "uniffi-bindgen", "generate", "src/repository_watcher.udl",
|
"--bin", "uniffi-bindgen", "generate", "src/gitnuro.udl",
|
||||||
"--language", "kotlin",
|
"--language", "kotlin",
|
||||||
"--out-dir", rustGeneratedSource
|
"--out-dir", rustGeneratedSource
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,6 +13,7 @@ name = "gitnuro_rs"
|
||||||
uniffi = { version = "0.24.1" }
|
uniffi = { version = "0.24.1" }
|
||||||
notify = "6.0.1"
|
notify = "6.0.1"
|
||||||
thiserror = "1.0.43"
|
thiserror = "1.0.43"
|
||||||
|
libssh-rs = "0.2.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
uniffi = { version = "0.24.1", features = [ "build" ] }
|
uniffi = { version = "0.24.1", features = [ "build" ] }
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
uniffi::generate_scaffolding("src/repository_watcher.udl").unwrap();
|
uniffi::generate_scaffolding("src/gitnuro.udl").unwrap();
|
||||||
}
|
}
|
||||||
|
|
46
rs/src/gitnuro.udl
Normal file
46
rs/src/gitnuro.udl
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
namespace gitnuro {
|
||||||
|
[Throws=WatcherInitError]
|
||||||
|
void watch_directory(string path, WatchDirectoryNotifier checker);
|
||||||
|
};
|
||||||
|
|
||||||
|
callback interface WatchDirectoryNotifier {
|
||||||
|
boolean should_keep_looping();
|
||||||
|
|
||||||
|
void detected_change(sequence<string> paths);
|
||||||
|
};
|
||||||
|
|
||||||
|
[Error]
|
||||||
|
interface WatcherInitError {
|
||||||
|
Generic(string error);
|
||||||
|
Io(string error);
|
||||||
|
PathNotFound();
|
||||||
|
WatchNotFound();
|
||||||
|
InvalidConfig();
|
||||||
|
MaxFilesWatch();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
interface Session {
|
||||||
|
constructor();
|
||||||
|
void setup(string host, string? user, i32 port);
|
||||||
|
void public_key_auth();
|
||||||
|
void password_auth(string password);
|
||||||
|
void disconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Channel {
|
||||||
|
constructor(Session session);
|
||||||
|
void open_session();
|
||||||
|
boolean is_open();
|
||||||
|
void close();
|
||||||
|
void request_exec(string command);
|
||||||
|
boolean poll_has_bytes(boolean is_stderr);
|
||||||
|
ReadResult read(boolean is_stderr, u64 len);
|
||||||
|
void write_byte(i32 byte);
|
||||||
|
void write_bytes(bytes data);
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary ReadResult {
|
||||||
|
u64 read_count;
|
||||||
|
bytes data;
|
||||||
|
};
|
129
rs/src/lib.rs
129
rs/src/lib.rs
|
@ -1,13 +1,18 @@
|
||||||
extern crate notify;
|
extern crate notify;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::io::{Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::mpsc::{channel, RecvTimeoutError};
|
use std::sync::mpsc::{channel, RecvTimeoutError};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use libssh_rs::{PollStatus, SshOption};
|
||||||
use notify::{Config, Error, ErrorKind, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{Config, Error, ErrorKind, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
|
||||||
uniffi::include_scaffolding!("repository_watcher");
|
uniffi::include_scaffolding!("gitnuro");
|
||||||
|
|
||||||
|
const ACCEPTED_SSH_TYPES: &str = "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss";
|
||||||
|
|
||||||
fn watch_directory(
|
fn watch_directory(
|
||||||
path: String,
|
path: String,
|
||||||
|
@ -114,3 +119,125 @@ impl WatcherInitErrorConverter for ErrorKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Session {
|
||||||
|
pub session: RwLock<libssh_rs::Session>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session {
|
||||||
|
fn new() -> Self {
|
||||||
|
let session = libssh_rs::Session::new().unwrap();
|
||||||
|
|
||||||
|
Session {
|
||||||
|
session: RwLock::new(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(&self, host: String, user: Option<String>, port: i32) {
|
||||||
|
let session = self.session.write().unwrap();
|
||||||
|
session.set_option(SshOption::Hostname(host)).unwrap();
|
||||||
|
|
||||||
|
if let Some(user) = user {
|
||||||
|
session.set_option(SshOption::User(Some(user))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(port) = port.try_into() {
|
||||||
|
session.set_option(SshOption::Port(port)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
session.set_option(SshOption::PublicKeyAcceptedTypes(ACCEPTED_SSH_TYPES.to_string())).unwrap();
|
||||||
|
session.options_parse_config(None).unwrap();
|
||||||
|
session.connect().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key_auth(&self) {
|
||||||
|
let session = self.session.write().unwrap();
|
||||||
|
session.userauth_public_key_auto(None, Some("")).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn password_auth(&self, password: String) {
|
||||||
|
let session = self.session.write().unwrap();
|
||||||
|
session.userauth_password(None, Some(&password)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disconnect(&self) {
|
||||||
|
let session = self.session.write().unwrap();
|
||||||
|
session.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
channel: RwLock<libssh_rs::Channel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Channel {}
|
||||||
|
unsafe impl Sync for Channel {}
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
fn new(session: Arc<Session>) -> Self {
|
||||||
|
let session = session.session.write().unwrap();
|
||||||
|
let channel = session.new_channel().unwrap();
|
||||||
|
|
||||||
|
Channel {
|
||||||
|
channel: RwLock::new(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn open_session(&self) {
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
channel.open_session().unwrap();
|
||||||
|
}
|
||||||
|
fn is_open(&self) -> bool {
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
channel.is_open()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&self) {
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
channel.close().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_exec(&self, command: String) {
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
channel.request_exec(&command).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_has_bytes(&self, is_stderr: bool) -> bool {
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
let poll_timeout = channel.poll_timeout(is_stderr, None).unwrap();
|
||||||
|
|
||||||
|
match poll_timeout {
|
||||||
|
PollStatus::AvailableBytes(count) => count > 0,
|
||||||
|
PollStatus::EndOfFile => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&self, is_stderr: bool, len: u64) -> ReadResult {
|
||||||
|
let ulen = len as usize;
|
||||||
|
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
|
||||||
|
let mut buffer = vec![0; ulen];
|
||||||
|
let read = channel.read_timeout(&mut buffer, is_stderr, None).unwrap();
|
||||||
|
|
||||||
|
ReadResult {
|
||||||
|
read_count: read as u64,
|
||||||
|
data: buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_byte(&self, byte: i32) {
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
channel.stdin().write_all(&byte.to_ne_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, data: Vec<u8>) {
|
||||||
|
let channel = self.channel.write().unwrap();
|
||||||
|
channel.stdin().write_all(&data).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReadResult {
|
||||||
|
read_count: u64,
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
namespace gitnuro {
|
|
||||||
[Throws=WatcherInitError]
|
|
||||||
void watch_directory(string path, WatchDirectoryNotifier checker);
|
|
||||||
};
|
|
||||||
|
|
||||||
callback interface WatchDirectoryNotifier {
|
|
||||||
boolean should_keep_looping();
|
|
||||||
|
|
||||||
void detected_change(sequence<string> paths);
|
|
||||||
};
|
|
||||||
|
|
||||||
[Error]
|
|
||||||
interface WatcherInitError {
|
|
||||||
Generic(string error);
|
|
||||||
Io(string error);
|
|
||||||
PathNotFound();
|
|
||||||
WatchNotFound();
|
|
||||||
InvalidConfig();
|
|
||||||
MaxFilesWatch();
|
|
||||||
};
|
|
|
@ -1,13 +1,14 @@
|
||||||
package com.jetpackduba.gitnuro.credentials
|
package com.jetpackduba.gitnuro.credentials
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.LibSshChannel
|
import com.jetpackduba.gitnuro.ssh.libssh.LibSshChannel
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.LibSshSession
|
import uniffi.gitnuro.Channel
|
||||||
|
import uniffi.gitnuro.Session
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
class GProcessLibSsh : Process() {
|
class GProcessLibSsh : Process() {
|
||||||
private lateinit var channel: LibSshChannel
|
private lateinit var channel: LibSshChannel
|
||||||
private lateinit var session: LibSshSession
|
private lateinit var session: Session
|
||||||
|
|
||||||
private val outputStream by lazy {
|
private val outputStream by lazy {
|
||||||
channel.outputStream
|
channel.outputStream
|
||||||
|
@ -42,7 +43,9 @@ class GProcessLibSsh : Process() {
|
||||||
check(!isRunning())
|
check(!isRunning())
|
||||||
println("exitValue called")
|
println("exitValue called")
|
||||||
|
|
||||||
return channel.close()
|
channel.close()
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroy() {
|
override fun destroy() {
|
||||||
|
@ -57,8 +60,8 @@ class GProcessLibSsh : Process() {
|
||||||
return channel.isOpen()
|
return channel.isOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setup(session: LibSshSession, commandName: String) {
|
fun setup(session: Session, commandName: String) {
|
||||||
val channel = session.createChannel()
|
val channel = LibSshChannel(session)
|
||||||
|
|
||||||
channel.openSession()
|
channel.openSession()
|
||||||
channel.requestExec(commandName)
|
channel.requestExec(commandName)
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
package com.jetpackduba.gitnuro.credentials
|
package com.jetpackduba.gitnuro.credentials
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.LibSshOptions
|
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.LibSshSession
|
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import org.eclipse.jgit.transport.RemoteSession
|
import org.eclipse.jgit.transport.RemoteSession
|
||||||
import org.eclipse.jgit.transport.URIish
|
import org.eclipse.jgit.transport.URIish
|
||||||
|
import uniffi.gitnuro.Session
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
|
||||||
|
|
||||||
|
|
||||||
private const val DEFAULT_SSH_PORT = 22
|
private const val DEFAULT_SSH_PORT = 22
|
||||||
|
|
||||||
class GRemoteSession @Inject constructor(
|
class GRemoteSession @Inject constructor(
|
||||||
private val processSession: Provider<LibSshSession>,
|
|
||||||
private val credentialsStateManager: CredentialsStateManager,
|
private val credentialsStateManager: CredentialsStateManager,
|
||||||
) : RemoteSession {
|
) : RemoteSession {
|
||||||
private var session: LibSshSession? = null
|
private var session: Session? = null
|
||||||
|
|
||||||
override fun exec(commandName: String, timeout: Int): Process {
|
override fun exec(commandName: String, timeout: Int): Process {
|
||||||
println("Running command $commandName")
|
println("Running command $commandName")
|
||||||
|
@ -32,44 +28,33 @@ class GRemoteSession @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setup(uri: URIish) {
|
fun setup(uri: URIish) {
|
||||||
val session = processSession.get()
|
val session = Session()
|
||||||
session.setOptions(LibSshOptions.SSH_OPTIONS_HOST, uri.host)
|
session.setup(uri.host, uri.user, uri.port)
|
||||||
|
|
||||||
uri.user?.let {
|
var result = session.publicKeyAuth()
|
||||||
session.setOptions(LibSshOptions.SSH_OPTIONS_USER, uri.user)
|
|
||||||
}
|
|
||||||
|
|
||||||
session.setOptions(
|
// if (result == 1) {
|
||||||
LibSshOptions.SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
|
// credentialsStateManager.updateState(CredentialsRequested.SshCredentialsRequested)
|
||||||
"ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
|
//
|
||||||
)
|
// var credentials = credentialsStateManager.currentCredentialsState
|
||||||
session.loadOptionsFromConfig()
|
// while (credentials is CredentialsRequested) {
|
||||||
|
// credentials = credentialsStateManager.currentCredentialsState
|
||||||
session.connect()
|
// }
|
||||||
var result = session.userAuthPublicKeyAuto(null, "")
|
//
|
||||||
|
// val password = if (credentials !is CredentialsAccepted.SshCredentialsAccepted)
|
||||||
if (result == 1) {
|
// throw CancellationException("Credentials cancelled")
|
||||||
credentialsStateManager.updateState(CredentialsRequested.SshCredentialsRequested)
|
// else
|
||||||
|
// credentials.password
|
||||||
var credentials = credentialsStateManager.currentCredentialsState
|
//
|
||||||
while (credentials is CredentialsRequested) {
|
// result = session.userAuthPublicKeyAuto(null, password)
|
||||||
credentials = credentialsStateManager.currentCredentialsState
|
//
|
||||||
}
|
// if (result != 0) {
|
||||||
|
// result = session.userAuthPassword(password)
|
||||||
val password = if (credentials !is CredentialsAccepted.SshCredentialsAccepted)
|
// }
|
||||||
throw CancellationException("Credentials cancelled")
|
// }
|
||||||
else
|
//
|
||||||
credentials.password
|
// if (result != 0)
|
||||||
|
// throw Exception("Something went wrong with authentication. Code $result")
|
||||||
result = session.userAuthPublicKeyAuto(null, password)
|
|
||||||
|
|
||||||
if (result != 0) {
|
|
||||||
result = session.userAuthPassword(password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
throw Exception("Something went wrong with authentication. Code $result")
|
|
||||||
|
|
||||||
this.session = session
|
this.session = session
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,30 +3,29 @@ package com.jetpackduba.gitnuro.ssh.libssh
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.streams.LibSshChannelInputErrStream
|
import com.jetpackduba.gitnuro.ssh.libssh.streams.LibSshChannelInputErrStream
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.streams.LibSshChannelInputStream
|
import com.jetpackduba.gitnuro.ssh.libssh.streams.LibSshChannelInputStream
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.streams.LibSshChannelOutputStream
|
import com.jetpackduba.gitnuro.ssh.libssh.streams.LibSshChannelOutputStream
|
||||||
|
import uniffi.gitnuro.Channel
|
||||||
|
import uniffi.gitnuro.Session
|
||||||
|
|
||||||
class LibSshChannel internal constructor(sshSession: ssh_session) {
|
class LibSshChannel internal constructor(sshSession: Session) {
|
||||||
private val sshLib = SSHLibrary.INSTANCE
|
private var channel = Channel(sshSession)
|
||||||
private var channel: ssh_channel = sshLib.ssh_channel_new(sshSession)
|
|
||||||
|
|
||||||
val outputStream = LibSshChannelOutputStream(channel)
|
val outputStream = LibSshChannelOutputStream(channel)
|
||||||
val inputStream = LibSshChannelInputStream(channel)
|
val inputStream = LibSshChannelInputStream(channel)
|
||||||
val errorOutputStream = LibSshChannelInputErrStream(channel)
|
val errorOutputStream = LibSshChannelInputErrStream(channel)
|
||||||
|
|
||||||
|
|
||||||
fun openSession() {
|
fun openSession() {
|
||||||
sshLib.ssh_channel_open_session(channel)
|
channel.openSession()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestExec(commandName: String) {
|
fun requestExec(commandName: String) {
|
||||||
sshLib.ssh_channel_request_exec(channel, commandName)
|
channel.requestExec(commandName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isOpen(): Boolean {
|
fun isOpen(): Boolean {
|
||||||
return sshLib.ssh_channel_is_open(channel) == 1
|
return channel.isOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun close(): Int {
|
fun close() {
|
||||||
return sshLib.ssh_channel_close(channel)
|
channel.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.ssh.libssh
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum based on the enum "ssh_options_e" of libssh/libssh.h
|
|
||||||
*/
|
|
||||||
enum class LibSshOptions {
|
|
||||||
SSH_OPTIONS_HOST,
|
|
||||||
SSH_OPTIONS_PORT,
|
|
||||||
SSH_OPTIONS_PORT_STR,
|
|
||||||
SSH_OPTIONS_FD,
|
|
||||||
SSH_OPTIONS_USER,
|
|
||||||
SSH_OPTIONS_SSH_DIR,
|
|
||||||
SSH_OPTIONS_IDENTITY,
|
|
||||||
SSH_OPTIONS_ADD_IDENTITY,
|
|
||||||
SSH_OPTIONS_KNOWNHOSTS,
|
|
||||||
SSH_OPTIONS_TIMEOUT,
|
|
||||||
SSH_OPTIONS_TIMEOUT_USEC,
|
|
||||||
SSH_OPTIONS_SSH1,
|
|
||||||
SSH_OPTIONS_SSH2,
|
|
||||||
SSH_OPTIONS_LOG_VERBOSITY,
|
|
||||||
SSH_OPTIONS_LOG_VERBOSITY_STR,
|
|
||||||
SSH_OPTIONS_CIPHERS_C_S,
|
|
||||||
SSH_OPTIONS_CIPHERS_S_C,
|
|
||||||
SSH_OPTIONS_COMPRESSION_C_S,
|
|
||||||
SSH_OPTIONS_COMPRESSION_S_C,
|
|
||||||
SSH_OPTIONS_PROXYCOMMAND,
|
|
||||||
SSH_OPTIONS_BINDADDR,
|
|
||||||
SSH_OPTIONS_STRICTHOSTKEYCHECK,
|
|
||||||
SSH_OPTIONS_COMPRESSION,
|
|
||||||
SSH_OPTIONS_COMPRESSION_LEVEL,
|
|
||||||
SSH_OPTIONS_KEY_EXCHANGE,
|
|
||||||
SSH_OPTIONS_HOSTKEYS,
|
|
||||||
SSH_OPTIONS_GSSAPI_SERVER_IDENTITY,
|
|
||||||
SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY,
|
|
||||||
SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS,
|
|
||||||
SSH_OPTIONS_HMAC_C_S,
|
|
||||||
SSH_OPTIONS_HMAC_S_C,
|
|
||||||
SSH_OPTIONS_PASSWORD_AUTH,
|
|
||||||
SSH_OPTIONS_PUBKEY_AUTH,
|
|
||||||
SSH_OPTIONS_KBDINT_AUTH,
|
|
||||||
SSH_OPTIONS_GSSAPI_AUTH,
|
|
||||||
SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
|
|
||||||
SSH_OPTIONS_NODELAY,
|
|
||||||
SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
|
|
||||||
SSH_OPTIONS_PROCESS_CONFIG,
|
|
||||||
SSH_OPTIONS_REKEY_DATA,
|
|
||||||
SSH_OPTIONS_REKEY_TIME,
|
|
||||||
SSH_OPTIONS_RSA_MIN_SIZE,
|
|
||||||
SSH_OPTIONS_IDENTITY_AGENT
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.ssh.libssh
|
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.logging.printError
|
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.streams.checkValidResult
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
private const val TAG = "LibSshSession"
|
|
||||||
|
|
||||||
class LibSshSession @Inject constructor() {
|
|
||||||
private val sshLib = SSHLibrary.INSTANCE
|
|
||||||
|
|
||||||
private var session: ssh_session = sshLib.ssh_new()
|
|
||||||
private var channel: LibSshChannel? = null
|
|
||||||
|
|
||||||
fun setOptions(option: LibSshOptions, value: String) {
|
|
||||||
sshLib.ssh_options_set(session, option.ordinal, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadOptionsFromConfig() {
|
|
||||||
checkValidResult("loadOptionsFromConfig", sshLib.ssh_options_parse_config(session, null))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun connect() {
|
|
||||||
sshLib.ssh_connect(session)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun userAuthPublicKeyAuto(username: String?, password: String?): Int {
|
|
||||||
val result = sshLib.ssh_userauth_publickey_auto(session, username, password)
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
printError(TAG, "Result is: $result.\nError is: ${getError()}")
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun userAuthPassword(password: String): Int {
|
|
||||||
val result = sshLib.ssh_userauth_password(session, null, password)
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
printError(TAG, "Result is: $result.\nError is: ${getError()}")
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createChannel(): LibSshChannel {
|
|
||||||
val newChannel = LibSshChannel(session)
|
|
||||||
|
|
||||||
this.channel = newChannel
|
|
||||||
|
|
||||||
return newChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getError(): String {
|
|
||||||
return sshLib.ssh_get_error(session)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun disconnect() {
|
|
||||||
sshLib.ssh_disconnect(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.ssh.libssh
|
|
||||||
|
|
||||||
import com.sun.jna.Library
|
|
||||||
import com.sun.jna.Native
|
|
||||||
import com.sun.jna.PointerType
|
|
||||||
|
|
||||||
class ssh_session : PointerType()
|
|
||||||
class ssh_channel : PointerType()
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
interface SSHLibrary : Library {
|
|
||||||
fun ssh_new(): ssh_session
|
|
||||||
fun ssh_disconnect(session: ssh_session): ssh_session
|
|
||||||
fun ssh_options_set(session: ssh_session, enumValue: Int, value: String)
|
|
||||||
|
|
||||||
fun ssh_options_parse_config(session: ssh_session, fileName: String?): Int
|
|
||||||
|
|
||||||
fun ssh_connect(session: ssh_session): Int
|
|
||||||
|
|
||||||
fun ssh_userauth_publickey_auto(session: ssh_session, username: String?, password: String?): Int
|
|
||||||
fun ssh_userauth_password(session: ssh_session, username: String?, password: String?): Int
|
|
||||||
fun ssh_get_error(session: ssh_session): String
|
|
||||||
|
|
||||||
fun ssh_channel_new(sshSession: ssh_session): ssh_channel
|
|
||||||
|
|
||||||
fun ssh_channel_open_session(sshChannel: ssh_channel): Int
|
|
||||||
|
|
||||||
fun ssh_channel_request_exec(sshChannel: ssh_channel, command: String): Int
|
|
||||||
|
|
||||||
fun ssh_channel_read(sshChannel: ssh_channel, buffer: ByteArray, count: Int, isStderr: Int): Int
|
|
||||||
|
|
||||||
fun ssh_channel_poll(sshChannel: ssh_channel, isStderr: Int): Int
|
|
||||||
|
|
||||||
fun ssh_channel_write(sshChannel: ssh_channel, data: ByteArray, len: Int): Int
|
|
||||||
|
|
||||||
fun ssh_channel_close(sshChannel: ssh_channel): Int
|
|
||||||
|
|
||||||
fun ssh_channel_is_open(sshChannel: ssh_channel): Int
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val INSTANCE = Native.load("ssh", SSHLibrary::class.java) as SSHLibrary
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +1,17 @@
|
||||||
package com.jetpackduba.gitnuro.ssh.libssh.streams
|
package com.jetpackduba.gitnuro.ssh.libssh.streams
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.SSHLibrary
|
import uniffi.gitnuro.Channel
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.ssh_channel
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
class LibSshChannelInputErrStream(private val sshChannel: ssh_channel) : InputStream() {
|
class LibSshChannelInputErrStream(private val sshChannel: Channel) : InputStream() {
|
||||||
private var cancelled = false
|
private var cancelled = false
|
||||||
private val sshLib = SSHLibrary.INSTANCE
|
|
||||||
|
|
||||||
override fun read(): Int {
|
override fun read(): Int {
|
||||||
val buffer = ByteArray(1)
|
return if (sshChannel.pollHasBytes(true)) {
|
||||||
|
val read = sshChannel.read(true, 1u)
|
||||||
|
val byteArray = read.data
|
||||||
|
|
||||||
return if (sshLib.ssh_channel_poll(sshChannel, 1) > 0) {
|
val first = byteArray.first()
|
||||||
sshLib.ssh_channel_read(sshChannel, buffer, 1, 1)
|
|
||||||
|
|
||||||
val first = buffer.first()
|
|
||||||
|
|
||||||
first.toInt()
|
first.toInt()
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -1,28 +1,26 @@
|
||||||
package com.jetpackduba.gitnuro.ssh.libssh.streams
|
package com.jetpackduba.gitnuro.ssh.libssh.streams
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.SSHLibrary
|
import uniffi.gitnuro.Channel
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.ssh_channel
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
class LibSshChannelInputStream(private val sshChannel: ssh_channel) : InputStream() {
|
class LibSshChannelInputStream(private val sshChannel: Channel) : InputStream() {
|
||||||
private val sshLib = SSHLibrary.INSTANCE
|
|
||||||
|
|
||||||
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||||
val byteArray = ByteArray(len)
|
val result = sshChannel.read(false, len.toULong())
|
||||||
val result = sshLib.ssh_channel_read(sshChannel, byteArray, len, 0)
|
val byteArray = result.data
|
||||||
|
val read = result.readCount
|
||||||
|
|
||||||
for (i in 0 until len) {
|
for (i in 0 until len) {
|
||||||
b[off + i] = byteArray[i]
|
b[off + i] = byteArray[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return read.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(): Int {
|
override fun read(): Int {
|
||||||
val buffer = ByteArray(1)
|
|
||||||
|
|
||||||
sshLib.ssh_channel_read(sshChannel, buffer, 1, 0)
|
val result = sshChannel.read(false, 1u)
|
||||||
|
|
||||||
val first = buffer.first()
|
val first = result.data.first()
|
||||||
|
|
||||||
return first.toInt()
|
return first.toInt()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,18 @@
|
||||||
package com.jetpackduba.gitnuro.ssh.libssh.streams
|
package com.jetpackduba.gitnuro.ssh.libssh.streams
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.SSHLibrary
|
import uniffi.gitnuro.Channel
|
||||||
import com.jetpackduba.gitnuro.ssh.libssh.ssh_channel
|
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
class LibSshChannelOutputStream(private val sshChannel: ssh_channel) : OutputStream() {
|
class LibSshChannelOutputStream(private val sshChannel: Channel) : OutputStream() {
|
||||||
private val sshLib = SSHLibrary.INSTANCE
|
|
||||||
|
|
||||||
override fun write(b: Int) {
|
override fun write(b: Int) {
|
||||||
val byteArrayData = byteArrayOf(b.toByte())
|
val byteArrayData = byteArrayOf(b.toByte())
|
||||||
write(byteArrayData)
|
write(byteArrayData)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(b: ByteArray) {
|
override fun write(b: ByteArray) {
|
||||||
sshLib.ssh_channel_write(sshChannel, b, b.size)
|
sshChannel.writeBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkValidResult(tag: String, result: Int) {
|
|
||||||
if (result != 0)
|
|
||||||
throw Exception("$tag - Result is $result")
|
|
||||||
}
|
|
Loading…
Reference in a new issue