• R/O
  • SSH
  • HTTPS

ttssh2: Commit


Commit MetaInfo

Revision8239 (tree)
Time2019-10-02 17:38:43
Authordoda

Log Message

ポート転送で、クライアントにデータを送り終える前に切断する問題を修正

Ticket: #39614

問題:

ポート転送でのデータ受信時、データの途中でポート転送の接続が切断され
受信したデータが欠けてしまう事がある。

原因:

SSH_MSG_CHANNEL_EOF を受けた時に、クライアントへの未送信のデータが
残っていてもソケットを shutdown していたため。

対処:

クライアントへの未送信のデータが残っていた場合にはすぐに shutdown
せず、データを送り終えた時に shutdown するようにした。

Change Summary

Incremental Difference

--- trunk/doc/en/html/about/history.html (revision 8238)
+++ trunk/doc/en/html/about/history.html (revision 8239)
@@ -3224,6 +3224,7 @@
32243224 <li>SSH1: The <a href="../commandline/ttssh.html#nosecuritywarning">/nosecuritywarning</a> option does not work well.</li>
32253225 <li>The problem is improved in the user authentication dialog that the delay occurs when the focus is moved from the user name to passphrase by using TAB key after entering the user name.</li>
32263226 <li>When the user name is left blank in the user authentication dialog, the focus may not be moved from the user name to the pull-down menu on the right side with TAB key.</li>
3227+ <li>Fixed a port forwarding issue that closes the client connection before completing all data transmission.</li>
32273228 </ul>
32283229 </li>
32293230 </ul>
--- trunk/doc/ja/html/about/history.html (revision 8238)
+++ trunk/doc/ja/html/about/history.html (revision 8239)
@@ -3230,6 +3230,7 @@
32303230 <li>SSH1: <a href="../commandline/ttssh.html#nosecuritywarning">/nosecuritywarning</a>オプションが機能していなかった問題を修正した。</li>
32313231 <li>ユーザ認証ダイアログで、ユーザ名を入力後のTABキーでパスフレーズ欄への移動に遅延がある問題を改善した。</li>
32323232 <li>ユーザ認証ダイアログで、ユーザ名を空欄にした後、TABキーで右側のプルダウンメニューにフォーカス移動しないことがある問題を修正した。</li>
3233+ <li>ポート転送で、クライアントにデータを送り終わる前に接続を切断する場合がある問題を修正した。</li>
32333234 </ul>
32343235 </li>
32353236 </ul>
--- trunk/tests/#39614-portforward.rb (nonexistent)
+++ trunk/tests/#39614-portforward.rb (revision 8239)
@@ -0,0 +1,49 @@
1+#!/usr/bin/ruby
2+# coding: UTF-8
3+#
4+# Ticket: #39614 のテストスクリプト
5+#
6+# Tera Term で /ssh-L8000:ttssh2.osdn.jp:80 を指定してサーバに接続後、
7+# このスクリプトを実行する。
8+# 再現しない場合は sleep の時間を適宜調整する
9+#
10+
11+require 'socket'
12+
13+s = TCPSocket.open("localhost", 8000)
14+
15+puts "Send request."
16+s.write "GET /tmp/ticket-39614-test.txt HTTP/1.0\r\nHost: ttssh2.osdn.jp\r\n\r\n"
17+
18+puts "Wait 5 seconds."
19+sleep 5
20+
21+puts "Skip HTTP header"
22+
23+while s.gets
24+ break if /^\r\n$/ =~ $_
25+end
26+
27+count = 0
28+
29+puts "Reading body part..."
30+while data = s.read(4096)
31+ count += data.size
32+ if ((count / 4096) % 64) == 0
33+ puts "#{count} bytes read"
34+ sleep 1
35+ end
36+end
37+
38+s.close
39+
40+result = ""
41+if count == 13229000
42+ result = "data OK."
43+elsif count < 13229000
44+ result = "data corrupted."
45+else
46+ result = "unknown data."
47+end
48+
49+puts "END: #{count} bytes read. #{result}"
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
--- trunk/ttssh2/ttxssh/fwd.c (revision 8238)
+++ trunk/ttssh2/ttxssh/fwd.c (revision 8239)
@@ -310,14 +310,28 @@
310310
311311 channel = pvar->fwd_state.channels + local_channel_num;
312312
313- if (channel->local_socket != INVALID_SOCKET) {
314- shutdown(channel->local_socket, 1);
315- }
316313 channel->status |= FWD_CLOSED_REMOTE_IN;
317- if ((channel->status & FWD_CLOSED_REMOTE_OUT) == FWD_CLOSED_REMOTE_OUT) {
318- closed_local_connection(pvar, local_channel_num);
319- FWD_free_channel(pvar, local_channel_num);
314+
315+ logprintf(LOG_LEVEL_VERBOSE, "%s: SSH_MSG_CHANNEL_EOF receive. channel: %d", __FUNCTION__, local_channel_num);
316+
317+ if (channel->writebuf.datalen == 0) {
318+ // クライアントへ送るデータが残っていない場合はコネクションを shutdown する
319+ logprintf(LOG_LEVEL_VERBOSE,
320+ "%s: shutdown local socket. channel: %d", __FUNCTION__, local_channel_num);
321+ if (channel->local_socket != INVALID_SOCKET) {
322+ shutdown(channel->local_socket, 1);
323+ }
324+ if ((channel->status & FWD_CLOSED_REMOTE_OUT) == FWD_CLOSED_REMOTE_OUT) {
325+ closed_local_connection(pvar, local_channel_num);
326+ FWD_free_channel(pvar, local_channel_num);
327+ }
320328 }
329+ else {
330+ // バッファにデータが残っている場合はここでは shutdown 出来ない
331+ // write_local_connection_buffer() でデータがすべて送り終わった時に shutdown が行われる
332+ logprintf(LOG_LEVEL_VERBOSE, "%s: buffer not empty. channel: %d, remained data length: %d",
333+ __FUNCTION__, local_channel_num, channel->writebuf.datalen);
334+ }
321335 }
322336
323337 void FWD_channel_output_eof(PTInstVar pvar, uint32 local_channel_num)
@@ -659,11 +673,30 @@
659673 {
660674 FWDChannel *channel = pvar->fwd_state.channels + channel_num;
661675
676+ if (channel->writebuf.datalen == 0) {
677+ logprintf(LOG_LEVEL_VERBOSE, "%s: write buffer is empty. channel: %d", __FUNCTION__, channel_num);
678+ return;
679+ }
680+
681+ logprintf(LOG_LEVEL_VERBOSE, "%s: remained data length: %d, channel: %d", __FUNCTION__,
682+ channel->writebuf.datalen, channel_num);
683+
662684 if ((channel->status & FWD_BOTH_CONNECTED) == FWD_BOTH_CONNECTED) {
663685 if (!UTIL_sock_write_more
664686 (pvar, &channel->writebuf, channel->local_socket)) {
665687 channel_error(pvar, "writing", channel_num, WSAGetLastError());
666688 }
689+ if (channel->writebuf.datalen == 0 && (channel->status & FWD_CLOSED_REMOTE_IN) == FWD_CLOSED_REMOTE_IN) {
690+ // クライアントへのデータがすべて送り終わっており、リモートからの EOF を受信済みならば
691+ // クライアントへのコネクションを shutdown する (送信方向のみ)
692+ logprintf(LOG_LEVEL_VERBOSE,
693+ "%s: shutdown local socket. channel: %d", __FUNCTION__, channel_num);
694+ shutdown(channel->local_socket, 1);
695+ if ((channel->status & FWD_CLOSED_REMOTE_OUT) == FWD_CLOSED_REMOTE_OUT) {
696+ closed_local_connection(pvar, channel_num);
697+ FWD_free_channel(pvar, channel_num);
698+ }
699+ }
667700 }
668701 }
669702
Show on old repository browser