I'm trying to realize a bot that simulates an user that write/read on a ssh console in Java.
I'm using the JSCH library to manage the ssh connection.
This is the code from which I started:
JSch jsch = new JSch();
Session session = jsch.getSession(username, ipAddress, port);
session.setPassword(password);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect(connectionTimeoutInMillis);
Channel channel = session.openChannel("shell");
InputStream is = new InputStream();
OutputStream out= new OutputStream();
channel.setInputStream(is);
channel.setOutputStream(out);
channel.connect();
channel.disconnect();
is.close();
out.close();
session.disconnect();
Obviously the InputStream and OutputStream in the code are wrong, I need to use something that the bot can use to send a String (a command line) and to receive a String(the result of the command execution), what type of streams should I use to obtain this?
Furthermore I noticed that if I send a command and use the System.out as output stream in many cases the output is empty because (I'm almost sure about this) the Java application terminates before that the command execution has produced the result. What is the best practice to tell to JSCH channel listener "wait till the command execution has completed" and then go on? I could use a Thread.sleep(someTime) after the command execution but I don't like much it for obvious reasons.
Consider using a third-party Expect-like Java library to ease the interaction with a remote shell. Here is a good set of options you can try:
Expect4J
ExpectJ
Expect-for-Java
You can also take a look at my own open source project which I created some time ago as the successor to the existing ones. It's called ExpectIt. The advantages of my library are stated on the project home page.
Here is an example of interacting with a public remote SSH service using JSch. It should be easy to adopt it for your use case.
JSch jSch = new JSch();
Session session = jSch.getSession("new", "sdf.org");
session.connect();
Channel channel = session.openChannel("shell");
Expect expect = new ExpectBuilder()
.withOutput(channel.getOutputStream())
.withInputs(channel.getInputStream(), channel.getExtInputStream())
.withErrorOnTimeout(true)
.build();
try {
expect.expect(contains("[RETURN]"));
expect.sendLine();
String ipAddress = expect.expect(regexp("Trying (.*)\\.\\.\\.")).group(1);
System.out.println("Captured IP: " + ipAddress);
expect.expect(contains("login:"));
expect.sendLine("new");
expect.expect(contains("(Y/N)"));
expect.send("N");
expect.expect(regexp(": $"));
expect.send("\b");
expect.expect(regexp("\\(y\\/n\\)"));
expect.sendLine("y");
expect.expect(contains("Would you like to sign the guestbook?"));
expect.send("n");
expect.expect(contains("[RETURN]"));
expect.sendLine();
} finally {
session.close();
ssh.close();
expect.close();
}
Here is the link to the complete workable example.
Related
The problem is that the SSH connection requires the provision of another userid and password info after the general log in.
I am using JSch to connect to the remote server. It takes input in the form of a InputStream. And this InputStream can only be passed once. This causes problems as the session is interactive.
I've tried passing the input stream as linefeed separate values ("username\npassword\n"). This however does not work. Any suggestions would be welcome. Even if I have to look for a new Java library altogether.
try {
JSch jsch=new JSch();
Session session=jsch.getSession( "username1", "host", 22);
session.setPassword("password1");
session.setConfig("StrictHostKeyChecking", "no");
session.connect(30000);
Channel channel=session.openChannel("shell");
String data = "username2\npassword2\n";
channel.setInputStream(new ByteArrayInputStream(data.getBytes()));
channel.setOutputStream(System.out);
channel.connect(3*1000);
} catch (Exception e) {
e.printStackTrace();
}
The password is not entered properly and it does not navigate to the next set of instruction displayed by the ssh connection.
However, if I try the same with the system console (System.in) as the input stream, it works as expected.
If I understand your question correctly, it looks like the password is provided too quickly and the way the server is implemented, it discards the input that comes too early (before a prompt).
You may need to wait before sending the password.
channel.connect();
OutputStream out = channel.getOutputStream();
out.write(("username2\n").getBytes());
out.flush();
Thread.sleep(1000);
out.write(("password2\n").getBytes());
out.flush();
A more advanced solution would be to implement an Expect-like functionality.
I'm making an Android app for personal use. The goal is to give an input on my phone, send the input to a Python script on my PC, and return the output as a string.
What is the best way to make it connect to my Windows on PC, execute the python file, and return the result as simple string output?
Have the app write something to Firebase, have the Python script monitoring Firebase and react, then write back to Firebase which the app is monitoring
https://firebase.google.com/docs/android/setup
https://pypi.org/project/python-firebase/
https://firebase.google.com/docs/database
You can use SSH to run commands on a remote computer
for this purpose there are several libraries in Java, for example, you can use JSH Library
it's a simple example:
public static String executeRemoteCommand(
String username,
String password,
String hostname,
int port) throws Exception {
JSch jsch = new JSch();
Session session = jsch.getSession(username, hostname, 22);
session.setPassword(password);
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect();
// SSH Channel
ChannelExec channelssh = (ChannelExec) session.openChannel("exec");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
channelssh.setOutputStream(baos);
// Execute command
channelssh.setCommand("ls");
channelssh.connect();
channelssh.disconnect();
return baos.toString();
}
Resource link: SSH, execute remote commands with Android
I have a binary executable file named as "datasimulator" that runs in a remote linux machine which starts an interactive process where a user can provide input and get its corresponding output like below -
[root#host]$ ./datasimulator
> set country USA //user passing this command
Country set to USA //response from process
> set country Canada
Country set to Canada
>exit
Thank you for using datasimulator.
[root#host]$
I am trying to automate this process using java jsch utility where through my java program I can pass input and get the output back to my program. Though I am able to start the datasimulator through jsch ssh connection but not able to pass input to this simulator process. Kindly help.
Below is the java program which I am trying to use -
JSch jsch = new JSch(); session = jsch.getSession(username, host, 22);
session.setPassword(password);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
channel=session.openChannel("shell");
OutputStream ops = channel.getOutputStream();
ps = new PrintStream(ops, true);
channel.connect();
ps.println("cd /path/To/datasimulator/ && ./datasimulator");
ps.println("Country set to USA");
Thread.sleep(5000);
ps.println("Country set to Canada");
Thread.sleep(5000);
ps.close();
channel.disconnect();
session.disconnect();
This program starts the datasimulator but not able to execute next consecutive commands successfully.
I'm trying to write to write to a named pipe over a connection facilitated by jsch.
// connect to server
JSch ssh = new JSch();
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
System.out.println("Establishing Connection...");
session.connect();
System.out.println("Connection established.");
System.out.println("Crating SFTP Channel.");
ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
sftpChannel.connect();
System.out.println("SFTP Channel created.");
// write to pipe
OutputStream strm = sftpChannel.put(remoteFile);
// failed attempts
// BufferedWriter wrtr = new BufferedWriter(new PrintWriter(new OutputStreamWriter(strm)));
// PrintWriter wrtr = new PrintWriter(new BufferedOutputStream(strm));
// Current version
BufferedWriter wrtr = new BufferedWriter(new PrintWriter(strm));
wrtr.write("hello world");
wrtr.flush();
session.disconnect();
sftpChannel.disconnect();
wrtr.close();
The connect to server part is essentially an exact copy from: SSH connection with Java
The code will even wait for something to be reading the pipe on the other side, meaning that if I don't use:
cat pipe
It will wait till I do so, and then once I have it will print out nothing and the cat pipe call will be over. Essentially it appears that I'm writing "" to the pipe instead of "hello world"
Any help would be much appreciated, thanks.
I suspect that if you talked to any SFTP server software author, he'd tell you that interacting with named pipes through SFTP isn't supported, and that you're on your own. Having said that, you could try requesting append mode for the put operation:
OutputStream strm = sftpChannel.put(remoteFile, ChannelSftp.APPEND);
^^^^^^^^^^^^^^^^^^
BufferedWriter wrtr = new BufferedWriter(new PrintWriter(strm));
wrtr.write("hello world");
It might work, or it might not. It depends on the SFTP server.
At the protocol level, an SFTP write request specifies what file to write to, where in the file to write it, and what data to write. Based on the the OpenSSH 5.8 source code that I have at hand, that version of the SFTP server program will always seek to the correct location in the file before writing data to it. Pipes don't support seeking, so the sftp-server program would report a seek failure to the client without trying to write the data.
However, the OpenSSH 6.6 version of sftp-server will omit the seek if the file was opened in append mode. I haven't tested it, but it may be possible to write to a named pipe using that version of the server, if the client requests append mode for the file in the first place. And by extension, it may work for some other versions of the OpenSSH server as well.
I try to use the library JSch - Java Secure Channel make an ssh connection in my Android app, it works.
Now I would like to execute a command and retrieve the result.
I tried several methods that works best is this. However, this method works only in part, because for some reason I can not explain, my program stops at the end of my while loop, yet I'm the result of the command that appears in my log.
Here is my code :
public static String executeRemoteCommand(String username, String password, String hostname, int port) throws Exception {
JSch jsch = new JSch();
Session session = jsch.getSession(username, hostname, port);
session.setPassword(password);
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect();
Channel channel = session.openChannel("shell");
channel.connect();
DataInputStream dataIn = new DataInputStream(channel.getInputStream());
DataOutputStream dataOut = new DataOutputStream(channel.getOutputStream());
// send ls command to the server
dataOut.writeBytes("ls\r\n");
dataOut.flush();
// and print the response
String line = dataIn.readLine();
String result = line + "\n";
while ((line = dataIn.readLine()) != null) {
result += line + "\n";
Log.i("TAG", "Line: "+line);
}
dataIn.close();
dataOut.close();
channel.disconnect();
session.disconnect();
return result;
}
Does anyone else have a better way to run a command sheel with JSch?
Thank you in advance !
Your method stops in the loop (instead of finishing it) because the remote shell doesn't close the output stream.
It has no reason to do this, since there you could send more commands.
If you only want to execute a single command (or a series of commands known before), you shouldn't use a Shell channel, but an "exec" channel.
This way the remote shell (which executes your command) will finish when your command is finished, and then the server will close the stream. So your loop will finish, and then you can close the streams.
If you think you need a shell channel (for example, if you need to fire up multiple commands in the same context, and react to one's output before deciding what would be the next one), you'll need some way to know when one command is finished (e.g. by recognizing the prompt), and then send the next one. To quit, either close the output stream or send a "logout" or "exit" command (both work with any standard unix shell, other shells might need different commands), then the remote site should close the other stream, too.
By the way, while disabling strict host key checking is convenient, it also opens up your connection to a man-in-the-middle attack, and in case of password authentication, the attacker can grab your password. The right way to do this would be to set up a correctly initialized host key repository to recognize the remote host's key.
As far as I am aware JSch is the only real option.
I have found that Jsch errors tend to take some digging. But in the first instance you will want to catch and print out the errors as a minimum.
try{
JSch jsch = new JSch();
Session session = jsch.getSession(username, hostname, port);
... omitted
} catch (Exception e) {
System.out.println(e)
}
also have a look a the example code on the site