Skip to content

Commit 759b833

Browse files
authored
Merge pull request #117 from cbenhagen/fix/116-channel-close
fix: Close channel correctly when receiving SSH_Message_Channel_Close
2 parents bb8fb29 + 519c628 commit 759b833

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

lib/src/ssh_client.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'package:dartssh2/src/message/msg_userauth.dart';
2121
import 'package:dartssh2/src/ssh_message.dart';
2222
import 'package:dartssh2/src/socket/ssh_socket.dart';
2323
import 'package:dartssh2/src/ssh_userauth.dart';
24+
import 'package:meta/meta.dart';
2425

2526
/// https://datatracker.ietf.org/doc/html/rfc4252#section-8
2627
typedef SSHPasswordRequestHandler = FutureOr<String?> Function();
@@ -486,6 +487,10 @@ class SSHClient {
486487
}
487488
}
488489

490+
/// Handles a raw SSH packet. This method is only exposed for testing purposes.
491+
@visibleForTesting
492+
void handlePacket(Uint8List packet) => _handlePacket(packet);
493+
489494
void _sendMessage(SSHMessage message) {
490495
printTrace?.call('-> $socket: $message');
491496
_transport.sendPacket(message.encode());
@@ -792,7 +797,7 @@ class SSHClient {
792797
printTrace?.call('<- $socket: $message');
793798
final channel = _channels[message.recipientChannel];
794799
if (channel != null) {
795-
channel.close();
800+
channel.handleMessage(message);
796801
_channels.remove(message.recipientChannel);
797802
_channelIdAllocator.release(message.recipientChannel);
798803
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:dartssh2/dartssh2.dart';
2+
import 'package:test/test.dart';
3+
4+
import '../../test_utils.dart';
5+
6+
void main() {
7+
group('SSHChannel', () {
8+
late SSHClient client;
9+
late SSHSession session;
10+
11+
setUp(() async {
12+
client = await getTestClient();
13+
await client.authenticated;
14+
session = await client.shell();
15+
});
16+
17+
tearDown(() {
18+
client.close();
19+
});
20+
21+
test('stdout stream handles remote channel close correctly', () async {
22+
final drainFuture = session.stdout.drain<void>();
23+
24+
final closeMessage = createChannelCloseMessage(0);
25+
client.handlePacket(closeMessage);
26+
27+
await drainFuture;
28+
}, timeout: const Timeout(Duration(seconds: 1)));
29+
});
30+
}

test/test_utils.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'dart:io';
2+
import 'dart:typed_data';
23

34
import 'package:dartssh2/dartssh2.dart';
5+
import 'package:dartssh2/src/message/msg_channel.dart';
46

57
/// A honeypot that accepts all passwords and public-keys
68
Future<SSHClient> getHoneypotClient() async {
@@ -40,3 +42,11 @@ Future<List<SSHKeyPair>> getTestKeyPairs() async {
4042
String fixture(String path) {
4143
return File('test/fixtures/$path').readAsStringSync();
4244
}
45+
46+
/// Create a [SSH_Message_Channel_Close] message.
47+
Uint8List createChannelCloseMessage(int recipientChannel) {
48+
final message = SSH_Message_Channel_Close(
49+
recipientChannel: recipientChannel,
50+
);
51+
return message.encode();
52+
}

0 commit comments

Comments
 (0)