hrr_rb_ssh 0.3.0.pre1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -3
- data/.travis.yml +1 -0
- data/README.md +208 -46
- data/demo/client.rb +71 -0
- data/demo/echo_server.rb +8 -3
- data/demo/more_flexible_auth.rb +105 -0
- data/demo/multi_step_auth.rb +99 -0
- data/demo/server.rb +10 -4
- data/demo/subsystem_echo_server.rb +8 -3
- data/hrr_rb_ssh.gemspec +6 -6
- data/lib/hrr_rb_ssh.rb +1 -1
- data/lib/hrr_rb_ssh/algorithm/publickey.rb +0 -1
- data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2.rb +12 -9
- data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/ecdsa_signature_blob.rb +2 -4
- data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/public_key_blob.rb +2 -4
- data/lib/hrr_rb_ssh/algorithm/publickey/ecdsa_sha2/signature.rb +2 -4
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss.rb +10 -7
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/public_key_blob.rb +2 -4
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_dss/signature.rb +2 -4
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa.rb +9 -6
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/public_key_blob.rb +2 -4
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_rsa/signature.rb +2 -4
- data/lib/hrr_rb_ssh/authentication.rb +103 -22
- data/lib/hrr_rb_ssh/authentication/constant.rb +14 -0
- data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive.rb +44 -7
- data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/context.rb +16 -9
- data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_request.rb +7 -6
- data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive/info_response.rb +5 -2
- data/lib/hrr_rb_ssh/authentication/method/none.rb +23 -7
- data/lib/hrr_rb_ssh/authentication/method/none/context.rb +15 -7
- data/lib/hrr_rb_ssh/authentication/method/password.rb +28 -7
- data/lib/hrr_rb_ssh/authentication/method/password/context.rb +16 -7
- data/lib/hrr_rb_ssh/authentication/method/publickey.rb +63 -10
- data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm.rb +0 -1
- data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/functionable.rb +32 -8
- data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/signature_blob.rb +2 -4
- data/lib/hrr_rb_ssh/authentication/method/publickey/context.rb +11 -2
- data/lib/hrr_rb_ssh/client.rb +234 -0
- data/lib/hrr_rb_ssh/codable.rb +15 -13
- data/lib/hrr_rb_ssh/compat/ruby.rb +0 -1
- data/lib/hrr_rb_ssh/connection.rb +145 -75
- data/lib/hrr_rb_ssh/connection/channel.rb +342 -109
- data/lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb +24 -19
- data/lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb +24 -19
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session.rb +19 -12
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain.rb +0 -2
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain/chain_context.rb +0 -3
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env.rb +2 -5
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env/context.rb +5 -4
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec.rb +2 -5
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec/context.rb +5 -4
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req.rb +2 -5
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req/context.rb +5 -4
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell.rb +2 -5
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell/context.rb +5 -4
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem.rb +2 -5
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem/context.rb +5 -4
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change.rb +2 -5
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change/context.rb +5 -4
- data/lib/hrr_rb_ssh/connection/global_request_handler.rb +14 -12
- data/lib/hrr_rb_ssh/connection/request_handler.rb +1 -3
- data/lib/hrr_rb_ssh/connection/request_handler/reference_env_request_handler.rb +0 -2
- data/lib/hrr_rb_ssh/connection/request_handler/reference_exec_request_handler.rb +4 -6
- data/lib/hrr_rb_ssh/connection/request_handler/reference_pty_req_request_handler.rb +10 -12
- data/lib/hrr_rb_ssh/connection/request_handler/reference_shell_request_handler.rb +4 -6
- data/lib/hrr_rb_ssh/connection/request_handler/reference_window_change_request_handler.rb +0 -2
- data/lib/hrr_rb_ssh/error/closed_authentication.rb +1 -1
- data/lib/hrr_rb_ssh/error/closed_connection.rb +1 -1
- data/lib/hrr_rb_ssh/error/closed_transport.rb +1 -1
- data/lib/hrr_rb_ssh/loggable.rb +42 -0
- data/lib/hrr_rb_ssh/message/001_ssh_msg_disconnect.rb +2 -4
- data/lib/hrr_rb_ssh/message/002_ssh_msg_ignore.rb +2 -4
- data/lib/hrr_rb_ssh/message/003_ssh_msg_unimplemented.rb +2 -4
- data/lib/hrr_rb_ssh/message/004_ssh_msg_debug.rb +2 -4
- data/lib/hrr_rb_ssh/message/005_ssh_msg_service_request.rb +2 -4
- data/lib/hrr_rb_ssh/message/006_ssh_msg_service_accept.rb +2 -4
- data/lib/hrr_rb_ssh/message/020_ssh_msg_kexinit.rb +2 -4
- data/lib/hrr_rb_ssh/message/021_ssh_msg_newkeys.rb +2 -4
- data/lib/hrr_rb_ssh/message/030_ssh_msg_kex_dh_gex_request_old.rb +2 -4
- data/lib/hrr_rb_ssh/message/030_ssh_msg_kexdh_init.rb +2 -4
- data/lib/hrr_rb_ssh/message/030_ssh_msg_kexecdh_init.rb +2 -4
- data/lib/hrr_rb_ssh/message/031_ssh_msg_kex_dh_gex_group.rb +2 -4
- data/lib/hrr_rb_ssh/message/031_ssh_msg_kexdh_reply.rb +2 -4
- data/lib/hrr_rb_ssh/message/031_ssh_msg_kexecdh_reply.rb +2 -4
- data/lib/hrr_rb_ssh/message/032_ssh_msg_kex_dh_gex_init.rb +2 -4
- data/lib/hrr_rb_ssh/message/033_ssh_msg_kex_dh_gex_reply.rb +2 -4
- data/lib/hrr_rb_ssh/message/034_ssh_msg_kex_dh_gex_request.rb +2 -4
- data/lib/hrr_rb_ssh/message/050_ssh_msg_userauth_request.rb +2 -4
- data/lib/hrr_rb_ssh/message/051_ssh_msg_userauth_failure.rb +2 -4
- data/lib/hrr_rb_ssh/message/052_ssh_msg_userauth_success.rb +2 -4
- data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_info_request.rb +2 -4
- data/lib/hrr_rb_ssh/message/060_ssh_msg_userauth_pk_ok.rb +2 -4
- data/lib/hrr_rb_ssh/message/061_ssh_msg_userauth_info_response.rb +2 -4
- data/lib/hrr_rb_ssh/message/080_ssh_msg_global_request.rb +2 -4
- data/lib/hrr_rb_ssh/message/081_ssh_msg_request_success.rb +2 -4
- data/lib/hrr_rb_ssh/message/082_ssh_msg_request_failure.rb +2 -4
- data/lib/hrr_rb_ssh/message/090_ssh_msg_channel_open.rb +2 -4
- data/lib/hrr_rb_ssh/message/091_ssh_msg_channel_open_confirmation.rb +2 -4
- data/lib/hrr_rb_ssh/message/092_ssh_msg_channel_open_failure.rb +2 -4
- data/lib/hrr_rb_ssh/message/093_ssh_msg_channel_window_adjust.rb +2 -4
- data/lib/hrr_rb_ssh/message/094_ssh_msg_channel_data.rb +2 -4
- data/lib/hrr_rb_ssh/message/095_ssh_msg_channel_extended_data.rb +2 -4
- data/lib/hrr_rb_ssh/message/096_ssh_msg_channel_eof.rb +2 -4
- data/lib/hrr_rb_ssh/message/097_ssh_msg_channel_close.rb +2 -4
- data/lib/hrr_rb_ssh/message/098_ssh_msg_channel_request.rb +3 -5
- data/lib/hrr_rb_ssh/message/099_ssh_msg_channel_success.rb +2 -4
- data/lib/hrr_rb_ssh/message/100_ssh_msg_channel_failure.rb +2 -4
- data/lib/hrr_rb_ssh/server.rb +16 -10
- data/lib/hrr_rb_ssh/transport.rb +113 -77
- data/lib/hrr_rb_ssh/transport/compression_algorithm/functionable.rb +5 -3
- data/lib/hrr_rb_ssh/transport/compression_algorithm/unfunctionable.rb +5 -3
- data/lib/hrr_rb_ssh/transport/encryption_algorithm/functionable.rb +5 -3
- data/lib/hrr_rb_ssh/transport/encryption_algorithm/unfunctionable.rb +5 -3
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman.rb +43 -37
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman/h0.rb +2 -4
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange.rb +87 -52
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange/h0.rb +2 -4
- data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman.rb +43 -37
- data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman/h0.rb +2 -4
- data/lib/hrr_rb_ssh/transport/mac_algorithm/functionable.rb +5 -3
- data/lib/hrr_rb_ssh/transport/mac_algorithm/unfunctionable.rb +5 -3
- data/lib/hrr_rb_ssh/transport/receiver.rb +8 -7
- data/lib/hrr_rb_ssh/transport/sender.rb +5 -3
- data/lib/hrr_rb_ssh/transport/sequence_number.rb +0 -4
- data/lib/hrr_rb_ssh/transport/server_host_key_algorithm.rb +0 -1
- data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/functionable.rb +5 -3
- data/lib/hrr_rb_ssh/version.rb +1 -1
- metadata +18 -51
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519.rb +0 -61
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key.rb +0 -29
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/openssh_private_key_content.rb +0 -26
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/pkey.rb +0 -158
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/public_key_blob.rb +0 -23
- data/lib/hrr_rb_ssh/algorithm/publickey/ssh_ed25519/signature.rb +0 -23
- data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/ssh_ed25519.rb +0 -21
- data/lib/hrr_rb_ssh/compat/ruby/array.rb +0 -14
- data/lib/hrr_rb_ssh/logger.rb +0 -56
- data/lib/hrr_rb_ssh/transport/server_host_key_algorithm/ssh_ed25519.rb +0 -20
@@ -9,10 +9,8 @@ module HrrRbSsh
|
|
9
9
|
class Method
|
10
10
|
class Publickey
|
11
11
|
class Algorithm
|
12
|
-
|
13
|
-
|
14
|
-
include Codable
|
15
|
-
end
|
12
|
+
class SignatureBlob
|
13
|
+
include Codable
|
16
14
|
DEFINITION = [
|
17
15
|
[DataType::String, :'session identifier'],
|
18
16
|
[DataType::Byte, :'message number'],
|
@@ -1,16 +1,21 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
# vim: et ts=2 sw=2
|
3
3
|
|
4
|
-
require 'hrr_rb_ssh/
|
4
|
+
require 'hrr_rb_ssh/loggable'
|
5
5
|
|
6
6
|
module HrrRbSsh
|
7
7
|
class Authentication
|
8
8
|
class Method
|
9
9
|
class Publickey
|
10
10
|
class Context
|
11
|
+
include Loggable
|
12
|
+
|
11
13
|
attr_reader \
|
12
14
|
:username,
|
13
15
|
:session_id,
|
16
|
+
:variables,
|
17
|
+
:vars,
|
18
|
+
:authentication_methods,
|
14
19
|
:message_number,
|
15
20
|
:service_name,
|
16
21
|
:method_name,
|
@@ -19,11 +24,15 @@ module HrrRbSsh
|
|
19
24
|
:public_key_blob,
|
20
25
|
:signature
|
21
26
|
|
22
|
-
def initialize username, algorithm, session_id, message
|
27
|
+
def initialize username, algorithm, session_id, message, variables, authentication_methods, logger: nil
|
28
|
+
self.logger = logger
|
23
29
|
@username = username
|
24
30
|
@algorithm = algorithm
|
25
31
|
@session_id = session_id
|
26
32
|
@message = message
|
33
|
+
@variables = variables
|
34
|
+
@vars = variables
|
35
|
+
@authentication_methods = authentication_methods
|
27
36
|
|
28
37
|
@message_number = message[:'message number']
|
29
38
|
@service_name = message[:'service name']
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# vim: et ts=2 sw=2
|
3
|
+
|
4
|
+
require 'socket'
|
5
|
+
require 'stringio'
|
6
|
+
require 'hrr_rb_ssh/loggable'
|
7
|
+
require 'hrr_rb_ssh/mode'
|
8
|
+
require 'hrr_rb_ssh/transport'
|
9
|
+
require 'hrr_rb_ssh/authentication'
|
10
|
+
require 'hrr_rb_ssh/connection'
|
11
|
+
|
12
|
+
module HrrRbSsh
|
13
|
+
class Client
|
14
|
+
include Loggable
|
15
|
+
|
16
|
+
def self.start target, options={}
|
17
|
+
client = self.new target, options
|
18
|
+
client.start
|
19
|
+
if block_given?
|
20
|
+
begin
|
21
|
+
yield client
|
22
|
+
ensure
|
23
|
+
client.close unless client.closed?
|
24
|
+
end
|
25
|
+
else
|
26
|
+
client
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize target, tmp_options={}
|
31
|
+
@closed = true
|
32
|
+
options = initialize_options tmp_options
|
33
|
+
io = case target
|
34
|
+
when IO
|
35
|
+
target
|
36
|
+
when Array
|
37
|
+
io = TCPSocket.new *target
|
38
|
+
when String
|
39
|
+
port = 22
|
40
|
+
io = TCPSocket.new target, port
|
41
|
+
end
|
42
|
+
transport = HrrRbSsh::Transport.new io, HrrRbSsh::Mode::CLIENT, options, logger: logger
|
43
|
+
authentication = HrrRbSsh::Authentication.new transport, HrrRbSsh::Mode::CLIENT, options, logger: logger
|
44
|
+
@connection = HrrRbSsh::Connection.new authentication, HrrRbSsh::Mode::CLIENT, options, logger: logger
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize_options tmp_options
|
48
|
+
tmp_options = Hash[tmp_options.map{|k, v| [k.to_s, v]}]
|
49
|
+
self.logger = tmp_options['logger']
|
50
|
+
options = {}
|
51
|
+
options['username'] = tmp_options['username']
|
52
|
+
options['authentication_preferred_authentication_methods'] = tmp_options['authentication_preferred_authentication_methods']
|
53
|
+
options['client_authentication_password'] = tmp_options['password']
|
54
|
+
options['client_authentication_publickey'] = tmp_options['publickey']
|
55
|
+
options['client_authentication_keyboard_interactive'] = tmp_options['keyboard_interactive']
|
56
|
+
options['transport_preferred_encryption_algorithms'] = tmp_options['transport_preferred_encryption_algorithms']
|
57
|
+
options['transport_preferred_server_host_key_algorithms'] = tmp_options['transport_preferred_server_host_key_algorithms']
|
58
|
+
options['transport_preferred_kex_algorithms'] = tmp_options['transport_preferred_kex_algorithms']
|
59
|
+
options['transport_preferred_mac_algorithms'] = tmp_options['transport_preferred_mac_algorithms']
|
60
|
+
options['transport_preferred_compression_algorithms'] = tmp_options['transport_preferred_compression_algorithms']
|
61
|
+
options
|
62
|
+
end
|
63
|
+
|
64
|
+
def start
|
65
|
+
@connection.start foreground: false
|
66
|
+
@closed = false
|
67
|
+
end
|
68
|
+
|
69
|
+
def loop
|
70
|
+
@connection.loop
|
71
|
+
end
|
72
|
+
|
73
|
+
def closed?
|
74
|
+
@closed
|
75
|
+
end
|
76
|
+
|
77
|
+
def close
|
78
|
+
log_info { "closing client" }
|
79
|
+
@closed = true
|
80
|
+
@connection.close
|
81
|
+
log_info { "client closed" }
|
82
|
+
end
|
83
|
+
|
84
|
+
def exec! command, pty: false, env: {}
|
85
|
+
log_info { "start exec!: #{command}" }
|
86
|
+
out_buf = StringIO.new
|
87
|
+
err_buf = StringIO.new
|
88
|
+
begin
|
89
|
+
log_info { "Opning channel" }
|
90
|
+
channel = @connection.request_channel_open "session"
|
91
|
+
log_info { "Channel opened" }
|
92
|
+
if pty
|
93
|
+
channel.send_channel_request_pty_req 'xterm', 80, 24, 580, 336, ''
|
94
|
+
end
|
95
|
+
env.each{ |variable_name, variable_value|
|
96
|
+
channel.send_channel_request_env variable_name, variable_value
|
97
|
+
}
|
98
|
+
channel.send_channel_request_exec command
|
99
|
+
out_t = Thread.new {
|
100
|
+
while true
|
101
|
+
begin
|
102
|
+
out_buf.write channel.io[1].readpartial(10240)
|
103
|
+
rescue
|
104
|
+
break
|
105
|
+
end
|
106
|
+
end
|
107
|
+
}
|
108
|
+
err_t = Thread.new {
|
109
|
+
while true
|
110
|
+
begin
|
111
|
+
err_buf.write channel.io[2].readpartial(10240)
|
112
|
+
rescue
|
113
|
+
break
|
114
|
+
end
|
115
|
+
end
|
116
|
+
}
|
117
|
+
[out_t, err_t].each{ |t|
|
118
|
+
begin
|
119
|
+
t.join
|
120
|
+
rescue => e
|
121
|
+
log_warn { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
122
|
+
end
|
123
|
+
}
|
124
|
+
rescue => e
|
125
|
+
log_error { "Failed opening channel" }
|
126
|
+
log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
127
|
+
raise "Error in exec!"
|
128
|
+
ensure
|
129
|
+
if channel
|
130
|
+
log_info { "closing channel IOs" }
|
131
|
+
channel.io.each{ |io| io.close rescue nil }
|
132
|
+
log_info { "channel IOs closed" }
|
133
|
+
log_info { "closing channel" }
|
134
|
+
log_info { "wait until threads closed in channel" }
|
135
|
+
channel.wait_until_closed
|
136
|
+
channel.close
|
137
|
+
log_info { "channel closed" }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
[out_buf.string, err_buf.string]
|
141
|
+
end
|
142
|
+
|
143
|
+
def exec command, pty: false, env: {}
|
144
|
+
log_info { "start exec: #{command}" }
|
145
|
+
begin
|
146
|
+
log_info { "Opning channel" }
|
147
|
+
channel = @connection.request_channel_open "session"
|
148
|
+
log_info { "Channel opened" }
|
149
|
+
if pty
|
150
|
+
channel.send_channel_request_pty_req 'xterm', 80, 24, 580, 336, ''
|
151
|
+
end
|
152
|
+
env.each{ |variable_name, variable_value|
|
153
|
+
channel.send_channel_request_env variable_name, variable_value
|
154
|
+
}
|
155
|
+
channel.send_channel_request_exec command
|
156
|
+
yield channel.io
|
157
|
+
rescue => e
|
158
|
+
log_error { "Failed opening channel" }
|
159
|
+
log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
160
|
+
raise "Error in shell"
|
161
|
+
ensure
|
162
|
+
if channel
|
163
|
+
log_info { "closing channel IOs" }
|
164
|
+
channel.io.each{ |io| io.close rescue nil }
|
165
|
+
log_info { "channel IOs closed" }
|
166
|
+
log_info { "closing channel" }
|
167
|
+
log_info { "wait until threads closed in channel" }
|
168
|
+
channel.wait_until_closed
|
169
|
+
channel.close
|
170
|
+
log_info { "channel closed" }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
channel_exit_status = channel.exit_status rescue nil
|
174
|
+
end
|
175
|
+
|
176
|
+
def shell env: {}
|
177
|
+
log_info { "start shell" }
|
178
|
+
begin
|
179
|
+
log_info { "Opning channel" }
|
180
|
+
channel = @connection.request_channel_open "session"
|
181
|
+
log_info { "Channel opened" }
|
182
|
+
channel.send_channel_request_pty_req 'xterm', 80, 24, 580, 336, ''
|
183
|
+
env.each{ |variable_name, variable_value|
|
184
|
+
channel.send_channel_request_env variable_name, variable_value
|
185
|
+
}
|
186
|
+
channel.send_channel_request_shell
|
187
|
+
yield channel.io
|
188
|
+
rescue => e
|
189
|
+
log_error { "Failed opening channel" }
|
190
|
+
log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
191
|
+
raise "Error in shell"
|
192
|
+
ensure
|
193
|
+
if channel
|
194
|
+
log_info { "closing channel IOs" }
|
195
|
+
channel.io.each{ |io| io.close rescue nil }
|
196
|
+
log_info { "channel IOs closed" }
|
197
|
+
log_info { "closing channel" }
|
198
|
+
log_info { "wait until threads closed in channel" }
|
199
|
+
channel.wait_until_closed
|
200
|
+
channel.close
|
201
|
+
log_info { "channel closed" }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
channel_exit_status = channel.exit_status rescue nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def subsystem name
|
208
|
+
log_info { "start subsystem" }
|
209
|
+
begin
|
210
|
+
log_info { "Opning channel" }
|
211
|
+
channel = @connection.request_channel_open "session"
|
212
|
+
log_info { "Channel opened" }
|
213
|
+
channel.send_channel_request_subsystem name
|
214
|
+
yield channel.io
|
215
|
+
rescue => e
|
216
|
+
log_error { "Failed opening channel" }
|
217
|
+
log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
218
|
+
raise "Error in subsystem"
|
219
|
+
ensure
|
220
|
+
if channel
|
221
|
+
log_info { "closing channel IOs" }
|
222
|
+
channel.io.each{ |io| io.close rescue nil }
|
223
|
+
log_info { "channel IOs closed" }
|
224
|
+
log_info { "closing channel" }
|
225
|
+
log_info { "wait until threads closed in channel" }
|
226
|
+
channel.wait_until_closed
|
227
|
+
channel.close
|
228
|
+
log_info { "channel closed" }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
channel_exit_status = channel.exit_status rescue nil
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
data/lib/hrr_rb_ssh/codable.rb
CHANGED
@@ -1,36 +1,38 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
# vim: et ts=2 sw=2
|
3
3
|
|
4
|
-
require 'hrr_rb_ssh/
|
4
|
+
require 'hrr_rb_ssh/loggable'
|
5
5
|
|
6
6
|
module HrrRbSsh
|
7
7
|
module Codable
|
8
|
-
|
9
|
-
|
8
|
+
include Loggable
|
9
|
+
|
10
|
+
def initialize logger: nil
|
11
|
+
self.logger = logger
|
10
12
|
end
|
11
13
|
|
12
14
|
def common_definition
|
13
|
-
self::DEFINITION
|
15
|
+
self.class::DEFINITION
|
14
16
|
end
|
15
17
|
|
16
18
|
def conditional_definition message
|
17
|
-
return [] unless self.const_defined? :CONDITIONAL_DEFINITION
|
19
|
+
return [] unless self.class.const_defined? :CONDITIONAL_DEFINITION
|
18
20
|
message.inject([]){ |a, (k,v)|
|
19
21
|
field_name = k
|
20
22
|
field_value = if v.instance_of? ::Proc then v.call else v end
|
21
|
-
a + (self::CONDITIONAL_DEFINITION.fetch(field_name, {})[field_value] || [])
|
23
|
+
a + (self.class::CONDITIONAL_DEFINITION.fetch(field_name, {})[field_value] || [])
|
22
24
|
}
|
23
25
|
end
|
24
26
|
|
25
27
|
def encode message, complementary_message={}
|
26
|
-
|
28
|
+
log_debug { 'encoding message: ' + message.inspect }
|
27
29
|
definition = common_definition + conditional_definition(message.merge complementary_message)
|
28
30
|
definition.map{ |data_type, field_name|
|
29
31
|
begin
|
30
32
|
field_value = if message[field_name].instance_of? ::Proc then message[field_name].call else message[field_name] end
|
31
|
-
data_type.encode
|
33
|
+
data_type.encode field_value
|
32
34
|
rescue => e
|
33
|
-
|
35
|
+
log_debug { "'field_name', 'field_value': #{field_name.inspect}, #{field_value.inspect}" }
|
34
36
|
raise e
|
35
37
|
end
|
36
38
|
}.join
|
@@ -49,7 +51,7 @@ module HrrRbSsh
|
|
49
51
|
decoded_message = definition.map{ |data_type, field_name|
|
50
52
|
[
|
51
53
|
field_name,
|
52
|
-
data_type.decode(
|
54
|
+
data_type.decode(payload_io)
|
53
55
|
]
|
54
56
|
}
|
55
57
|
decoded_message + decode_recursively(payload_io, decoded_message)
|
@@ -58,11 +60,11 @@ module HrrRbSsh
|
|
58
60
|
|
59
61
|
def decode payload, complementary_message={}
|
60
62
|
payload_io = StringIO.new payload
|
61
|
-
decoded_message = decode_recursively(payload_io).
|
63
|
+
decoded_message = decode_recursively(payload_io).inject(Hash.new){ |h, (k, v)| h.update({k => v}) }
|
62
64
|
if complementary_message.any?
|
63
|
-
decoded_message.merge! decode_recursively(payload_io, complementary_message.to_a).
|
65
|
+
decoded_message.merge! decode_recursively(payload_io, complementary_message.to_a).inject(Hash.new){ |h, (k, v)| h.update({k => v}) }
|
64
66
|
end
|
65
|
-
|
67
|
+
log_debug { 'decoded message: ' + decoded_message.inspect }
|
66
68
|
decoded_message
|
67
69
|
end
|
68
70
|
end
|
@@ -1,26 +1,30 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
# vim: et ts=2 sw=2
|
3
3
|
|
4
|
-
require 'hrr_rb_ssh/
|
4
|
+
require 'hrr_rb_ssh/loggable'
|
5
5
|
require 'hrr_rb_ssh/error/closed_connection'
|
6
6
|
require 'hrr_rb_ssh/connection/global_request_handler'
|
7
7
|
require 'hrr_rb_ssh/connection/channel'
|
8
8
|
|
9
9
|
module HrrRbSsh
|
10
10
|
class Connection
|
11
|
+
include Loggable
|
12
|
+
|
11
13
|
attr_reader \
|
12
14
|
:username,
|
13
|
-
:
|
14
|
-
|
15
|
-
|
16
|
-
@logger = Logger.new self.class.name
|
15
|
+
:variables,
|
16
|
+
:options,
|
17
|
+
:mode
|
17
18
|
|
19
|
+
def initialize authentication, mode, options={}, logger: nil
|
20
|
+
self.logger = logger
|
18
21
|
@authentication = authentication
|
22
|
+
@mode = mode
|
19
23
|
@options = options
|
20
|
-
|
21
|
-
@global_request_handler = GlobalRequestHandler.new self
|
24
|
+
@global_request_handler = GlobalRequestHandler.new self, logger: logger
|
22
25
|
@channels = Hash.new
|
23
26
|
@username = nil
|
27
|
+
@variables = nil
|
24
28
|
@closed = nil
|
25
29
|
end
|
26
30
|
|
@@ -36,75 +40,103 @@ module HrrRbSsh
|
|
36
40
|
def assign_channel
|
37
41
|
i = 0
|
38
42
|
res = nil
|
39
|
-
|
43
|
+
while true
|
40
44
|
break unless @channels.keys.include?(i)
|
41
45
|
i += 1
|
42
46
|
end
|
43
47
|
i
|
44
48
|
end
|
45
49
|
|
46
|
-
def start
|
47
|
-
|
50
|
+
def start foreground: true
|
51
|
+
log_info { "start connection" }
|
52
|
+
begin
|
53
|
+
@authentication.start
|
54
|
+
rescue Error::ClosedAuthentication
|
55
|
+
close
|
56
|
+
raise Error::ClosedConnection
|
57
|
+
end
|
48
58
|
@closed = false
|
49
|
-
|
59
|
+
@connection_loop_thread = connection_loop_thread
|
60
|
+
if foreground
|
61
|
+
@connection_loop_thread.join
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def loop
|
66
|
+
@connection_loop_thread.join
|
50
67
|
end
|
51
68
|
|
52
69
|
def close
|
70
|
+
return if @closed
|
71
|
+
log_info { "close connection" }
|
53
72
|
@closed = true
|
73
|
+
@authentication.close
|
54
74
|
@channels.values.each do |channel|
|
55
75
|
begin
|
56
76
|
channel.close
|
57
77
|
rescue => e
|
58
|
-
|
78
|
+
log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
59
79
|
end
|
60
80
|
end
|
61
81
|
@channels.clear
|
62
82
|
@global_request_handler.close
|
83
|
+
@connection_loop_thread.join if @connection_loop_thread && @connection_loop_thread != Thread.current
|
84
|
+
log_info { "connection closed" }
|
63
85
|
end
|
64
86
|
|
65
87
|
def closed?
|
66
88
|
@closed
|
67
89
|
end
|
68
90
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
91
|
+
def connection_loop_thread
|
92
|
+
log_info { "start connection loop" }
|
93
|
+
Thread.new do
|
72
94
|
begin
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
while true
|
96
|
+
begin
|
97
|
+
payload = @authentication.receive
|
98
|
+
rescue Error::ClosedAuthentication => e
|
99
|
+
log_info { "authentication closed" }
|
100
|
+
break
|
101
|
+
end
|
102
|
+
@username ||= @authentication.username
|
103
|
+
@variables ||= @authentication.variables
|
104
|
+
case payload[0,1].unpack("C")[0]
|
105
|
+
when Message::SSH_MSG_GLOBAL_REQUEST::VALUE
|
106
|
+
global_request payload
|
107
|
+
when Message::SSH_MSG_CHANNEL_OPEN::VALUE
|
108
|
+
channel_open payload
|
109
|
+
when Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE
|
110
|
+
channel_open_confirmation payload
|
111
|
+
when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
|
112
|
+
channel_request payload
|
113
|
+
when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
|
114
|
+
channel_window_adjust payload
|
115
|
+
when Message::SSH_MSG_CHANNEL_DATA::VALUE
|
116
|
+
channel_data payload
|
117
|
+
when Message::SSH_MSG_CHANNEL_EXTENDED_DATA::VALUE
|
118
|
+
channel_extended_data payload
|
119
|
+
when Message::SSH_MSG_CHANNEL_EOF::VALUE
|
120
|
+
channel_eof payload
|
121
|
+
when Message::SSH_MSG_CHANNEL_CLOSE::VALUE
|
122
|
+
channel_close payload
|
123
|
+
else
|
124
|
+
log_warn { "received unsupported message: id: #{payload[0,1].unpack("C")[0]}" }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
rescue => e
|
128
|
+
log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
129
|
+
ensure
|
130
|
+
log_info { "closing connection loop" }
|
131
|
+
close
|
132
|
+
log_info { "connection loop closed" }
|
98
133
|
end
|
99
134
|
end
|
100
|
-
@logger.info { "closing connection" }
|
101
|
-
close
|
102
|
-
@logger.info { "connection closed" }
|
103
135
|
end
|
104
136
|
|
105
137
|
def global_request payload
|
106
|
-
|
107
|
-
message = Message::SSH_MSG_GLOBAL_REQUEST.decode payload
|
138
|
+
log_info { 'received ' + Message::SSH_MSG_GLOBAL_REQUEST::ID }
|
139
|
+
message = Message::SSH_MSG_GLOBAL_REQUEST.new(logger: logger).decode payload
|
108
140
|
begin
|
109
141
|
@global_request_handler.request message
|
110
142
|
rescue
|
@@ -119,10 +151,10 @@ module HrrRbSsh
|
|
119
151
|
end
|
120
152
|
|
121
153
|
def channel_open_start address, port, socket
|
122
|
-
|
123
|
-
channel = Channel.new self, {:'channel type' => "forwarded-tcpip"}, socket
|
154
|
+
log_info { 'channel open start' }
|
155
|
+
channel = Channel.new self, {:'channel type' => "forwarded-tcpip"}, socket, logger: logger
|
124
156
|
@channels[channel.local_channel] = channel
|
125
|
-
|
157
|
+
log_info { 'channel opened' }
|
126
158
|
message = {
|
127
159
|
:'message number' => Message::SSH_MSG_CHANNEL_OPEN::VALUE,
|
128
160
|
:'channel type' => "forwarded-tcpip",
|
@@ -138,72 +170,110 @@ module HrrRbSsh
|
|
138
170
|
end
|
139
171
|
|
140
172
|
def channel_open payload
|
141
|
-
|
142
|
-
message = Message::SSH_MSG_CHANNEL_OPEN.decode payload
|
173
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN::ID }
|
174
|
+
message = Message::SSH_MSG_CHANNEL_OPEN.new(logger: logger).decode payload
|
143
175
|
begin
|
144
|
-
channel = Channel.new self, message
|
176
|
+
channel = Channel.new self, message, logger: logger
|
145
177
|
@channels[channel.local_channel] = channel
|
146
178
|
channel.start
|
147
179
|
send_channel_open_confirmation channel
|
148
180
|
rescue => e
|
181
|
+
log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
149
182
|
recipient_channel = message[:'sender channel']
|
150
183
|
send_channel_open_failure recipient_channel, Message::SSH_MSG_CHANNEL_OPEN_FAILURE::ReasonCode::SSH_OPEN_CONNECT_FAILED, e.message
|
151
184
|
end
|
152
185
|
end
|
153
186
|
|
187
|
+
def request_channel_open channel_type, channel_specific_message={}, wait_response=true
|
188
|
+
log_info { 'request channel open' }
|
189
|
+
case channel_type
|
190
|
+
when "session"
|
191
|
+
channel = Channel.new self, {:'channel type' => channel_type}, logger: logger
|
192
|
+
@channels[channel.local_channel] = channel
|
193
|
+
end
|
194
|
+
message = {
|
195
|
+
:'message number' => Message::SSH_MSG_CHANNEL_OPEN::VALUE,
|
196
|
+
:'channel type' => channel_type,
|
197
|
+
:'sender channel' => channel.local_channel,
|
198
|
+
:'initial window size' => channel.local_window_size,
|
199
|
+
:'maximum packet size' => channel.local_maximum_packet_size,
|
200
|
+
}
|
201
|
+
send_channel_open message.merge(channel_specific_message)
|
202
|
+
log_info { 'sent channel open' }
|
203
|
+
if wait_response
|
204
|
+
log_info { 'wait response' }
|
205
|
+
channel.wait_until_started
|
206
|
+
end
|
207
|
+
unless channel.closed?
|
208
|
+
log_info { 'channel opened' }
|
209
|
+
channel
|
210
|
+
else
|
211
|
+
raise "Faild opening channel"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
154
215
|
def channel_open_confirmation payload
|
155
|
-
|
156
|
-
message = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.decode payload
|
216
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::ID }
|
217
|
+
message = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.new(logger: logger).decode payload
|
157
218
|
channel = @channels[message[:'recipient channel']]
|
158
219
|
channel.set_remote_parameters message
|
159
220
|
channel.start
|
160
221
|
end
|
161
222
|
|
162
223
|
def channel_request payload
|
163
|
-
|
164
|
-
message = Message::SSH_MSG_CHANNEL_REQUEST.decode payload
|
224
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_REQUEST::ID }
|
225
|
+
message = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).decode payload
|
165
226
|
local_channel = message[:'recipient channel']
|
166
|
-
@channels[local_channel].receive_message_queue.enq message
|
227
|
+
@channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
|
167
228
|
end
|
168
229
|
|
169
230
|
def channel_window_adjust payload
|
170
|
-
|
171
|
-
message = Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.decode payload
|
231
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::ID }
|
232
|
+
message = Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.new(logger: logger).decode payload
|
172
233
|
local_channel = message[:'recipient channel']
|
173
|
-
@channels[local_channel].receive_message_queue.enq message
|
234
|
+
@channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
|
174
235
|
end
|
175
236
|
|
176
237
|
def channel_data payload
|
177
|
-
|
178
|
-
message = Message::SSH_MSG_CHANNEL_DATA.decode payload
|
238
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_DATA::ID }
|
239
|
+
message = Message::SSH_MSG_CHANNEL_DATA.new(logger: logger).decode payload
|
179
240
|
local_channel = message[:'recipient channel']
|
180
|
-
@channels[local_channel].receive_message_queue.enq message
|
241
|
+
@channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
|
242
|
+
end
|
243
|
+
|
244
|
+
def channel_extended_data payload
|
245
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_EXTENDED_DATA::ID }
|
246
|
+
message = Message::SSH_MSG_CHANNEL_EXTENDED_DATA.new(logger: logger).decode payload
|
247
|
+
local_channel = message[:'recipient channel']
|
248
|
+
@channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
|
181
249
|
end
|
182
250
|
|
183
251
|
def channel_eof payload
|
184
|
-
|
185
|
-
message = Message::SSH_MSG_CHANNEL_EOF.decode payload
|
252
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_EOF::ID }
|
253
|
+
message = Message::SSH_MSG_CHANNEL_EOF.new(logger: logger).decode payload
|
186
254
|
local_channel = message[:'recipient channel']
|
187
|
-
|
188
|
-
channel.receive_message_queue.close
|
255
|
+
@channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
|
189
256
|
end
|
190
257
|
|
191
258
|
def channel_close payload
|
192
|
-
|
193
|
-
message = Message::SSH_MSG_CHANNEL_CLOSE.decode payload
|
259
|
+
log_info { 'received ' + Message::SSH_MSG_CHANNEL_CLOSE::ID }
|
260
|
+
message = Message::SSH_MSG_CHANNEL_CLOSE.new(logger: logger).decode payload
|
194
261
|
local_channel = message[:'recipient channel']
|
195
262
|
channel = @channels[local_channel]
|
196
263
|
channel.close
|
197
|
-
|
264
|
+
log_info { "wait until threads closed in channel" }
|
265
|
+
channel.wait_until_closed
|
266
|
+
log_info { "channel closed" }
|
267
|
+
log_info { "deleting channel" }
|
198
268
|
@channels.delete local_channel
|
199
|
-
|
269
|
+
log_info { "channel deleted" }
|
200
270
|
end
|
201
271
|
|
202
272
|
def send_request_success
|
203
273
|
message = {
|
204
274
|
:'message number' => Message::SSH_MSG_REQUEST_SUCCESS::VALUE,
|
205
275
|
}
|
206
|
-
payload = Message::SSH_MSG_REQUEST_SUCCESS.encode message
|
276
|
+
payload = Message::SSH_MSG_REQUEST_SUCCESS.new(logger: logger).encode message
|
207
277
|
@authentication.send payload
|
208
278
|
end
|
209
279
|
|
@@ -211,12 +281,12 @@ module HrrRbSsh
|
|
211
281
|
message = {
|
212
282
|
:'message number' => Message::SSH_MSG_REQUEST_FAILURE::VALUE,
|
213
283
|
}
|
214
|
-
payload = Message::SSH_MSG_REQUEST_FAILURE.encode message
|
284
|
+
payload = Message::SSH_MSG_REQUEST_FAILURE.new(logger: logger).encode message
|
215
285
|
@authentication.send payload
|
216
286
|
end
|
217
287
|
|
218
288
|
def send_channel_open message
|
219
|
-
payload = Message::SSH_MSG_CHANNEL_OPEN.encode message
|
289
|
+
payload = Message::SSH_MSG_CHANNEL_OPEN.new(logger: logger).encode message
|
220
290
|
@authentication.send payload
|
221
291
|
end
|
222
292
|
|
@@ -229,7 +299,7 @@ module HrrRbSsh
|
|
229
299
|
:'initial window size' => channel.local_window_size,
|
230
300
|
:'maximum packet size' => channel.local_maximum_packet_size,
|
231
301
|
}
|
232
|
-
payload = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.encode message
|
302
|
+
payload = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.new(logger: logger).encode message
|
233
303
|
@authentication.send payload
|
234
304
|
end
|
235
305
|
|
@@ -241,7 +311,7 @@ module HrrRbSsh
|
|
241
311
|
:'description' => description,
|
242
312
|
:'language tag' => "",
|
243
313
|
}
|
244
|
-
payload = Message::SSH_MSG_CHANNEL_OPEN_FAILURE.encode message
|
314
|
+
payload = Message::SSH_MSG_CHANNEL_OPEN_FAILURE.new(logger: logger).encode message
|
245
315
|
@authentication.send payload
|
246
316
|
end
|
247
317
|
end
|