I'm trying to send a simple command over SSH using SSH.NET. Even the simplest test code fails. Here is a snippet.
using (var ssh = new SshClient("ip", port, "username", "password"))
{
ssh.Connect();
while (true)
{
var result = ssh.RunCommand("AT").Execute();
Console.Out.WriteLine(result);
}
}
The AT command should echo back OK but doesn't. Instead I receive a custom timeout message issued by the SSH target. I see the device name in the timeout message which corresponds to the prompt it uses and from that i can conclude that the login works (also tested with various SSH programs) but the command itself is not executed. I tried adding \n and \r\n to the commands but to no results. What am I doing wrong?
Edit 1:
output from result is \r\nCommand Line Interface\r\nDeviceName> Custom idle timeout I think the line endings are converted to Windows ones by Visual Studio.
Edit 2:
Using Plink plink.exe username#ip -pw password "AT" > log.txt results in the same output as Visual Studio. Plink waits till timeout and terminates and log.txtcontains \r\nCommand Line Interface\r\nDeviceName> Custom idle timeout.
Using PuTTY I see that
Using username "username".
username#host's password:
Entering character mode
Escape character is '^]'.
Command Line Interface
DeviceName>
is written before you can start entering commands. Might it be that the command is entered before the host is ready to receive it and as a result the command hangs until some reaction comes?
Your server obviously does not support the SSH "exec" channel that is used behind the SSH.NET SshClient.RunCommand method and PLink's plink.exe command syntax.
Or actually it does support it, but incorrectly. It seems to start an interactive session (a shell), instead of executing the command.
To confirm this assumption, try this with PLink:
echo AT| plink username#ip -pw password > log.txt
If that works, you may need to use the SshClient.CreateShell (SSH "shell" channel") and write the command to its input stream, instead of using the SshClient.RunCommand. While this is generally a wrong approach, you need to take this approach due to your broken server.
A similar question for Python/Paramiko:
Executing command using Paramiko exec_command on device is not working.
Related
I'm trying to send a command to a device using windows terminal, so what I do is
[System.IO.Ports.SerialPort]::getportnames()
For getting the good port, then
$port= new-Object System.IO.Ports.SerialPort COM3,9600,None,8,one
$port.open()
for configure it and open it
After that I'm trying to send a command in this form :
<DLE> <command> <CR>
But I clearly have no idea how to do it, I try with the command OFF (easiest because it power off my device)
So I test with this code :
[Byte[]] $request = 0x10,79,70,70,0x0D
$port.Write($request)
Didn't work so I test this :
[Char[]] $request = 0x10,"O","F","F",0x0D
$port.Write($request)
Same didn't work,thing is I don't know how to writing it's absolutly not explain in the manual of my device they just give the command form I wrote above and the list of command type OFF / SET / CLR ...
If someone can help me with this thanks :)
I just succeeded. The successful syntax of the command was:
[Byte[]] $request = 0x10,0x4F,0x46,0x46,0x0D
$port.Write($request,0,$request.count)
I'm still a little surprised there is absolutely no explanation in the manual of my device. Maybe the command was obvious, but not for me. It was the first time I used PowerShell.
I created a program using Renci SSH.NET library. Its sending all the commands and reading the result normally. However, when I send the command below:
client.RunCommand("cli");
The program hangs on this line indefinitely.
Any explanation of what is happening?
The cli is a command is used on Juniper switches/routers.
AFAIK, cli is a kind of a shell/interactive program. So I assume you have tried to do something like:
client.RunCommand("cli");
client.RunCommand("some cli subcommand");
That's wrong. cli will keep waiting for subcommands and never exit, until you explicitly close it with a respective command (like exit). And after it exits, the server will try to execute the cli subcommand as a separate top-level command, failing too.
You have to feed the "cli subcommand" to the input of the cli command. But SSH.NET unfortunately does not support providing an input with the SshClient.RunCommand/SshClient.CreateCommand interface. See Allow writing to SshCommand.
There are two solutions:
Use the appropriate syntax of the server's shell to generate the input on the server, like:
client.RunCommand("echo \"cli subcommand\" | cli");
Or use a shell session (what is otherwise a not recommended approach for automating a command execution).
Use SshClient.CreateShellStream or SshClient.CreateShell and send the commands to its input:
"cli\n" + "cli subcommand\n"
For a sample code see Providing subcommands to a command (sudo/su) executed with SSH.NET SshClient.CreateShellStream or C# send Ctrl+Y over SSH.NET.
I'm using the following code to export a copy of MySql database on a remove server using SSH.NET:
using (SshClient client = new SshClient(sshConnectionInfo))
{
client.Connect();
//strCmd is:
// mysqldump -h "<server>.dreamhosters.com" -u "<dbuser>" -p"<actual_password>" "<dbid>" > "/home/<user_name>/<temp_file_name>.sql"
//
// with <...> parts are obviously filled in with correct credentials
SshCommand resCmd = client.RunCommand(strCmd);
//Check result
string strExpDesc = resCmd.Error;
if (string.IsNullOrWhiteSpace(strExpDesc))
{
Console.WriteLine("Exported OK");
}
else
{
Console.WriteLine("Error: " + strExpDesc);
}
}
This worked really well until this month when the shared hosting company (that my database is hosted with) had upgraded their version of Ubuntu server, so the mysqldump command above started returning the following warning:
[Warning] Using a password on the command line interface can be
insecure.
Which my script interprets as an error and fails.
I contacted the hosting company, but their tech support was less than useful. They told me to type in the password instead of specifying it in the command line. Thus my questions:
How to use SSH.NET to interact with the remote server via SSH and send it the password after its prompt?
Otherwise, can I mute that warning from my script w/o having access to the server configuration?
The normal approach to suppressing that warning from MySQL is to put the username and password in an options file, and include the options file in in place of the -u and -p , using the --defaults-extra-file option, e.g.
mysqldump --defaults-extra-file=/path/config.cnf -h "myhost" "<dbid>"
(NOTE: if it's provided, the --defaults-extra-file option must be first option.)
The file would have the user and password options under the [mysqldump] section, something like:
[mysqldump]
user=jonsnow
password=kn0wsn0thing
The credentials could be provided in the [client] section of the optionas file. Since mysqldump is a "standard client" it reads the [client] section.
[client]
user=alannister
password=alwayspayshisdebts
The options file should be properly protected, since it contains credentials. Read permission should be restricted, only the unix user that is connecting via ssh.
chown sshuser /path/config.cnf
chmod 600 /path/config.cnf
(where sshuser is the username used in the ssh connection)
I think there may also be some specific paths and filenames that are automatically included as options files, if they are found, when mysqldump starts. Similar to how MySQL server finds the my.cnf file when it starts. It might be possible to leverage that, in place of explicit --defaults-extra-file option.
MySQL Reference Manual explains usage of options files here:
https://dev.mysql.com/doc/refman/8.0/en/option-files.html
You can write the credentials to input of the remote mysqldump process. But SSH.NET unfortunately does not support providing an input with the CreateCommand interface.
You have to open a shell session (what is otherwise a not recommended approach for automating a command execution).
Use SshClient.CreateShellStream or SshClient.CreateShell and send the mysqldump command and credentials to its input:
"mysqldump ...\nusername\npassword"
For a sample code see C# send Ctrl+Y over SSH.NET.
See also Providing input/subcommands to a command (cli) executed with SSH.NET SshClient.RunCommand.
I have a web service that uses SSH.NET to call a shell script on a Unix box.
If I run the script normally, it works fine, does its work correctly on the Informix DB.
Just some background:
I call a script that executes a .4gl (cant show this as its business knowledge).
The g4l is giving the following error back in a log, when I execute it with SSH.NET:
fglgo: error while loading shared libraries: libiffgisql.so: cannot open shared object file: No such file or directory
file_load ended: 2017-09-21 15:37:01
C# code to execute SSH.NET script
sshclients = new SshClient(p, 22, username, password);
sshclients.Connect();
sshclients.KeepAliveInterval = new TimeSpan(0, 0, 1);
sshclients.RunCommand("sh " + Script_dir);
I added the KeepAliveInterval, to see, if it helps.
My question is the error I am getting from Unix/4gl.
Why is this happening and who can I get the script to execute correctly?
The SshClient.RunCommand uses SSH "exec" channel internally. It, by default, (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced. And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.
So, in your case, the PATH is probably set differently; and consequently the shared object cannot be found.
To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your g4l command. Check Session > Close window on exit > Never and open the session. You should get the same "No such file or directory" error.
Ways to fix this, in preference order:
Fix the scripts not to rely on a specific environment.
Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
sshclients.RunCommand("PATH=\"$PATH;/path/to/g4l\" && sh ...");
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel.
Though SSH.NET does not support this. You would have to modify its code issue SendPseudoTerminalRequest request in .RunCommand implementation (I didn't test this).
You can also try to use "shell" channel using .CreateShell method. For it, SSH.NET does support pseudo terminal allocation.
Though, using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
For a similar issues, see
Renci SSH.NET - no result string returned for opmnctl
Certain Unix commands fail with "... not found", when executed through Java using JSch
Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
JSch: Is there a way to expose user environment variables to "exec" channel?
Have seen similar questions asked by Informix-4gl developers as they transition to FourJs Genero and using its Web Services functionality. The question I'll put to them is "who owns the fglgo/fglrun process that the Genero Application Server has launched, where is it running from, and what is its environment". If needed, I'll illustrate with a simple program that does something like ...
MAIN
RUN "env > /tmp/myname.txt"
RUN "who >> /tmp/myname.txt"
RUN "pwd >> /tmp/myname.txt"
END MAIN
... and say compare with when program is running from command line. It is normally a case like in the earlier answer of configuring so that the environment is set correctly before the 4gl program is executed.
I've been trying very hard to automatically ssh into a Linux server. What's crazy, is that I can create a .bat script, that will do it, but I have to be there physically, to type in the password.
I've tried automating this using System.Diagnostics.Process object in c# to no end. There is no way, I've found, to make this object allow you to see the password prompt. It out puts every line up until that point, and then doesn't output any more lines.
cmd.exe, does really well at allowing you to jump from process to process in a script; It consolidates everything into one screen, and just prompts you for things such as passwords, and then you type them in.
Is there any simple way, in C#, to make cmd.exe think you are a human being typing in it, so I can simulated this programmatically? Otherwise, System.Diagnostics.Process, doesn't seem to offer a way to interacted with a process that outputs a password prompt. You never get to see the prompt that clearly happens when you run the same thing in cmd.exe
cmd.exe is not the SSH client; it's just a shell for one. Explore the capabilities of the actual client. In PuTTY (very popular!) there's an autologon feature, if you have an SSH key.
You could use
Windows.Forms.SendKeys()
to send password as keystrokes
Reference Material
Without knowing which commandline tool you are using to ssh to the server, isn't there a commandline parameter to provide a password.
C:\> ssh user#ip -p password
Or have you tried input redirection:
ssh user#ip <
Yourpassword
EOF
In looking into your earlier question, what is the trouble you are having in using the programmer interface?
Are you sure you need a full fledged ssh client, or do you just want to execute a command remotely. If it is the latter you might want to check out plink.
plink.exe -ssh user#host -2 -i user.ppk -m command_line.txt
How to create the ppk file: http://theillustratednetwork.mvps.org/Ssh/Private-publicKey.html