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();
Related
I am facing problem while transferring the file using JSCH in java. The data in the file is getting corrupted, and this is happening intermittently . I mean sometimes the file is getting uploaded properly and majority times we noticed data is being corrupted when the file size is greater than 5 MB.
The program is behaving differently in different scenarios.
Windows-10 : Program works perfectly fine without issues for all sizes of files.
Unix : Program works fine for files less than 2 MB. But for files that are greater than 2 MB, sometimes the file is able to upload correctly, but majority times we see the data being corrupted.
I am still not getting what is causing the data corruption ? I don't think code has the issue as program works fine in windows environment, and sometimes in unix environment as well.
Is there any problem with the way the program reads the data and writes to remote server or any other thing I am missing here? Please help.
public boolean putFile(String report, String user, String password, String location,
String folder) throws Exception {
boolean status=true;
JSch shell = new JSch();
Session session = null;
session = shell.getSession(user, location, 22);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
Channel channel = null;
channel = session.openChannel("shell");
channel.setInputStream(null);
channel.setOutputStream(null);
channel = session.openChannel("sftp");
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
sftp.cd(folder);
File outputFile = new File(report);
FileInputStream fileInputStream = new FileInputStream(outputFile);
sftp.put(fileInputStream, outputFile.getName());
session.disconnect();
return status;
}
There was a bug in the jsch version we were using. Read through the change log of jsch releases, and updated the version. That solved the problem.
I am uploading a file from my local computer to remote server in a simple java application using Jsch, and Sftp protocol.
My code is getting no error or no exception and it runs successfully, but when I look at the remote location, the file is uploaded as 0kb with no extension and named as 'D'.
I have tried many ways but I am not able to figure out the mistake.
Here is my code..
String remoteDirectory = "D:\\Datastores\\RootStore\\Test\\";
String localDirectory = "C:\\pdffiles\\";
String fileToFTP = "demo.pdf";
String SFTPHOST = "hostIPaddress";
int SFTPPORT = 22;
String SFTPUSER = "username";
String SFTPPASS = "password";
String local_filename = fileToFTP;
String local_pathname = localDirectory;
String remote_filename = fileToFTP;
JSch jsch = null;
Session session = null;
ChannelSftp sftpChannel = null;
try
{
jsch = new JSch();
session = jsch.getSession(SFTPUSER, SFTPHOST);
session.setPassword(SFTPPASS);
session.setPort(SFTPPORT);
session.setConfig("StrictHostKeyChecking", "no");
session.setConfig("cipher.s2c", "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,aes256-ctr");
session.setConfig("cipher.c2s", "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,aes256-ctr");
session.connect();
sftpChannel = (ChannelSftp)session.openChannel("sftp");
sftpChannel.connect();
System.out.println(sftpChannel);
System.out.println(sftpChannel.getSession().isConnected());
FileInputStream fileBodyIns = new FileInputStream(local_pathname + local_filename);
System.out.println(local_pathname + local_filename);
System.out.println(remoteDirectory + remote_filename);
sftpChannel.put(fileBodyIns, remoteDirectory + remote_filename);
fileBodyIns.close();
sftpChannel.exit();
session.disconnect();
System.out.println("File uploaded successfully");
return "File uploaded successfully";
}
catch (Exception e)
{
e.printStackTrace();
return e.getMessage();
}
My connection makes successfully, the line
System.out.println(sftpChannel.getSession().isConnected());
gives true
and the following line prints successfully
System.out.println("File uploaded successfully");
I agree with Kenster's answer. I had similar problem on remote windows machine and the path with linux style solve my problem. Just wanted to add, the root directory for ChannelSftp is C:\ disk on remote windows machine and I'm not sure there is a way to put file on D:\ disk.
String remoteDirectory = "D:\\Datastores\\RootStore\\Test\\";
The SFTP protocol uses a unix-like naming scheme for file pathnames on the remote server. The file separator is "/", and a pathname without a leading "/" is considered to be relative to the current directory. Colons and backslashes have no special meaning.
By those rules, you're asking the remote server to create a file in your home directory named "D:\DataStores..." with a literal colon and backslashes. If your remote server is the Cygwin OpenSSH server or some other port of a Unix-based server, there may be a mismatch between the way the SFTP server interprets the filename and the way the Windows OS interprets the filename.
You need to figure out the correct syntax to refer to the paths on the D: drive through this SFTP server. If it's the Cygwin SFTP server, I think the right syntax would be "/cygdrive/d/Datastores/RootStore/Test/...". If the SFTP server is by some other vendor, you may need to consult the server documentation. Alternately, you could try logging into the SFTP server interactively, cd'ing to the "/" directory, and exploring from there. It ought to become obvious how to refer to folders on the D drive.
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'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 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