The first thing you will probably want to do is to connect to an SSH server using Maverick.NET. This is a fairly straightforward procedure using the SSHConnector class. This class creates the initial connection, determines which protocol versions are supported by the server and depending upon which settings have been specified informs the server which protocol version to use for the newly created connection.
using System;
using Maverick.SSH;
using Maverick.PublicKey;
using Maverick.Crypto.Util;
namespace CSharp_Example
{
class GettingStarted
{
static void Main(string[] args)
{
SSHConnector con = SSHConnector.Create();
The next few sections will guide you through making the initial connection, authenticating the user and executing a command or starting the users shell for a simple console based SSH application. First lets look at the SSHConnector and the basic connection options available.
The SSHConnector class is designed to be configured and reused for all subsequent connections that require the same settings. To create a connection we use the following code
SSHClient ssh = con.Connect(new TcpClientTransport("cvs", 22), "lee", false);
First lets take a look at the parameters of the connect method. The first parameter provides the transport mechanism for the connection which in this case is a Socket. Maverick has been designed to operate over a transport interface called SSHTransport which at its simplist requires just a standard stream. This enables Maverick to operate over any type of current or future communication medium supported by the runtime. Notice that we have passed the hostname of the computer we wish to connect to and the port number to the TcpIpTransport constructor, which in our case is the default SSH port number 22. The second parameter identifies the username for the connection.
Once a connection has been established the method returns an instance of the SSHClient interface. This interface provides a protocol independent contract for an SSH client allowing applications to support both versions of the SSH protocol without the need for seperate client libraries. It should be noted that there are many differences between the protocols and some restrictions may apply when using the interface, these will be identified as and when required but for general usage this interface provides a suitable means for SSH protocol independence.
When the client connects to the server, the server supplies its public key for the client to verify. It is good practice to check the supplied key to verify the identity of the server and with Maverick this is possible using the HostKeyVerification interface. You can provide your own implementation of this interface if you require custom handling of the server's public key however most SSH implementations verify the key against a list of known hosts stored in a special file $HOME/.ssh/known_hosts. Maverick provides an abstract implementation called AbstractKnownHostsKeyVerification to handle the reading of keys from the known_hosts file should you want to prompt the user within your own user interface but for the purposes of this command line example we will use the ConsoleKnownHostsKeyVerification class to prompt the user through System.out.
To configure our SSHConnector to use host key verification we need to import the following class:
using Maverick.PublicKey.ConsoleKnownHostsKeyVerification
And add the following code before we call the SSHConnector's connect method:
con.KnownHosts = new ConsoleKnownHostsKeyVerification();
Now when we run the example we should be promted in the console to confirm the identity of the server.
The host shell.sourceforge.net is currently unknown to the system
The host key (ssh-dss) fingerprint is: f2:fd:57:3f:b1:f4:90:2d:2d:e8:50:de:99:9c:dd:fe:13:1f:8c:8c
Do you want to allow this host key? [Yes|No|Always]:
Let now take a look at the SSHClient class; the code so far has created a connection to the SSH server and returned an instance of SSHClient. There are two concrete implementations of this interface, SSH1Client and SSH2Client which provide the SSH1 and SSH2 protocol implementations respectivley. The default behaviour for the SSHConnector is to select an SSH2 connection where ever possible, this is because SSH1 has known vunerabilities and should only be used when no SSH2 connection is available. If you wish to force the use of a specific version you can configure the SSHConnector using the following code before calling the connect method:
con.SupportedVersions = SSHConnector.SSH1;
con.SupportedVersions = SSHConnector.SSH2;
con.SupportedVersions = SSHConnector.SSH1 | SSHConnector.SSH2;
We now have a connection to the SSH server and the next task is to authenticate the user we declared earlier in our connect method. We will not be able to perform any other operation until the user has been authenticated. Password authentication is ideal for first time users as it requires no additional configuration within the SSH client or server. The user simply supplies his username and password to the client which is then transmitted over the encrypted connection to the server. The server then checks that the given password is acceptable to the native password-authentication mechanism of the host operating system and returns the result to the client.
Add the following lines to our example code:
PasswordAuthentication pwd = new PasswordAuthentication();
This creates a PasswordAuthentication object and configures it with the users password. We are now ready to authenticate the user.
AuthenticationResult result = ssh.Authenticate(pwd);
while(result!=AuthenticationResult.COMPLETE); setup the session for our command. First we open the channel itself by calling the SSHClient method:
SSHSession session = ssh.OpenSessionChannel();
Now that we have a session instance we need to configure it for our command, there are several options that can be set before we invoke one of the methods that will start the session.
Requesting a Pseudo Terminal
A pseudo terminal is a device that imitates a terminal. Rather than being connected to an actual terminal, a pseudo-terminal (or pty) is connected to a process. If the command you are executing is expecting a terminal (such as a shell command) you can request that a pseudo terminal be attached to the process by calling the requestPseudoTerminal method.
session.RequestPseudoTerminal("vt100", 80, 24, 0, 0);
Invoking a command
After the above operations have been performed you can then request that the session either start the user's shell, execute a specific command or start an SSH subsystem (such as SFTP). You should not invoke a subsystem unless you are able to read/write the subsystem protocol, there are many additional utilities within Maverick that provide for the available subsystems.
To start the users default shell use:
session.StartShell();
Or to execute a specific command use:
session.ExecuteCommand("ls");
An important note to remember is that this does not execute a shell command. You cannot for instance issue the command executeCommand("dir")" on the Windows Operating system as this is a shell command, instead use "cmd.exe /C dir". This method executes a binary executable and so should be used to execute any program other than the users shell.
Handling Session Data
Once the session has been configured and a command or shell has been started, you can begin to transfer data to and from the remote computer using the sessions IO streams. These streams provide you with a standardized interface for reading and writing the data.
The Session Channel's OutputStream
The format of writing data varies according to how you configured the session, for example if you executed the users shell then the data should be written as if the user had entered the commands interactively.
StreamWriter writer = new System.IO.StreamWriter(session.GetStream());
String line;
while(!session.IsClosed)
{
line = Console.ReadLine();
if(!session.IsClosed)
{
writer.Write(line + "r");
writer.Flush();
}
}
The Session Channel's InputStream
System.IO.TextReader reader = new System.IO.StreamReader(session.GetStream());
String line;
while((line = reader.ReadLine()) != null)
System.Console.WriteLine(line);
Reading from STDERR
The session also provides the stderr data provided by the remote session. Again an InputStream is provided.
TextWriter errorWriter = session.getStderrStream();
Closing the Session
When executing commands the session is normally closed by the server as soon as the command has completed. If you are reading from the session's InputStream and the session closes, the stream will return EOF. When starting the users shell, the session will not end until the user quits the terminal by typing the "exit" command however you can close the session at any time by calling the session's close method
session.Close();
Disconnecting
The connection can be terminated by either side. To terminate the connection call the SSHClient disconnect method:
ssh.Disconnect();
So we can now execute a single command on the remote server, but what's that I hear you say? I want to execute more than one command? Well if you cast your mind back I told you that the SSH protocol provides multiplexed channels over a single connection, so executing another command is as simple as executing the first, just create a new instance of the SessionChannelClient for every command you want to execute. You can execute them simultaneously or one after another, but always create a new session (since the session is closed when the command finishes and the protocol does not allow for re-using of a session to execute another command).
There is a drawback to this in that the process environment is not passed on from one session to another, so you cannot for example execute a command to change directory and then another to execute a script in that directory, since the change directory is lost when the session closes and the new command starts back in the default working directory. Of course you could always put the cd command into the script? Or use the shell to execute both commands. This subject is certainly a bit more advanced so I will leave it for another day and pencil in a new article to discuss all the alternatives for executing multiple commands.
This concludes our getting started tutorial, you should now have a basic working knowledge of how to connect, authenticate and execute commands using Maverick.NET.
2005 SSHTools Ltd, All Rights Reserved