SSH2でログアウト時に SSH2_MSG_DISCONNECT(SSH2_DISCONNECT_PROTOCOL_ERROR)
SSH は一つの接続の上に複数のチャネルが乗っかる構造になっています。
SSH2_MSG_CHANNEL_CLOSE はチャネルを閉じるもので、SSH2_MSG_DISCONNECT は接続を閉じるものなので、用途というかレイヤーが違います。
ですが、ほかのクライアントの実装を見ると、SSH2_MSG_DISCONNECT を送らない実装もあるようです。
どうやらすべてのチャネルが閉じると接続も閉じられるようなので、試しに変更してみました。
http://ttssh2.sourceforge.jp/snapshot/snapshot-20141001.zip
To: 各位
以下の確認はどうしたらできるでしょうか。
・すべてのチャネルが閉じたとき、サーバは必ず SSH2_MSG_DISCONNECT を送ることになっているのか。RFC を見てもわかりませんでした。
・クライアントから SSH2_MSG_DISCONNECT を送らなくてよいのか。RFC では SHOULD なので、いいのでしょうか。
・サーバからの SSH2_MSG_DISCONNECT を待たずにTCP接続を切断しに行くと思われるが大丈夫か。
maya への返信
SSH は一つの接続の上に複数のチャネルが乗っかる構造になっています。 SSH2_MSG_CHANNEL_CLOSE はチャネルを閉じるもので、SSH2_MSG_DISCONNECT は接続を閉じるものなので、用途というかレイヤーが違います。
ご説明 ありがとうございます。
...だとすると、TTSSH.LOGに記録されているように SSH2_MSG_DISCONNECT 送信後に SSH2_MSG_CHANNEL_CLOSEを送信する (チャネルを閉じる前に、接続丸ごと閉じる)のは、ちょっと 気持ち悪い 感じがします。
... SSH2_MSG_CHANNEL_CLOSE was received. local:0 remote:0 ... SSH2_MSG_DISCONNECT was sent at handle_SSH2_channel_close(). ... SSH2_MSG_CHANNEL_CLOSE was sent at SSH_notify_disconnecting(). ... Terminating SSH session...
...だとすると、TTSSH.LOGに記録されているように SSH2_MSG_DISCONNECT 送信後に SSH2_MSG_CHANNEL_CLOSEを送信する (チャネルを閉じる前に、接続丸ごと閉じる)のは、ちょっと 気持ち悪い 感じがします。
接続を閉じれば全部閉じてしまうので、それで全部かたが付くはずです。
接続を閉じるメッセージ送信後にチャネルを閉じるメッセージを送っているのが、順序が逆で気持ち悪いですね。
今回入れた r5672 の修正ではこうなります。
SSH2_MSG_CHANNEL_REQUEST was received. local:0 remote:0 exit-status reply:0 SSH2_MSG_CHANNEL_EOF was received. local:0 remote:0 SSH2_MSG_CHANNEL_CLOSE was received. local:0 remote:0 SSH2_MSG_CHANNEL_CLOSE was sent at SSH_notify_disconnecting(). Terminating SSH session...
maya への返信
...だとすると、TTSSH.LOGに記録されているように SSH2_MSG_DISCONNECT 送信後に SSH2_MSG_CHANNEL_CLOSEを送信する (チャネルを閉じる前に、接続丸ごと閉じる)のは、ちょっと 気持ち悪い 感じがします。
接続を閉じれば全部閉じてしまうので、それで全部かたが付くはずです。
接続を閉じるメッセージ送信後にチャネルを閉じるメッセージを送っているのが、順序が逆で気持ち悪いですね。
はい、了解です。
# 「ちょっと 気持ち悪い」→ 「SSH_MSG_DISSCONNECT送信後に(SSH2_MSG_CHANNEL_CLOSEを含め)メッセージ送信してOK?」と思っただけです。
# RFC4253では "The sender MUST NOT send or receive any data after this message"と言っているようですし
今回入れた r5672 の修正ではこうなります。
はい、これで 私の気にしていたsyslogメッセージは出なくなりました。ありがとうございます。
あとは maya さんの「TO:各位」以下の部分ですが、私にはわかりませんでした。
# そもそも SSH Transport layer を 正常終了(CLOSE)するには どうするべきなのかが わかってない... orz
maya への返信
・すべてのチャネルが閉じたとき、サーバは必ず SSH2_MSG_DISCONNECT を送ることになっているのか。RFC を見てもわかりませんでした。
これはきまりはなさそうです。
・クライアントから SSH2_MSG_DISCONNECT を送らなくてよいのか。RFC では SHOULD なので、いいのでしょうか。
SHOULDは特別な理由がなければするべき事なので、よほどの理由がなければ送るべきです。
ただ、個別のエラーに関しては SSH2_MSG_DISCONNECT を送って(SHOULD)から接続を切れ(MUST)となっていますが、 すべてのチャネルを閉じた時に関しての記述は見当たりませんでした。
今回の問題ですが、SSH_MSG_DISCONNECT を送る事が問題ではなく、reason code として SSH_DISCONNECT_PROTOCOL_ERROR を送っている事だと思います。
通常の切断でPROTOCOL ERRORはちょっと違うと思います。どちらかというとSSH_DISCONNECT_BY_APPLICATION 辺りではないでしょうか。
実際、OpenSSH の ssh client は SSH_DISCONNECT_BY_APPLICATION を送っています。
OpenSSH の過去の修正内容を確認したところ、
という状況なので、reason code を SSH_DISCONNECT_BY_APPLICATION に変更するのがいいと思います。
それとは別に、SSH_MSG_DISCONNECT の後に SSH_MSG_CHANNEL_CLOSE を送るのはまずいので、これの修正も必要ですね。
接続の切り方を追いかける必要がありそうですね。
shell のチャネルを SSH2_MSG_CHANNEL_CLOSE 閉じてすべてのチャネルが閉じられても、接続は残るようです。
notify_closed_connection は SSH_notify_disconnecting を呼んで shell のチャネルの CHANNEL_CLOSE を送信してすぐソケットを閉じてしまうようです。
また、SSH2_MSG_DISCONNECT を受け取ると handle_disconnect が必ず notify_fatal_error に渡って、その先で SSH_notify_disconnecting を呼んで(以下同様
以下メモ
・shell のチャネルを SSH2_MSG_CHANNEL_CLOSE したら(受け付けたら?送ったら?)SSH2_MSG_DISCONNECT を送るようにする?
・SSH_notify_disconnecting の呼び出し元を確認。認証途中の切断などは reason code がどうあるべきか調べる。
・SSH2_MSG_DISCONNECT を送る関数の作成、reason code と文字列を引数で受け付ける。
・notify_fatal_error による切断の仕方をもう少し行儀よくする。
r5678 にて、切断時の動きをさらに調整しました。MSG_DISCONNECT のかわりに MSG_CHANNEL_CLOSE を送っていたのを修正しました。
http://ttssh2.sourceforge.jp/snapshot/snapshot-20141004.zip
MSG_DISCONNECT を送る箇所が増えました。送られる理由が複数ありますが、reason code は岩本さんのおっしゃるとおり常に SSH_DISCONNECT_BY_APPLICATION になるようにしました。本件の理由であるサーバのログに残らないようにするためです。
# ちなみに私のサーバ (FreeBSD) の syslog.conf は "*.notice;authpriv.none;kern.debug;lpr.info;mail.crit;news.err /var/log/messages", "auth.info;authpriv.info /var/log/auth.log" となっているので、messages には出現せず auth.log には出現しています。
今回調べていて、File - Disconnect で切断された場合は、なにもサーバに通知せずに接続を切っていることがわかりましたので記しておきます。
Windows 7上で Teraterm 4.84(TTSSH 2.70)を使って SSH2でUNIXサーバにログインし、ログインシェルからexitすると、UNIXサーバのsyslogに以下のメッセージが出ます。 (SSHクライアントが OpenSSHや puttyでは 出力されないようです)
エラーは "disconnected by server request"という文字列とともに reason_code 2:SSH2_DISCONNECT_PROTOCOL_ERRORの SSH2_MSG_DISCONNECTを 受信したため出力されています。TTSSHのログレベルを上げてみると 確かに SSH2_MSG_DISCONNECTを送信しているようです。
ソースを見ると (http://sourceforge.jp/projects/ttssh2/scm/svn/blobs/head/trunk/ttssh2/ttxssh/ssh.c)
handle_SSH2_channel_close() にて、(c->type == TYPE_SHELL) のときに 該当メッセージを出しているようです。
SSH2_MSG_DISCONNECT無しで、SSH2_MSG_CHENNEL_CLOSEを送信するわけにはいかないでしょうか? わざわざ 条件判断して送信しているので、何らかの理由があって SSH2_MSG_DISCONNECTを送信しているのであれば、 メッセージを無視しようと思いますので、修正不要です。