net-ssh 4.1.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +5 -0
  5. data/.rubocop.yml +8 -2
  6. data/.rubocop_todo.yml +405 -552
  7. data/.travis.yml +23 -22
  8. data/CHANGES.txt +112 -1
  9. data/Gemfile +1 -7
  10. data/{Gemfile.norbnacl → Gemfile.noed25519} +1 -1
  11. data/Manifest +4 -5
  12. data/README.md +287 -0
  13. data/Rakefile +40 -29
  14. data/appveyor.yml +12 -6
  15. data/lib/net/ssh.rb +68 -32
  16. data/lib/net/ssh/authentication/agent.rb +234 -222
  17. data/lib/net/ssh/authentication/certificate.rb +175 -164
  18. data/lib/net/ssh/authentication/constants.rb +17 -14
  19. data/lib/net/ssh/authentication/ed25519.rb +162 -141
  20. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -29
  21. data/lib/net/ssh/authentication/key_manager.rb +40 -9
  22. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  23. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  24. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +1 -1
  25. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  26. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  27. data/lib/net/ssh/authentication/methods/publickey.rb +56 -55
  28. data/lib/net/ssh/authentication/pageant.rb +468 -465
  29. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  30. data/lib/net/ssh/authentication/session.rb +130 -122
  31. data/lib/net/ssh/buffer.rb +345 -312
  32. data/lib/net/ssh/buffered_io.rb +163 -163
  33. data/lib/net/ssh/config.rb +316 -238
  34. data/lib/net/ssh/connection/channel.rb +670 -650
  35. data/lib/net/ssh/connection/constants.rb +30 -26
  36. data/lib/net/ssh/connection/event_loop.rb +108 -105
  37. data/lib/net/ssh/connection/keepalive.rb +54 -50
  38. data/lib/net/ssh/connection/session.rb +682 -671
  39. data/lib/net/ssh/connection/term.rb +180 -176
  40. data/lib/net/ssh/errors.rb +101 -99
  41. data/lib/net/ssh/key_factory.rb +195 -108
  42. data/lib/net/ssh/known_hosts.rb +161 -152
  43. data/lib/net/ssh/loggable.rb +57 -55
  44. data/lib/net/ssh/packet.rb +82 -78
  45. data/lib/net/ssh/prompt.rb +55 -53
  46. data/lib/net/ssh/proxy/command.rb +104 -89
  47. data/lib/net/ssh/proxy/errors.rb +12 -8
  48. data/lib/net/ssh/proxy/http.rb +93 -91
  49. data/lib/net/ssh/proxy/https.rb +42 -39
  50. data/lib/net/ssh/proxy/jump.rb +50 -47
  51. data/lib/net/ssh/proxy/socks4.rb +0 -2
  52. data/lib/net/ssh/proxy/socks5.rb +11 -12
  53. data/lib/net/ssh/service/forward.rb +370 -317
  54. data/lib/net/ssh/test.rb +83 -77
  55. data/lib/net/ssh/test/channel.rb +146 -142
  56. data/lib/net/ssh/test/extensions.rb +150 -146
  57. data/lib/net/ssh/test/kex.rb +35 -31
  58. data/lib/net/ssh/test/local_packet.rb +48 -44
  59. data/lib/net/ssh/test/packet.rb +87 -84
  60. data/lib/net/ssh/test/remote_packet.rb +35 -31
  61. data/lib/net/ssh/test/script.rb +173 -171
  62. data/lib/net/ssh/test/socket.rb +59 -55
  63. data/lib/net/ssh/transport/algorithms.rb +430 -364
  64. data/lib/net/ssh/transport/cipher_factory.rb +95 -91
  65. data/lib/net/ssh/transport/constants.rb +33 -25
  66. data/lib/net/ssh/transport/ctr.rb +33 -11
  67. data/lib/net/ssh/transport/hmac.rb +15 -13
  68. data/lib/net/ssh/transport/hmac/abstract.rb +82 -63
  69. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  70. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  71. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  72. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  73. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  74. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  75. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  76. data/lib/net/ssh/transport/kex.rb +14 -13
  77. data/lib/net/ssh/transport/kex/abstract.rb +123 -0
  78. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  79. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +38 -0
  80. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  81. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  82. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +112 -217
  83. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -62
  84. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  85. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  86. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  87. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  88. data/lib/net/ssh/transport/key_expander.rb +29 -25
  89. data/lib/net/ssh/transport/openssl.rb +116 -116
  90. data/lib/net/ssh/transport/packet_stream.rb +223 -190
  91. data/lib/net/ssh/transport/server_version.rb +64 -66
  92. data/lib/net/ssh/transport/session.rb +306 -257
  93. data/lib/net/ssh/transport/state.rb +198 -196
  94. data/lib/net/ssh/verifiers/accept_new.rb +35 -0
  95. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +34 -0
  96. data/lib/net/ssh/verifiers/always.rb +56 -0
  97. data/lib/net/ssh/verifiers/never.rb +21 -0
  98. data/lib/net/ssh/version.rb +55 -53
  99. data/net-ssh-public_cert.pem +18 -19
  100. data/net-ssh.gemspec +12 -11
  101. data/support/ssh_tunnel_bug.rb +2 -2
  102. metadata +86 -75
  103. metadata.gz.sig +0 -0
  104. data/Gemfile.norbnacl.lock +0 -41
  105. data/README.rdoc +0 -169
  106. data/lib/net/ssh/ruby_compat.rb +0 -24
  107. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  108. data/lib/net/ssh/verifiers/null.rb +0 -12
  109. data/lib/net/ssh/verifiers/secure.rb +0 -52
  110. data/lib/net/ssh/verifiers/strict.rb +0 -24
  111. data/support/arcfour_check.rb +0 -20
@@ -2,671 +2,691 @@ require 'net/ssh/loggable'
2
2
  require 'net/ssh/connection/constants'
3
3
  require 'net/ssh/connection/term'
4
4
 
5
- module Net; module SSH; module Connection
6
-
7
- # The channel abstraction. Multiple "channels" can be multiplexed onto a
8
- # single SSH channel, each operating independently and seemingly in parallel.
9
- # This class represents a single such channel. Most operations performed
10
- # with the Net::SSH library will involve using one or more channels.
11
- #
12
- # Channels are intended to be used asynchronously. You request that one be
13
- # opened (via Connection::Session#open_channel), and when it is opened, your
14
- # callback is invoked. Then, you set various other callbacks on the newly
15
- # opened channel, which are called in response to the corresponding events.
16
- # Programming with Net::SSH works best if you think of your programs as
17
- # state machines. Complex programs are best implemented as objects that
18
- # wrap a channel. See Net::SCP and Net::SFTP for examples of how complex
19
- # state machines can be built on top of the SSH protocol.
20
- #
21
- # ssh.open_channel do |channel|
22
- # channel.exec("/invoke/some/command") do |ch, success|
23
- # abort "could not execute command" unless success
24
- #
25
- # channel.on_data do |ch, data|
26
- # puts "got stdout: #{data}"
27
- # channel.send_data "something for stdin\n"
28
- # end
29
- #
30
- # channel.on_extended_data do |ch, type, data|
31
- # puts "got stderr: #{data}"
32
- # end
33
- #
34
- # channel.on_close do |ch|
35
- # puts "channel is closing!"
36
- # end
37
- # end
38
- # end
39
- #
40
- # ssh.loop
41
- #
42
- # Channels also have a basic hash-like interface, that allows programs to
43
- # store arbitrary state information on a channel object. This helps simplify
44
- # the writing of state machines, especially when you may be juggling
45
- # multiple open channels at the same time.
46
- #
47
- # Note that data sent across SSH channels are governed by maximum packet
48
- # sizes and maximum window sizes. These details are managed internally
49
- # by Net::SSH::Connection::Channel, so you may remain blissfully ignorant
50
- # if you so desire, but you can always inspect the current maximums, as
51
- # well as the remaining window size, using the reader attributes for those
52
- # values.
53
- class Channel
54
- include Constants, Loggable
55
-
56
- # The local id for this channel, assigned by the Net::SSH::Connection::Session instance.
57
- attr_reader :local_id
58
-
59
- # The remote id for this channel, assigned by the remote host.
60
- attr_reader :remote_id
61
-
62
- # The type of this channel, usually "session".
63
- attr_reader :type
64
-
65
- # The underlying Net::SSH::Connection::Session instance that supports this channel.
66
- attr_reader :connection
67
-
68
- # The maximum packet size that the local host can receive.
69
- attr_reader :local_maximum_packet_size
70
-
71
- # The maximum amount of data that the local end of this channel can
72
- # receive. This is a total, not per-packet.
73
- attr_reader :local_maximum_window_size
74
-
75
- # The maximum packet size that the remote host can receive.
76
- attr_reader :remote_maximum_packet_size
77
-
78
- # The maximum amount of data that the remote end of this channel can
79
- # receive. This is a total, not per-packet.
80
- attr_reader :remote_maximum_window_size
81
-
82
- # This is the remaining window size on the local end of this channel. When
83
- # this reaches zero, no more data can be received.
84
- attr_reader :local_window_size
85
-
86
- # This is the remaining window size on the remote end of this channel. When
87
- # this reaches zero, no more data can be sent.
88
- attr_reader :remote_window_size
89
-
90
- # A hash of properties for this channel. These can be used to store state
91
- # information about this channel. See also #[] and #[]=.
92
- attr_reader :properties
93
-
94
- # The output buffer for this channel. Data written to the channel is
95
- # enqueued here, to be written as CHANNEL_DATA packets during each pass of
96
- # the event loop. See Connection::Session#process and #enqueue_pending_output.
97
- attr_reader :output #:nodoc:
98
-
99
- # The list of pending requests. Each time a request is sent which requires
100
- # a reply, the corresponding callback is pushed onto this queue. As responses
101
- # arrive, they are shifted off the front and handled.
102
- attr_reader :pending_requests #:nodoc:
103
-
104
- # Instantiates a new channel on the given connection, of the given type,
105
- # and with the given id. If a block is given, it will be remembered until
106
- # the channel is confirmed open by the server, and will be invoked at
107
- # that time (see #do_open_confirmation).
108
- #
109
- # This also sets the default maximum packet size and maximum window size.
110
- def initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open)
111
- self.logger = connection.logger
112
-
113
- @connection = connection
114
- @type = type
115
- @local_id = local_id
116
-
117
- @local_maximum_packet_size = max_pkt_size
118
- @local_window_size = @local_maximum_window_size = max_win_size
119
-
120
- @on_confirm_open = on_confirm_open
121
-
122
- @output = Buffer.new
123
-
124
- @properties = {}
125
-
126
- @pending_requests = []
127
- @on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil
128
- @on_request = {}
129
- @closing = @eof = @sent_eof = @local_closed = @remote_closed = false
130
- end
131
-
132
- # A shortcut for accessing properties of the channel (see #properties).
133
- def [](name)
134
- @properties[name]
135
- end
136
-
137
- # A shortcut for setting properties of the channel (see #properties).
138
- def []=(name, value)
139
- @properties[name] = value
140
- end
141
-
142
- # Syntactic sugar for executing a command. Sends a channel request asking
143
- # that the given command be invoked. If the block is given, it will be
144
- # called when the server responds. The first parameter will be the
145
- # channel, and the second will be true or false, indicating whether the
146
- # request succeeded or not. In this case, success means that the command
147
- # is being executed, not that it has completed, and failure means that the
148
- # command altogether failed to be executed.
149
- #
150
- # channel.exec "ls -l /home" do |ch, success|
151
- # if success
152
- # puts "command has begun executing..."
153
- # # this is a good place to hang callbacks like #on_data...
154
- # else
155
- # puts "alas! the command could not be invoked!"
156
- # end
157
- # end
158
- def exec(command, &block)
159
- send_channel_request("exec", :string, command, &block)
160
- end
161
-
162
- # Syntactic sugar for requesting that a subsystem be started. Subsystems
163
- # are a way for other protocols (like SFTP) to be run, using SSH as
164
- # the transport. Generally, you'll never need to call this directly unless
165
- # you are the implementor of something that consumes an SSH subsystem, like
166
- # SFTP.
167
- #
168
- # channel.subsystem("sftp") do |ch, success|
169
- # if success
170
- # puts "subsystem successfully started"
171
- # else
172
- # puts "subsystem could not be started"
173
- # end
174
- # end
175
- def subsystem(subsystem, &block)
176
- send_channel_request("subsystem", :string, subsystem, &block)
177
- end
178
-
179
- # Syntactic sugar for setting an environment variable in the remote
180
- # process' environment. Note that for security reasons, the server may
181
- # refuse to set certain environment variables, or all, at the server's
182
- # discretion. If you are connecting to an OpenSSH server, you will
183
- # need to update the AcceptEnv setting in the sshd_config to include the
184
- # environment variables you want to send.
185
- #
186
- # channel.env "PATH", "/usr/local/bin"
187
- def env(variable_name, variable_value, &block)
188
- send_channel_request("env", :string, variable_name, :string, variable_value, &block)
189
- end
190
-
191
- # A hash of the valid PTY options (see #request_pty).
192
- VALID_PTY_OPTIONS = { term: "xterm",
193
- chars_wide: 80,
194
- chars_high: 24,
195
- pixels_wide: 640,
196
- pixels_high: 480,
197
- modes: {} }
198
-
199
- # Requests that a pseudo-tty (or "pty") be made available for this channel.
200
- # This is useful when you want to invoke and interact with some kind of
201
- # screen-based program (e.g., vim, or some menuing system).
202
- #
203
- # Note, that without a pty some programs (e.g. sudo, or subversion) on
204
- # some systems, will not be able to run interactively, and will error
205
- # instead of prompt if they ever need some user interaction.
206
- #
207
- # Note, too, that when a pty is requested, user's shell configuration
208
- # scripts (.bashrc and such) are not run by default, whereas they are
209
- # run when a pty is not present.
210
- #
211
- # channel.request_pty do |ch, success|
212
- # if success
213
- # puts "pty successfully obtained"
214
- # else
215
- # puts "could not obtain pty"
216
- # end
217
- # end
218
- def request_pty(opts={}, &block)
219
- extra = opts.keys - VALID_PTY_OPTIONS.keys
220
- raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any?
221
-
222
- opts = VALID_PTY_OPTIONS.merge(opts)
223
-
224
- modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)|
225
- memo.write_byte(mode).write_long(data)
226
- end
227
- # mark the end of the mode opcode list with a 0 byte
228
- modes.write_byte(0)
229
-
230
- send_channel_request("pty-req", :string, opts[:term],
231
- :long, opts[:chars_wide], :long, opts[:chars_high],
232
- :long, opts[:pixels_wide], :long, opts[:pixels_high],
233
- :string, modes.to_s, &block)
234
- end
235
-
236
- # Sends data to the channel's remote endpoint. This usually has the
237
- # effect of sending the given string to the remote process' stdin stream.
238
- # Note that it does not immediately send the data across the channel,
239
- # but instead merely appends the given data to the channel's output buffer,
240
- # preparatory to being packaged up and sent out the next time the connection
241
- # is accepting data. (A connection might not be accepting data if, for
242
- # instance, it has filled its data window and has not yet been resized by
243
- # the remote end-point.)
244
- #
245
- # This will raise an exception if the channel has previously declared
246
- # that no more data will be sent (see #eof!).
247
- #
248
- # channel.send_data("the password\n")
249
- def send_data(data)
250
- raise EOFError, "cannot send data if channel has declared eof" if eof?
251
- output.append(data.to_s)
252
- end
253
-
254
- # Returns true if the channel exists in the channel list of the session,
255
- # and false otherwise. This can be used to determine whether a channel has
256
- # been closed or not.
257
- #
258
- # ssh.loop { channel.active? }
259
- def active?
260
- connection.channels.key?(local_id)
261
- end
262
-
263
- # Runs the SSH event loop until the channel is no longer active. This is
264
- # handy for blocking while you wait for some channel to finish.
265
- #
266
- # channel.exec("grep ...") { ... }
267
- # channel.wait
268
- def wait
269
- connection.loop { active? }
270
- end
271
-
272
- # True if close() has been called; NOTE: if the channel has data waiting to
273
- # be sent then the channel will close after all the data is sent. See
274
- # closed?() to determine if we have actually sent CHANNEL_CLOSE to server.
275
- # This may be true for awhile before closed? returns true if we are still
276
- # sending buffered output to server.
277
- def closing?
278
- @closing
279
- end
280
-
281
- # True if we have sent CHANNEL_CLOSE to the remote server.
282
- def local_closed?
283
- @local_closed
284
- end
285
-
286
- def remote_closed?
287
- @remote_closed
288
- end
289
-
290
- def remote_closed!
291
- @remote_closed = true
292
- end
293
-
294
- # Requests that the channel be closed. It only marks the channel to be closed
295
- # the CHANNEL_CLOSE message will be sent from event loop
296
- def close
297
- return if @closing
298
- @closing = true
299
- end
300
-
301
- # Returns true if the local end of the channel has declared that no more
302
- # data is forthcoming (see #eof!). Trying to send data via #send_data when
303
- # this is true will result in an exception being raised.
304
- def eof?
305
- @eof
306
- end
307
-
308
- # Tells the remote end of the channel that no more data is forthcoming
309
- # from this end of the channel. The remote end may still send data.
310
- # The CHANNEL_EOF packet will be sent once the output buffer is empty.
311
- def eof!
312
- return if eof?
313
- @eof = true
314
- end
315
-
316
- # If an #on_process handler has been set up, this will cause it to be
317
- # invoked (passing the channel itself as an argument). It also causes all
318
- # pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output).
319
- def process
320
- @on_process.call(self) if @on_process
321
- enqueue_pending_output
322
-
323
- if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed
324
- connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
325
- @sent_eof = true
326
- end
327
-
328
- if @closing and not @local_closed and output.empty? and remote_id
329
- connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
330
- @local_closed = true
331
- connection.cleanup_channel(self)
332
- end
333
- end
334
-
335
- # Registers a callback to be invoked when data packets are received by the
336
- # channel. The callback is called with the channel as the first argument,
337
- # and the data as the second.
338
- #
339
- # channel.on_data do |ch, data|
340
- # puts "got data: #{data.inspect}"
341
- # end
342
- #
343
- # Data received this way is typically the data written by the remote
344
- # process to its +stdout+ stream.
345
- def on_data(&block)
346
- old, @on_data = @on_data, block
347
- old
348
- end
349
-
350
- # Registers a callback to be invoked when extended data packets are received
351
- # by the channel. The callback is called with the channel as the first
352
- # argument, the data type (as an integer) as the second, and the data as
353
- # the third. Extended data is almost exclusively used to send +stderr+ data
354
- # (+type+ == 1). Other extended data types are not defined by the SSH
355
- # protocol.
356
- #
357
- # channel.on_extended_data do |ch, type, data|
358
- # puts "got stderr: #{data.inspect}"
359
- # end
360
- def on_extended_data(&block)
361
- old, @on_extended_data = @on_extended_data, block
362
- old
363
- end
364
-
365
- # Registers a callback to be invoked for each pass of the event loop for
366
- # this channel. There are no guarantees on timeliness in the event loop,
367
- # but it will be called roughly once for each packet received by the
368
- # connection (not the channel). This callback is invoked with the channel
369
- # as the sole argument.
370
- #
371
- # Here's an example that accumulates the channel data into a variable on
372
- # the channel itself, and displays individual lines in the input one
373
- # at a time when the channel is processed:
374
- #
375
- # channel[:data] = ""
376
- #
377
- # channel.on_data do |ch, data|
378
- # channel[:data] << data
379
- # end
380
- #
381
- # channel.on_process do |ch|
382
- # if channel[:data] =~ /^.*?\n/
383
- # puts $&
384
- # channel[:data] = $'
385
- # end
386
- # end
387
- def on_process(&block)
388
- old, @on_process = @on_process, block
389
- old
390
- end
391
-
392
- # Registers a callback to be invoked when the server acknowledges that a
393
- # channel is closed. This is invoked with the channel as the sole argument.
394
- #
395
- # channel.on_close do |ch|
396
- # puts "remote end is closing!"
397
- # end
398
- def on_close(&block)
399
- old, @on_close = @on_close, block
400
- old
401
- end
402
-
403
- # Registers a callback to be invoked when the server indicates that no more
404
- # data will be sent to the channel (although the channel can still send
405
- # data to the server). The channel is the sole argument to the callback.
406
- #
407
- # channel.on_eof do |ch|
408
- # puts "remote end is done sending data"
409
- # end
410
- def on_eof(&block)
411
- old, @on_eof = @on_eof, block
412
- old
413
- end
414
-
415
- # Registers a callback to be invoked when the server was unable to open
416
- # the requested channel. The channel itself will be passed to the block,
417
- # along with the integer "reason code" for the failure, and a textual
418
- # description of the failure from the server.
419
- #
420
- # channel = session.open_channel do |ch|
421
- # # ..
422
- # end
423
- #
424
- # channel.on_open_failed { |ch, code, desc| ... }
425
- def on_open_failed(&block)
426
- old, @on_open_failed = @on_open_failed, block
427
- old
428
- end
429
-
430
- # Registers a callback to be invoked when a channel request of the given
431
- # type is received. The callback will receive the channel as the first
432
- # argument, and the associated (unparsed) data as the second. The data
433
- # will be a Net::SSH::Buffer that you will need to parse, yourself,
434
- # according to the kind of request you are watching.
435
- #
436
- # By default, if the request wants a reply, Net::SSH will send a
437
- # CHANNEL_SUCCESS response for any request that was handled by a registered
438
- # callback, and CHANNEL_FAILURE for any that wasn't, but if you want your
439
- # registered callback to result in a CHANNEL_FAILURE response, just raise
440
- # Net::SSH::ChannelRequestFailed.
441
- #
442
- # Some common channel requests that your programs might want to listen
443
- # for are:
444
- #
445
- # * "exit-status" : the exit status of the remote process will be reported
446
- # as a long integer in the data buffer, which you can grab via
447
- # data.read_long.
448
- # * "exit-signal" : if the remote process died as a result of a signal
449
- # being sent to it, the signal will be reported as a string in the
450
- # data, via data.read_string. (Not all SSH servers support this channel
451
- # request type.)
452
- #
453
- # channel.on_request "exit-status" do |ch, data|
454
- # puts "process terminated with exit status: #{data.read_long}"
455
- # end
456
- def on_request(type, &block)
457
- old, @on_request[type] = @on_request[type], block
458
- old
459
- end
460
-
461
- # Sends a new channel request with the given name. The extra +data+
462
- # parameter must either be empty, or consist of an even number of
463
- # arguments. See Net::SSH::Buffer.from for a description of their format.
464
- # If a block is given, it is registered as a callback for a pending
465
- # request, and the packet will be flagged so that the server knows a
466
- # reply is required. If no block is given, the server will send no
467
- # response to this request. Responses, where required, will cause the
468
- # callback to be invoked with the channel as the first argument, and
469
- # either true or false as the second, depending on whether the request
470
- # succeeded or not. The meaning of "success" and "failure" in this context
471
- # is dependent on the specific request that was sent.
472
- #
473
- # channel.send_channel_request "shell" do |ch, success|
474
- # if success
475
- # puts "user shell started successfully"
476
- # else
477
- # puts "could not start user shell"
478
- # end
479
- # end
480
- #
481
- # Most channel requests you'll want to send are already wrapped in more
482
- # convenient helper methods (see #exec and #subsystem).
483
- def send_channel_request(request_name, *data, &callback)
484
- info { "sending channel request #{request_name.inspect}" }
485
- fail "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel" unless remote_id
486
- msg = Buffer.from(:byte, CHANNEL_REQUEST,
487
- :long, remote_id, :string, request_name,
488
- :bool, !callback.nil?, *data)
489
- connection.send_message(msg)
490
- pending_requests << callback if callback
491
- end
492
-
493
- public # these methods are public, but for Net::SSH internal use only
494
-
495
- # Enqueues pending output at the connection as CHANNEL_DATA packets. This
496
- # does nothing if the channel has not yet been confirmed open (see
497
- # #do_open_confirmation). This is called automatically by #process, which
498
- # is called from the event loop (Connection::Session#process). You will
499
- # generally not need to invoke it directly.
500
- def enqueue_pending_output #:nodoc:
501
- return unless remote_id
502
-
503
- while output.length > 0
504
- length = output.length
505
- length = remote_window_size if length > remote_window_size
506
- length = remote_maximum_packet_size if length > remote_maximum_packet_size
507
-
508
- if length > 0
509
- connection.send_message(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length)))
510
- output.consume!
511
- @remote_window_size -= length
512
- else
513
- break
5
+ module Net
6
+ module SSH
7
+ module Connection
8
+
9
+ # The channel abstraction. Multiple "channels" can be multiplexed onto a
10
+ # single SSH channel, each operating independently and seemingly in parallel.
11
+ # This class represents a single such channel. Most operations performed
12
+ # with the Net::SSH library will involve using one or more channels.
13
+ #
14
+ # Channels are intended to be used asynchronously. You request that one be
15
+ # opened (via Connection::Session#open_channel), and when it is opened, your
16
+ # callback is invoked. Then, you set various other callbacks on the newly
17
+ # opened channel, which are called in response to the corresponding events.
18
+ # Programming with Net::SSH works best if you think of your programs as
19
+ # state machines. Complex programs are best implemented as objects that
20
+ # wrap a channel. See Net::SCP and Net::SFTP for examples of how complex
21
+ # state machines can be built on top of the SSH protocol.
22
+ #
23
+ # ssh.open_channel do |channel|
24
+ # channel.exec("/invoke/some/command") do |ch, success|
25
+ # abort "could not execute command" unless success
26
+ #
27
+ # channel.on_data do |ch, data|
28
+ # puts "got stdout: #{data}"
29
+ # channel.send_data "something for stdin\n"
30
+ # end
31
+ #
32
+ # channel.on_extended_data do |ch, type, data|
33
+ # puts "got stderr: #{data}"
34
+ # end
35
+ #
36
+ # channel.on_close do |ch|
37
+ # puts "channel is closing!"
38
+ # end
39
+ # end
40
+ # end
41
+ #
42
+ # ssh.loop
43
+ #
44
+ # Channels also have a basic hash-like interface, that allows programs to
45
+ # store arbitrary state information on a channel object. This helps simplify
46
+ # the writing of state machines, especially when you may be juggling
47
+ # multiple open channels at the same time.
48
+ #
49
+ # Note that data sent across SSH channels are governed by maximum packet
50
+ # sizes and maximum window sizes. These details are managed internally
51
+ # by Net::SSH::Connection::Channel, so you may remain blissfully ignorant
52
+ # if you so desire, but you can always inspect the current maximums, as
53
+ # well as the remaining window size, using the reader attributes for those
54
+ # values.
55
+ class Channel
56
+ include Loggable
57
+ include Constants
58
+
59
+ # The local id for this channel, assigned by the Net::SSH::Connection::Session instance.
60
+ attr_reader :local_id
61
+
62
+ # The remote id for this channel, assigned by the remote host.
63
+ attr_reader :remote_id
64
+
65
+ # The type of this channel, usually "session".
66
+ attr_reader :type
67
+
68
+ # The underlying Net::SSH::Connection::Session instance that supports this channel.
69
+ attr_reader :connection
70
+
71
+ # The maximum packet size that the local host can receive.
72
+ attr_reader :local_maximum_packet_size
73
+
74
+ # The maximum amount of data that the local end of this channel can
75
+ # receive. This is a total, not per-packet.
76
+ attr_reader :local_maximum_window_size
77
+
78
+ # The maximum packet size that the remote host can receive.
79
+ attr_reader :remote_maximum_packet_size
80
+
81
+ # The maximum amount of data that the remote end of this channel can
82
+ # receive. This is a total, not per-packet.
83
+ attr_reader :remote_maximum_window_size
84
+
85
+ # This is the remaining window size on the local end of this channel. When
86
+ # this reaches zero, no more data can be received.
87
+ attr_reader :local_window_size
88
+
89
+ # This is the remaining window size on the remote end of this channel. When
90
+ # this reaches zero, no more data can be sent.
91
+ attr_reader :remote_window_size
92
+
93
+ # A hash of properties for this channel. These can be used to store state
94
+ # information about this channel. See also #[] and #[]=.
95
+ attr_reader :properties
96
+
97
+ # The output buffer for this channel. Data written to the channel is
98
+ # enqueued here, to be written as CHANNEL_DATA packets during each pass of
99
+ # the event loop. See Connection::Session#process and #enqueue_pending_output.
100
+ attr_reader :output #:nodoc:
101
+
102
+ # The list of pending requests. Each time a request is sent which requires
103
+ # a reply, the corresponding callback is pushed onto this queue. As responses
104
+ # arrive, they are shifted off the front and handled.
105
+ attr_reader :pending_requests #:nodoc:
106
+
107
+ # Instantiates a new channel on the given connection, of the given type,
108
+ # and with the given id. If a block is given, it will be remembered until
109
+ # the channel is confirmed open by the server, and will be invoked at
110
+ # that time (see #do_open_confirmation).
111
+ #
112
+ # This also sets the default maximum packet size and maximum window size.
113
+ def initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open)
114
+ self.logger = connection.logger
115
+
116
+ @connection = connection
117
+ @type = type
118
+ @local_id = local_id
119
+
120
+ @local_maximum_packet_size = max_pkt_size
121
+ @local_window_size = @local_maximum_window_size = max_win_size
122
+
123
+ @on_confirm_open = on_confirm_open
124
+
125
+ @output = Buffer.new
126
+
127
+ @properties = {}
128
+
129
+ @pending_requests = []
130
+ @on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil
131
+ @on_request = {}
132
+ @closing = @eof = @sent_eof = @local_closed = @remote_closed = false
133
+ end
134
+
135
+ # A shortcut for accessing properties of the channel (see #properties).
136
+ def [](name)
137
+ @properties[name]
138
+ end
139
+
140
+ # A shortcut for setting properties of the channel (see #properties).
141
+ def []=(name, value)
142
+ @properties[name] = value
143
+ end
144
+
145
+ # Syntactic sugar for executing a command. Sends a channel request asking
146
+ # that the given command be invoked. If the block is given, it will be
147
+ # called when the server responds. The first parameter will be the
148
+ # channel, and the second will be true or false, indicating whether the
149
+ # request succeeded or not. In this case, success means that the command
150
+ # is being executed, not that it has completed, and failure means that the
151
+ # command altogether failed to be executed.
152
+ #
153
+ # channel.exec "ls -l /home" do |ch, success|
154
+ # if success
155
+ # puts "command has begun executing..."
156
+ # # this is a good place to hang callbacks like #on_data...
157
+ # else
158
+ # puts "alas! the command could not be invoked!"
159
+ # end
160
+ # end
161
+ def exec(command, &block)
162
+ send_channel_request("exec", :string, command, &block)
163
+ end
164
+
165
+ # Syntactic sugar for requesting that a subsystem be started. Subsystems
166
+ # are a way for other protocols (like SFTP) to be run, using SSH as
167
+ # the transport. Generally, you'll never need to call this directly unless
168
+ # you are the implementor of something that consumes an SSH subsystem, like
169
+ # SFTP.
170
+ #
171
+ # channel.subsystem("sftp") do |ch, success|
172
+ # if success
173
+ # puts "subsystem successfully started"
174
+ # else
175
+ # puts "subsystem could not be started"
176
+ # end
177
+ # end
178
+ def subsystem(subsystem, &block)
179
+ send_channel_request("subsystem", :string, subsystem, &block)
180
+ end
181
+
182
+ # Syntactic sugar for setting an environment variable in the remote
183
+ # process' environment. Note that for security reasons, the server may
184
+ # refuse to set certain environment variables, or all, at the server's
185
+ # discretion. If you are connecting to an OpenSSH server, you will
186
+ # need to update the AcceptEnv setting in the sshd_config to include the
187
+ # environment variables you want to send.
188
+ #
189
+ # channel.env "PATH", "/usr/local/bin"
190
+ def env(variable_name, variable_value, &block)
191
+ send_channel_request("env", :string, variable_name, :string, variable_value, &block)
192
+ end
193
+
194
+ # A hash of the valid PTY options (see #request_pty).
195
+ VALID_PTY_OPTIONS = { term: "xterm",
196
+ chars_wide: 80,
197
+ chars_high: 24,
198
+ pixels_wide: 640,
199
+ pixels_high: 480,
200
+ modes: {} }
201
+
202
+ # Requests that a pseudo-tty (or "pty") be made available for this channel.
203
+ # This is useful when you want to invoke and interact with some kind of
204
+ # screen-based program (e.g., vim, or some menuing system).
205
+ #
206
+ # Note, that without a pty some programs (e.g. sudo, or subversion) on
207
+ # some systems, will not be able to run interactively, and will error
208
+ # instead of prompt if they ever need some user interaction.
209
+ #
210
+ # Note, too, that when a pty is requested, user's shell configuration
211
+ # scripts (.bashrc and such) are not run by default, whereas they are
212
+ # run when a pty is not present.
213
+ #
214
+ # channel.request_pty do |ch, success|
215
+ # if success
216
+ # puts "pty successfully obtained"
217
+ # else
218
+ # puts "could not obtain pty"
219
+ # end
220
+ # end
221
+ def request_pty(opts={}, &block)
222
+ extra = opts.keys - VALID_PTY_OPTIONS.keys
223
+ raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any?
224
+
225
+ opts = VALID_PTY_OPTIONS.merge(opts)
226
+
227
+ modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)|
228
+ memo.write_byte(mode).write_long(data)
514
229
  end
230
+ # mark the end of the mode opcode list with a 0 byte
231
+ modes.write_byte(0)
232
+
233
+ send_channel_request("pty-req", :string, opts[:term],
234
+ :long, opts[:chars_wide], :long, opts[:chars_high],
235
+ :long, opts[:pixels_wide], :long, opts[:pixels_high],
236
+ :string, modes.to_s, &block)
515
237
  end
516
- end
517
-
518
- # Invoked when the server confirms that a channel has been opened.
519
- # The remote_id is the id of the channel as assigned by the remote host,
520
- # and max_window and max_packet are the maximum window and maximum
521
- # packet sizes, respectively. If an open-confirmation callback was
522
- # given when the channel was created, it is invoked at this time with
523
- # the channel itself as the sole argument.
524
- def do_open_confirmation(remote_id, max_window, max_packet) #:nodoc:
525
- @remote_id = remote_id
526
- @remote_window_size = @remote_maximum_window_size = max_window
527
- @remote_maximum_packet_size = max_packet
528
- connection.forward.agent(self) if connection.options[:forward_agent] && type == "session"
529
- forward_local_env(connection.options[:send_env]) if connection.options[:send_env]
530
- @on_confirm_open.call(self) if @on_confirm_open
531
- end
532
-
533
- # Invoked when the server failed to open the channel. If an #on_open_failed
534
- # callback was specified, it will be invoked with the channel, reason code,
535
- # and description as arguments. Otherwise, a ChannelOpenFailed exception
536
- # will be raised.
537
- def do_open_failed(reason_code, description)
538
- if @on_open_failed
539
- @on_open_failed.call(self, reason_code, description)
540
- else
541
- raise ChannelOpenFailed.new(reason_code, description)
238
+
239
+ # Sends data to the channel's remote endpoint. This usually has the
240
+ # effect of sending the given string to the remote process' stdin stream.
241
+ # Note that it does not immediately send the data across the channel,
242
+ # but instead merely appends the given data to the channel's output buffer,
243
+ # preparatory to being packaged up and sent out the next time the connection
244
+ # is accepting data. (A connection might not be accepting data if, for
245
+ # instance, it has filled its data window and has not yet been resized by
246
+ # the remote end-point.)
247
+ #
248
+ # This will raise an exception if the channel has previously declared
249
+ # that no more data will be sent (see #eof!).
250
+ #
251
+ # channel.send_data("the password\n")
252
+ def send_data(data)
253
+ raise EOFError, "cannot send data if channel has declared eof" if eof?
254
+ output.append(data.to_s)
542
255
  end
543
- end
544
-
545
- # Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and
546
- # causes the remote window size to be adjusted upwards by the given
547
- # number of bytes. This has the effect of allowing more data to be sent
548
- # from the local end to the remote end of the channel.
549
- def do_window_adjust(bytes) #:nodoc:
550
- @remote_maximum_window_size += bytes
551
- @remote_window_size += bytes
552
- end
553
-
554
- # Invoked when the server sends a channel request. If any #on_request
555
- # callback has been registered for the specific type of this request,
556
- # it is invoked. If +want_reply+ is true, a packet will be sent of
557
- # either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback
558
- # to handle the request, CHANNEL_FAILURE will be sent. Otherwise,
559
- # CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The
560
- # callback should accept the channel as the first argument, and the
561
- # request-specific data as the second.
562
- def do_request(request, want_reply, data) #:nodoc:
563
- result = true
564
-
565
- begin
566
- callback = @on_request[request] or raise ChannelRequestFailed
567
- callback.call(self, data)
568
- rescue ChannelRequestFailed
569
- result = false
256
+
257
+ # Returns true if the channel exists in the channel list of the session,
258
+ # and false otherwise. This can be used to determine whether a channel has
259
+ # been closed or not.
260
+ #
261
+ # ssh.loop { channel.active? }
262
+ def active?
263
+ connection.channels.key?(local_id)
570
264
  end
571
-
572
- if want_reply
573
- msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id)
265
+
266
+ # Runs the SSH event loop until the channel is no longer active. This is
267
+ # handy for blocking while you wait for some channel to finish.
268
+ #
269
+ # channel.exec("grep ...") { ... }
270
+ # channel.wait
271
+ def wait
272
+ connection.loop { active? }
273
+ end
274
+
275
+ # True if close() has been called; NOTE: if the channel has data waiting to
276
+ # be sent then the channel will close after all the data is sent. See
277
+ # closed?() to determine if we have actually sent CHANNEL_CLOSE to server.
278
+ # This may be true for awhile before closed? returns true if we are still
279
+ # sending buffered output to server.
280
+ def closing?
281
+ @closing
282
+ end
283
+
284
+ # True if we have sent CHANNEL_CLOSE to the remote server.
285
+ def local_closed?
286
+ @local_closed
287
+ end
288
+
289
+ def remote_closed?
290
+ @remote_closed
291
+ end
292
+
293
+ def remote_closed!
294
+ @remote_closed = true
295
+ end
296
+
297
+ # Requests that the channel be closed. It only marks the channel to be closed
298
+ # the CHANNEL_CLOSE message will be sent from event loop
299
+ def close
300
+ return if @closing
301
+ @closing = true
302
+ end
303
+
304
+ # Returns true if the local end of the channel has declared that no more
305
+ # data is forthcoming (see #eof!). Trying to send data via #send_data when
306
+ # this is true will result in an exception being raised.
307
+ def eof?
308
+ @eof
309
+ end
310
+
311
+ # Tells the remote end of the channel that no more data is forthcoming
312
+ # from this end of the channel. The remote end may still send data.
313
+ # The CHANNEL_EOF packet will be sent once the output buffer is empty.
314
+ def eof!
315
+ return if eof?
316
+ @eof = true
317
+ end
318
+
319
+ # If an #on_process handler has been set up, this will cause it to be
320
+ # invoked (passing the channel itself as an argument). It also causes all
321
+ # pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output).
322
+ def process
323
+ @on_process.call(self) if @on_process
324
+ enqueue_pending_output
325
+
326
+ if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed
327
+ connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
328
+ @sent_eof = true
329
+ end
330
+
331
+ if @closing and not @local_closed and output.empty? and remote_id
332
+ connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
333
+ @local_closed = true
334
+ connection.cleanup_channel(self)
335
+ end
336
+ end
337
+
338
+ # Registers a callback to be invoked when data packets are received by the
339
+ # channel. The callback is called with the channel as the first argument,
340
+ # and the data as the second.
341
+ #
342
+ # channel.on_data do |ch, data|
343
+ # puts "got data: #{data.inspect}"
344
+ # end
345
+ #
346
+ # Data received this way is typically the data written by the remote
347
+ # process to its +stdout+ stream.
348
+ def on_data(&block)
349
+ old, @on_data = @on_data, block
350
+ old
351
+ end
352
+
353
+ # Registers a callback to be invoked when extended data packets are received
354
+ # by the channel. The callback is called with the channel as the first
355
+ # argument, the data type (as an integer) as the second, and the data as
356
+ # the third. Extended data is almost exclusively used to send +stderr+ data
357
+ # (+type+ == 1). Other extended data types are not defined by the SSH
358
+ # protocol.
359
+ #
360
+ # channel.on_extended_data do |ch, type, data|
361
+ # puts "got stderr: #{data.inspect}"
362
+ # end
363
+ def on_extended_data(&block)
364
+ old, @on_extended_data = @on_extended_data, block
365
+ old
366
+ end
367
+
368
+ # Registers a callback to be invoked for each pass of the event loop for
369
+ # this channel. There are no guarantees on timeliness in the event loop,
370
+ # but it will be called roughly once for each packet received by the
371
+ # connection (not the channel). This callback is invoked with the channel
372
+ # as the sole argument.
373
+ #
374
+ # Here's an example that accumulates the channel data into a variable on
375
+ # the channel itself, and displays individual lines in the input one
376
+ # at a time when the channel is processed:
377
+ #
378
+ # channel[:data] = ""
379
+ #
380
+ # channel.on_data do |ch, data|
381
+ # channel[:data] << data
382
+ # end
383
+ #
384
+ # channel.on_process do |ch|
385
+ # if channel[:data] =~ /^.*?\n/
386
+ # puts $&
387
+ # channel[:data] = $'
388
+ # end
389
+ # end
390
+ def on_process(&block)
391
+ old, @on_process = @on_process, block
392
+ old
393
+ end
394
+
395
+ # Registers a callback to be invoked when the server acknowledges that a
396
+ # channel is closed. This is invoked with the channel as the sole argument.
397
+ #
398
+ # channel.on_close do |ch|
399
+ # puts "remote end is closing!"
400
+ # end
401
+ def on_close(&block)
402
+ old, @on_close = @on_close, block
403
+ old
404
+ end
405
+
406
+ # Registers a callback to be invoked when the server indicates that no more
407
+ # data will be sent to the channel (although the channel can still send
408
+ # data to the server). The channel is the sole argument to the callback.
409
+ #
410
+ # channel.on_eof do |ch|
411
+ # puts "remote end is done sending data"
412
+ # end
413
+ def on_eof(&block)
414
+ old, @on_eof = @on_eof, block
415
+ old
416
+ end
417
+
418
+ # Registers a callback to be invoked when the server was unable to open
419
+ # the requested channel. The channel itself will be passed to the block,
420
+ # along with the integer "reason code" for the failure, and a textual
421
+ # description of the failure from the server.
422
+ #
423
+ # channel = session.open_channel do |ch|
424
+ # # ..
425
+ # end
426
+ #
427
+ # channel.on_open_failed { |ch, code, desc| ... }
428
+ def on_open_failed(&block)
429
+ old, @on_open_failed = @on_open_failed, block
430
+ old
431
+ end
432
+
433
+ # Registers a callback to be invoked when a channel request of the given
434
+ # type is received. The callback will receive the channel as the first
435
+ # argument, and the associated (unparsed) data as the second. The data
436
+ # will be a Net::SSH::Buffer that you will need to parse, yourself,
437
+ # according to the kind of request you are watching.
438
+ #
439
+ # By default, if the request wants a reply, Net::SSH will send a
440
+ # CHANNEL_SUCCESS response for any request that was handled by a registered
441
+ # callback, and CHANNEL_FAILURE for any that wasn't, but if you want your
442
+ # registered callback to result in a CHANNEL_FAILURE response, just raise
443
+ # Net::SSH::ChannelRequestFailed.
444
+ #
445
+ # Some common channel requests that your programs might want to listen
446
+ # for are:
447
+ #
448
+ # * "exit-status" : the exit status of the remote process will be reported
449
+ # as a long integer in the data buffer, which you can grab via
450
+ # data.read_long.
451
+ # * "exit-signal" : if the remote process died as a result of a signal
452
+ # being sent to it, the signal will be reported as a string in the
453
+ # data, via data.read_string. (Not all SSH servers support this channel
454
+ # request type.)
455
+ #
456
+ # channel.on_request "exit-status" do |ch, data|
457
+ # puts "process terminated with exit status: #{data.read_long}"
458
+ # end
459
+ def on_request(type, &block)
460
+ old, @on_request[type] = @on_request[type], block
461
+ old
462
+ end
463
+
464
+ # Sends a new channel request with the given name. The extra +data+
465
+ # parameter must either be empty, or consist of an even number of
466
+ # arguments. See Net::SSH::Buffer.from for a description of their format.
467
+ # If a block is given, it is registered as a callback for a pending
468
+ # request, and the packet will be flagged so that the server knows a
469
+ # reply is required. If no block is given, the server will send no
470
+ # response to this request. Responses, where required, will cause the
471
+ # callback to be invoked with the channel as the first argument, and
472
+ # either true or false as the second, depending on whether the request
473
+ # succeeded or not. The meaning of "success" and "failure" in this context
474
+ # is dependent on the specific request that was sent.
475
+ #
476
+ # channel.send_channel_request "shell" do |ch, success|
477
+ # if success
478
+ # puts "user shell started successfully"
479
+ # else
480
+ # puts "could not start user shell"
481
+ # end
482
+ # end
483
+ #
484
+ # Most channel requests you'll want to send are already wrapped in more
485
+ # convenient helper methods (see #exec and #subsystem).
486
+ def send_channel_request(request_name, *data, &callback)
487
+ info { "sending channel request #{request_name.inspect}" }
488
+ fail "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel" unless remote_id
489
+ msg = Buffer.from(:byte, CHANNEL_REQUEST,
490
+ :long, remote_id, :string, request_name,
491
+ :bool, !callback.nil?, *data)
574
492
  connection.send_message(msg)
493
+ pending_requests << callback if callback
575
494
  end
576
- end
577
-
578
- # Invokes the #on_data callback when the server sends data to the
579
- # channel. This will reduce the available window size on the local end,
580
- # but does not actually throttle requests that come in illegally when
581
- # the window size is too small. The callback is invoked with the channel
582
- # as the first argument, and the data as the second.
583
- def do_data(data) #:nodoc:
584
- update_local_window_size(data.length)
585
- @on_data.call(self, data) if @on_data
586
- end
587
-
588
- # Invokes the #on_extended_data callback when the server sends
589
- # extended data to the channel. This will reduce the available window
590
- # size on the local end. The callback is invoked with the channel,
591
- # type, and data.
592
- def do_extended_data(type, data)
593
- update_local_window_size(data.length)
594
- @on_extended_data.call(self, type, data) if @on_extended_data
595
- end
596
-
597
- # Invokes the #on_eof callback when the server indicates that no
598
- # further data is forthcoming. The callback is invoked with the channel
599
- # as the argument.
600
- def do_eof
601
- @on_eof.call(self) if @on_eof
602
- end
603
-
604
- # Invokes the #on_close callback when the server closes a channel.
605
- # The channel is the only argument.
606
- def do_close
607
- @on_close.call(self) if @on_close
608
- end
609
-
610
- # Invokes the next pending request callback with +false+ as the second
611
- # argument.
612
- def do_failure
613
- if callback = pending_requests.shift
614
- callback.call(self, false)
615
- else
616
- error { "channel failure received with no pending request to handle it (bug?)" }
495
+
496
+ public # these methods are public, but for Net::SSH internal use only
497
+
498
+ # Enqueues pending output at the connection as CHANNEL_DATA packets. This
499
+ # does nothing if the channel has not yet been confirmed open (see
500
+ # #do_open_confirmation). This is called automatically by #process, which
501
+ # is called from the event loop (Connection::Session#process). You will
502
+ # generally not need to invoke it directly.
503
+ def enqueue_pending_output #:nodoc:
504
+ return unless remote_id
505
+
506
+ while output.length > 0
507
+ length = output.length
508
+ length = remote_window_size if length > remote_window_size
509
+ length = remote_maximum_packet_size if length > remote_maximum_packet_size
510
+
511
+ if length > 0
512
+ connection.send_message(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length)))
513
+ output.consume!
514
+ @remote_window_size -= length
515
+ else
516
+ break
517
+ end
518
+ end
617
519
  end
618
- end
619
-
620
- # Invokes the next pending request callback with +true+ as the second
621
- # argument.
622
- def do_success
623
- if callback = pending_requests.shift
624
- callback.call(self, true)
625
- else
626
- error { "channel success received with no pending request to handle it (bug?)" }
520
+
521
+ # Invoked when the server confirms that a channel has been opened.
522
+ # The remote_id is the id of the channel as assigned by the remote host,
523
+ # and max_window and max_packet are the maximum window and maximum
524
+ # packet sizes, respectively. If an open-confirmation callback was
525
+ # given when the channel was created, it is invoked at this time with
526
+ # the channel itself as the sole argument.
527
+ def do_open_confirmation(remote_id, max_window, max_packet) #:nodoc:
528
+ @remote_id = remote_id
529
+ @remote_window_size = @remote_maximum_window_size = max_window
530
+ @remote_maximum_packet_size = max_packet
531
+ connection.forward.agent(self) if connection.options[:forward_agent] && type == "session"
532
+ forward_local_env(connection.options[:send_env]) if connection.options[:send_env]
533
+ set_remote_env(connection.options[:set_env]) if connection.options[:set_env]
534
+ @on_confirm_open.call(self) if @on_confirm_open
535
+ end
536
+
537
+ # Invoked when the server failed to open the channel. If an #on_open_failed
538
+ # callback was specified, it will be invoked with the channel, reason code,
539
+ # and description as arguments. Otherwise, a ChannelOpenFailed exception
540
+ # will be raised.
541
+ def do_open_failed(reason_code, description)
542
+ if @on_open_failed
543
+ @on_open_failed.call(self, reason_code, description)
544
+ else
545
+ raise ChannelOpenFailed.new(reason_code, description)
546
+ end
547
+ end
548
+
549
+ # Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and
550
+ # causes the remote window size to be adjusted upwards by the given
551
+ # number of bytes. This has the effect of allowing more data to be sent
552
+ # from the local end to the remote end of the channel.
553
+ def do_window_adjust(bytes) #:nodoc:
554
+ @remote_maximum_window_size += bytes
555
+ @remote_window_size += bytes
556
+ end
557
+
558
+ # Invoked when the server sends a channel request. If any #on_request
559
+ # callback has been registered for the specific type of this request,
560
+ # it is invoked. If +want_reply+ is true, a packet will be sent of
561
+ # either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback
562
+ # to handle the request, CHANNEL_FAILURE will be sent. Otherwise,
563
+ # CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The
564
+ # callback should accept the channel as the first argument, and the
565
+ # request-specific data as the second.
566
+ def do_request(request, want_reply, data) #:nodoc:
567
+ result = true
568
+
569
+ begin
570
+ callback = @on_request[request] or raise ChannelRequestFailed
571
+ callback.call(self, data)
572
+ rescue ChannelRequestFailed
573
+ result = false
574
+ end
575
+
576
+ if want_reply
577
+ msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id)
578
+ connection.send_message(msg)
579
+ end
580
+ end
581
+
582
+ # Invokes the #on_data callback when the server sends data to the
583
+ # channel. This will reduce the available window size on the local end,
584
+ # but does not actually throttle requests that come in illegally when
585
+ # the window size is too small. The callback is invoked with the channel
586
+ # as the first argument, and the data as the second.
587
+ def do_data(data) #:nodoc:
588
+ update_local_window_size(data.length)
589
+ @on_data.call(self, data) if @on_data
590
+ end
591
+
592
+ # Invokes the #on_extended_data callback when the server sends
593
+ # extended data to the channel. This will reduce the available window
594
+ # size on the local end. The callback is invoked with the channel,
595
+ # type, and data.
596
+ def do_extended_data(type, data)
597
+ update_local_window_size(data.length)
598
+ @on_extended_data.call(self, type, data) if @on_extended_data
599
+ end
600
+
601
+ # Invokes the #on_eof callback when the server indicates that no
602
+ # further data is forthcoming. The callback is invoked with the channel
603
+ # as the argument.
604
+ def do_eof
605
+ @on_eof.call(self) if @on_eof
606
+ end
607
+
608
+ # Invokes the #on_close callback when the server closes a channel.
609
+ # The channel is the only argument.
610
+ def do_close
611
+ @on_close.call(self) if @on_close
612
+ end
613
+
614
+ # Invokes the next pending request callback with +false+ as the second
615
+ # argument.
616
+ def do_failure
617
+ if callback = pending_requests.shift
618
+ callback.call(self, false)
619
+ else
620
+ error { "channel failure received with no pending request to handle it (bug?)" }
621
+ end
622
+ end
623
+
624
+ # Invokes the next pending request callback with +true+ as the second
625
+ # argument.
626
+ def do_success
627
+ if callback = pending_requests.shift
628
+ callback.call(self, true)
629
+ else
630
+ error { "channel success received with no pending request to handle it (bug?)" }
631
+ end
627
632
  end
628
- end
629
-
630
- private
631
633
 
632
- # Runs the SSH event loop until the remote confirmed channel open
633
- # experimental api
634
- def wait_until_open_confirmed
635
- connection.loop { !remote_id }
636
- end
634
+ private
637
635
 
638
- # Updates the local window size by the given amount. If the window
639
- # size drops to less than half of the local maximum (an arbitrary
640
- # threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the
641
- # server telling it that the window size has grown.
642
- def update_local_window_size(size)
643
- @local_window_size -= size
644
- if local_window_size < local_maximum_window_size/2
645
- connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST,
646
- :long, remote_id, :long, 0x20000))
647
- @local_window_size += 0x20000
648
- @local_maximum_window_size += 0x20000
636
+ # Runs the SSH event loop until the remote confirmed channel open
637
+ # experimental api
638
+ def wait_until_open_confirmed
639
+ connection.loop { !remote_id }
649
640
  end
650
- end
651
641
 
652
- # Gets an +Array+ of local environment variables in the remote process'
653
- # environment.
654
- # A variable name can either be described by a +Regexp+ or +String+.
655
- #
656
- # channel.forward_local_env [/^GIT_.*$/, "LANG"]
657
- def forward_local_env(env_variable_patterns)
658
- Array(env_variable_patterns).each do |env_variable_pattern|
659
- matched_variables = ENV.find_all do |env_name, _|
660
- case env_variable_pattern
661
- when Regexp then env_name =~ env_variable_pattern
662
- when String then env_name == env_variable_pattern
642
+ LOCAL_WINDOW_SIZE_INCREMENT = 0x20000
643
+ GOOD_LOCAL_MAXIUMUM_WINDOW_SIZE = 10 * LOCAL_WINDOW_SIZE_INCREMENT
644
+
645
+ # Updates the local window size by the given amount. If the window
646
+ # size drops to less than half of the local maximum (an arbitrary
647
+ # threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the
648
+ # server telling it that the window size has grown.
649
+ def update_local_window_size(size)
650
+ @local_window_size -= size
651
+ if local_window_size < local_maximum_window_size / 2
652
+ connection.send_message(
653
+ Buffer.from(:byte, CHANNEL_WINDOW_ADJUST, :long, remote_id, :long, LOCAL_WINDOW_SIZE_INCREMENT)
654
+ )
655
+ @local_window_size += LOCAL_WINDOW_SIZE_INCREMENT
656
+
657
+ if @local_maximum_window_size < @local_window_size || @local_maximum_window_size < GOOD_LOCAL_MAXIUMUM_WINDOW_SIZE
658
+ @local_maximum_window_size += LOCAL_WINDOW_SIZE_INCREMENT
663
659
  end
664
660
  end
665
- matched_variables.each do |env_name, env_value|
666
- self.env(env_name, env_value)
661
+ end
662
+
663
+ # Gets an +Array+ of local environment variables in the remote process'
664
+ # environment.
665
+ # A variable name can either be described by a +Regexp+ or +String+.
666
+ #
667
+ # channel.forward_local_env [/^GIT_.*$/, "LANG"]
668
+ def forward_local_env(env_variable_patterns)
669
+ Array(env_variable_patterns).each do |env_variable_pattern|
670
+ matched_variables = ENV.find_all do |env_name, _|
671
+ case env_variable_pattern
672
+ when Regexp then env_name =~ env_variable_pattern
673
+ when String then env_name == env_variable_pattern
674
+ end
675
+ end
676
+ matched_variables.each do |env_name, env_value|
677
+ self.env(env_name, env_value)
678
+ end
667
679
  end
668
680
  end
681
+
682
+ # Set a +Hash+ of environment variables in the remote process' environment.
683
+ #
684
+ # channel.set_remote_env foo: 'bar', baz: 'whale'
685
+ def set_remote_env(env)
686
+ env.each { |key, value| self.env(key, value) }
687
+ end
669
688
  end
670
- end
671
689
 
672
- end; end; end
690
+ end
691
+ end
692
+ end