ssh2 icon indicating copy to clipboard operation
ssh2 copied to clipboard

[SFTP Server] Client hangs on exit

Open hugeblank opened this issue 10 months ago • 9 comments

I'm using a lightly modified version of the SFTP server example in the readme, along with the REALPATH logic from the SFTP example in the examples directory of this repo (found here). When I attempt to exit with this configuration, the server refuses to respond with an EOF after the client sends one.

Client logs:

What I see:

Connected to nmdebug.
debug2: Sending SSH2_FXP_REALPATH "."
debug3: Sent message fd 3 T:16 I:1
debug3: SSH2_FXP_REALPATH . -> /
sftp> exit
debug2: channel 0: read failed rfd 4 maxlen 32768: Broken pipe
debug2: channel 0: read failed
debug2: chan_shutdown_read: channel 0: (i0 o0 sock -1 wfd 4 efd 6 [write])
debug2: channel 0: input open -> drain
debug2: channel 0: ibuf empty
debug2: channel 0: send eof
debug3: send packet: type 96
debug2: channel 0: input drain -> closed

What I expect to see:

Connected to nmdebug.
debug2: Sending SSH2_FXP_REALPATH "."
debug3: Sent message fd 3 T:16 I:2
debug3: SSH2_FXP_REALPATH . -> /
sftp> exit
debug2: channel 0: read failed rfd 4 maxlen 32768: Broken pipe
debug2: channel 0: read failed
debug2: chan_shutdown_read: channel 0: (i0 o0 sock -1 wfd 4 efd 6 [write])
debug2: channel 0: input open -> drain
debug2: channel 0: ibuf empty
debug2: channel 0: send eof
debug3: send packet: type 96
debug2: channel 0: input drain -> closed
debug3: receive packet: type 96
debug2: channel 0: rcvd eof
debug2: channel 0: output open -> drain
debug2: channel 0: obuf empty
debug2: chan_shutdown_write: channel 0: (i3 o1 sock -1 wfd 5 efd 6 [write])
debug2: channel 0: output drain -> closed
debug3: receive packet: type 98
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug3: receive packet: type 97
debug2: channel 0: rcvd close
debug3: channel 0: will not send data after close
debug2: channel 0: almost dead
debug2: channel 0: gc: notify user
debug2: channel 0: gc: user detached
debug2: channel 0: send_close2
debug2: channel 0: send close for remote id 0
debug3: send packet: type 97
debug2: channel 0: is dead
debug2: channel 0: garbage collecting
debug1: channel 0: free: client-session, nchannels 1
debug3: channel 0: status: The following connections are open:
  #0 client-session (t4 [session] r0 nm0 i3/0 o3/0 e[write]/0 fd -1/-1/6 sock -1 cc -1 nc0 io 0x00/0x00)

debug3: send packet: type 1
Transferred: sent 4644, received 4684 bytes, in 5.2 seconds
Bytes per second: sent 887.8, received 895.4
debug1: Exit status 0

Server Logs:

Client authenticated!
[49892.626835718] Inbound: CHANNEL_OPEN (s:0, session)
[49892.626835718] Outbound: Sending CHANNEL_OPEN_CONFIRMATION (r:0, l:0)
[49892.626835718] Inbound: CHANNEL_REQUEST (r:0, subsystem: sftp)
Client SFTP session
[49892.626835718] Outbound: Sending CHANNEL_SUCCESS (r:0)
[49892.626835718] Inbound: CHANNEL_DATA (r:0, 9)
[49892.626835718] SFTP: Inbound: Received INIT (v3)
[49892.626835718] Outbound: Sending CHANNEL_DATA (r:0, 9)
[49892.626835718] Inbound: CHANNEL_DATA (r:0, 14)
[49892.626835718] SFTP: Inbound: Received REALPATH (id:1)
[49892.626835718] Outbound: Sending CHANNEL_DATA (r:0, 67)
[49892.626835718] SFTP: Outbound: Buffered NAME
[49892.626835718] Inbound: CHANNEL_EOF (r:0)

hugeblank avatar Feb 08 '25 15:02 hugeblank

We're facing the exact same issue. We haven't found any way to listen to the client disconnecting and force that disconnection. We also tried adding a lower limit for the keepalive timeout with no success.

arpadgabor avatar Feb 11 '25 11:02 arpadgabor

Managed to fix it. Had to dig into the code. Seems like the session emits undocumented end and eof events. I added the code below:

  const session = accept()

+ session.on('end', () => {
+   // session can be undefined for clients other than `sftp`.
+   if (session) session.end()
+ })

  session.on('sftp', (accept, reject) => {
    ...
  })

arpadgabor avatar Feb 13 '25 12:02 arpadgabor

What version are you using? I get an error that says session.end() is not a function for me when I do that (part of why I reported this issue)

edit: I assume 1.16.0, it's been out for a while.

hugeblank avatar Feb 13 '25 12:02 hugeblank

That's why I added that if (session) because I got that error for other clients. Yes, 1.16.0.

arpadgabor avatar Feb 13 '25 12:02 arpadgabor

Wait, did you add it in the correct place maybe? This is before the sftp handler.

Edit: our code pretty heavily deviates from the examples that's why I provided an excerpt Edit 2: Also, we are on Node 20 if that helps

arpadgabor avatar Feb 13 '25 12:02 arpadgabor

Wait, did you add it in the correct place maybe?

I think so? Image

Error:

/run/media/watergate/programming/netmountcc/src/sftp.ts:29
                    if (session) session.end()
                                         ^
TypeError: session.end is not a function
    at Session.<anonymous> (/run/media/watergate/programming/netmountcc/src/sftp.ts:29:42)
    at Session.emit (node:events:507:28)
    at Session.emit (node:domain:489:12)
    at CHANNEL_EOF (/run/media/watergate/programming/netmountcc/node_modules/ssh2/lib/server.js:1024:23)
    at 96 (/run/media/watergate/programming/netmountcc/node_modules/ssh2/lib/protocol/handlers.misc.js:978:16)
    at Protocol.onPayload (/run/media/watergate/programming/netmountcc/node_modules/ssh2/lib/protocol/Protocol.js:2059:10)
    at ChaChaPolyDecipherBinding.decrypt (/run/media/watergate/programming/netmountcc/node_modules/ssh2/lib/protocol/crypto.js:851:26)
    at Protocol.parsePacket [as _parse] (/run/media/watergate/programming/netmountcc/node_modules/ssh2/lib/protocol/Protocol.js:2028:25)
    at Protocol.parse (/run/media/watergate/programming/netmountcc/node_modules/ssh2/lib/protocol/Protocol.js:313:16)
    at Socket.<anonymous> (/run/media/watergate/programming/netmountcc/node_modules/ssh2/lib/server.js:1213:17)

hugeblank avatar Feb 13 '25 12:02 hugeblank

Hmm interesting, now I see this too. Apparently the error caused the disconnection 😅 I'll investigate.

arpadgabor avatar Feb 13 '25 12:02 arpadgabor

Seems like closing the parent client ssh Connection works instead of the SFTP session, I don't see any errors.

Edit: deploying now to our live server and testing that as well. Edit2: works now without issues, so in your code, doing client.end() should solve the problem

arpadgabor avatar Feb 13 '25 12:02 arpadgabor

Nice, that worked!

Going to leave the issue open for now, since it's technically an issue with documentation/example code. Thanks for the help!

hugeblank avatar Feb 13 '25 13:02 hugeblank