本日はC#でSSH接続してコマンド実行するツールを作成したお話。SSH通信するライブラリでメジャー所はSSH.NETか、
SharpSSHあたりが有名ですが、後者はもうかなり前に開発が終了してるようなので今回はSSH.NETを利用し作成してみた。
SSH通信を行う為の下準備
SSH通信を行う為にSsh.NetをNugetでインストールを試みる。が、
‘SSH.NET’ にはすでに ‘SshNet.Security.Cryptography’ に対して定義された依存関係があります。
とポップアップが出現しインストール段階で即躓く。
nuget(VS自体)の更新が必要だった様で更新したら無事インストール出来たので、同じ症状が出た人は先ず更新すべし。
SSH接続(ユーザー名とパスワード)を行う
取り敢えずkssh通信を行うクラスを作成し、接続部分はこんな感じ↓
using Renci.SshNet;
static public class ClsSsh
{
static private SshClient m_sshClient;
static private ShellStream m_shellStream;
/////////////////////////////////////////////////////////
// 概要:ssh接続処理
// はまみ:2020/02/20
/////////////////////////////////////////////////////////
static public Boolean Connect(string strHostName, string strLoginId, string strPassword, ref string strResult)
{
Boolean bRet = true;
try
{
// 接続情報
ConnectionInfo ConnNfo = new ConnectionInfo(strHostName,
22,
strLoginId,
new AuthenticationMethod[]{new PasswordAuthenticationMethod(strLoginId, strPassword),});
// SSHクライアント生成
m_sshClient = new SshClient(ConnNfo);
m_sshClient.ConnectionInfo.Timeout = new TimeSpan(0, 0, 5);
// SSH接続
m_sshClient.Connect();
// SSH接続成功
if (m_sshClient.IsConnected)
{
// コマンド実行
m_shellStream = m_sshClient.CreateShellStream(string.Empty, 0, 0, 0, 0, 0);
StreamReader streamReader = new StreamReader(m_shellStream, Encoding.GetEncoding("shift_jis"));
StreamWriter streamWriter = new StreamWriter(m_shellStream, Encoding.GetEncoding("shift_jis"));
streamWriter.AutoFlush = true;
// 接続結果読み込み
strResult += streamReader.ReadToEnd();
}
}
catch (Exception ex)
{
bRet = false;
strResult = ex.ToString();
}
return bRet;
}
渡されたIPアドレス・ID・PASSにログインを試みて結果(strResult)を返す。
ssh.Connect()して、接続成功すればIsConnected=Trueとなりますが、失敗した場合はただFalseになってくれる訳では無く例外を吐くのでしっかり拾ってやる必要がある。
パスワード認証に失敗した場合はエラーメッセージでPermission denied (password).を返します。
切断処理を行う
続いて切断処理↓
/////////////////////////////////////////////////////////
// 概要:ssh切断処理
// はまみ:2020/02/20
/////////////////////////////////////////////////////////
static public Boolean DisConnect(ref string strResult)
{
Boolean bRet = true;
try
{
m_shellStream.Dispose();
// SSH切断
m_sshClient.Disconnect();
m_sshClient.Dispose();
m_shellStream = null;
m_sshClient = null;
}
catch (Exception ex)
{
bRet = false;
strResult = ex.ToString();
}
return bRet;
}
接続後はしっかり切断も忘れず行うべし(かなり忘れがち)。実際に使う場合はこんな感じでクラス化して接続・コマンド実行・切断で分ける形になると思うので、不要になったらしっかりDisconnect&Disposeを!
コマンドを実行する
/////////////////////////////////////////////////////////
// 概要:コマンド実行処理
// はまみ:2020/02/20
/////////////////////////////////////////////////////////
static public Boolean Cmd(string strCmd, ref string strResult)
{
Boolean bRet = true;
try
{
StreamReader streamReader = new StreamReader(m_shellStream, Encoding.GetEncoding("shift_jis"));
StreamWriter streamWriter = new StreamWriter(m_shellStream, Encoding.GetEncoding("shift_jis"));
streamWriter.AutoFlush = true;
streamWriter.WriteLine("console lines infinity");
// コマンド実行結果読み込み
strResult = streamReader.ReadToEnd();
// コマンド実行
streamWriter.WriteLine(strCmd);
// コマンド実行結果読み込み
strResult = streamReader.ReadToEnd();
}
catch (Exception ex)
{
bRet = false;
strResult = ex.ToString();
}
return bRet;
}
strCmdで渡されたコマンドを実行して結果(strResult)を返す。
ログインされてない状態でコマンドを実行するとエラーとなるため未ログインの状態ではこの関数は呼ばれないようにするか、事前に拾ってfalseで返す等の処置が必要。
最初のログインが遅い、時間が掛かる場合
TeraTermとかだとそんなに遅くないのに、ssh.netを用いたアプリの場合のみログインに時間が掛かる場合が有ります。
相手がサーバやPC等の場合はDNSでの名前解決に時間がかかっているケースが殆どで、その場合UseDNS=noの設定を入れると改善される。
だけどもIOT機器等でそんな設定が存在しない!って場合はDLLが古い可能性大なので更新してみましょう~(ᵔᴥᵔ)
コメント