I have a C# application that needs to run certain commands on a piece of hardware over SSH. The application is using SSH.Net
to make the connection, send the command, and the read the result. I have this working if I connect to my local machine using OpenSSH. Finally, I wanted to go a step further and setup my own SSH server so I could simulate multiple hardware devices at one time (need to simulate having 50+ devices to SSH into).
For this I have setup a simple SSH server using nodejs and the ssh2
package. So far I have the client connected, authenticated (all connections are accepted for now), and I can see a session
object being created. Although where I'm hitting a wall is with the execution of commands sent by the client. I noticed that ssh2
has an event for exec
on the session
object but this never seems to trigger (regardless of what i put in SSH.Net
's ShellStream
).
The C# client code that initiates the connection is the following (command
is already defined that the command string to be executed):
using(SshClient client = new SshClient(hostname, port, username, password))
{
try
{
client.ErrorOccurred += Client_ErrorOccurred;
client.Connect();
ShellStream shellStream = client.CreateShellStream("xterm", Columns, Rows, Width, Height, BufferSize, terminalModes);
var initialPrompt = await ReadDataAsync(shellStream);
// The command I write to the stream will get executed on OpenSSH
// but not on the nodejs SSH server
shellStream.WriteLine(command);
var output = await ReadDataAsync(shellStream);
var results = $"Command: {command} \nResult: {output}";
client.Disconnect();
Console.WriteLine($"Prompt: {initialPrompt} \n{results}\n");
}
catch (Exception ex)
{
Console.WriteLine($"Exception during SSH connection: {ex.ToString()}");
}
}
The nodejs server code that set ups the ssh2 server is following:
new ssh2.Server({
hostKeys: [fs.readFileSync('host.key')]
}, function(client) {
console.log('Client connected!');
client.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
console.log('Client authenticated!');
client.on('session', function(accept, reject) {
var session = accept();
// Code gets here but never triggers the exec
session.once('exec', function(accept, reject, info) {
console.log('Client wants to execute: ' + inspect(info.command));
var stream = accept();
stream.write('returned result\n');
stream.exit(0);
stream.end();
});
});
}).on('end', function() {
console.log('Client disconnected');
});
}).listen(port, '127.0.0.1', function() {
console.log('Listening on port ' + this.address().port);
});
I have seen various ssh2
client examples invoking a client.exec
function but I was assuming that it did not matter that my client was not using the ssh2
node package. Is there something that I'm missing here?
The "exec" Node.js server session event is for "non-interactive (exec) command execution". By which they most likely mean SSH "exec" channel (which is intended for "non-interactive command execution").
To execute a command using "exec" SSH channel in SSH.NET, use
SshClient.RunCommand
.On the contrary
SshClient.CreateShellStream
uses SSH "shell" channel, which is intended for implementing an interactive shell session.For that, you need to handle "shell" Node.js server session event.