Hard Time Keeping JSCH Session Alive - java

I am trying to run a sequence of commands on a CSICO router using JSCH and SSH. But the problem I am having is after executing the first command, the second one just doesn't execute. The session seems to go down after running the first command. It throws this exception:
com.jcraft.jsch.JSchException: Packet corrupt
at com.jcraft.jsch.Session.start_discard(Session.java:1067)
at com.jcraft.jsch.Session.read(Session.java:937)
at com.jcraft.jsch.Session.connect(Session.java:309)
at com.jcraft.jsch.Session.connect(Session.java:183)
at com.att.ncaas.device.connection.ssh.SSHConnectionJSch.connect(SSHConnectionJSch.java:120)
The code look likes this:
public void connect () {
ChannelExec channelExec = null;
JSch jSch = new JSch();
Session session = null;
try {
session = jSch.getSession(userid, ipAddress, port);
// Set the password.
session.setPassword("XXXXXXXXX");
Properties properties = new Properties();
properties.put("StrictHostKeyChecking", "no");
// Set the Ciphers.
/*
* properties.put("cipher.s2c", "aes128-cbc"); properties.put("cipher.c2s",
* "aes128-cbc"); properties.put("CheckCiphers", "aes128-cbc");
*/
session.setConfig(properties);
// Finally make the connection.
session.connect();
channelExec = (ChannelExec) session.openChannel("exec");
// Command 1
channelExec.setCommand("show run");
ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
channelExec.setOutputStream(responseStream);
channelExec.connect();
while (channelExec.isConnected()) {
Thread.sleep(1000);
}
String responseString = new String(responseStream.toByteArray());
System.out.println("Show Run Output:\n");
System.out.println(responseString);
if (session.isConnected()) {
System.out.println("Wooooooooooooooooooohoooooooooooooooooooooooo!!!!");
} else {
session.connect();
}
// Command 2
channelExec.setCommand("show version");
channelExec.connect();
while (channelExec.isConnected()) {
Thread.sleep(1000);
}
responseString = new String(responseStream.toByteArray());
System.out.println("Show Version Output:\n");
System.out.println(responseString);
} catch (JSchException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.disconnect();
}
if (channelExec != null) {
channelExec.disconnect();
}
}
}
When it comes for the second session.connect() inside the if block, it throws the error. The only other thing I noticed is the when I put the ciphers in, this part:
// Set the Ciphers.
properties.put("cipher.s2c", "aes128-cbc");
properties.put("cipher.c2s", "aes128-cbc");
properties.put("CheckCiphers", "aes128-cbc");
I get a different error on the same line. I get:
com.jcraft.jsch.JSchException: Session.connect: java.io.IOException: End of IO Stream Read
at com.jcraft.jsch.Session.connect(Session.java:565)
at com.jcraft.jsch.Session.connect(Session.java:183)
Any pointers on how to keep the session alive between two ChannelExec execute would really helpful. Thanks.

First, you cannot reuse the "exec" channel this way. You have to open a new channel for the new command.
And note that Cisco is known not to support multiple commands in one "exec" channel, even if specified together upfront:
Error passing multiple commands to Cisco CLI via plink
I would not be surprised if it did not support multiple "exec" channels in one connection either.
Did you test with an SSH client, if it is even possible, to do what you are trying to implement?

Related

Running Command on a Router Line By Line (JSCH API)

I have written a method in Java, which takes an ArrayList of Strings and tries to run the command on a CISCO router console one by one. And I am trying to print the output of each command on the console. I am using the Jsch API for it.
I am able to connect to the router just fine using SSH. But when I print the OutputStream to the console it just prints the same thing over and over again. No output of the commands I sent. I intentionally have a few incorrect commands in there so that the console prints the error, but I get no such thing.
Here is my method:
/**
* Deliver a config to the router line by line.
* #param configLines
* #throws DeviceExcpetion
*/
public void deliverConfig(List<String> configLines) throws DeviceExcpetion {
Channel channel = null;
JSch jSch = new JSch();
Session session = null;
try {
session = jSch.getSession(userid, ipAddress, port);
// Set the password.
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
// Finally make the connection.
session.connect();
channel = session.openChannel("shell");
InputStream inputStream = null;
ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
channel.setOutputStream(responseStream);
channel.connect();
for (String line : configLines) {
inputStream = new ByteArrayInputStream(line.getBytes(StandardCharsets.UTF_8));
channel.setInputStream(inputStream);
if (channel.isConnected()) {
Thread.sleep(1000);
}
String responseString = new String(responseStream.toByteArray());
System.out.println(responseString);
}
} catch (JSchException e) {
e.printStackTrace();
throw new DeviceExcpetion(e.getMessage());
} catch (InterruptedException e) {
e.printStackTrace();
throw new DeviceExcpetion(e.getMessage());
} finally {
if (session != null) {
session.disconnect();
}
if (channel != null) {
channel.disconnect();
}
}
}

ssh to remote server inside remote server using jsch(jump server to dev machine)

I have two machines dev server and log server which has logs stored, I want to first ssh to dev server and then from there i want to do ssh to log server to execute some script.
Below is the code I tried using jsch :
Get jschSession:
private Session getSSHSession(String user,String host,String password) {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
Session session = null;
try {
JSch jsch = new JSch();
session=jsch.getSession(user, host, 22);
session.setPassword(password);
session.setConfig(config);
session.connect();
} catch (JSchException e) {
e.printStackTrace();
}
return session;
}
Execute a command
private String execute(Session session, String cmd) {
StringBuffer sb = new StringBuffer();
try{
Channel channel=session.openChannel("exec");
((ChannelExec) channel).setPty(true);
((ChannelExec)channel).setCommand(cmd);
channel.setInputStream(null);
((ChannelExec)channel).setErrStream(System.err);
channel.connect();
BufferedReader br = new BufferedReader(new InputStreamReader(channel.getInputStream()));
String msg = null;
while ((msg = br.readLine())!=null) {
sb.append(msg);
}
System.out.println("------------msg: -----------"+sb.toString());
while(true){
System.out.println("channel.getExitStatus(): "+channel.getExitStatus());
if(channel.isClosed()){
System.out.println("exit-status: "+channel.getExitStatus());
if(channel.getExitStatus()==0) {
System.out.println("script executed successfully..........");
}
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
channel.disconnect();
session.disconnect();
}catch(Exception e){
e.printStackTrace();
}
return sb.toString();
}
call the execute
public String audit(AuditRequest request) {
Session session = getSSHSession(user,host,password);
String cmd = "ssh user#server; cd /mmt/test;ls -ltr";
String result = execute(session,cmd);
System.out.println("cmd1: "+cmd+" result: -------------"+result);
return result;
}
So basically I am first connecting to dev server and then using its session I am trying to execute the ssh command to connect to log server.
By the posted code I am able to connect to my dev machine but from there not able to do ssh to log machine.
If i remove ((ChannelExec) channel).setPty(true); below is the error I am getting:
Pseudo-terminal will not be allocated because stdin is not a terminal.
Permission denied, please try again.
Permission denied, please try again.
And ifI put the setPty for pseudo terminal , it's stucking in channel.connect();
Read multiple posts/questions lots of people mentioned using channel.setPty(true) But that doesn't seems to work
Could anyone help me to know what is going wrong here.
Thanks

Sending Jsch commands

Beating my head against a wall here.
I am trying in the constructor to establish a link and log on. Then in the begin method to execute a java application on the remote computer. Finally in terminate, to kill the java process.
No exceptions show up, the command just doesn't execute. I can see the user has logged in if I go to the remote terminal and run a last. The user has only a login time of 0 though. I can also get an error if I change the password to the wrong password.
But the remote java app isn't running. Remote app runs fine when I log in directly from that terminal or ssh in and run it from the command line.
Ideas? Thanks in advance.
//Constants for the login information
String HOST="<HOSTNAME>";
String USER="<userid>";
String PASSWORD="<password>";
//Constants for command strings
String START_COMMAND="java -Djava.library.path=/usr/local/share/OpenCV/java -jar <filename>";
String TERMINATE_COMMAND="killall java";
Channel channel;
Session session;
InputStream in;
public VisionProcessing(){
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
try {
System.out.println("Trying login");
session = jsch.getSession(USER, HOST, 22);
session.setPassword(PASSWORD);
session.setConfig(config); //XXX No idea what this does
session.connect();
channel=session.openChannel("exec");
((ChannelExec)channel).setPty(true);
channel.connect();//This is important
if (channel.isConnected()){
System.out.println("Connected");
} else {
System.out.println("Not Connected");
}
} catch (JSchException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("Exception logging in");
}
}
public boolean beginTargeting() {
try {
StringBuilder outputBuffer = new StringBuilder();
Channel channel = session.openChannel("exec");
InputStream commandOutput = channel.getInputStream();
int readByte = commandOutput.read();
while(readByte != 0xffffffff)
{
outputBuffer.append((char)readByte);
readByte = commandOutput.read();
}
System.out.println("Begin");
if (channel.isConnected()){
System.out.println("Connected");
((ChannelExec)channel).setCommand(START_COMMAND);
} else {
System.out.println("Not Connected");
}
channel.connect();//This is important
channel.disconnect();
}catch(IOException ioX)
{
System.out.println(ioX);
}
catch(JSchException jschX)
{
System.out.println(jschX);
}
return true;
}
public boolean terminateTargeting() {
try {
System.out.println("Terminate");
channel=session.openChannel("exec");
((ChannelExec)channel).setCommand(TERMINATE_COMMAND);
channel.connect();//This is important
System.out.println("Terminating");
channel.setInputStream(null);
channel.disconnect();
session.disconnect();
System.out.println("DONE");
}catch(Exception ee){
System.out.println(ee);
}
return true;
}

Jsch SFTP client unable to create new native thread

I use Jsch as SFTP client to read and write XML files from a remote SFTP directory.
I use a 5 second job to check if new files available for drafts, after 30 or 40 min loop I get the following error
Caused by: java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method) [rt.jar:1.7.0_65]
at java.lang.Thread.start(Thread.java:714) [rt.jar:1.7.0_65]
at com.jcraft.jsch.Session.connect(Session.java:528) [jsch-0.1.53.jar:]
at com.jcraft.jsch.Session.connect(Session.java:183) [jsch-0.1.53.jar:]
This is the source code used to create connexion
public InputStream getFile(String path){
Session session = null;
Channel channel = null;
try {
ChannelSftp sftp = openConnexion(session, channel);
return sftp.get(path);
} catch (SftpException e) {
new RuntimeException("Error detected during get file from SFTP specific path : " + e.getMessage(), e);
} finally {
closeConnexion(session, channel);
}
}
private ChannelSftp openConnexion(Session session, Channel channel) {
try {
JSch ssh = new JSch();
session = ssh.getSession("user", "hostname", 22);
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
channel = session.openChannel(SFTP_CHANNEL);
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
return sftp;
} catch (JSchException e) {
throw new RuntimeException("Error detected during open SFTP connexion : " + e.getMessage(), e);
}
}
private void closeConnexion(Session session, Channel channel) {
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
}
I tried to increase the size of JVM thread stack and also increase the limits of native process allowed by unix => same error.
I used the following command to do that :
ulimit -u unlimited
I tried to create a pool of jsch session, jsch session when it is not disconnected, it is unusable => "SFTP Error 4"
My job is runned into war deployed on jboss-as-7, this is the JVM option :
JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=256m -Xss1024k"
Do you have a suggestion for this kind of treatment?
Thank you !
The problem is that you're not closing the channel and session after each loop, which will leak at least the thread that's used to perform the download over SFTP.
The attempt to close the session and channel in the finally block, if it worked, would unfortunately invalidate the InputStream that you're trying to read from; preventing you from processing the file properly.
I'm going to refactor the code slightly, which should address the resource exhaustion issue, with comments:
// session and channel are at the object scope
Session session = null;
Channel channel = null;
public InputStream getFile(String path){
// First, close any existing connections.
try {
closeConnexion();
} catch (SftpException e) {
// You can try to handle an issue here; but it's
// probably not worth it
}
try {
ChannelSftp sftp = openConnexion();
return sftp.get(path);
} catch (SftpException e) {
new RuntimeException("Error detected during get file from SFTP specific path : " + e.getMessage(), e);
} finally {
}
}
private ChannelSftp openConnexion() {
try {
JSch ssh = new JSch();
// use the object's session variable
session = ssh.getSession("user", "hostname", 22);
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
// use the object's channel object
channel = session.openChannel(SFTP_CHANNEL);
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
return sftp;
} catch (JSchException e) {
throw new RuntimeException("Error detected during open SFTP connexion : " + e.getMessage(), e);
}
}
private void closeConnexion() {
// close object's channel and session
if (channel != null) {
channel.disconnect();
channel = null;
}
if (session != null) {
session.disconnect();
session = null;
}
}
If I was to re-design this, I would return a container class rather than an InputStream that contained the channel, session and InputStream. The container class would have a 'close' method, which would close the InputStream, channel and session, and then I wouldn't store the channel and session in the object.

JSch: How to keep the session alive and up

I'm writing Java GUI program for static route management using SSH. My code is as follows:
import com.jcraft.jsch.*;
import java.io.*;
public class Konsep {
String status;
static String username;
static String hostname;
String inputcommand;
String output;
static Session session;
JSch jsch = new JSch();
public String status(String stringstatus) {
stringstatus = status;
return stringstatus;
}
public String InputCommand(String inputcommandstatus) {
inputcommandstatus = inputcommand;
return inputcommandstatus;
}
public void connect(String usernamelokal, String hostnamelokal,
String password, int port) {
// JSch jsch=new JSch();
try {
Session sessionlokal = jsch.getSession(usernamelokal,
hostnamelokal, port);
sessionlokal.setPassword(password);
UserInfo ui = new UserInfoku.Infoku();
sessionlokal.setUserInfo(ui);
sessionlokal.setTimeout(0);
sessionlokal.connect();
status = "tersambung \n";
username = usernamelokal;
hostname = hostnamelokal;
session = sessionlokal;
System.out.println(username + " " + hostname);
} catch (Exception e) {
System.out.println(e);
status = "Exception = \n " + e + "\n";
}
}
public void disconnect() {
// JSch jsch=new JSch();
try {
Session sessionlokal = jsch.getSession(username, hostname);
// System.out.println(username +" "+ hostname);
sessionlokal.disconnect();
status = "wes pedhoott \n";
} catch (Exception e) {
System.out.println(e);
status = "Exception = \n " + e + "\n";
}
}
public void addRoute() {
// JSch jsch=new JSch();
System.out.println(username + " " + hostname);
try {
Session sessionlokal = session; // =jsch.getSession(username, hostname);
Channel channel = sessionlokal.openChannel("exec");
((ChannelExec) channel).setCommand(inputcommand);
channel.setInputStream(null);
channel.connect();
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
channel.connect();
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
System.out.print(new String(tmp, 0, i));
}
if (channel.isClosed()) {
System.out.println("exit-status: "
+ channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channel.disconnect();
} catch (Exception e) {
System.out.println(e);
}
}
}
The problem is when I call the connect method and then calling the addroute, the program returns
root 192.168.50.2
root 192.168.50.2
com.jcraft.jsch.JSchException: session is down
I've been trying to get session status with either
Session sessionlokal=session; //returns com.jcraft.jsch.JSchException: ChannelExec
or
Session sessionlokal=jsch.getSession(username, hostname); //returns session is down
I've also tried to use keepalive, but its not working either.
My intention is to create a session to host (log in), while leaving the session up, execute a command or commands and maybe executing other commands later, and then closing the session when its not needed (log out).
I've been searching on this forum and I found this question but the code is create a method to define a command to execute first, and then creating the session, call the command's method and close the session.
Any ideas about how to do as I mentioned above ?
After trying Session.sendKeepAliveMsg() without success, I came to the following solution which seems to be rather stable:
private Session getSession() throws Exception {
try {
if (!session.isConnected()) {
logger.info("Session successfully tested, use it again.");
session.connect();
}
} catch (Throwable t) {
logger.info("Session terminated. Create a new one.");
session = jsch.getSession(user, host, port);
session.setConfig(config);
session.connect();
}
return session;
}
Update: Some days later it failed.
I tried to test it by killing the open session on the server. All prior versions I tested this way showed the exact same behavior, regardless whether the problem popped up after waiting some days or killing the server process, so I thought this test - and its outcome for the above solution - to be meaningful. Unfortunately, it isn't.
I'm going to try some other ways to fix it and keep you up to date.
Update 2: Final solution, guaranteed inelegant and working:
private Session getSession() throws Exception {
try {
ChannelExec testChannel = (ChannelExec) session.openChannel("exec");
testChannel.setCommand("true");
testChannel.connect();
if(logger.isDebugEnabled()) {
logger.debug("Session successfully tested, use it again.");
}
testChannel.exit();
} catch (Throwable t) {
logger.info("Session terminated. Create a new one.");
session = jsch.getSession(user, host, port);
session.setConfig(config);
session.connect();
}
return session;
}
This version runs several weeks in a productive environment. Once a day I have the info message logged.
The costs of opening a channel and performing some do-nothing-command are somewhat annoying, but I found no other way to be definitely sure about the state of the session.
You can try by invoking the following method before invoking Session#connect().
Session#setServerAliveInterval(int milliseconds)
Although I found one more function Session#sendKeepAliveMsg() which can be tried.
You can use a pool , I used org.apache.commons.pool2 so the pool is responsible of providing connected sessions, basically:
Create JSCH Session on makeObject
connect on activate if disconnected
check if isConnected on validate
sendKeepAliveMsg on passivate
Above worked for me, also keep in mind that if you add logic on validate, the poolConfig should set true on testOnBorrow or testOnReturn.
So I used KeyedObjectPool you should get connections from the pool via borrowObject method and make sure you return them to the pool once you operation is complete via returnObject.
https://commons.apache.org/proper/commons-pool/api-2.2/org/apache/commons/pool2/PooledObjectFactory.html

Categories

Resources