hrr_rb_ssh 0.3.0.pre1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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