hrr_rb_ssh 0.1.5 → 0.1.6

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