Added first version of libssh implementation
This commit is contained in:
parent
1d5085b689
commit
944f4e9955
8 changed files with 328 additions and 44 deletions
|
@ -40,7 +40,7 @@ dependencies {
|
|||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
|
||||
implementation("net.i2p.crypto:eddsa:0.3.0")
|
||||
|
||||
implementation("com.sun.jna:jna:3.0.9")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package com.jetpackduba.gitnuro.credentials
|
||||
|
||||
import com.jetpackduba.gitnuro.credentials.streams.LibSshInputErrStream
|
||||
import com.jetpackduba.gitnuro.credentials.streams.LibSshInputStream
|
||||
import com.jetpackduba.gitnuro.credentials.streams.LibSshOutputStream
|
||||
import com.jetpackduba.gitnuro.credentials.streams.checkValidResult
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
class GProcessLibSsh : Process() {
|
||||
private lateinit var channel: ssh_channel
|
||||
private lateinit var session: ssh_session
|
||||
|
||||
private val outputStream by lazy {
|
||||
LibSshOutputStream(channel)
|
||||
}
|
||||
private val inputStream by lazy {
|
||||
LibSshInputStream(channel)
|
||||
}
|
||||
private val errorOutputStream by lazy {
|
||||
LibSshInputErrStream(channel)
|
||||
}
|
||||
|
||||
override fun getOutputStream(): OutputStream {
|
||||
return outputStream
|
||||
}
|
||||
|
||||
override fun getInputStream(): InputStream {
|
||||
return inputStream
|
||||
}
|
||||
|
||||
override fun getErrorStream(): InputStream {
|
||||
return errorOutputStream
|
||||
}
|
||||
|
||||
override fun waitFor(): Int {
|
||||
if (isRunning())
|
||||
Thread.sleep(100)
|
||||
|
||||
return exitValue()
|
||||
}
|
||||
|
||||
override fun exitValue(): Int {
|
||||
check(!isRunning())
|
||||
println("exitValue called")
|
||||
|
||||
return sshLib.ssh_channel_close(channel)
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
if (sshLib.ssh_channel_is_open(channel) == 1) {
|
||||
checkValidResult(sshLib.ssh_channel_close(channel))
|
||||
}
|
||||
|
||||
sshLib.ssh_disconnect(session)
|
||||
println("Destroy called")
|
||||
}
|
||||
|
||||
private fun isRunning(): Boolean {
|
||||
return sshLib.ssh_channel_is_open(channel) == 1
|
||||
}
|
||||
|
||||
fun setup(session: ssh_session, commandName: String) {
|
||||
val channel = sshLib.ssh_channel_new(session)
|
||||
|
||||
checkValidResult(sshLib.ssh_channel_open_session(channel))
|
||||
checkValidResult(sshLib.ssh_channel_request_exec(channel, commandName))
|
||||
|
||||
this.session = session
|
||||
this.channel = channel
|
||||
}
|
||||
|
||||
}
|
|
@ -20,29 +20,30 @@ class GRemoteSession @Inject constructor(
|
|||
private val client = SshClient.setUpDefaultClient()
|
||||
|
||||
private var connectFuture: ConnectFuture? = null
|
||||
private var ssh_session: ssh_session? = null
|
||||
|
||||
override fun exec(commandName: String, timeout: Int): Process {
|
||||
println(commandName)
|
||||
// val session = connectFuture!!.clientSession
|
||||
//
|
||||
// val auth = session.auth()
|
||||
// auth.addListener { arg0 ->
|
||||
// println("Authentication completed with " + if (arg0.isSuccess) "success" else "failure")
|
||||
// }
|
||||
//
|
||||
// session.waitFor(
|
||||
// listOf(
|
||||
// ClientSession.ClientSessionEvent.WAIT_AUTH,
|
||||
// ClientSession.ClientSessionEvent.CLOSED,
|
||||
// ClientSession.ClientSessionEvent.AUTHED
|
||||
// ), Duration.ofHours(2)
|
||||
// )
|
||||
// auth.verify()
|
||||
// val process = processProvider.get()
|
||||
|
||||
val connectFuture = checkNotNull(connectFuture)
|
||||
val session = this.ssh_session ?: throw Exception("Session is null")
|
||||
val process = GProcessLibSsh()
|
||||
|
||||
val session = connectFuture.clientSession
|
||||
|
||||
val auth = session.auth()
|
||||
auth.addListener { arg0 ->
|
||||
println("Authentication completed with " + if (arg0.isSuccess) "success" else "failure")
|
||||
}
|
||||
|
||||
session.waitFor(
|
||||
listOf(
|
||||
ClientSession.ClientSessionEvent.WAIT_AUTH,
|
||||
ClientSession.ClientSessionEvent.CLOSED,
|
||||
ClientSession.ClientSessionEvent.AUTHED
|
||||
), Duration.ofHours(2)
|
||||
)
|
||||
auth.verify()
|
||||
|
||||
val process = processProvider.get()
|
||||
process.setup(session, commandName)
|
||||
return process
|
||||
}
|
||||
|
@ -53,33 +54,46 @@ class GRemoteSession @Inject constructor(
|
|||
}
|
||||
|
||||
fun setup(uri: URIish) {
|
||||
client.open()
|
||||
|
||||
val port = if (uri.port == -1) {
|
||||
DEFAULT_SSH_PORT
|
||||
} else
|
||||
uri.port
|
||||
val session = sshLib.ssh_new()
|
||||
sshLib.ssh_options_set(session, 0, uri.host)
|
||||
sshLib.ssh_connect(session)
|
||||
checkValidResult(sshLib.ssh_userauth_publickey_auto(session, uri.user, null))
|
||||
|
||||
val filePasswordProvider =
|
||||
FilePasswordProvider { _, _, _ ->
|
||||
credentialsStateManager.updateState(CredentialsState.SshCredentialsRequested)
|
||||
this.ssh_session = session
|
||||
|
||||
var credentials = credentialsStateManager.currentCredentialsState
|
||||
while (credentials is CredentialsState.CredentialsRequested) {
|
||||
credentials = credentialsStateManager.currentCredentialsState
|
||||
}
|
||||
|
||||
if (credentials !is CredentialsState.SshCredentialsAccepted)
|
||||
null
|
||||
else
|
||||
credentials.password
|
||||
}
|
||||
|
||||
client.filePasswordProvider = filePasswordProvider
|
||||
|
||||
val connectFuture = client.connect(uri.user, uri.host, port)
|
||||
connectFuture.await()
|
||||
|
||||
this.connectFuture = connectFuture
|
||||
// client.open()
|
||||
//
|
||||
// val port = if (uri.port == -1) {
|
||||
// DEFAULT_SSH_PORT
|
||||
// } else
|
||||
// uri.port
|
||||
//
|
||||
// val filePasswordProvider =
|
||||
// FilePasswordProvider { _, _, _ ->
|
||||
// credentialsStateManager.updateState(CredentialsState.SshCredentialsRequested)
|
||||
//
|
||||
// var credentials = credentialsStateManager.currentCredentialsState
|
||||
// while (credentials is CredentialsState.CredentialsRequested) {
|
||||
// credentials = credentialsStateManager.currentCredentialsState
|
||||
// }
|
||||
//
|
||||
// if (credentials !is CredentialsState.SshCredentialsAccepted)
|
||||
// null
|
||||
// else
|
||||
// credentials.password
|
||||
// }
|
||||
//
|
||||
// client.filePasswordProvider = filePasswordProvider
|
||||
//
|
||||
// val connectFuture = client.connect(uri.user, uri.host, port)
|
||||
// connectFuture.await()
|
||||
//
|
||||
// this.connectFuture = connectFuture
|
||||
}
|
||||
}
|
||||
|
||||
fun checkValidResult(result: Int) {
|
||||
if (result != 0)
|
||||
throw Exception("Result is $result")
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.jetpackduba.gitnuro.credentials
|
||||
|
||||
import com.sun.jna.Library
|
||||
import com.sun.jna.Native
|
||||
import com.sun.jna.PointerType
|
||||
|
||||
class ssh_session : PointerType()
|
||||
class ssh_channel : PointerType()
|
||||
|
||||
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_connect(session: ssh_session) : Int
|
||||
|
||||
fun ssh_userauth_password(session: ssh_session, username: String, password: String): Int
|
||||
fun ssh_userauth_publickey_auto(session: ssh_session, username: String?, password: String?): Int
|
||||
|
||||
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_read_timeout(sshChannel: ssh_channel, buffer: ByteArray, count: Int, isStderr: Int, timeoutMs: Int): Int
|
||||
fun ssh_channel_poll(sshChannel: ssh_channel, isStderr: Int): Int
|
||||
fun ssh_channel_read_nonblocking(sshChannel: ssh_channel, buffer: ByteArray, count: Int, 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_send_eof(sshChannel: ssh_channel): Int
|
||||
|
||||
fun ssh_channel_free(sshChannel: ssh_channel)
|
||||
fun ssh_channel_is_open(sshChannel: ssh_channel): Int
|
||||
|
||||
|
||||
companion object {
|
||||
val INSTANCE = Native.loadLibrary(
|
||||
"libssh",
|
||||
SSHLibrary::class.java
|
||||
) as SSHLibrary
|
||||
}
|
||||
}
|
||||
|
||||
val sshLib = SSHLibrary.INSTANCE
|
|
@ -0,0 +1,10 @@
|
|||
package com.jetpackduba.gitnuro.credentials.streams
|
||||
|
||||
class Convertest {
|
||||
companion object {
|
||||
fun printIt(byteArray: ByteArray) {
|
||||
val intList = byteArray.map { it.toInt() }
|
||||
println(intList)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.jetpackduba.gitnuro.credentials.streams
|
||||
|
||||
import com.jetpackduba.gitnuro.credentials.sshLib
|
||||
import com.jetpackduba.gitnuro.credentials.ssh_channel
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
class LibSshInputErrStream(private val sshChannel: ssh_channel) : InputStream() {
|
||||
private var cancelled = false
|
||||
private var calls = 0
|
||||
|
||||
// override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||
// return sshLib.ssh_channel_read(sshChannel, b, len, 1)
|
||||
// }
|
||||
|
||||
override fun read(): Int {
|
||||
println("Read error")
|
||||
val buffer = ByteArray(1)
|
||||
|
||||
return if (sshLib.ssh_channel_poll(sshChannel, 1) > 0) {
|
||||
sshLib.ssh_channel_read(sshChannel, buffer, 1, 1)
|
||||
|
||||
val first = buffer.first()
|
||||
println("Read error finished ${first.toInt()}")
|
||||
|
||||
print(String(buffer))
|
||||
|
||||
first.toInt()
|
||||
} else
|
||||
-1
|
||||
}
|
||||
|
||||
// override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||
// calls++
|
||||
//
|
||||
// println("Read error started, call $calls for len of $len with offset $off")
|
||||
//
|
||||
// val byteArray = ByteArray(len)
|
||||
// val result = sshLib.ssh_channel_read(sshChannel, byteArray, len, 1)
|
||||
// for(i in 0 until len) {
|
||||
// b[off + i] = byteArray[i]
|
||||
// }
|
||||
//
|
||||
// println("Read ended ${byteArray.map { it.toInt() }}")
|
||||
//
|
||||
// return result
|
||||
// }
|
||||
//
|
||||
// override fun read(): Int {
|
||||
// val buffer = ByteArray(1)
|
||||
//
|
||||
// sshLib.ssh_channel_read(sshChannel, buffer, 1, 1)
|
||||
//
|
||||
// val first = buffer.first()
|
||||
//
|
||||
// println("Error message is ${String(buffer)}")
|
||||
//
|
||||
// return first.toInt()
|
||||
// }
|
||||
|
||||
override fun close() {
|
||||
println("Closing error")
|
||||
cancelled = true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.jetpackduba.gitnuro.credentials.streams
|
||||
|
||||
import com.jetpackduba.gitnuro.credentials.sshLib
|
||||
import com.jetpackduba.gitnuro.credentials.ssh_channel
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
class LibSshInputStream(private val sshChannel: ssh_channel) : InputStream() {
|
||||
private var calls = 0
|
||||
|
||||
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||
calls++
|
||||
|
||||
println("Read started, call $calls for len of $len with offset $off")
|
||||
|
||||
val byteArray = ByteArray(len)
|
||||
val result = sshLib.ssh_channel_read(sshChannel, byteArray, len, 0)
|
||||
for(i in 0 until len) {
|
||||
b[off + i] = byteArray[i]
|
||||
}
|
||||
|
||||
println("Read ended ${byteArray.map { it.toInt() }}")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun read(): Int {
|
||||
val buffer = ByteArray(1)
|
||||
|
||||
sshLib.ssh_channel_read(sshChannel, buffer, 1, 0)
|
||||
|
||||
val first = buffer.first()
|
||||
|
||||
print(String(buffer))
|
||||
|
||||
return first.toInt()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
println("Closing input")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.jetpackduba.gitnuro.credentials.streams
|
||||
|
||||
import com.jetpackduba.gitnuro.credentials.sshLib
|
||||
import com.jetpackduba.gitnuro.credentials.ssh_channel
|
||||
import java.io.OutputStream
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class LibSshOutputStream(private val sshChannel: ssh_channel) : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
println("write int")
|
||||
|
||||
val byteArrayData = byteArrayOf(b.toByte())
|
||||
write(byteArrayData)
|
||||
|
||||
println("write int finished")
|
||||
}
|
||||
|
||||
override fun write(b: ByteArray) {
|
||||
println("write byte")
|
||||
sshLib.ssh_channel_write(sshChannel, b, b.size)
|
||||
println("write byte finished")
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
println("Closing output")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun checkValidResult(result: Int) {
|
||||
if (result != 0)
|
||||
throw Exception("Result is $result")
|
||||
}
|
Loading…
Reference in a new issue