hrr_rb_ssh 0.3.0.pre1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/.travis.yml +1 -0
  4. data/README.md +208 -46
  5. data/demo/client.rb +71 -0
  6. data/demo/echo_server.rb +8 -3
  7. data/demo/more_flexible_auth.rb +105 -0
  8. data/demo/multi_step_auth.rb +99 -0
  9. data/demo/server.rb +10 -4
  10. data/demo/subsystem_echo_server.rb +8 -3
  11. data/hrr_rb_ssh.gemspec +6 -6
  12. data/lib/hrr_rb_ssh.rb +1 -1
  13. data/lib/hrr_rb_ssh/algorithm/publickey.rb +0 -1
  14. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2.rb +12 -9
  15. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/ecdsa_signature_blob.rb +2 -4
  16. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/public_key_blob.rb +2 -4
  17. data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/signature.rb +2 -4
  18. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss.rb +10 -7
  19. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/public_key_blob.rb +2 -4
  20. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/signature.rb +2 -4
  21. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa.rb +9 -6
  22. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/public_key_blob.rb +2 -4
  23. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/signature.rb +2 -4
  24. data/lib/hrr_rb_ssh/authentication.rb +103 -22
  25. data/lib/hrr_rb_ssh/authentication/constant.rb +14 -0
  26. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive.rb +44 -7
  27. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/context.rb +16 -9
  28. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_request.rb +7 -6
  29. data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_response.rb +5 -2
  30. data/lib/hrr_rb_ssh/authentication/method/none.rb +23 -7
  31. data/lib/hrr_rb_ssh/authentication/method/none/context.rb +15 -7
  32. data/lib/hrr_rb_ssh/authentication/method/password.rb +28 -7
  33. data/lib/hrr_rb_ssh/authentication/method/password/context.rb +16 -7
  34. data/lib/hrr_rb_ssh/authentication/method/publickey.rb +63 -10
  35. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm.rb +0 -1
  36. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/functionable.rb +32 -8
  37. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/signature_blob.rb +2 -4
  38. data/lib/hrr_rb_ssh/authentication/method/publickey/context.rb +11 -2
  39. data/lib/hrr_rb_ssh/client.rb +234 -0
  40. data/lib/hrr_rb_ssh/codable.rb +15 -13
  41. data/lib/hrr_rb_ssh/compat/ruby.rb +0 -1
  42. data/lib/hrr_rb_ssh/connection.rb +145 -75
  43. data/lib/hrr_rb_ssh/connection/channel.rb +342 -109
  44. data/lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb +24 -19
  45. data/lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb +24 -19
  46. data/lib/hrr_rb_ssh/connection/channel/channel_type/session.rb +19 -12
  47. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain.rb +0 -2
  48. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain/chain_context.rb +0 -3
  49. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env.rb +2 -5
  50. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env/context.rb +5 -4
  51. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec.rb +2 -5
  52. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec/context.rb +5 -4
  53. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req.rb +2 -5
  54. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req/context.rb +5 -4
  55. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell.rb +2 -5
  56. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell/context.rb +5 -4
  57. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem.rb +2 -5
  58. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem/context.rb +5 -4
  59. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change.rb +2 -5
  60. data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change/context.rb +5 -4
  61. data/lib/hrr_rb_ssh/connection/global_request_handler.rb +14 -12
  62. data/lib/hrr_rb_ssh/connection/request_handler.rb +1 -3
  63. data/lib/hrr_rb_ssh/connection/request_handler/reference_env_request_handler.rb +0 -2
  64. data/lib/hrr_rb_ssh/connection/request_handler/reference_exec_request_handler.rb +4 -6
  65. data/lib/hrr_rb_ssh/connection/request_handler/reference_pty_req_request_handler.rb +10 -12
  66. data/lib/hrr_rb_ssh/connection/request_handler/reference_shell_request_handler.rb +4 -6
  67. data/lib/hrr_rb_ssh/connection/request_handler/reference_window_change_request_handler.rb +0 -2
  68. data/lib/hrr_rb_ssh/error/closed_authentication.rb +1 -1
  69. data/lib/hrr_rb_ssh/error/closed_connection.rb +1 -1
  70. data/lib/hrr_rb_ssh/error/closed_transport.rb +1 -1
  71. data/lib/hrr_rb_ssh/loggable.rb +42 -0
  72. data/lib/hrr_rb_ssh/message/001_ssh_msg_disconnect.rb +2 -4
  73. data/lib/hrr_rb_ssh/message/002_ssh_msg_ignore.rb +2 -4
  74. data/lib/hrr_rb_ssh/message/003_ssh_msg_unimplemented.rb +2 -4
  75. data/lib/hrr_rb_ssh/message/004_ssh_msg_debug.rb +2 -4
  76. data/lib/hrr_rb_ssh/message/005_ssh_msg_service_request.rb +2 -4
  77. data/lib/hrr_rb_ssh/message/006_ssh_msg_service_accept.rb +2 -4
  78. data/lib/hrr_rb_ssh/message/020_ssh_msg_kexinit.rb +2 -4
  79. data/lib/hrr_rb_ssh/message/021_ssh_msg_newkeys.rb +2 -4
  80. data/lib/hrr_rb_ssh/message/030_ssh_msg_kex_dh_gex_request_old.rb +2 -4
  81. data/lib/hrr_rb_ssh/message/030_ssh_msg_kexdh_init.rb +2 -4
  82. data/lib/hrr_rb_ssh/message/030_ssh_msg_kexecdh_init.rb +2 -4
  83. data/lib/hrr_rb_ssh/message/031_ssh_msg_kex_dh_gex_group.rb +2 -4
  84. data/lib/hrr_rb_ssh/message/031_ssh_msg_kexdh_reply.rb +2 -4
  85. data/lib/hrr_rb_ssh/message/031_ssh_msg_kexecdh_reply.rb +2 -4
  86. data/lib/hrr_rb_ssh/message/032_ssh_msg_kex_dh_gex_init.rb +2 -4
  87. data/lib/hrr_rb_ssh/message/033_ssh_msg_kex_dh_gex_reply.rb +2 -4
  88. data/lib/hrr_rb_ssh/message/034_ssh_msg_kex_dh_gex_request.rb +2 -4
  89. data/lib/hrr_rb_ssh/message/050_ssh_msg_userauth_request.rb +2 -4
  90. data/lib/hrr_rb_ssh/message/051_ssh_msg_userauth_failure.rb +2 -4
  91. data/lib/hrr_rb_ssh/message/052_ssh_msg_userauth_success.rb +2 -4
  92. data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_info_request.rb +2 -4
  93. data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_pk_ok.rb +2 -4
  94. data/lib/hrr_rb_ssh/message/061_ssh_msg_userauth_info_response.rb +2 -4
  95. data/lib/hrr_rb_ssh/message/080_ssh_msg_global_request.rb +2 -4
  96. data/lib/hrr_rb_ssh/message/081_ssh_msg_request_success.rb +2 -4
  97. data/lib/hrr_rb_ssh/message/082_ssh_msg_request_failure.rb +2 -4
  98. data/lib/hrr_rb_ssh/message/090_ssh_msg_channel_open.rb +2 -4
  99. data/lib/hrr_rb_ssh/message/091_ssh_msg_channel_open_confirmation.rb +2 -4
  100. data/lib/hrr_rb_ssh/message/092_ssh_msg_channel_open_failure.rb +2 -4
  101. data/lib/hrr_rb_ssh/message/093_ssh_msg_channel_window_adjust.rb +2 -4
  102. data/lib/hrr_rb_ssh/message/094_ssh_msg_channel_data.rb +2 -4
  103. data/lib/hrr_rb_ssh/message/095_ssh_msg_channel_extended_data.rb +2 -4
  104. data/lib/hrr_rb_ssh/message/096_ssh_msg_channel_eof.rb +2 -4
  105. data/lib/hrr_rb_ssh/message/097_ssh_msg_channel_close.rb +2 -4
  106. data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +3 -5
  107. data/lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb +2 -4
  108. data/lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb +2 -4
  109. data/lib/hrr_rb_ssh/server.rb +16 -10
  110. data/lib/hrr_rb_ssh/transport.rb +113 -77
  111. data/lib/hrr_rb_ssh/transport/compression_algorithm/functionable.rb +5 -3
  112. data/lib/hrr_rb_ssh/transport/compression_algorithm/unfunctionable.rb +5 -3
  113. data/lib/hrr_rb_ssh/transport/encryption_algorithm/functionable.rb +5 -3
  114. data/lib/hrr_rb_ssh/transport/encryption_algorithm/unfunctionable.rb +5 -3
  115. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman.rb +43 -37
  116. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman/h0.rb +2 -4
  117. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange.rb +87 -52
  118. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange/h0.rb +2 -4
  119. data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman.rb +43 -37
  120. data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman/h0.rb +2 -4
  121. data/lib/hrr_rb_ssh/transport/mac_algorithm/functionable.rb +5 -3
  122. data/lib/hrr_rb_ssh/transport/mac_algorithm/unfunctionable.rb +5 -3
  123. data/lib/hrr_rb_ssh/transport/receiver.rb +8 -7
  124. data/lib/hrr_rb_ssh/transport/sender.rb +5 -3
  125. data/lib/hrr_rb_ssh/transport/sequence_number.rb +0 -4
  126. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm.rb +0 -1
  127. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/functionable.rb +5 -3
  128. data/lib/hrr_rb_ssh/version.rb +1 -1
  129. metadata +18 -51
  130. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519.rb +0 -61
  131. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key.rb +0 -29
  132. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key_content.rb +0 -26
  133. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/pkey.rb +0 -158
  134. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/public_key_blob.rb +0 -23
  135. data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/signature.rb +0 -23
  136. data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/ssh_ed25519.rb +0 -21
  137. data/lib/hrr_rb_ssh/compat/ruby/array.rb +0 -14
  138. data/lib/hrr_rb_ssh/logger.rb +0 -56
  139. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/ssh_ed25519.rb +0 -20
@@ -2,12 +2,16 @@
2
2
  # vim: et ts=2 sw=2
3
3
 
4
4
  require 'socket'
5
- require 'hrr_rb_ssh/logger'
5
+ require 'thread'
6
+ require 'monitor'
7
+ require 'hrr_rb_ssh/loggable'
6
8
  require 'hrr_rb_ssh/connection/channel/channel_type'
7
9
 
8
10
  module HrrRbSsh
9
11
  class Connection
10
12
  class Channel
13
+ include Loggable
14
+
11
15
  INITIAL_WINDOW_SIZE = 100000
12
16
  MAXIMUM_PACKET_SIZE = 100000
13
17
 
@@ -19,10 +23,11 @@ module HrrRbSsh
19
23
  :local_maximum_packet_size,
20
24
  :remote_window_size,
21
25
  :remote_maximum_packet_size,
22
- :receive_message_queue
26
+ :receive_message_queue,
27
+ :exit_status
23
28
 
24
- def initialize connection, message, socket=nil
25
- @logger = Logger.new self.class.name
29
+ def initialize connection, message, socket=nil, logger: nil
30
+ self.logger = logger
26
31
 
27
32
  @connection = connection
28
33
 
@@ -34,64 +39,85 @@ module HrrRbSsh
34
39
  @remote_window_size = message[:'initial window size']
35
40
  @remote_maximum_packet_size = message[:'maximum packet size']
36
41
 
37
- @channel_type_instance = ChannelType[@channel_type].new connection, self, message, socket
42
+ @channel_type_instance = ChannelType[@channel_type].new connection, self, message, socket, logger: logger
38
43
 
39
44
  @receive_message_queue = Queue.new
40
45
  @receive_data_queue = Queue.new
46
+ @receive_extended_data_queue = Queue.new
41
47
 
42
48
  @r_io_in, @w_io_in = IO.pipe
43
49
  @r_io_out, @w_io_out = IO.pipe
44
50
  @r_io_err, @w_io_err = IO.pipe
45
51
 
52
+ @channel_closing_monitor = Monitor.new
53
+
46
54
  @closed = nil
55
+ @exit_status = nil
47
56
  end
48
57
 
49
58
  def set_remote_parameters message
50
59
  @remote_channel = message[:'sender channel']
51
- @remote_window_size = message[:'initial window size']
60
+ @remote_window_size = message[:'initial window size']
52
61
  @remote_maximum_packet_size = message[:'maximum packet size']
53
62
  end
54
63
 
55
64
  def io
56
- [@r_io_in, @w_io_out, @w_io_err]
65
+ case @connection.mode
66
+ when Mode::SERVER
67
+ [@r_io_in, @w_io_out, @w_io_err]
68
+ when Mode::CLIENT
69
+ [@w_io_in, @r_io_out, @r_io_err]
70
+ end
57
71
  end
58
72
 
59
73
  def start
60
74
  @channel_loop_thread = channel_loop_thread
61
- @out_sender_thread = out_sender_thread
62
- @err_sender_thread = err_sender_thread
63
- @receiver_thread = receiver_thread
64
- @channel_type_instance.start
75
+ case @connection.mode
76
+ when Mode::SERVER
77
+ @out_sender_thread = out_sender_thread
78
+ @err_sender_thread = err_sender_thread
79
+ @receiver_thread = receiver_thread
80
+ @channel_type_instance.start
81
+ when Mode::CLIENT
82
+ @out_receiver_thread = out_receiver_thread
83
+ @err_receiver_thread = err_receiver_thread
84
+ @sender_thread = sender_thread
85
+ @channel_type_instance.start
86
+ end
65
87
  @closed = false
88
+ log_debug { "in start: #{@waiting_thread.inspect}" }
89
+ @waiting_thread.wakeup if @waiting_thread
90
+ end
91
+
92
+ def wait_until_started
93
+ @waiting_thread = Thread.current
94
+ log_debug { "in wait_until_started: #{@waiting_thread.inspect}" }
95
+ Thread.stop
66
96
  end
67
97
 
68
98
  def wait_until_senders_closed
69
- [@w_io_out, @w_io_err].each{ |io|
99
+ [
100
+ @out_sender_thread,
101
+ @err_sender_thread,
102
+ ].each{ |t|
70
103
  begin
71
- io.close
72
- rescue IOError # for compatibility for Ruby version < 2.3
73
- Thread.pass
104
+ t.join if t.instance_of? Thread
105
+ rescue => e
106
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
74
107
  end
75
108
  }
76
- [@out_sender_thread, @err_sender_thread].select{ |t| t.instance_of? Thread }.each(&:join)
77
109
  end
78
110
 
79
111
  def close from=:outside, exitstatus=0
80
- return if @closed
81
- @logger.info { "close channel" }
82
- @closed = true
112
+ @channel_closing_monitor.synchronize {
113
+ return if @closed
114
+ log_info { "close channel" }
115
+ @closed = true
116
+ }
83
117
  unless from == :channel_type_instance
84
118
  @channel_type_instance.close
85
119
  end
86
120
  @receive_message_queue.close
87
- @receive_data_queue.close
88
- [@r_io_in, @w_io_in, @r_io_out, @w_io_out, @r_io_err, @w_io_err].each{ |io|
89
- begin
90
- io.close
91
- rescue IOError # for compatibility for Ruby version < 2.3
92
- Thread.pass
93
- end
94
- }
95
121
  begin
96
122
  if from == :channel_type_instance
97
123
  send_channel_eof
@@ -99,16 +125,36 @@ module HrrRbSsh
99
125
  when Integer
100
126
  send_channel_request_exit_status exitstatus
101
127
  else
102
- @logger.warn { "skip sending exit-status because exitstatus is not an instance of Integer" }
128
+ log_warn { "skip sending exit-status because exitstatus is not an instance of Integer" }
103
129
  end
130
+ elsif from == :sender_thread
131
+ send_channel_eof
104
132
  end
105
133
  send_channel_close
106
134
  rescue Error::ClosedConnection => e
107
135
  Thread.pass
108
136
  rescue => e
109
- @logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
137
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
110
138
  end
111
- @logger.info { "channel closed" }
139
+ log_info { "channel closed" }
140
+ end
141
+
142
+ def wait_until_closed
143
+ [
144
+ @out_sender_thread,
145
+ @err_sender_thread,
146
+ @receiver_thread,
147
+ @out_receiver_thread,
148
+ @err_receiver_thread,
149
+ @sender_thread,
150
+ @channel_loop_thread
151
+ ].each{ |t|
152
+ begin
153
+ t.join if t.instance_of? Thread
154
+ rescue => e
155
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
156
+ end
157
+ }
112
158
  end
113
159
 
114
160
  def closed?
@@ -117,56 +163,72 @@ module HrrRbSsh
117
163
 
118
164
  def channel_loop_thread
119
165
  Thread.start do
120
- @logger.info { "start channel loop thread" }
121
- loop do
122
- begin
123
- message = @receive_message_queue.deq
124
- if message.nil? && @receive_message_queue.closed?
125
- @receive_data_queue.close
126
- @logger.info { "closing channel loop thread" }
127
- break
128
- end
129
- case message[:'message number']
130
- when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
131
- @logger.info { "received channel request: #{message[:'request type']}" }
132
- begin
133
- @channel_type_instance.request message
134
- rescue => e
135
- @logger.warn { "request failed: #{e.message}" }
136
- if message[:'want reply']
137
- send_channel_failure
166
+ log_info { "start channel loop thread" }
167
+ begin
168
+ loop do
169
+ begin
170
+ message = @receive_message_queue.deq
171
+ if message.nil? && @receive_message_queue.closed?
172
+ break
173
+ end
174
+ case message[:'message number']
175
+ when Message::SSH_MSG_CHANNEL_EOF::VALUE
176
+ @receive_data_queue.close
177
+ @receive_extended_data_queue.close
178
+ when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
179
+ log_info { "received channel request: #{message[:'request type']}" }
180
+ case @connection.mode
181
+ when Mode::SERVER
182
+ begin
183
+ @channel_type_instance.request message
184
+ rescue => e
185
+ log_warn { "request failed: #{e.message}" }
186
+ send_channel_failure if message[:'want reply']
187
+ else
188
+ send_channel_success if message[:'want reply']
189
+ end
190
+ when Mode::CLIENT
191
+ case message[:'request type']
192
+ when "exit-status"
193
+ log_info { "exit status: #{message[:'exit status']}" }
194
+ @exit_status = message[:'exit status'].to_i
195
+ end
138
196
  end
197
+ when Message::SSH_MSG_CHANNEL_DATA::VALUE
198
+ log_info { "received channel data" }
199
+ local_channel = message[:'recipient channel']
200
+ @receive_data_queue.enq message[:'data']
201
+ when Message::SSH_MSG_CHANNEL_EXTENDED_DATA::VALUE
202
+ log_info { "received channel extended data" }
203
+ local_channel = message[:'recipient channel']
204
+ @receive_extended_data_queue.enq message[:'data']
205
+ when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
206
+ log_info { "received channel window adjust" }
207
+ @remote_window_size = [@remote_window_size + message[:'bytes to add'], 0xffff_ffff].min
139
208
  else
140
- if message[:'want reply']
141
- send_channel_success
142
- end
209
+ log_warn { "received unsupported message: #{message.inspect}" }
143
210
  end
144
- when Message::SSH_MSG_CHANNEL_DATA::VALUE
145
- @logger.info { "received channel data" }
146
- local_channel = message[:'recipient channel']
147
- @receive_data_queue.enq message[:'data']
148
- when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
149
- @logger.debug { "received channel window adjust" }
150
- @remote_window_size = [@remote_window_size + message[:'bytes to add'], 0xffff_ffff].min
151
- else
152
- @logger.warn { "received unsupported message: #{message.inspect}" }
211
+ rescue => e
212
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
213
+ close from=:channel_loop_thread
214
+ break
153
215
  end
154
- rescue => e
155
- @logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
156
- close from=:channel_loop_thread
157
- break
158
216
  end
217
+ ensure
218
+ log_info { "closing channel loop thread" }
219
+ @receive_data_queue.close
220
+ @receive_extended_data_queue.close
159
221
  end
160
- @logger.info { "channel loop thread closed" }
222
+ log_info { "channel loop thread closed" }
161
223
  end
162
224
  end
163
225
 
164
226
  def out_sender_thread
165
227
  Thread.start {
166
- @logger.info { "start out sender thread" }
228
+ log_info { "start out sender thread" }
167
229
  loop do
168
230
  if @r_io_out.closed?
169
- @logger.info { "closing out sender thread" }
231
+ log_info { "closing out sender thread" }
170
232
  break
171
233
  end
172
234
  begin
@@ -175,30 +237,24 @@ module HrrRbSsh
175
237
  sending_data = data[0, sendable_size]
176
238
  send_channel_data sending_data if sendable_size > 0
177
239
  @remote_window_size -= sendable_size
178
- rescue EOFError => e
179
- begin
180
- @r_io_out.close
181
- rescue IOError # for compatibility for Ruby version < 2.3
182
- Thread.pass
183
- end
184
- rescue IOError => e
185
- @logger.warn { "channel IO is closed" }
186
- close
240
+ rescue EOFError, IOError => e
241
+ @r_io_out.close rescue nil
187
242
  rescue => e
188
- @logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
243
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
244
+ @r_io_out.close rescue nil
189
245
  close
190
246
  end
191
247
  end
192
- @logger.info { "out sender thread closed" }
248
+ log_info { "out sender thread closed" }
193
249
  }
194
250
  end
195
251
 
196
252
  def err_sender_thread
197
253
  Thread.start {
198
- @logger.info { "start err sender thread" }
254
+ log_info { "start err sender thread" }
199
255
  loop do
200
256
  if @r_io_err.closed?
201
- @logger.info { "closing err sender thread" }
257
+ log_info { "closing err sender thread" }
202
258
  break
203
259
  end
204
260
  begin
@@ -207,55 +263,140 @@ module HrrRbSsh
207
263
  sending_data = data[0, sendable_size]
208
264
  send_channel_extended_data sending_data if sendable_size > 0
209
265
  @remote_window_size -= sendable_size
210
- rescue EOFError => e
211
- begin
212
- @r_io_err.close
213
- rescue IOError # for compatibility for Ruby version < 2.3
214
- Thread.pass
215
- end
216
- rescue IOError => e
217
- @logger.warn { "channel IO is closed" }
218
- close
266
+ rescue EOFError, IOError => e
267
+ @r_io_err.close rescue nil
219
268
  rescue => e
220
- @logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
269
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
270
+ @r_io_err.close rescue nil
221
271
  close
222
272
  end
223
273
  end
224
- @logger.info { "err sender thread closed" }
274
+ log_info { "err sender thread closed" }
225
275
  }
226
276
  end
227
277
 
228
278
  def receiver_thread
229
279
  Thread.start {
230
- @logger.info { "start receiver thread" }
280
+ log_info { "start receiver thread" }
231
281
  loop do
232
282
  begin
233
283
  data = @receive_data_queue.deq
234
284
  if data.nil? && @receive_data_queue.closed?
235
- @logger.info { "closing receiver thread" }
236
- @logger.info { "closing channel IO write" }
237
- @w_io_in.close_write
238
- @logger.info { "channel IO write closed" }
285
+ log_info { "closing receiver thread" }
286
+ log_info { "closing w_io_in" }
287
+ @w_io_in.close
288
+ log_info { "w_io_in closed" }
239
289
  break
240
290
  end
241
291
  @w_io_in.write data
242
292
  @local_window_size -= data.size
243
293
  if @local_window_size < INITIAL_WINDOW_SIZE/2
244
- @logger.info { "send channel window adjust" }
294
+ log_info { "send channel window adjust" }
245
295
  send_channel_window_adjust
246
296
  @local_window_size += INITIAL_WINDOW_SIZE
247
297
  end
248
- rescue IOError => e
249
- @logger.warn { "channel IO is closed" }
298
+ rescue Errno::EPIPE, IOError => e
250
299
  close
251
300
  break
252
301
  rescue => e
253
- @logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
302
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
254
303
  close
255
304
  break
256
305
  end
257
306
  end
258
- @logger.info { "receiver thread closed" }
307
+ log_info { "receiver thread closed" }
308
+ }
309
+ end
310
+
311
+ def out_receiver_thread
312
+ Thread.start {
313
+ log_info { "start out receiver thread" }
314
+ loop do
315
+ begin
316
+ data = @receive_data_queue.deq
317
+ if data.nil? && @receive_data_queue.closed?
318
+ log_info { "closing out receiver thread" }
319
+ log_info { "closing w_io_out" }
320
+ @w_io_out.close
321
+ log_info { "w_io_out closed" }
322
+ break
323
+ end
324
+ @w_io_out.write data
325
+ @local_window_size -= data.size
326
+ if @local_window_size < INITIAL_WINDOW_SIZE/2
327
+ log_info { "send channel window adjust" }
328
+ send_channel_window_adjust
329
+ @local_window_size += INITIAL_WINDOW_SIZE
330
+ end
331
+ rescue Errno::EPIPE, IOError => e
332
+ close
333
+ break
334
+ rescue => e
335
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
336
+ close
337
+ break
338
+ end
339
+ end
340
+ log_info { "out receiver thread closed" }
341
+ }
342
+ end
343
+
344
+ def err_receiver_thread
345
+ Thread.start {
346
+ log_info { "start err receiver thread" }
347
+ loop do
348
+ begin
349
+ data = @receive_extended_data_queue.deq
350
+ if data.nil? && @receive_extended_data_queue.closed?
351
+ log_info { "closing err receiver thread" }
352
+ log_info { "closing w_io_err" }
353
+ @w_io_err.close
354
+ log_info { "w_io_err closed" }
355
+ break
356
+ end
357
+ @w_io_err.write data
358
+ @local_window_size -= data.size
359
+ if @local_window_size < INITIAL_WINDOW_SIZE/2
360
+ log_info { "send channel window adjust" }
361
+ send_channel_window_adjust
362
+ @local_window_size += INITIAL_WINDOW_SIZE
363
+ end
364
+ rescue Error::EPIPE, IOError => e
365
+ close
366
+ break
367
+ rescue => e
368
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
369
+ close
370
+ break
371
+ end
372
+ end
373
+ log_info { "err receiver thread closed" }
374
+ }
375
+ end
376
+
377
+ def sender_thread
378
+ Thread.start {
379
+ log_info { "start sender thread" }
380
+ loop do
381
+ if @r_io_in.closed?
382
+ log_info { "closing sender thread" }
383
+ break
384
+ end
385
+ begin
386
+ data = @r_io_in.readpartial(10240)
387
+ sendable_size = [data.size, @remote_window_size].min
388
+ sending_data = data[0, sendable_size]
389
+ send_channel_data sending_data if sendable_size > 0
390
+ @remote_window_size -= sendable_size
391
+ rescue EOFError, IOError => e
392
+ @r_io_in.close rescue nil
393
+ rescue => e
394
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
395
+ @r_io_in.close rescue nil
396
+ end
397
+ end
398
+ close from=:sender_thread
399
+ log_info { "sender thread closed" }
259
400
  }
260
401
  end
261
402
 
@@ -264,7 +405,7 @@ module HrrRbSsh
264
405
  :'message number' => Message::SSH_MSG_CHANNEL_SUCCESS::VALUE,
265
406
  :'recipient channel' => @remote_channel,
266
407
  }
267
- payload = Message::SSH_MSG_CHANNEL_SUCCESS.encode message
408
+ payload = Message::SSH_MSG_CHANNEL_SUCCESS.new(logger: logger).encode message
268
409
  @connection.send payload
269
410
  end
270
411
 
@@ -273,7 +414,7 @@ module HrrRbSsh
273
414
  :'message number' => Message::SSH_MSG_CHANNEL_FAILURE::VALUE,
274
415
  :'recipient channel' => @remote_channel,
275
416
  }
276
- payload = Message::SSH_MSG_CHANNEL_FAILURE.encode message
417
+ payload = Message::SSH_MSG_CHANNEL_FAILURE.new(logger: logger).encode message
277
418
  @connection.send payload
278
419
  end
279
420
 
@@ -283,7 +424,7 @@ module HrrRbSsh
283
424
  :'recipient channel' => @remote_channel,
284
425
  :'bytes to add' => INITIAL_WINDOW_SIZE,
285
426
  }
286
- payload = Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.encode message
427
+ payload = Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.new(logger: logger).encode message
287
428
  @connection.send payload
288
429
  end
289
430
 
@@ -293,7 +434,7 @@ module HrrRbSsh
293
434
  :'recipient channel' => @remote_channel,
294
435
  :'data' => data,
295
436
  }
296
- payload = Message::SSH_MSG_CHANNEL_DATA.encode message
437
+ payload = Message::SSH_MSG_CHANNEL_DATA.new(logger: logger).encode message
297
438
  @connection.send payload
298
439
  end
299
440
 
@@ -304,7 +445,99 @@ module HrrRbSsh
304
445
  :'data type code' => code,
305
446
  :'data' => data,
306
447
  }
307
- payload = Message::SSH_MSG_CHANNEL_EXTENDED_DATA.encode message
448
+ payload = Message::SSH_MSG_CHANNEL_EXTENDED_DATA.new(logger: logger).encode message
449
+ @connection.send payload
450
+ end
451
+
452
+ def send_channel_request_pty_req term_env_var_val, term_width_chars, term_height_rows, term_width_pixel, term_height_pixel, encoded_term_modes
453
+ message = {
454
+ :'message number' => Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
455
+ :'recipient channel' => @remote_channel,
456
+ :'request type' => "pty-req",
457
+ :'want reply' => false,
458
+ :'TERM environment variable value' => term_env_var_val,
459
+ :'terminal width, characters' => term_width_chars,
460
+ :'terminal height, rows' => term_height_rows,
461
+ :'terminal width, pixels' => term_width_pixel,
462
+ :'terminal height, pixels' => term_height_pixel,
463
+ :'encoded terminal modes' => encoded_term_modes,
464
+ }
465
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
466
+ @connection.send payload
467
+ end
468
+
469
+ def send_channel_request_env variable_name, variable_value
470
+ message = {
471
+ :'message number' => Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
472
+ :'recipient channel' => @remote_channel,
473
+ :'request type' => "env",
474
+ :'want reply' => false,
475
+ :'variable name' => variable_name,
476
+ :'variable value' => variable_value,
477
+ }
478
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
479
+ @connection.send payload
480
+ end
481
+
482
+ def send_channel_request_shell
483
+ message = {
484
+ :'message number' => Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
485
+ :'recipient channel' => @remote_channel,
486
+ :'request type' => "shell",
487
+ :'want reply' => false,
488
+ }
489
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
490
+ @connection.send payload
491
+ end
492
+
493
+ def send_channel_request_exec command
494
+ message = {
495
+ :'message number' => Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
496
+ :'recipient channel' => @remote_channel,
497
+ :'request type' => "exec",
498
+ :'want reply' => false,
499
+ :'command' => command,
500
+ }
501
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
502
+ @connection.send payload
503
+ end
504
+
505
+ def send_channel_request_subsystem subsystem_name
506
+ message = {
507
+ :'message number' => Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
508
+ :'recipient channel' => @remote_channel,
509
+ :'request type' => "subsystem",
510
+ :'want reply' => false,
511
+ :'subsystem name' => subsystem_name,
512
+ }
513
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
514
+ @connection.send payload
515
+ end
516
+
517
+ def send_channel_request_window_change term_width_cols, term_height_rows, term_width_pixel, term_height_pixel
518
+ message = {
519
+ :'message number' => Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
520
+ :'recipient channel' => @remote_channel,
521
+ :'request type' => "window-change",
522
+ :'want reply' => false,
523
+ :'terminal width, columns' => term_width_cols,
524
+ :'terminal height, rows' => term_height_rows,
525
+ :'terminal width, pixels' => term_width_pixel,
526
+ :'terminal height, pixels' => term_height_pixel,
527
+ }
528
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
529
+ @connection.send payload
530
+ end
531
+
532
+ def send_channel_request_signal signal_name
533
+ message = {
534
+ :'message number' => Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
535
+ :'recipient channel' => @remote_channel,
536
+ :'request type' => "signal",
537
+ :'want reply' => false,
538
+ :'signal name' => signal_name,
539
+ }
540
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
308
541
  @connection.send payload
309
542
  end
310
543
 
@@ -316,7 +549,7 @@ module HrrRbSsh
316
549
  :'want reply' => false,
317
550
  :'exit status' => exitstatus,
318
551
  }
319
- payload = Message::SSH_MSG_CHANNEL_REQUEST.encode message
552
+ payload = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).encode message
320
553
  @connection.send payload
321
554
  end
322
555
 
@@ -325,7 +558,7 @@ module HrrRbSsh
325
558
  :'message number' => Message::SSH_MSG_CHANNEL_EOF::VALUE,
326
559
  :'recipient channel' => @remote_channel,
327
560
  }
328
- payload = Message::SSH_MSG_CHANNEL_EOF.encode message
561
+ payload = Message::SSH_MSG_CHANNEL_EOF.new(logger: logger).encode message
329
562
  @connection.send payload
330
563
  end
331
564
 
@@ -334,7 +567,7 @@ module HrrRbSsh
334
567
  :'message number' => Message::SSH_MSG_CHANNEL_CLOSE::VALUE,
335
568
  :'recipient channel' => @remote_channel,
336
569
  }
337
- payload = Message::SSH_MSG_CHANNEL_CLOSE.encode message
570
+ payload = Message::SSH_MSG_CHANNEL_CLOSE.new(logger: logger).encode message
338
571
  @connection.send payload
339
572
  end
340
573
  end