📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
細 文字列「__FORCETOC__」を「{{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This pag… |
|||
| (同じ利用者による、間の38版が非表示) | |||
| 2行目: | 2行目: | ||
Qtにおいて、簡潔にSSH接続を確立する手順を記載する。<br> | Qtにおいて、簡潔にSSH接続を確立する手順を記載する。<br> | ||
<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> | <br><br> | ||
== | == libSSHとlibSSH2の比較 == | ||
libSSHとlibSSH2は、どちらもSSHプロトコルを実装したライブラリである。<br> | libSSHとlibSSH2は、どちらもSSHプロトコルを実装したライブラリである。<br> | ||
libSSHはC言語、libSSH2はC++言語で記述されている。<br> | libSSHはC言語、libSSH2はC++言語で記述されている。<br> | ||
<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> | <br> | ||
<center> | <center> | ||
{| class="wikitable" | style="background-color:#fefefe;" | {| class="wikitable" | style="background-color:#fefefe;" | ||
|- | |- | ||
! style="background-color:#66CCFF; width: | ! style="background-color:#66CCFF; width: 50%;" | 項目 | ||
! style="background-color:#66CCFF; width: | ! style="background-color:#66CCFF; width: 25%;" | libSSH2 | ||
! style="background-color:#66CCFF; width: | ! style="background-color:#66CCFF; width: 25%;" | libSSH | ||
|- | |- | ||
| ライブラリ名 || libssh2.so || libssh.so | | ライブラリ名 || libssh2.so || libssh.so | ||
| 48行目: | 79行目: | ||
| Tutorial || Yes || Yes | | Tutorial || Yes || Yes | ||
|- | |- | ||
| | | SSH v1のサポート || No || Yes | ||
|} | |} | ||
</center> | </center> | ||
<br><br> | <br><br> | ||
== | == libSSHライブラリのインストール == | ||
==== | ==== libSSHとは ==== | ||
libSSHの多くは、LGPL 2.1ライセンスである。<br> | |||
一部の機能には、2条項BSDライセンスのものがある。<br> | |||
<br> | <br> | ||
==== パッケージ管理システムからインストール ==== | ==== パッケージ管理システムからインストール ==== | ||
sudo zypper install libssh-devel | sudo zypper install libssh-devel | ||
<br> | <br> | ||
==== ソースコードからインストール ==== | ==== ソースコードからインストール ==== | ||
libSSHのビルドに必要なライブラリをインストールする。<br> | |||
sudo zypper install zlib-devel readline-devel | 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 | openpgm-devel ldns-devel zeromq-devel unbound-devel libunwind-devel \ | ||
libheimdal-devel libgssglue-devel gssntlmssp-devel | libheimdal-devel libgssglue-devel gssntlmssp-devel | ||
<br> | <br> | ||
[https://www.libssh.org/ | [https://www.libssh.org/ libSSHの公式Webサイト]または[https://git.libssh.org/projects/libssh.git libSSH向けのGit]にアクセスして、libSSHのソースコードをダウンロードする。<br> | ||
ダウンロードしたファイルを解凍する。<br> | |||
tar xf libssh-<バージョン>.tar.xz | tar xf libssh-<バージョン>.tar.xz | ||
cd libssh-<バージョン> | cd libssh-<バージョン> | ||
<br> | <br> | ||
libSSHをビルドおよびインストールする。<br> | |||
mkdir build && cd build | mkdir build && cd build | ||
cmake .. \ | cmake .. \ | ||
-DCMAKE_INSTALL_PREFIX=/< | -DCMAKE_INSTALL_PREFIX=/<libSSHのインストールディレクトリ> -DCMAKE_BUILD_TYPE=Release \ | ||
-DCMAKE_C_COMPILER=/<GCCのインストールディレクトリ>/bin/gcc \ # オプション | -DWITH_GSSAPI=ON -DWITH_DSA=ON -DWITH_GCRYPT=ON \ # オプション | ||
-DCMAKE_CXX_COMPILER=/<GCCのインストールディレクトリ>/bin/g++ | -DWITH_PKCS11_URI=ON -DWITH_BLOWFISH_CIPHER=ON \ # オプション | ||
-DCMAKE_C_COMPILER=/<GCCのインストールディレクトリ>/bin/gcc \ # オプション | |||
-DCMAKE_CXX_COMPILER=/<GCCのインストールディレクトリ>/bin/g++ # オプション | |||
make -j $(nproc) | make -j $(nproc) | ||
make install | make install | ||
<br> | <br> | ||
libSSHの使用例は、以下に示すURLを参考にすること。<br> | |||
https://api.libssh.org/stable/libssh_tutorial.html | https://api.libssh.org/stable/libssh_tutorial.html | ||
<br><br> | <br><br> | ||
| 149行目: | 184行目: | ||
<br><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 my_ssh_session, QString &strErrMsg); | |||
int main(int argc, char *argv[]) | |||
{ | |||
QCoreApplication a(argc, argv); | |||
// SSHセッションの作成 | |||
ssh_session my_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(my_ssh_session, SSH_OPTIONS_HOST, host.toUtf8().data()); | |||
ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, user.toUtf8().data()); | |||
ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT_STR, port.toUtf8().data()); | |||
// SSH接続 | |||
int rc = ssh_connect(my_ssh_session); | |||
if (rc != SSH_OK) { | |||
// 接続に失敗した場合 | |||
fprintf(stderr, "Error connecting to host: %s\n", ssh_get_error(my_ssh_session)); | |||
ssh_free(my_ssh_session); | |||
return -1; | |||
} | |||
// ~/.sshディレクトリ等にあるファイルに記述されているサーバのIDを検証 | |||
QString strErrMsg = ""; | |||
if(VerifyKnownsHost(my_ssh_session, strErrMsg) < 0) | |||
{ | |||
fprintf(stderr, "%s\n", strErrMsg.toUtf8().constData(); | |||
if(my_ssh_session != nullptr) | |||
{ | |||
ssh_disconnect(my_ssh_session); | |||
ssh_free(my_ssh_session); | |||
} | |||
return -1; | |||
} | |||
// 公開鍵認証 | |||
// 秘密鍵の設定 | |||
const char *private_key_path = "<秘密鍵のパス 例: /home/user/sshkey/id_rsa"; | |||
// 秘密鍵のパスフレーズを設定していない場合 | |||
rc = ssh_userauth_privatekey_file(my_ssh_session, nullptr, private_key_path, nullptr); | |||
// 秘密鍵のパスフレーズを設定している場合 | |||
rc = ssh_userauth_privatekey_file(my_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(my_ssh_session); | |||
// SSHセッションの解放 | |||
ssh_free(my_ssh_session); | |||
return 0; | |||
} | |||
int VerifyKnownsHost(ssh_session my_ssh_session, QString &strErrMsg) | |||
{ | |||
// Authenticating the server. | |||
ssh_key srv_pubkey = {}; | |||
if(ssh_get_server_publickey(my_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(my_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(my_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(my_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> | 以下に示すクラスは、クロスプラットフォームの非同期SSHおよびSCPソケットである。<br> | ||
このクラスは、libSSHを必要とする。(RSA鍵の受け渡しを隠したり、コマンドの応答をreadyReadシグナル経由ではなく、シングルショットで送信している)<br> | |||
<br> | <br> | ||
Windowsで動作させるには、このクラスを使用するファイルの最上部にインクルードする必要がある。<br> | Windowsで動作させるには、このクラスを使用するファイルの最上部にインクルードする必要がある。<br> | ||
| 158行目: | 416行目: | ||
<center><code><u>[[ファイル:CSSH.zip|フレームなし|中央]]</u></code></center> | <center><code><u>[[ファイル:CSSH.zip|フレームなし|中央]]</u></code></center> | ||
<br><br> | <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_Session, 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(), | |||
"", // クライアントPCに秘密鍵があり、 | |||
// リモートPC側に公開鍵が設置されている場合 | |||
m_PrivateKey.toUtf8().constData(), | |||
m_Passphrase.isEmpty() ? nullptr : 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("<リモートPC側のIPアドレスまたはホスト名>", <リモートPCのポート番号>, | |||
// "<リモートPC側のユーザ名>", "<リモートPC側のユーザパスワード>"); | |||
// 公開鍵認証を行う場合 | |||
SSHClient client("<リモートPC側のIPアドレスまたはホスト名>", <リモートPCのポート番号>, | |||
"<リモートPC側のユーザ名>", | |||
"<公開鍵ファイルのパス>", // クライアントPCに秘密鍵があり、リモートPC側に公開鍵が設置されている場合は空欄 ("") にする | |||
"<秘密鍵ファイルのパス>", | |||
"<秘密鍵のパスフレーズ>"); | |||
client.connectToHost(); | |||
return a.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== SCPコマンドの使用例 == | |||
libSSHライブラリおよびlibSSH2ライブラリを使用したSCPコマンドの使用例は、以下に示すページに記載している。<br> | |||
* [[Qtの基礎_-_SCP#libSSHライブラリを使用したSCPコマンドの使用例|libSSHライブラリを使用したSCPコマンドの使用例]] | |||
* [[Qtの基礎_-_SCP#libSSH2ライブラリを使用したSCPコマンドの使用例|libSSH2ライブラリを使用したSCPコマンドの使用例]] | |||
<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> | |||
{{#seo: | |||
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki | |||
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 | |||
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux | |||
|image=/resources/assets/MochiuLogo_Single_Blue.png | |||
}} | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] | ||