hrr_rb_ssh 0.3.0.pre1 → 0.4.2
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/.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
|