12,964
回編集
393行目: | 393行目: | ||
<br><br> | <br><br> | ||
== | == libSSHを使用した独自クラスの使用 == | ||
以下に示すクラスは、クロスプラットフォームの非同期SSHおよびSCPソケットである。<br> | 以下に示すクラスは、クロスプラットフォームの非同期SSHおよびSCPソケットである。<br> | ||
このクラスは、libSSHを必要とする。(RSA鍵の受け渡しを隠したり、コマンドの応答をreadyReadシグナル経由ではなく、シングルショットで送信している)<br> | このクラスは、libSSHを必要とする。(RSA鍵の受け渡しを隠したり、コマンドの応答をreadyReadシグナル経由ではなく、シングルショットで送信している)<br> | ||
402行目: | 402行目: | ||
<center><code><u>[[ファイル:CSSH.zip|フレームなし|中央]]</u></code></center> | <center><code><u>[[ファイル:CSSH.zip|フレームなし|中央]]</u></code></center> | ||
<br><br> | <br><br> | ||
== libSSH2の使用例 == | |||
<syntaxhighlight lang="c++"> | |||
#include <QCoreApplication> | |||
#include <QFileInfo> | |||
#include <QTimer> | |||
#include <QElapsedTimer> | |||
#include <QDebug> | |||
#include <libssh2.h> | |||
#include <libssh2_sftp.h> | |||
#include "DivideByZeroException.h" | |||
#ifdef Q_OS_LINUX | |||
#include <sys/socket.h> | |||
#include <netinet/in.h> | |||
#include <arpa/inet.h> | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#endif | |||
void DisConnect(int &sock, LIBSSH2_SESSION *session); | |||
int Send(int &sock, LIBSSH2_SESSION *session); | |||
int waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session); | |||
int main(int argc, char *argv[]) | |||
{ | |||
QCoreApplication a(argc, argv); | |||
int rc = 0; | |||
int sock = 0; | |||
LIBSSH2_SESSION *session = nullptr; | |||
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64) | |||
WSADATA wsadata; | |||
rc = WSAStartup(MAKEWORD(2, 0), &wsadata); | |||
if(rc) { | |||
std::cerr << "WSAStartup failed with error: " << rc << std::endl; | |||
return -1; | |||
} | |||
#endif | |||
// 初期化 | |||
rc = libssh2_init(0); | |||
if (rc != 0) { | |||
qDebug() << QString("libssh2 initialization failed %1").arg(rc); | |||
DisConnect(sock, session); | |||
return -1; | |||
} | |||
// ソケットの作成 | |||
sock = socket(AF_INET, SOCK_STREAM, 0); | |||
if (sock == -1) { | |||
qDebug() << QString("failed to create socket."); | |||
DisConnect(sock, session); | |||
return -1; | |||
} | |||
struct sockaddr_in sin = {}; | |||
sin.sin_family = AF_INET; | |||
sin.sin_port = htons(50022); | |||
sin.sin_addr.s_addr = inet_addr("<リモート側PCのIPアドレス または ホスト名>"); | |||
// SSH接続 | |||
if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) == -1) { | |||
qDebug() << QString("failed to connect."); | |||
DisConnect(sock, session); | |||
return -1; | |||
} | |||
// libSSH2のセッションを初期化 | |||
session = libssh2_session_init(); | |||
// リモート側のPCとのハンドシェイク | |||
libssh2_session_handshake(session, sock); | |||
// パスワード認証 | |||
if (libssh2_userauth_password(session, "<リモート側のユーザ名>", "<ユーザ名のパスワード>") != 0) { | |||
qDebug() << QString("Authentication failed."); | |||
DisConnect(sock, session); | |||
return -1; | |||
} | |||
qDebug() << QString("Authentication succeeded."); | |||
// SSH接続後の処理 | |||
// SCPでファイルを送信 | |||
rc = Send(sock, session); | |||
if (rc != 0) { | |||
DisConnect(sock, session); | |||
return -1; | |||
} | |||
// SSH接続の終了 | |||
DisConnect(sock, session); | |||
return 0; | |||
} | |||
void DisConnect(int &sock, LIBSSH2_SESSION *session) | |||
{ | |||
// セッションの終了 | |||
if (session) { | |||
libssh2_session_disconnect(session, "Normal Shutdown"); | |||
libssh2_session_free(session); | |||
session = nullptr; | |||
} | |||
// ソケットを閉じる | |||
if (sock != LIBSSH2_INVALID_SOCKET) { | |||
shutdown(sock, 2); | |||
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64) | |||
closesocket(sock); | |||
#else | |||
close(sock); | |||
#endif | |||
} | |||
// libSSH2の終了 | |||
libssh2_exit(); | |||
return; | |||
} | |||
int Send(int &sock, LIBSSH2_SESSION *session) | |||
{ | |||
// Send a file via scp. The mode parameter must only have permissions. | |||
LIBSSH2_CHANNEL *channel = nullptr; | |||
QString localFilePath = "<ローカルPCのファイルパス 例: /tmp/hoge.png>"; | |||
QFile File(localFilePath); | |||
if(!File.open(QIODevice::ReadOnly)) | |||
{ | |||
qDebug() << QString("Cannot open local File(%1) Open Error: %2").arg(QFileInfo(File).fileName(), File.errorString()); | |||
return -1; | |||
} | |||
// QFileInfoクラスではパーミッションは16進数のため、libssh2_scp_send64関数で使用するため8進数に変換する | |||
// これは、見た目の数値を同じにする必要がある 例: 0x644(16進数) --> 0644(8進数) | |||
QFileInfo FileInfo(localFilePath); | |||
//// ステッキービットは不要なため削除 | |||
auto hexPermisshionString = QString::number(FileInfo.permissions(), 16).mid(1); | |||
bool ok; | |||
//// 8進数に変換 | |||
unsigned int octPermisshionValue = hexPermisshionString.toInt(&ok, 8); | |||
if (!ok) { | |||
qDebug() << QString("Failed to convert permission value."); | |||
return -1; | |||
} | |||
QString remoteFilePath = "<リモート側PCのファイルパス 例: /home/remote-user/hoge.png>"; | |||
do { | |||
channel = libssh2_scp_send64(session, remoteFilePath.toUtf8().constData(), octPermisshionValue & 0777, FileInfo.size(), 0, 0); | |||
if (!channel && libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { | |||
File.close(); | |||
char *err_msg; | |||
libssh2_session_last_error(session, &err_msg, NULL, 0); | |||
qDebug() << err_msg; | |||
return -1; | |||
} | |||
} while (!channel); | |||
qDebug() << QString("SCP session waiting to send file."); | |||
// タイマの開始 | |||
QElapsedTimer elapsedTimer; | |||
elapsedTimer.start(); | |||
int nread = 0, | |||
total = 0; | |||
char mem[1024 * 100] = {}; | |||
do { | |||
nread = File.read(mem, sizeof(mem)); | |||
if(nread <= 0) { | |||
// EOF | |||
break; | |||
} | |||
auto ptr = mem; | |||
total += nread; | |||
auto prev = 0; | |||
do { | |||
ssize_t nwritten; | |||
while ((nwritten = libssh2_channel_write(channel, ptr, nread)) == LIBSSH2_ERROR_EAGAIN) { | |||
waitsocket(sock, session); | |||
prev = 0; | |||
} | |||
if (nwritten < 0) { | |||
qDebug() << QString("ERROR %1 total %2 / %3 prev %4").arg((int)nwritten) | |||
.arg((long)total) | |||
.arg((int)nread) | |||
.arg((int)prev); | |||
break; | |||
} | |||
else { | |||
prev = nread; | |||
// nwritten indicates how many bytes were written this time. | |||
nread -= nwritten; | |||
ptr += nwritten; | |||
} | |||
} while (nread); | |||
} while (!nread); // only continue if nread was drained. | |||
File.close(); | |||
// 経過時間をミリ秒単位で取得 | |||
try { | |||
qint64 duration = elapsedTimer.elapsed() == 0 ? throw DivideByZeroException() : elapsedTimer.elapsed(); | |||
qDebug() << QString("%1 bytes in %2 milli-seconds makes %3 bytes/sec").arg(static_cast<long>(total)) | |||
.arg(duration) | |||
.arg(QString::number((double)((total * 1000) / duration), 'f', 1)); | |||
} | |||
catch (const DivideByZeroException &ex) { | |||
qDebug() << ex.what(); | |||
qDebug() << QString("Transfer rate could not be calculated."); | |||
} | |||
qDebug() << QString("Sending EOF"); | |||
while(libssh2_channel_send_eof(channel) == LIBSSH2_ERROR_EAGAIN); | |||
qDebug() << QString("Waiting for EOF"); | |||
while(libssh2_channel_wait_eof(channel) == LIBSSH2_ERROR_EAGAIN); | |||
qDebug() << QString("Waiting for channel to close"); | |||
while(libssh2_channel_wait_closed(channel) == LIBSSH2_ERROR_EAGAIN); | |||
return 0; | |||
} | |||
int waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session) | |||
{ | |||
struct timeval timeout = {}; | |||
timeout.tv_sec = 10; | |||
timeout.tv_usec = 0; | |||
fd_set fd; | |||
FD_ZERO(&fd); | |||
FD_SET(socket_fd, &fd); | |||
// now make sure we wait in the correct direction. | |||
auto dir = libssh2_session_block_directions(session); | |||
fd_set *writefd = nullptr; | |||
fd_set *readfd = nullptr; | |||
if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) readfd = &fd; | |||
if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) writefd = &fd; | |||
auto rc = select((int)(socket_fd + 1), readfd, writefd, nullptr, &timeout); | |||
return rc; | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] |