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

scmno****@osdn***** scmno****@osdn*****
2019年 10月 2日 (水) 17:38:43 JST


Revision: 8239
          https://osdn.net/projects/ttssh2/scm/svn/commits/8239
Author:   doda
Date:     2019-10-02 17:38:43 +0900 (Wed, 02 Oct 2019)
Log Message:
-----------
ポート転送で、クライアントにデータを送り終える前に切断する問題を修正

Ticket: #39614

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

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

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

Ticket Links:
------------
    https://osdn.net/projects/ttssh2/tracker/detail/39614

Modified Paths:
--------------
    trunk/doc/en/html/about/history.html
    trunk/doc/ja/html/about/history.html
    trunk/ttssh2/ttxssh/fwd.c

Added Paths:
-----------
    trunk/tests/#39614-portforward.rb

-------------- next part --------------
Modified: trunk/doc/en/html/about/history.html
===================================================================
--- trunk/doc/en/html/about/history.html	2019-09-29 15:42:37 UTC (rev 8238)
+++ trunk/doc/en/html/about/history.html	2019-10-02 08:38:43 UTC (rev 8239)
@@ -3224,6 +3224,7 @@
       <li>SSH1: The <a href="../commandline/ttssh.html#nosecuritywarning">/nosecuritywarning</a> option does not work well.</li>
       <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>
       <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>
+      <li>Fixed a port forwarding issue that closes the client connection before completing all data transmission.</li>
     </ul>
   </li>
 </ul>

Modified: trunk/doc/ja/html/about/history.html
===================================================================
--- trunk/doc/ja/html/about/history.html	2019-09-29 15:42:37 UTC (rev 8238)
+++ trunk/doc/ja/html/about/history.html	2019-10-02 08:38:43 UTC (rev 8239)
@@ -3230,6 +3230,7 @@
       <li>SSH1: <a href="../commandline/ttssh.html#nosecuritywarning">/nosecuritywarning</a>\x83I\x83v\x83V\x83\x87\x83\x93\x82\xAA\x8B@\x94\\x82\xB5\x82Ă\xA2\x82Ȃ\xA9\x82\xC1\x82\xBD\x96\xE2\x91\xE8\x82\xF0\x8FC\x90\xB3\x82\xB5\x82\xBD\x81B</li>
       <li>\x83\x86\x81[\x83U\x94F\x8F؃_\x83C\x83A\x83\x8D\x83O\x82ŁA\x83\x86\x81[\x83U\x96\xBC\x82\xF0\x93\xFC\x97͌\xE3\x82\xCCTAB\x83L\x81[\x82Ńp\x83X\x83t\x83\x8C\x81[\x83Y\x97\x93\x82ւ̈ړ\xAE\x82ɒx\x89\x84\x82\xAA\x82\xA0\x82\xE9\x96\xE2\x91\xE8\x82\xF0\x89\xFC\x91P\x82\xB5\x82\xBD\x81B</li>
       <li>\x83\x86\x81[\x83U\x94F\x8F؃_\x83C\x83A\x83\x8D\x83O\x82ŁA\x83\x86\x81[\x83U\x96\xBC\x82\xF0\x8B󗓂ɂ\xB5\x82\xBD\x8C\xE3\x81ATAB\x83L\x81[\x82ʼnE\x91\xA4\x82̃v\x83\x8B\x83_\x83E\x83\x93\x83\x81\x83j\x83\x85\x81[\x82Ƀt\x83H\x81[\x83J\x83X\x88ړ\xAE\x82\xB5\x82Ȃ\xA2\x82\xB1\x82Ƃ\xAA\x82\xA0\x82\xE9\x96\xE2\x91\xE8\x82\xF0\x8FC\x90\xB3\x82\xB5\x82\xBD\x81B</li>
+      <li>\x83|\x81[\x83g\x93]\x91\x97\x82ŁA\x83N\x83\x89\x83C\x83A\x83\x93\x83g\x82Ƀf\x81[\x83^\x82𑗂\xE8\x8FI\x82\xED\x82\xE9\x91O\x82ɐڑ\xB1\x82\xF0\x90ؒf\x82\xB7\x82\xE9\x8Fꍇ\x82\xAA\x82\xA0\x82\xE9\x96\xE2\x91\xE8\x82\xF0\x8FC\x90\xB3\x82\xB5\x82\xBD\x81B</li>
     </ul>
   </li>
 </ul>

Added: trunk/tests/#39614-portforward.rb
===================================================================
--- trunk/tests/#39614-portforward.rb	                        (rev 0)
+++ trunk/tests/#39614-portforward.rb	2019-10-02 08:38:43 UTC (rev 8239)
@@ -0,0 +1,49 @@
+#!/usr/bin/ruby
+# coding: UTF-8
+#
+# Ticket: #39614 のテストスクリプト
+#
+# Tera Term で /ssh-L8000:ttssh2.osdn.jp:80 を指定してサーバに接続後、
+# このスクリプトを実行する。
+# 再現しない場合は sleep の時間を適宜調整する
+#
+
+require 'socket'
+
+s = TCPSocket.open("localhost", 8000)
+
+puts "Send request."
+s.write "GET /tmp/ticket-39614-test.txt HTTP/1.0\r\nHost: ttssh2.osdn.jp\r\n\r\n"
+
+puts "Wait 5 seconds."
+sleep 5
+
+puts "Skip HTTP header"
+
+while s.gets
+  break if /^\r\n$/ =~ $_
+end
+
+count = 0
+
+puts "Reading body part..."
+while data = s.read(4096)
+  count += data.size
+  if ((count / 4096) % 64) == 0
+    puts "#{count} bytes read"
+    sleep 1
+  end
+end
+
+s.close
+
+result = ""
+if count == 13229000
+  result = "data OK."
+elsif count < 13229000
+  result = "data corrupted."
+else
+  result = "unknown data."
+end
+
+puts "END: #{count} bytes read. #{result}"


Property changes on: trunk/tests/#39614-portforward.rb
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Modified: trunk/ttssh2/ttxssh/fwd.c
===================================================================
--- trunk/ttssh2/ttxssh/fwd.c	2019-09-29 15:42:37 UTC (rev 8238)
+++ trunk/ttssh2/ttxssh/fwd.c	2019-10-02 08:38:43 UTC (rev 8239)
@@ -310,14 +310,28 @@
 
 	channel = pvar->fwd_state.channels + local_channel_num;
 
-	if (channel->local_socket != INVALID_SOCKET) {
-		shutdown(channel->local_socket, 1);
-	}
 	channel->status |= FWD_CLOSED_REMOTE_IN;
-	if ((channel->status & FWD_CLOSED_REMOTE_OUT) == FWD_CLOSED_REMOTE_OUT) {
-		closed_local_connection(pvar, local_channel_num);
-		FWD_free_channel(pvar, local_channel_num);
+
+	logprintf(LOG_LEVEL_VERBOSE, "%s: SSH_MSG_CHANNEL_EOF receive. channel: %d", __FUNCTION__, local_channel_num);
+
+	if (channel->writebuf.datalen == 0) {
+		// \x83N\x83\x89\x83C\x83A\x83\x93\x83g\x82֑\x97\x82\xE9\x83f\x81[\x83^\x82\xAA\x8Ec\x82\xC1\x82Ă\xA2\x82Ȃ\xA2\x8Fꍇ\x82̓R\x83l\x83N\x83V\x83\x87\x83\x93\x82\xF0 shutdown \x82\xB7\x82\xE9
+		logprintf(LOG_LEVEL_VERBOSE,
+		          "%s: shutdown local socket. channel: %d", __FUNCTION__, local_channel_num);
+		if (channel->local_socket != INVALID_SOCKET) {
+			shutdown(channel->local_socket, 1);
+		}
+		if ((channel->status & FWD_CLOSED_REMOTE_OUT) == FWD_CLOSED_REMOTE_OUT) {
+			closed_local_connection(pvar, local_channel_num);
+			FWD_free_channel(pvar, local_channel_num);
+		}
 	}
+	else {
+		// \x83o\x83b\x83t\x83@\x82Ƀf\x81[\x83^\x82\xAA\x8Ec\x82\xC1\x82Ă\xA2\x82\xE9\x8Fꍇ\x82͂\xB1\x82\xB1\x82ł\xCD shutdown \x8Fo\x97\x88\x82Ȃ\xA2
+		// write_local_connection_buffer() \x82Ńf\x81[\x83^\x82\xAA\x82\xB7\x82ׂđ\x97\x82\xE8\x8FI\x82\xED\x82\xC1\x82\xBD\x8E\x9E\x82\xC9 shutdown \x82\xAA\x8Ds\x82\xED\x82\xEA\x82\xE9
+		logprintf(LOG_LEVEL_VERBOSE, "%s: buffer not empty. channel: %d, remained data length: %d",
+		          __FUNCTION__, local_channel_num, channel->writebuf.datalen);
+	}
 }
 
 void FWD_channel_output_eof(PTInstVar pvar, uint32 local_channel_num)
@@ -659,11 +673,30 @@
 {
 	FWDChannel *channel = pvar->fwd_state.channels + channel_num;
 
+	if (channel->writebuf.datalen == 0) {
+		logprintf(LOG_LEVEL_VERBOSE, "%s: write buffer is empty. channel: %d", __FUNCTION__, channel_num);
+		return;
+	}
+
+	logprintf(LOG_LEVEL_VERBOSE, "%s: remained data length: %d, channel: %d", __FUNCTION__,
+	          channel->writebuf.datalen, channel_num);
+
 	if ((channel->status & FWD_BOTH_CONNECTED) == FWD_BOTH_CONNECTED) {
 		if (!UTIL_sock_write_more
 			(pvar, &channel->writebuf, channel->local_socket)) {
 			channel_error(pvar, "writing", channel_num, WSAGetLastError());
 		}
+		if (channel->writebuf.datalen == 0 && (channel->status & FWD_CLOSED_REMOTE_IN) == FWD_CLOSED_REMOTE_IN) {
+			// \x83N\x83\x89\x83C\x83A\x83\x93\x83g\x82ւ̃f\x81[\x83^\x82\xAA\x82\xB7\x82ׂđ\x97\x82\xE8\x8FI\x82\xED\x82\xC1\x82Ă\xA8\x82\xE8\x81A\x83\x8A\x83\x82\x81[\x83g\x82\xA9\x82\xE7\x82\xCC EOF \x82\xF0\x8E\xF3\x90M\x8Dς݂Ȃ\xE7\x82\xCE
+			// \x83N\x83\x89\x83C\x83A\x83\x93\x83g\x82ւ̃R\x83l\x83N\x83V\x83\x87\x83\x93\x82\xF0 shutdown \x82\xB7\x82\xE9 (\x91\x97\x90M\x95\xFB\x8C\xFC\x82̂\xDD)
+			logprintf(LOG_LEVEL_VERBOSE,
+				  "%s: shutdown local socket. channel: %d", __FUNCTION__, channel_num);
+			shutdown(channel->local_socket, 1);
+			if ((channel->status & FWD_CLOSED_REMOTE_OUT) == FWD_CLOSED_REMOTE_OUT) {
+				closed_local_connection(pvar, channel_num);
+				FWD_free_channel(pvar, channel_num);
+			}
+		}
 	}
 }
 


Ttssh2-commit メーリングリストの案内