Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
88e36fc
Add parameter disableHostkeyVerification
alexander-irion Mar 12, 2025
e32b132
Catch exceptions when sshd terminated
alexander-irion Mar 26, 2025
b0fdd01
Implemented support for server rekey (by default every 1 GB or 1 hour)
May 15, 2025
86805cb
According to RFC, we shouldn't send anything except KEX stuff when do…
May 15, 2025
dcce7af
Improved function to allow critical ssh messages through as well
May 15, 2025
bead8a8
Add support for new MAC mac-sha2-256-96", "hmac-sha2-512-96", "hmac-s…
reinbeumer Jun 18, 2025
81c6905
Corrected the ETM encryptions
reinbeumer Jun 19, 2025
c55e269
Corrected the ETM encryptions
reinbeumer Jun 19, 2025
cd3bc77
Format code
xtyxtyx Jun 22, 2025
40948ea
Merge pull request #123 from alexander-irion/master
xtyxtyx Jun 22, 2025
e29ba65
Merge pull request #125 from MarBazuz/feat/rekey
xtyxtyx Jun 22, 2025
364856d
Add more tests
xtyxtyx Jun 22, 2025
81cf1c5
Format code
xtyxtyx Jun 22, 2025
d9cd523
Merge branch 'master' into pr/reinbeumer/127
xtyxtyx Jun 22, 2025
25429ab
Format code
xtyxtyx Jun 22, 2025
3056782
Merge pull request #127 from reinbeumer/feature/algorithm-corrections
xtyxtyx Jun 22, 2025
dc8607b
Update example
xtyxtyx Jun 22, 2025
c7d6321
Add .vscode to .gitignore
xtyxtyx Jun 22, 2025
df127d7
Bump version
xtyxtyx Jun 22, 2025
1d9613c
Removed Dependabot on package
vicajilau Jul 20, 2025
5cba8d7
Added publish flow
vicajilau Jul 20, 2025
1312766
Renamed file
vicajilau Jul 20, 2025
a4ad5c7
Merge pull request #130 from TerminalStudio/feature/CI-CD-improvements
vicajilau Jul 20, 2025
3aa45a5
Add NaviTerm screenshot to README
jc-hk-1916 Jan 1, 2026
f9d3a1d
add NaviTerm title and repository link
jc-hk-1916 Jan 1, 2026
2bf1ef2
Update App Store link for NaviTerm
jc-hk-1916 Jan 1, 2026
416d74e
add NaviTerm to showcase and refine table structure
jc-hk-1916 Jan 1, 2026
b2e1264
Update image alt text for clarity
jc-hk-1916 Jan 1, 2026
4499ab6
Introducing domain sockets as forwarding connect destination
isegal Feb 13, 2026
81e88d7
Add example of forwarding to a remote unix domain socket
isegal Feb 14, 2026
963e9f9
Fix potential hang when closing channel while opening
isegal Mar 14, 2026
e7a38ee
register channel
Mar 18, 2026
17ba902
Merge pull request #141 from shihuili1218/master
vicajilau Mar 19, 2026
7b1a439
Bump version to 2.14.0 and update CHANGELOG for SSH connection fix
vicajilau Mar 19, 2026
bcdafd0
Merge pull request #140 from few-sh/domain-sockets
vicajilau Mar 19, 2026
78cf43a
Update GitHub Actions workflow to include pull request triggers and m…
vicajilau Mar 19, 2026
e4bb2d8
Merge pull request #138 from jc-hk-1916/master
vicajilau Mar 19, 2026
6ae02d4
Add forwardLocalUnix() function to CHANGELOG for SSH forwarding
vicajilau Mar 19, 2026
21c0c67
Merge v2.14.0 into merge-v2.14.0 branch
GT-610 Mar 29, 2026
27a6480
feat(forward): Adds local Unix domain socket forwarding functionality
GT-610 Mar 30, 2026
79059a3
docs (CHANGELOG): Added missing PR links #116 and #115
GT-610 Mar 30, 2026
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
27 changes: 17 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
## [2.14.0] - 2026-03-19
- Fixed SSH connections through bastion hosts where the target server sends its version string immediately upon connection (which is standard behavior per RFC 4253) [#141]. Thanks @shihuili1218.
- Adds a new forwardLocalUnix() function, which is an equivalent of ssh -L localPort:remoteSocketPath [#140]. Thanks @isegal.


## [2.13.0] - 2025-06-22
- docs: Update NoPorts naming [`#115`]. [`@XavierChanth`].
- Add parameter disableHostkeyVerification [`#123`]. Thanks [`@alexander-irion`].
- Add support for server-initiated re-keying [`#125`]. Thanks [`@MarBazuz`].
- Add support for new algorithms "mac-sha2-256-96", "hmac-sha2-512-96", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com" [`#126`] [`#127`]. Thanks [`@reinbeumer`].
- docs: Update NoPorts naming [#115]. [@XavierChanth].
- Add parameter disableHostkeyVerification [#123]. Thanks [@alexander-irion].
- Add support for server-initiated re-keying [#125]. Thanks [@MarBazuz].
- Add support for new algorithms "mac-sha2-256-96", "hmac-sha2-512-96", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com" [#126] [#127]. Thanks [@reinbeumer].

## [2.12.0] - 2025-02-08
- Fixed streams and channel not closing after receiving SSH_Message_Channel_Close [#116]. [@cbenhagen].
Expand Down Expand Up @@ -171,12 +176,14 @@

- Initial release.

[`#127`]: https://github.com/TerminalStudio/dartssh2/pull/127
[`#126`]: https://github.com/TerminalStudio/dartssh2/pull/126
[`#125`]: https://github.com/TerminalStudio/dartssh2/pull/125
[`#123`]: https://github.com/TerminalStudio/dartssh2/pull/123
[`#116`]: https://github.com/TerminalStudio/dartssh2/pull/116
[`#115`]: https://github.com/TerminalStudio/dartssh2/pull/115
[#141]: https://github.com/TerminalStudio/dartssh2/pull/141
[#140]: https://github.com/TerminalStudio/dartssh2/pull/140
[#127]: https://github.com/TerminalStudio/dartssh2/pull/127
[#126]: https://github.com/TerminalStudio/dartssh2/pull/126
[#125]: https://github.com/TerminalStudio/dartssh2/pull/125
[#123]: https://github.com/TerminalStudio/dartssh2/pull/123
[#116]: https://github.com/TerminalStudio/dartssh2/pull/116
[#115]: https://github.com/TerminalStudio/dartssh2/pull/115
[#101]: https://github.com/TerminalStudio/dartssh2/pull/101
[#100]: https://github.com/TerminalStudio/dartssh2/issues/100
[#80]: https://github.com/TerminalStudio/dartssh2/issues/80
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as e
<td style="text-align: center;">
<b><a href="https://github.com/hsduren/dartshell">DartShell</a></b>
</td>
<!-- Naviterm -->
<td style="text-align: center;">
<b><a href="https://github.com/jc-hk-1916/NaviTerm">Naviterm</a></b>
</td>
</tr>

<tr>
Expand All @@ -65,6 +69,12 @@ SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as e
<td>
<img src="https://github.com/hsduren/dartshell/blob/main/info1.png" width="300px" alt="dartShell displaying terminal and session information for SSH operations">
</td>
<!-- NaviTerm -->
<td>
<a href="https://apps.apple.com/us/app/naviterm-ssh-sftp-tunnels/id6747072398">
<img src="https://raw.githubusercontent.com/jc-hk-1916/NaviTerm/main/images/1.png" width="300px" alt="Your all-in-one SSH terminal, SFTP client, and port forwarding tool, built from the ground up for macOS, iPhone, and iPad.">
</a>
</td>
</tr>
</table>

Expand Down
40 changes: 40 additions & 0 deletions example/forward_local_unix.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'dart:io';

import 'package:dartssh2/dartssh2.dart';

/// Example of forwarding a local TCP port to a remote Unix domain socket using `ssh -L localPort:remoteSocketPath`.
void main(List<String> args) async {
final socket = await SSHSocket.connect('localhost', 22);

final client = SSHClient(
socket,
username: 'root',
onPasswordRequest: () {
stdout.write('Password: ');
stdin.echoMode = false;
String? password;
try {
password = stdin.readLineSync();
} finally {
stdin.echoMode = true;
}
if (password == null) exit(1);
return password;
},
);

await client.authenticated;

final serverSocket = await ServerSocket.bind('localhost', 8080);

print('Listening on ${serverSocket.address.address}:${serverSocket.port}');

await for (final socket in serverSocket) {
final forward = await client.forwardLocalUnix('/var/run/docker.sock');
forward.stream.cast<List<int>>().pipe(socket);
socket.cast<List<int>>().pipe(forward.sink);
}

client.close();
await client.done;
}
2 changes: 2 additions & 0 deletions lib/src/algorithm/ssh_mac_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class SSHMacType extends SSHAlgorithm {
macFactory: _hmacSha512Factory,
isEtm: true,
);
// end added by Rein

const SSHMacType._({
required this.name,
required this.keySize,
Expand Down
39 changes: 39 additions & 0 deletions lib/src/message/msg_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class SSH_Message_Channel_Open implements SSHMessage {
final String? originatorIP;
final int? originatorPort;

/// "direct-streamlocal@openssh.com" channel specific data.
final String? socketPath;

SSH_Message_Channel_Open({
required this.channelType,
required this.senderChannel,
Expand All @@ -31,6 +34,7 @@ class SSH_Message_Channel_Open implements SSHMessage {
this.port,
this.originatorIP,
this.originatorPort,
this.socketPath,
});

factory SSH_Message_Channel_Open.session({
Expand Down Expand Up @@ -105,6 +109,23 @@ class SSH_Message_Channel_Open implements SSHMessage {
);
}

/// Opens a channel to forward data to a Unix domain socket on the remote
/// side. See OpenSSH PROTOCOL, section 2.4.
factory SSH_Message_Channel_Open.directStreamLocal({
required int senderChannel,
required int initialWindowSize,
required int maximumPacketSize,
required String socketPath,
}) {
return SSH_Message_Channel_Open(
channelType: 'direct-streamlocal@openssh.com',
senderChannel: senderChannel,
initialWindowSize: initialWindowSize,
maximumPacketSize: maximumPacketSize,
socketPath: socketPath,
);
}

factory SSH_Message_Channel_Open.decode(Uint8List bytes) {
final reader = SSHMessageReader(bytes);
reader.skip(1);
Expand Down Expand Up @@ -157,6 +178,17 @@ class SSH_Message_Channel_Open implements SSHMessage {
originatorIP: originatorIP,
originatorPort: originatorPort,
);
case 'direct-streamlocal@openssh.com':
final socketPath = reader.readUtf8();
// reserved string and uint32 per OpenSSH PROTOCOL spec
reader.readUtf8();
reader.readUint32();
return SSH_Message_Channel_Open.directStreamLocal(
senderChannel: senderChannel,
initialWindowSize: initialWindowSize,
maximumPacketSize: maximumPacketSize,
socketPath: socketPath,
);

default:
return SSH_Message_Channel_Open(
Expand Down Expand Up @@ -195,6 +227,11 @@ class SSH_Message_Channel_Open implements SSHMessage {
writer.writeUtf8(originatorIP!);
writer.writeUint32(originatorPort!);
break;
case 'direct-streamlocal@openssh.com':
writer.writeUtf8(socketPath!);
writer.writeUtf8(''); // reserved
writer.writeUint32(0); // reserved
break;
}
return writer.takeBytes();
}
Expand All @@ -210,6 +247,8 @@ class SSH_Message_Channel_Open implements SSHMessage {
return 'SSH_Message_Channel_Open(channelType: $channelType, senderChannel: $senderChannel, initialWindowSize: $initialWindowSize, maximumPacketSize: $maximumPacketSize, host: $host, port: $port, originatorIP: $originatorIP, originatorPort: $originatorPort)';
case 'direct-tcpip':
return 'SSH_Message_Channel_Open(channelType: $channelType, senderChannel: $senderChannel, initialWindowSize: $initialWindowSize, maximumPacketSize: $maximumPacketSize, host: $host, port: $port, originatorIP: $originatorIP, originatorPort: $originatorPort)';
case 'direct-streamlocal@openssh.com':
return 'SSH_Message_Channel_Open(channelType: $channelType, senderChannel: $senderChannel, initialWindowSize: $initialWindowSize, maximumPacketSize: $maximumPacketSize, socketPath: $socketPath)';
default:
return 'SSH_Message_Channel_Open(channelType: $channelType, senderChannel: $senderChannel, initialWindowSize: $initialWindowSize, maximumPacketSize: $maximumPacketSize)';
}
Expand Down
5 changes: 4 additions & 1 deletion lib/src/ssh_algorithm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,17 @@ class SSHAlgorithms {
SSHCipherType.aes256cbc,
SSHCipherType.aes128cbc,
],
// Prefer modern SHA-2 MACs by default; keep SHA-1 as fallback and MD5 last.
// Prefer modern SHA-2 MACs by default; full-length variants first,
// ETM variants for better security, truncated 96-bit as last-resort fallback.
this.mac = const [
SSHMacType.hmacSha256Etm,
SSHMacType.hmacSha512Etm,
SSHMacType.hmacSha256,
SSHMacType.hmacSha512,
SSHMacType.hmacSha1,
SSHMacType.hmacMd5,
SSHMacType.hmacSha256_96,
SSHMacType.hmacSha512_96,
],
});
}
31 changes: 31 additions & 0 deletions lib/src/ssh_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,21 @@ class SSHClient {
return SSHForwardChannel(channelController.channel);
}

/// Forward local connections to a remote Unix domain socket at
/// [remoteSocketPath] on the remote side via a
/// `direct-streamlocal@openssh.com` channel.
///
/// This is the equivalent of `ssh -L localPort:remoteSocketPath`.
Future<SSHForwardChannel> forwardLocalUnix(
String remoteSocketPath,
) async {
await _authenticated.future;
final channelController = await _openForwardLocalUnixChannel(
remoteSocketPath,
);
return SSHForwardChannel(channelController.channel);
}

/// Execute [command] on the remote side. Returns a [SSHChannel] that can be
/// used to read and write to the remote side.
Future<SSHSession> execute(
Expand Down Expand Up @@ -1265,6 +1280,22 @@ class SSHClient {
return await _waitChannelOpen(localChannelId);
}

Future<SSHChannelController> _openForwardLocalUnixChannel(
String socketPath,
) async {
final localChannelId = _channelIdAllocator.allocate();

final request = SSH_Message_Channel_Open.directStreamLocal(
senderChannel: localChannelId,
initialWindowSize: _initialWindowSize,
maximumPacketSize: _maximumPacketSize,
socketPath: socketPath,
);
_sendMessage(request);

return await _waitChannelOpen(localChannelId);
}

Future<SSHChannelController> _waitChannelOpen(
SSHChannelId localChannelId,
) async {
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: dartssh2
version: 2.13.0
version: 2.14.0
description: SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use.
homepage: https://github.com/TerminalStudio/dartssh2

Expand Down
6 changes: 3 additions & 3 deletions test/src/algorithm/ssh_cipher_type_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,14 @@ void main() {
expect(
algorithms.mac,
equals([
SSHMacType.hmacSha256_96,
SSHMacType.hmacSha512_96,
SSHMacType.hmacSha256Etm,
SSHMacType.hmacSha512Etm,
SSHMacType.hmacSha1,
SSHMacType.hmacSha256,
SSHMacType.hmacSha512,
SSHMacType.hmacSha1,
SSHMacType.hmacMd5,
SSHMacType.hmacSha256_96,
SSHMacType.hmacSha512_96,
]));
});
}
Expand Down
2 changes: 1 addition & 1 deletion test/src/socket/ssh_socket_io_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:test/test.dart';
void main() {
group('SSHSocket', () {
test('can establish tcp connections', () async {
final socket = await SSHSocket.connect('time.nist.gov', 13);
final socket = await SSHSocket.connect('test.rebex.net', 22);
final firstPacket = await socket.stream.first;
expect(firstPacket, isNotEmpty);
await socket.close();
Expand Down