サンプルプログラムの実装


https://github.com/haruyama/ssh_client_sample/blob...

  private def execCommand(transportManager: TransportManager, command: String) {
    // SSH-2.0-OpenSSH_5.8p1 Debian-4 の sshd に依存した作りになっている
    // 他の環境では動かない可能性がある

    // クライアント側のチャンネル番号
    val senderChannel = 0

    // initial window size と maximum packet size 値は適当
    var windowSize = 32678
    val maximumPacketSize = 32678

    // SSH_MSG_CHANNEL_OPEN を送信する
    transportManager.sendMessage(ConnectionMessageBuilder.buildChannelOpenSession(senderChannel, windowSize, maximumPacketSize))

    // SSH_MSG_CHANNEL_OPEN_CONFIRMATION を受信する
    val channelOpenConfirmation = transportManager.recvMessage().asInstanceOf[ChannelOpenConfirmation]

    // サーバ側のチャンネル番号
    val serverChannel = channelOpenConfirmation.senderChannel.value

    // SSH_MSG_CHANNEL_REQUEST を送信する
    transportManager.sendMessage(ConnectionMessageBuilder.buildChannelRequestExec(serverChannel, command))

    // SSH_MSG_CHANNEL_WINDOW_ADJUST を受信する
    val channelWindowAdjust = transportManager.recvMessage().asInstanceOf[ChannelWindowAdjust]

    // SSH_MSG_CHANNEL_SUCCESS を受信する
    val channelSuccess = transportManager.recvMessage().asInstanceOf[ChannelSuccess]

    // SSH_MSG_CHANNEL_DATA を受信する コマンド実行結果が含まれている
    val channelData = transportManager.recvMessage().asInstanceOf[ChannelData]
    println(new String(channelData.data.value))

    // SSH_MSG_CHANNEL_EOF を受信する
    val channelEof = transportManager.recvMessage().asInstanceOf[ChannelEof]

    // SSH_MSG_CHANNEL_REQUEST を受信する
    val channelExitStatus = transportManager.recvMessage().asInstanceOf[ChannelRequestExitStatus]

    // SSH_MSG_CHANNEL_CLOSE を送信する
    transportManager.sendMessage(ConnectionMessageBuilder.buildChannelClose(serverChannel))

    // SSH_MSG_CHANNEL_CLOSE を受信する
    val channelClose = transportManager.recvMessage().asInstanceOf[ChannelClose]
  }

解説

SSH_MSG_CHANNEL_OPEN の送信


コマンドの実行には, インタラクティブなセッションのチャンネルを開始する必要があります.

以下の形式の, SSH_MSG_CHANNEL_OPEN をクライアントはサーバに送ります.


byte      SSH_MSG_CHANNEL_OPEN
string    "session"
uint32    sender channel
uint32    initial window size
uint32    maximum packet size
  • sender channel : 送り手(ここではクライアント)側のチャンネル. コネクションプロトコルでは同時に複数のチャンネルを実行することが可能なため, チャンネル番号でどのチャンネルへのメッセージかを判別する. サンプルプログラムでは1つのチャンネルのみを利用している.
  • initial window size : チャンネルのデータを送り手に window の調整なしで送れる byte 数を指定する SSHコネクションプロトコルではチャンネルごとにフロー制御をするが, サンプルプログラムでは省略している.
  • maximum packet size : 送り手に送ることができる1つのデータパケットの最大サイズを指定する

SSH_MSG_CHANNEL_OPEN_CONFIRMATION の受信


サーバはクライアントに SSH_MSG_CHANNEL_OPEN_CONFIRMATION を返します.

byte      SSH_MSG_CHANNEL_OPEN_CONFIRMATION
uint32    recipient channel
uint32    sender channel
uint32    initial window size
uint32    maximum packet size
  • recipient channel : 受け取り手(ここではクライアント)側のチャンネル
  • sender channel : 送り手(ここではサーバ)側のチャンネル

SSH_MSG_CHANNEL_REQUEST の送信


コマンドの実行には, 以下の形式の SSH_MSG_CHANNEL_REQUEST をクライアントはサーバに送ります.

byte      SSH_MSG_CHANNEL_REQUEST
uint32    recipient channel
string    "exec"
boolean   want reply
string    command
  • recipient channel : 受け取り手(ここではサーバ)側のチャンネル. 受信した SSH_MSG_CHANNEL_OPEN_CONFIRMATION の recipient channel を指定します.
  • want reply : 要求に対する返答が欲しいかどうか. サンプルプログラムでは TRUE を指定. FALSE を指定すると 後の SSH_MSG_CHANNEL_SUCCESS が送られてきません.
  • command : コマンド

SSH_MSG_CHANNEL_WINDOW_ADJUST の受信


サーバはクライアントに SSH_MSG_CHANNEL_WINDOW_ADJUST を返します.

byte      SSH_MSG_CHANNEL_WINDOW_ADJUST
uint32    recipient channel
uint32    bytes to add
  • bytes to add : window に何バイト追加するか

SSH_MSG_CHANNEL_SUCCESS の受信


サーバはクライアントに 要求が成功したことを示す SSH_MSG_CHANNEL_SUCCESS を返します. SSH_MSG_CHANNEL_REQUEST で want reply が FALSE の場合は返されません.

byte      SSH_MSG_CHANNEL_SUCCESS
uint32    recipient channel

SSH_MSG_CHANNEL_DATA の受信


サーバはクライアントに, コマンドの実行結果を含む SSH_MSG_CHANNEL_DATA を返します.

byte      SSH_MSG_CHANNEL_DATA
uint32    recipient channel
string    data
  • data : この場合はコマンドの実行結果

SSH_MSG_CHANNEL_EOF の受信



サーバはクライアントに, もうこれ以上データを送らないことを示す SSH_MSG_CHANNEL_EOF を返します.

byte      SSH_MSG_CHANNEL_EOF
uint32    recipient channel

SSH_MSG_CHANNEL_REQUEST の受信


サーバはクライアントに, 終了ステータスの返却を行なう SSH_MSG_CHANNEL_REQUEST を返します.

この SSH_MSG_CHANNEL_REQUEST は以下の形式です. クライアントからコマンド文字列を送ったのも SSH_MSG_CHANNEL_REQUEST メッセージですが, 少し形式が異なります.

byte      SSH_MSG_CHANNEL_REQUEST
uint32    recipient channel
string    "exit-status"
boolean   FALSE
uint32    exit_status

クライアントはこのメッセージに応答する必要はありません.

SSH_MSG_CHANNEL_CLOSE の交換


クライアントはサーバに チャンネルの終了を要求する SSH_MSG_CHANNEL_CLOSE を送り, サーバも SSH_MSG_CHANNEL_CLOSE を返します.

byte      SSH_MSG_CHANNEL_CLOSE
uint32    recipient channel

サンプルプログラムの実行結果


ローカルホストにid:test, password:test というユーザを作成して, 「ls」コマンドを実行してみた結果です. 外からアクセスできるホストにこのようなユーザは作らないようにしましょう.

% ls ~test
a  hotate  ほげ

% ls -l ~test
合計 0
-rw-r--r-- 1 test test 0 2011-06-21 13:33 a
-rw-r--r-- 1 root root 0 2011-06-21 13:25 hotate
-rw-r--r-- 1 root root 0 2011-07-04 15:11 ほげ

% scala -cp lib/ganymed-ssh2-build210.jar target/scala-2.9.0.final/ssh-client-sample_2.9.0-1.0.jar \                                             
localhost 22 test test "ls"   
a
hotate
ほげ

% scala -cp lib/ganymed-ssh2-build210.jar target/scala-2.9.0.final/ssh-client-sample_2.9.0-1.0.jar \
  localhost 22 test test "ls -l"
合計 0
-rw-r--r-- 1 test test 0 2011-06-21 13:33 a
-rw-r--r-- 1 root root 0 2011-06-21 13:25 hotate
-rw-r--r-- 1 root root 0 2011-07-04 15:11 ほげ

メンバーのみ編集できます