alexcrichton / ssh2-rs

Rust bindings for libssh2
https://docs.rs/ssh2
Apache License 2.0
467 stars 142 forks source link

Help communicating with a device #286

Open jmcgill298 opened 1 year ago

jmcgill298 commented 1 year ago

I reached out on the beginners channel on Discord, but I haven't been able to get help there, so I thought maybe I could open an issue on here. First, I want to say that I tested against a unix device, and this worked as shown in the example; the issue I'm having is with a network appliance. I first tried using the channel.exec() function, but this gave me an exit code of 127, and the trace logs showed sh: show: command not found. The command I'm trying to send is show version, so I started thinking that this function was doing more than just sending messages back and forth across the channel. I thought that perhaps the channel.write() or channel.write_all() would be better suited for my purposes. However, when using these functions, the channel hangs as though it is never done writing, and I'm not sure how to get it to end. I would appreciate any help you can provide.

using exec:

fn main() {
    let tcp = TcpStream::connect(ip).unwrap();
    let mut sess = Session::new().unwrap();
    sess.trace(TraceFlags::all());
    sess.set_tcp_stream(tcp);
    sess.handshake().unwrap();
    sess.userauth_password(username, password).unwrap();
    assert!(sess.authenticated());
    let mut channel = sess.channel_session().unwrap();
    channel.exec("show version").unwrap();
    let mut s = String::new();
    channel.read_to_string(&mut s).unwrap();
    println!("{}", s);
    channel.wait_close().unwrap();
   println!("{}", s);```
}

using write[_all]:

fn main() {
    let tcp = TcpStream::connect("").unwrap();
    let mut sess = Session::new().unwrap();
    sess.set_tcp_stream(tcp);
    sess.handshake().unwrap();
    sess.userauth_password("", "").unwrap();
    assert!(sess.authenticated());
    let mut channel = sess.channel_session().unwrap();
    channel.write(b"show version\n").unwrap();
    // nothing below here is executed and program hangs
    println!("{}", "called write_all");
    channel.flush().unwrap();
    let mut s = String::new();
    channel.read_to_string(&mut s).unwrap();
    println!("{}", s);
    channel.wait_close().unwrap();
}

An example that works using python:

import time

from paramiko import SSHClient, AutoAddPolicy

RECIEVE_BYTES = 4096

def read_from_channel(channel):
    response = []
    time.sleep(.2)
    while channel.recv_ready():
        data = channel.recv(RECIEVE_BYTES)
        response.append(data.decode())
        time.sleep(.2)
    return "".join(response)

client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect("", username="", password="")
channel = client.invoke_shell()
# clear channel
read_from_channel(channel)
channel.sendall("show version\n")
version_data = read_from_channel(channel)
channel.close()
client.close()

print(version_data)
jmcgill298 commented 1 year ago

so I did find out that I needed to add channel.shell().unwrap(); before calling channel.write(message) in order to have available bytes to write across the channel. However, doing this results in the same issue as using channel.exec(message) where the response is -vbash: line 1: show: command not found. I'm not sure why bash is present since the remote system is not bash, nor why it says show: command not found when it should be sending show version, and the normal response from the remote system would be something like incomplete command.