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 +4 -4
- data/demo/server.rb +1 -1
- data/lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb +6 -6
- data/lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb +98 -0
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session.rb +5 -2
- data/lib/hrr_rb_ssh/connection/channel/channel_type.rb +1 -0
- data/lib/hrr_rb_ssh/connection/channel.rb +102 -29
- data/lib/hrr_rb_ssh/connection/global_request_handler.rb +90 -0
- data/lib/hrr_rb_ssh/connection/request_handler/reference_exec_request_handler.rb +2 -1
- data/lib/hrr_rb_ssh/connection/request_handler/reference_pty_req_request_handler.rb +2 -0
- data/lib/hrr_rb_ssh/connection/request_handler/reference_shell_request_handler.rb +6 -2
- data/lib/hrr_rb_ssh/connection.rb +77 -6
- data/lib/hrr_rb_ssh/logger.rb +2 -0
- data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +61 -0
- data/lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha2_256.rb +19 -0
- data/lib/hrr_rb_ssh/transport/mac_algorithm/hmac_sha2_512.rb +19 -0
- data/lib/hrr_rb_ssh/transport/mac_algorithm.rb +2 -0
- data/lib/hrr_rb_ssh/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20686d9dca5a433f36d523d681d18ce280d0dca33f90ebe205d98a1f4dc80980
|
4
|
+
data.tar.gz: bd3c9514d667d7780735663ee258506455d6832ce85ee19d8c136aa4973832d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
47
|
+
@channel.io[1].write s.readpartial(10240)
|
48
48
|
rescue EOFError
|
49
49
|
@logger.info("socket is EOF")
|
50
|
-
@channel.
|
50
|
+
@channel.io[1].close
|
51
51
|
break
|
52
52
|
rescue IOError
|
53
53
|
@logger.info("socket is closed")
|
54
|
-
@channel.
|
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.
|
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.
|
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.
|
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
|
@@ -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 =
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
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
|
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 @
|
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 = @
|
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
|
-
@
|
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
|
-
@
|
230
|
+
@w_io_in.close_write
|
178
231
|
@logger.info("channel IO write closed")
|
179
232
|
break
|
180
233
|
end
|
181
|
-
@
|
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
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
103
|
-
|
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
|
data/lib/hrr_rb_ssh/logger.rb
CHANGED
@@ -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'
|
data/lib/hrr_rb_ssh/version.rb
CHANGED
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.
|
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-
|
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
|