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
@@ -9,10 +9,8 @@ module HrrRbSsh
9
9
  class Method
10
10
  class Publickey
11
11
  class Algorithm
12
- module SignatureBlob
13
- class << self
14
- include Codable
15
- end
12
+ class SignatureBlob
13
+ include Codable
16
14
  DEFINITION = [
17
15
  [DataType::String, :'session identifier'],
18
16
  [DataType::Byte, :'message number'],
@@ -1,16 +1,21 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/logger'
4
+ require 'hrr_rb_ssh/loggable'
5
5
 
6
6
  module HrrRbSsh
7
7
  class Authentication
8
8
  class Method
9
9
  class Publickey
10
10
  class Context
11
+ include Loggable
12
+
11
13
  attr_reader \
12
14
  :username,
13
15
  :session_id,
16
+ :variables,
17
+ :vars,
18
+ :authentication_methods,
14
19
  :message_number,
15
20
  :service_name,
16
21
  :method_name,
@@ -19,11 +24,15 @@ module HrrRbSsh
19
24
  :public_key_blob,
20
25
  :signature
21
26
 
22
- def initialize username, algorithm, session_id, message
27
+ def initialize username, algorithm, session_id, message, variables, authentication_methods, logger: nil
28
+ self.logger = logger
23
29
  @username = username
24
30
  @algorithm = algorithm
25
31
  @session_id = session_id
26
32
  @message = message
33
+ @variables = variables
34
+ @vars = variables
35
+ @authentication_methods = authentication_methods
27
36
 
28
37
  @message_number = message[:'message number']
29
38
  @service_name = message[:'service name']
@@ -0,0 +1,234 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'socket'
5
+ require 'stringio'
6
+ require 'hrr_rb_ssh/loggable'
7
+ require 'hrr_rb_ssh/mode'
8
+ require 'hrr_rb_ssh/transport'
9
+ require 'hrr_rb_ssh/authentication'
10
+ require 'hrr_rb_ssh/connection'
11
+
12
+ module HrrRbSsh
13
+ class Client
14
+ include Loggable
15
+
16
+ def self.start target, options={}
17
+ client = self.new target, options
18
+ client.start
19
+ if block_given?
20
+ begin
21
+ yield client
22
+ ensure
23
+ client.close unless client.closed?
24
+ end
25
+ else
26
+ client
27
+ end
28
+ end
29
+
30
+ def initialize target, tmp_options={}
31
+ @closed = true
32
+ options = initialize_options tmp_options
33
+ io = case target
34
+ when IO
35
+ target
36
+ when Array
37
+ io = TCPSocket.new *target
38
+ when String
39
+ port = 22
40
+ io = TCPSocket.new target, port
41
+ end
42
+ transport = HrrRbSsh::Transport.new io, HrrRbSsh::Mode::CLIENT, options, logger: logger
43
+ authentication = HrrRbSsh::Authentication.new transport, HrrRbSsh::Mode::CLIENT, options, logger: logger
44
+ @connection = HrrRbSsh::Connection.new authentication, HrrRbSsh::Mode::CLIENT, options, logger: logger
45
+ end
46
+
47
+ def initialize_options tmp_options
48
+ tmp_options = Hash[tmp_options.map{|k, v| [k.to_s, v]}]
49
+ self.logger = tmp_options['logger']
50
+ options = {}
51
+ options['username'] = tmp_options['username']
52
+ options['authentication_preferred_authentication_methods'] = tmp_options['authentication_preferred_authentication_methods']
53
+ options['client_authentication_password'] = tmp_options['password']
54
+ options['client_authentication_publickey'] = tmp_options['publickey']
55
+ options['client_authentication_keyboard_interactive'] = tmp_options['keyboard_interactive']
56
+ options['transport_preferred_encryption_algorithms'] = tmp_options['transport_preferred_encryption_algorithms']
57
+ options['transport_preferred_server_host_key_algorithms'] = tmp_options['transport_preferred_server_host_key_algorithms']
58
+ options['transport_preferred_kex_algorithms'] = tmp_options['transport_preferred_kex_algorithms']
59
+ options['transport_preferred_mac_algorithms'] = tmp_options['transport_preferred_mac_algorithms']
60
+ options['transport_preferred_compression_algorithms'] = tmp_options['transport_preferred_compression_algorithms']
61
+ options
62
+ end
63
+
64
+ def start
65
+ @connection.start foreground: false
66
+ @closed = false
67
+ end
68
+
69
+ def loop
70
+ @connection.loop
71
+ end
72
+
73
+ def closed?
74
+ @closed
75
+ end
76
+
77
+ def close
78
+ log_info { "closing client" }
79
+ @closed = true
80
+ @connection.close
81
+ log_info { "client closed" }
82
+ end
83
+
84
+ def exec! command, pty: false, env: {}
85
+ log_info { "start exec!: #{command}" }
86
+ out_buf = StringIO.new
87
+ err_buf = StringIO.new
88
+ begin
89
+ log_info { "Opning channel" }
90
+ channel = @connection.request_channel_open "session"
91
+ log_info { "Channel opened" }
92
+ if pty
93
+ channel.send_channel_request_pty_req 'xterm', 80, 24, 580, 336, ''
94
+ end
95
+ env.each{ |variable_name, variable_value|
96
+ channel.send_channel_request_env variable_name, variable_value
97
+ }
98
+ channel.send_channel_request_exec command
99
+ out_t = Thread.new {
100
+ while true
101
+ begin
102
+ out_buf.write channel.io[1].readpartial(10240)
103
+ rescue
104
+ break
105
+ end
106
+ end
107
+ }
108
+ err_t = Thread.new {
109
+ while true
110
+ begin
111
+ err_buf.write channel.io[2].readpartial(10240)
112
+ rescue
113
+ break
114
+ end
115
+ end
116
+ }
117
+ [out_t, err_t].each{ |t|
118
+ begin
119
+ t.join
120
+ rescue => e
121
+ log_warn { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
122
+ end
123
+ }
124
+ rescue => e
125
+ log_error { "Failed opening channel" }
126
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
127
+ raise "Error in exec!"
128
+ ensure
129
+ if channel
130
+ log_info { "closing channel IOs" }
131
+ channel.io.each{ |io| io.close rescue nil }
132
+ log_info { "channel IOs closed" }
133
+ log_info { "closing channel" }
134
+ log_info { "wait until threads closed in channel" }
135
+ channel.wait_until_closed
136
+ channel.close
137
+ log_info { "channel closed" }
138
+ end
139
+ end
140
+ [out_buf.string, err_buf.string]
141
+ end
142
+
143
+ def exec command, pty: false, env: {}
144
+ log_info { "start exec: #{command}" }
145
+ begin
146
+ log_info { "Opning channel" }
147
+ channel = @connection.request_channel_open "session"
148
+ log_info { "Channel opened" }
149
+ if pty
150
+ channel.send_channel_request_pty_req 'xterm', 80, 24, 580, 336, ''
151
+ end
152
+ env.each{ |variable_name, variable_value|
153
+ channel.send_channel_request_env variable_name, variable_value
154
+ }
155
+ channel.send_channel_request_exec command
156
+ yield channel.io
157
+ rescue => e
158
+ log_error { "Failed opening channel" }
159
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
160
+ raise "Error in shell"
161
+ ensure
162
+ if channel
163
+ log_info { "closing channel IOs" }
164
+ channel.io.each{ |io| io.close rescue nil }
165
+ log_info { "channel IOs closed" }
166
+ log_info { "closing channel" }
167
+ log_info { "wait until threads closed in channel" }
168
+ channel.wait_until_closed
169
+ channel.close
170
+ log_info { "channel closed" }
171
+ end
172
+ end
173
+ channel_exit_status = channel.exit_status rescue nil
174
+ end
175
+
176
+ def shell env: {}
177
+ log_info { "start shell" }
178
+ begin
179
+ log_info { "Opning channel" }
180
+ channel = @connection.request_channel_open "session"
181
+ log_info { "Channel opened" }
182
+ channel.send_channel_request_pty_req 'xterm', 80, 24, 580, 336, ''
183
+ env.each{ |variable_name, variable_value|
184
+ channel.send_channel_request_env variable_name, variable_value
185
+ }
186
+ channel.send_channel_request_shell
187
+ yield channel.io
188
+ rescue => e
189
+ log_error { "Failed opening channel" }
190
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
191
+ raise "Error in shell"
192
+ ensure
193
+ if channel
194
+ log_info { "closing channel IOs" }
195
+ channel.io.each{ |io| io.close rescue nil }
196
+ log_info { "channel IOs closed" }
197
+ log_info { "closing channel" }
198
+ log_info { "wait until threads closed in channel" }
199
+ channel.wait_until_closed
200
+ channel.close
201
+ log_info { "channel closed" }
202
+ end
203
+ end
204
+ channel_exit_status = channel.exit_status rescue nil
205
+ end
206
+
207
+ def subsystem name
208
+ log_info { "start subsystem" }
209
+ begin
210
+ log_info { "Opning channel" }
211
+ channel = @connection.request_channel_open "session"
212
+ log_info { "Channel opened" }
213
+ channel.send_channel_request_subsystem name
214
+ yield channel.io
215
+ rescue => e
216
+ log_error { "Failed opening channel" }
217
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
218
+ raise "Error in subsystem"
219
+ ensure
220
+ if channel
221
+ log_info { "closing channel IOs" }
222
+ channel.io.each{ |io| io.close rescue nil }
223
+ log_info { "channel IOs closed" }
224
+ log_info { "closing channel" }
225
+ log_info { "wait until threads closed in channel" }
226
+ channel.wait_until_closed
227
+ channel.close
228
+ log_info { "channel closed" }
229
+ end
230
+ end
231
+ channel_exit_status = channel.exit_status rescue nil
232
+ end
233
+ end
234
+ end
@@ -1,36 +1,38 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/logger'
4
+ require 'hrr_rb_ssh/loggable'
5
5
 
6
6
  module HrrRbSsh
7
7
  module Codable
8
- def logger
9
- @logger ||= Logger.new self.name
8
+ include Loggable
9
+
10
+ def initialize logger: nil
11
+ self.logger = logger
10
12
  end
11
13
 
12
14
  def common_definition
13
- self::DEFINITION
15
+ self.class::DEFINITION
14
16
  end
15
17
 
16
18
  def conditional_definition message
17
- return [] unless self.const_defined? :CONDITIONAL_DEFINITION
19
+ return [] unless self.class.const_defined? :CONDITIONAL_DEFINITION
18
20
  message.inject([]){ |a, (k,v)|
19
21
  field_name = k
20
22
  field_value = if v.instance_of? ::Proc then v.call else v end
21
- a + (self::CONDITIONAL_DEFINITION.fetch(field_name, {})[field_value] || [])
23
+ a + (self.class::CONDITIONAL_DEFINITION.fetch(field_name, {})[field_value] || [])
22
24
  }
23
25
  end
24
26
 
25
27
  def encode message, complementary_message={}
26
- logger.debug { 'encoding message: ' + message.inspect }
28
+ log_debug { 'encoding message: ' + message.inspect }
27
29
  definition = common_definition + conditional_definition(message.merge complementary_message)
28
30
  definition.map{ |data_type, field_name|
29
31
  begin
30
32
  field_value = if message[field_name].instance_of? ::Proc then message[field_name].call else message[field_name] end
31
- data_type.encode( field_value )
33
+ data_type.encode field_value
32
34
  rescue => e
33
- logger.debug { "'field_name', 'field_value': #{field_name.inspect}, #{field_value.inspect}" }
35
+ log_debug { "'field_name', 'field_value': #{field_name.inspect}, #{field_value.inspect}" }
34
36
  raise e
35
37
  end
36
38
  }.join
@@ -49,7 +51,7 @@ module HrrRbSsh
49
51
  decoded_message = definition.map{ |data_type, field_name|
50
52
  [
51
53
  field_name,
52
- data_type.decode( payload_io )
54
+ data_type.decode(payload_io)
53
55
  ]
54
56
  }
55
57
  decoded_message + decode_recursively(payload_io, decoded_message)
@@ -58,11 +60,11 @@ module HrrRbSsh
58
60
 
59
61
  def decode payload, complementary_message={}
60
62
  payload_io = StringIO.new payload
61
- decoded_message = decode_recursively(payload_io).to_h
63
+ decoded_message = decode_recursively(payload_io).inject(Hash.new){ |h, (k, v)| h.update({k => v}) }
62
64
  if complementary_message.any?
63
- decoded_message.merge! decode_recursively(payload_io, complementary_message.to_a).to_h
65
+ decoded_message.merge! decode_recursively(payload_io, complementary_message.to_a).inject(Hash.new){ |h, (k, v)| h.update({k => v}) }
64
66
  end
65
- logger.debug { 'decoded message: ' + decoded_message.inspect }
67
+ log_debug { 'decoded message: ' + decoded_message.inspect }
66
68
  decoded_message
67
69
  end
68
70
  end
@@ -1,6 +1,5 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/compat/ruby/array'
5
4
  require 'hrr_rb_ssh/compat/ruby/openssl'
6
5
  require 'hrr_rb_ssh/compat/ruby/queue'
@@ -1,26 +1,30 @@
1
1
  # coding: utf-8
2
2
  # vim: et ts=2 sw=2
3
3
 
4
- require 'hrr_rb_ssh/logger'
4
+ require 'hrr_rb_ssh/loggable'
5
5
  require 'hrr_rb_ssh/error/closed_connection'
6
6
  require 'hrr_rb_ssh/connection/global_request_handler'
7
7
  require 'hrr_rb_ssh/connection/channel'
8
8
 
9
9
  module HrrRbSsh
10
10
  class Connection
11
+ include Loggable
12
+
11
13
  attr_reader \
12
14
  :username,
13
- :options
14
-
15
- def initialize authentication, options={}
16
- @logger = Logger.new self.class.name
15
+ :variables,
16
+ :options,
17
+ :mode
17
18
 
19
+ def initialize authentication, mode, options={}, logger: nil
20
+ self.logger = logger
18
21
  @authentication = authentication
22
+ @mode = mode
19
23
  @options = options
20
-
21
- @global_request_handler = GlobalRequestHandler.new self
24
+ @global_request_handler = GlobalRequestHandler.new self, logger: logger
22
25
  @channels = Hash.new
23
26
  @username = nil
27
+ @variables = nil
24
28
  @closed = nil
25
29
  end
26
30
 
@@ -36,75 +40,103 @@ module HrrRbSsh
36
40
  def assign_channel
37
41
  i = 0
38
42
  res = nil
39
- loop do
43
+ while true
40
44
  break unless @channels.keys.include?(i)
41
45
  i += 1
42
46
  end
43
47
  i
44
48
  end
45
49
 
46
- def start
47
- @authentication.start
50
+ def start foreground: true
51
+ log_info { "start connection" }
52
+ begin
53
+ @authentication.start
54
+ rescue Error::ClosedAuthentication
55
+ close
56
+ raise Error::ClosedConnection
57
+ end
48
58
  @closed = false
49
- connection_loop
59
+ @connection_loop_thread = connection_loop_thread
60
+ if foreground
61
+ @connection_loop_thread.join
62
+ end
63
+ end
64
+
65
+ def loop
66
+ @connection_loop_thread.join
50
67
  end
51
68
 
52
69
  def close
70
+ return if @closed
71
+ log_info { "close connection" }
53
72
  @closed = true
73
+ @authentication.close
54
74
  @channels.values.each do |channel|
55
75
  begin
56
76
  channel.close
57
77
  rescue => e
58
- @logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
78
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
59
79
  end
60
80
  end
61
81
  @channels.clear
62
82
  @global_request_handler.close
83
+ @connection_loop_thread.join if @connection_loop_thread && @connection_loop_thread != Thread.current
84
+ log_info { "connection closed" }
63
85
  end
64
86
 
65
87
  def closed?
66
88
  @closed
67
89
  end
68
90
 
69
- def connection_loop
70
- @logger.info { "start connection" }
71
- loop do
91
+ def connection_loop_thread
92
+ log_info { "start connection loop" }
93
+ Thread.new do
72
94
  begin
73
- payload = @authentication.receive
74
- rescue Error::ClosedAuthentication => e
75
- @logger.info { "closing connection loop" }
76
- break
77
- end
78
- @username ||= @authentication.username
79
- case payload[0,1].unpack("C")[0]
80
- when Message::SSH_MSG_GLOBAL_REQUEST::VALUE
81
- global_request payload
82
- when Message::SSH_MSG_CHANNEL_OPEN::VALUE
83
- channel_open payload
84
- when Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE
85
- channel_open_confirmation payload
86
- when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
87
- channel_request payload
88
- when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
89
- channel_window_adjust payload
90
- when Message::SSH_MSG_CHANNEL_DATA::VALUE
91
- channel_data payload
92
- when Message::SSH_MSG_CHANNEL_EOF::VALUE
93
- channel_eof payload
94
- when Message::SSH_MSG_CHANNEL_CLOSE::VALUE
95
- channel_close payload
96
- else
97
- @logger.warn { "received unsupported message: id: #{payload[0,1].unpack("C")[0]}" }
95
+ while true
96
+ begin
97
+ payload = @authentication.receive
98
+ rescue Error::ClosedAuthentication => e
99
+ log_info { "authentication closed" }
100
+ break
101
+ end
102
+ @username ||= @authentication.username
103
+ @variables ||= @authentication.variables
104
+ case payload[0,1].unpack("C")[0]
105
+ when Message::SSH_MSG_GLOBAL_REQUEST::VALUE
106
+ global_request payload
107
+ when Message::SSH_MSG_CHANNEL_OPEN::VALUE
108
+ channel_open payload
109
+ when Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE
110
+ channel_open_confirmation payload
111
+ when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
112
+ channel_request payload
113
+ when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
114
+ channel_window_adjust payload
115
+ when Message::SSH_MSG_CHANNEL_DATA::VALUE
116
+ channel_data payload
117
+ when Message::SSH_MSG_CHANNEL_EXTENDED_DATA::VALUE
118
+ channel_extended_data payload
119
+ when Message::SSH_MSG_CHANNEL_EOF::VALUE
120
+ channel_eof payload
121
+ when Message::SSH_MSG_CHANNEL_CLOSE::VALUE
122
+ channel_close payload
123
+ else
124
+ log_warn { "received unsupported message: id: #{payload[0,1].unpack("C")[0]}" }
125
+ end
126
+ end
127
+ rescue => e
128
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
129
+ ensure
130
+ log_info { "closing connection loop" }
131
+ close
132
+ log_info { "connection loop closed" }
98
133
  end
99
134
  end
100
- @logger.info { "closing connection" }
101
- close
102
- @logger.info { "connection closed" }
103
135
  end
104
136
 
105
137
  def global_request payload
106
- @logger.info { 'received ' + Message::SSH_MSG_GLOBAL_REQUEST::ID }
107
- message = Message::SSH_MSG_GLOBAL_REQUEST.decode payload
138
+ log_info { 'received ' + Message::SSH_MSG_GLOBAL_REQUEST::ID }
139
+ message = Message::SSH_MSG_GLOBAL_REQUEST.new(logger: logger).decode payload
108
140
  begin
109
141
  @global_request_handler.request message
110
142
  rescue
@@ -119,10 +151,10 @@ module HrrRbSsh
119
151
  end
120
152
 
121
153
  def channel_open_start address, port, socket
122
- @logger.info { 'channel open start' }
123
- channel = Channel.new self, {:'channel type' => "forwarded-tcpip"}, socket
154
+ log_info { 'channel open start' }
155
+ channel = Channel.new self, {:'channel type' => "forwarded-tcpip"}, socket, logger: logger
124
156
  @channels[channel.local_channel] = channel
125
- @logger.info { 'channel opened' }
157
+ log_info { 'channel opened' }
126
158
  message = {
127
159
  :'message number' => Message::SSH_MSG_CHANNEL_OPEN::VALUE,
128
160
  :'channel type' => "forwarded-tcpip",
@@ -138,72 +170,110 @@ module HrrRbSsh
138
170
  end
139
171
 
140
172
  def channel_open payload
141
- @logger.info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN::ID }
142
- message = Message::SSH_MSG_CHANNEL_OPEN.decode payload
173
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN::ID }
174
+ message = Message::SSH_MSG_CHANNEL_OPEN.new(logger: logger).decode payload
143
175
  begin
144
- channel = Channel.new self, message
176
+ channel = Channel.new self, message, logger: logger
145
177
  @channels[channel.local_channel] = channel
146
178
  channel.start
147
179
  send_channel_open_confirmation channel
148
180
  rescue => e
181
+ log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
149
182
  recipient_channel = message[:'sender channel']
150
183
  send_channel_open_failure recipient_channel, Message::SSH_MSG_CHANNEL_OPEN_FAILURE::ReasonCode::SSH_OPEN_CONNECT_FAILED, e.message
151
184
  end
152
185
  end
153
186
 
187
+ def request_channel_open channel_type, channel_specific_message={}, wait_response=true
188
+ log_info { 'request channel open' }
189
+ case channel_type
190
+ when "session"
191
+ channel = Channel.new self, {:'channel type' => channel_type}, logger: logger
192
+ @channels[channel.local_channel] = channel
193
+ end
194
+ message = {
195
+ :'message number' => Message::SSH_MSG_CHANNEL_OPEN::VALUE,
196
+ :'channel type' => channel_type,
197
+ :'sender channel' => channel.local_channel,
198
+ :'initial window size' => channel.local_window_size,
199
+ :'maximum packet size' => channel.local_maximum_packet_size,
200
+ }
201
+ send_channel_open message.merge(channel_specific_message)
202
+ log_info { 'sent channel open' }
203
+ if wait_response
204
+ log_info { 'wait response' }
205
+ channel.wait_until_started
206
+ end
207
+ unless channel.closed?
208
+ log_info { 'channel opened' }
209
+ channel
210
+ else
211
+ raise "Faild opening channel"
212
+ end
213
+ end
214
+
154
215
  def channel_open_confirmation payload
155
- @logger.info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::ID }
156
- message = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.decode payload
216
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::ID }
217
+ message = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.new(logger: logger).decode payload
157
218
  channel = @channels[message[:'recipient channel']]
158
219
  channel.set_remote_parameters message
159
220
  channel.start
160
221
  end
161
222
 
162
223
  def channel_request payload
163
- @logger.info { 'received ' + Message::SSH_MSG_CHANNEL_REQUEST::ID }
164
- message = Message::SSH_MSG_CHANNEL_REQUEST.decode payload
224
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_REQUEST::ID }
225
+ message = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).decode payload
165
226
  local_channel = message[:'recipient channel']
166
- @channels[local_channel].receive_message_queue.enq message
227
+ @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
167
228
  end
168
229
 
169
230
  def channel_window_adjust payload
170
- @logger.info { 'received ' + Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::ID }
171
- message = Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.decode payload
231
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::ID }
232
+ message = Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.new(logger: logger).decode payload
172
233
  local_channel = message[:'recipient channel']
173
- @channels[local_channel].receive_message_queue.enq message
234
+ @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
174
235
  end
175
236
 
176
237
  def channel_data payload
177
- @logger.info { 'received ' + Message::SSH_MSG_CHANNEL_DATA::ID }
178
- message = Message::SSH_MSG_CHANNEL_DATA.decode payload
238
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_DATA::ID }
239
+ message = Message::SSH_MSG_CHANNEL_DATA.new(logger: logger).decode payload
179
240
  local_channel = message[:'recipient channel']
180
- @channels[local_channel].receive_message_queue.enq message
241
+ @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
242
+ end
243
+
244
+ def channel_extended_data payload
245
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_EXTENDED_DATA::ID }
246
+ message = Message::SSH_MSG_CHANNEL_EXTENDED_DATA.new(logger: logger).decode payload
247
+ local_channel = message[:'recipient channel']
248
+ @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
181
249
  end
182
250
 
183
251
  def channel_eof payload
184
- @logger.info { 'received ' + Message::SSH_MSG_CHANNEL_EOF::ID }
185
- message = Message::SSH_MSG_CHANNEL_EOF.decode payload
252
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_EOF::ID }
253
+ message = Message::SSH_MSG_CHANNEL_EOF.new(logger: logger).decode payload
186
254
  local_channel = message[:'recipient channel']
187
- channel = @channels[local_channel]
188
- channel.receive_message_queue.close
255
+ @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
189
256
  end
190
257
 
191
258
  def channel_close payload
192
- @logger.info { 'received ' + Message::SSH_MSG_CHANNEL_CLOSE::ID }
193
- message = Message::SSH_MSG_CHANNEL_CLOSE.decode payload
259
+ log_info { 'received ' + Message::SSH_MSG_CHANNEL_CLOSE::ID }
260
+ message = Message::SSH_MSG_CHANNEL_CLOSE.new(logger: logger).decode payload
194
261
  local_channel = message[:'recipient channel']
195
262
  channel = @channels[local_channel]
196
263
  channel.close
197
- @logger.info { "deleting channel" }
264
+ log_info { "wait until threads closed in channel" }
265
+ channel.wait_until_closed
266
+ log_info { "channel closed" }
267
+ log_info { "deleting channel" }
198
268
  @channels.delete local_channel
199
- @logger.info { "channel deleted" }
269
+ log_info { "channel deleted" }
200
270
  end
201
271
 
202
272
  def send_request_success
203
273
  message = {
204
274
  :'message number' => Message::SSH_MSG_REQUEST_SUCCESS::VALUE,
205
275
  }
206
- payload = Message::SSH_MSG_REQUEST_SUCCESS.encode message
276
+ payload = Message::SSH_MSG_REQUEST_SUCCESS.new(logger: logger).encode message
207
277
  @authentication.send payload
208
278
  end
209
279
 
@@ -211,12 +281,12 @@ module HrrRbSsh
211
281
  message = {
212
282
  :'message number' => Message::SSH_MSG_REQUEST_FAILURE::VALUE,
213
283
  }
214
- payload = Message::SSH_MSG_REQUEST_FAILURE.encode message
284
+ payload = Message::SSH_MSG_REQUEST_FAILURE.new(logger: logger).encode message
215
285
  @authentication.send payload
216
286
  end
217
287
 
218
288
  def send_channel_open message
219
- payload = Message::SSH_MSG_CHANNEL_OPEN.encode message
289
+ payload = Message::SSH_MSG_CHANNEL_OPEN.new(logger: logger).encode message
220
290
  @authentication.send payload
221
291
  end
222
292
 
@@ -229,7 +299,7 @@ module HrrRbSsh
229
299
  :'initial window size' => channel.local_window_size,
230
300
  :'maximum packet size' => channel.local_maximum_packet_size,
231
301
  }
232
- payload = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.encode message
302
+ payload = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.new(logger: logger).encode message
233
303
  @authentication.send payload
234
304
  end
235
305
 
@@ -241,7 +311,7 @@ module HrrRbSsh
241
311
  :'description' => description,
242
312
  :'language tag' => "",
243
313
  }
244
- payload = Message::SSH_MSG_CHANNEL_OPEN_FAILURE.encode message
314
+ payload = Message::SSH_MSG_CHANNEL_OPEN_FAILURE.new(logger: logger).encode message
245
315
  @authentication.send payload
246
316
  end
247
317
  end