Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/dartssh2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export 'package:dartssh2/src/socket_io.dart'

export 'src/ssh.dart' show SSH;

export 'src/transport.dart' show SSHChannel, Forward, SSHTransportState;
export 'src/transport.dart'
show SSHChannel, Forward, SSHTransportState, KeepaliveConfig;

export 'package:dartssh2/src/websocket_io.dart'
if (dart.library.html) 'package:dartssh2/websocket_html.dart'
Expand Down
95 changes: 49 additions & 46 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,46 +74,46 @@ class SSHClient extends SSHTransport with SSHAgentForwarding {
/// shell or `xterm-256color` for better color support.
final String termvar;

SSHClient({
Uri? hostport,
required this.username,
this.loadIdentity,
this.onUserauthRequest,
this.onPasswordRequest,
this.termvar = 'xterm',
this.termWidth = 80,
this.termHeight = 25,
bool compress = false,
this.agentForwarding = false,
this.closeOnDisconnect,
this.startShell = true,
List<Forward>? forwardLocal,
List<Forward>? forwardRemote,
VoidCallback? disconnected,
ResponseCallback? response,
StringCallback? print,
StringCallback? debugPrint,
StringCallback? tracePrint,
VoidCallback? success,
this.acceptHostFingerprint,
SocketInterface? socketInput,
Random? random,
SecureRandom? secureRandom,
}) : super(
false,
hostport: hostport,
compress: compress,
forwardLocal: forwardLocal,
forwardRemote: forwardRemote,
disconnected: disconnected,
response: response,
print: print,
debugPrint: debugPrint,
tracePrint: tracePrint,
socket: socketInput,
random: random,
secureRandom: secureRandom,
) {
SSHClient(
{Uri? hostport,
required this.username,
this.loadIdentity,
this.onUserauthRequest,
this.onPasswordRequest,
this.termvar = 'xterm',
this.termWidth = 80,
this.termHeight = 25,
bool compress = false,
this.agentForwarding = false,
this.closeOnDisconnect,
this.startShell = true,
List<Forward>? forwardLocal,
List<Forward>? forwardRemote,
VoidCallback? disconnected,
ResponseCallback? response,
StringCallback? print,
StringCallback? debugPrint,
StringCallback? tracePrint,
VoidCallback? success,
this.acceptHostFingerprint,
SocketInterface? socketInput,
Random? random,
SecureRandom? secureRandom,
KeepaliveConfig? keepaliveConfig})
: super(false,
hostport: hostport,
compress: compress,
forwardLocal: forwardLocal,
forwardRemote: forwardRemote,
disconnected: disconnected,
response: response,
print: print,
debugPrint: debugPrint,
tracePrint: tracePrint,
socket: socketInput,
random: random,
secureRandom: secureRandom,
keepaliveConfig: keepaliveConfig) {
if (success != null) {
this.success.add(success);
}
Expand Down Expand Up @@ -154,9 +154,6 @@ class SSHClient extends SSHTransport with SSHAgentForwarding {
/// If we have tried to authenticate with "password" method.
bool _triedAuthWithPassword = false;

/// If we have tried to authenticate with "keyboard-interactive" method.
bool _triedAuthWithKeyboardInteractive = false;

/// If we have tried to authenticate with "none" method.
bool _triedAuthWithNone = false;

Expand Down Expand Up @@ -266,6 +263,14 @@ class SSHClient extends SSHTransport with SSHAgentForwarding {
handleMSG_DEBUG(MSG_DEBUG()..deserialize(packetS!));
break;

case MSG_REQUEST_FAILURE_MESSAGE.ID:
handleMSG_REQUEST_FAILURE_MESSAGE(MSG_REQUEST_FAILURE_MESSAGE());
break;

case MSG_REQUEST_SUCCESS_MESSAGE.ID:
handleMSG_REQUEST_SUCCESS_MESSAGE(MSG_REQUEST_SUCCESS_MESSAGE());
break;

default:
this.print?.call(
'$hostport: unknown packet number: $packetId, len $packetLen');
Expand Down Expand Up @@ -416,7 +421,6 @@ class SSHClient extends SSHTransport with SSHAgentForwarding {
}

if (onUserauthRequest != null) {
_triedAuthWithKeyboardInteractive = true;
sendKeyboardInteractive();
return;
}
Expand Down Expand Up @@ -460,8 +464,7 @@ class SSHClient extends SSHTransport with SSHAgentForwarding {
}

if (msg.authLeft.contains(kAuthMethodKeyboardInteractive)) {
if (!_triedAuthWithKeyboardInteractive && onUserauthRequest != null) {
_triedAuthWithKeyboardInteractive = true;
if (onUserauthRequest != null) {
sendKeyboardInteractive();
return;
}
Expand Down
51 changes: 50 additions & 1 deletion lib/src/protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,10 @@ class MSG_GLOBAL_REQUEST extends SSHMessage {
int get serializedSize => serializedHeaderSize + request.length;

@override
void serialize(SerializableOutput output) {}
void serialize(SerializableOutput output) {
serializeString(output, request);
output.addUint8(wantReply);
}

@override
void deserialize(SerializableInput input) {
Expand All @@ -783,6 +786,52 @@ class MSG_GLOBAL_REQUEST extends SSHMessage {
}
}

class MSG_GLOBAL_REQUEST_KEEPALIVE extends MSG_GLOBAL_REQUEST {
static const int ID = 80;
@override
final String request = "[email protected]";

int wantReply = 1;
}

class MSG_REQUEST_SUCCESS_MESSAGE extends SSHMessage {
static const int ID = 81;

MSG_REQUEST_SUCCESS_MESSAGE() : super(ID);

@override
void deserialize(SerializableInput input) {}

@override
void serialize(SerializableOutput output) {}

@override
int get serializedHeaderSize => 4;

@override
// TODO: implement serializedSize
int get serializedSize => 4;
}

class MSG_REQUEST_FAILURE_MESSAGE extends SSHMessage {
static const int ID = 82;

MSG_REQUEST_FAILURE_MESSAGE() : super(ID);

@override
void deserialize(SerializableInput input) {}

@override
void serialize(SerializableOutput output) {}

@override
int get serializedHeaderSize => 4;

@override
// TODO: implement serializedSize
int get serializedSize => 4;
}

/// https://tools.ietf.org/html/rfc4254#section-7.1
class MSG_GLOBAL_REQUEST_TCPIP extends SSHMessage {
static const int ID = 80;
Expand Down
108 changes: 91 additions & 17 deletions lib/src/transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import 'dart:math';
import 'dart:collection';
import 'dart:typed_data';
import 'dart:async';

import 'package:convert/convert.dart';
import 'package:meta/meta.dart';
Expand Down Expand Up @@ -42,6 +43,14 @@ class Forward {
String? targetHost;
}

class KeepaliveConfig {
KeepaliveConfig(
{required this.keepaliveCountMax, required this.keepaliveInterval});

final Duration keepaliveInterval;
final int keepaliveCountMax;
}

/// All terminal sessions, forwarded connections, etc., are [SSHChannel]s.
/// Multiple [SSHChannel]s are multiplexed into a single connection and
/// [SSHChannel]s are flow-controlled. No data may be sent to a channel until
Expand Down Expand Up @@ -158,6 +167,7 @@ abstract class SSHTransport with SSHDiffieHellman {
var nextChannelId = 1;
var maxPacketSize = 32768;
var initialWindowSize = 1048576;
var pingCount = 0;

num serverVersion = 0;

Expand All @@ -181,23 +191,24 @@ abstract class SSHTransport with SSHDiffieHellman {
SSHChannel? sessionChannel;
HashMap<int, SSHChannel> channels = HashMap<int, SSHChannel>();
HashMap<int?, Forward>? forwardingRemote;

SSHTransport(
this.server, {
this.identity,
this.hostport,
this.compress = false,
this.forwardLocal,
this.forwardRemote,
this.disconnected,
this.response,
this.print,
this.debugPrint,
this.tracePrint,
this.socket,
Random? random,
this.secureRandom,
}) : random = random ?? Random.secure();
KeepaliveConfig? keepaliveConfig;

SSHTransport(this.server,
{this.identity,
this.hostport,
this.compress = false,
this.forwardLocal,
this.forwardRemote,
this.disconnected,
this.response,
this.print,
this.debugPrint,
this.tracePrint,
this.socket,
Random? random,
this.secureRandom,
this.keepaliveConfig})
: random = random ?? Random.secure();

// Interface
@visibleForOverriding
Expand Down Expand Up @@ -265,6 +276,53 @@ abstract class SSHTransport with SSHDiffieHellman {
socket!.send(verC + '\r\n');
tracePrint?.call('-> $hostport $verC');
if (client) sendKeyExchangeInit(false);

if (keepaliveConfig != null) {
handleKeepalive(keepaliveConfig!);
}
}

@internal
void handleKeepalive(KeepaliveConfig config) {
if (config.keepaliveInterval.inMilliseconds <= 0) {
return;
}

if (socket == null) {
return;
}
if (!socket!.connected) {
return;
}

pingCount = 0;
Timer.periodic(config.keepaliveInterval, (timer) {
if (++pingCount > config.keepaliveCountMax) {
timer.cancel();
return;
}

if (socket!.connected) {
ping();
} else {
timer.cancel();
}
});
}

@internal
void ping() {
if (socket != null && socket!.connected && encrypt != null) {
writeCipher(MSG_GLOBAL_REQUEST_KEEPALIVE());
}
}

@internal
void resetPingCount() {
if (pingCount > 0) {
tracePrint?.call('reset ping count');
pingCount = 0;
}
}

/// Key exchange begins by each side sending SSH_MSG_KEXINIT.
Expand Down Expand Up @@ -638,10 +696,14 @@ abstract class SSHTransport with SSHDiffieHellman {
channels.remove(msg.recipientChannel);
}

@internal
void handleMSG_GLOBAL_REQUEST_KEEPALIVE(MSG_GLOBAL_REQUEST_KEEPALIVE msg) {}

/// https://tools.ietf.org/html/rfc4254#section-4
@internal
void handleMSG_GLOBAL_REQUEST(MSG_GLOBAL_REQUEST msg) {
tracePrint?.call('<- $hostport: $msg');

// writeClearOrEncrypted(MSG_REQUEST_FAILURE())
}

Expand All @@ -667,6 +729,18 @@ abstract class SSHTransport with SSHDiffieHellman {
tracePrint?.call('<- $hostport: MSG_DEBUG ${msg.message}');
}

@internal
void handleMSG_REQUEST_FAILURE_MESSAGE(MSG_REQUEST_FAILURE_MESSAGE msg) {
tracePrint?.call('<- $hostport: MSG_REQUEST_FAILURE_MESSAGE');
resetPingCount();
}

@internal
void handleMSG_REQUEST_SUCCESS_MESSAGE(MSG_REQUEST_SUCCESS_MESSAGE msg) {
tracePrint?.call('<- $hostport: MSG_REQUEST_SUCCESS_MESSAGE');
resetPingCount();
}

/// Computes a new exchange hash [exH] given the server key [kS].
@internal
void updateExchangeHash(Uint8List kS) {
Expand Down
Loading