96 lines
2.6 KiB
C#
96 lines
2.6 KiB
C#
using Renci.SshNet;
|
|
using Renci.SshNet.Common;
|
|
using McpSsh.Server.Ssh;
|
|
|
|
namespace McpSsh.Server.Terminal;
|
|
|
|
public interface ITerminalConnection : IDisposable
|
|
{
|
|
bool DataAvailable { get; }
|
|
|
|
string ReadAvailable();
|
|
|
|
void Write(string input);
|
|
}
|
|
|
|
public interface ITerminalConnectionFactory
|
|
{
|
|
ITerminalConnection Create(TerminalStartRequest request);
|
|
}
|
|
|
|
public sealed class SshNetTerminalConnectionFactory : ITerminalConnectionFactory
|
|
{
|
|
private const int ShellBufferSize = 16 * 1024;
|
|
private readonly ISshKeyResolver _keyResolver;
|
|
|
|
public SshNetTerminalConnectionFactory(ISshKeyResolver keyResolver)
|
|
{
|
|
_keyResolver = keyResolver;
|
|
}
|
|
|
|
public ITerminalConnection Create(TerminalStartRequest request)
|
|
{
|
|
try
|
|
{
|
|
var keyPath = _keyResolver.ResolveKeyPath(request.KeyPath);
|
|
var keyFile = string.IsNullOrEmpty(request.KeyPassphrase)
|
|
? new PrivateKeyFile(keyPath)
|
|
: new PrivateKeyFile(keyPath, request.KeyPassphrase);
|
|
var client = new SshClient(request.Host, request.Port, request.Username, keyFile);
|
|
|
|
client.Connect();
|
|
|
|
var stream = client.CreateShellStream(
|
|
"xterm-256color",
|
|
(uint)request.Cols,
|
|
(uint)request.Rows,
|
|
width: 0,
|
|
height: 0,
|
|
bufferSize: ShellBufferSize);
|
|
|
|
return new SshNetTerminalConnection(client, stream, keyFile);
|
|
}
|
|
catch (SshAuthenticationException ex)
|
|
{
|
|
throw new SshToolException("ssh_authentication_failed", "SSH key authentication failed. Check the username, key path, and key passphrase.", ex);
|
|
}
|
|
catch (SshException ex)
|
|
{
|
|
throw new SshToolException("ssh_error", ex.Message, ex);
|
|
}
|
|
}
|
|
|
|
private sealed class SshNetTerminalConnection : ITerminalConnection
|
|
{
|
|
private readonly SshClient _client;
|
|
private readonly ShellStream _stream;
|
|
private readonly PrivateKeyFile _keyFile;
|
|
|
|
public SshNetTerminalConnection(SshClient client, ShellStream stream, PrivateKeyFile keyFile)
|
|
{
|
|
_client = client;
|
|
_stream = stream;
|
|
_keyFile = keyFile;
|
|
}
|
|
|
|
public bool DataAvailable => _stream.DataAvailable;
|
|
|
|
public string ReadAvailable()
|
|
{
|
|
return _stream.Read();
|
|
}
|
|
|
|
public void Write(string input)
|
|
{
|
|
_stream.Write(input);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_stream.Dispose();
|
|
_client.Dispose();
|
|
_keyFile.Dispose();
|
|
}
|
|
}
|
|
}
|