0 purchases
dartssh2 plus
DartSSH 2
SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use.
dartssh2 is now a complete rewrite of dartssh.
โจ Features #
Pure Dart: Working with both Dart VM and Flutter.
SSH Session: Executing commands, spawning shells, setting environment variables, pseudo terminals, etc.
Authentication: Supports password, private key and interactive authentication method.
Forwarding: Supports local forwarding and remote forwarding.
SFTP: Supports all operations defined in SFTPv3 protocol including upload, download, list, link, remove, rename, etc.
๐งฌ Built with dartssh2 #
ServerBox
Ssh! No Ports
Feel free to add your own app here by opening a pull request.
๐งช Try #
# Install the `dartssh` command.
dart pub global activate dartssh2_cli
# Then use `dartssh` as regular `ssh` command.
dartssh [email protected]
# Example: execute a command on remote host.
dartssh [email protected] ls -al
# Example: connect to a non-standard port.
dartssh [email protected]:<port>
# Transfer files via SFTP.
dartsftp [email protected]
copied to clipboard
If the dartssh command can't be found after installation, you might need to set up your path.
๐ Quick start #
Connect to a remote host #
final client = SSHClient(
await SSHSocket.connect('localhost', 22),
username: '<username>',
onPasswordRequest: () => '<password>',
);
copied to clipboard
SSHSocket is an interface and it's possible to implement your own SSHSocket if you want to use a different underlying transport rather than standard TCP socket. For example WebSocket or Unix domain socket.
Spawn a shell on remote host #
final shell = await client.shell();
stdout.addStream(shell.stdout); // listening for stdout
stderr.addStream(shell.stderr); // listening for stderr
stdin.cast<Uint8List>().listen(shell.write); // writing to stdin
await shell.done; // wait for shell to exit
client.close();
copied to clipboard
Execute a command on remote host #
final uptime = await client.run('uptime');
print(utf8.decode(uptime));
copied to clipboard
Ignoring stderr:
final uptime = await client.run('uptime', stderr: false);
print(utf8.decode(uptime));
copied to clipboard
client.run() is a convenience method that wraps client.execute() for running non-interactive commands.
Start a process on remote host #
final session = await client.execute('cat > file.txt');
await session.stdin.addStream(File('local_file.txt').openRead().cast());
await session.stdin.close(); // Close the sink to send EOF to the remote process.
await session.done; // Wait for session to exit to ensure all data is flushed to the remote process.
print(session.exitCode); // You can get the exit code after the session is done
copied to clipboard
session.write() is a shorthand for session.stdin.add(). It's recommended to use session.stdin.addStream() instead of session.write() when you want to stream large amount of data to the remote process.
Killing a remote process by sending signal
session.kill(SSHSignal.KILL);
await session.done;
print('exitCode: ${session.exitCode}'); // -> exitCode: null
print('signal: ${session.exitSignal?.signalName}'); // -> signal: KILL
copied to clipboard
Processes killed by signals do not have an exit code, instead they have an exit signal property.
Forward connections on local port 8080 to the server #
final serverSocket = await ServerSocket.bind('localhost', 8080);
await for (final socket in serverSocket) {
final forward = await client.forwardLocal('httpbin.org', 80);
forward.stream.cast<List<int>>().pipe(socket);
socket.pipe(forward.sink);
}
copied to clipboard
Forward connections to port 2222 on the server to local port 22 #
final forward = await client.forwardRemote(port: 2222);
if (forward == null) {
print('Failed to forward remote port');
return;
}
await for (final connection in forward.connections) {
final socket = await Socket.connect('localhost', 22);
connection.stream.cast<List<int>>().pipe(socket);
socket.pipe(connection.sink);
}
copied to clipboard
Authenticate with public keys #
final client = SSHClient(
socket,
username: '<username>',
identities: [
// A single private key file may contain multiple keys.
...SSHKeyPair.fromPem(await File('path/to/id_rsa').readAsString())
],
);
copied to clipboard
Use encrypted PEM files #
// Test whether the private key is encrypted.
final encrypted = SSHKeyPair.isEncrypted(await File('path/to/id_rsa').readAsString());
print(encrypted);
// If the private key is encrypted, you need to provide the passphrase.
final keys = SSHKeyPair.fromPem('<pem text>', '<passphrase>');
print(keys);
copied to clipboard
Decrypt PEM file with compute in Flutter
List<SSHKeyPair> decryptKeyPairs(List<String> args) {
return SSHKeyPair.fromPem(args[0], args[1]);
}
final keypairs = await compute(decryptKeyPairs, ['<pem text>', '<passphrase>']);
copied to clipboard
Get the version of SSH server #
await client.authenticated;
print(client.remoteVersion); // SSH-2.0-OpenSSH_7.4p1
copied to clipboard
Connect through a jump server #
final jumpServer = SSHClient(
await SSHSocket.connect('<jump server>', 22),
username: '...',
onPasswordRequest: () => '...',
);
final client = SSHClient(
await jumpServer.forwardLocal('<target server>', 22),
username: '...',
onPasswordRequest: () => '...',
);
print(utf8.decode(await client.run('hostname'))); // -> hostname of <target server>
copied to clipboard
}
SFTP #
List remote directory #
final sftp = await client.sftp();
final items = await sftp.listdir('/');
for (final item in items) {
print(item.longname);
}
copied to clipboard
Read remote file #
final sftp = await client.sftp();
final file = await sftp.open('/etc/passwd');
final content = await file.readBytes();
print(latin1.decode(content));
copied to clipboard
Write remote file #
final sftp = await client.sftp();
final file = await sftp.open('file.txt', mode: SftpFileOpenMode.write);
await file.writeBytes(utf8.encode('hello there!') as Uint8List);
copied to clipboard
Write at specific offset
final data = utf8.encode('world') as Uint8List
await file.writeBytes(data, offset: 6);
copied to clipboard
File upload #
final sftp = await client.sftp();
final file = await sftp.open('file.txt', mode: SftpFileOpenMode.create | SftpFileOpenMode.write);
await file.write(File('local_file.txt').openRead().cast());
copied to clipboard
Pause and resume file upload
final uploader = await file.write(File('local_file.txt').openRead().cast());
// ...
await uploader.pause();
// ...
await uploader.resume();
await uploader.done;
copied to clipboard
Clear the remote file before opening it
final file = await sftp.open('file.txt',
mode: SftpFileOpenMode.create | SftpFileOpenMode.truncate | SftpFileOpenMode.write
);
copied to clipboard
Directory operations #
final sftp = await client.sftp();
await sftp.mkdir('/path/to/dir');
await sftp.rmdir('/path/to/dir');
copied to clipboard
Get/Set attributes from/to remote file/directory #
await sftp.stat('/path/to/file');
await sftp.setStat(
'/path/to/file',
SftpFileAttrs(mode: SftpFileMode(userRead: true)),
);
copied to clipboard
Get the type of a remote file #
final stat = await sftp.stat('/path/to/file');
print(stat.type);
// or
print(stat.isDirectory);
print(stat.isSocket);
print(stat.isSymbolicLink);
// ...
copied to clipboard
Create a link #
final sftp = await client.sftp();
sftp.link('/from', '/to');
copied to clipboard
Get (estimated) total and free space on the remote filesystem #
final sftp = await client.sftp();
final statvfs = await sftp.statvfs('/root');
print('total: ${statvfs.blockSize * statvfs.totalBlocks}');
print('free: ${statvfs.blockSize * statvfs.freeBlocks}');
copied to clipboard
๐ช Example #
SSH client: #
example/example.dart
example/execute.dart
example/forward_local.dart
example/forward_remote.dart
example/pubkey.dart
example/shell.dart
example/ssh_jump.dart
SFTP: #
example/sftp_read.dart
example/sftp_list.dart
example/sftp_stat.dart
example/sftp_upload.dart
example/sftp_filetype.dart
๐ Supported algorithms #
Host key:
ssh-rsa
rsa-sha2-[256|512]
ecdsa-sha2-nistp[256|384|521]
ssh-ed25519
Key exchange:
curve25519-sha256
ecdh-sha2-nistp[256|384|521]
diffie-hellman-group-exchange-sha[1|256]
diffie-hellman-group14-sha[1|256]
diffie-hellman-group1-sha1
Cipher:
aes[128|192|256]-ctr
aes[128|192|256]-cbc
Integrity:
hmac-md5
hmac-sha1
hmac-sha2-[256|512]
Private key:
Type
Decode
Decrypt
Encode
Encrypt
RSA
โ๏ธ
โ๏ธ
โ๏ธ
WIP
OpenSSH RSA
โ๏ธ
โ๏ธ
โ๏ธ
WIP
OpenSSH ECDSA
โ๏ธ
โ๏ธ
โ๏ธ
WIP
OpenSSH Ed25519
โ๏ธ
โ๏ธ
โ๏ธ
WIP
โณ Roadmap #
โ
Fix broken tests
โ
Sound null safety
โ
Redesign API to allow starting multiple sessions.
โ
Full SFTP
โ Server
References #
RFC 4250 The Secure Shell (SSH) Protocol Assigned Numbers
RFC 4251 The Secure Shell (SSH) Protocol Architecture
RFC 4252 The Secure Shell (SSH) Authentication Protocol
RFC 4253 The Secure Shell (SSH) Transport Layer Protocol
RFC 4254 The Secure Shell (SSH) Connection Protocol
RFC 4255 Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
RFC 4256 Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)
RFC 4419 Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol
RFC 4716 The Secure Shell (SSH) Public Key File Format
RFC 5656 Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer
RFC 8332 Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol
RFC 8731 Secure Shell (SSH) Key Exchange Method Using Curve25519 and Curve448
draft-miller-ssh-agent-03 SSH Agent Protocol
draft-ietf-secsh-filexfer-02 SSH File Transfer Protocol
draft-dbider-sha2-mac-for-ssh-06 SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol
Credits #
https://github.com/GreenAppers/dartssh by GreenAppers
License #
dartssh is released under the terms of the MIT license. See LICENSE.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.