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.
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 am using the excellent com.jcraft.jsch API to connect to a remote server using the following code.
JSch ssh = new JSch();
JSch.setConfig(FileTransferConstants.STRICT_HOST_KEY_CHECKING, FileTransferConstants.NO);
session = ssh.getSession(user, host, port);
session.setPassword(password);
session.connect();
channel = session.openChannel(FileTransferConstants.SFTP);
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
Once connected, I then use SFTP to download some log files. This works very well.
Once the files have been retrieved, I interrogate them locally based on the timestamps of the log entries - I am looking for entries within a specific time window.
Up until now it has caused me no problem because my local system timestamp has been very similar to the remote timestamp. But recently my tests started failing due to there now being a 5 minute discrepancy between my local timestamp and that on the remote server.
So my question is, is there is a simple way of getting the remote system time using Jsch? If so, I would simply retrieve this and use it in my tests instead of my local system time. Then hopefully the discrepancy issue should immediately go away!
Thank you for reading and considering my question.
Adding working suggestion from comment as an answer if anybody else faces the same problem.
ChannelExec channelExec = (ChannelExec)session.openChannel("exec");
InputStream in = channelExec.getInputStream();
channelExec.setCommand("date +%m%d%Y%H%M%S"); //Date format could be changed to your desired format
channelExec.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
int index = 0;
while ((line = reader.readLine()) != null) {
System.out.println(++index + " : " + line);
}
channelExec.disconnect();
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.
I have the below code which works for FTP. How do I make it to work for SFTP
((ChannelExec) channel).setCommand(cmd);
channel.setXForwarding(true);
channel.setInputStream(System.in);
InputStream in=channel.getInputStream();
channel.connect();
return in;
I know that I need to use ChannelSftp instead of Channel class, but I get type cast error in the setcommand line.
Cannot cast type ChannelSftp to ChannelExec
The first thing to understand is SFTP is different than FTP or FTP/s. SFTP works off of SSH whereas FTP/s uses SSL.
That being said, JSCH provides a pretty straight forward way to use SFTP, including setting X forwarding. Take a look at the examples as well as the linked question from mabbas.
Based upon your comment, it appears that you actually want a remote shell to be invoked/executed against, try the following to see if it'll do what you need:
//connect to the remote shell
Channel channel=session.openChannel("shell");
((ChannelShell)channel).setAgentForwarding(true);
channel.setInputStream(System.in); //Send commands here
channel.setOutputStream(System.out); //output responses here
channel.connect();
You won't be able to use ChannelSftp as it does not have a setCommand or exec method
If you're using JSCH, they have several example programs which illustrate how to use the library. The SFTP client example illustrates how to open an SFTP session.
Session session=jsch.getSession(user, host, port);
...
session.connect();
Channel channel=session.openChannel("sftp");
channel.connect();
ChannelSftp c=(ChannelSftp)channel;
That's all you have to do. ChannelSftp contains functions to send and receive files, get file listings, and so on. You don't have to access the channel's input or output streams.
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