minmb-net-ssh 2.5.1

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.
Files changed (137) hide show
  1. data/CHANGELOG.rdoc +291 -0
  2. data/Manifest +132 -0
  3. data/README.rdoc +184 -0
  4. data/Rakefile +86 -0
  5. data/Rudyfile +96 -0
  6. data/THANKS.rdoc +19 -0
  7. data/lib/net/ssh.rb +223 -0
  8. data/lib/net/ssh/authentication/agent.rb +23 -0
  9. data/lib/net/ssh/authentication/agent/java_pageant.rb +85 -0
  10. data/lib/net/ssh/authentication/agent/socket.rb +170 -0
  11. data/lib/net/ssh/authentication/constants.rb +18 -0
  12. data/lib/net/ssh/authentication/key_manager.rb +253 -0
  13. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  14. data/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  15. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  16. data/lib/net/ssh/authentication/methods/password.rb +43 -0
  17. data/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  18. data/lib/net/ssh/authentication/pageant.rb +301 -0
  19. data/lib/net/ssh/authentication/session.rb +154 -0
  20. data/lib/net/ssh/buffer.rb +350 -0
  21. data/lib/net/ssh/buffered_io.rb +207 -0
  22. data/lib/net/ssh/config.rb +207 -0
  23. data/lib/net/ssh/connection/channel.rb +630 -0
  24. data/lib/net/ssh/connection/constants.rb +33 -0
  25. data/lib/net/ssh/connection/session.rb +603 -0
  26. data/lib/net/ssh/connection/term.rb +178 -0
  27. data/lib/net/ssh/errors.rb +88 -0
  28. data/lib/net/ssh/key_factory.rb +107 -0
  29. data/lib/net/ssh/known_hosts.rb +141 -0
  30. data/lib/net/ssh/loggable.rb +61 -0
  31. data/lib/net/ssh/packet.rb +102 -0
  32. data/lib/net/ssh/prompt.rb +93 -0
  33. data/lib/net/ssh/proxy/command.rb +75 -0
  34. data/lib/net/ssh/proxy/errors.rb +14 -0
  35. data/lib/net/ssh/proxy/http.rb +94 -0
  36. data/lib/net/ssh/proxy/socks4.rb +70 -0
  37. data/lib/net/ssh/proxy/socks5.rb +142 -0
  38. data/lib/net/ssh/ruby_compat.rb +77 -0
  39. data/lib/net/ssh/service/forward.rb +327 -0
  40. data/lib/net/ssh/test.rb +89 -0
  41. data/lib/net/ssh/test/channel.rb +129 -0
  42. data/lib/net/ssh/test/extensions.rb +152 -0
  43. data/lib/net/ssh/test/kex.rb +44 -0
  44. data/lib/net/ssh/test/local_packet.rb +51 -0
  45. data/lib/net/ssh/test/packet.rb +81 -0
  46. data/lib/net/ssh/test/remote_packet.rb +38 -0
  47. data/lib/net/ssh/test/script.rb +157 -0
  48. data/lib/net/ssh/test/socket.rb +64 -0
  49. data/lib/net/ssh/transport/algorithms.rb +407 -0
  50. data/lib/net/ssh/transport/cipher_factory.rb +106 -0
  51. data/lib/net/ssh/transport/constants.rb +32 -0
  52. data/lib/net/ssh/transport/ctr.rb +95 -0
  53. data/lib/net/ssh/transport/hmac.rb +45 -0
  54. data/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  55. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  56. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  57. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  58. data/lib/net/ssh/transport/hmac/ripemd160.rb +13 -0
  59. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  60. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  61. data/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  62. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  63. data/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  66. data/lib/net/ssh/transport/kex.rb +28 -0
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +44 -0
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +216 -0
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  70. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +93 -0
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +13 -0
  73. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +13 -0
  74. data/lib/net/ssh/transport/key_expander.rb +26 -0
  75. data/lib/net/ssh/transport/openssl.rb +237 -0
  76. data/lib/net/ssh/transport/packet_stream.rb +235 -0
  77. data/lib/net/ssh/transport/server_version.rb +71 -0
  78. data/lib/net/ssh/transport/session.rb +278 -0
  79. data/lib/net/ssh/transport/state.rb +206 -0
  80. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  81. data/lib/net/ssh/verifiers/null.rb +12 -0
  82. data/lib/net/ssh/verifiers/strict.rb +53 -0
  83. data/lib/net/ssh/version.rb +62 -0
  84. data/net-ssh.gemspec +164 -0
  85. data/setup.rb +1585 -0
  86. data/support/arcfour_check.rb +20 -0
  87. data/support/ssh_tunnel_bug.rb +65 -0
  88. data/test/authentication/methods/common.rb +28 -0
  89. data/test/authentication/methods/test_abstract.rb +51 -0
  90. data/test/authentication/methods/test_hostbased.rb +114 -0
  91. data/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  92. data/test/authentication/methods/test_password.rb +52 -0
  93. data/test/authentication/methods/test_publickey.rb +148 -0
  94. data/test/authentication/test_agent.rb +205 -0
  95. data/test/authentication/test_key_manager.rb +218 -0
  96. data/test/authentication/test_session.rb +106 -0
  97. data/test/common.rb +107 -0
  98. data/test/configs/eqsign +3 -0
  99. data/test/configs/exact_match +8 -0
  100. data/test/configs/host_plus +10 -0
  101. data/test/configs/multihost +4 -0
  102. data/test/configs/wild_cards +14 -0
  103. data/test/connection/test_channel.rb +467 -0
  104. data/test/connection/test_session.rb +488 -0
  105. data/test/known_hosts/github +1 -0
  106. data/test/test_all.rb +9 -0
  107. data/test/test_buffer.rb +426 -0
  108. data/test/test_buffered_io.rb +63 -0
  109. data/test/test_config.rb +120 -0
  110. data/test/test_key_factory.rb +121 -0
  111. data/test/test_known_hosts.rb +13 -0
  112. data/test/transport/hmac/test_md5.rb +39 -0
  113. data/test/transport/hmac/test_md5_96.rb +25 -0
  114. data/test/transport/hmac/test_none.rb +34 -0
  115. data/test/transport/hmac/test_ripemd160.rb +34 -0
  116. data/test/transport/hmac/test_sha1.rb +34 -0
  117. data/test/transport/hmac/test_sha1_96.rb +25 -0
  118. data/test/transport/hmac/test_sha2_256.rb +35 -0
  119. data/test/transport/hmac/test_sha2_256_96.rb +25 -0
  120. data/test/transport/hmac/test_sha2_512.rb +35 -0
  121. data/test/transport/hmac/test_sha2_512_96.rb +25 -0
  122. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +13 -0
  123. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  124. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  125. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  126. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +161 -0
  127. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +37 -0
  128. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +37 -0
  129. data/test/transport/test_algorithms.rb +330 -0
  130. data/test/transport/test_cipher_factory.rb +441 -0
  131. data/test/transport/test_hmac.rb +34 -0
  132. data/test/transport/test_identity_cipher.rb +40 -0
  133. data/test/transport/test_packet_stream.rb +1745 -0
  134. data/test/transport/test_server_version.rb +78 -0
  135. data/test/transport/test_session.rb +315 -0
  136. data/test/transport/test_state.rb +179 -0
  137. metadata +208 -0
@@ -0,0 +1,33 @@
1
+ module Net; module SSH; module Connection
2
+
3
+ # Definitions of constants that are specific to the connection layer of the
4
+ # SSH protocol.
5
+ module Constants
6
+
7
+ #--
8
+ # Connection protocol generic messages
9
+ #++
10
+
11
+ GLOBAL_REQUEST = 80
12
+ REQUEST_SUCCESS = 81
13
+ REQUEST_FAILURE = 82
14
+
15
+ #--
16
+ # Channel related messages
17
+ #++
18
+
19
+ CHANNEL_OPEN = 90
20
+ CHANNEL_OPEN_CONFIRMATION = 91
21
+ CHANNEL_OPEN_FAILURE = 92
22
+ CHANNEL_WINDOW_ADJUST = 93
23
+ CHANNEL_DATA = 94
24
+ CHANNEL_EXTENDED_DATA = 95
25
+ CHANNEL_EOF = 96
26
+ CHANNEL_CLOSE = 97
27
+ CHANNEL_REQUEST = 98
28
+ CHANNEL_SUCCESS = 99
29
+ CHANNEL_FAILURE = 100
30
+
31
+ end
32
+
33
+ end; end end
@@ -0,0 +1,603 @@
1
+ require 'net/ssh/loggable'
2
+ require 'net/ssh/ruby_compat'
3
+ require 'net/ssh/connection/channel'
4
+ require 'net/ssh/connection/constants'
5
+ require 'net/ssh/service/forward'
6
+
7
+ module Net; module SSH; module Connection
8
+
9
+ # A session class representing the connection service running on top of
10
+ # the SSH transport layer. It manages the creation of channels (see
11
+ # #open_channel), and the dispatching of messages to the various channels.
12
+ # It also encapsulates the SSH event loop (via #loop and #process),
13
+ # and serves as a central point-of-reference for all SSH-related services (e.g.
14
+ # port forwarding, SFTP, SCP, etc.).
15
+ #
16
+ # You will rarely (if ever) need to instantiate this class directly; rather,
17
+ # you'll almost always use Net::SSH.start to initialize a new network
18
+ # connection, authenticate a user, and return a new connection session,
19
+ # all in one call.
20
+ #
21
+ # Net::SSH.start("localhost", "user") do |ssh|
22
+ # # 'ssh' is an instance of Net::SSH::Connection::Session
23
+ # ssh.exec! "/etc/init.d/some_process start"
24
+ # end
25
+ class Session
26
+ include Constants, Loggable
27
+
28
+ # The underlying transport layer abstraction (see Net::SSH::Transport::Session).
29
+ attr_reader :transport
30
+
31
+ # The map of options that were used to initialize this instance.
32
+ attr_reader :options
33
+
34
+ # The collection of custom properties for this instance. (See #[] and #[]=).
35
+ attr_reader :properties
36
+
37
+ # The map of channels, each key being the local-id for the channel.
38
+ attr_reader :channels #:nodoc:
39
+
40
+ # The map of listeners that the event loop knows about. See #listen_to.
41
+ attr_reader :listeners #:nodoc:
42
+
43
+ # The map of specialized handlers for opening specific channel types. See
44
+ # #on_open_channel.
45
+ attr_reader :channel_open_handlers #:nodoc:
46
+
47
+ # The list of callbacks for pending requests. See #send_global_request.
48
+ attr_reader :pending_requests #:nodoc:
49
+
50
+ class NilChannel
51
+ def initialize(session)
52
+ @session = session
53
+ end
54
+
55
+ def method_missing(sym, *args)
56
+ @session.lwarn { "ignoring request #{sym.inspect} for non-existent (closed?) channel; probably ssh server bug" }
57
+ end
58
+ end
59
+
60
+ # Create a new connection service instance atop the given transport
61
+ # layer. Initializes the listeners to be only the underlying socket object.
62
+ def initialize(transport, options={})
63
+ self.logger = transport.logger
64
+
65
+ @transport = transport
66
+ @options = options
67
+
68
+ @channel_id_counter = -1
69
+ @channels = Hash.new(NilChannel.new(self))
70
+ @listeners = { transport.socket => nil }
71
+ @pending_requests = []
72
+ @channel_open_handlers = {}
73
+ @on_global_request = {}
74
+ @properties = (options[:properties] || {}).dup
75
+ end
76
+
77
+ # Retrieves a custom property from this instance. This can be used to
78
+ # store additional state in applications that must manage multiple
79
+ # SSH connections.
80
+ def [](key)
81
+ @properties[key]
82
+ end
83
+
84
+ # Sets a custom property for this instance.
85
+ def []=(key, value)
86
+ @properties[key] = value
87
+ end
88
+
89
+ # Returns the name of the host that was given to the transport layer to
90
+ # connect to.
91
+ def host
92
+ transport.host
93
+ end
94
+
95
+ # Returns true if the underlying transport has been closed. Note that
96
+ # this can be a little misleading, since if the remote server has
97
+ # closed the connection, the local end will still think it is open
98
+ # until the next operation on the socket. Nevertheless, this method can
99
+ # be useful if you just want to know if _you_ have closed the connection.
100
+ def closed?
101
+ transport.closed?
102
+ end
103
+
104
+ # Closes the session gracefully, blocking until all channels have
105
+ # successfully closed, and then closes the underlying transport layer
106
+ # connection.
107
+ def close
108
+ info { "closing remaining channels (#{channels.length} open)" }
109
+ channels.each { |id, channel| channel.close }
110
+ loop { channels.any? }
111
+ transport.close
112
+ end
113
+
114
+ # Performs a "hard" shutdown of the connection. In general, this should
115
+ # never be done, but it might be necessary (in a rescue clause, for instance,
116
+ # when the connection needs to close but you don't know the status of the
117
+ # underlying protocol's state).
118
+ def shutdown!
119
+ transport.shutdown!
120
+ end
121
+
122
+ # preserve a reference to Kernel#loop
123
+ alias :loop_forever :loop
124
+
125
+ # Returns +true+ if there are any channels currently active on this
126
+ # session. By default, this will not include "invisible" channels
127
+ # (such as those created by forwarding ports and such), but if you pass
128
+ # a +true+ value for +include_invisible+, then those will be counted.
129
+ #
130
+ # This can be useful for determining whether the event loop should continue
131
+ # to be run.
132
+ #
133
+ # ssh.loop { ssh.busy? }
134
+ def busy?(include_invisible=false)
135
+ if include_invisible
136
+ channels.any?
137
+ else
138
+ channels.any? { |id, ch| !ch[:invisible] }
139
+ end
140
+ end
141
+
142
+ # The main event loop. Calls #process until #process returns false. If a
143
+ # block is given, it is passed to #process, otherwise a default proc is
144
+ # used that just returns true if there are any channels active (see #busy?).
145
+ # The # +wait+ parameter is also passed through to #process (where it is
146
+ # interpreted as the maximum number of seconds to wait for IO.select to return).
147
+ #
148
+ # # loop for as long as there are any channels active
149
+ # ssh.loop
150
+ #
151
+ # # loop for as long as there are any channels active, but make sure
152
+ # # the event loop runs at least once per 0.1 second
153
+ # ssh.loop(0.1)
154
+ #
155
+ # # loop until ctrl-C is pressed
156
+ # int_pressed = false
157
+ # trap("INT") { int_pressed = true }
158
+ # ssh.loop(0.1) { not int_pressed }
159
+ def loop(wait=nil, &block)
160
+ running = block || Proc.new { busy? }
161
+ loop_forever { break unless process(wait, &running) }
162
+ end
163
+
164
+ # The core of the event loop. It processes a single iteration of the event
165
+ # loop. If a block is given, it should return false when the processing
166
+ # should abort, which causes #process to return false. Otherwise,
167
+ # #process returns true. The session itself is yielded to the block as its
168
+ # only argument.
169
+ #
170
+ # If +wait+ is nil (the default), this method will block until any of the
171
+ # monitored IO objects are ready to be read from or written to. If you want
172
+ # it to not block, you can pass 0, or you can pass any other numeric value
173
+ # to indicate that it should block for no more than that many seconds.
174
+ # Passing 0 is a good way to poll the connection, but if you do it too
175
+ # frequently it can make your CPU quite busy!
176
+ #
177
+ # This will also cause all active channels to be processed once each (see
178
+ # Net::SSH::Connection::Channel#on_process).
179
+ #
180
+ # # process multiple Net::SSH connections in parallel
181
+ # connections = [
182
+ # Net::SSH.start("host1", ...),
183
+ # Net::SSH.start("host2", ...)
184
+ # ]
185
+ #
186
+ # connections.each do |ssh|
187
+ # ssh.exec "grep something /in/some/files"
188
+ # end
189
+ #
190
+ # condition = Proc.new { |s| s.busy? }
191
+ #
192
+ # loop do
193
+ # connections.delete_if { |ssh| !ssh.process(0.1, &condition) }
194
+ # break if connections.empty?
195
+ # end
196
+ def process(wait=nil, &block)
197
+ return false unless preprocess(&block)
198
+
199
+ r = listeners.keys
200
+ w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
201
+ readers, writers, = Net::SSH::Compat.io_select(r, w, nil, wait)
202
+
203
+ postprocess(readers, writers)
204
+ end
205
+
206
+ # This is called internally as part of #process. It dispatches any
207
+ # available incoming packets, and then runs Net::SSH::Connection::Channel#process
208
+ # for any active channels. If a block is given, it is invoked at the
209
+ # start of the method and again at the end, and if the block ever returns
210
+ # false, this method returns false. Otherwise, it returns true.
211
+ def preprocess
212
+ return false if block_given? && !yield(self)
213
+ dispatch_incoming_packets
214
+ channels.each { |id, channel| channel.process unless channel.closing? }
215
+ return false if block_given? && !yield(self)
216
+ return true
217
+ end
218
+
219
+ # This is called internally as part of #process. It loops over the given
220
+ # arrays of reader IO's and writer IO's, processing them as needed, and
221
+ # then calls Net::SSH::Transport::Session#rekey_as_needed to allow the
222
+ # transport layer to rekey. Then returns true.
223
+ def postprocess(readers, writers)
224
+ # sometimes, got an execution hang on reader.fill.zero?
225
+ # the reader and writer socket was the same in those cases
226
+ # swapping the order so that pending writes execute before readers are closed seems to work
227
+
228
+ Array(writers).each do |writer|
229
+ writer.send_pending
230
+ end
231
+
232
+ Array(readers).each do |reader|
233
+ if listeners[reader]
234
+ listeners[reader].call(reader)
235
+ else
236
+ z = reader.fill.zero?
237
+ if z
238
+ reader.close
239
+ stop_listening_to(reader)
240
+ end
241
+ end
242
+ end
243
+
244
+
245
+ transport.rekey_as_needed
246
+
247
+ return true
248
+ end
249
+
250
+ # Send a global request of the given type. The +extra+ parameters must
251
+ # be even in number, and conform to the same format as described for
252
+ # Net::SSH::Buffer.from. If a callback is not specified, the request will
253
+ # not require a response from the server, otherwise the server is required
254
+ # to respond and indicate whether the request was successful or not. This
255
+ # success or failure is indicated by the callback being invoked, with the
256
+ # first parameter being true or false (success, or failure), and the second
257
+ # being the packet itself.
258
+ #
259
+ # Generally, Net::SSH will manage global requests that need to be sent
260
+ # (e.g. port forward requests and such are handled in the Net::SSH::Service::Forward
261
+ # class, for instance). However, there may be times when you need to
262
+ # send a global request that isn't explicitly handled by Net::SSH, and so
263
+ # this method is available to you.
264
+ #
265
+ # ssh.send_global_request("keep-alive@openssh.com")
266
+ def send_global_request(type, *extra, &callback)
267
+ info { "sending global request #{type}" }
268
+ msg = Buffer.from(:byte, GLOBAL_REQUEST, :string, type.to_s, :bool, !callback.nil?, *extra)
269
+ send_message(msg)
270
+ pending_requests << callback if callback
271
+ self
272
+ end
273
+
274
+ # Requests that a new channel be opened. By default, the channel will be
275
+ # of type "session", but if you know what you're doing you can select any
276
+ # of the channel types supported by the SSH protocol. The +extra+ parameters
277
+ # must be even in number and conform to the same format as described for
278
+ # Net::SSH::Buffer.from. If a callback is given, it will be invoked when
279
+ # the server confirms that the channel opened successfully. The sole parameter
280
+ # for the callback is the channel object itself.
281
+ #
282
+ # In general, you'll use #open_channel without any arguments; the only
283
+ # time you'd want to set the channel type or pass additional initialization
284
+ # data is if you were implementing an SSH extension.
285
+ #
286
+ # channel = ssh.open_channel do |ch|
287
+ # ch.exec "grep something /some/files" do |ch, success|
288
+ # ...
289
+ # end
290
+ # end
291
+ #
292
+ # channel.wait
293
+ def open_channel(type="session", *extra, &on_confirm)
294
+ local_id = get_next_channel_id
295
+ channel = Channel.new(self, type, local_id, &on_confirm)
296
+
297
+ msg = Buffer.from(:byte, CHANNEL_OPEN, :string, type, :long, local_id,
298
+ :long, channel.local_maximum_window_size,
299
+ :long, channel.local_maximum_packet_size, *extra)
300
+ send_message(msg)
301
+
302
+ channels[local_id] = channel
303
+ end
304
+
305
+ # A convenience method for executing a command and interacting with it. If
306
+ # no block is given, all output is printed via $stdout and $stderr. Otherwise,
307
+ # the block is called for each data and extended data packet, with three
308
+ # arguments: the channel object, a symbol indicating the data type
309
+ # (:stdout or :stderr), and the data (as a string).
310
+ #
311
+ # Note that this method returns immediately, and requires an event loop
312
+ # (see Session#loop) in order for the command to actually execute.
313
+ #
314
+ # This is effectively identical to calling #open_channel, and then
315
+ # Net::SSH::Connection::Channel#exec, and then setting up the channel
316
+ # callbacks. However, for most uses, this will be sufficient.
317
+ #
318
+ # ssh.exec "grep something /some/files" do |ch, stream, data|
319
+ # if stream == :stderr
320
+ # puts "ERROR: #{data}"
321
+ # else
322
+ # puts data
323
+ # end
324
+ # end
325
+ def exec(command, &block)
326
+ open_channel do |channel|
327
+ channel.exec(command) do |ch, success|
328
+ raise "could not execute command: #{command.inspect}" unless success
329
+
330
+ channel.on_data do |ch2, data|
331
+ if block
332
+ block.call(ch2, :stdout, data)
333
+ else
334
+ $stdout.print(data)
335
+ end
336
+ end
337
+
338
+ channel.on_extended_data do |ch2, type, data|
339
+ if block
340
+ block.call(ch2, :stderr, data)
341
+ else
342
+ $stderr.print(data)
343
+ end
344
+ end
345
+ end
346
+ end
347
+ end
348
+
349
+ # Same as #exec, except this will block until the command finishes. Also,
350
+ # if a block is not given, this will return all output (stdout and stderr)
351
+ # as a single string.
352
+ #
353
+ # matches = ssh.exec!("grep something /some/files")
354
+ def exec!(command, &block)
355
+ block ||= Proc.new do |ch, type, data|
356
+ ch[:result] ||= ""
357
+ ch[:result] << data
358
+ end
359
+
360
+ channel = exec(command, &block)
361
+ channel.wait
362
+
363
+ return channel[:result]
364
+ end
365
+
366
+ # Enqueues a message to be sent to the server as soon as the socket is
367
+ # available for writing. Most programs will never need to call this, but
368
+ # if you are implementing an extension to the SSH protocol, or if you
369
+ # need to send a packet that Net::SSH does not directly support, you can
370
+ # use this to send it.
371
+ #
372
+ # ssh.send_message(Buffer.from(:byte, REQUEST_SUCCESS).to_s)
373
+ def send_message(message)
374
+ transport.enqueue_message(message)
375
+ end
376
+
377
+ # Adds an IO object for the event loop to listen to. If a callback
378
+ # is given, it will be invoked when the io is ready to be read, otherwise,
379
+ # the io will merely have its #fill method invoked.
380
+ #
381
+ # Any +io+ value passed to this method _must_ have mixed into it the
382
+ # Net::SSH::BufferedIo functionality, typically by calling #extend on the
383
+ # object.
384
+ #
385
+ # The following example executes a process on the remote server, opens
386
+ # a socket to somewhere, and then pipes data from that socket to the
387
+ # remote process' stdin stream:
388
+ #
389
+ # channel = ssh.open_channel do |ch|
390
+ # ch.exec "/some/process/that/wants/input" do |ch, success|
391
+ # abort "can't execute!" unless success
392
+ #
393
+ # io = TCPSocket.new(somewhere, port)
394
+ # io.extend(Net::SSH::BufferedIo)
395
+ # ssh.listen_to(io)
396
+ #
397
+ # ch.on_process do
398
+ # if io.available > 0
399
+ # ch.send_data(io.read_available)
400
+ # end
401
+ # end
402
+ #
403
+ # ch.on_close do
404
+ # ssh.stop_listening_to(io)
405
+ # io.close
406
+ # end
407
+ # end
408
+ # end
409
+ #
410
+ # channel.wait
411
+ def listen_to(io, &callback)
412
+ listeners[io] = callback
413
+ end
414
+
415
+ # Removes the given io object from the listeners collection, so that the
416
+ # event loop will no longer monitor it.
417
+ def stop_listening_to(io)
418
+ listeners.delete(io)
419
+ end
420
+
421
+ # Returns a reference to the Net::SSH::Service::Forward service, which can
422
+ # be used for forwarding ports over SSH.
423
+ def forward
424
+ @forward ||= Service::Forward.new(self)
425
+ end
426
+
427
+ # Registers a handler to be invoked when the server wants to open a
428
+ # channel on the client. The callback receives the connection object,
429
+ # the new channel object, and the packet itself as arguments, and should
430
+ # raise ChannelOpenFailed if it is unable to open the channel for some
431
+ # reason. Otherwise, the channel will be opened and a confirmation message
432
+ # sent to the server.
433
+ #
434
+ # This is used by the Net::SSH::Service::Forward service to open a channel
435
+ # when a remote forwarded port receives a connection. However, you are
436
+ # welcome to register handlers for other channel types, as needed.
437
+ def on_open_channel(type, &block)
438
+ channel_open_handlers[type] = block
439
+ end
440
+
441
+ # Registers a handler to be invoked when the server sends a global request
442
+ # of the given type. The callback receives the request data as the first
443
+ # parameter, and true/false as the second (indicating whether a response
444
+ # is required). If the callback sends the response, it should return
445
+ # :sent. Otherwise, if it returns true, REQUEST_SUCCESS will be sent, and
446
+ # if it returns false, REQUEST_FAILURE will be sent.
447
+ def on_global_request(type, &block)
448
+ old, @on_global_request[type] = @on_global_request[type], block
449
+ old
450
+ end
451
+
452
+ private
453
+
454
+ # Read all pending packets from the connection and dispatch them as
455
+ # appropriate. Returns as soon as there are no more pending packets.
456
+ def dispatch_incoming_packets
457
+ while packet = transport.poll_message
458
+ unless MAP.key?(packet.type)
459
+ raise Net::SSH::Exception, "unexpected response #{packet.type} (#{packet.inspect})"
460
+ end
461
+
462
+ send(MAP[packet.type], packet)
463
+ end
464
+ end
465
+
466
+ # Returns the next available channel id to be assigned, and increments
467
+ # the counter.
468
+ def get_next_channel_id
469
+ @channel_id_counter += 1
470
+ end
471
+
472
+ # Invoked when a global request is received. The registered global
473
+ # request callback will be invoked, if one exists, and the necessary
474
+ # reply returned.
475
+ def global_request(packet)
476
+ info { "global request received: #{packet[:request_type]} #{packet[:want_reply]}" }
477
+ callback = @on_global_request[packet[:request_type]]
478
+ result = callback ? callback.call(packet[:request_data], packet[:want_reply]) : false
479
+
480
+ if result != :sent && result != true && result != false
481
+ raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}"
482
+ end
483
+
484
+ if packet[:want_reply] && result != :sent
485
+ msg = Buffer.from(:byte, result ? REQUEST_SUCCESS : REQUEST_FAILURE)
486
+ send_message(msg)
487
+ end
488
+ end
489
+
490
+ # Invokes the next pending request callback with +true+.
491
+ def request_success(packet)
492
+ info { "global request success" }
493
+ callback = pending_requests.shift
494
+ callback.call(true, packet) if callback
495
+ end
496
+
497
+ # Invokes the next pending request callback with +false+.
498
+ def request_failure(packet)
499
+ info { "global request failure" }
500
+ callback = pending_requests.shift
501
+ callback.call(false, packet) if callback
502
+ end
503
+
504
+ # Called when the server wants to open a channel. If no registered
505
+ # channel handler exists for the given channel type, CHANNEL_OPEN_FAILURE
506
+ # is returned, otherwise the callback is invoked and everything proceeds
507
+ # accordingly.
508
+ def channel_open(packet)
509
+ info { "channel open #{packet[:channel_type]}" }
510
+
511
+ local_id = get_next_channel_id
512
+ channel = Channel.new(self, packet[:channel_type], local_id)
513
+ channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
514
+
515
+ callback = channel_open_handlers[packet[:channel_type]]
516
+
517
+ if callback
518
+ begin
519
+ callback[self, channel, packet]
520
+ rescue ChannelOpenFailed => err
521
+ failure = [err.code, err.reason]
522
+ else
523
+ channels[local_id] = channel
524
+ msg = Buffer.from(:byte, CHANNEL_OPEN_CONFIRMATION, :long, channel.remote_id, :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size)
525
+ end
526
+ else
527
+ failure = [3, "unknown channel type #{channel.type}"]
528
+ end
529
+
530
+ if failure
531
+ error { failure.inspect }
532
+ msg = Buffer.from(:byte, CHANNEL_OPEN_FAILURE, :long, channel.remote_id, :long, failure[0], :string, failure[1], :string, "")
533
+ end
534
+
535
+ send_message(msg)
536
+ end
537
+
538
+ def channel_open_confirmation(packet)
539
+ info { "channel_open_confirmation: #{packet[:local_id]} #{packet[:remote_id]} #{packet[:window_size]} #{packet[:packet_size]}" }
540
+ channel = channels[packet[:local_id]]
541
+ channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
542
+ end
543
+
544
+ def channel_open_failure(packet)
545
+ error { "channel_open_failed: #{packet[:local_id]} #{packet[:reason_code]} #{packet[:description]}" }
546
+ channel = channels.delete(packet[:local_id])
547
+ channel.do_open_failed(packet[:reason_code], packet[:description])
548
+ end
549
+
550
+ def channel_window_adjust(packet)
551
+ info { "channel_window_adjust: #{packet[:local_id]} +#{packet[:extra_bytes]}" }
552
+ channels[packet[:local_id]].do_window_adjust(packet[:extra_bytes])
553
+ end
554
+
555
+ def channel_request(packet)
556
+ info { "channel_request: #{packet[:local_id]} #{packet[:request]} #{packet[:want_reply]}" }
557
+ channels[packet[:local_id]].do_request(packet[:request], packet[:want_reply], packet[:request_data])
558
+ end
559
+
560
+ def channel_data(packet)
561
+ info { "channel_data: #{packet[:local_id]} #{packet[:data].length}b" }
562
+ channels[packet[:local_id]].do_data(packet[:data])
563
+ end
564
+
565
+ def channel_extended_data(packet)
566
+ info { "channel_extended_data: #{packet[:local_id]} #{packet[:data_type]} #{packet[:data].length}b" }
567
+ channels[packet[:local_id]].do_extended_data(packet[:data_type], packet[:data])
568
+ end
569
+
570
+ def channel_eof(packet)
571
+ info { "channel_eof: #{packet[:local_id]}" }
572
+ channels[packet[:local_id]].do_eof
573
+ end
574
+
575
+ def channel_close(packet)
576
+ info { "channel_close: #{packet[:local_id]}" }
577
+
578
+ channel = channels[packet[:local_id]]
579
+ channel.close
580
+
581
+ channels.delete(packet[:local_id])
582
+ channel.do_close
583
+ end
584
+
585
+ def channel_success(packet)
586
+ info { "channel_success: #{packet[:local_id]}" }
587
+ channels[packet[:local_id]].do_success
588
+ end
589
+
590
+ def channel_failure(packet)
591
+ info { "channel_failure: #{packet[:local_id]}" }
592
+ channels[packet[:local_id]].do_failure
593
+ end
594
+
595
+ MAP = Constants.constants.inject({}) do |memo, name|
596
+ value = const_get(name)
597
+ next unless Integer === value
598
+ memo[value] = name.downcase.to_sym
599
+ memo
600
+ end
601
+ end
602
+
603
+ end; end; end