hrr_rb_ssh 0.1.0

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 (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +22 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +201 -0
  8. data/README.md +47 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/demo/server.rb +134 -0
  13. data/hrr_rb_ssh.gemspec +27 -0
  14. data/lib/hrr_rb_ssh/authentication/authenticator.rb +16 -0
  15. data/lib/hrr_rb_ssh/authentication/method/none/context.rb +28 -0
  16. data/lib/hrr_rb_ssh/authentication/method/none.rb +38 -0
  17. data/lib/hrr_rb_ssh/authentication/method/password/context.rb +29 -0
  18. data/lib/hrr_rb_ssh/authentication/method/password.rb +37 -0
  19. data/lib/hrr_rb_ssh/authentication/method.rb +21 -0
  20. data/lib/hrr_rb_ssh/authentication.rb +107 -0
  21. data/lib/hrr_rb_ssh/closed_authentication_error.rb +7 -0
  22. data/lib/hrr_rb_ssh/closed_connection_error.rb +7 -0
  23. data/lib/hrr_rb_ssh/closed_transport_error.rb +7 -0
  24. data/lib/hrr_rb_ssh/compat.rb +65 -0
  25. data/lib/hrr_rb_ssh/connection/channel/proc_chain/chain_context.rb +22 -0
  26. data/lib/hrr_rb_ssh/connection/channel/proc_chain.rb +25 -0
  27. data/lib/hrr_rb_ssh/connection/channel/session/env/context.rb +43 -0
  28. data/lib/hrr_rb_ssh/connection/channel/session/env.rb +31 -0
  29. data/lib/hrr_rb_ssh/connection/channel/session/exec/context.rb +41 -0
  30. data/lib/hrr_rb_ssh/connection/channel/session/exec.rb +31 -0
  31. data/lib/hrr_rb_ssh/connection/channel/session/pty_req/context.rb +50 -0
  32. data/lib/hrr_rb_ssh/connection/channel/session/pty_req.rb +31 -0
  33. data/lib/hrr_rb_ssh/connection/channel/session/shell/context.rb +37 -0
  34. data/lib/hrr_rb_ssh/connection/channel/session/shell.rb +31 -0
  35. data/lib/hrr_rb_ssh/connection/channel/session/subsystem/context.rb +40 -0
  36. data/lib/hrr_rb_ssh/connection/channel/session/subsystem.rb +31 -0
  37. data/lib/hrr_rb_ssh/connection/channel/session.rb +31 -0
  38. data/lib/hrr_rb_ssh/connection/channel.rb +278 -0
  39. data/lib/hrr_rb_ssh/connection/request_handler.rb +18 -0
  40. data/lib/hrr_rb_ssh/connection.rb +170 -0
  41. data/lib/hrr_rb_ssh/logger.rb +52 -0
  42. data/lib/hrr_rb_ssh/message/001_ssh_msg_disconnect.rb +44 -0
  43. data/lib/hrr_rb_ssh/message/002_ssh_msg_ignore.rb +24 -0
  44. data/lib/hrr_rb_ssh/message/003_ssh_msg_unimplemented.rb +24 -0
  45. data/lib/hrr_rb_ssh/message/004_ssh_msg_debug.rb +26 -0
  46. data/lib/hrr_rb_ssh/message/005_ssh_msg_service_request.rb +24 -0
  47. data/lib/hrr_rb_ssh/message/006_ssh_msg_service_accept.rb +24 -0
  48. data/lib/hrr_rb_ssh/message/020_ssh_msg_kexinit.rb +51 -0
  49. data/lib/hrr_rb_ssh/message/021_ssh_msg_newkeys.rb +23 -0
  50. data/lib/hrr_rb_ssh/message/030_ssh_msg_kexdh_init.rb +24 -0
  51. data/lib/hrr_rb_ssh/message/031_ssh_msg_kexdh_reply.rb +26 -0
  52. data/lib/hrr_rb_ssh/message/050_ssh_msg_userauth_request.rb +58 -0
  53. data/lib/hrr_rb_ssh/message/051_ssh_msg_userauth_failure.rb +25 -0
  54. data/lib/hrr_rb_ssh/message/052_ssh_msg_userauth_success.rb +23 -0
  55. data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_pk_ok.rb +25 -0
  56. data/lib/hrr_rb_ssh/message/080_ssh_msg_global_request.rb +47 -0
  57. data/lib/hrr_rb_ssh/message/081_ssh_msg_request_success.rb +36 -0
  58. data/lib/hrr_rb_ssh/message/082_ssh_msg_request_failure.rb +23 -0
  59. data/lib/hrr_rb_ssh/message/090_ssh_msg_channel_open.rb +67 -0
  60. data/lib/hrr_rb_ssh/message/091_ssh_msg_channel_open_confirmation.rb +67 -0
  61. data/lib/hrr_rb_ssh/message/092_ssh_msg_channel_open_failure.rb +34 -0
  62. data/lib/hrr_rb_ssh/message/093_ssh_msg_channel_window_adjust.rb +25 -0
  63. data/lib/hrr_rb_ssh/message/094_ssh_msg_channel_data.rb +25 -0
  64. data/lib/hrr_rb_ssh/message/095_ssh_msg_channel_extended_data.rb +30 -0
  65. data/lib/hrr_rb_ssh/message/096_ssh_msg_channel_eof.rb +24 -0
  66. data/lib/hrr_rb_ssh/message/097_ssh_msg_channel_close.rb +24 -0
  67. data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +139 -0
  68. data/lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb +24 -0
  69. data/lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb +24 -0
  70. data/lib/hrr_rb_ssh/message/codable.rb +67 -0
  71. data/lib/hrr_rb_ssh/message.rb +36 -0
  72. data/lib/hrr_rb_ssh/transport/compression_algorithm/none.rb +33 -0
  73. data/lib/hrr_rb_ssh/transport/compression_algorithm/zlib.rb +38 -0
  74. data/lib/hrr_rb_ssh/transport/compression_algorithm.rb +22 -0
  75. data/lib/hrr_rb_ssh/transport/constant.rb +11 -0
  76. data/lib/hrr_rb_ssh/transport/data_type.rb +163 -0
  77. data/lib/hrr_rb_ssh/transport/encryption_algorithm/aes_128_cbc.rb +73 -0
  78. data/lib/hrr_rb_ssh/transport/encryption_algorithm/none.rb +49 -0
  79. data/lib/hrr_rb_ssh/transport/encryption_algorithm.rb +22 -0
  80. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman.rb +129 -0
  81. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group14_sha1.rb +42 -0
  82. data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group1_sha1.rb +34 -0
  83. data/lib/hrr_rb_ssh/transport/kex_algorithm.rb +22 -0
  84. data/lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha1.rb +45 -0
  85. data/lib/hrr_rb_ssh/transport/mac_algorithm/none.rb +40 -0
  86. data/lib/hrr_rb_ssh/transport/mac_algorithm.rb +22 -0
  87. data/lib/hrr_rb_ssh/transport/mode.rb +11 -0
  88. data/lib/hrr_rb_ssh/transport/receiver.rb +75 -0
  89. data/lib/hrr_rb_ssh/transport/sender.rb +57 -0
  90. data/lib/hrr_rb_ssh/transport/sequence_number.rb +22 -0
  91. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/ssh_rsa.rb +108 -0
  92. data/lib/hrr_rb_ssh/transport/server_host_key_algorithm.rb +21 -0
  93. data/lib/hrr_rb_ssh/transport.rb +459 -0
  94. data/lib/hrr_rb_ssh/version.rb +6 -0
  95. data/lib/hrr_rb_ssh.rb +13 -0
  96. metadata +193 -0
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/connection/request_handler'
6
+ require 'hrr_rb_ssh/connection/channel/session/subsystem/context'
7
+
8
+ module HrrRbSsh
9
+ class Connection
10
+ class Channel
11
+ module Session
12
+ request_type = 'subsystem'
13
+
14
+ class Subsystem
15
+ def self.run proc_chain, username, io, variables, message, options
16
+ logger = HrrRbSsh::Logger.new self.class.name
17
+
18
+ context = Context.new proc_chain, username, io, variables, message
19
+ handler = options.fetch('connection_channel_request_subsystem', RequestHandler.new {})
20
+ handler.run context
21
+
22
+ proc_chain.connect context.chain_proc
23
+ end
24
+ end
25
+
26
+ @@request_type_list ||= Hash.new
27
+ @@request_type_list[request_type] = Subsystem
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/connection/channel/session/pty_req'
5
+ require 'hrr_rb_ssh/connection/channel/session/env'
6
+ require 'hrr_rb_ssh/connection/channel/session/shell'
7
+ require 'hrr_rb_ssh/connection/channel/session/exec'
8
+ require 'hrr_rb_ssh/connection/channel/session/subsystem'
9
+
10
+ module HrrRbSsh
11
+ class Connection
12
+ class Channel
13
+ channel_type = 'session'
14
+
15
+ module Session
16
+ @@request_type_list ||= Hash.new
17
+
18
+ def self.[] key
19
+ @@request_type_list[key]
20
+ end
21
+
22
+ def self.request_type_list
23
+ @@request_type_list.keys
24
+ end
25
+ end
26
+
27
+ @@type_list ||= Hash.new
28
+ @@type_list[channel_type] = Session
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,278 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'socket'
5
+ require 'hrr_rb_ssh/logger'
6
+ require 'hrr_rb_ssh/connection/channel/proc_chain'
7
+ require 'hrr_rb_ssh/connection/channel/session'
8
+
9
+ module HrrRbSsh
10
+ class Connection
11
+ class Channel
12
+ @@type_list ||= Hash.new
13
+
14
+ def self.[] key
15
+ @@type_list[key]
16
+ end
17
+
18
+ def self.type_list
19
+ @@type_list.keys
20
+ end
21
+
22
+ INITIAL_WINDOW_SIZE = 100000
23
+ MAXIMUM_PACKET_SIZE = 100000
24
+
25
+ attr_reader \
26
+ :receive_payload_queue
27
+
28
+ def initialize connection, channel_type, local_channel, remote_channel, initial_window_size, maximum_packet_size
29
+ @logger = HrrRbSsh::Logger.new self.class.name
30
+
31
+ @connection = connection
32
+ @channel_type = channel_type
33
+ @local_channel = local_channel
34
+ @remote_channel = remote_channel
35
+ @local_window_size = INITIAL_WINDOW_SIZE
36
+ @local_maximum_packet_size = MAXIMUM_PACKET_SIZE
37
+ @remote_window_size = initial_window_size
38
+ @remote_maximum_packet_size = maximum_packet_size
39
+
40
+ @receive_payload_queue = Queue.new
41
+ @receive_data_queue = Queue.new
42
+
43
+ @proc_chain = ProcChain.new
44
+ @channel_io, @request_handler_io = UNIXSocket.pair
45
+
46
+ @closed = nil
47
+ end
48
+
49
+ def start
50
+ @channel_loop_thread = channel_loop_thread
51
+ @sender_thread = sender_thread
52
+ @receiver_thread = receiver_thread
53
+ @proc_chain_thread = proc_chain_thread
54
+ @closed = false
55
+ end
56
+
57
+ def close from=:outside, exitstatus=0
58
+ return if @closed
59
+ @logger.info("close channel")
60
+ @closed = true
61
+ unless from == :proc_chain_thread
62
+ @proc_chain_thread.exit
63
+ end
64
+ @receive_payload_queue.close
65
+ @receive_data_queue.close
66
+ begin
67
+ @request_handler_io.close
68
+ rescue IOError # for compatibility for Ruby version < 2.3
69
+ Thread.pass
70
+ end
71
+ begin
72
+ @channel_io.close
73
+ rescue IOError # for compatibility for Ruby version < 2.3
74
+ Thread.pass
75
+ end
76
+ begin
77
+ if from == :proc_chain_thread
78
+ send_channel_eof
79
+ case exitstatus
80
+ when Integer
81
+ send_channel_request_exit_status exitstatus
82
+ else
83
+ @logger.warn("skip sending exit-status because exitstatus is not an instance of Integer")
84
+ end
85
+ end
86
+ send_channel_close
87
+ rescue HrrRbSsh::ClosedConnectionError => e
88
+ Thread.pass
89
+ rescue => e
90
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
91
+ end
92
+ @logger.info("channel closed")
93
+ end
94
+
95
+ def closed?
96
+ @closed
97
+ end
98
+
99
+ def channel_loop_thread
100
+ Thread.start do
101
+ @logger.info("start channel loop thread")
102
+ variables = {}
103
+ loop do
104
+ begin
105
+ message = @receive_payload_queue.deq
106
+ if message.nil? && @receive_payload_queue.closed?
107
+ @logger.info("closing channel loop thread")
108
+ break
109
+ end
110
+ if message.has_key?(HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::ID)
111
+ @logger.info("received channel request: #{message['request type']}")
112
+ request message, variables
113
+ if message['want reply']
114
+ send_channel_success
115
+ end
116
+ elsif message.has_key?(HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::ID)
117
+ @logger.info("received channel data")
118
+ local_channel = message['recipient channel']
119
+ @receive_data_queue.enq message['data']
120
+ elsif message.has_key?(HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::ID)
121
+ @logger.debug("received channel window adjust")
122
+ @remote_window_size = [@remote_window_size + message['bytes to add'], 0xffff_ffff].min
123
+ else
124
+ @logger.warn("received unsupported message: #{message.inspect}")
125
+ end
126
+ rescue => e
127
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
128
+ break
129
+ end
130
+ end
131
+ close from=:channel_loop_thread
132
+ @logger.info("channel loop thread closed")
133
+ end
134
+ end
135
+
136
+ def sender_thread
137
+ Thread.start {
138
+ @logger.info("start sender thread")
139
+ loop do
140
+ if @channel_io.closed?
141
+ @logger.info("closing sender thread")
142
+ break
143
+ end
144
+ begin
145
+ data = @channel_io.readpartial(1024)
146
+ sendable_size = [data.size, @remote_window_size].min
147
+ sending_data = data[0, sendable_size]
148
+ send_channel_data sending_data if sendable_size > 0
149
+ @remote_window_size -= sendable_size
150
+ rescue EOFError => e
151
+ begin
152
+ @channel_io.close
153
+ rescue IOError # for compatibility for Ruby version < 2.3
154
+ Thread.pass
155
+ end
156
+ rescue IOError => e
157
+ @logger.warn("channel IO is closed")
158
+ close
159
+ rescue => e
160
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
161
+ close
162
+ end
163
+ end
164
+ @logger.info("sender thread closed")
165
+ }
166
+ end
167
+
168
+ def receiver_thread
169
+ Thread.start {
170
+ @logger.info("start receiver thread")
171
+ loop do
172
+ begin
173
+ data = @receive_data_queue.deq
174
+ if data.nil? && @receive_data_queue.closed?
175
+ @logger.info("closing receiver thread")
176
+ break
177
+ end
178
+ @channel_io.write data
179
+ @local_window_size -= data.size
180
+ if @local_window_size < INITIAL_WINDOW_SIZE/2
181
+ @logger.info("send channel window adjust")
182
+ send_channel_window_adjust
183
+ @local_window_size += INITIAL_WINDOW_SIZE
184
+ end
185
+ rescue IOError => e
186
+ @logger.warn("channel IO is closed")
187
+ close
188
+ rescue => e
189
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
190
+ close
191
+ end
192
+ end
193
+ @logger.info("receiver thread closed")
194
+ }
195
+ end
196
+
197
+ def proc_chain_thread
198
+ Thread.start {
199
+ @logger.info("start proc chain thread")
200
+ begin
201
+ exitstatus = @proc_chain.call_next
202
+ rescue => e
203
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
204
+ exitstatus = 1
205
+ ensure
206
+ @logger.info("closing proc chain thread")
207
+ close from=:proc_chain_thread, exitstatus=exitstatus
208
+ @logger.info("proc chain thread closed")
209
+ end
210
+ }
211
+ end
212
+
213
+ def request message, variables
214
+ request_type = message['request type']
215
+ @@type_list[@channel_type][request_type].run @proc_chain, @connection.username, @request_handler_io, variables, message, @connection.options
216
+ end
217
+
218
+ def send_channel_success
219
+ message = {
220
+ 'SSH_MSG_CHANNEL_SUCCESS' => HrrRbSsh::Message::SSH_MSG_CHANNEL_SUCCESS::VALUE,
221
+ 'recipient channel' => @remote_channel,
222
+ }
223
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_SUCCESS.encode message
224
+ @connection.send payload
225
+ end
226
+
227
+ def send_channel_window_adjust
228
+ message = {
229
+ 'SSH_MSG_CHANNEL_WINDOW_ADJUST' => HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE,
230
+ 'recipient channel' => @remote_channel,
231
+ 'bytes to add' => INITIAL_WINDOW_SIZE,
232
+ }
233
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.encode message
234
+ @connection.send payload
235
+ end
236
+
237
+ def send_channel_data data
238
+ message = {
239
+ 'SSH_MSG_CHANNEL_DATA' => HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::VALUE,
240
+ 'recipient channel' => @remote_channel,
241
+ 'data' => data,
242
+ }
243
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA.encode message
244
+ @connection.send payload
245
+ end
246
+
247
+ def send_channel_request_exit_status exitstatus
248
+ message = {
249
+ 'SSH_MSG_CHANNEL_REQUEST' => HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
250
+ 'recipient channel' => @remote_channel,
251
+ 'request type' => 'exit-status',
252
+ 'want reply' => false,
253
+ 'exit status' => exitstatus,
254
+ }
255
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST.encode message
256
+ @connection.send payload
257
+ end
258
+
259
+ def send_channel_eof
260
+ message = {
261
+ 'SSH_MSG_CHANNEL_EOF' => HrrRbSsh::Message::SSH_MSG_CHANNEL_EOF::VALUE,
262
+ 'recipient channel' => @remote_channel,
263
+ }
264
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_EOF.encode message
265
+ @connection.send payload
266
+ end
267
+
268
+ def send_channel_close
269
+ message = {
270
+ 'SSH_MSG_CHANNEL_CLOSE' => HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE::VALUE,
271
+ 'recipient channel' => @remote_channel,
272
+ }
273
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE.encode message
274
+ @connection.send payload
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/logger'
5
+
6
+ module HrrRbSsh
7
+ class Connection
8
+ class RequestHandler
9
+ def initialize &block
10
+ @logger = HrrRbSsh::Logger.new self.class.name
11
+ @proc = block
12
+ end
13
+ def run context
14
+ @proc.call context
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,170 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/closed_connection_error'
6
+ require 'hrr_rb_ssh/connection/channel'
7
+
8
+ module HrrRbSsh
9
+ class Connection
10
+ attr_reader \
11
+ :username,
12
+ :options
13
+
14
+ def initialize authentication, options={}
15
+ @logger = HrrRbSsh::Logger.new self.class.name
16
+
17
+ @authentication = authentication
18
+ @options = options
19
+
20
+ @channels = Hash.new
21
+ @username = nil
22
+ @closed = nil
23
+ end
24
+
25
+ def send payload
26
+ raise ClosedConnectionError if @closed
27
+ begin
28
+ @authentication.send payload
29
+ rescue ClosedAuthenticationError
30
+ raise ClosedConnectionError
31
+ end
32
+ end
33
+
34
+ def start
35
+ @authentication.start
36
+ @closed = false
37
+ connection_loop
38
+ end
39
+
40
+ def close
41
+ @closed = true
42
+ @channels.values.each do |channel|
43
+ begin
44
+ channel.close
45
+ rescue => e
46
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
47
+ end
48
+ end
49
+ @channels.clear
50
+ end
51
+
52
+ def closed?
53
+ @closed
54
+ end
55
+
56
+ def connection_loop
57
+ @logger.info("start connection")
58
+ loop do
59
+ begin
60
+ payload = @authentication.receive
61
+ rescue HrrRbSsh::ClosedAuthenticationError => e
62
+ @logger.info("closing connection loop")
63
+ break
64
+ end
65
+ @username ||= @authentication.username
66
+ case payload[0,1].unpack("C")[0]
67
+ when HrrRbSsh::Message::SSH_MSG_GLOBAL_REQUEST::VALUE
68
+ global_request payload
69
+ when HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN::VALUE
70
+ channel_open payload
71
+ when HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::VALUE
72
+ channel_request payload
73
+ when HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
74
+ channel_window_adjust payload
75
+ when HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::VALUE
76
+ channel_data payload
77
+ when HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE::VALUE
78
+ channel_close payload
79
+ else
80
+ @logger.warn("received unsupported message: id: #{payload[0,1].unpack("C")[0]}")
81
+ end
82
+ end
83
+ @logger.info("closing connection")
84
+ close
85
+ @logger.info("connection closed")
86
+ end
87
+
88
+ def global_request payload
89
+ @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_GLOBAL_REQUEST::ID)
90
+ message = HrrRbSsh::Message::SSH_MSG_GLOBAL_REQUEST.decode payload
91
+ if message['want reply']
92
+ # returns always failure because global request is not supported so far
93
+ send_request_failure
94
+ end
95
+ end
96
+
97
+ def channel_open payload
98
+ @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN::ID)
99
+ message = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN.decode payload
100
+ channel_type = message['channel type']
101
+ local_channel = message['sender channel']
102
+ remote_channel = message['sender channel']
103
+ initial_window_size = message['initial window size']
104
+ maximum_packet_size = message['maximum packet size']
105
+ channel = Channel.new self, channel_type, local_channel, remote_channel, initial_window_size, maximum_packet_size
106
+ @channels[local_channel] = channel
107
+ channel.start
108
+ send_channel_open_confirmation channel_type, local_channel, remote_channel, initial_window_size, maximum_packet_size
109
+ end
110
+
111
+ def channel_request payload
112
+ @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::ID)
113
+ message = HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST.decode payload
114
+ local_channel = message['recipient channel']
115
+ @channels[local_channel].receive_payload_queue.enq message
116
+ end
117
+
118
+ def channel_window_adjust payload
119
+ @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::ID)
120
+ message = HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.decode payload
121
+ local_channel = message['recipient channel']
122
+ @channels[local_channel].receive_payload_queue.enq message
123
+ end
124
+
125
+ def channel_data payload
126
+ @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::ID)
127
+ message = HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA.decode payload
128
+ local_channel = message['recipient channel']
129
+ @channels[local_channel].receive_payload_queue.enq message
130
+ end
131
+
132
+ def channel_close payload
133
+ @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE::ID)
134
+ message = HrrRbSsh::Message::SSH_MSG_CHANNEL_CLOSE.decode payload
135
+ local_channel = message['recipient channel']
136
+ channel = @channels[local_channel]
137
+ channel.close
138
+ @channels.delete local_channel
139
+ end
140
+
141
+ def send_request_success
142
+ message = {
143
+ 'SSH_MSG_REQUEST_SUCCESS' => HrrRbSsh::Message::SSH_MSG_REQUEST_SUCCESS::VALUE,
144
+ }
145
+ payload = HrrRbSsh::Message::SSH_MSG_REQUEST_SUCCESS.encode message
146
+ @authentication.send payload
147
+ end
148
+
149
+ def send_request_failure
150
+ message = {
151
+ 'SSH_MSG_REQUEST_FAILURE' => HrrRbSsh::Message::SSH_MSG_REQUEST_FAILURE::VALUE,
152
+ }
153
+ payload = HrrRbSsh::Message::SSH_MSG_REQUEST_FAILURE.encode message
154
+ @authentication.send payload
155
+ end
156
+
157
+ def send_channel_open_confirmation channel_type, local_channel, remote_channel, initial_window_size, maximum_packet_size
158
+ message = {
159
+ 'SSH_MSG_CHANNEL_OPEN_CONFIRMATION' => HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE,
160
+ 'channel type' => channel_type,
161
+ 'recipient channel' => remote_channel,
162
+ 'sender channel' => local_channel,
163
+ 'initial window size' => initial_window_size,
164
+ 'maximum packet size' => maximum_packet_size,
165
+ }
166
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.encode message
167
+ @authentication.send payload
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ module HrrRbSsh
5
+ class Logger
6
+ def self.initialize logger
7
+ @@logger = logger
8
+ end
9
+
10
+ def self.uninitialize
11
+ @@logger = nil
12
+ end
13
+
14
+ def self.initialized?
15
+ @@logger != nil
16
+ end
17
+
18
+ def initialize name
19
+ @name = name
20
+ end
21
+
22
+ def fatal message
23
+ if self.class.initialized?
24
+ @@logger.fatal "#{@name}: #{message}"
25
+ end
26
+ end
27
+
28
+ def error message
29
+ if self.class.initialized?
30
+ @@logger.error "#{@name}: #{message}"
31
+ end
32
+ end
33
+
34
+ def warn message
35
+ if self.class.initialized?
36
+ @@logger.warn "#{@name}: #{message}"
37
+ end
38
+ end
39
+
40
+ def info message
41
+ if self.class.initialized?
42
+ @@logger.info "#{@name}: #{message}"
43
+ end
44
+ end
45
+
46
+ def debug message
47
+ if self.class.initialized?
48
+ @@logger.debug "#{@name}: #{message}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/message/codable'
6
+
7
+ module HrrRbSsh
8
+ module Message
9
+ module SSH_MSG_DISCONNECT
10
+ module ReasonCode
11
+ SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1
12
+ SSH_DISCONNECT_PROTOCOL_ERROR = 2
13
+ SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3
14
+ SSH_DISCONNECT_RESERVED = 4
15
+ SSH_DISCONNECT_MAC_ERROR = 5
16
+ SSH_DISCONNECT_COMPRESSION_ERROR = 6
17
+ SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7
18
+ SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8
19
+ SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9
20
+ SSH_DISCONNECT_CONNECTION_LOST = 10
21
+ SSH_DISCONNECT_BY_APPLICATION = 11
22
+ SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12
23
+ SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13
24
+ SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14
25
+ SSH_DISCONNECT_ILLEGAL_USER_NAME = 15
26
+ end
27
+
28
+ class << self
29
+ include Codable
30
+ end
31
+
32
+ ID = self.name.split('::').last
33
+ VALUE = 1
34
+
35
+ DEFINITION = [
36
+ # [Data Type, Field Name]
37
+ ['byte', 'SSH_MSG_DISCONNECT'],
38
+ ['uint32', 'reason code'],
39
+ ['string', 'description'],
40
+ ['string', 'language tag'],
41
+ ]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/message/codable'
6
+
7
+ module HrrRbSsh
8
+ module Message
9
+ module SSH_MSG_IGNORE
10
+ class << self
11
+ include Codable
12
+ end
13
+
14
+ ID = self.name.split('::').last
15
+ VALUE = 2
16
+
17
+ DEFINITION = [
18
+ # [Data Type, Field Name]
19
+ ['byte', 'SSH_MSG_IGNORE'],
20
+ ['string', 'data'],
21
+ ]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/logger'
5
+ require 'hrr_rb_ssh/message/codable'
6
+
7
+ module HrrRbSsh
8
+ module Message
9
+ module SSH_MSG_UNIMPLEMENTED
10
+ class << self
11
+ include Codable
12
+ end
13
+
14
+ ID = self.name.split('::').last
15
+ VALUE = 3
16
+
17
+ DEFINITION = [
18
+ # [Data Type, Field Name]
19
+ ['byte', 'SSH_MSG_UNIMPLEMENTED'],
20
+ ['uint32', 'packet sequence number of rejected message'],
21
+ ]
22
+ end
23
+ end
24
+ end