Opening a second session channel on the same connection fails once the first has been closed. Found while promoting sshlib 0.4.0 from SFTP-only to a whole-connection engine in Haven (follow-on from #231 — thanks again for that fix, 0.4.0 is solid on rekey).
It surfaces differently depending on the server, which is why I'm reporting both:
Against Apache MINA sshd 2.19.0 — the second openSession() simply returns null:
val client = SshClient(SshClientConfig {
host = "127.0.0.1"; port = server.port
hostKeyVerifier = trustAll
})
client.connect() // Success
client.authenticatePassword(...) // Success
repeat(12) { attempt ->
val session = client.openSession()
?: error("openSession returned null on attempt $attempt") // fires at attempt 1
session.requestExec("true")
session.close()
}
Against a real OpenSSH server (OpenSSH 9.x on Ubuntu, from an Android client) it throws instead — and takes the whole connection down with it:
INFO - Opening session channel (local=1)
INFO - Channel open confirmed
INFO - Channel opened: local=1, remote=1, remoteWindow=0
INFO - Opening session channel (local=2)
INFO - Channel open confirmed
java.lang.IllegalStateException: Remote channel 1 is already registered
ERROR - Failed to open session channel
…after which the transport drops and the client reconnects. So it isn't only the new channel that fails — the existing session dies too, which is what makes it hard to work around.
Reading of it (happy to be corrected): the second channel is confirmed by the server with the same remote channel number (remote=1) as the first, because the server freed that number when we closed channel 1. The client still has remote=1 in its registry, so it rejects its own channel. That would mean a channel is registered under the remote number but not deregistered on close. The MINA null and the OpenSSH exception look like the same root cause reached by two paths (MINA appears to hand out a fresh remote number more slowly, so it fails a little differently).
Impact: any long-lived connection that opens more than one session channel — which for us is the normal case: an interactive shell, then exec for a command, then another exec later. A single exec per connection is fine; the second is where it bites.
Repro is a plain JUnit test against MINA if that's useful — happy to push it somewhere or turn it into a PR against the test suite if you'd like it as a regression test.
For now Haven keeps the sshlib whole-connection engine behind an explicit opt-in flag, with this as a known limitation.
Opening a second session channel on the same connection fails once the first has been closed. Found while promoting sshlib 0.4.0 from SFTP-only to a whole-connection engine in Haven (follow-on from #231 — thanks again for that fix, 0.4.0 is solid on rekey).
It surfaces differently depending on the server, which is why I'm reporting both:
Against Apache MINA sshd 2.19.0 — the second
openSession()simply returnsnull:Against a real OpenSSH server (OpenSSH 9.x on Ubuntu, from an Android client) it throws instead — and takes the whole connection down with it:
…after which the transport drops and the client reconnects. So it isn't only the new channel that fails — the existing session dies too, which is what makes it hard to work around.
Reading of it (happy to be corrected): the second channel is confirmed by the server with the same remote channel number (
remote=1) as the first, because the server freed that number when we closed channel 1. The client still hasremote=1in its registry, so it rejects its own channel. That would mean a channel is registered under the remote number but not deregistered on close. The MINAnulland the OpenSSH exception look like the same root cause reached by two paths (MINA appears to hand out a fresh remote number more slowly, so it fails a little differently).Impact: any long-lived connection that opens more than one session channel — which for us is the normal case: an interactive shell, then
execfor a command, then anotherexeclater. A singleexecper connection is fine; the second is where it bites.Repro is a plain JUnit test against MINA if that's useful — happy to push it somewhere or turn it into a PR against the test suite if you'd like it as a regression test.
For now Haven keeps the sshlib whole-connection engine behind an explicit opt-in flag, with this as a known limitation.