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 +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
|