MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Qtの基礎 - SSHのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Qtの基礎 - SSH
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == Qtにおいて、簡潔にSSH接続を確立する手順を記載する。<br> <br> * libSSHライブラリ (LGPLライセンス) を使用する。 * libSSH2ライブラリ (3条項BSDライセンス) を使用する。 * <code>QProcess</code>クラスを使用して、外部のSSHプロセスをフォークして接続を確立する。 * wolfSSH *: wolfSSHライブラリは、wolfSSLと連携して使用する。 *: 無償版は、<u>GPL v2ライセンス</u>である。 *: 有償版は、独自ライセンスである。 *: https://wolfssl.jp/products/wolfssh/ *: Github : https://github.com/wolfSSL/wolfssh *: wolfSSLのGithub : https://github.com/wolfSSL/wolfssl *: <br> *: また、wolfSSLは、IoTデバイス、組み込み向け小型デバイスにも対応した軽量のSSL/TLSライブラリである。 * QSshライブラリを使用する。 *: QSshは、QtアプリケーションにSSHとSFTPのサポートを提供する。 *: QSshプロジェクトは、過去のQt CreatorのSSHプラグインに基づいており、全てのクレジットはQt Creatorチームに帰属する。 *: ただし、現在では、Qt CreatorはOpenSSHを使用している。 *: <br> *: https://github.com/sandsmark/QSsh <br><br> == libSSHとlibSSH2の比較 == libSSHとlibSSH2は、どちらもSSHプロトコルを実装したライブラリである。<br> libSSHはC言語、libSSH2はC++言語で記述されている。<br> <br> また、libSSHは単独のライブラリとして提供されているのに対して、libSSH2はlibSSL等の他のライブラリと組み合わせて使用することが多い。<br> <br> プロジェクトの起源と開発者<br> * libSSH *: libSSHは、フランスのBenjamin GilbertとArel Corderoによって開発された。 *: libSSHは、SSHプロトコルの実装を提供しており、クライアントおよびサーバの機能をサポートしている。 *: <br> * libSSH2 *: libSSH2は、curlライブラリの主要な開発者でもあるスウェーデンのDaniel Stenbergによって開発された。 *: libSSH2は、SSHのクライアントおよびサーバ機能を提供することを目的としている。 *: ただし、libSSH2はサーバサイドのサポートは行っていない。 <br> 使用されるコードベース<br> * libSSH *: libSSHは、元々PuTTYの一部として開発され、その後独立したプロジェクトになった。 *: PuTTYは、Windows環境でのSSHクライアントとして広く知られている。 *: <br> * libSSH2 *: libSSH2は、curlライブラリとは独立したプロジェクトとして開発されている。 <br> <center> {| class="wikitable" | style="background-color:#fefefe;" |- ! style="background-color:#66CCFF; width: 50%;" | 項目 ! style="background-color:#66CCFF; width: 25%;" | libSSH2 ! style="background-color:#66CCFF; width: 25%;" | libSSH |- | ライブラリ名 || libssh2.so || libssh.so |- | ライセンス || 3条項BSD || LGPL 2.1 |- | サーバサイドのサポート || No || Yes |- | GSSAPI認証 || No || Yes |- | 楕円曲線鍵の交換 || No || Yes |- | 楕円曲線鍵のホスト鍵 || No || Yes |- | ナイトリーテストによるテストケースの自動化 || No || Yes |- | Stable API || Yes || ほとんどの部分 |- | C言語との互換 || C89 || C99 |- | 厳格な名前空間 || Yes || Yes |- | 全ての関数のマニュアル || Yes || No |- | 全ての関数のDoxygenドキュメント || No || Yes |- | Tutorial || Yes || Yes |- | SSH v1のサポート || No || Yes |} </center> <br><br> == libSSHライブラリのインストール == ==== libSSHとは ==== libSSHの多くは、LGPL 2.1ライセンスである。<br> 一部の機能には、2条項BSDライセンスのものがある。<br> <br> ==== パッケージ管理システムからインストール ==== sudo zypper install libssh-devel <br> ==== ソースコードからインストール ==== libSSHのビルドに必要なライブラリをインストールする。<br> sudo zypper install zlib-devel readline-devel libpcap-devel libopenssl-devel libopenssl-1_1-devel libgcrypt-devel p11-kit-devel libsodium-devel libcmocka-devel doxygen \ # 以下に示すライブラリは不要の可能性あり openpgm-devel ldns-devel zeromq-devel unbound-devel libunwind-devel \ libheimdal-devel libgssglue-devel gssntlmssp-devel <br> [https://www.libssh.org/ libSSHの公式Webサイト]または[https://git.libssh.org/projects/libssh.git libSSH向けのGit]にアクセスして、libSSHのソースコードをダウンロードする。<br> ダウンロードしたファイルを解凍する。<br> tar xf libssh-<バージョン>.tar.xz cd libssh-<バージョン> <br> libSSHをビルドおよびインストールする。<br> mkdir build && cd build cmake .. \ -DCMAKE_INSTALL_PREFIX=/<libSSHのインストールディレクトリ> -DCMAKE_BUILD_TYPE=Release \ -DWITH_GSSAPI=ON -DWITH_DSA=ON -DWITH_GCRYPT=ON \ # オプション -DWITH_PKCS11_URI=ON -DWITH_BLOWFISH_CIPHER=ON \ # オプション -DCMAKE_C_COMPILER=/<GCCのインストールディレクトリ>/bin/gcc \ # オプション -DCMAKE_CXX_COMPILER=/<GCCのインストールディレクトリ>/bin/g++ # オプション make -j $(nproc) make install <br> libSSHの使用例は、以下に示すURLを参考にすること。<br> https://api.libssh.org/stable/libssh_tutorial.html <br><br> == libSSH2ライブラリのインストール == ==== libSSH2とは ==== libSSH2は、3条項BSDライセンスである。(4条項BSDライセンスから、3番目にあった「宣伝条項」を削除したもの)<br> これにより、GPLと互換性が生まれたため、BSDライセンスのソフトウェアをGPLで配布することができる。(その逆は不可)<br> <br> ==== パッケージ管理システムからインストール ==== sudo zypper install libssh2-devel <br> ==== ソースコードからインストール ==== libSSH2のビルドに必要なライブラリをインストールする。<br> sudo zypper install zlib-devel libopenssl-devel libopenssl-1_1-devel <br> [https://www.libssh2.org/ libSSH2の公式Webサイト]または[https://github.com/libssh2/libssh2 Github]にアクセスして、libSSH2のソースコードをダウンロードする。<br> ダウンロードしたソースコードを解凍する。<br> tar xf libssh2.tar.xz cd libssh2 <br> libSSH2をビルドおよびインストールする。<br> mkdir build && cd build # configureスクリプトを使用する場合 ../cofigur --prefix=<libSSH2のインストールディレクトリ> \ --enable-examples-build --disable-debug --with-libz make -j $(nproc) make install # CMakeを使用する場合 cmake .. \ -DCMAKE_INSTALL_PREFIX=/<libssh2のインストールディレクトリ> \ -DLINT=ON -DBUILD_SHARED_LIBS=ON -DCLEAR_MEMORY=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_DEBUG_LOGGING=OFF \ -DCMAKE_C_COMPILER=/<GCCのインストールディレクトリ>/bin/gcc \ # オプション -DCMAKE_CXX_COMPILER=/<GCCのインストールディレクトリ>/bin/g++ # オプション make -j $(nproc) make install <br> libSSH2の使用例は、以下に示すURLを参考にすること。<br> http://www.chaosstuff.com/2013/09/gnome-mplayer-remote-with-qt-and-libssh2.html<br> https://bitbucket.org/nchokoev/qtsshremote<br> <br> Windowsの場合、CMakeおよびVisual Studioを使用してlibSSH2をビルドおよびインストールすることができる。<br> * CMakeを使用する場合 ** 32ビット Windows **: <code>cmake -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -A Win32 .. -B "x86"</code> **: <code>cmake --build x86 --config Release</code> **: <br> ** 64ビット Windows **: <code>cmake -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -A x64 .. -B "x64"</code> **: <code>cmake --build x64 --config Release</code> **: <br> *: コンパイルされたDLLファイルとlibファイルは、x86とx64のディレクトリのsrc/Releaseディレクトリに配置される。 *: <br> * Visual Studioを使用する場合 *: libSSH2ディレクトリと同階層にプロジェクトを作成する。 *: プロジェクトのプロパティを選択して、プロパティ画面左ペインにある[General] - プロパティ画面右ペインにある[ターゲット名]を"libSSH2_x64"等と入力する。 *: プロパティ画面左ペインにある[リンカ] - [Advanced] - プロパティ画面右ペインにある[インポートライブラリ]プルダウンから[<Inherit from parent or project defaults>]を選択する。 <br> また、ビルドされたDLLファイルは、以下に示すWebサイトからダウンロードできる。<br> https://download.csdn.net/download/sdhongjun/15682389<br> <br><br> == libSSHの使用例 == 以下の例では、公開鍵認証を使用してリモートPCへSSH接続している。<br> <br> <syntaxhighlight lang="make"> # .pro プロジェクトファイル # pkg-configを使用する場合 CONFIG += link_pkgconfig LIBS += \ -L/<libSSHのインストールディレクトリ>/lib64 -lssh # pkg-configを使用しない場合 LIBS += \ -L/<libSSHのインストールディレクトリ>/lib64 -lssh INCLUDEPATH += \ /<libSSHのインストールディレクトリ>/include </syntaxhighlight> <br> <syntaxhighlight lang="c++"> #include <QCoreApplication> #include <QMessageBox> #include <libssh/libssh.h> int VerifyKnownsHost(ssh_session session, QString &strErrMsg); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // SSHセッションの作成 ssh_session ssh_session = ssh_new(); if (my_ssh_session == NULL) { // 作成に失敗した場合 return -1; } // SSHセッションの設定 QString host = "<リモートPCのIPアドレス または ホスト名>"; QString user = "<リモートPCのユーザ名>"; QString port = "<SSHポート番号 例: 22>"; ssh_options_set(ssh_session, SSH_OPTIONS_HOST, host.toUtf8().data()); ssh_options_set(ssh_session, SSH_OPTIONS_USER, user.toUtf8().data()); ssh_options_set(ssh_session, SSH_OPTIONS_PORT_STR, port.toUtf8().data()); // SSH接続 int rc = ssh_connect(ssh_session); if (rc != SSH_OK) { // 接続に失敗した場合 fprintf(stderr, "Error connecting to host: %s\n", ssh_get_error(ssh_session)); ssh_free(ssh_session); return -1; } // ~/.sshディレクトリ等にあるファイルに記述されているサーバのIDを検証 QString strErrMsg = ""; if(VerifyKnownsHost(ssh_session, strErrMsg) < 0) { fprintf(stderr, "%s\n", strErrMsg.toUtf8().constData(); if(ssh_session != nullptr) { ssh_disconnect(ssh_session); ssh_free(ssh_session); } return -1; } // 公開鍵認証 // 秘密鍵の設定 const char *private_key_path = "<秘密鍵のパス 例: /home/user/sshkey/id_rsa"; // 秘密鍵のパスフレーズを設定していない場合 rc = ssh_userauth_privatekey_file(ssh_session, nullptr, private_key_path, nullptr); // 秘密鍵のパスフレーズを設定している場合 rc = ssh_userauth_privatekey_file(ssh_session, nullptr, private_key_path, "<秘密鍵のパスフレーズ>"); if (rc != SSH_AUTH_SUCCESS) { // 認証に失敗した場合 fprintf(stderr, "Error authenticating with private key: %s\n", ssh_get_error(my_ssh_session)); ssh_disconnect(my_ssh_session); ssh_free(my_ssh_session); return -1; } // SSHセッションを使用して任意の処理を実行 // scpコマンドの実行、または、リモート先で任意のコマンドの実行等 // ...略 // SSH接続の切断 ssh_disconnect(ssh_session); // SSHセッションの解放 ssh_free(ssh_session); return 0; } int VerifyKnownsHost(ssh_session ssh_session, QString &strErrMsg) { // Authenticating the server. ssh_key srv_pubkey = {}; if(ssh_get_server_publickey(ssh_session, &srv_pubkey) < 0) { strErrMsg = tr("Failed to get public key."); return -1; } unsigned char *hash = nullptr; size_t hlen = 0L; auto iRet = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hlen); ssh_key_free(srv_pubkey); if(iRet < 0) { strErrMsg = tr("Failed to get public key hash."); return -1; } auto state = ssh_session_is_known_server(ssh_session); if(state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_OK) { // Authentication Successful } else if(state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_CHANGED) { QString strHexa = ssh_get_hexa(hash, hlen); // print string in reverse order strErrMsg = tr("Host key for server changed:") + "<br>" + tr("For security reasons, connection will be stopped.") + "<br><br>" + tr("Public key hash:") + "<br>" + strHexa + "<br>" + hlen; ssh_clean_pubkey_hash(&hash); return -1; } else if(state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_OTHER) { strErrMsg = tr("The host key for this server was not found but an other type of key exists.") + "<br>" + tr("An attacker might change the default server key to confuse your client into") + "<br>" + tr("thinking the key does not exist"); ssh_clean_pubkey_hash(&hash); return -1; } else if(state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_NOT_FOUND) { /* FALL THROUGH to SSH_KNOWN_HOSTS_UNKNOWN behavior */ QString strHexa = ssh_get_hexa(hash, hlen); QString strAddHostMessage = tr("Could not find known host file.") + "\n" + tr("If you accept the host key here, the file will be automatically created.") + "\n\n" + tr("The server is unknown. Do you trust the host key?") + "\n" + tr("Public key hash: ") + "\n" + strHexa; auto ret = QMessageBox(QMessageBox::Warning, QMessageBox::tr("Add Host"), strAddHostMessage, QMessageBox::Yes | QMessageBox::No, nullptr).exec(); ssh_clean_pubkey_hash(&hash); if(ret == QMessageBox::No) { strErrMsg = tr("To connect, please add host key."); return -1; } else { iRet = ssh_session_update_known_hosts(ssh_session); if(iRet < 0) { strErrMsg = tr("Failed to update host key."); return -1; } } } else if(state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_UNKNOWN) { QString strHexa = ssh_get_hexa(hash, hlen); QString strAddHostMessage = tr("The server is unknown. Do you trust the host key?") + "\n" + tr("Public key hash: ") + "\n" + strHexa; auto msgRet = QMessageBox(QMessageBox::Warning, QMessageBox::tr("Add Host"), strAddHostMessage, QMessageBox::Yes | QMessageBox::No, nullptr).exec(); ssh_clean_pubkey_hash(&hash); if(msgRet == QMessageBox::No) { strErrMsg = tr("To connect, please add host key."); return -1; } else { iRet = ssh_session_update_known_hosts(ssh_session); if(iRet < 0) { strErrMsg = tr("Failed to update host key."); return -1; } } } else if(state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_ERROR) { strErrMsg = tr("There was an error in checking the host."); ssh_clean_pubkey_hash(&hash); return -1; } ssh_clean_pubkey_hash(&hash); return 0; } </syntaxhighlight> <br><br> == libSSHを使用した独自クラスの使用 == 以下に示すクラスは、クロスプラットフォームの非同期SSHおよびSCPソケットである。<br> このクラスは、libSSHを必要とする。(RSA鍵の受け渡しを隠したり、コマンドの応答をreadyReadシグナル経由ではなく、シングルショットで送信している)<br> <br> Windowsで動作させるには、このクラスを使用するファイルの最上部にインクルードする必要がある。<br> これにより、QtがWindowsソケットをインクルードする前にWindows.hファイルをインクルードさせないようにする。<br> <br> <center><code><u>[[ファイル:CSSH.zip|フレームなし|中央]]</u></code></center> <br><br> == libSSH2ライブラリを使用したコマンドの使用例 == ==== ブロッキングモード ==== 以下の例では、リモート側のPCにSSH接続して、lsコマンドを実行してその結果をローカル側のPCに表示している。<br> <br> 以下の例は、<u>ブロッキングモード</u>でlsコマンドを実行している。<br> <br> <syntaxhighlight lang="make"> # .pro プロジェクトファイル # QTcpSocketを使用する場合 QT += network # pkg-configを使用する場合 CONFIG += link_pkgconfig LIBS += \ -L/<libSSH2のインストールディレクトリ>/lib64 -lssh2 # pkg-configを使用しない場合 LIBS += \ -L/<libSSH2のインストールディレクトリ>/lib64 -lssh2 INCLUDEPATH += \ /<libSSH2のインストールディレクトリ>/include </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // main.cpp #include <QCoreApplication> #include <QTcpSocket> #include <QDebug> #include <libssh2.h> #include "DivideByZeroException.h" void DisConnect(QTcpSocket &sock, LIBSSH2_SESSION *session); int ExecCommand(QTcpSocket &sock, LIBSSH2_SESSION *session); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); int rc = 0; QTcpSocket sock; LIBSSH2_SESSION *session = nullptr; // libSSH2オブジェクトの初期化 auto rc = libssh2_init(0); if (rc != 0) { qDebug() << "libssh2 initialization failed"; DisConnect(sock, session); return -1; } // QTcpSocketクラスのインスタンスを生成して、リモートPCに接続 sock.connectToHost(<リモートPCのIPアドレスまたはホスト名>, <リモートPCにSSH接続するポート番号>); // 最大10[秒]待機 if (!sock.waitForConnected(10000)) { qDebug() << "SSH接続に失敗:" << socket.errorString(); DisConnect(sock, session); return -1; } // SSHのセッションの初期化 session = libssh2_session_init(); if (!session) { qDebug() << "SSHのセッションの初期化に失敗"; DisConnect(sock, session); return -1; } // SSHのハンドシェイク if (libssh2_session_handshake(session, sock.socketDescriptor()) != 0) { qDebug() << "SSHのハンドシェイクに失敗"; DisConnect(sock, session); return -1; } // リモートPCのフィンガープリントを確認 auto fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); qDebug() << "リモートPCのフィンガープリント:" << fingerprint; // 認証方法の確認 auto userauth_list = libssh2_userauth_list(session, "<リモートPCのユーザ名>", strlen(USERNAME)); qDebug() << "認証方法:" << userauth_list; // パスワード認証を行う場合 //if (libssh2_userauth_password(session, "<リモートPCのユーザ名>", "<リモートPCのユーザパスワード>") != 0) { // qDebug() << "パスワードによる認証に失敗"; // return -1; //} // 公開鍵認証 if (libssh2_userauth_publickey_fromfile(session, "<リモートPCのユーザ名>", "<公開鍵ファイルのパス>", "<秘密鍵ファイルのパス>", "<秘密鍵のパスフレーズ>") != 0) { qDebug() << "公開鍵認証に失敗"; DisConnect(sock, session); return -1; } qDebug() << QString("Authentication succeeded."); // SSH接続後の処理 // リモートPC上でls -alコマンドを実行 rc = ExecCommand(sock, session); if (rc != 0) { DisConnect(sock, session); return -1; } // SSH接続の終了 DisConnect(sock, session); return 0; } void DisConnect(QTcpSocket &sock, LIBSSH2_SESSION *session) { // セッションの終了 if (session) { libssh2_session_disconnect(session, "Normal Shutdown"); libssh2_session_free(session); session = nullptr; } // ソケットを閉じる if(sock.isOpen()) { sock.close(); } // libSSH2の終了 libssh2_exit(); return; } int ExecCommand(QTcpSocket &sock, LIBSSH2_SESSION *session) { // チャンネルをオープン LIBSSH2_CHANNEL *channel = libssh2_channel_open_session(session); if (!channel) { qDebug() << "チャンネルのオープンに失敗"; return -1; } // リモートPC上でls -alコマンドを実行 if (libssh2_channel_exec(channel, "ls -la") != 0) { qDebug() << "コマンドの実行に失敗"; return -1; } // ls -alコマンドの実行結果を取得 int rc = 0; QByteArray buffer; buffer.resize(1024); // バッファサイズを1024バイトに指定 do { rc = libssh2_channel_read(channel, buffer.data(), buffer.size()); if (rc > 0) { qDebug().noquote() << QString::fromUtf8(buffer.constData(), rc); } else if (rc < 0) { qDebug() << "Error reading channel:" << rc; break; } } while (rc > 0); // チャンネルの終了を確認 // 終了コードおよび終了シグナルを取得して表示 int exitcode = 127; char *exitsignal = nullptr; rc = libssh2_channel_close(channel); if (rc == 0) { exitcode = libssh2_channel_get_exit_status(channel); libssh2_channel_get_exit_signal(channel, &exitsignal, nullptr, nullptr, nullptr, nullptr, nullptr); } if (exitsignal) qDebug() << "Exit signal:" << exitsignal; else qDebug() << "Exit code:" << exitcode; return 0; } </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // DivideByZeroException.h #ifndef DIVIDEBYZEROEXCEPTION_H #define DIVIDEBYZEROEXCEPTION_H #include <QException> class DivideByZeroException : public QException { public: DivideByZeroException() {}; virtual ~DivideByZeroException() {}; const char* what() const noexcept override { return "Divide by zero exception"; } }; #endif // DIVIDEBYZEROEXCEPTION_H </syntaxhighlight> <br> ==== ノンブロッキングモード ==== 以下の例では、リモート側のPCにSSH接続して、lsコマンドを実行してその結果をローカル側のPCに表示している。<br> <br> 以下の例は、<u>ノンブロッキングモード</u>でlsコマンドを実行している。<br> <u>ノンブロッキングモード</u> (シグナル / スロット) で設計する場合、libSSH2ライブラリの関数呼び出しは即座に返されて、処理がバックグラウンドで非同期に進行する。<br> これにより、プログラムは他の処理を続行でき、必要に応じてリモートホストとの通信が完了するのを待つことができる。<br> <br> ノンブロッキングモードを使用する場合、特に入出力操作が発生する待ち時間を最小限に抑え、プログラムがより効率的に動作することが期待される。<br> しかし、ノンブロッキングモードを扱う際には、非同期処理やイベント駆動型のプログラミングに慣れる必要がある。<br> <br> <syntaxhighlight lang="c++"> // SSHClient.hファイル #include <QCoreApplication> #include <QTcpSocket> #include <QTimer> #include <QDebug> #include <libssh2.h> class SSHClient : public QObject { Q_OBJECT private: QTcpSocket *m_Socket; LIBSSH2_SESSION *m_Session; LIBSSH2_CHANNEL *m_Channel; QString m_Host; quint16 m_Port; QString m_User; QString m_Passwaord; QString m_PubKey; QString m_PrivateKey; QString m_Passphrase; public: SSHClient(QString host, quint16 port, QString user, QString password, QObject *parent = nullptr); SSHClient(QString host, quint16 port, QString user, QString pubkey, QString privkey, QString phrase, QObject *parent = nullptr); ~SSHClient(); void connectToHost(); private slots: void onConnected(); void authenticateUser(); void openChannel(); void executeCommand(); void onReadyRead(); void readOutput(); void closeChannel(); }; </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // SSHClient.cppファイル #include "SSHClient.h" SSHClient::SSHClient(QString host, quint16 port, QString user, QString password, QObject *parent = nullptr) : QObject(parent), m_Session(nullptr), m_Channel(nullptr), m_Host(host), m_Port(port), m_User(user), m_Password(password) { m_Socket = new QTcpSocket(this); connect(m_Socket, &QTcpSocket::connected, this, &SSHClient::onConnected); connect(m_Socket, &QTcpSocket::readyRead, this, &SSHClient::onReadyRead); // libSSH2ライブラリの初期化 if (libssh2_init(0) != 0) { qDebug() << "libSSH2ライブラリの初期化に失敗"; return; } } SSHClient::SSHClient(QString host, quint16 port, QString user, QString pubkey, QString privkey, QString phrase, QObject *parent = nullptr) : QObject(parent), m_Session(nullptr), m_Channel(nullptr), m_Host(host), m_Port(port), m_User(user), m_PublicKey(pubkey), m_PrivateKey(privkey), m_Passphrase(phrase) { m_Socket = new QTcpSocket(this); connect(m_Socket, &QTcpSocket::connected, this, &SSHClient::onConnected); connect(m_Socket, &QTcpSocket::readyRead, this, &SSHClient::onReadyRead); // libSSH2ライブラリの初期化 if (libssh2_init(0) != 0) { qDebug() << "libSSH2ライブラリの初期化に失敗"; return; } } SSHClient::~SSHClient() { if (m_Channel) libssh2_channel_free(m_Channel); if (m_Session) { libssh2_session_disconnect(m_Session, "Normal Shutdown"); libssh2_session_free(m_Session); } libssh2_exit(); } void SSHClient::connectToHost() { m_Socket->connectToHost(m_Host, m_Port); } void SSHClient::onConnected() { qDebug() << "ホストへの接続に成功"; m_Session = libssh2_session_init(); libssh2_session_set_blocking(m_Session, 0); int rc = 0; do { rc = libssh2_session_handshake(m_Sssion, m_Socket->socketDescriptor()); } while (rc == LIBSSH2_ERROR_EAGAIN); if (rc) { qDebug() << "SSHのセッションの確立に失敗:" << rc; return; } authenticateUser(); } void SSHClient::authenticateUser() { int rc = 0; do { // パスワード認証を行う場合 //rc = libssh2_userauth_password(m_Session, // m_User.toUtf8().constData(), // m_Password.toUtf8().constData()); // 公開鍵認証を行う場合 rc = libssh2_userauth_publickey_fromfile(m_Session, m_User.toUtf8().constData(), m_PubKey.toUtf8().constData(), m_PrivateKey.toUtf8().constData(), m_Passphrase.toUtf8().constData()); } while (rc == LIBSSH2_ERROR_EAGAIN); if (rc) { qDebug() << "認証に失敗:" << rc; return; } openChannel(); } void SSHClient::openChannel() { int rc = 0; do { m_Channel = libssh2_channel_open_session(m_Session); } while (m_Channel == nullptr && libssh2_session_last_error(m_Session, nullptr, nullptr, 0) == LIBSSH2_ERROR_EAGAIN); if (!m_Channel) { qDebug() << "チャンネルのオープンに失敗"; return; } executeCommand(); } void SSHClient::executeCommand() { int rc = 0; do { rc = libssh2_channel_exec(m_Channel, "ls -la"); } while (rc == LIBSSH2_ERROR_EAGAIN); if (rc) { qDebug() << "lsコマンドの実行に失敗:" << rc; return; } // lsコマンドの出力結果を取得を開始 QTimer::singleShot(0, this, &SSHClient::readOutput); } void SSHClient::onReadyRead() { readOutput(); } void SSHClient::readOutput() { if (!m_Channel) return; QByteArray buffer; buffer.resize(1024); int rc = 0; while (rc != LIBSSH2_ERROR_EAGAIN) { rc = libssh2_channel_read(m_Channel, buffer.data(), buffer.size()); if (rc > 0) { qDebug().noquote() << QString::fromUtf8(buffer.constData(), rc); } else if (rc < 0) { qDebug() << "チャンネルの読み取りエラー:" << rc; closeChannel(); return; } } // チャンネルがEOFかどうかを確認 if (libssh2_channel_eof(m_Channel) == 1) { closeChannel(); } else { // 続きを読み込む QTimer::singleShot(0, this, &SSHClient::readOutput); } } void SSHClient::closeChannel() { if (!m_Channel) return; int rc = 0; do { rc = libssh2_channel_close(m_Channel); } while (rc == LIBSSH2_ERROR_EAGAIN); if (rc == 0) { int exitcode = libssh2_channel_get_exit_status(m_Channel); char *exitsignal = nullptr; libssh2_channel_get_exit_signal(m_Channel, &exitsignal, nullptr, nullptr, nullptr, nullptr, nullptr); if (exitsignal) { qDebug() << "Exit signal:" << exitsignal; } else { qDebug() << "Exit code:" << exitcode; } } libssh2_channel_free(m_Channel); m_Channel = nullptr; // Disconnect from the host m_Socket->disconnectFromHost(); } </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // main.cppファイル int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); SSHClient client; client.connectToHost("<リモートPC側のIPアドレスまたはホスト名>", <リモートPCのポート番号>, "<リモートPC側のユーザ名>", "<リモートPC側のユーザパスワード>"); return a.exec(); } </syntaxhighlight> <br><br> == sshdコマンドの使用 == ==== OpenSSHのソースコードのダウンロード ==== [https://www.openssh.com/portable.html OpenSSHの公式Webサイト]にアクセスして、ソースコードをダウンロードする。<br> ダウンロードしたファイルを解凍する。<br> tar xf openssh-<バージョン>.tar.gz <br> このディレクトリ内に必要なヘッダファイル等が存在する。<br> <br> なお、OpenSSHのライセンスの全てのコンポーネントは、BSDライセンス、もしくはそれよりも自由なライセンスに属している。<br> <br> ==== sshd -tコマンド ==== 以下の例では、openSSHライブラリを使用して、<code>sshd -t</code>コマンドと同様、指定された設定ファイルの解析と妥当性を確認している。<br> <br> # <code>initialize_server_options</code>関数を呼び出して、サーバオプションを初期化する。 # コマンドライン引数からSSHの設定ファイルのパスを取得する。<br>引数が指定されていない場合は、デフォルトの"sshd_config"ファイルを使用する。 # <code>parse_server_config_depth</code>関数を呼び出して、指定されたSSHの設定ファイルを解析する。<br>解析に成功した場合は<code>0</code>、失敗した場合は<code>0以外</code>の値を返す。 # <code>validate_server_config</code>関数を呼び出して、解析された設定ファイルの妥当性を確認する。<br>確認に成功した場合は<code>0</code>、失敗した場合は<code>0以外</code>の値を返す。 # SSHの設定ファイルの解析と妥当性の結果に応じて、成功または失敗のメッセージを表示している。 <br> <syntaxhighlight lang="c++"> #include <QCoreApplication> #include <QDebug> #include <cstdio> #include <cstdlib> #include <cstring> #include <unistd.h> #include <sshbuf.h> #include <sshkey.h> #include <authfile.h> #include <auth-options.h> #include <servconf.h> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); ServerOptions options; initialize_server_options(&options); // コマンドライン引数からSSHの設定ファイルのパスを取得 QString configFile = "sshd_config"; if (a.arguments().size() > 1) { configFile = a.arguments().at(1); } // SSHの設定ファイルの解析 if (parse_server_config_depth(&options, configFile.toUtf8().constData(), NULL, 0) != 0) { qDebug() << QString("SSHの設定ファイルのパースに失敗: %1).arg(configFile); return -1; } // SSHの設定ファイルの妥当性を確認 if (validate_server_config(&options) != 0) { qDebug() << QString("SSHの設定ファイルに誤りがあります: %1").arg(configFile); return -1; } qDebug() << QString("SSHの設定ファイル (%1) は正常です).arg(configFile); return 0; } </syntaxhighlight> <br> ==== sshd -Tコマンド ==== 以下の例では、openSSHライブラリを使用して、<code>sshd -T</code>コマンドと同様、現在の設定オプションの値をダンプ形式で表示している。<br> <br> # <code>initialize_server_options</code>関数を呼び出して、サーバオプションを初期化する。 # <code>fill_default_server_options</code>関数を呼び出して、デフォルトのサーバオプションを設定する。 # <code>parse_server_config_depth</code>関数を呼び出して、sshd_configファイルを解析する。<br>解析に成功した場合は<code>0</code>、失敗した場合は<code>0以外</code>の値を返す。 # 解析に成功した場合は、<code>dump_config</code>関数を呼び出して、設定オプションのダンプを表示する。<br>これは、<code>sshd -T</code>コマンドと同様、現在の設定オプションの値を表示する。 <br> <syntaxhighlight lang="c++"> #include <QCoreApplication> #include <QDebug> #include <cstdio> #include <cstdlib> #include <cstring> #include <unistd.h> #include <sshbuf.h> #include <sshkey.h> #include <authfile.h> #include <auth-options.h> #include <servconf.h> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // SSHの設定ファイルオプション ServerOptions options; initialize_server_options(&options); fill_default_server_options(&options); if (parse_server_config_depth(&options, "sshd_config", NULL, 0) != 0) { qDebug() << "sshd_configファイルのパースに失敗"; return -1; } qDebug() << "========= Dump of configuration options ========="; dump_config(&options); qDebug() << "================================================"; return 0; } </syntaxhighlight> <br><br> __FORCETOC__ [[カテゴリ:Qt]]
Qtの基礎 - SSH
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse