SshChannelClosedException occurs when SSH client disconnects: Issue with PosixPtyTerminal and ChannelOutputStream
Alexandre01Dev opened this issue · 2 comments
Hello, I am using JLINE3 with the MinaSSHD library. When creating the LineReaderImpl with the terminal generated by ShellFactoryImpl via the ShellParams consumer, and executing the readLine function, everything works fine except when the SSH client disconnects, whether the user disconnects from their terminal or is ejected by the java process, which causes an error that cannot be handled. Here it is:
org.apache.sshd.common.channel.exception.SshChannelClosedException: write(ChannelOutputStream[ChannelSession[id=0, recipient=0]-ServerSessionImpl[testuser@/217.136.124.54:8978]] SSH_MSG_CHANNEL_DATA) len=1 - channel already closed
at org.apache.sshd.common.channel.ChannelOutputStream.write(ChannelOutputStream.java:146)
at org.apache.sshd.common.channel.ChannelOutputStream.write(ChannelOutputStream.java:138)
at org.jline.terminal.impl.PosixPtyTerminal.pumpOut(PosixPtyTerminal.java:227)
at java.base/java.lang.Thread.run(Thread.java:1583)
java.io.IOError: java.io.IOException: Input/output error
at org.jline.keymap.BindingReader.readCharacter(BindingReader.java:169)
at org.jline.keymap.BindingReader.readBinding(BindingReader.java:109)
at org.jline.keymap.BindingReader.readBinding(BindingReader.java:61)
at org.jline.reader.impl.LineReaderImpl.doReadBinding(LineReaderImpl.java:974)
at org.jline.reader.impl.LineReaderImpl.readBinding(LineReaderImpl.java:1007)
at org.jline.reader.impl.LineReaderImpl.readLine(LineReaderImpl.java:690)
at org.jline.reader.impl.LineReaderImpl.readLine(LineReaderImpl.java:512)
at be.alexandre01.dreamnetwork.core.console.InputThread.run(InputThread.java:40)
at be.alexandre01.dreamnetwork.core.ssh.SSHServerCore.lambda$init$13(SSHServerCore.java:264)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.io.IOException: Input/output error
at java.base/java.io.FileInputStream.read0(Native Method)
at java.base/java.io.FileInputStream.read(FileInputStream.java:231)
at java.base/java.io.FilterInputStream.read(FilterInputStream.java:71)
at org.jline.terminal.impl.AbstractPty$1.read(AbstractPty.java:58)
at org.jline.terminal.impl.AbstractPty$PtyInputStream.read(AbstractPty.java:124)
at org.jline.terminal.impl.PosixPtyTerminal$InputStreamWrapper.read(PosixPtyTerminal.java:178)
at org.jline.utils.NonBlockingInputStream.read(NonBlockingInputStream.java:62)
at org.jline.utils.NonBlocking$NonBlockingInputStreamReader.read(NonBlocking.java:157)
at org.jline.utils.NonBlockingReader.read(NonBlockingReader.java:56)
at org.jline.keymap.BindingReader.readCharacter(BindingReader.java:159)
... 11 more
Here are the steps to reproduce the error:
SshServer sshd = SshServer.setUpDefaultServer();
sshd.setPort(2222);
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(Paths.get("hostkey.ser")));
sshd.setPasswordAuthenticator((username, password, session) -> true);
sshd.setCipherFactories(Arrays.asList(BuiltinCiphers.aes256ctr, BuiltinCiphers.aes192ctr));
sshd.setKeyExchangeFactories(ServerBuilder.setUpDefaultKeyExchanges(false));
sshd.setShellFactory(new ShellFactoryImpl(shellParams -> {
LineReader reader = LineReaderBuilder.builder()
.terminal(shellParams.getTerminal())
.build();
try {
String line;
reader.printAbove("Welcome to SSH Server");
while ((line = reader.readLine("sshTest > ")) != null) {
System.out.println(line);
}
} catch (UserInterruptException e) {
// Ignore
} catch (EndOfFileException e) {
// Ignore
}catch (Exception e){
// ignore OTHER EXCEPTIONS
}
}));
try {
// Start the server
sshd.start();
System.out.println("SSH Server started on port 2222");
// Keep the server running
Thread.sleep(Long.MAX_VALUE);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
sshd.stop();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Thanks for looking into this issue. If you need more details or have any suggestions, please let me know. Looking forward to your feedback
Up !
I think I found a temporary solution on Unix. In the ShellFactoryImpl class (line 204), when we force the creation of an ExternalTerminal and not a PosixPtyTerminal, the bug is gone.