hrr_rb_ssh 0.1.5 → 0.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ef9c8b694642b51f6b8cf1533da4243a688b3a5627f2e95a46903a882d3df00
4
- data.tar.gz: ccfba489514d746b7b973ce6ed6551e84f34c3a7067891970d8401f944ba98e2
3
+ metadata.gz: 20686d9dca5a433f36d523d681d18ce280d0dca33f90ebe205d98a1f4dc80980
4
+ data.tar.gz: bd3c9514d667d7780735663ee258506455d6832ce85ee19d8c136aa4973832d3
5
5
  SHA512:
6
- metadata.gz: bf79eec5cdcddb81d3a8fa97a67e5e49664d3dc763633822c85d6355a0a3df76475172c9f06f3c571a6207483f8d616ede74a9f502929adc11ff4b8a9793e024
7
- data.tar.gz: 3aa22c9f967259b0ac76bdc4a33b0aa6057a4c912369dfe3be48a38c6726ebd84af2c2ebefdf203527facc58159fd6c753edfd4cc09d4d5f9d24124637db670e
6
+ metadata.gz: 89045342800f000833a172255d4ec4ed8b061898eb2f4c4780a211fdc3207385e3ede4b8542bbaf5ac7f65b34f128554bd73acfc05877aea74d3b6c95dfdfe98
7
+ data.tar.gz: 5fc28c6c9ddff4de8cd39d60b41aff4db8fb94d37d3dda98bf28d964ee0696b4c2040a7f33c92608c3f4b08fdb4ddb114e056b8206de20f3546782840de3c839
data/demo/server.rb CHANGED
@@ -21,7 +21,7 @@ HrrRbSsh::Logger.initialize logger
21
21
  tran_preferred_encryption_algorithms = %w(aes128-ctr aes192-ctr aes256-ctr aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc aes256-cbc arcfour)
22
22
  tran_preferred_server_host_key_algorithms = %w(ecdsa-sha2-nistp521 ecdsa-sha2-nistp384 ecdsa-sha2-nistp256 ssh-rsa ssh-dss)
23
23
  tran_preferred_kex_algorithms = %w(ecdh-sha2-nistp521 ecdh-sha2-nistp384 ecdh-sha2-nistp256 diffie-hellman-group14-sha1 diffie-hellman-group1-sha1)
24
- tran_preferred_mac_algorithms = %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96)
24
+ tran_preferred_mac_algorithms = %w(hmac-sha2-512 hmac-sha2-256 hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96)
25
25
  tran_preferred_compression_algorithms = %w(none zlib)
26
26
 
27
27
  auth_none = HrrRbSsh::Authentication::Authenticator.new { |context|
@@ -10,7 +10,7 @@ module HrrRbSsh
10
10
  class DirectTcpip < ChannelType
11
11
  NAME = 'direct-tcpip'
12
12
 
13
- def initialize connection, channel, message
13
+ def initialize connection, channel, message, socket=nil
14
14
  @logger = HrrRbSsh::Logger.new self.class.name
15
15
  @connection = connection
16
16
  @channel = channel
@@ -44,18 +44,18 @@ module HrrRbSsh
44
44
  begin
45
45
  loop do
46
46
  begin
47
- @channel.request_handler_io.write s.readpartial(10240)
47
+ @channel.io[1].write s.readpartial(10240)
48
48
  rescue EOFError
49
49
  @logger.info("socket is EOF")
50
- @channel.request_handler_io.close_write
50
+ @channel.io[1].close
51
51
  break
52
52
  rescue IOError
53
53
  @logger.info("socket is closed")
54
- @channel.request_handler_io.close_write
54
+ @channel.io[1].close
55
55
  break
56
56
  rescue => e
57
57
  @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
58
- @channel.request_handler_io.close_write
58
+ @channel.io[1].close
59
59
  break
60
60
  end
61
61
  end
@@ -73,7 +73,7 @@ module HrrRbSsh
73
73
  begin
74
74
  loop do
75
75
  begin
76
- s.write @channel.request_handler_io.readpartial(10240)
76
+ s.write @channel.io[0].readpartial(10240)
77
77
  rescue EOFError
78
78
  @logger.info("io is EOF")
79
79
  s.close_write
@@ -0,0 +1,98 @@
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 Channel
9
+ class ChannelType
10
+ class ForwardedTcpip < ChannelType
11
+ NAME = 'forwarded-tcpip'
12
+
13
+ def initialize connection, channel, message, socket
14
+ @logger = HrrRbSsh::Logger.new self.class.name
15
+ @connection = connection
16
+ @channel = channel
17
+ @socket = socket
18
+ end
19
+
20
+ def start
21
+ @sender_thread = sender_thread
22
+ @receiver_thread = receiver_thread
23
+ end
24
+
25
+ def close
26
+ begin
27
+ if @sender_thread_finished && @receiver_thread_finished
28
+ @logger.info("closing forwarded-tcpip")
29
+ @socket.close
30
+ @channel.close from=:channel_type_instance
31
+ @logger.info("forwarded-tcpip closed")
32
+ end
33
+ rescue => e
34
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
35
+ end
36
+ end
37
+
38
+ def sender_thread
39
+ Thread.new(@socket){ |s|
40
+ begin
41
+ loop do
42
+ begin
43
+ @channel.io[1].write s.readpartial(10240)
44
+ rescue EOFError
45
+ @logger.info("socket is EOF")
46
+ @channel.io[1].close
47
+ break
48
+ rescue IOError
49
+ @logger.info("socket is closed")
50
+ @channel.io[1].close
51
+ break
52
+ rescue => e
53
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
54
+ @channel.io[1].close
55
+ break
56
+ end
57
+ end
58
+ @logger.info("finishing sender thread")
59
+ @sender_thread_finished = true
60
+ close
61
+ ensure
62
+ @logger.info("sender thread finished")
63
+ end
64
+ }
65
+ end
66
+
67
+ def receiver_thread
68
+ Thread.new(@socket){ |s|
69
+ begin
70
+ loop do
71
+ begin
72
+ s.write @channel.io[0].readpartial(10240)
73
+ rescue EOFError
74
+ @logger.info("io is EOF")
75
+ s.close_write
76
+ break
77
+ rescue IOError
78
+ @logger.info("socket is closed")
79
+ break
80
+ rescue => e
81
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
82
+ s.close_write
83
+ break
84
+ end
85
+ end
86
+ @logger.info("finishing receiver thread")
87
+ @receiver_thread_finished = true
88
+ close
89
+ ensure
90
+ @logger.info("receiver thread finished")
91
+ end
92
+ }
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -10,7 +10,7 @@ module HrrRbSsh
10
10
  class Session < ChannelType
11
11
  NAME = 'session'
12
12
 
13
- def initialize connection, channel, message
13
+ def initialize connection, channel, message, socket=nil
14
14
  @logger = HrrRbSsh::Logger.new self.class.name
15
15
  @connection = connection
16
16
  @channel = channel
@@ -30,7 +30,7 @@ module HrrRbSsh
30
30
 
31
31
  def request message
32
32
  request_type = message[:'request type']
33
- RequestType[request_type].run @proc_chain, @connection.username, @channel.request_handler_io, @variables, message, @connection.options
33
+ RequestType[request_type].run @proc_chain, @connection.username, @channel.io, @variables, message, @connection.options
34
34
  end
35
35
 
36
36
  def proc_chain_thread
@@ -43,6 +43,9 @@ module HrrRbSsh
43
43
  exitstatus = 1
44
44
  ensure
45
45
  @logger.info("closing proc chain thread")
46
+ @logger.info("wait for sending output")
47
+ @channel.wait_until_senders_closed
48
+ @logger.info("sending output finished")
46
49
  @channel.close from=:channel_type_instance, exitstatus=exitstatus
47
50
  @logger.info("proc chain thread closed")
48
51
  end
@@ -29,3 +29,4 @@ end
29
29
 
30
30
  require 'hrr_rb_ssh/connection/channel/channel_type/session'
31
31
  require 'hrr_rb_ssh/connection/channel/channel_type/direct_tcpip'
32
+ require 'hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip'
@@ -19,40 +19,56 @@ module HrrRbSsh
19
19
  :local_maximum_packet_size,
20
20
  :remote_window_size,
21
21
  :remote_maximum_packet_size,
22
- :receive_message_queue,
23
- :request_handler_io
22
+ :receive_message_queue
24
23
 
25
- def initialize connection, message
24
+ def initialize connection, message, socket=nil
26
25
  @logger = HrrRbSsh::Logger.new self.class.name
27
26
 
28
27
  @connection = connection
29
28
 
30
29
  @channel_type = message[:'channel type']
31
- @local_channel = message[:'sender channel']
30
+ @local_channel = connection.assign_channel
32
31
  @remote_channel = message[:'sender channel']
33
32
  @local_window_size = INITIAL_WINDOW_SIZE
34
33
  @local_maximum_packet_size = MAXIMUM_PACKET_SIZE
35
34
  @remote_window_size = message[:'initial window size']
36
35
  @remote_maximum_packet_size = message[:'maximum packet size']
37
36
 
38
- @channel_type_instance = ChannelType[@channel_type].new connection, self, message
37
+ @channel_type_instance = ChannelType[@channel_type].new connection, self, message, socket
39
38
 
40
39
  @receive_message_queue = Queue.new
41
40
  @receive_data_queue = Queue.new
42
41
 
43
- @channel_io, @request_handler_io = UNIXSocket.pair
42
+ @r_io_in, @w_io_in = IO.pipe
43
+ @r_io_out, @w_io_out = IO.pipe
44
+ @r_io_err, @w_io_err = IO.pipe
44
45
 
45
46
  @closed = nil
46
47
  end
47
48
 
49
+ def set_remote_parameters message
50
+ @remote_channel = message[:'sender channel']
51
+ @remote_window_size = message[:'initial window size']
52
+ @remote_maximum_packet_size = message[:'maximum packet size']
53
+ end
54
+
55
+ def io
56
+ [@r_io_in, @w_io_out, @w_io_err]
57
+ end
58
+
48
59
  def start
49
60
  @channel_loop_thread = channel_loop_thread
50
- @sender_thread = sender_thread
61
+ @out_sender_thread = out_sender_thread
62
+ @err_sender_thread = err_sender_thread
51
63
  @receiver_thread = receiver_thread
52
64
  @channel_type_instance.start
53
65
  @closed = false
54
66
  end
55
67
 
68
+ def wait_until_senders_closed
69
+ [@out_sender_thread, @err_sender_thread].select{ |t| t.instance_of? Thread }.each(&:join)
70
+ end
71
+
56
72
  def close from=:outside, exitstatus=0
57
73
  return if @closed
58
74
  @logger.info("close channel")
@@ -62,16 +78,13 @@ module HrrRbSsh
62
78
  end
63
79
  @receive_message_queue.close
64
80
  @receive_data_queue.close
65
- begin
66
- @request_handler_io.close
67
- rescue IOError # for compatibility for Ruby version < 2.3
68
- Thread.pass
69
- end
70
- begin
71
- @channel_io.close
72
- rescue IOError # for compatibility for Ruby version < 2.3
73
- Thread.pass
74
- end
81
+ [@r_io_in, @w_io_in, @r_io_out, @w_io_out, @r_io_err, @w_io_err].each{ |io|
82
+ begin
83
+ io.close
84
+ rescue IOError # for compatibility for Ruby version < 2.3
85
+ Thread.pass
86
+ end
87
+ }
75
88
  begin
76
89
  if from == :channel_type_instance
77
90
  send_channel_eof
@@ -109,9 +122,17 @@ module HrrRbSsh
109
122
  case message[:'message number']
110
123
  when HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::VALUE
111
124
  @logger.info("received channel request: #{message[:'request type']}")
112
- @channel_type_instance.request message
113
- if message[:'want reply']
114
- send_channel_success
125
+ begin
126
+ @channel_type_instance.request message
127
+ rescue => e
128
+ @logger.warn("request failed: #{e.message}")
129
+ if message[:'want reply']
130
+ send_channel_failure
131
+ end
132
+ else
133
+ if message[:'want reply']
134
+ send_channel_success
135
+ end
115
136
  end
116
137
  when HrrRbSsh::Message::SSH_MSG_CHANNEL_DATA::VALUE
117
138
  @logger.info("received channel data")
@@ -133,23 +154,55 @@ module HrrRbSsh
133
154
  end
134
155
  end
135
156
 
136
- def sender_thread
157
+ def out_sender_thread
137
158
  Thread.start {
138
- @logger.info("start sender thread")
159
+ @logger.info("start out sender thread")
139
160
  loop do
140
- if @channel_io.closed?
141
- @logger.info("closing sender thread")
161
+ if @r_io_out.closed?
162
+ @logger.info("closing out sender thread")
142
163
  break
143
164
  end
144
165
  begin
145
- data = @channel_io.readpartial(1024)
166
+ data = @r_io_out.readpartial(1024)
146
167
  sendable_size = [data.size, @remote_window_size].min
147
168
  sending_data = data[0, sendable_size]
148
169
  send_channel_data sending_data if sendable_size > 0
149
170
  @remote_window_size -= sendable_size
150
171
  rescue EOFError => e
151
172
  begin
152
- @channel_io.close
173
+ @r_io_out.close
174
+ rescue IOError # for compatibility for Ruby version < 2.3
175
+ Thread.pass
176
+ end
177
+ rescue IOError => e
178
+ @logger.warn("channel IO is closed")
179
+ close
180
+ rescue => e
181
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
182
+ close
183
+ end
184
+ end
185
+ @logger.info("out sender thread closed")
186
+ }
187
+ end
188
+
189
+ def err_sender_thread
190
+ Thread.start {
191
+ @logger.info("start err sender thread")
192
+ loop do
193
+ if @r_io_err.closed?
194
+ @logger.info("closing err sender thread")
195
+ break
196
+ end
197
+ begin
198
+ data = @r_io_err.readpartial(1024)
199
+ sendable_size = [data.size, @remote_window_size].min
200
+ sending_data = data[0, sendable_size]
201
+ send_channel_extended_data sending_data if sendable_size > 0
202
+ @remote_window_size -= sendable_size
203
+ rescue EOFError => e
204
+ begin
205
+ @r_io_err.close
153
206
  rescue IOError # for compatibility for Ruby version < 2.3
154
207
  Thread.pass
155
208
  end
@@ -161,7 +214,7 @@ module HrrRbSsh
161
214
  close
162
215
  end
163
216
  end
164
- @logger.info("sender thread closed")
217
+ @logger.info("err sender thread closed")
165
218
  }
166
219
  end
167
220
 
@@ -174,11 +227,11 @@ module HrrRbSsh
174
227
  if data.nil? && @receive_data_queue.closed?
175
228
  @logger.info("closing receiver thread")
176
229
  @logger.info("closing channel IO write")
177
- @channel_io.close_write
230
+ @w_io_in.close_write
178
231
  @logger.info("channel IO write closed")
179
232
  break
180
233
  end
181
- @channel_io.write data
234
+ @w_io_in.write data
182
235
  @local_window_size -= data.size
183
236
  if @local_window_size < INITIAL_WINDOW_SIZE/2
184
237
  @logger.info("send channel window adjust")
@@ -208,6 +261,15 @@ module HrrRbSsh
208
261
  @connection.send payload
209
262
  end
210
263
 
264
+ def send_channel_failure
265
+ message = {
266
+ :'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_FAILURE::VALUE,
267
+ :'recipient channel' => @remote_channel,
268
+ }
269
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_FAILURE.encode message
270
+ @connection.send payload
271
+ end
272
+
211
273
  def send_channel_window_adjust
212
274
  message = {
213
275
  :'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE,
@@ -228,6 +290,17 @@ module HrrRbSsh
228
290
  @connection.send payload
229
291
  end
230
292
 
293
+ def send_channel_extended_data data, code=HrrRbSsh::Message::SSH_MSG_CHANNEL_EXTENDED_DATA::DataTypeCode::SSH_EXTENDED_DATA_STDERR
294
+ message = {
295
+ :'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_EXTENDED_DATA::VALUE,
296
+ :'recipient channel' => @remote_channel,
297
+ :'data type code' => code,
298
+ :'data' => data,
299
+ }
300
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_EXTENDED_DATA.encode message
301
+ @connection.send payload
302
+ end
303
+
231
304
  def send_channel_request_exit_status exitstatus
232
305
  message = {
233
306
  :'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::VALUE,
@@ -0,0 +1,90 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'socket'
5
+ require 'hrr_rb_ssh/logger'
6
+
7
+ module HrrRbSsh
8
+ class Connection
9
+ class GlobalRequestHandler
10
+ attr_reader \
11
+ :accepted
12
+
13
+ def initialize connection
14
+ @logger = HrrRbSsh::Logger.new self.class.name
15
+ @connection = connection
16
+ @tcpip_forward_servers = Hash.new
17
+ @tcpip_forward_threads = Hash.new
18
+ end
19
+
20
+ def close
21
+ @logger.info("closing tcpip-forward")
22
+ @tcpip_forward_threads.values.each(&:exit)
23
+ @tcpip_forward_servers.values.each{ |s|
24
+ begin
25
+ s.close
26
+ rescue IOError # for compatibility for Ruby version < 2.3
27
+ Thread.pass
28
+ end
29
+ }
30
+ @tcpip_forward_threads.clear
31
+ @tcpip_forward_servers.clear
32
+ @logger.info("tcpip-forward closed")
33
+ end
34
+
35
+ def request message
36
+ case message[:'request name']
37
+ when "tcpip-forward"
38
+ tcpip_forward message
39
+ when "cancel-tcpip-forward"
40
+ cancel_tcpip_forward message
41
+ else
42
+ @logger.warn("unsupported request name: #{message[:'request name']}")
43
+ raise
44
+ end
45
+ end
46
+
47
+ def tcpip_forward message
48
+ @logger.info("starting tcpip-forward")
49
+ begin
50
+ address_to_bind = message[:'address to bind']
51
+ port_number_to_bind = message[:'port number to bind']
52
+ id = "#{address_to_bind}:#{port_number_to_bind}"
53
+ server = TCPServer.new address_to_bind, port_number_to_bind
54
+ @tcpip_forward_servers[id] = server
55
+ @tcpip_forward_threads[id] = Thread.new(server){ |server|
56
+ begin
57
+ loop do
58
+ Thread.new(server.accept){ |s|
59
+ @connection.channel_open_start address_to_bind, port_number_to_bind, s
60
+ }
61
+ end
62
+ rescue => e
63
+ @logger.error([e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join)
64
+ end
65
+ }
66
+ @logger.info("tcpip-forward started")
67
+ rescue => e
68
+ @logger.warn("starting tcpip-forward failed: #{e.message}")
69
+ raise e
70
+ end
71
+ end
72
+
73
+ def cancel_tcpip_forward message
74
+ @logger.info("canceling tcpip-forward")
75
+ address_to_bind = message[:'address to bind']
76
+ port_number_to_bind = message[:'port number to bind']
77
+ id = "#{address_to_bind}:#{port_number_to_bind}"
78
+ @tcpip_forward_threads[id].exit
79
+ begin
80
+ @tcpip_forward_servers[id].close
81
+ rescue IOError # for compatibility for Ruby version < 2.3
82
+ Thread.pass
83
+ end
84
+ @tcpip_forward_threads.delete id
85
+ @tcpip_forward_servers.delete id
86
+ @logger.info("tcpip-forward canceled")
87
+ end
88
+ end
89
+ end
90
+ end
@@ -15,8 +15,9 @@ module HrrRbSsh
15
15
  pid = fork do
16
16
  Process.setsid
17
17
  context.vars[:env] ||= Hash.new
18
- exec context.vars[:env], context.command, in: context.io, out: context.io, err: context.io
18
+ exec context.vars[:env], context.command, in: context.io[0], out: context.io[1], err: context.io[2]
19
19
  end
20
+ context.io.each{ |io| io.close }
20
21
  pid, status = Process.waitpid2 pid
21
22
  status.exitstatus
22
23
  }
@@ -16,6 +16,8 @@ module HrrRbSsh
16
16
  ptm.winsize = [context.terminal_height_rows, context.terminal_width_characters]
17
17
  context.vars[:ptm] = ptm
18
18
  context.vars[:pts] = pts
19
+ context.vars[:env] ||= Hash.new
20
+ context.vars[:env]['TERM'] = context.term_environment_variable_value
19
21
  context.chain_proc { |chain|
20
22
  begin
21
23
  chain.call_next
@@ -15,6 +15,8 @@ module HrrRbSsh
15
15
  ptm = context.vars[:ptm]
16
16
  pts = context.vars[:pts]
17
17
 
18
+ context.io[2].close # never use err output in shell handler
19
+
18
20
  context.chain_proc { |chain|
19
21
  pid = fork do
20
22
  ptm.close
@@ -33,7 +35,7 @@ module HrrRbSsh
33
35
  threads.push Thread.start {
34
36
  loop do
35
37
  begin
36
- context.io.write ptm.readpartial(1024)
38
+ context.io[1].write ptm.readpartial(1024)
37
39
  rescue EOFError => e
38
40
  context.logger.info("ptm is EOF")
39
41
  break
@@ -45,11 +47,12 @@ module HrrRbSsh
45
47
  break
46
48
  end
47
49
  end
50
+ context.io[1].close
48
51
  }
49
52
  threads.push Thread.start {
50
53
  loop do
51
54
  begin
52
- ptm.write context.io.readpartial(1024)
55
+ ptm.write context.io[0].readpartial(1024)
53
56
  rescue EOFError => e
54
57
  context.logger.info("IO is EOF")
55
58
  break
@@ -61,6 +64,7 @@ module HrrRbSsh
61
64
  break
62
65
  end
63
66
  end
67
+ ptm.close
64
68
  }
65
69
 
66
70
  begin
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'hrr_rb_ssh/logger'
5
5
  require 'hrr_rb_ssh/closed_connection_error'
6
+ require 'hrr_rb_ssh/connection/global_request_handler'
6
7
  require 'hrr_rb_ssh/connection/channel'
7
8
 
8
9
  module HrrRbSsh
@@ -17,6 +18,7 @@ module HrrRbSsh
17
18
  @authentication = authentication
18
19
  @options = options
19
20
 
21
+ @global_request_handler = GlobalRequestHandler.new self
20
22
  @channels = Hash.new
21
23
  @username = nil
22
24
  @closed = nil
@@ -31,6 +33,16 @@ module HrrRbSsh
31
33
  end
32
34
  end
33
35
 
36
+ def assign_channel
37
+ i = 0
38
+ res = nil
39
+ loop do
40
+ break unless @channels.keys.include?(i)
41
+ i += 1
42
+ end
43
+ i
44
+ end
45
+
34
46
  def start
35
47
  @authentication.start
36
48
  @closed = false
@@ -47,6 +59,7 @@ module HrrRbSsh
47
59
  end
48
60
  end
49
61
  @channels.clear
62
+ @global_request_handler.close
50
63
  end
51
64
 
52
65
  def closed?
@@ -68,6 +81,8 @@ module HrrRbSsh
68
81
  global_request payload
69
82
  when HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN::VALUE
70
83
  channel_open payload
84
+ when HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE
85
+ channel_open_confirmation payload
71
86
  when HrrRbSsh::Message::SSH_MSG_CHANNEL_REQUEST::VALUE
72
87
  channel_request payload
73
88
  when HrrRbSsh::Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
@@ -90,19 +105,58 @@ module HrrRbSsh
90
105
  def global_request payload
91
106
  @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_GLOBAL_REQUEST::ID)
92
107
  message = HrrRbSsh::Message::SSH_MSG_GLOBAL_REQUEST.decode payload
93
- if message[:'want reply']
94
- # returns always failure because global request is not supported so far
95
- send_request_failure
108
+ begin
109
+ @global_request_handler.request message
110
+ rescue
111
+ if message[:'want reply']
112
+ send_request_failure
113
+ end
114
+ else
115
+ if message[:'want reply']
116
+ send_request_success
117
+ end
96
118
  end
97
119
  end
98
120
 
121
+ def channel_open_start address, port, socket
122
+ @logger.info('channel open start')
123
+ channel = Channel.new self, {:'channel type' => "forwarded-tcpip"}, socket
124
+ @channels[channel.local_channel] = channel
125
+ @logger.info('channel opened')
126
+ message = {
127
+ :'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN::VALUE,
128
+ :'channel type' => "forwarded-tcpip",
129
+ :'sender channel' => channel.local_channel,
130
+ :'initial window size' => channel.local_window_size,
131
+ :'maximum packet size' => channel.local_maximum_packet_size,
132
+ :'address that was connected' => address,
133
+ :'port that was connected' => port,
134
+ :'originator IP address' => socket.remote_address.ip_address,
135
+ :'originator port' => socket.remote_address.ip_port,
136
+ }
137
+ send_channel_open message
138
+ end
139
+
99
140
  def channel_open payload
100
141
  @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN::ID)
101
142
  message = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN.decode payload
102
- channel = Channel.new self, message
103
- @channels[channel.local_channel] = channel
143
+ begin
144
+ channel = Channel.new self, message
145
+ @channels[channel.local_channel] = channel
146
+ channel.start
147
+ send_channel_open_confirmation channel
148
+ rescue => e
149
+ recipient_channel = message[:'sender channel']
150
+ send_channel_open_failure recipient_channel, Message::SSH_MSG_CHANNEL_OPEN_FAILURE::ReasonCode::SSH_OPEN_CONNECT_FAILED, e.message
151
+ end
152
+ end
153
+
154
+ def channel_open_confirmation payload
155
+ @logger.info('received ' + HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::ID)
156
+ message = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.decode payload
157
+ channel = @channels[message[:'recipient channel']]
158
+ channel.set_remote_parameters message
104
159
  channel.start
105
- send_channel_open_confirmation channel
106
160
  end
107
161
 
108
162
  def channel_request payload
@@ -161,6 +215,11 @@ module HrrRbSsh
161
215
  @authentication.send payload
162
216
  end
163
217
 
218
+ def send_channel_open message
219
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN.encode message
220
+ @authentication.send payload
221
+ end
222
+
164
223
  def send_channel_open_confirmation channel
165
224
  message = {
166
225
  :'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE,
@@ -173,5 +232,17 @@ module HrrRbSsh
173
232
  payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.encode message
174
233
  @authentication.send payload
175
234
  end
235
+
236
+ def send_channel_open_failure recipient_channel, reason_code, description
237
+ message = {
238
+ :'message number' => HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_FAILURE::VALUE,
239
+ :'recipient channel' => recipient_channel,
240
+ :'reason code' => reason_code,
241
+ :'description' => description,
242
+ :'language tag' => "",
243
+ }
244
+ payload = HrrRbSsh::Message::SSH_MSG_CHANNEL_OPEN_FAILURE.encode message
245
+ @authentication.send payload
246
+ end
176
247
  end
177
248
  end
@@ -3,6 +3,8 @@
3
3
 
4
4
  module HrrRbSsh
5
5
  class Logger
6
+ @@logger = nil
7
+
6
8
  def self.initialize logger
7
9
  @@logger = logger
8
10
  end
@@ -23,6 +23,65 @@ module HrrRbSsh
23
23
  USR2 = 'USR2'
24
24
  end
25
25
 
26
+ module TerminalMode
27
+ TTY_OP_END = 0
28
+ VINTR = 1
29
+ VQUIT = 2
30
+ VERASE = 3
31
+ VKILL = 4
32
+ VEOF = 5
33
+ VEOL = 6
34
+ VEOL2 = 7
35
+ VSTART = 8
36
+ VSTOP = 9
37
+ VSUSP = 10
38
+ VDSUSP = 11
39
+ VREPRINT = 12
40
+ VWERASE = 13
41
+ VLNEXT = 14
42
+ VFLUSH = 15
43
+ VSWTCH = 16
44
+ VSTATUS = 17
45
+ VDISCARD = 18
46
+ IGNPAR = 30
47
+ PARMRK = 31
48
+ INPCK = 32
49
+ ISTRIP = 33
50
+ INLCR = 34
51
+ IGNCR = 35
52
+ ICRNL = 36
53
+ IUCLC = 37
54
+ IXON = 38
55
+ IXANY = 39
56
+ IXOFF = 40
57
+ IMAXBEL = 41
58
+ ISIG = 50
59
+ ICANON = 51
60
+ XCASE = 52
61
+ ECHO = 53
62
+ ECHOE = 54
63
+ ECHOK = 55
64
+ ECHONL = 56
65
+ NOFLSH = 57
66
+ TOSTOP = 58
67
+ IEXTEN = 59
68
+ ECHOCTL = 60
69
+ ECHOKE = 61
70
+ PENDIN = 62
71
+ OPOST = 70
72
+ OLCUC = 71
73
+ ONLCR = 72
74
+ OCRNL = 73
75
+ ONOCR = 74
76
+ ONLRET = 75
77
+ CS7 = 90
78
+ CS8 = 91
79
+ PARENB = 92
80
+ PARODD = 93
81
+ TTY_OP_ISPEED = 128
82
+ TTY_OP_OSPEED = 129
83
+ end
84
+
26
85
  class << self
27
86
  include Codable
28
87
  end
@@ -30,6 +89,8 @@ module HrrRbSsh
30
89
  ID = self.name.split('::').last
31
90
  VALUE = 98
32
91
 
92
+ TERMINAL_MODE_INV = TerminalMode.constants.map{|c| [TerminalMode.const_get(c), c.to_s]}.to_h
93
+
33
94
  DEFINITION = [
34
95
  #[DataType, Field Name]
35
96
  [DataType::Byte, :'message number'],
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/transport/mac_algorithm/functionable'
5
+
6
+ module HrrRbSsh
7
+ class Transport
8
+ class MacAlgorithm
9
+ class HmacSha2_256 < MacAlgorithm
10
+ NAME = 'hmac-sha2-256'
11
+ PREFERENCE = 50
12
+ DIGEST = 'sha256'
13
+ DIGEST_LENGTH = 32
14
+ KEY_LENGTH = 32
15
+ include Functionable
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ # vim: et ts=2 sw=2
3
+
4
+ require 'hrr_rb_ssh/transport/mac_algorithm/functionable'
5
+
6
+ module HrrRbSsh
7
+ class Transport
8
+ class MacAlgorithm
9
+ class HmacSha2_512 < MacAlgorithm
10
+ NAME = 'hmac-sha2-512'
11
+ PREFERENCE = 60
12
+ DIGEST = 'sha512'
13
+ DIGEST_LENGTH = 64
14
+ KEY_LENGTH = 64
15
+ include Functionable
16
+ end
17
+ end
18
+ end
19
+ end
@@ -19,3 +19,5 @@ require 'hrr_rb_ssh/transport/mac_algorithm/hmac_sha1'
19
19
  require 'hrr_rb_ssh/transport/mac_algorithm/hmac_sha1_96'
20
20
  require 'hrr_rb_ssh/transport/mac_algorithm/hmac_md5'
21
21
  require 'hrr_rb_ssh/transport/mac_algorithm/hmac_md5_96'
22
+ require 'hrr_rb_ssh/transport/mac_algorithm/hmac_sha2_256'
23
+ require 'hrr_rb_ssh/transport/mac_algorithm/hmac_sha2_512'
@@ -2,5 +2,5 @@
2
2
  # vim: et ts=2 sw=2
3
3
 
4
4
  module HrrRbSsh
5
- VERSION = "0.1.5"
5
+ VERSION = "0.1.6"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hrr_rb_ssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - hirura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-27 00:00:00.000000000 Z
11
+ date: 2018-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -127,6 +127,7 @@ files:
127
127
  - lib/hrr_rb_ssh/connection/channel.rb
128
128
  - lib/hrr_rb_ssh/connection/channel/channel_type.rb
129
129
  - lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb
130
+ - lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb
130
131
  - lib/hrr_rb_ssh/connection/channel/channel_type/session.rb
131
132
  - lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain.rb
132
133
  - lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain/chain_context.rb
@@ -143,6 +144,7 @@ files:
143
144
  - lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem/context.rb
144
145
  - lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change.rb
145
146
  - lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change/context.rb
147
+ - lib/hrr_rb_ssh/connection/global_request_handler.rb
146
148
  - lib/hrr_rb_ssh/connection/request_handler.rb
147
149
  - lib/hrr_rb_ssh/connection/request_handler/reference_env_request_handler.rb
148
150
  - lib/hrr_rb_ssh/connection/request_handler/reference_exec_request_handler.rb
@@ -235,6 +237,8 @@ files:
235
237
  - lib/hrr_rb_ssh/transport/mac_algorithm/hmac_md5_96.rb
236
238
  - lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha1.rb
237
239
  - lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha1_96.rb
240
+ - lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha2_256.rb
241
+ - lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha2_512.rb
238
242
  - lib/hrr_rb_ssh/transport/mac_algorithm/none.rb
239
243
  - lib/hrr_rb_ssh/transport/mac_algorithm/unfunctionable.rb
240
244
  - lib/hrr_rb_ssh/transport/mode.rb