hrr_rb_ssh 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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