Recently, we started creating sporadic problems on the client site, where JSch started reporting that the “channel does not open” during the call to sendChannelOpen after a successful launch for many years. After digging into the code and looking at two options (timeout versus error message), it turned out that we most likely got a failure.
Since the messages were the same, I downloaded the source and added some output statements to check. After several tests, I was surprised to find that we were really pushing the timeout section, but so fast that it made no sense when looking at the code and accepting the wait () statement.
For further troubleshooting, I installed a quick stand-alone test application using the following method as the main call:
public static void testConnectionJCraft(String host, String user, String password, int port, int timeout) throws Exception {
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
try {
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
if (session.isConnected()) {
System.out.println("JCraft Connected sucessfully to server : " + host);
} else {
throw new Exception("JCraft Connection Failed: " + host);
}
Channel channel = session.openChannel("sftp");
try {
if (timeout > 0) {
channel.connect(timeout);
} else {
channel.connect();
}
} finally {
channel.disconnect();
}
} finally {
if (session != null) {
session.disconnect();
}
}
}
Note. I know that if (timeout> 0) is extraneous, and I could just call channel.connect (timeout) and get the same result with a timeout == 0, but I wanted to make sure that the call simulated a call inside an application that I eliminate, just in case.
Then I added some additional instructions to the sendChannelOpen method in com.jcraft.jsch.Channel:
protected void sendChannelOpen() throws Exception {
Session _session=getSession();
if(!_session.isConnected()){
throw new JSchException("session is down");
}
Packet packet = genChannelOpenPacket();
_session.write(packet);
int retry=2000;
long start=System.currentTimeMillis();
long timeout=connectTimeout;
long iteration = 0;
if(timeout!=0L) retry = 1;
System.out.println("Timeout: " + timeout);
long t = timeout== 0L ? 10L : timeout;
System.out.println("t: " + t);
synchronized(this){
while(this.getRecipient()==-1 &&
_session.isConnected() &&
retry>0){
if(timeout>0L){
long dur = (System.currentTimeMillis()-start);
if(dur>timeout){
System.out.println("Dur: " + dur + " > " + timeout);
retry=0;
continue;
}
}
try{
this.notifyme=1;
wait(t);
}
catch(java.lang.InterruptedException e){
System.out.println("Interrupted?");
}
finally{
this.notifyme=0;
}
retry--;
iteration++;
}
}
long end = System.currentTimeMillis();
System.out.println("Channel open duration: " + (end - start) + "ms");
System.out.println("Channel open iterations: " + iteration);
if(!_session.isConnected()){
throw new JSchException("session is down");
}
if(this.getRecipient()==-1){
throw new JSchException("channel is not opened.");
}
if(this.open_confirmation==false){
throw new JSchException("channel unable to be opened, SSH_MSG_CHANNEL_OPEN_FAILURE received.");
}
connected=true;
}
I moved the definition of t just to not get the spam value. The results of attempts reporting the message "channel is not open" showed that the above wait (t) expression does not match the timeout. Sample output (server name x'd out):
JCraft Connected sucessfully to server : xxx.xxxx.xxx
Timeout: 0
t: 10
Channel open duration: 8ms
Channel open iterations: 2000
ERROR: channel is not opened.
com.jcraft.jsch.JSchException: channel is not opened.
at com.jcraft.jsch.Channel.sendChannelOpen(Channel.java:783)
at com.jcraft.jsch.Channel.connect(Channel.java:153)
at com.jcraft.jsch.Channel.connect(Channel.java:147)
at com.gorman.tools.FTPTestUtility.testConnectionJCraft(FTPTestUtility.java:46)
at com.gorman.tools.FTPTestUtility.main(FTPTestUtility.java:21)
, 2000 8 . , , wait(), , :
JCraft Connected sucessfully to server : xxx.xxxx.xxx
Timeout: 0
t: 10
Notifying all for setRecipient
Channel open duration: 21ms
Channel open iterations: 3
3 , , 10/11/0 , . , notifyAll(), , , , , - .
, >= 1200 , . , wait(), JavaDoc :
, channel.connect(), , . overkill println , , , 21 - , , , , .
, channel.connect(rationalValue). , - !